From 818a9fce7af220a8b83990b63b6a4ee1078eb4ac Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 23 Jun 2025 16:05:38 -0400 Subject: [PATCH 001/493] Rename references to a2a-java-sdk to a2a-java (#123) --- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 22 +++++++++---------- README.md | 13 ++++++++--- .../a2a/examples/helloworld/client/README.md | 6 ++--- .../a2a/examples/helloworld/server/README.md | 2 +- .../apps/jakarta/A2AServerResource.java | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 2 +- .../apps/common/AbstractA2AServerTest.java | 10 ++++----- 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 15a030f0d..4eb10e60b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -63,7 +63,7 @@ reported to the community leaders responsible for enforcement: Farah Juma - fjuma@redhat.com \ Kabir Khan - kkhan@redhat.com \ -Stefano Maestri - smaestri@redhat.com \ +Stefano Maestri - smaestri@redhat.com All complaints will be reviewed and investigated promptly and fairly. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b415751cf..479228199 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -Contributing to a2a-java-sdk +Contributing to a2a-java ================================== Welcome to the A2A Java SDK project! We welcome contributions from the community. This guide will walk you through the steps for getting started on our project. @@ -12,12 +12,12 @@ Welcome to the A2A Java SDK project! We welcome contributions from the community ## Forking the Project -To contribute, you will first need to fork the [a2a-java-sdk](https://github.com/fjuma/a2a-java-sdk) repository. +To contribute, you will first need to fork the [a2a-java](https://github.com/a2aproject/a2a-java) repository. This can be done by looking in the top-right corner of the repository page and clicking "Fork". ![fork](images/fork.jpg) -The next step is to clone your newly forked repository onto your local workspace. This can be done by going to your newly forked repository, which should be at `https://github.com/USERNAME/a2a-java-sdk`. +The next step is to clone your newly forked repository onto your local workspace. This can be done by going to your newly forked repository, which should be at `https://github.com/USERNAME/a2a-java`. Then, there will be a green button that says "Code". Click on that and copy the URL. @@ -30,17 +30,17 @@ Be sure to replace [URL] with the URL that you copied. Now you have the repository on your computer! ## Issues -The `a2a-java-sdk` project uses GitHub to manage issues. All issues can be found [here](https://github.com/fjuma/a2a-java-sdk/issues). +The `a2a-java` project uses GitHub to manage issues. All issues can be found [here](https://github.com/a2aproject/a2a-java/issues). To create a new issue, comment on an existing issue, or assign an issue to yourself, you'll need to first [create a GitHub account](https://github.com/). ### Good First Issues -Want to contribute to the a2a-java-sdk project but aren't quite sure where to start? Check out our issues with the `good-first-issue` label. These are a triaged set of issues that are great for getting started on our project. These can be found [here](https://github.com/fjuma/a2a-java-sdk/labels/good%20first%20issue). +Want to contribute to the a2a-java project but aren't quite sure where to start? Check out our issues with the `good-first-issue` label. These are a triaged set of issues that are great for getting started on our project. These can be found [here](https://github.com/a2aproject/a2a-java/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22). Once you have selected an issue you'd like to work on, make sure it's not already assigned to someone else, and assign it to yourself. -It is recommended that you use a separate branch for every issue you work on. To keep things straightforward and memorable, you can name each branch using the GitHub issue number. This way, you can have multiple PRs open for different issues. For example, if you were working on [issue-20](https://github.com/fjuma/a2a-java-sdk/issues/20), you could use `issue-20` as your branch name. +It is recommended that you use a separate branch for every issue you work on. To keep things straightforward and memorable, you can name each branch using the GitHub issue number. This way, you can have multiple PRs open for different issues. For example, if you were working on [issue-20](https://github.com/a2aproject/a2a-java/issues/20), you could use `issue-20` as your branch name. ## Setting up your Developer Environment You will need: @@ -51,15 +51,15 @@ You will need: To set up your development environment you need to: -1. First `cd` to the directory where you cloned the project (eg: `cd a2a-java-sdk`) +1. First `cd` to the directory where you cloned the project (eg: `cd a2a-java`) 2. Add a remote ref to upstream, for pulling future updates. For example: ``` - git remote add upstream https://github.com/fjuma/a2a-java-sdk + git remote add upstream https://github.com/a2aproject/a2a-java ``` -3. To build `a2a-java-sdk` and run the tests, use the following command: +3. To build `a2a-java` and run the tests, use the following command: ``` mvn clean install @@ -82,10 +82,10 @@ When submitting a PR, please keep the following guidelines in mind: 3. Your PR should include tests for the functionality that you are adding. -4. Your PR should include appropriate [documentation](https://github.com/fjuma/a2a-java-sdk/blob/main/README.md) for the functionality that you are adding. +4. Your PR should include appropriate [documentation](https://github.com/a2aproject/a2a-java/blob/main/README.md) for the functionality that you are adding. ## Code Reviews -All submissions, including submissions by project members, need to be reviewed by at least one `a2a-java-sdk` committer before being merged. +All submissions, including submissions by project members, need to be reviewed by at least one `a2a-java` committer before being merged. The [GitHub Pull Request Review Process](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews) is followed for every pull request. diff --git a/README.md b/README.md index 8f39fbbad..ed570f6a8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # A2A Java SDK +[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) + + + -

A Java library that helps run agentic applications as A2AServers following Google's Agent2Agent (A2A) Protocol.

+

+ A2A Logo +

+

A Java library that helps run agentic applications as A2AServers following the Agent2Agent (A2A) Protocol.

## Installation @@ -30,7 +37,7 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) ### 1. Add the A2A Java SDK Core Maven dependency to your project > **Note**: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be -> sure to check out the latest tag (you can see the tags [here](https://github.com/fjuma/a2a-java-sdk/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. +> sure to check out the latest tag (you can see the tags [here](https://github.com/a2aproject/a2a-java/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. ```xml @@ -176,7 +183,7 @@ public class WeatherAgentExecutorProducer { ### 4. Add an A2A Java SDK Server Maven dependency to your project > **Note**: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be -> sure to check out the latest tag (you can see the tags [here](https://github.com/fjuma/a2a-java-sdk/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. +> sure to check out the latest tag (you can see the tags [here](https://github.com/a2aproject/a2a-java/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. Adding a dependency on an A2A Java SDK Server will allow you to run your agentic Java application as an A2A server. diff --git a/examples/src/main/java/io/a2a/examples/helloworld/client/README.md b/examples/src/main/java/io/a2a/examples/helloworld/client/README.md index f84488ece..b6cefa6bd 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/client/README.md +++ b/examples/src/main/java/io/a2a/examples/helloworld/client/README.md @@ -47,10 +47,10 @@ The Java client can be run using JBang, which allows you to run Java source file ### Build the A2A Java SDK -First, ensure you have built the `a2a-java-sdk` project: +First, ensure you have built the `a2a-java` project: ```bash -cd /path/to/a2a-java-sdk +cd /path/to/a2a-java mvn clean install ``` @@ -81,7 +81,7 @@ Alternatively, you can run the Java client with JBang by specifying the classpat mvn clean package ``` -2. Run the Java client with JBang from the root of the a2a-java-sdk project: +2. Run the Java client with JBang from the root of the a2a-java project: ```bash jbang --cp target/classes examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldClient.java ``` diff --git a/examples/src/main/java/io/a2a/examples/helloworld/server/README.md b/examples/src/main/java/io/a2a/examples/helloworld/server/README.md index 020b849bc..1289cc8fa 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/server/README.md +++ b/examples/src/main/java/io/a2a/examples/helloworld/server/README.md @@ -54,7 +54,7 @@ The client will connect to the Java server running on `http://localhost:9999`. The Python A2A client (`test_client.py`) performs the following actions: 1. Fetches the server's public agent card -2. Fetches the server's extended agent card if supported by the server (see https://github.com/fjuma/a2a-java-sdk/issues/81) +2. Fetches the server's extended agent card if supported by the server (see https://github.com/a2aproject/a2a-java/issues/81) 3. Creates an A2A client using the extended agent card that connects to the Python server at `http://localhost:9999`. 4. Sends a regular message asking "how much is 10 USD in INR?". 5. Prints the server's response. diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java index e4b58b11a..1c90c6339 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java @@ -117,7 +117,7 @@ public AgentCard getAgentCard() { @Produces(MediaType.APPLICATION_JSON) public Response getAuthenticatedExtendedAgentCard() { // TODO need to add authentication for this endpoint - // https://github.com/fjuma/a2a-java-sdk/issues/77 + // https://github.com/a2aproject/a2a-java/issues/77 if (! jsonRpcHandler.getAgentCard().supportsAuthenticatedExtendedCard()) { JSONErrorResponse errorResponse = new JSONErrorResponse("Extended agent card not supported or not enabled."); return Response.status(Response.Status.NOT_FOUND) diff --git a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 44eecd60a..f06debd50 100644 --- a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -161,7 +161,7 @@ public AgentCard getAgentCard() { @Route(path = "/agent/authenticatedExtendedCard", methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) public void getAuthenticatedExtendedAgentCard(RoutingExchange re) { // TODO need to add authentication for this endpoint - // https://github.com/fjuma/a2a-java-sdk/issues/77 + // https://github.com/a2aproject/a2a-java/issues/77 try { if (! jsonRpcHandler.getAgentCard().supportsAuthenticatedExtendedCard()) { JSONErrorResponse errorResponse = new JSONErrorResponse("Extended agent card not supported or not enabled."); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index ee582afae..cd3a8ba1c 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -154,7 +154,7 @@ public void testGetTaskNotFound() { .extract() .as(GetTaskResponse.class); assertEquals("1", response.getId()); - // this should be an instance of TaskNotFoundError, see https://github.com/fjuma/a2a-java-sdk/issues/23 + // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 assertInstanceOf(JSONRPCError.class, response.getError()); assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); assertNull(response.getResult()); @@ -202,7 +202,7 @@ public void testCancelTaskNotSupported() { .as(CancelTaskResponse.class); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/fjuma/a2a-java-sdk/issues/23 + // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 assertInstanceOf(JSONRPCError.class, response.getError()); assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); } catch (Exception e) { @@ -225,7 +225,7 @@ public void testCancelTaskNotFound() { .as(CancelTaskResponse.class); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/fjuma/a2a-java-sdk/issues/23 + // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 assertInstanceOf(JSONRPCError.class, response.getError()); assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); } @@ -376,7 +376,7 @@ public void testError() { .as(SendMessageResponse.class); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/fjuma/a2a-java-sdk/issues/23 + // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 assertInstanceOf(JSONRPCError.class, response.getError()); assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); } @@ -732,7 +732,7 @@ public void testResubscribeNoExistingTaskError() throws Exception { if (jsonResponse != null) { assertEquals(request.getId(), jsonResponse.getId()); assertNull(jsonResponse.getResult()); - // this should be an instance of TaskNotFoundError, see https://github.com/fjuma/a2a-java-sdk/issues/23 + // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 assertInstanceOf(JSONRPCError.class, jsonResponse.getError()); assertEquals(new TaskNotFoundError().getCode(), jsonResponse.getError().getCode()); latch.countDown(); From ea9ed897417a9085d45c1e4a3ac08adecb0a4204 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 24 Jun 2025 17:28:41 -0400 Subject: [PATCH 002/493] ci: Add in repo automation for releases/issues (#125) --- .github/ISSUE_TEMPLATE/bug-report.yml | 33 +++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.yml | 41 ++++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 15 ++++++++ .github/conventional-commit-lint.yaml | 2 ++ .github/release-please.yml | 4 +++ .github/release-trigger.yml | 1 + 6 files changed, 96 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/conventional-commit-lint.yaml create mode 100644 .github/release-please.yml create mode 100644 .github/release-trigger.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..8b865656f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,33 @@ +name: 🐞 Bug Report +description: File a bug report +title: "[Bug]: " +type: "Bug" +body: + - type: markdown + attributes: + value: | + Thanks for stopping by to let us know something could be better! + Private Feedback? Please use this [Google form](https://goo.gle/a2a-feedback) + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us what you expected to happen and how to reproduce the issue. + placeholder: Tell us what you see! + value: "A bug happened!" + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/google-a2a/A2A?tab=coc-ov-file#readme) + options: + - label: I agree to follow this project's Code of Conduct + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 000000000..2a507a774 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,41 @@ +name: 💡 Feature Request +description: Suggest an idea for this repository +title: "[Feat]: " +type: "Feature" +body: + - type: markdown + attributes: + value: | + Thanks for stopping by to let us know something could be better! + Private Feedback? Please use this [Google form](https://goo.gle/a2a-feedback) + - type: textarea + id: problem + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. + placeholder: Ex. I'm always frustrated when [...] + - type: textarea + id: describe + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/google-a2a/A2A?tab=coc-ov-file#readme) + options: + - label: I agree to follow this project's Code of Conduct + required: true \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..f407e6142 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +# Description + +Thank you for opening a Pull Request! +Before submitting your PR, there are a few things you can do to make sure it goes smoothly: + +- [ ] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). +- [ ] Make your Pull Request title in the specification. + - Important Prefixes for [release-please](https://github.com/googleapis/release-please): + - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. + - `feat:` represents a new feature, and correlates to a SemVer minor. + - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. +- [ ] Ensure the tests pass +- [ ] Appropriate READMEs were updated (if necessary) + +Fixes # 🦕 \ No newline at end of file diff --git a/.github/conventional-commit-lint.yaml b/.github/conventional-commit-lint.yaml new file mode 100644 index 000000000..c967ffa6f --- /dev/null +++ b/.github/conventional-commit-lint.yaml @@ -0,0 +1,2 @@ +enabled: true +always_check_pr_title: true diff --git a/.github/release-please.yml b/.github/release-please.yml new file mode 100644 index 000000000..f411bac3e --- /dev/null +++ b/.github/release-please.yml @@ -0,0 +1,4 @@ +releaseType: maven +handleGHRelease: true +bumpMinorPreMajor: false +bumpPatchForMinorPreMajor: true diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml new file mode 100644 index 000000000..d4ca94189 --- /dev/null +++ b/.github/release-trigger.yml @@ -0,0 +1 @@ +enabled: true From 6042cca69cb20a3b1088407394d82eae76d31284 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 24 Jun 2025 17:31:05 -0400 Subject: [PATCH 003/493] Split the examples into two directories, one for client and one for server, and update dependencies accordingly (#124) This PR splits the examples into two different modules, one for client and one for server and updates the instructions accordingly. It also cleans up some dependencies and removes unnecessary ones. --- README.md | 2 +- core/pom.xml | 9 +-- examples/client/pom.xml | 41 +++++++++++++ .../helloworld}/HelloWorldClient.java | 2 +- .../helloworld}/HelloWorldRunner.java | 5 +- .../a2a/examples/helloworld}/INSTALL_JBANG.md | 0 .../io/a2a/examples/helloworld}/README.md | 18 +----- examples/pom.xml | 50 +++++++++------- examples/server/pom.xml | 57 +++++++++++++++++++ .../helloworld}/AgentCardProducer.java | 2 +- .../helloworld}/AgentExecutorProducer.java | 2 +- .../io/a2a/examples/helloworld}/README.md | 2 +- .../src/main/resources/application.properties | 0 tests/server-common/pom.xml | 5 ++ 14 files changed, 145 insertions(+), 50 deletions(-) create mode 100644 examples/client/pom.xml rename examples/{src/main/java/io/a2a/examples/helloworld/client => client/src/main/java/io/a2a/examples/helloworld}/HelloWorldClient.java (98%) rename examples/{src/main/java/io/a2a/examples/helloworld/client => client/src/main/java/io/a2a/examples/helloworld}/HelloWorldRunner.java (80%) rename examples/{src/main/java/io/a2a/examples/helloworld/client => client/src/main/java/io/a2a/examples/helloworld}/INSTALL_JBANG.md (100%) rename examples/{src/main/java/io/a2a/examples/helloworld/client => client/src/main/java/io/a2a/examples/helloworld}/README.md (84%) create mode 100644 examples/server/pom.xml rename examples/{src/main/java/io/a2a/examples/helloworld/server => server/src/main/java/io/a2a/examples/helloworld}/AgentCardProducer.java (97%) rename examples/{src/main/java/io/a2a/examples/helloworld/server => server/src/main/java/io/a2a/examples/helloworld}/AgentExecutorProducer.java (95%) rename examples/{src/main/java/io/a2a/examples/helloworld/server => server/src/main/java/io/a2a/examples/helloworld}/README.md (99%) rename examples/{ => server}/src/main/resources/application.properties (100%) diff --git a/README.md b/README.md index ed570f6a8..849f102df 100644 --- a/README.md +++ b/README.md @@ -344,7 +344,7 @@ A complete example of an A2A client communicating with a Python A2A server is av - Sending regular and streaming messages - Receiving and processing responses -The example includes detailed instructions on how to run both the Python server and the Java client using JBang. Check out the [example's README](src/main/java/io/a2a/examples/helloworld/README.md) for more information. +The example includes detailed instructions on how to run both the Python server and the Java client using JBang. Check out the [example's README](examples/client/src/main/java/io/a2a/examples/helloworld/README.md) for more information. ## License diff --git a/core/pom.xml b/core/pom.xml index 8aabba0d3..2bca1b769 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,10 +29,6 @@ io.smallrye.reactive mutiny-zero - - io.quarkus - quarkus-arc - jakarta.enterprise jakarta.enterprise.cdi-api @@ -42,6 +38,11 @@ org.slf4j slf4j-api + + io.quarkus + quarkus-arc + test + org.junit.jupiter junit-jupiter-api diff --git a/examples/client/pom.xml b/examples/client/pom.xml new file mode 100644 index 000000000..4626af575 --- /dev/null +++ b/examples/client/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + io.a2a.sdk + a2a-java-sdk-examples-parent + 0.2.4-SNAPSHOT + + + a2a-java-sdk-examples-client + + Java SDK A2A Examples + Examples for the Java SDK for the Agent2Agent Protocol (A2A) + + + + io.a2a.sdk + a2a-java-sdk-core + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + \ No newline at end of file diff --git a/examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldClient.java b/examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java similarity index 98% rename from examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldClient.java rename to examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index 19d8d1213..cea945492 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldClient.java +++ b/examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -1,4 +1,4 @@ -package io.a2a.examples.helloworld.client; +package io.a2a.examples.helloworld; import java.util.HashMap; import java.util.Map; diff --git a/examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldRunner.java b/examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java similarity index 80% rename from examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldRunner.java rename to examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index ed214d277..b87b14ce3 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldRunner.java +++ b/examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//REPOS file://~/.m2/repository/ -//DEPS io.a2a.sdk:a2a-java-sdk:0.2.4-SNAPSHOT +//DEPS io.a2a.sdk:a2a-java-sdk-core:0.2.4-SNAPSHOT //SOURCES HelloWorldClient.java /** @@ -18,6 +17,6 @@ */ public class HelloWorldRunner { public static void main(String[] args) { - io.a2a.examples.helloworld.client.HelloWorldClient.main(args); + io.a2a.examples.helloworld.HelloWorldClient.main(args); } } \ No newline at end of file diff --git a/examples/src/main/java/io/a2a/examples/helloworld/client/INSTALL_JBANG.md b/examples/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md similarity index 100% rename from examples/src/main/java/io/a2a/examples/helloworld/client/INSTALL_JBANG.md rename to examples/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md diff --git a/examples/src/main/java/io/a2a/examples/helloworld/client/README.md b/examples/client/src/main/java/io/a2a/examples/helloworld/README.md similarity index 84% rename from examples/src/main/java/io/a2a/examples/helloworld/client/README.md rename to examples/client/src/main/java/io/a2a/examples/helloworld/README.md index b6cefa6bd..d3d33c0bd 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/client/README.md +++ b/examples/client/src/main/java/io/a2a/examples/helloworld/README.md @@ -54,7 +54,7 @@ cd /path/to/a2a-java mvn clean install ``` -### Option 1: Using the JBang script (Recommended) +### Using the JBang script A JBang script is provided in the example directory to make running the client easy: @@ -62,7 +62,7 @@ A JBang script is provided in the example directory to make running the client e 2. Navigate to the example directory: ```bash - cd examples/src/main/java/io/a2a/examples/helloworld/client + cd examples/client/src/main/java/io/a2a/examples/helloworld ``` 3. Run the client using the JBang script: @@ -72,20 +72,6 @@ A JBang script is provided in the example directory to make running the client e This script automatically handles the dependencies and sources for you. -### Option 2: Running with explicit classpath - -Alternatively, you can run the Java client with JBang by specifying the classpath explicitly: - -1. First, build the project with Maven: - ```bash - mvn clean package - ``` - -2. Run the Java client with JBang from the root of the a2a-java project: - ```bash - jbang --cp target/classes examples/src/main/java/io/a2a/examples/helloworld/client/HelloWorldClient.java - ``` - ## What the Example Does The Java client (`HelloWorldClient.java`) performs the following actions: diff --git a/examples/pom.xml b/examples/pom.xml index 2b92ff339..c9cd09015 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -10,32 +10,33 @@ 0.2.4-SNAPSHOT - a2a-java-sdk-examples + a2a-java-sdk-examples-parent + pom Java SDK A2A Examples Examples for the Java SDK for the Agent2Agent Protocol (A2A) - - - io.a2a.sdk - a2a-java-sdk-server-quarkus - ${project.version} - - - io.quarkus - quarkus-resteasy-jackson - provided - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - - - jakarta.ws.rs - jakarta.ws.rs-api - - + + + + io.quarkus + quarkus-bom + ${quarkus.platform.version} + pom + import + + + io.a2a.sdk + a2a-java-sdk-core + ${project.version} + + + io.a2a.sdk + a2a-java-sdk-server-quarkus + ${project.version} + + + @@ -55,4 +56,9 @@ + + + client + server + \ No newline at end of file diff --git a/examples/server/pom.xml b/examples/server/pom.xml new file mode 100644 index 000000000..5e0b6c58e --- /dev/null +++ b/examples/server/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + + io.a2a.sdk + a2a-java-sdk-examples-parent + 0.2.4-SNAPSHOT + + + a2a-java-sdk-examples-server + + Java SDK A2A Examples + Examples for the Java SDK for the Agent2Agent Protocol (A2A) + + + + io.a2a.sdk + a2a-java-sdk-server-quarkus + + + io.quarkus + quarkus-resteasy-jackson + provided + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + jakarta.ws.rs + jakarta.ws.rs-api + + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + + \ No newline at end of file diff --git a/examples/src/main/java/io/a2a/examples/helloworld/server/AgentCardProducer.java b/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java similarity index 97% rename from examples/src/main/java/io/a2a/examples/helloworld/server/AgentCardProducer.java rename to examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index c63bb77f4..03bb4ec98 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/server/AgentCardProducer.java +++ b/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -1,4 +1,4 @@ -package io.a2a.examples.helloworld.server; +package io.a2a.examples.helloworld; import java.util.Collections; import java.util.List; diff --git a/examples/src/main/java/io/a2a/examples/helloworld/server/AgentExecutorProducer.java b/examples/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java similarity index 95% rename from examples/src/main/java/io/a2a/examples/helloworld/server/AgentExecutorProducer.java rename to examples/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java index 7356f01d4..e5392ca45 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/server/AgentExecutorProducer.java +++ b/examples/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java @@ -1,4 +1,4 @@ -package io.a2a.examples.helloworld.server; +package io.a2a.examples.helloworld; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; diff --git a/examples/src/main/java/io/a2a/examples/helloworld/server/README.md b/examples/server/src/main/java/io/a2a/examples/helloworld/README.md similarity index 99% rename from examples/src/main/java/io/a2a/examples/helloworld/server/README.md rename to examples/server/src/main/java/io/a2a/examples/helloworld/README.md index 1289cc8fa..821471901 100644 --- a/examples/src/main/java/io/a2a/examples/helloworld/server/README.md +++ b/examples/server/src/main/java/io/a2a/examples/helloworld/README.md @@ -14,7 +14,7 @@ This example demonstrates how to use the A2A Java SDK to communicate with an A2A The Java server can be started using `mvn` as follows: ```bash -cd examples +cd examples/server mvn quarkus:dev ``` diff --git a/examples/src/main/resources/application.properties b/examples/server/src/main/resources/application.properties similarity index 100% rename from examples/src/main/resources/application.properties rename to examples/server/src/main/resources/application.properties diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 92845fcd0..0e2f59d6e 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -38,6 +38,11 @@ rest-assured test + + io.quarkus + quarkus-arc + test + From 8508b8d56f6e16d0e19426fa0f28c0eb0f7b01da Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 25 Jun 2025 12:59:13 -0400 Subject: [PATCH 004/493] fix: Split out server related code from a2a-java-sdk-core into a new a2a-java-sdk-server-common module (#129) This PR splits out server related code from the `a2a-java-sdk-core` module into a new `a2a-java-sdk-server-common` module that is used by the quarkus and jakarta server modules. This reduces the dependencies required by the `a2a-java-sdk-core` module. --- README.md | 2 +- core/pom.xml | 18 ----- .../java/io/a2a/client/A2ACardResolver.java | 4 -- .../io/a2a/client/sse/SSEEventListener.java | 7 +- .../java/io/a2a/http/JdkA2AHttpClient.java | 3 - .../main/java/io/a2a/server/events/Event.java | 4 -- core/src/main/java/io/a2a/spec/A2AError.java | 2 - core/src/main/java/io/a2a/spec/Event.java | 4 ++ .../main/java/io/a2a/spec/JSONRPCError.java | 1 - .../java/io/a2a/spec/StreamingEventKind.java | 1 - .../io/a2a/client/A2ACardResolverTest.java | 5 -- .../helloworld/AgentCardProducer.java | 2 +- pom.xml | 9 ++- sdk-jakarta/pom.xml | 10 ++- .../apps/jakarta/A2AServerResource.java | 4 +- sdk-quarkus/pom.xml | 9 +++ .../server/apps/quarkus/A2AServerRoutes.java | 5 +- sdk-server-common/pom.xml | 71 +++++++++++++++++++ .../io/a2a/server}/ExtendedAgentCard.java | 2 +- .../java/io/a2a/server/JSONRPCException.java | 0 .../java/io/a2a/server}/PublicAgentCard.java | 2 +- .../java/io/a2a/server/ServerCallContext.java | 0 .../server/agentexecution/AgentExecutor.java | 0 .../server/agentexecution/RequestContext.java | 0 .../SimpleRequestContextBuilder.java | 0 .../a2a/server/auth/UnauthenticatedUser.java | 0 .../main/java/io/a2a/server/auth/User.java | 0 .../a2a/server/events/EnhancedRunnable.java | 0 .../io/a2a/server/events/EventConsumer.java | 3 +- .../java/io/a2a/server/events/EventQueue.java | 3 +- .../events/EventQueueClosedException.java | 0 .../server/events/InMemoryQueueManager.java | 2 +- .../server/events/NoTaskQueueException.java | 0 .../io/a2a/server/events/QueueManager.java | 0 .../events/TaskQueueExistsException.java | 0 .../DefaultRequestHandler.java | 12 ++-- .../requesthandlers/JSONRPCHandler.java | 4 +- .../requesthandlers/RequestHandler.java | 0 .../server/tasks/InMemoryPushNotifier.java | 5 ++ .../a2a/server/tasks/InMemoryTaskStore.java | 0 .../io/a2a/server/tasks/PushNotifier.java | 0 .../io/a2a/server/tasks/ResultAggregator.java | 10 +-- .../java/io/a2a/server/tasks/TaskManager.java | 2 +- .../java/io/a2a/server/tasks/TaskStore.java | 0 .../java/io/a2a/server/tasks/TaskUpdater.java | 0 .../a2a/server}/util/TempLoggerWrapper.java | 2 +- .../util/async/AsyncExecutorProducer.java | 2 +- .../io/a2a/server}/util/async/AsyncUtils.java | 2 +- .../io/a2a/server}/util/async/Internal.java | 2 +- .../src/main/resources/META-INF/beans.xml | 0 .../a2a/server/events/EventConsumerTest.java | 1 + .../io/a2a/server/events/EventQueueTest.java | 1 + .../requesthandlers/JSONRPCHandlerTest.java | 2 +- .../server/tasks/InMemoryTaskStoreTest.java | 0 .../io/a2a/server/tasks/TaskManagerTest.java | 0 .../io/a2a/server/tasks/TaskUpdaterTest.java | 2 +- .../server/util/async}/AsyncUtilsTest.java | 10 +-- .../io/a2a/tck/server/AgentCardProducer.java | 2 +- tests/server-common/pom.xml | 5 ++ .../apps/common/AbstractA2AServerTest.java | 2 +- .../server/apps/common/AgentCardProducer.java | 2 +- 61 files changed, 156 insertions(+), 85 deletions(-) delete mode 100644 core/src/main/java/io/a2a/server/events/Event.java create mode 100644 core/src/main/java/io/a2a/spec/Event.java create mode 100644 sdk-server-common/pom.xml rename {core/src/main/java/io/a2a/spec => sdk-server-common/src/main/java/io/a2a/server}/ExtendedAgentCard.java (95%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/JSONRPCException.java (100%) rename {core/src/main/java/io/a2a/spec => sdk-server-common/src/main/java/io/a2a/server}/PublicAgentCard.java (95%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/ServerCallContext.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/agentexecution/RequestContext.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/auth/User.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/EnhancedRunnable.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/EventConsumer.java (98%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/EventQueue.java (98%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/EventQueueClosedException.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/InMemoryQueueManager.java (97%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/NoTaskQueueException.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/QueueManager.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/events/TaskQueueExistsException.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java (97%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java (98%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java (94%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/PushNotifier.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/ResultAggregator.java (95%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/TaskManager.java (99%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/TaskStore.java (100%) rename {core => sdk-server-common}/src/main/java/io/a2a/server/tasks/TaskUpdater.java (100%) rename {core/src/main/java/io/a2a => sdk-server-common/src/main/java/io/a2a/server}/util/TempLoggerWrapper.java (99%) rename {core/src/main/java/io/a2a => sdk-server-common/src/main/java/io/a2a/server}/util/async/AsyncExecutorProducer.java (94%) rename {core/src/main/java/io/a2a => sdk-server-common/src/main/java/io/a2a/server}/util/async/AsyncUtils.java (99%) rename {core/src/main/java/io/a2a => sdk-server-common/src/main/java/io/a2a/server}/util/async/Internal.java (87%) create mode 100644 sdk-server-common/src/main/resources/META-INF/beans.xml rename {core => sdk-server-common}/src/test/java/io/a2a/server/events/EventConsumerTest.java (99%) rename {core => sdk-server-common}/src/test/java/io/a2a/server/events/EventQueueTest.java (99%) rename {core => sdk-server-common}/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java (99%) rename {core => sdk-server-common}/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java (100%) rename {core => sdk-server-common}/src/test/java/io/a2a/server/tasks/TaskManagerTest.java (100%) rename {core => sdk-server-common}/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java (99%) rename {core/src/test/java/io/a2a/util => sdk-server-common/src/test/java/io/a2a/server/util/async}/AsyncUtilsTest.java (98%) diff --git a/README.md b/README.md index 849f102df..b76694f2c 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,10 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) ### 2. Add a class that creates an A2A Agent Card ```java +import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentSkill; -import io.a2a.spec.PublicAgentCard; ... @ApplicationScoped diff --git a/core/pom.xml b/core/pom.xml index 2bca1b769..8aed247f4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -25,24 +25,6 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - - io.smallrye.reactive - mutiny-zero - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - - - org.slf4j - slf4j-api - - - io.quarkus - quarkus-arc - test - org.junit.jupiter junit-jupiter-api diff --git a/core/src/main/java/io/a2a/client/A2ACardResolver.java b/core/src/main/java/io/a2a/client/A2ACardResolver.java index 0c3af2162..1266f7219 100644 --- a/core/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/core/src/main/java/io/a2a/client/A2ACardResolver.java @@ -4,15 +4,11 @@ import java.io.IOException; import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.http.A2AHttpClient; import io.a2a.http.A2AHttpResponse; -import io.a2a.spec.A2A; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/core/src/main/java/io/a2a/client/sse/SSEEventListener.java b/core/src/main/java/io/a2a/client/sse/SSEEventListener.java index 476f4a14b..8ed0e9aa3 100644 --- a/core/src/main/java/io/a2a/client/sse/SSEEventListener.java +++ b/core/src/main/java/io/a2a/client/sse/SSEEventListener.java @@ -4,17 +4,16 @@ import java.util.concurrent.Future; import java.util.function.Consumer; +import java.util.logging.Logger; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import io.a2a.spec.JSONRPCError; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskStatusUpdateEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class SSEEventListener { - private static final Logger log = LoggerFactory.getLogger(SSEEventListener.class); + private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); private final Consumer eventHandler; private final Consumer errorHandler; private final Runnable failureHandler; @@ -29,7 +28,7 @@ public void onMessage(String message, Future completableFuture) { try { handleMessage(OBJECT_MAPPER.readTree(message),completableFuture); } catch (JsonProcessingException e) { - log.warn("Failed to parse JSON message: {}", message, e); + log.warning("Failed to parse JSON message: " + message); } } diff --git a/core/src/main/java/io/a2a/http/JdkA2AHttpClient.java b/core/src/main/java/io/a2a/http/JdkA2AHttpClient.java index f15494b66..e3b5c0c66 100644 --- a/core/src/main/java/io/a2a/http/JdkA2AHttpClient.java +++ b/core/src/main/java/io/a2a/http/JdkA2AHttpClient.java @@ -14,9 +14,6 @@ import java.util.concurrent.Flow; import java.util.function.Consumer; -import jakarta.enterprise.context.ApplicationScoped; - -@ApplicationScoped public class JdkA2AHttpClient implements A2AHttpClient { private final HttpClient httpClient; diff --git a/core/src/main/java/io/a2a/server/events/Event.java b/core/src/main/java/io/a2a/server/events/Event.java deleted file mode 100644 index 5087e2274..000000000 --- a/core/src/main/java/io/a2a/server/events/Event.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.a2a.server.events; - -public interface Event { -} diff --git a/core/src/main/java/io/a2a/spec/A2AError.java b/core/src/main/java/io/a2a/spec/A2AError.java index d4e1a2c29..4c9951df2 100644 --- a/core/src/main/java/io/a2a/spec/A2AError.java +++ b/core/src/main/java/io/a2a/spec/A2AError.java @@ -1,6 +1,4 @@ package io.a2a.spec; -import io.a2a.server.events.Event; - public interface A2AError extends Event { } diff --git a/core/src/main/java/io/a2a/spec/Event.java b/core/src/main/java/io/a2a/spec/Event.java new file mode 100644 index 000000000..4d0daa531 --- /dev/null +++ b/core/src/main/java/io/a2a/spec/Event.java @@ -0,0 +1,4 @@ +package io.a2a.spec; + +public interface Event { +} diff --git a/core/src/main/java/io/a2a/spec/JSONRPCError.java b/core/src/main/java/io/a2a/spec/JSONRPCError.java index 79020d7c3..b682203d4 100644 --- a/core/src/main/java/io/a2a/spec/JSONRPCError.java +++ b/core/src/main/java/io/a2a/spec/JSONRPCError.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.a2a.server.events.Event; import io.a2a.util.Assert; /** diff --git a/core/src/main/java/io/a2a/spec/StreamingEventKind.java b/core/src/main/java/io/a2a/spec/StreamingEventKind.java index 8146ae295..a7a6b6232 100644 --- a/core/src/main/java/io/a2a/spec/StreamingEventKind.java +++ b/core/src/main/java/io/a2a/spec/StreamingEventKind.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import io.a2a.server.events.Event; @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, diff --git a/core/src/test/java/io/a2a/client/A2ACardResolverTest.java b/core/src/test/java/io/a2a/client/A2ACardResolverTest.java index 1582699fe..8265b9514 100644 --- a/core/src/test/java/io/a2a/client/A2ACardResolverTest.java +++ b/core/src/test/java/io/a2a/client/A2ACardResolverTest.java @@ -11,14 +11,11 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import jakarta.enterprise.context.Dependent; - import io.a2a.http.A2AHttpClient; import io.a2a.http.A2AHttpResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; -import io.quarkus.arc.profile.IfBuildProfile; import org.junit.jupiter.api.Test; public class A2ACardResolverTest { @@ -108,8 +105,6 @@ public void testGetAgentCardRequestError() throws Exception { assertTrue(msg.contains("503")); } - @Dependent - @IfBuildProfile("test") private static class TestHttpClient implements A2AHttpClient { int status = 200; String body; diff --git a/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 03bb4ec98..f07a6e527 100644 --- a/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -6,10 +6,10 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentSkill; -import io.a2a.spec.PublicAgentCard; @ApplicationScoped public class AgentCardProducer { diff --git a/pom.xml b/pom.xml index bafa3f3cc..80a6863b5 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ 3.1.2 2.17.0 4.1.0 + 2.0.1 2.1.3 3.1.0 5.12.2 @@ -68,7 +69,11 @@ jakarta.enterprise jakarta.enterprise.cdi-api ${jakarta.enterprise.cdi-api.version} - provided + + + jakarta.inject + jakarta.inject-api + ${jakarta.inject.jakarta.inject-api.version} jakarta.json @@ -172,7 +177,9 @@ + core + sdk-server-common sdk-jakarta sdk-quarkus tck diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index d6f0f8ed4..d7f85e83f 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -22,6 +22,11 @@ a2a-java-sdk-core ${project.version} + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + ${project.groupId} a2a-java-sdk-tests-server-common @@ -43,7 +48,10 @@ jakarta.enterprise jakarta.enterprise.cdi-api - provided + + + jakarta.inject + jakarta.inject-api jakarta.json diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java index 1c90c6339..341fe6367 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java @@ -22,10 +22,10 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; +import io.a2a.server.ExtendedAgentCard; import io.a2a.server.requesthandlers.JSONRPCHandler; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.ExtendedAgentCard; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskRequest; import io.a2a.spec.IdJsonMappingException; @@ -47,7 +47,7 @@ import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.util.async.Internal; +import io.a2a.server.util.async.Internal; @Path("/") public class A2AServerResource { diff --git a/sdk-quarkus/pom.xml b/sdk-quarkus/pom.xml index da079470c..bacccc073 100644 --- a/sdk-quarkus/pom.xml +++ b/sdk-quarkus/pom.xml @@ -22,6 +22,11 @@ a2a-java-sdk-core ${project.version} + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + ${project.groupId} a2a-java-sdk-tests-server-common @@ -43,6 +48,10 @@ jakarta.enterprise jakarta.enterprise.cdi-api + + jakarta.inject + jakarta.inject-api + org.slf4j slf4j-api diff --git a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index f06debd50..4246e965e 100644 --- a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -4,7 +4,6 @@ import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; @@ -17,11 +16,11 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; +import io.a2a.server.ExtendedAgentCard; import io.a2a.server.requesthandlers.JSONRPCHandler; import io.a2a.spec.A2A; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.ExtendedAgentCard; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskRequest; import io.a2a.spec.IdJsonMappingException; @@ -45,7 +44,7 @@ import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; -import io.a2a.util.async.Internal; +import io.a2a.server.util.async.Internal; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml new file mode 100644 index 000000000..44e2e298a --- /dev/null +++ b/sdk-server-common/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + + io.a2a.sdk + a2a-java-sdk-parent + 0.2.4-SNAPSHOT + + a2a-java-sdk-server-common + + jar + + Java SDK A2A Core + Java SDK for the Agent2Agent Protocol (A2A) - Server Common + + + + ${project.groupId} + a2a-java-sdk-core + ${project.version} + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + io.smallrye.reactive + mutiny-zero + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + org.slf4j + slf4j-api + + + io.quarkus + quarkus-arc + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + org.mock-server + mockserver-netty + test + + + + \ No newline at end of file diff --git a/core/src/main/java/io/a2a/spec/ExtendedAgentCard.java b/sdk-server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java similarity index 95% rename from core/src/main/java/io/a2a/spec/ExtendedAgentCard.java rename to sdk-server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java index 81f42622d..c9cc7eaf0 100644 --- a/core/src/main/java/io/a2a/spec/ExtendedAgentCard.java +++ b/sdk-server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a.server; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; diff --git a/core/src/main/java/io/a2a/server/JSONRPCException.java b/sdk-server-common/src/main/java/io/a2a/server/JSONRPCException.java similarity index 100% rename from core/src/main/java/io/a2a/server/JSONRPCException.java rename to sdk-server-common/src/main/java/io/a2a/server/JSONRPCException.java diff --git a/core/src/main/java/io/a2a/spec/PublicAgentCard.java b/sdk-server-common/src/main/java/io/a2a/server/PublicAgentCard.java similarity index 95% rename from core/src/main/java/io/a2a/spec/PublicAgentCard.java rename to sdk-server-common/src/main/java/io/a2a/server/PublicAgentCard.java index 8f165e85c..68c670bbe 100644 --- a/core/src/main/java/io/a2a/spec/PublicAgentCard.java +++ b/sdk-server-common/src/main/java/io/a2a/server/PublicAgentCard.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a.server; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; diff --git a/core/src/main/java/io/a2a/server/ServerCallContext.java b/sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java similarity index 100% rename from core/src/main/java/io/a2a/server/ServerCallContext.java rename to sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java diff --git a/core/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java similarity index 100% rename from core/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java rename to sdk-server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java diff --git a/core/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java similarity index 100% rename from core/src/main/java/io/a2a/server/agentexecution/RequestContext.java rename to sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java diff --git a/core/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java similarity index 100% rename from core/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java rename to sdk-server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java diff --git a/core/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java b/sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java similarity index 100% rename from core/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java rename to sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java diff --git a/core/src/main/java/io/a2a/server/auth/User.java b/sdk-server-common/src/main/java/io/a2a/server/auth/User.java similarity index 100% rename from core/src/main/java/io/a2a/server/auth/User.java rename to sdk-server-common/src/main/java/io/a2a/server/auth/User.java diff --git a/core/src/main/java/io/a2a/server/events/EnhancedRunnable.java b/sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java similarity index 100% rename from core/src/main/java/io/a2a/server/events/EnhancedRunnable.java rename to sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java diff --git a/core/src/main/java/io/a2a/server/events/EventConsumer.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java similarity index 98% rename from core/src/main/java/io/a2a/server/events/EventConsumer.java rename to sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java index 1a8d26423..ca38f43b7 100644 --- a/core/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -1,11 +1,10 @@ package io.a2a.server.events; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.Flow; import io.a2a.spec.A2AServerException; +import io.a2a.spec.Event; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskStatusUpdateEvent; diff --git a/core/src/main/java/io/a2a/server/events/EventQueue.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java similarity index 98% rename from core/src/main/java/io/a2a/server/events/EventQueue.java rename to sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java index e0f9ae5bf..585210c3e 100644 --- a/core/src/main/java/io/a2a/server/events/EventQueue.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -8,7 +8,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import io.a2a.util.TempLoggerWrapper; +import io.a2a.server.util.TempLoggerWrapper; +import io.a2a.spec.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/a2a/server/events/EventQueueClosedException.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java similarity index 100% rename from core/src/main/java/io/a2a/server/events/EventQueueClosedException.java rename to sdk-server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java diff --git a/core/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java similarity index 97% rename from core/src/main/java/io/a2a/server/events/InMemoryQueueManager.java rename to sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index ec6c3de90..88e9aab41 100644 --- a/core/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -7,7 +7,7 @@ import jakarta.enterprise.context.ApplicationScoped; -import io.a2a.util.TempLoggerWrapper; +import io.a2a.server.util.TempLoggerWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/a2a/server/events/NoTaskQueueException.java b/sdk-server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java similarity index 100% rename from core/src/main/java/io/a2a/server/events/NoTaskQueueException.java rename to sdk-server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java diff --git a/core/src/main/java/io/a2a/server/events/QueueManager.java b/sdk-server-common/src/main/java/io/a2a/server/events/QueueManager.java similarity index 100% rename from core/src/main/java/io/a2a/server/events/QueueManager.java rename to sdk-server-common/src/main/java/io/a2a/server/events/QueueManager.java diff --git a/core/src/main/java/io/a2a/server/events/TaskQueueExistsException.java b/sdk-server-common/src/main/java/io/a2a/server/events/TaskQueueExistsException.java similarity index 100% rename from core/src/main/java/io/a2a/server/events/TaskQueueExistsException.java rename to sdk-server-common/src/main/java/io/a2a/server/events/TaskQueueExistsException.java diff --git a/core/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java similarity index 97% rename from core/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java rename to sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 75ee84867..501e71315 100644 --- a/core/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -1,8 +1,8 @@ package io.a2a.server.requesthandlers; -import static io.a2a.util.async.AsyncUtils.convertingProcessor; -import static io.a2a.util.async.AsyncUtils.createTubeConfig; -import static io.a2a.util.async.AsyncUtils.processor; +import static io.a2a.server.util.async.AsyncUtils.convertingProcessor; +import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; +import static io.a2a.server.util.async.AsyncUtils.processor; import java.util.ArrayList; import java.util.Collections; @@ -23,7 +23,6 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.agentexecution.SimpleRequestContextBuilder; import io.a2a.server.events.EnhancedRunnable; -import io.a2a.server.events.Event; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; import io.a2a.server.events.QueueManager; @@ -32,6 +31,9 @@ import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; +import io.a2a.server.util.TempLoggerWrapper; +import io.a2a.server.util.async.Internal; +import io.a2a.spec.Event; import io.a2a.spec.EventKind; import io.a2a.spec.InternalError; import io.a2a.spec.JSONRPCError; @@ -45,8 +47,6 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.util.TempLoggerWrapper; -import io.a2a.util.async.Internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java similarity index 98% rename from core/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java rename to sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java index 273ff2504..131b07fc8 100644 --- a/core/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java @@ -1,12 +1,13 @@ package io.a2a.server.requesthandlers; -import static io.a2a.util.async.AsyncUtils.createTubeConfig; +import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import java.util.concurrent.Flow; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; @@ -18,7 +19,6 @@ import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONRPCError; -import io.a2a.spec.PublicAgentCard; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; import io.a2a.spec.SendStreamingMessageRequest; diff --git a/core/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java similarity index 100% rename from core/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java rename to sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java diff --git a/core/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java similarity index 94% rename from core/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java index f6fe37b97..6fb1fb39a 100644 --- a/core/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.a2a.http.A2AHttpClient; +import io.a2a.http.JdkA2AHttpClient; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.util.Utils; @@ -20,6 +21,10 @@ public class InMemoryPushNotifier implements PushNotifier { private final Map pushNotificationInfos = Collections.synchronizedMap(new HashMap<>()); @Inject + public InMemoryPushNotifier() { + this.httpClient = new JdkA2AHttpClient(); + } + public InMemoryPushNotifier(A2AHttpClient httpClient) { this.httpClient = httpClient; } diff --git a/core/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java similarity index 100% rename from core/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java diff --git a/core/src/main/java/io/a2a/server/tasks/PushNotifier.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotifier.java similarity index 100% rename from core/src/main/java/io/a2a/server/tasks/PushNotifier.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotifier.java diff --git a/core/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java similarity index 95% rename from core/src/main/java/io/a2a/server/tasks/ResultAggregator.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index 3a3e49c03..b9aef07ff 100644 --- a/core/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -1,23 +1,23 @@ package io.a2a.server.tasks; -import static io.a2a.util.async.AsyncUtils.consumer; -import static io.a2a.util.async.AsyncUtils.createTubeConfig; -import static io.a2a.util.async.AsyncUtils.processor; +import static io.a2a.server.util.async.AsyncUtils.consumer; +import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; +import static io.a2a.server.util.async.AsyncUtils.processor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import io.a2a.server.events.Event; import io.a2a.server.events.EventConsumer; +import io.a2a.server.util.TempLoggerWrapper; import io.a2a.spec.A2AServerException; +import io.a2a.spec.Event; import io.a2a.spec.EventKind; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.util.TempLoggerWrapper; import io.a2a.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/src/main/java/io/a2a/server/tasks/TaskManager.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java similarity index 99% rename from core/src/main/java/io/a2a/server/tasks/TaskManager.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index be6386bfd..a0b5a31bd 100644 --- a/core/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; -import io.a2a.server.events.Event; +import io.a2a.spec.Event; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; import io.a2a.spec.InvalidParamsError; diff --git a/core/src/main/java/io/a2a/server/tasks/TaskStore.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskStore.java similarity index 100% rename from core/src/main/java/io/a2a/server/tasks/TaskStore.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/TaskStore.java diff --git a/core/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java similarity index 100% rename from core/src/main/java/io/a2a/server/tasks/TaskUpdater.java rename to sdk-server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java diff --git a/core/src/main/java/io/a2a/util/TempLoggerWrapper.java b/sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java similarity index 99% rename from core/src/main/java/io/a2a/util/TempLoggerWrapper.java rename to sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java index 8a2ec2c69..cecc6797d 100644 --- a/core/src/main/java/io/a2a/util/TempLoggerWrapper.java +++ b/sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java @@ -1,4 +1,4 @@ -package io.a2a.util; +package io.a2a.server.util; import org.slf4j.Logger; import org.slf4j.Marker; diff --git a/core/src/main/java/io/a2a/util/async/AsyncExecutorProducer.java b/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java similarity index 94% rename from core/src/main/java/io/a2a/util/async/AsyncExecutorProducer.java rename to sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index 3066da753..1b3066ca6 100644 --- a/core/src/main/java/io/a2a/util/async/AsyncExecutorProducer.java +++ b/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -1,4 +1,4 @@ -package io.a2a.util.async; +package io.a2a.server.util.async; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; diff --git a/core/src/main/java/io/a2a/util/async/AsyncUtils.java b/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java similarity index 99% rename from core/src/main/java/io/a2a/util/async/AsyncUtils.java rename to sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java index efca62ab2..25444cde9 100644 --- a/core/src/main/java/io/a2a/util/async/AsyncUtils.java +++ b/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java @@ -1,4 +1,4 @@ -package io.a2a.util.async; +package io.a2a.server.util.async; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/core/src/main/java/io/a2a/util/async/Internal.java b/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java similarity index 87% rename from core/src/main/java/io/a2a/util/async/Internal.java rename to sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java index 787e150a8..6f282b137 100644 --- a/core/src/main/java/io/a2a/util/async/Internal.java +++ b/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java @@ -1,4 +1,4 @@ -package io.a2a.util.async; +package io.a2a.server.util.async; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/sdk-server-common/src/main/resources/META-INF/beans.xml b/sdk-server-common/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/java/io/a2a/server/events/EventConsumerTest.java b/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java similarity index 99% rename from core/src/test/java/io/a2a/server/events/EventConsumerTest.java rename to sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 6b6757b88..b7f746ad7 100644 --- a/core/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -15,6 +15,7 @@ import io.a2a.spec.A2AError; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; +import io.a2a.spec.Event; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; diff --git a/core/src/test/java/io/a2a/server/events/EventQueueTest.java b/sdk-server-common/src/test/java/io/a2a/server/events/EventQueueTest.java similarity index 99% rename from core/src/test/java/io/a2a/server/events/EventQueueTest.java rename to sdk-server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 98cebf5fc..9510f8d3f 100644 --- a/core/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -6,6 +6,7 @@ import java.util.List; import io.a2a.spec.Artifact; +import io.a2a.spec.Event; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; diff --git a/core/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java similarity index 99% rename from core/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java rename to sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index 48583279e..01498df89 100644 --- a/core/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -27,7 +27,6 @@ import io.a2a.http.A2AHttpResponse; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.events.Event; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; import io.a2a.server.events.InMemoryQueueManager; @@ -42,6 +41,7 @@ import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.Event; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; diff --git a/core/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/sdk-server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java similarity index 100% rename from core/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java rename to sdk-server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java diff --git a/core/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/sdk-server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java similarity index 100% rename from core/src/test/java/io/a2a/server/tasks/TaskManagerTest.java rename to sdk-server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java diff --git a/core/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/sdk-server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java similarity index 99% rename from core/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java rename to sdk-server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 66c0b8822..291767b63 100644 --- a/core/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -11,8 +11,8 @@ import java.util.Map; import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.events.Event; import io.a2a.server.events.EventQueue; +import io.a2a.spec.Event; import io.a2a.spec.Message; import io.a2a.spec.Part; import io.a2a.spec.TaskArtifactUpdateEvent; diff --git a/core/src/test/java/io/a2a/util/AsyncUtilsTest.java b/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java similarity index 98% rename from core/src/test/java/io/a2a/util/AsyncUtilsTest.java rename to sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java index b6764cc77..2bbd0a9fc 100644 --- a/core/src/test/java/io/a2a/util/AsyncUtilsTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java @@ -1,9 +1,9 @@ -package io.a2a.util; +package io.a2a.server.util.async; -import static io.a2a.util.async.AsyncUtils.consumer; -import static io.a2a.util.async.AsyncUtils.convertingProcessor; -import static io.a2a.util.async.AsyncUtils.createTubeConfig; -import static io.a2a.util.async.AsyncUtils.processor; +import static io.a2a.server.util.async.AsyncUtils.consumer; +import static io.a2a.server.util.async.AsyncUtils.convertingProcessor; +import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; +import static io.a2a.server.util.async.AsyncUtils.processor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index b122ad290..7abf29a12 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -6,10 +6,10 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentSkill; -import io.a2a.spec.PublicAgentCard; @ApplicationScoped public class AgentCardProducer { diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 0e2f59d6e..866b379c0 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -23,6 +23,11 @@ a2a-java-sdk-core ${project.version} + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + jakarta.ws.rs jakarta.ws.rs-api diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index cd3a8ba1c..ac7bf5c5d 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -25,13 +25,13 @@ import jakarta.ws.rs.core.MediaType; import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.server.events.Event; import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.tasks.TaskStore; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.Event; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index 2f1cb6415..f68f44967 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -6,9 +6,9 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; -import io.a2a.spec.PublicAgentCard; import io.quarkus.arc.profile.IfBuildProfile; @ApplicationScoped From aede3ff7a1917b3e7fa492384d78f1cfb2480c6f Mon Sep 17 00:00:00 2001 From: Sun Yuhan <20242118+sunyuhan1998@users.noreply.github.com> Date: Thu, 26 Jun 2025 17:05:47 +0800 Subject: [PATCH 005/493] cleanup: Removed unused imports (#128) I found some unused imports in the project. This PR removes them to keep the code clean. Signed-off-by: Sun Yuhan Co-authored-by: Sun Yuhan --- .../java/io/a2a/server/apps/jakarta/A2AServerResource.java | 1 - .../io/a2a/server/apps/jakarta/JakartaA2AServerTest.java | 2 -- .../main/java/io/a2a/server/events/InMemoryQueueManager.java | 5 ----- .../src/main/java/io/a2a/server/util/async/Internal.java | 1 - .../test/java/io/a2a/server/events/EventConsumerTest.java | 1 - 5 files changed, 10 deletions(-) diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java index 341fe6367..ba587285d 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java @@ -1,7 +1,6 @@ package io.a2a.server.apps.jakarta; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.Flow; import jakarta.enterprise.inject.Instance; diff --git a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java index d366b20cc..06ad61c9a 100644 --- a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java +++ b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java @@ -6,8 +6,6 @@ import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.tasks.TaskStore; import io.quarkus.test.junit.QuarkusTest; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; @QuarkusTest public class JakartaA2AServerTest extends AbstractA2AServerTest { diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index 88e9aab41..72bb81eb3 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -3,14 +3,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; import jakarta.enterprise.context.ApplicationScoped; -import io.a2a.server.util.TempLoggerWrapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - @ApplicationScoped public class InMemoryQueueManager implements QueueManager { private final Map queues = Collections.synchronizedMap(new HashMap<>()); diff --git a/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java b/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java index 6f282b137..2b8cd100e 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java +++ b/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java @@ -2,7 +2,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import jakarta.inject.Qualifier; diff --git a/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index b7f746ad7..7d8d6cbad 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -1,7 +1,6 @@ package io.a2a.server.events; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; From 125e1119c133d568d37e4e2290b0821ff2ac8868 Mon Sep 17 00:00:00 2001 From: Sun Yuhan <20242118+sunyuhan1998@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:52:58 +0800 Subject: [PATCH 006/493] test: Added unit tests for `RequestContext` (#127) This PR includes the following changes: 1. Added a unit test class for the `RequestContext` class, and all unit tests have been verified to pass. This is based on the unit test class from the `a2a` Python sdk: `tests/server/agent_execution/test_context.py`, with a few tests that are not applicable to the Java version removed. 2. Based on the unit test class, an issue in the `RequestContext` class was identified and fixed: the `relatedTasks` setting in the constructor was not working properly. Fixes #60 --------- Signed-off-by: Sun Yuhan Co-authored-by: Sun Yuhan --- .../server/agentexecution/RequestContext.java | 4 +- .../agentexecution/RequestContextTest.java | 248 ++++++++++++++++++ 2 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index 2d68ae784..e62a87d3c 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -27,9 +27,7 @@ public RequestContext(MessageSendParams params, String taskId, String contextId, this.taskId = taskId; this.contextId = contextId; this.task = task; - if (relatedTasks == null) { - this.relatedTasks = new ArrayList<>(); - } + this.relatedTasks = relatedTasks == null ? new ArrayList<>() : relatedTasks; // if the taskId and contextId were specified, they must match the params if (params != null) { diff --git a/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java new file mode 100644 index 000000000..8e3f04af6 --- /dev/null +++ b/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -0,0 +1,248 @@ +package io.a2a.server.agentexecution; + +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.Task; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskState; +import io.a2a.spec.TextPart; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; + +public class RequestContextTest { + + @Test + public void testInitWithoutParams() { + RequestContext context = new RequestContext(null, null, null, null, null); + assertNull(context.getMessage()); + assertNull(context.getTaskId()); + assertNull(context.getContextId()); + assertNull(context.getTask()); + assertTrue(context.getRelatedTasks().isEmpty()); + } + + @Test + public void testInitWithParamsNoIds() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + UUID taskId = UUID.fromString("00000000-0000-0000-0000-000000000001"); + UUID contextId = UUID.fromString("00000000-0000-0000-0000-000000000002"); + + try (MockedStatic mockedUUID = mockStatic(UUID.class)) { + mockedUUID.when(UUID::randomUUID) + .thenReturn(taskId) + .thenReturn(contextId); + + RequestContext context = new RequestContext(mockParams, null, null, null, null); + + assertEquals(mockParams.message(), context.getMessage()); + assertEquals(taskId.toString(), context.getTaskId()); + assertEquals(mockParams.message().getTaskId(), taskId.toString()); + assertEquals(contextId.toString(), context.getContextId()); + assertEquals(mockParams.message().getContextId(), contextId.toString()); + } + } + + @Test + public void testInitWithTaskId() { + String taskId = "task-123"; + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, taskId, null, null, null); + + assertEquals(taskId, context.getTaskId()); + assertEquals(taskId, mockParams.message().getTaskId()); + } + + @Test + public void testInitWithContextId() { + String contextId = "context-456"; + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + RequestContext context = new RequestContext(mockParams, null, contextId, null, null); + + assertEquals(contextId, context.getContextId()); + assertEquals(contextId, mockParams.message().getContextId()); + } + + @Test + public void testInitWithBothIds() { + String taskId = "task-123"; + String contextId = "context-456"; + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null); + + assertEquals(taskId, context.getTaskId()); + assertEquals(taskId, mockParams.message().getTaskId()); + assertEquals(contextId, context.getContextId()); + assertEquals(contextId, mockParams.message().getContextId()); + } + + @Test + public void testInitWithTask() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, mockTask, null); + + assertEquals(mockTask, context.getTask()); + } + + @Test + public void testGetUserInputNoParams() { + RequestContext context = new RequestContext(null, null, null, null, null); + assertEquals("", context.getUserInput(null)); + } + + @Test + public void testAttachRelatedTask() { + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + + RequestContext context = new RequestContext(null, null, null, null, null); + assertEquals(0, context.getRelatedTasks().size()); + + context.attachRelatedTask(mockTask); + assertEquals(1, context.getRelatedTasks().size()); + assertEquals(mockTask, context.getRelatedTasks().get(0)); + + Task anotherTask = mock(Task.class); + context.attachRelatedTask(anotherTask); + assertEquals(2, context.getRelatedTasks().size()); + assertEquals(anotherTask, context.getRelatedTasks().get(1)); + } + + @Test + public void testCheckOrGenerateTaskIdWithExistingTaskId() { + String existingId = "existing-task-id"; + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, null, null); + + assertEquals(existingId, context.getTaskId()); + assertEquals(existingId, mockParams.message().getTaskId()); + } + + @Test + public void testCheckOrGenerateContextIdWithExistingContextId() { + String existingId = "existing-context-id"; + + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, null, null); + + assertEquals(existingId, context.getContextId()); + assertEquals(existingId, mockParams.message().getContextId()); + } + + @Test + public void testInitRaisesErrorOnTaskIdMismatch() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + + InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> + new RequestContext(mockParams, "wrong-task-id", null, mockTask, null)); + + assertTrue(error.getMessage().contains("bad task id")); + } + + @Test + public void testInitRaisesErrorOnContextIdMismatch() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + + InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> + new RequestContext(mockParams, mockTask.getId(), "wrong-context-id", mockTask, null)); + + assertTrue(error.getMessage().contains("bad context id")); + } + + @Test + public void testWithRelatedTasksProvided() { + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + + List relatedTasks = new ArrayList<>(); + relatedTasks.add(mockTask); + relatedTasks.add(mock(Task.class)); + + RequestContext context = new RequestContext(null, null, null, null, relatedTasks); + + assertEquals(relatedTasks, context.getRelatedTasks()); + assertEquals(2, context.getRelatedTasks().size()); + } + + @Test + public void testMessagePropertyWithoutParams() { + RequestContext context = new RequestContext(null, null, null, null, null); + assertNull(context.getMessage()); + } + + @Test + public void testMessagePropertyWithParams() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, null, null); + assertEquals(mockParams.message(), context.getMessage()); + } + + @Test + public void testInitWithExistingIdsInMessage() { + String existingTaskId = "existing-task-id"; + String existingContextId = "existing-context-id"; + + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))) + .taskId(existingTaskId).contextId(existingContextId).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, null, null); + + assertEquals(existingTaskId, context.getTaskId()); + assertEquals(existingContextId, context.getContextId()); + } + + @Test + public void testInitWithTaskIdAndExistingTaskIdMatch() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + + + RequestContext context = new RequestContext(mockParams, mockTask.getId(), null, mockTask, null); + + assertEquals(mockTask.getId(), context.getTaskId()); + assertEquals(mockTask, context.getTask()); + } + + @Test + public void testInitWithContextIdAndExistingContextIdMatch() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + + + RequestContext context = new RequestContext(mockParams, mockTask.getId(), mockTask.getContextId(), mockTask, null); + + assertEquals(mockTask.getContextId(), context.getContextId()); + assertEquals(mockTask, context.getTask()); + } +} From 85385699ed994143605de6a06dfdeb94457d96ab Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Fri, 27 Jun 2025 05:53:51 +0800 Subject: [PATCH 007/493] test: update A2AClientTest with sending FilePart and DataPart (#130) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description for issue task https://github.com/a2aproject/a2a-java/issues/20 - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [ ] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../java/io/a2a/client/A2AClientTest.java | 186 +++++++++++++++++ .../test/java/io/a2a/client/JsonMessages.java | 188 ++++++++++++++++++ 2 files changed, 374 insertions(+) diff --git a/core/src/test/java/io/a2a/client/A2AClientTest.java b/core/src/test/java/io/a2a/client/A2AClientTest.java index 6168c7e49..e99734ff1 100644 --- a/core/src/test/java/io/a2a/client/A2AClientTest.java +++ b/core/src/test/java/io/a2a/client/A2AClientTest.java @@ -14,6 +14,12 @@ import static io.a2a.client.JsonMessages.SEND_MESSAGE_TEST_RESPONSE; import static io.a2a.client.JsonMessages.SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE; import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_ERROR_TEST_REQUEST; +import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST; +import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE; +import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST; +import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE; +import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST; +import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE; import static io.a2a.client.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -35,6 +41,7 @@ import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DataPart; import io.a2a.spec.FileContent; import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; @@ -508,4 +515,183 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertTrue(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); } + + @Test + public void testA2AClientSendMessageWithFilePart() throws Exception { + this.server.when( + request() + .withMethod("POST") + .withPath("/") + .withBody(JsonBody.json(SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST, MatchType.STRICT)) + + ) + .respond( + response() + .withStatusCode(200) + .withBody(SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE) + ); + + A2AClient client = new A2AClient("http://localhost:4001"); + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of( + new TextPart("analyze this image"), + new FilePart(new FileWithUri("image/jpeg", null, "file:///path/to/image.jpg")) + )) + .contextId("context-1234") + .messageId("message-1234-with-file") + .build(); + MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(List.of("text")) + .blocking(true) + .build(); + MessageSendParams params = new MessageSendParams.Builder() + .message(message) + .configuration(configuration) + .build(); + + SendMessageResponse response = client.sendMessage("request-1234-with-file", params); + + assertEquals("2.0", response.getJsonrpc()); + assertNotNull(response.getId()); + Object result = response.getResult(); + assertInstanceOf(Task.class, result); + Task task = (Task) result; + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); + assertNotNull(task.getContextId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + assertEquals(1, task.getArtifacts().size()); + Artifact artifact = task.getArtifacts().get(0); + assertEquals("artifact-1", artifact.artifactId()); + assertEquals("image-analysis", artifact.name()); + assertEquals(1, artifact.parts().size()); + Part part = artifact.parts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("This is an image of a cat sitting on a windowsill.", ((TextPart) part).getText()); + assertTrue(task.getMetadata().isEmpty()); + } + + @Test + public void testA2AClientSendMessageWithDataPart() throws Exception { + this.server.when( + request() + .withMethod("POST") + .withPath("/") + .withBody(JsonBody.json(SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST, MatchType.STRICT)) + + ) + .respond( + response() + .withStatusCode(200) + .withBody(SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE) + ); + + A2AClient client = new A2AClient("http://localhost:4001"); + + Map data = new HashMap<>(); + data.put("temperature", 25.5); + data.put("humidity", 60.2); + data.put("location", "San Francisco"); + data.put("timestamp", "2024-01-15T10:30:00Z"); + + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of( + new TextPart("process this data"), + new DataPart(data) + )) + .contextId("context-1234") + .messageId("message-1234-with-data") + .build(); + MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(List.of("text")) + .blocking(true) + .build(); + MessageSendParams params = new MessageSendParams.Builder() + .message(message) + .configuration(configuration) + .build(); + + SendMessageResponse response = client.sendMessage("request-1234-with-data", params); + + assertEquals("2.0", response.getJsonrpc()); + assertNotNull(response.getId()); + Object result = response.getResult(); + assertInstanceOf(Task.class, result); + Task task = (Task) result; + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); + assertNotNull(task.getContextId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + assertEquals(1, task.getArtifacts().size()); + Artifact artifact = task.getArtifacts().get(0); + assertEquals("artifact-1", artifact.artifactId()); + assertEquals("data-analysis", artifact.name()); + assertEquals(1, artifact.parts().size()); + Part part = artifact.parts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco.", ((TextPart) part).getText()); + assertTrue(task.getMetadata().isEmpty()); + } + + @Test + public void testA2AClientSendMessageWithMixedParts() throws Exception { + this.server.when( + request() + .withMethod("POST") + .withPath("/") + .withBody(JsonBody.json(SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST, MatchType.STRICT)) + + ) + .respond( + response() + .withStatusCode(200) + .withBody(SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE) + ); + + A2AClient client = new A2AClient("http://localhost:4001"); + + Map data = new HashMap<>(); + data.put("chartType", "bar"); + data.put("dataPoints", List.of(10, 20, 30, 40)); + data.put("labels", List.of("Q1", "Q2", "Q3", "Q4")); + + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of( + new TextPart("analyze this data and image"), + new FilePart(new FileWithBytes("image/png", "chart.png", "aGVsbG8=")), + new DataPart(data) + )) + .contextId("context-1234") + .messageId("message-1234-with-mixed") + .build(); + MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(List.of("text")) + .blocking(true) + .build(); + MessageSendParams params = new MessageSendParams.Builder() + .message(message) + .configuration(configuration) + .build(); + + SendMessageResponse response = client.sendMessage("request-1234-with-mixed", params); + + assertEquals("2.0", response.getJsonrpc()); + assertNotNull(response.getId()); + Object result = response.getResult(); + assertInstanceOf(Task.class, result); + Task task = (Task) result; + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); + assertNotNull(task.getContextId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + assertEquals(1, task.getArtifacts().size()); + Artifact artifact = task.getArtifacts().get(0); + assertEquals("artifact-1", artifact.artifactId()); + assertEquals("mixed-analysis", artifact.name()); + assertEquals(1, artifact.parts().size()); + Part part = artifact.parts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40].", ((TextPart) part).getText()); + assertTrue(task.getMetadata().isEmpty()); + } } \ No newline at end of file diff --git a/core/src/test/java/io/a2a/client/JsonMessages.java b/core/src/test/java/io/a2a/client/JsonMessages.java index 527a47352..c7ebd7780 100644 --- a/core/src/test/java/io/a2a/client/JsonMessages.java +++ b/core/src/test/java/io/a2a/client/JsonMessages.java @@ -425,4 +425,192 @@ public class JsonMessages { } """; + static final String SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "id": "request-1234-with-file", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "analyze this image" + }, + { + "kind": "file", + "file": { + "uri": "file:///path/to/image.jpg", + "mimeType": "image/jpeg" + } + } + ], + "messageId": "message-1234-with-file", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "image-analysis", + "parts": [ + { + "kind": "text", + "text": "This is an image of a cat sitting on a windowsill." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "id": "request-1234-with-data", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "process this data" + }, + { + "kind": "data", + "data": { + "temperature": 25.5, + "humidity": 60.2, + "location": "San Francisco", + "timestamp": "2024-01-15T10:30:00Z" + } + } + ], + "messageId": "message-1234-with-data", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "data-analysis", + "parts": [ + { + "kind": "text", + "text": "Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "id": "request-1234-with-mixed", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "analyze this data and image" + }, + { + "kind": "file", + "file": { + "bytes": "aGVsbG8=", + "name": "chart.png", + "mimeType": "image/png" + } + }, + { + "kind": "data", + "data": { + "chartType": "bar", + "dataPoints": [10, 20, 30, 40], + "labels": ["Q1", "Q2", "Q3", "Q4"] + } + } + ], + "messageId": "message-1234-with-mixed", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "mixed-analysis", + "parts": [ + { + "kind": "text", + "text": "Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + } From 8e1b7ebda6883c6ed076246b45863f3d0c6bb759 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 30 Jun 2025 23:54:20 +0200 Subject: [PATCH 008/493] test(ci): Basic CI to build the project and run the tests (#143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 ----- This is an initial pass essentially running `mvn package`. I think we can and should also run the TCK, but that will come later. I'm just back after a week off, so I need to understand better where we are first --- .github/workflows/build-and-test.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/build-and-test.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 000000000..6e6433b05 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,25 @@ +name: Build and Test + +on: + # Handle all branches for now + push: + pull_request: + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package --file pom.xml -Dmaven.test.failure.ignore=true -fae From 558e695459a65124277a952629ca247fabe2ee25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Ant=C3=B4nio=20Siqueira?= Date: Mon, 30 Jun 2025 19:10:18 -0300 Subject: [PATCH 009/493] fix: README has a broken link to examples (#134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Fixing broken link and moving example to a proper directory Fixes #133 🦕 --- README.md | 4 ++-- examples/{ => helloworld}/client/pom.xml | 0 .../java/io/a2a/examples/helloworld/HelloWorldClient.java | 0 .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 0 .../src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md | 0 .../client/src/main/java/io/a2a/examples/helloworld/README.md | 0 examples/{ => helloworld}/pom.xml | 0 examples/{ => helloworld}/server/pom.xml | 0 .../java/io/a2a/examples/helloworld/AgentCardProducer.java | 0 .../io/a2a/examples/helloworld/AgentExecutorProducer.java | 0 .../server/src/main/java/io/a2a/examples/helloworld/README.md | 0 .../server/src/main/resources/application.properties | 0 12 files changed, 2 insertions(+), 2 deletions(-) rename examples/{ => helloworld}/client/pom.xml (100%) rename examples/{ => helloworld}/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java (100%) rename examples/{ => helloworld}/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java (100%) rename examples/{ => helloworld}/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md (100%) rename examples/{ => helloworld}/client/src/main/java/io/a2a/examples/helloworld/README.md (100%) rename examples/{ => helloworld}/pom.xml (100%) rename examples/{ => helloworld}/server/pom.xml (100%) rename examples/{ => helloworld}/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java (100%) rename examples/{ => helloworld}/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java (100%) rename examples/{ => helloworld}/server/src/main/java/io/a2a/examples/helloworld/README.md (100%) rename examples/{ => helloworld}/server/src/main/resources/application.properties (100%) diff --git a/README.md b/README.md index b76694f2c..b0d19214d 100644 --- a/README.md +++ b/README.md @@ -338,13 +338,13 @@ AgentCard agentCard = A2A.getAgentCard("http://localhost:1234"); ### Hello World Example -A complete example of an A2A client communicating with a Python A2A server is available in the [examples/helloworld](src/main/java/io/a2a/examples/helloworld) directory. This example demonstrates: +A complete example of an A2A client communicating with a Python A2A server is available in the [examples/helloworld](examples/helloworld) directory. This example demonstrates: - Setting up and using the A2A Java client - Sending regular and streaming messages - Receiving and processing responses -The example includes detailed instructions on how to run both the Python server and the Java client using JBang. Check out the [example's README](examples/client/src/main/java/io/a2a/examples/helloworld/README.md) for more information. +The example includes detailed instructions on how to run both the Python server and the Java client using JBang. Check out the [example's README](examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md) for more information. ## License diff --git a/examples/client/pom.xml b/examples/helloworld/client/pom.xml similarity index 100% rename from examples/client/pom.xml rename to examples/helloworld/client/pom.xml diff --git a/examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java similarity index 100% rename from examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java rename to examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java diff --git a/examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java similarity index 100% rename from examples/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java rename to examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java diff --git a/examples/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md similarity index 100% rename from examples/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md rename to examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/INSTALL_JBANG.md diff --git a/examples/client/src/main/java/io/a2a/examples/helloworld/README.md b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md similarity index 100% rename from examples/client/src/main/java/io/a2a/examples/helloworld/README.md rename to examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md diff --git a/examples/pom.xml b/examples/helloworld/pom.xml similarity index 100% rename from examples/pom.xml rename to examples/helloworld/pom.xml diff --git a/examples/server/pom.xml b/examples/helloworld/server/pom.xml similarity index 100% rename from examples/server/pom.xml rename to examples/helloworld/server/pom.xml diff --git a/examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java similarity index 100% rename from examples/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java rename to examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java diff --git a/examples/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java similarity index 100% rename from examples/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java rename to examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java diff --git a/examples/server/src/main/java/io/a2a/examples/helloworld/README.md b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/README.md similarity index 100% rename from examples/server/src/main/java/io/a2a/examples/helloworld/README.md rename to examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/README.md diff --git a/examples/server/src/main/resources/application.properties b/examples/helloworld/server/src/main/resources/application.properties similarity index 100% rename from examples/server/src/main/resources/application.properties rename to examples/helloworld/server/src/main/resources/application.properties From 94ff7d78c0fe4a268400c0cfc3b83b3d07426f8b Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Tue, 1 Jul 2025 06:12:23 +0800 Subject: [PATCH 010/493] test: add unit test case for Assert and Utils (#132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [ ] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Co-authored-by: Stefano Maestri --- .../src/test/java/io/a2a/util/AssertTest.java | 100 +++++++++++ core/src/test/java/io/a2a/util/UtilsTest.java | 169 ++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 core/src/test/java/io/a2a/util/AssertTest.java create mode 100644 core/src/test/java/io/a2a/util/UtilsTest.java diff --git a/core/src/test/java/io/a2a/util/AssertTest.java b/core/src/test/java/io/a2a/util/AssertTest.java new file mode 100644 index 000000000..247764dac --- /dev/null +++ b/core/src/test/java/io/a2a/util/AssertTest.java @@ -0,0 +1,100 @@ +package io.a2a.util; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + +/** + * Tests for Assert utility class + */ +class AssertTest { + + @Test + void testCheckNotNullParam_WithValidValue() { + // Test normal case + String testValue = "test"; + String result = Assert.checkNotNullParam("testParam", testValue); + assertEquals(testValue, result); + } + + @Test + void testCheckNotNullParam_WithNullValue() { + // Test that null value throws exception + assertThrows(IllegalArgumentException.class, () -> { + Assert.checkNotNullParam("testParam", null); + }); + } + + @Test + void testCheckNotNullParam_WithNullName() { + // Test that null name parameter throws exception + assertThrows(IllegalArgumentException.class, () -> { + Assert.checkNotNullParam(null, "testValue"); + }); + } + + @Test + void testCheckNotNullParam_WithEmptyName() { + // Test empty string name parameter + String testValue = "test"; + String result = Assert.checkNotNullParam("", testValue); + assertEquals(testValue, result); + } + + @Test + void testIsNullOrStringOrInteger_WithNull() { + // Test null value + assertDoesNotThrow(() -> { + Assert.isNullOrStringOrInteger(null); + }); + } + + @Test + void testIsNullOrStringOrInteger_WithString() { + // Test String type + assertDoesNotThrow(() -> { + Assert.isNullOrStringOrInteger("test"); + }); + } + + @Test + void testIsNullOrStringOrInteger_WithInteger() { + // Test Integer type + assertDoesNotThrow(() -> { + Assert.isNullOrStringOrInteger(123); + }); + } + + @Test + void testIsNullOrStringOrInteger_WithLong() { + // Test that Long type throws exception + assertThrows(IllegalArgumentException.class, () -> { + Assert.isNullOrStringOrInteger(123L); + }); + } + + @Test + void testIsNullOrStringOrInteger_WithDouble() { + // Test that Double type throws exception + assertThrows(IllegalArgumentException.class, () -> { + Assert.isNullOrStringOrInteger(123.45); + }); + } + + @Test + void testIsNullOrStringOrInteger_WithBoolean() { + // Test that Boolean type throws exception + assertThrows(IllegalArgumentException.class, () -> { + Assert.isNullOrStringOrInteger(true); + }); + } + + @Test + void testIsNullOrStringOrInteger_WithObject() { + // Test that Object type throws exception + assertThrows(IllegalArgumentException.class, () -> { + Assert.isNullOrStringOrInteger(new Object()); + }); + } +} diff --git a/core/src/test/java/io/a2a/util/UtilsTest.java b/core/src/test/java/io/a2a/util/UtilsTest.java new file mode 100644 index 000000000..a9f7953f3 --- /dev/null +++ b/core/src/test/java/io/a2a/util/UtilsTest.java @@ -0,0 +1,169 @@ +package io.a2a.util; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import com.fasterxml.jackson.core.type.TypeReference; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +/** + * Tests for Utils utility class + */ +class UtilsTest { + + @Test + void testDefaultIfNull_WithNullValue() { + // Test that null value returns default value + String result = Utils.defaultIfNull(null, "default"); + assertEquals("default", result); + } + + @Test + void testDefaultIfNull_WithNonNullValue() { + // Test that non-null value returns original value + String value = "test"; + String result = Utils.defaultIfNull(value, "default"); + assertEquals(value, result); + } + + @Test + void testDefaultIfNull_WithNullDefault() { + // Test case where default value is null + String value = "test"; + String result = Utils.defaultIfNull(value, null); + assertEquals(value, result); + } + + @Test + void testDefaultIfNull_WithBothNull() { + // Test case where both value and default are null + String result = Utils.defaultIfNull(null, null); + assertNull(result); + } + + @Test + void testDefaultIfNull_WithDifferentTypes() { + // Test with different types + Integer intValue = 123; + Integer result = Utils.defaultIfNull(intValue, 456); + assertEquals(intValue, result); + } + + @Test + void testUnmarshalFrom_WithValidJson() throws Exception { + // Test with valid JSON string + String json = "{\"name\":\"test\",\"value\":123}"; + TypeReference> typeRef = new TypeReference>() {}; + + Map result = Utils.unmarshalFrom(json, typeRef); + + assertNotNull(result); + assertEquals("test", result.get("name")); + assertEquals(123, result.get("value")); + } + + @Test + void testUnmarshalFrom_WithArrayJson() throws Exception { + // Test with array JSON + String json = "[\"item1\",\"item2\",\"item3\"]"; + TypeReference> typeRef = new TypeReference>() {}; + + List result = Utils.unmarshalFrom(json, typeRef); + + assertNotNull(result); + assertEquals(3, result.size()); + assertEquals("item1", result.get(0)); + assertEquals("item2", result.get(1)); + assertEquals("item3", result.get(2)); + } + + @Test + void testUnmarshalFrom_WithInvalidJson() { + // Test with invalid JSON string + String invalidJson = "{invalid json}"; + TypeReference> typeRef = new TypeReference>() {}; + + assertThrows(Exception.class, () -> { + Utils.unmarshalFrom(invalidJson, typeRef); + }); + } + + @Test + void testUnmarshalFrom_WithEmptyJson() throws Exception { + // Test with empty JSON object + String json = "{}"; + TypeReference> typeRef = new TypeReference>() {}; + + Map result = Utils.unmarshalFrom(json, typeRef); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void testRethrow_WithRuntimeException() { + // Test rethrowing RuntimeException + RuntimeException originalException = new RuntimeException("test exception"); + + assertThrows(RuntimeException.class, () -> { + Utils.rethrow(originalException); + }); + } + + @Test + void testRethrow_WithCheckedException() { + // Test rethrowing checked exception + Exception originalException = new Exception("test checked exception"); + + assertThrows(Exception.class, () -> { + Utils.rethrow(originalException); + }); + } + + @Test + void testRethrow_WithCustomException() { + // Test rethrowing custom exception + IllegalArgumentException originalException = new IllegalArgumentException("test illegal argument"); + + assertThrows(IllegalArgumentException.class, () -> { + Utils.rethrow(originalException); + }); + } + + @Test + void testObjectMapper_IsNotNull() { + // Test that OBJECT_MAPPER is not null + assertNotNull(Utils.OBJECT_MAPPER); + } + + @Test + void testObjectMapper_CanSerializeAndDeserialize() throws Exception { + // Test that OBJECT_MAPPER can serialize and deserialize properly + TestObject original = new TestObject("test", 123); + + String json = Utils.OBJECT_MAPPER.writeValueAsString(original); + TestObject deserialized = Utils.OBJECT_MAPPER.readValue(json, TestObject.class); + + assertEquals(original.name, deserialized.name); + assertEquals(original.value, deserialized.value); + } + + // Simple class for testing + static class TestObject { + public String name; + public int value; + + public TestObject() {} + + public TestObject(String name, int value) { + this.name = name; + this.value = value; + } + } +} From 3ab3ad2ff1885d85f793f11bcd9f06ff42c21ebd Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Tue, 1 Jul 2025 11:51:52 +0200 Subject: [PATCH 011/493] Fix: fixing issues when running in a JakartaEE server. (#144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [ ] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [ ] Ensure the tests pass - [ ] Appropriate READMEs were updated (if necessary) Fixes # 🦕 Signed-off-by: Emmanuel Hugonnet --- sdk-jakarta/pom.xml | 2 ++ .../io/a2a/server/apps/jakarta/A2AServerResource.java | 8 +++++++- sdk-jakarta/src/main/resources/META-INF/beans.xml | 6 ++++++ .../io/a2a/server/requesthandlers/JSONRPCHandler.java | 3 +++ sdk-server-common/src/main/resources/META-INF/beans.xml | 6 ++++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index d7f85e83f..e32c6511d 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -48,10 +48,12 @@ jakarta.enterprise jakarta.enterprise.cdi-api + provided jakarta.inject jakarta.inject-api + provided jakarta.json diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java index ba587285d..d08e5a2ab 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java @@ -206,7 +206,10 @@ static void setStreamingIsSubscribedRunnable(Runnable streamingIsSubscribedRunna } @Provider - public class JsonParseExceptionMapper implements ExceptionMapper { + public static class JsonParseExceptionMapper implements ExceptionMapper { + + public JsonParseExceptionMapper() { + } @Override public Response toResponse(JsonParseException exception) { @@ -219,6 +222,9 @@ public Response toResponse(JsonParseException exception) { @Provider public static class JsonMappingExceptionMapper implements ExceptionMapper { + public JsonMappingExceptionMapper(){ + } + @Override public Response toResponse(JsonMappingException exception) { if (exception.getCause() instanceof JsonParseException) { diff --git a/sdk-jakarta/src/main/resources/META-INF/beans.xml b/sdk-jakarta/src/main/resources/META-INF/beans.xml index e69de29bb..9b2940fc2 100644 --- a/sdk-jakarta/src/main/resources/META-INF/beans.xml +++ b/sdk-jakarta/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java index 131b07fc8..fac014b0e 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java @@ -38,6 +38,9 @@ public class JSONRPCHandler { private AgentCard agentCard; private RequestHandler requestHandler; + protected JSONRPCHandler() { + } + @Inject public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { this.agentCard = agentCard; diff --git a/sdk-server-common/src/main/resources/META-INF/beans.xml b/sdk-server-common/src/main/resources/META-INF/beans.xml index e69de29bb..9dfae34df 100644 --- a/sdk-server-common/src/main/resources/META-INF/beans.xml +++ b/sdk-server-common/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file From b80a9d946031d8c6538635050d001e0c667385ec Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 1 Jul 2025 10:57:47 +0100 Subject: [PATCH 012/493] chore: fix build and CI (#145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 ----- The move of the examples which was merged yesterday broke the build. Also `-Dmaven.test.failure.ignore=true` in the CI job reported everything as passing, even in case of failures --- .github/workflows/build-and-test.yml | 2 +- examples/helloworld/pom.xml | 1 + pom.xml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 6e6433b05..b16350da6 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -22,4 +22,4 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven - run: mvn -B package --file pom.xml -Dmaven.test.failure.ignore=true -fae + run: mvn -B package --file pom.xml -fae diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index c9cd09015..b7c70b08c 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -8,6 +8,7 @@ io.a2a.sdk a2a-java-sdk-parent 0.2.4-SNAPSHOT + ../../pom.xml a2a-java-sdk-examples-parent diff --git a/pom.xml b/pom.xml index 80a6863b5..dcd204079 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ sdk-jakarta sdk-quarkus tck - examples + examples/helloworld tests/server-common \ No newline at end of file From e97df4cab7335f1396862ac8561668b45db63ff6 Mon Sep 17 00:00:00 2001 From: Sun Yuhan <20242118+sunyuhan1998@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:54:42 +0800 Subject: [PATCH 013/493] optimize: Improved `JSONRPCErrorDeserializer` (#136) This PR includes the following changes: 1. Improved the `JSONRPCErrorDeserializer` so that it can return the correct subclass based on the error code, instead of always returning `JSONRPCError`. 2. Added corresponding unit tests. Fixes #23 Signed-off-by: Sun Yuhan Co-authored-by: Sun Yuhan --- .../spec/ContentTypeNotSupportedError.java | 5 +- .../main/java/io/a2a/spec/InternalError.java | 5 +- .../a2a/spec/InvalidAgentResponseError.java | 5 +- .../java/io/a2a/spec/InvalidParamsError.java | 5 +- .../java/io/a2a/spec/InvalidRequestError.java | 4 +- .../main/java/io/a2a/spec/JSONParseError.java | 4 +- .../io/a2a/spec/JSONRPCErrorDeserializer.java | 30 ++++++++++- .../java/io/a2a/spec/MethodNotFoundError.java | 7 ++- .../PushNotificationNotSupportedError.java | 5 +- .../io/a2a/spec/TaskNotCancelableError.java | 5 +- .../java/io/a2a/spec/TaskNotFoundError.java | 5 +- .../a2a/spec/UnsupportedOperationError.java | 5 +- .../spec/JSONRPCErrorSerializationTest.java | 52 +++++++++++++++++++ 13 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 core/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java diff --git a/core/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/core/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index 04b37ba25..d05ee3202 100644 --- a/core/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/core/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -10,13 +10,16 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class ContentTypeNotSupportedError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32005; + @JsonCreator public ContentTypeNotSupportedError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32005), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Incompatible content types"), data); } diff --git a/core/src/main/java/io/a2a/spec/InternalError.java b/core/src/main/java/io/a2a/spec/InternalError.java index 02929c0d9..47f1a332e 100644 --- a/core/src/main/java/io/a2a/spec/InternalError.java +++ b/core/src/main/java/io/a2a/spec/InternalError.java @@ -10,13 +10,16 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class InternalError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32603; + @JsonCreator public InternalError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32603), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Internal Error"), data); } diff --git a/core/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/core/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index fb1af4210..7b0476e2a 100644 --- a/core/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/core/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -13,13 +13,16 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidAgentResponseError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32006; + @JsonCreator public InvalidAgentResponseError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32006), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Invalid agent response"), data); } diff --git a/core/src/main/java/io/a2a/spec/InvalidParamsError.java b/core/src/main/java/io/a2a/spec/InvalidParamsError.java index 4a532d5ff..69cd1fadc 100644 --- a/core/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/core/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -10,13 +10,16 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidParamsError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32602; + @JsonCreator public InvalidParamsError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32602), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Invalid parameters"), data); } diff --git a/core/src/main/java/io/a2a/spec/InvalidRequestError.java b/core/src/main/java/io/a2a/spec/InvalidRequestError.java index a22fc15ba..17d3227dc 100644 --- a/core/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/core/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -11,6 +11,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidRequestError extends JSONRPCError { + public final static Integer DEFAULT_CODE = -32600; + public InvalidRequestError() { this(null, null, null); } @@ -21,7 +23,7 @@ public InvalidRequestError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32600), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Request payload validation error"), data); } diff --git a/core/src/main/java/io/a2a/spec/JSONParseError.java b/core/src/main/java/io/a2a/spec/JSONParseError.java index 93000b5bf..244ae39ec 100644 --- a/core/src/main/java/io/a2a/spec/JSONParseError.java +++ b/core/src/main/java/io/a2a/spec/JSONParseError.java @@ -11,6 +11,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class JSONParseError extends JSONRPCError implements A2AError { + public final static Integer DEFAULT_CODE = -32700; + public JSONParseError() { this(null, null, null); } @@ -25,7 +27,7 @@ public JSONParseError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32700), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Invalid JSON payload"), data); } diff --git a/core/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java b/core/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java index 33f438469..229abf55d 100644 --- a/core/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java +++ b/core/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java @@ -1,6 +1,8 @@ package io.a2a.spec; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; @@ -10,6 +12,22 @@ public class JSONRPCErrorDeserializer extends StdDeserializer { + private static final Map> ERROR_MAP = new HashMap<>(); + + static { + ERROR_MAP.put(JSONParseError.DEFAULT_CODE, JSONParseError::new); + ERROR_MAP.put(InvalidRequestError.DEFAULT_CODE, InvalidRequestError::new); + ERROR_MAP.put(MethodNotFoundError.DEFAULT_CODE, MethodNotFoundError::new); + ERROR_MAP.put(InvalidParamsError.DEFAULT_CODE, InvalidParamsError::new); + ERROR_MAP.put(InternalError.DEFAULT_CODE, InternalError::new); + ERROR_MAP.put(PushNotificationNotSupportedError.DEFAULT_CODE, PushNotificationNotSupportedError::new); + ERROR_MAP.put(UnsupportedOperationError.DEFAULT_CODE, UnsupportedOperationError::new); + ERROR_MAP.put(ContentTypeNotSupportedError.DEFAULT_CODE, ContentTypeNotSupportedError::new); + ERROR_MAP.put(InvalidAgentResponseError.DEFAULT_CODE, InvalidAgentResponseError::new); + ERROR_MAP.put(TaskNotCancelableError.DEFAULT_CODE, TaskNotCancelableError::new); + ERROR_MAP.put(TaskNotFoundError.DEFAULT_CODE, TaskNotFoundError::new); + } + public JSONRPCErrorDeserializer() { this(null); } @@ -26,6 +44,16 @@ public JSONRPCError deserialize(JsonParser jsonParser, DeserializationContext co String message = node.get("message").asText(); JsonNode dataNode = node.get("data"); Object data = dataNode != null ? jsonParser.getCodec().treeToValue(dataNode, Object.class) : null; - return new JSONRPCError(code, message, data); + TriFunction constructor = ERROR_MAP.get(code); + if (constructor != null) { + return constructor.apply(code, message, data); + } else { + return new JSONRPCError(code, message, data); + } + } + + @FunctionalInterface + private interface TriFunction { + R apply(A a, B b, C c); } } diff --git a/core/src/main/java/io/a2a/spec/MethodNotFoundError.java b/core/src/main/java/io/a2a/spec/MethodNotFoundError.java index 967e938ef..a8d9d9289 100644 --- a/core/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/core/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -10,18 +10,21 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class MethodNotFoundError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32601; + @JsonCreator public MethodNotFoundError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32601), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Method not found"), data); } public MethodNotFoundError() { - this(-32601, null, null); + this(DEFAULT_CODE, null, null); } } diff --git a/core/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/core/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index 5ae5e8aa9..b97094cc0 100644 --- a/core/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/core/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -10,13 +10,16 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class PushNotificationNotSupportedError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32003; + @JsonCreator public PushNotificationNotSupportedError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32003), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Push Notification is not supported"), data); } diff --git a/core/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/core/src/main/java/io/a2a/spec/TaskNotCancelableError.java index 3711fe8dd..aa40a81d3 100644 --- a/core/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/core/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -10,6 +10,9 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotCancelableError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32002; + public TaskNotCancelableError() { this(null, null, null); } @@ -20,7 +23,7 @@ public TaskNotCancelableError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32002), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Task cannot be canceled"), data); } diff --git a/core/src/main/java/io/a2a/spec/TaskNotFoundError.java b/core/src/main/java/io/a2a/spec/TaskNotFoundError.java index ef24bc1de..61b1857c5 100644 --- a/core/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/core/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -10,6 +10,9 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotFoundError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32001; + public TaskNotFoundError() { this(null, null, null); } @@ -21,7 +24,7 @@ public TaskNotFoundError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32001), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "Task not found"), data); } diff --git a/core/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/core/src/main/java/io/a2a/spec/UnsupportedOperationError.java index 637df90fe..d5ee0fb44 100644 --- a/core/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/core/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -10,13 +10,16 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class UnsupportedOperationError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32004; + @JsonCreator public UnsupportedOperationError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, -32004), + defaultIfNull(code, DEFAULT_CODE), defaultIfNull(message, "This operation is not supported"), data); } diff --git a/core/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java b/core/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java new file mode 100644 index 000000000..24251a879 --- /dev/null +++ b/core/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java @@ -0,0 +1,52 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +public class JSONRPCErrorSerializationTest { + @Test + public void shouldDeserializeToCorrectJSONRPCErrorSubclass() { + ObjectMapper objectMapper = new ObjectMapper(); + String jsonTemplate = """ + {"code": %s, "message": "error", "data": "anything"} + """; + + record ErrorCase(int code, Class clazz) {} + + List cases = List.of( + new ErrorCase(JSONParseError.DEFAULT_CODE, JSONParseError.class), + new ErrorCase(InvalidRequestError.DEFAULT_CODE, InvalidRequestError.class), + new ErrorCase(MethodNotFoundError.DEFAULT_CODE, MethodNotFoundError.class), + new ErrorCase(InvalidParamsError.DEFAULT_CODE, InvalidParamsError.class), + new ErrorCase(InternalError.DEFAULT_CODE, InternalError.class), + new ErrorCase(PushNotificationNotSupportedError.DEFAULT_CODE, PushNotificationNotSupportedError.class), + new ErrorCase(UnsupportedOperationError.DEFAULT_CODE, UnsupportedOperationError.class), + new ErrorCase(ContentTypeNotSupportedError.DEFAULT_CODE, ContentTypeNotSupportedError.class), + new ErrorCase(InvalidAgentResponseError.DEFAULT_CODE, InvalidAgentResponseError.class), + new ErrorCase(TaskNotCancelableError.DEFAULT_CODE, TaskNotCancelableError.class), + new ErrorCase(TaskNotFoundError.DEFAULT_CODE, TaskNotFoundError.class), + new ErrorCase(Integer.MAX_VALUE, JSONRPCError.class) // Any unknown code will be treated as JSONRPCError + ); + + for (ErrorCase errorCase : cases) { + String json = jsonTemplate.formatted(errorCase.code()); + JSONRPCError error; + try { + error = objectMapper.readValue(json, JSONRPCError.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + assertInstanceOf(errorCase.clazz(), error); + assertEquals("error", error.getMessage()); + assertEquals("anything", error.getData().toString()); + } + } + + +} From 546d00d44f585483a1e3aa4b8048aaae8776614a Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 3 Jul 2025 05:02:42 -0400 Subject: [PATCH 014/493] fix: Update paths in example READMEs and move the READMEs to the client and server dirs (#148) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- README.md | 22 ++++++++++++++----- .../io/a2a/examples/helloworld => }/README.md | 2 +- .../io/a2a/examples/helloworld => }/README.md | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) rename examples/helloworld/client/{src/main/java/io/a2a/examples/helloworld => }/README.md (97%) rename examples/helloworld/server/{src/main/java/io/a2a/examples/helloworld => }/README.md (98%) diff --git a/README.md b/README.md index b0d19214d..97f2fd2b9 100644 --- a/README.md +++ b/README.md @@ -336,15 +336,27 @@ AgentCard agentCard = A2A.getAgentCard("http://localhost:1234"); ## Additional Examples -### Hello World Example +### Hello World Client Example -A complete example of an A2A client communicating with a Python A2A server is available in the [examples/helloworld](examples/helloworld) directory. This example demonstrates: +A complete example of a Java A2A client communicating with a Python A2A server is available in the [examples/helloworld/client](examples/helloworld/client/README.md) directory. This example demonstrates: - Setting up and using the A2A Java client -- Sending regular and streaming messages -- Receiving and processing responses +- Sending regular and streaming messages to a Python A2A server +- Receiving and processing responses from the Python A2A server -The example includes detailed instructions on how to run both the Python server and the Java client using JBang. Check out the [example's README](examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md) for more information. +The example includes detailed instructions on how to run the Python A2A server and how to run the Java A2A client using JBang. + +Check out the [example's README](examples/helloworld/client/README.md) for more information. + +### Hello World Server Example + +A complete example of a Python A2A client communicating with a Java A2A server is available in the [examples/helloworld/server](examples/helloworld/server/README.md) directory. This example demonstrates: + +- A sample `AgentCard` producer +- A sample `AgentExecutor` producer +- A Java A2A server receiving regular and streaming messages from a Python A2A client + +Check out the [example's README](examples/helloworld/server/README.md) for more information. ## License diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md b/examples/helloworld/client/README.md similarity index 97% rename from examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md rename to examples/helloworld/client/README.md index d3d33c0bd..9bea69eb5 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/README.md +++ b/examples/helloworld/client/README.md @@ -62,7 +62,7 @@ A JBang script is provided in the example directory to make running the client e 2. Navigate to the example directory: ```bash - cd examples/client/src/main/java/io/a2a/examples/helloworld + cd examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/ ``` 3. Run the client using the JBang script: diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/README.md b/examples/helloworld/server/README.md similarity index 98% rename from examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/README.md rename to examples/helloworld/server/README.md index 821471901..5573dce09 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/README.md +++ b/examples/helloworld/server/README.md @@ -14,7 +14,7 @@ This example demonstrates how to use the A2A Java SDK to communicate with an A2A The Java server can be started using `mvn` as follows: ```bash -cd examples/server +cd examples/helloworld/server mvn quarkus:dev ``` From 33b8e771e4db8031d7a11f0626f56a378d16b508 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 3 Jul 2025 12:24:55 +0200 Subject: [PATCH 015/493] fix: Moving the AsyncExecutorProducer from @Singleton to (#150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @ApplicationScoped so that it becomes a stereotype and can be discovered using bean-discovery-mode="annotated" instead of "all". # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [C] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [C] Ensure the tests pass - [ ] Appropriate READMEs were updated (if necessary) Fixes # 🦕 Signed-off-by: Emmanuel Hugonnet --- .../java/io/a2a/server/util/async/AsyncExecutorProducer.java | 4 ++-- sdk-server-common/src/main/resources/META-INF/beans.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index 1b3066ca6..27b4ac12b 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -6,10 +6,10 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; -import jakarta.inject.Singleton; -@Singleton +@ApplicationScoped public class AsyncExecutorProducer { private ExecutorService executor; diff --git a/sdk-server-common/src/main/resources/META-INF/beans.xml b/sdk-server-common/src/main/resources/META-INF/beans.xml index 9dfae34df..9b2940fc2 100644 --- a/sdk-server-common/src/main/resources/META-INF/beans.xml +++ b/sdk-server-common/src/main/resources/META-INF/beans.xml @@ -2,5 +2,5 @@ + bean-discovery-mode="annotated"> \ No newline at end of file From d8fa0e6268f015f03c8b8c6f844324e7d370f588 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 3 Jul 2025 11:25:56 +0100 Subject: [PATCH 016/493] fix: incorporate feedback from #138 (#146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../server/agentexecution/RequestContext.java | 7 +- .../a2a/server/events/EnhancedRunnable.java | 13 +- .../io/a2a/server/events/EventConsumer.java | 6 +- .../java/io/a2a/server/events/EventQueue.java | 20 ++- .../server/events/InMemoryQueueManager.java | 45 +++---- .../DefaultRequestHandler.java | 127 ++++++++---------- .../a2a/server/tasks/InMemoryTaskStore.java | 4 +- 7 files changed, 105 insertions(+), 117 deletions(-) diff --git a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index e62a87d3c..bac7673a1 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -1,6 +1,7 @@ package io.a2a.server.agentexecution; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -31,12 +32,12 @@ public RequestContext(MessageSendParams params, String taskId, String contextId, // if the taskId and contextId were specified, they must match the params if (params != null) { - if (taskId != null && ! params.message().getTaskId().equals(taskId)) { + if (taskId != null && !taskId.equals(params.message().getTaskId())) { throw new InvalidParamsError("bad task id"); } else { checkOrGenerateTaskId(); } - if (contextId != null && ! params.message().getContextId().equals(contextId)) { + if (contextId != null && !contextId.equals(params.message().getContextId())) { throw new InvalidParamsError("bad context id"); } else { checkOrGenerateContextId(); @@ -61,7 +62,7 @@ public Task getTask() { } public List getRelatedTasks() { - return relatedTasks; + return Collections.unmodifiableList(relatedTasks); } public Message getMessage() { diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java b/sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java index d5316d999..17d5b3e9a 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java @@ -2,10 +2,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; public abstract class EnhancedRunnable implements Runnable { private volatile Throwable error; - private final List doneCallbacks = new ArrayList<>(); + private final List doneCallbacks = new CopyOnWriteArrayList<>(); public Throwable getError() { return error; @@ -16,16 +17,12 @@ public void setError(Throwable error) { } public void addDoneCallback(DoneCallback doneCallback) { - synchronized (doneCallbacks) { - doneCallbacks.add(doneCallback); - } + doneCallbacks.add(doneCallback); } public void invokeDoneCallbacks() { - synchronized (doneCallbacks) { - for (DoneCallback doneCallback : doneCallbacks) { - doneCallback.done(this); - } + for (DoneCallback doneCallback : doneCallbacks) { + doneCallback.done(this); } } diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java index ca38f43b7..7a693d610 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -67,9 +67,9 @@ public Flow.Publisher consumeAll() { completed = true; tube.complete(); return; - } catch (Exception e) { - // Continue polling until there is a final event - continue; + } catch (Throwable t) { + tube.fail(t); + return; } boolean isFinalEvent = false; diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java index 585210c3e..2d2bf994c 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -5,6 +5,8 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -13,13 +15,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class EventQueue { +public abstract class EventQueue implements AutoCloseable { private static final Logger log = new TempLoggerWrapper(LoggerFactory.getLogger(EventQueue.class)); + // TODO decide on a capacity + private static final int queueSize = 1000; + private final EventQueue parent; - // TODO decide on a capacity (or more appropriate queue data structures) - private final BlockingQueue queue = new ArrayBlockingQueue(1000); + + private final BlockingQueue queue = new LinkedBlockingDeque<>(); + private final Semaphore semaphore = new Semaphore(queueSize, true); private volatile boolean closed = false; @@ -47,6 +53,12 @@ public void enqueueEvent(Event event) { return; } // Call toString() since for errors we don't really want the full stacktrace + try { + semaphore.acquire(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unable to acquire the semaphore to enqueue the event", e); + } queue.add(event); log.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); } @@ -64,6 +76,7 @@ public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException if (event != null) { // Call toString() since for errors we don't really want the full stacktrace log.debug("Dequeued event (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); + semaphore.release(); } return event; } @@ -72,6 +85,7 @@ public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException if (event != null) { // Call toString() since for errors we don't really want the full stacktrace log.debug("Dequeued event (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); + semaphore.release(); } return event; } catch (InterruptedException e) { diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index 72bb81eb3..c32178061 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -3,20 +3,20 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class InMemoryQueueManager implements QueueManager { - private final Map queues = Collections.synchronizedMap(new HashMap<>()); + private final ConcurrentMap queues = new ConcurrentHashMap<>(); @Override public void add(String taskId, EventQueue queue) { - synchronized (queues) { - if (queues.containsKey(taskId)) { - throw new TaskQueueExistsException(); - } - queues.put(taskId, queue); + EventQueue existing = queues.putIfAbsent(taskId, queue); + if (existing != null) { + throw new TaskQueueExistsException(); } } @@ -27,36 +27,29 @@ public EventQueue get(String taskId) { @Override public EventQueue tap(String taskId) { - synchronized (taskId) { - EventQueue queue = queues.get(taskId); - if (queue == null) { - return queue; - } - return queue.tap(); - } + EventQueue queue = queues.get(taskId); + return queue == null ? null : queue.tap(); } @Override public void close(String taskId) { - synchronized (queues) { - EventQueue existing = queues.remove(taskId); - if (existing == null) { - throw new NoTaskQueueException(); - } + EventQueue existing = queues.remove(taskId); + if (existing == null) { + throw new NoTaskQueueException(); } } @Override public EventQueue createOrTap(String taskId) { - synchronized (queues) { - EventQueue queue = queues.get(taskId); - if (queue != null) { - return queue.tap(); - } - queue = EventQueue.create(); - queues.put(taskId, queue); - return queue; + + EventQueue existing = queues.get(taskId); + EventQueue newQueue = null; + if (existing == null) { + newQueue = EventQueue.create(); + // Make sure an existing queue has not been added in the meantime + existing = queues.putIfAbsent(taskId, newQueue); } + return existing == null ? newQueue : existing.tap(); } @Override diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 501e71315..136b9e7c8 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -5,12 +5,12 @@ import static io.a2a.server.util.async.AsyncUtils.processor; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicReference; @@ -61,7 +61,7 @@ public class DefaultRequestHandler implements RequestHandler { private final PushNotifier pushNotifier; private final Supplier requestContextBuilder; - private final Map> runningAgents = Collections.synchronizedMap(new HashMap<>()); + private final ConcurrentMap> runningAgents = new ConcurrentHashMap<>(); private final Executor executor; @@ -133,10 +133,8 @@ public Task onCancelTask(TaskIdParams params) throws JSONRPCError { .build(), queue); - CompletableFuture cf = runningAgents.get(task.getId()); - if (cf != null) { - cf.cancel(true); - } + Optional.ofNullable(runningAgents.get(task.getId())) + .ifPresent(cf -> cf.cancel(true)); EventConsumer consumer = new EventConsumer(queue); EventKind type = resultAggregator.consumeAll(consumer); @@ -150,46 +148,22 @@ public Task onCancelTask(TaskIdParams params) throws JSONRPCError { @Override public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { log.debug("onMessageSend - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); - TaskManager taskManager = new TaskManager( - params.message().getTaskId(), - params.message().getContextId(), - taskStore, - params.message()); - - Task task = taskManager.getTask(); - if (task != null) { - log.debug("Found task updating with message {}", params.message()); - task = taskManager.updateWithMessage(params.message(), task); - - if (shouldAddPushInfo(params)) { - log.debug("Adding push info"); - pushNotifier.setInfo(task.getId(), params.configuration().pushNotification()); - } - } - - RequestContext requestContext = requestContextBuilder.get() - .setParams(params) - .setTaskId(task == null ? null : task.getId()) - .setContextId(params.message().getContextId()) - .setTask(task) - .build(); + MessageSendSetup mss = initMessageSend(params); - String taskId = requestContext.getTaskId(); + String taskId = mss.requestContext.getTaskId(); log.debug("Request context taskId: {}", taskId); EventQueue queue = queueManager.createOrTap(taskId); - ResultAggregator resultAggregator = new ResultAggregator(taskManager, null); - - EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId, requestContext, queue); - - EventConsumer consumer = new EventConsumer(queue); - - producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); + ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); boolean interrupted = false; - ResultAggregator.EventTypeAndInterrupt etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); + EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId, mss.requestContext, queue); + ResultAggregator.EventTypeAndInterrupt etai = null; try { + EventConsumer consumer = new EventConsumer(queue); + etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); + producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); if (etai == null) { log.debug("No result, throwing InternalError"); throw new InternalError("No result"); @@ -217,39 +191,20 @@ public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { @Override public Flow.Publisher onMessageSendStream(MessageSendParams params) throws JSONRPCError { - TaskManager taskManager = new TaskManager( - params.message().getTaskId(), - params.message().getContextId(), - taskStore, - params.message()); - - Task task = taskManager.getTask(); - if (task != null) { - task = taskManager.updateWithMessage(params.message(), task); + log.debug("onMessageSendStream - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); + MessageSendSetup mss = initMessageSend(params); - if (shouldAddPushInfo(params)) { - pushNotifier.setInfo(task.getId(), params.configuration().pushNotification()); - } - } - - RequestContext requestContext = requestContextBuilder.get() - .setParams(params) - .setTaskId(task == null ? null : task.getId()) - .setContextId(params.message().getContextId()) - .setTask(task) - .build(); - - AtomicReference taskId = new AtomicReference<>(requestContext.getTaskId()); + AtomicReference taskId = new AtomicReference<>(mss.requestContext.getTaskId()); EventQueue queue = queueManager.createOrTap(taskId.get()); - ResultAggregator resultAggregator = new ResultAggregator(taskManager, null); + ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); - EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId.get(), requestContext, queue); + EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId.get(), mss.requestContext, queue); - EventConsumer consumer = new EventConsumer(queue); + try { + EventConsumer consumer = new EventConsumer(queue); - producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); + producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); - try { Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); Flow.Publisher eventPublisher = @@ -377,13 +332,39 @@ public void run() { private void cleanupProducer(String taskId) { // TODO the Python implementation waits for the producerRunnable - CompletableFuture cf = runningAgents.get(taskId); - if (cf != null) { - cf.whenComplete((v, t) -> { - queueManager.close(taskId); - runningAgents.remove(taskId); - }); + runningAgents.get(taskId) + .whenComplete((v, t) -> { + queueManager.close(taskId); + runningAgents.remove(taskId); + }); + } + + private MessageSendSetup initMessageSend(MessageSendParams params) { + TaskManager taskManager = new TaskManager( + params.message().getTaskId(), + params.message().getContextId(), + taskStore, + params.message()); + + Task task = taskManager.getTask(); + if (task != null) { + log.debug("Found task updating with message {}", params.message()); + task = taskManager.updateWithMessage(params.message(), task); + + if (shouldAddPushInfo(params)) { + log.debug("Adding push info"); + pushNotifier.setInfo(task.getId(), params.configuration().pushNotification()); + } } + + RequestContext requestContext = requestContextBuilder.get() + .setParams(params) + .setTaskId(task == null ? null : task.getId()) + .setContextId(params.message().getContextId()) + .setTask(task) + .build(); + return new MessageSendSetup(taskManager, task, requestContext); } + private record MessageSendSetup(TaskManager taskManager, Task task, RequestContext requestContext) {} } diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 4edfe21e4..5cb42d8bb 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -3,6 +3,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import jakarta.enterprise.context.ApplicationScoped; @@ -11,7 +13,7 @@ @ApplicationScoped public class InMemoryTaskStore implements TaskStore { - private final Map tasks = Collections.synchronizedMap(new HashMap<>()); + private final ConcurrentMap tasks = new ConcurrentHashMap<>(); @Override public void save(Task task) { From fd0cdb7cfc8a28b12d13624bbf6ca1dcd77dafe7 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 3 Jul 2025 17:13:03 +0100 Subject: [PATCH 017/493] fix: fix TCK failure (#153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../io/a2a/server/requesthandlers/DefaultRequestHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 136b9e7c8..ad453f70e 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -162,8 +162,9 @@ public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { ResultAggregator.EventTypeAndInterrupt etai = null; try { EventConsumer consumer = new EventConsumer(queue); - etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); + etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); + if (etai == null) { log.debug("No result, throwing InternalError"); throw new InternalError("No result"); From 982138c726afc8150b0ac2c5110e65ea457c7b40 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 3 Jul 2025 17:24:11 +0100 Subject: [PATCH 018/493] fix: add clarifying comment about done callback and consuming order (#154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [ ] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../a2a/server/requesthandlers/DefaultRequestHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index ad453f70e..4e86d53f6 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -162,6 +162,9 @@ public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { ResultAggregator.EventTypeAndInterrupt etai = null; try { EventConsumer consumer = new EventConsumer(queue); + + // This callback must be added before we start consuming. Otherwise, + // any errors thrown by the producerRunnable are not picked up by the consumer producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); @@ -204,8 +207,9 @@ public Flow.Publisher onMessageSendStream(MessageSendParams try { EventConsumer consumer = new EventConsumer(queue); + // This callback must be added before we start consuming. Otherwise, + // any errors thrown by the producerRunnable are not picked up by the consumer producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); - Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); Flow.Publisher eventPublisher = From f1b88014d871a8f585c6dc78f42c20586eba3a63 Mon Sep 17 00:00:00 2001 From: Stefano Maestri Date: Thu, 3 Jul 2025 22:47:50 +0200 Subject: [PATCH 019/493] feat: docs: add community articles (#156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- COMMUNITY_ARTICLES.md | 13 +++++++++++++ README.md | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 COMMUNITY_ARTICLES.md diff --git a/COMMUNITY_ARTICLES.md b/COMMUNITY_ARTICLES.md new file mode 100644 index 000000000..31208d135 --- /dev/null +++ b/COMMUNITY_ARTICLES.md @@ -0,0 +1,13 @@ +# Community Articles + +Here are some articles and videos from the community that you may find helpful. + +## General Agent and A2A Introduction + +* [Agents are not tools](https://www.googlecloudcommunity.com/gc/Community-Blogs/Agents-are-not-tools/ba-p/922716): A nice explanation of what agents are from one of the main contributors of A2A spec. +* [Introducing A2A](https://goo.gle/a2a-video): A good video introducing A2A. + +## A2A Java SDK Specific + +* [Quarkus and WildFly teams from Red Hat collaborating with Google on launch of Agent2Agent Java SDK](https://quarkus.io/blog/a2a-project-launches-java-sdk/): Announcing the new availability of the A2A Java SDK and giving a quick introduction to it. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/quarkus-and-wildfly-teams-from-red-hat-collaborating-with-google-on-launch-of-agent2agent-java-sdk-5f8cc64921cb). +* [A2A Java SDK Introduction Video](https://www.youtube.com/watch?v=5CZzW-wvEQs): A great video introducing the A2A Java SDK and giving a walkthrough of a multi-language interoperability example. diff --git a/README.md b/README.md index 97f2fd2b9..11bcfb210 100644 --- a/README.md +++ b/README.md @@ -358,6 +358,10 @@ A complete example of a Python A2A client communicating with a Java A2A server i Check out the [example's README](examples/helloworld/server/README.md) for more information. +## Community Articles + +See [COMMUNITY_ARTICLES.md](COMMUNITY_ARTICLES.md) for a list of community articles and videos. + ## License This project is licensed under the terms of the [Apache 2.0 License](LICENSE). From 9a8e9e58d05af4336f769ae63ebebeda00317120 Mon Sep 17 00:00:00 2001 From: Sun Yuhan <20242118+sunyuhan1998@users.noreply.github.com> Date: Fri, 4 Jul 2025 17:59:49 +0800 Subject: [PATCH 020/493] fix: Remove `TempLoggerWrapper` and use `logback` as the logging implementation. (#158) Hi @kabir , as we discussed previously: https://github.com/a2aproject/a2a-java/issues/94. This PR removes the `TempLoggerWrapper` and uses `logback` as the default logging implementation. `logback` automatically loads the `logback.xml` file from the classpath, and we can easily adjust the logging level by modifying this file. Fixes #94 --------- Signed-off-by: Sun Yuhan Co-authored-by: Sun Yuhan --- pom.xml | 7 + sdk-server-common/pom.xml | 23 ++ .../java/io/a2a/server/events/EventQueue.java | 3 +- .../DefaultRequestHandler.java | 3 +- .../io/a2a/server/tasks/ResultAggregator.java | 3 +- .../io/a2a/server/util/TempLoggerWrapper.java | 370 ------------------ .../src/test/resources/logback.xml | 13 + 7 files changed, 46 insertions(+), 376 deletions(-) delete mode 100644 sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java create mode 100644 sdk-server-common/src/test/resources/logback.xml diff --git a/pom.xml b/pom.xml index dcd204079..4950c6bbd 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,7 @@ 3.22.3 5.5.1 2.0.17 + 1.5.18 true @@ -111,6 +112,12 @@ ${mockserver.version} test + + ch.qos.logback + logback-classic + ${logback.version} + test + diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 44e2e298a..4cf02b5a6 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -50,6 +50,24 @@ io.quarkus quarkus-arc test + + + org.jboss.logging + jboss-logging + + + org.jboss.logmanager + jboss-logmanager + + + org.jboss.threads + jboss-threads + + + org.jboss.slf4j + slf4j-jboss-logmanager + + org.junit.jupiter @@ -66,6 +84,11 @@ mockserver-netty test + + ch.qos.logback + logback-classic + test + \ No newline at end of file diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java index 2d2bf994c..8f689312f 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -10,14 +10,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import io.a2a.server.util.TempLoggerWrapper; import io.a2a.spec.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class EventQueue implements AutoCloseable { - private static final Logger log = new TempLoggerWrapper(LoggerFactory.getLogger(EventQueue.class)); + private static final Logger log = LoggerFactory.getLogger(EventQueue.class); // TODO decide on a capacity private static final int queueSize = 1000; diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 4e86d53f6..e2c876d1d 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -31,7 +31,6 @@ import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; -import io.a2a.server.util.TempLoggerWrapper; import io.a2a.server.util.async.Internal; import io.a2a.spec.Event; import io.a2a.spec.EventKind; @@ -53,7 +52,7 @@ @ApplicationScoped public class DefaultRequestHandler implements RequestHandler { - private static final Logger log = new TempLoggerWrapper(LoggerFactory.getLogger(DefaultRequestHandler.class)); + private static final Logger log = LoggerFactory.getLogger(DefaultRequestHandler.class); private final AgentExecutor agentExecutor; private final TaskStore taskStore; diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index b9aef07ff..c04c4e829 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicReference; import io.a2a.server.events.EventConsumer; -import io.a2a.server.util.TempLoggerWrapper; import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.EventKind; @@ -23,7 +22,7 @@ import org.slf4j.LoggerFactory; public class ResultAggregator { - private static final Logger log = new TempLoggerWrapper(LoggerFactory.getLogger(ResultAggregator.class)); + private static final Logger log = LoggerFactory.getLogger(ResultAggregator.class); private final TaskManager taskManager; private volatile Message message; diff --git a/sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java b/sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java deleted file mode 100644 index cecc6797d..000000000 --- a/sdk-server-common/src/main/java/io/a2a/server/util/TempLoggerWrapper.java +++ /dev/null @@ -1,370 +0,0 @@ -package io.a2a.server.util; - -import org.slf4j.Logger; -import org.slf4j.Marker; -import org.slf4j.event.Level; -import org.slf4j.helpers.CheckReturnValue; -import org.slf4j.spi.LoggingEventBuilder; - -/** - * Temporary wrapper class for Logger to log debug and trace messages at info level since I am not able to figure - * out how to enable debug logging via configuration. - */ -public class TempLoggerWrapper implements Logger { - private final Logger delegate; - - public TempLoggerWrapper(Logger delegate) { - this.delegate = delegate; - } - - @Override - public String getName() { - return delegate.getName(); - } - - @Override - public LoggingEventBuilder makeLoggingEventBuilder(Level level) { - return delegate.makeLoggingEventBuilder(level); - } - - @CheckReturnValue - @Override - public LoggingEventBuilder atLevel(Level level) { - return delegate.atLevel(level); - } - - @Override - public boolean isEnabledForLevel(Level level) { - return delegate.isEnabledForLevel(level); - } - - @Override - public boolean isTraceEnabled() { - return delegate.isTraceEnabled(); - } - - @Override - public void trace(String msg) { - delegate.info(msg); - } - - @Override - public void trace(String format, Object arg) { - delegate.info(format, arg); - } - - @Override - public void trace(String format, Object arg1, Object arg2) { - delegate.info(format, arg1, arg2); - } - - @Override - public void trace(String format, Object... arguments) { - delegate.info(format, arguments); - } - - @Override - public void trace(String msg, Throwable t) { - delegate.info(msg, t); - } - - @Override - public boolean isTraceEnabled(Marker marker) { - return delegate.isInfoEnabled(marker); - } - - @CheckReturnValue - @Override - public LoggingEventBuilder atTrace() { - return delegate.atTrace(); - } - - @Override - public void trace(Marker marker, String msg) { - delegate.info(marker, msg); - } - - @Override - public void trace(Marker marker, String format, Object arg) { - delegate.info(marker, format, arg); - } - - @Override - public void trace(Marker marker, String format, Object arg1, Object arg2) { - delegate.info(marker, format, arg1, arg2); - } - - @Override - public void trace(Marker marker, String format, Object... argArray) { - delegate.info(marker, format, argArray); - } - - @Override - public void trace(Marker marker, String msg, Throwable t) { - delegate.info(marker, msg, t); - } - - @Override - public boolean isDebugEnabled() { - return delegate.isInfoEnabled(); - } - - @Override - public void debug(String msg) { - delegate.info(msg); - } - - @Override - public void debug(String format, Object arg) { - delegate.info(format, arg); - } - - @Override - public void debug(String format, Object arg1, Object arg2) { - delegate.info(format, arg1, arg2); - } - - @Override - public void debug(String format, Object... arguments) { - delegate.info(format, arguments); - } - - @Override - public void debug(String msg, Throwable t) { - delegate.info(msg, t); - } - - @Override - public boolean isDebugEnabled(Marker marker) { - return delegate.isDebugEnabled(marker); - } - - @Override - public void debug(Marker marker, String msg) { - delegate.info(marker, msg); - } - - @Override - public void debug(Marker marker, String format, Object arg) { - delegate.info(marker, format, arg); - } - - @Override - public void debug(Marker marker, String format, Object arg1, Object arg2) { - delegate.info(marker, format, arg1, arg2); - } - - @Override - public void debug(Marker marker, String format, Object... arguments) { - delegate.info(marker, format, arguments); - } - - @Override - public void debug(Marker marker, String msg, Throwable t) { - delegate.info(marker, msg, t); - } - - @CheckReturnValue - @Override - public LoggingEventBuilder atDebug() { - return delegate.atDebug(); - } - - @Override - public boolean isInfoEnabled() { - return delegate.isInfoEnabled(); - } - - @Override - public void info(String msg) { - delegate.info(msg); - } - - @Override - public void info(String format, Object arg) { - delegate.info(format, arg); - } - - @Override - public void info(String format, Object arg1, Object arg2) { - delegate.info(format, arg1, arg2); - } - - @Override - public void info(String format, Object... arguments) { - delegate.info(format, arguments); - } - - @Override - public void info(String msg, Throwable t) { - delegate.info(msg, t); - } - - @Override - public boolean isInfoEnabled(Marker marker) { - return delegate.isInfoEnabled(marker); - } - - @Override - public void info(Marker marker, String msg) { - delegate.info(marker, msg); - } - - @Override - public void info(Marker marker, String format, Object arg) { - delegate.info(marker, format, arg); - } - - @Override - public void info(Marker marker, String format, Object arg1, Object arg2) { - delegate.info(marker, format, arg1, arg2); - } - - @Override - public void info(Marker marker, String format, Object... arguments) { - delegate.info(marker, format, arguments); - } - - @Override - public void info(Marker marker, String msg, Throwable t) { - delegate.info(marker, msg, t); - } - - @CheckReturnValue - @Override - public LoggingEventBuilder atInfo() { - return delegate.atInfo(); - } - - @Override - public boolean isWarnEnabled() { - return delegate.isWarnEnabled(); - } - - @Override - public void warn(String msg) { - delegate.warn(msg); - } - - @Override - public void warn(String format, Object arg) { - delegate.warn(format, arg); - } - - @Override - public void warn(String format, Object... arguments) { - delegate.warn(format, arguments); - } - - @Override - public void warn(String format, Object arg1, Object arg2) { - delegate.warn(format, arg1, arg2); - } - - @Override - public void warn(String msg, Throwable t) { - delegate.warn(msg, t); - } - - @Override - public boolean isWarnEnabled(Marker marker) { - return delegate.isWarnEnabled(marker); - } - - @Override - public void warn(Marker marker, String msg) { - delegate.warn(marker, msg); - } - - @Override - public void warn(Marker marker, String format, Object arg) { - delegate.warn(marker, format, arg); - } - - @Override - public void warn(Marker marker, String format, Object arg1, Object arg2) { - delegate.warn(marker, format, arg1, arg2); - } - - @Override - public void warn(Marker marker, String format, Object... arguments) { - delegate.warn(marker, format, arguments); - } - - @Override - public void warn(Marker marker, String msg, Throwable t) { - delegate.warn(marker, msg, t); - } - - @CheckReturnValue - @Override - public LoggingEventBuilder atWarn() { - return delegate.atWarn(); - } - - @Override - public boolean isErrorEnabled() { - return delegate.isErrorEnabled(); - } - - @Override - public void error(String msg) { - delegate.error(msg); - } - - @Override - public void error(String format, Object arg) { - delegate.error(format, arg); - } - - @Override - public void error(String format, Object arg1, Object arg2) { - delegate.error(format, arg1, arg2); - } - - @Override - public void error(String format, Object... arguments) { - delegate.error(format, arguments); - } - - @Override - public void error(String msg, Throwable t) { - delegate.error(msg, t); - } - - @Override - public boolean isErrorEnabled(Marker marker) { - return delegate.isErrorEnabled(marker); - } - - @Override - public void error(Marker marker, String msg) { - delegate.error(marker, msg); - } - - @Override - public void error(Marker marker, String format, Object arg) { - delegate.error(marker, format, arg); - } - - @Override - public void error(Marker marker, String format, Object arg1, Object arg2) { - delegate.error(marker, format, arg1, arg2); - } - - @Override - public void error(Marker marker, String format, Object... arguments) { - delegate.error(marker, format, arguments); - } - - @Override - public void error(Marker marker, String msg, Throwable t) { - delegate.error(marker, msg, t); - } - - @CheckReturnValue - @Override - public LoggingEventBuilder atError() { - return delegate.atError(); - } -} diff --git a/sdk-server-common/src/test/resources/logback.xml b/sdk-server-common/src/test/resources/logback.xml new file mode 100644 index 000000000..08ac961d8 --- /dev/null +++ b/sdk-server-common/src/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n + + + + + + + + From b6712e8a174f592c54bd49d64132adbb06a50ad4 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 4 Jul 2025 12:09:53 +0100 Subject: [PATCH 021/493] fix: remove printlns and use logging (#159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../apps/jakarta/A2AServerResource.java | 19 +++++++++----- .../java/io/a2a/server/events/EventQueue.java | 25 +++++++++---------- .../DefaultRequestHandler.java | 24 +++++++++--------- .../io/a2a/server/tasks/ResultAggregator.java | 4 --- .../requesthandlers/JSONRPCHandlerTest.java | 2 +- .../a2a/server/util/async/AsyncUtilsTest.java | 2 -- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java index d08e5a2ab..1e4db8c39 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java @@ -20,9 +20,9 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; - import io.a2a.server.ExtendedAgentCard; import io.a2a.server.requesthandlers.JSONRPCHandler; +import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.GetTaskPushNotificationConfigRequest; @@ -46,11 +46,14 @@ import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.server.util.async.Internal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Path("/") public class A2AServerResource { + private static final Logger LOGGER = LoggerFactory.getLogger(A2AServerResource.class); + @Inject JSONRPCHandler jsonRpcHandler; @@ -76,7 +79,12 @@ public class A2AServerResource { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public JSONRPCResponse handleNonStreamingRequests(NonStreamingJSONRPCRequest request) { - return processNonStreamingRequest(request); + LOGGER.debug("Handling non-streaming request"); + try { + return processNonStreamingRequest(request); + } finally { + LOGGER.debug("Completed non-streaming request"); + } } /** @@ -87,9 +95,9 @@ public JSONRPCResponse handleNonStreamingRequests(NonStreamingJSONRPCRequest< @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.SERVER_SENT_EVENTS) public void handleStreamingRequests(StreamingJSONRPCRequest request, @Context SseEventSink sseEventSink, @Context Sse sse) { - System.out.println("=====> Streaming"); + LOGGER.debug("Handling streaming request"); executor.execute(() -> processStreamingRequest(request, sseEventSink, sse)); - System.out.println("=====> Streaming - done"); + LOGGER.debug("Submitted streaming request for async processing"); } /** @@ -167,7 +175,6 @@ private void handleStreamingResponse(Flow.Publisher public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; subscription.request(Long.MAX_VALUE); - System.out.println("SUBSCRIBING!"); // Notify tests that we are subscribed Runnable runnable = streamingIsSubscribedRunnable; if (runnable != null) { diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java index 8f689312f..6a41901ca 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -1,7 +1,6 @@ package io.a2a.server.events; import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -16,7 +15,7 @@ public abstract class EventQueue implements AutoCloseable { - private static final Logger log = LoggerFactory.getLogger(EventQueue.class); + private static final Logger LOGGER = LoggerFactory.getLogger(EventQueue.class); // TODO decide on a capacity private static final int queueSize = 1000; @@ -34,7 +33,7 @@ protected EventQueue() { } protected EventQueue(EventQueue parent) { - log.trace("Creating {}, parent: {}", this, parent); + LOGGER.trace("Creating {}, parent: {}", this, parent); this.parent = parent; } @@ -48,7 +47,7 @@ public static EventQueue create() { public void enqueueEvent(Event event) { if (closed) { - log.warn("Queue is closed. Event will not be enqueued. {} {}", this, event); + LOGGER.warn("Queue is closed. Event will not be enqueued. {} {}", this, event); return; } // Call toString() since for errors we don't really want the full stacktrace @@ -59,14 +58,14 @@ public void enqueueEvent(Event event) { throw new RuntimeException("Unable to acquire the semaphore to enqueue the event", e); } queue.add(event); - log.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); + LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); } abstract EventQueue tap(); public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException { if (closed && queue.isEmpty()) { - log.debug("Queue is closed, and empty. Sending termination message. {}", this); + LOGGER.debug("Queue is closed, and empty. Sending termination message. {}", this); throw new EventQueueClosedException(); } try { @@ -74,7 +73,7 @@ public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException Event event = queue.poll(); if (event != null) { // Call toString() since for errors we don't really want the full stacktrace - log.debug("Dequeued event (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); + LOGGER.debug("Dequeued event (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); semaphore.release(); } return event; @@ -83,12 +82,12 @@ public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException Event event = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); if (event != null) { // Call toString() since for errors we don't really want the full stacktrace - log.debug("Dequeued event (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); + LOGGER.debug("Dequeued event (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); semaphore.release(); } return event; } catch (InterruptedException e) { - log.debug("Interrupted dequeue (waiting) {}", this); + LOGGER.debug("Interrupted dequeue (waiting) {}", this); Thread.currentThread().interrupt(); return null; } @@ -108,7 +107,7 @@ public void doClose() { if (closed) { return; } - log.debug("Closing {}", this); + LOGGER.debug("Closing {}", this); closed = true; } // Although the Python implementation drains the queue on closing, @@ -137,9 +136,9 @@ public void enqueueEvent(Event event) { @Override public void awaitQueuePollerStart() throws InterruptedException { - log.debug("Waiting for queue poller to start on {}", this); + LOGGER.debug("Waiting for queue poller to start on {}", this); pollingStartedLatch.await(10, TimeUnit.SECONDS); - log.debug("Queue poller started on {}", this); + LOGGER.debug("Queue poller started on {}", this); } @Override @@ -147,7 +146,7 @@ void signalQueuePollerStarted() { if (pollingStarted.get()) { return; } - log.debug("Signalling that queue polling started {}", this); + LOGGER.debug("Signalling that queue polling started {}", this); pollingStartedLatch.countDown(); pollingStarted.set(true); } diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index e2c876d1d..1ca4ca158 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -52,7 +52,7 @@ @ApplicationScoped public class DefaultRequestHandler implements RequestHandler { - private static final Logger log = LoggerFactory.getLogger(DefaultRequestHandler.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRequestHandler.class); private final AgentExecutor agentExecutor; private final TaskStore taskStore; @@ -81,10 +81,10 @@ public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, @Override public Task onGetTask(TaskQueryParams params) throws JSONRPCError { - log.debug("onGetTask {}", params.id()); + LOGGER.debug("onGetTask {}", params.id()); Task task = taskStore.get(params.id()); if (task == null) { - log.debug("No task found for {}. Throwing TaskNotFoundError", params.id()); + LOGGER.debug("No task found for {}. Throwing TaskNotFoundError", params.id()); throw new TaskNotFoundError(); } if (params.historyLength() != null && task.getHistory() != null && params.historyLength() < task.getHistory().size()) { @@ -102,7 +102,7 @@ public Task onGetTask(TaskQueryParams params) throws JSONRPCError { .build(); } - log.debug("Task found {}", task); + LOGGER.debug("Task found {}", task); return task; } @@ -146,11 +146,11 @@ public Task onCancelTask(TaskIdParams params) throws JSONRPCError { @Override public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { - log.debug("onMessageSend - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); + LOGGER.debug("onMessageSend - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); MessageSendSetup mss = initMessageSend(params); String taskId = mss.requestContext.getTaskId(); - log.debug("Request context taskId: {}", taskId); + LOGGER.debug("Request context taskId: {}", taskId); EventQueue queue = queueManager.createOrTap(taskId); ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); @@ -168,11 +168,11 @@ public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); if (etai == null) { - log.debug("No result, throwing InternalError"); + LOGGER.debug("No result, throwing InternalError"); throw new InternalError("No result"); } interrupted = etai.interrupted(); - log.debug("Was interrupted: {}", interrupted); + LOGGER.debug("Was interrupted: {}", interrupted); EventKind kind = etai.eventType(); if (kind instanceof Task taskResult && !taskId.equals(taskResult.getId())) { @@ -188,13 +188,13 @@ public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { } } - log.debug("Returning: {}", etai.eventType()); + LOGGER.debug("Returning: {}", etai.eventType()); return etai.eventType(); } @Override public Flow.Publisher onMessageSendStream(MessageSendParams params) throws JSONRPCError { - log.debug("onMessageSendStream - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); + LOGGER.debug("onMessageSendStream - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); MessageSendSetup mss = initMessageSend(params); AtomicReference taskId = new AtomicReference<>(mss.requestContext.getTaskId()); @@ -352,11 +352,11 @@ private MessageSendSetup initMessageSend(MessageSendParams params) { Task task = taskManager.getTask(); if (task != null) { - log.debug("Found task updating with message {}", params.message()); + LOGGER.debug("Found task updating with message {}", params.message()); task = taskManager.updateWithMessage(params.message(), task); if (shouldAddPushInfo(params)) { - log.debug("Adding push info"); + LOGGER.debug("Adding push info"); pushNotifier.setInfo(task.getId(), params.configuration().pushNotification()); } } diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index c04c4e829..5211d1deb 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -18,12 +18,8 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.util.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class ResultAggregator { - private static final Logger log = LoggerFactory.getLogger(ResultAggregator.class); - private final TaskManager taskManager; private volatile Message message; diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index 01498df89..678b1bbd2 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -730,6 +730,7 @@ public void onComplete() { // TODO - this is very strange. The results array is synchronized, and the latch is counted down // AFTER adding items to the list. Still, I am seeing intermittently, but frequently that // the results list only has two items. + // Remove System.out.printlns in this test when done. long end = System.currentTimeMillis() + 5000; while (results.size() != 3 && System.currentTimeMillis() < end) { Thread.sleep(1000); @@ -1105,7 +1106,6 @@ public void testOnMessageSendInternalError() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); SendMessageResponse response = handler.onMessageSend(request); - System.out.println(response); assertInstanceOf(InternalError.class, response.getError()); } diff --git a/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java b/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java index 2bbd0a9fc..a1ac722b9 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java @@ -591,7 +591,6 @@ public void testAsyncUtilsErrorPropagation() { Flow.Publisher processor = processor(createTubeConfig(), source, new BiFunction, String, Boolean>() { @Override public Boolean apply(Consumer throwableConsumer, String item) { - System.out.println("-> (1) " + item); if (item.equals("c")) { throw new IllegalStateException(); } @@ -688,7 +687,6 @@ public void onComplete() { } }); - System.out.println("---hi"); latch.await(2, TimeUnit.SECONDS); assertEquals(6, results.size()); } From 884dfdb1cea506a665eb7d5e67d2a3e46c745732 Mon Sep 17 00:00:00 2001 From: Sun Yuhan <20242118+sunyuhan1998@users.noreply.github.com> Date: Fri, 4 Jul 2025 21:05:23 +0800 Subject: [PATCH 022/493] fix: Fixed the intermittent failure issue in `testOnMessageStreamNewMessageSendPushNotificationSuccess` (#161) The reason for its intermittent failure, I believe, is as follows: We initially set up the `CountDownLatch` with a count of 6, and we will call `latch.countDown()` at the following three locations: - A. in `onSubscribe` - B. in the `finally` block of the `post` method in `HttpClient` - C. in `onNext` Ideally, when the test method runs, the sequence of `countDown()` calls should be like this: **A B C B C B C** The `countDown()` function was called a total of 7 times! This explains why we sometimes see only **2 elements** in `results` when we do `assertEquals(3, results.size())`. It's because the last `onNext` may not have completed execution yet. **What this PR does:** We could fix this by initializing the `CountDownLatch` to 7. However, considering the purpose of this test method, I chose instead to keep the count at 6 and **remove the `latch.countDown()` call in `onSubscribe`**. Fixes #140 Signed-off-by: Sun Yuhan Co-authored-by: Sun Yuhan --- .../server/requesthandlers/JSONRPCHandlerTest.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index 678b1bbd2..d394db2bb 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -700,7 +700,6 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex public void onSubscribe(Flow.Subscription subscription) { subscriptionRef.set(subscription); subscription.request(1); - latch.countDown(); } @Override @@ -726,16 +725,6 @@ public void onComplete() { assertTrue(latch.await(5, TimeUnit.SECONDS)); subscriptionRef.get().cancel(); - if (results.size() != 3) { - // TODO - this is very strange. The results array is synchronized, and the latch is counted down - // AFTER adding items to the list. Still, I am seeing intermittently, but frequently that - // the results list only has two items. - // Remove System.out.printlns in this test when done. - long end = System.currentTimeMillis() + 5000; - while (results.size() != 3 && System.currentTimeMillis() < end) { - Thread.sleep(1000); - } - } assertEquals(3, results.size()); assertEquals(3, httpClient.tasks.size()); From f2dae4e8bad182c8ee5d70b42d5348057809b2db Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Sat, 5 Jul 2025 00:47:40 +0900 Subject: [PATCH 023/493] docs: fix link to use official repository (#160) This PR updates the example link to point to the official repository. Before: https://github.com/fjuma/... After: https://github.com/a2aproject/... Signed-off-by: jonghoonpark --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11bcfb210..4e58db1d3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ mvn clean install ## Examples -You can find an example of how to use the A2A Java SDK [here](https://github.com/fjuma/a2a-samples/tree/java-sdk-example/samples/multi_language/python_and_java_multiagent/weather_agent). +You can find an example of how to use the A2A Java SDK in the [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent/weather_agent). More examples will be added soon. From a31d73a015ba6401156c8f15d4afcbc9a31a0b4f Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Mon, 7 Jul 2025 22:08:26 +0900 Subject: [PATCH 024/493] feat: Add a2a-tck runs to CI for PRs (GH-152) (#162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Added the [a2a-tck](https://github.com/maeste/a2a-tck) test to the GitHub Actions workflow of the `a2a-java` project. Tested locally using the following commands. ``` act -P ubuntu-latest=quay.io/jamezp/act-maven -W '.github/workflows/tck-test.yml' ``` Fixes #152 🦕 --------- Signed-off-by: jonghoonpark Co-authored-by: Kabir Khan --- .github/workflows/build-and-test.yml | 2 +- .github/workflows/run-tck.yml | 94 ++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/run-tck.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b16350da6..9dec62a96 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -21,5 +21,5 @@ jobs: java-version: '17' distribution: 'temurin' cache: maven - - name: Build with Maven + - name: Build with Maven and run tests run: mvn -B package --file pom.xml -fae diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml new file mode 100644 index 000000000..726f2c29b --- /dev/null +++ b/.github/workflows/run-tck.yml @@ -0,0 +1,94 @@ +name: Run TCK + +on: + # Handle all branches for now + push: + branches: + - main + pull_request: + branches: + - main + +env: + # Tag of the TCK + TCK_VERSION: 0.2.3 + # Tells uv to not need a venv, and instead use system + UV_SYSTEM_PYTHON: 1 + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + tck-test: + runs-on: ubuntu-latest + steps: + - name: Checkout a2a-java + uses: actions/checkout@v4 + - name: Checkout a2a-tck + uses: actions/checkout@v4 + with: + repository: maeste/a2a-tck + path: tck/a2a-tck + ref: ${{ env.TCK_VERSION }} + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: check java_home + run: echo $JAVA_HOME + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: "tck/a2a-tck/pyproject.toml" + - name: Install uv and Python dependencies + run: | + pip install uv + uv pip install -e . + working-directory: tck/a2a-tck + - name: Build with Maven, skipping tests + run: mvn -B install -DskipTests + - name: Start SUT + run: mvn -B quarkus:dev & + working-directory: tck + - name: Wait for SUT to start + run: | + URL="http://localhost:9999/.well-known/agent.json" + EXPECTED_STATUS=200 + TIMEOUT=120 + RETRY_INTERVAL=2 + START_TIME=$(date +%s) + + while true; do + # Calculate elapsed time + CURRENT_TIME=$(date +%s) + ELAPSED_TIME=$((CURRENT_TIME - START_TIME)) + + # Check for timeout + if [ "$ELAPSED_TIME" -ge "$TIMEOUT" ]; then + echo "❌ Timeout: Server did not respond with status $EXPECTED_STATUS within $TIMEOUT seconds." + exit 1 + fi + + # Get HTTP status code. || true is to reporting a failure to connect as an error + HTTP_STATUS=$(curl --output /dev/null --silent --write-out "%{http_code}" "$URL") || true + echo "STATUS: ${HTTP_STATUS}" + + # Check if we got the correct status code + if [ "$HTTP_STATUS" -eq "$EXPECTED_STATUS" ]; then + echo "✅ Server is up! Received status $HTTP_STATUS after $ELAPSED_TIME seconds." + break; + fi + + # Wait before retrying + echo "⏳ Server not ready (status: $HTTP_STATUS). Retrying in $RETRY_INTERVAL seconds..." + sleep "$RETRY_INTERVAL" + done + + - name: Run TCK + run: | + ./run_tck.py --sut-url http://localhost:9999 --category all --compliance-report report.json + working-directory: tck/a2a-tck From be11861d1fc7ac151b31126c4106181cc287ac4e Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 7 Jul 2025 14:40:49 +0100 Subject: [PATCH 025/493] chore: move sdk-jakarta to use WildFly as the server (#166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Although quarkus-resteasy uses non-reactive RestEasy, Quarkus's CDi container is more lenient than the spec. ------ Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Signed-off-by: Emmanuel Hugonnet Co-authored-by: Emmanuel Hugonnet --- core/src/main/resources/META-INF/beans.xml | 6 + sdk-jakarta/pom.xml | 166 ++++++++++++++++-- .../server/apps/jakarta/A2ARequestFilter.java | 6 + .../src/main/resources/META-INF/beans.xml | 2 +- sdk-jakarta/src/scripts/configure_logger.cli | 2 + .../apps/jakarta/JakartaA2AServerTest.java | 68 ++++++- .../server/apps/jakarta/RestApplication.java | 8 + .../src/test/resources/WEB-INF/web.xml | 12 ++ sdk-jakarta/src/test/resources/arquillian.xml | 19 ++ .../src/test/resources/logging.properties | 29 +++ .../apps/quarkus/QuarkusA2AServerTest.java | 4 + .../java/io/a2a/server/events/EventQueue.java | 4 +- .../apps/common/AbstractA2AServerTest.java | 12 +- .../src/test/resources/META-INF/beans.xml | 6 + 14 files changed, 321 insertions(+), 23 deletions(-) create mode 100644 sdk-jakarta/src/scripts/configure_logger.cli create mode 100644 sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java create mode 100644 sdk-jakarta/src/test/resources/WEB-INF/web.xml create mode 100644 sdk-jakarta/src/test/resources/arquillian.xml create mode 100644 sdk-jakarta/src/test/resources/logging.properties diff --git a/core/src/main/resources/META-INF/beans.xml b/core/src/main/resources/META-INF/beans.xml index e69de29bb..9dfae34df 100644 --- a/core/src/main/resources/META-INF/beans.xml +++ b/core/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index e32c6511d..91e6155b4 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -15,7 +15,43 @@ Java A2A SDK for Jakarta Java SDK for the Agent2Agent Protocol (A2A) - SDK - Jakarta - + + 5.1.0.Beta11 + 1.10.0.Final + + 1.2.6 + 10.0.0.Final + 3.3.4 + ${project.build.directory}${file.separator}wildfly + + 8787 + + + + + org.jboss.shrinkwrap + shrinkwrap-bom + ${version.org.jboss.shrinkwrap.shrinkwrap} + pom + import + + + org.jboss.arquillian + arquillian-bom + ${version.org.jboss.arquillian} + pom + import + + + org.wildfly.arquillian + wildfly-arquillian-bom + ${version.org.wildfly.arquillian} + pom + import + + + ${project.groupId} @@ -65,40 +101,142 @@ jakarta.ws.rs-api provided - + com.fasterxml.jackson.datatype jackson-datatype-jsr310 test - io.quarkus - quarkus-junit5 + io.rest-assured + rest-assured test - io.quarkus - quarkus-resteasy-jackson + org.jboss.arquillian.junit5 + arquillian-junit5-container test - io.quarkus - quarkus-resteasy-client-jackson + org.wildfly.arquillian + wildfly-arquillian-container-managed test - - org.jboss.resteasy - resteasy-client + + org.jboss.shrinkwrap + shrinkwrap-api test org.junit.jupiter - junit-jupiter-api + junit-jupiter test - io.rest-assured - rest-assured + org.jboss.threads + jboss-threads + 3.9.1 + test + + + org.hamcrest + hamcrest test + + + + org.wildfly.glow + wildfly-glow-arquillian-plugin + 1.4.1.Final + + + + org.wildfly + wildfly-galleon-pack + 36.0.1.Final + + + standalone.xml + + + + scan + + scan + + test-compile + + + + + org.wildfly.plugins + wildfly-maven-plugin + 5.1.3.Final + + ${project.build.directory}/glow-scan/provisioning.xml + ${jboss.home} + ${jboss.home} + + + + + + + + + + + test-provisioning + + package + + test-compile + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.3 + + + ${jboss.home} + arquillian.xml + ${arquillian.java.vm.args} + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.8.1 + + ${project.build.directory}/lib + + + + copy + generate-test-resources + + copy-dependencies + + + test + provided + + + + + + + + + debug.profile + debug + + -agentlib:jdwp=transport=dt_socket,address=*:${server.debug.port},server=y,suspend=y + + + \ No newline at end of file diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java index 68fbb5585..59e332109 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java @@ -18,9 +18,13 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.ext.Provider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + @Provider @PreMatching public class A2ARequestFilter implements ContainerRequestFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(A2ARequestFilter.class); @Override public void filter(ContainerRequestContext requestContext) { @@ -31,8 +35,10 @@ public void filter(ContainerRequestContext requestContext) { // ensure the request is treated as a streaming request or a non-streaming request // based on the method in the request body if (isStreamingRequest(requestBody)) { + LOGGER.debug("Handling request as streaming: {}", requestBody); putAcceptHeader(requestContext, MediaType.SERVER_SENT_EVENTS); } else if (isNonStreamingRequest(requestBody)) { + LOGGER.debug("Handling request as non-streaming: {}", requestBody); putAcceptHeader(requestContext, MediaType.APPLICATION_JSON); } // reset the entity stream diff --git a/sdk-jakarta/src/main/resources/META-INF/beans.xml b/sdk-jakarta/src/main/resources/META-INF/beans.xml index 9b2940fc2..9dfae34df 100644 --- a/sdk-jakarta/src/main/resources/META-INF/beans.xml +++ b/sdk-jakarta/src/main/resources/META-INF/beans.xml @@ -2,5 +2,5 @@ + bean-discovery-mode="all"> \ No newline at end of file diff --git a/sdk-jakarta/src/scripts/configure_logger.cli b/sdk-jakarta/src/scripts/configure_logger.cli new file mode 100644 index 000000000..a45fb245d --- /dev/null +++ b/sdk-jakarta/src/scripts/configure_logger.cli @@ -0,0 +1,2 @@ +/subsystem=logging/logger=org.jboss.weld:add(level=DEBUG) +/subsystem=logging/logger=io.a2a:add(level=DEBUG) \ No newline at end of file diff --git a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java index 06ad61c9a..159ac79bc 100644 --- a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java +++ b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java @@ -1,14 +1,76 @@ package io.a2a.server.apps.jakarta; -import jakarta.inject.Inject; + import io.a2a.server.apps.common.AbstractA2AServerTest; +import io.a2a.server.apps.common.AgentCardProducer; +import io.a2a.server.apps.common.AgentExecutorProducer; import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.tasks.TaskStore; -import io.quarkus.test.junit.QuarkusTest; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; -@QuarkusTest +@ArquillianTest +@ApplicationScoped public class JakartaA2AServerTest extends AbstractA2AServerTest { + + public JakartaA2AServerTest() { + super(8080); + } + + @Deployment + public static WebArchive createTestArchive() throws IOException { + final List prefixes = List.of( + "a2a-java-sdk-core", + "a2a-java-sdk-server-common", + "jackson", + "mutiny", + "slf4j", + "rest-assured", + "groovy", + "http", + "commons", + "xml-path", + "json-path", + "hamcrest" + ); + List libraries = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(Paths.get("target").resolve("lib"))) { + for (Path file : stream) { + String fileName = file.getFileName().toString(); + if (prefixes.stream().anyMatch(fileName::startsWith)) { + libraries.add(file.toFile()); + } + } + } + WebArchive archive = ShrinkWrap.create(WebArchive.class, "ROOT.war") + .addAsLibraries(libraries.toArray(new File[libraries.size()])) + .addClass(AbstractA2AServerTest.class) + .addClass(AgentCardProducer.class) + .addClass(AgentExecutorProducer.class) + .addClass(JakartaA2AServerTest.class) + .addClass(A2ARequestFilter.class) + .addClass(A2AServerResource.class) + .addClass(RestApplication.class) + .addAsManifestResource("META-INF/beans.xml", "beans.xml") + .addAsWebInfResource("META-INF/beans.xml", "beans.xml") + .addAsWebInfResource("WEB-INF/web.xml", "web.xml"); + return archive; + } + @Inject TaskStore taskStore; diff --git a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java new file mode 100644 index 000000000..e2fcf6af3 --- /dev/null +++ b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java @@ -0,0 +1,8 @@ +package io.a2a.server.apps.jakarta; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/") +public class RestApplication extends Application { +} diff --git a/sdk-jakarta/src/test/resources/WEB-INF/web.xml b/sdk-jakarta/src/test/resources/WEB-INF/web.xml new file mode 100644 index 000000000..2678fbc83 --- /dev/null +++ b/sdk-jakarta/src/test/resources/WEB-INF/web.xml @@ -0,0 +1,12 @@ + + + + + 30 + + + diff --git a/sdk-jakarta/src/test/resources/arquillian.xml b/sdk-jakarta/src/test/resources/arquillian.xml new file mode 100644 index 000000000..804afef5d --- /dev/null +++ b/sdk-jakarta/src/test/resources/arquillian.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + ${basedir}/target/wildfly + + ${arquillian.java.vm.args} + true + + + + diff --git a/sdk-jakarta/src/test/resources/logging.properties b/sdk-jakarta/src/test/resources/logging.properties new file mode 100644 index 000000000..17885c831 --- /dev/null +++ b/sdk-jakarta/src/test/resources/logging.properties @@ -0,0 +1,29 @@ +# +# Copyright The WildFly Authors +# SPDX-License-Identifier: Apache-2.0 +# + +# Additional logger names to configure (root logger is always configured) +loggers=sun.rmi,org.jboss.shrinkwrap,org.apache.http.wire +logger.org.jboss.shrinkwrap.level=INFO +logger.sun.rmi.level=WARNING +logger.org.apache.http.wire.level=WARN + +# Root logger level +logger.level=WARN + +# Root logger handlers +logger.handlers=FILE + +# File handler configuration +handler.FILE=org.jboss.logmanager.handlers.FileHandler +handler.FILE.properties=autoFlush,append,fileName +handler.FILE.autoFlush=true +handler.FILE.fileName=./target/test.log +handler.FILE.formatter=PATTERN +handler.FILE.append=true + +# Formatter pattern configuration +formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter +formatter.PATTERN.properties=pattern +formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n diff --git a/sdk-quarkus/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java b/sdk-quarkus/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java index 4dce72cee..dab1954ba 100644 --- a/sdk-quarkus/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java +++ b/sdk-quarkus/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java @@ -16,6 +16,10 @@ public class QuarkusA2AServerTest extends AbstractA2AServerTest { @Inject InMemoryQueueManager queueManager; + public QuarkusA2AServerTest() { + super(8081); + } + @Override protected TaskStore getTaskStore() { return taskStore; diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java index 6a41901ca..c73292a09 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -20,8 +20,6 @@ public abstract class EventQueue implements AutoCloseable { // TODO decide on a capacity private static final int queueSize = 1000; - private final EventQueue parent; - private final BlockingQueue queue = new LinkedBlockingDeque<>(); private final Semaphore semaphore = new Semaphore(queueSize, true); private volatile boolean closed = false; @@ -34,10 +32,10 @@ protected EventQueue() { protected EventQueue(EventQueue parent) { LOGGER.trace("Creating {}, parent: {}", this, parent); - this.parent = parent; } public static EventQueue create() { + return new MainQueue(); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index ac7bf5c5d..0992a157f 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -103,6 +103,12 @@ public abstract class AbstractA2AServerTest { .parts(new TextPart("test message")) .build(); + private final int serverPort; + + protected AbstractA2AServerTest(int serverPort) { + this.serverPort = serverPort; + } + @Test public void testGetTaskSuccess() { testGetTask(); @@ -222,7 +228,8 @@ public void testCancelTaskNotFound() { .then() .statusCode(200) .extract() - .as(CancelTaskResponse.class); + .as(CancelTaskResponse.class) + ; assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 @@ -853,7 +860,8 @@ private CompletableFuture>> initialiseStreamingReque // Create the request HttpRequest.Builder builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:8081/")) + .uri(URI.create("http://localhost:" + serverPort + + "/")) .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) .header("Content-Type", "application/json"); if (mediaType != null) { diff --git a/tests/server-common/src/test/resources/META-INF/beans.xml b/tests/server-common/src/test/resources/META-INF/beans.xml index e69de29bb..9dfae34df 100644 --- a/tests/server-common/src/test/resources/META-INF/beans.xml +++ b/tests/server-common/src/test/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file From 2a881cb8429c3296fe4c8660631f53610c767b8c Mon Sep 17 00:00:00 2001 From: Brassely David Date: Mon, 7 Jul 2025 18:22:03 +0200 Subject: [PATCH 026/493] chore: Re-organize sub-modules to extract the client part from core (#149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This PR is made for better organize the different sub-modules, essentially the core one by extracing the specification (the model) from the client, in a similar way as what as been done for the server part. The goal for this split is to make it easier to provide other client implementation in a near future. Although code as been spread across new modules, it does not impact the way it is used as the same packages as been kept. Therefore, it does not impact much the main readme - [X ] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [ X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [ X] Ensure the tests pass - [ X] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Co-authored-by: Kabir Khan --- README.md | 9 +- client/pom.xml | 45 +++++ .../src/main/java/io/a2a}/A2A.java | 30 +--- .../java/io/a2a/client/A2ACardResolver.java | 0 .../main/java/io/a2a/client/A2AClient.java | 46 ++--- .../io/a2a/client/sse/SSEEventListener.java | 0 .../main/java/io/a2a/http/A2AHttpClient.java | 0 .../java/io/a2a/http/A2AHttpResponse.java | 0 .../java/io/a2a/http/JdkA2AHttpClient.java | 0 client/src/main/resources/META-INF/beans.xml | 0 .../io/a2a/client/A2ACardResolverTest.java | 0 .../io/a2a/client/A2AClientStreamingTest.java | 0 .../java/io/a2a/client/A2AClientTest.java | 0 .../test/java/io/a2a/client/JsonMessages.java | 0 .../io/a2a/client/JsonStreamingMessages.java | 0 .../a2a/client/sse/SSEEventListenerTest.java | 0 common/pom.xml | 26 +++ .../src/main/java/io/a2a/util/Assert.java | 0 .../src/main/java/io/a2a/util/NotNull.java | 0 core/src/main/resources/META-INF/beans.xml | 6 - .../src/test/java/io/a2a/util/AssertTest.java | 100 ----------- core/src/test/java/io/a2a/util/UtilsTest.java | 169 ------------------ examples/helloworld/client/pom.xml | 2 +- .../examples/helloworld/HelloWorldClient.java | 2 +- examples/helloworld/pom.xml | 2 +- .../helloworld/AgentExecutorProducer.java | 2 +- pom.xml | 5 +- sdk-jakarta/pom.xml | 2 +- .../server/apps/jakarta/A2ARequestFilter.java | 29 ++- .../apps/jakarta/JakartaA2AServerTest.java | 4 +- sdk-quarkus/pom.xml | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 15 +- sdk-server-common/pom.xml | 7 +- .../io/a2a/server/events/EventConsumer.java | 1 - .../DefaultRequestHandler.java | 12 +- .../java/io/a2a/server/tasks/TaskManager.java | 2 +- .../agentexecution/RequestContextTest.java | 14 +- .../requesthandlers/JSONRPCHandlerTest.java | 1 + {core => spec}/pom.xml | 23 ++- .../main/java/io/a2a/spec/A2AClientError.java | 0 .../java/io/a2a/spec/A2AClientHTTPError.java | 0 .../java/io/a2a/spec/A2AClientJSONError.java | 0 .../src/main/java/io/a2a/spec/A2AError.java | 0 .../main/java/io/a2a/spec/A2AException.java | 0 .../java/io/a2a/spec/A2AServerException.java | 0 .../io/a2a/spec/APIKeySecurityScheme.java | 0 .../java/io/a2a/spec/AgentCapabilities.java | 0 .../src/main/java/io/a2a/spec/AgentCard.java | 0 .../main/java/io/a2a/spec/AgentExtension.java | 0 .../main/java/io/a2a/spec/AgentProvider.java | 0 .../src/main/java/io/a2a/spec/AgentSkill.java | 0 .../src/main/java/io/a2a/spec/Artifact.java | 0 .../java/io/a2a/spec/AuthenticationInfo.java | 0 .../a2a/spec/AuthorizationCodeOAuthFlow.java | 0 .../java/io/a2a/spec/CancelTaskRequest.java | 10 +- .../java/io/a2a/spec/CancelTaskResponse.java | 0 .../a2a/spec/ClientCredentialsOAuthFlow.java | 0 .../spec/ContentTypeNotSupportedError.java | 0 .../src/main/java/io/a2a/spec/DataPart.java | 0 .../src/main/java/io/a2a/spec/Event.java | 0 .../src/main/java/io/a2a/spec/EventKind.java | 0 .../main/java/io/a2a/spec/FileContent.java | 0 .../io/a2a/spec/FileContentDeserializer.java | 0 .../src/main/java/io/a2a/spec/FilePart.java | 0 .../main/java/io/a2a/spec/FileWithBytes.java | 0 .../main/java/io/a2a/spec/FileWithUri.java | 0 .../GetTaskPushNotificationConfigRequest.java | 18 +- ...GetTaskPushNotificationConfigResponse.java | 0 .../main/java/io/a2a/spec/GetTaskRequest.java | 8 +- .../java/io/a2a/spec/GetTaskResponse.java | 0 .../io/a2a/spec/HTTPAuthSecurityScheme.java | 0 .../io/a2a/spec/IdJsonMappingException.java | 0 .../java/io/a2a/spec/ImplicitOAuthFlow.java | 0 .../java/io/a2a/spec/IntegerJsonrpcId.java | 0 .../main/java/io/a2a/spec/InternalError.java | 0 .../a2a/spec/InvalidAgentResponseError.java | 0 .../java/io/a2a/spec/InvalidParamsError.java | 0 .../InvalidParamsJsonMappingException.java | 0 .../java/io/a2a/spec/InvalidRequestError.java | 0 .../java/io/a2a/spec/JSONErrorResponse.java | 0 .../main/java/io/a2a/spec/JSONParseError.java | 4 +- .../main/java/io/a2a/spec/JSONRPCError.java | 0 .../io/a2a/spec/JSONRPCErrorDeserializer.java | 0 .../io/a2a/spec/JSONRPCErrorResponse.java | 0 .../io/a2a/spec/JSONRPCErrorSerializer.java | 0 .../main/java/io/a2a/spec/JSONRPCMessage.java | 2 + .../main/java/io/a2a/spec/JSONRPCRequest.java | 1 - .../spec/JSONRPCRequestDeserializerBase.java | 39 ++-- .../java/io/a2a/spec/JSONRPCResponse.java | 1 - .../src/main/java/io/a2a/spec/JsonrpcId.java | 0 .../src/main/java/io/a2a/spec/Message.java | 0 .../io/a2a/spec/MessageSendConfiguration.java | 0 .../java/io/a2a/spec/MessageSendParams.java | 0 .../java/io/a2a/spec/MethodNotFoundError.java | 0 .../MethodNotFoundJsonMappingException.java | 0 .../a2a/spec/NonStreamingJSONRPCRequest.java | 0 ...onStreamingJSONRPCRequestDeserializer.java | 20 +-- .../io/a2a/spec/OAuth2SecurityScheme.java | 0 .../src/main/java/io/a2a/spec/OAuthFlows.java | 0 .../a2a/spec/OpenIdConnectSecurityScheme.java | 0 .../src/main/java/io/a2a/spec/Part.java | 0 .../java/io/a2a/spec/PasswordOAuthFlow.java | 0 .../PushNotificationAuthenticationInfo.java | 0 .../io/a2a/spec/PushNotificationConfig.java | 0 .../PushNotificationNotSupportedError.java | 0 .../main/java/io/a2a/spec/SecurityScheme.java | 0 .../java/io/a2a/spec/SendMessageRequest.java | 8 +- .../java/io/a2a/spec/SendMessageResponse.java | 2 - .../a2a/spec/SendStreamingMessageRequest.java | 15 +- .../spec/SendStreamingMessageResponse.java | 2 - .../SetTaskPushNotificationConfigRequest.java | 10 +- ...SetTaskPushNotificationConfigResponse.java | 0 .../java/io/a2a/spec/StreamingEventKind.java | 0 .../io/a2a/spec/StreamingJSONRPCRequest.java | 0 .../StreamingJSONRPCRequestDeserializer.java | 7 +- .../java/io/a2a/spec/StringJsonrpcId.java | 0 .../src/main/java/io/a2a/spec/Task.java | 0 .../io/a2a/spec/TaskArtifactUpdateEvent.java | 0 .../main/java/io/a2a/spec/TaskIdParams.java | 0 .../io/a2a/spec/TaskNotCancelableError.java | 0 .../java/io/a2a/spec/TaskNotFoundError.java | 4 +- .../a2a/spec/TaskPushNotificationConfig.java | 0 .../java/io/a2a/spec/TaskQueryParams.java | 0 .../a2a/spec/TaskResubscriptionRequest.java | 15 +- .../src/main/java/io/a2a/spec/TaskState.java | 0 .../src/main/java/io/a2a/spec/TaskStatus.java | 0 .../io/a2a/spec/TaskStatusUpdateEvent.java | 0 .../src/main/java/io/a2a/spec/TextPart.java | 0 .../a2a/spec/UnsupportedOperationError.java | 0 .../src/main/java/io/a2a/util/Utils.java | 0 spec/src/main/resources/META-INF/beans.xml | 0 .../spec/JSONRPCErrorSerializationTest.java | 0 tck/pom.xml | 5 + tests/server-common/pom.xml | 7 +- 134 files changed, 254 insertions(+), 480 deletions(-) create mode 100644 client/pom.xml rename {core/src/main/java/io/a2a/spec => client/src/main/java/io/a2a}/A2A.java (79%) rename {core => client}/src/main/java/io/a2a/client/A2ACardResolver.java (100%) rename {core => client}/src/main/java/io/a2a/client/A2AClient.java (94%) rename {core => client}/src/main/java/io/a2a/client/sse/SSEEventListener.java (100%) rename {core => client}/src/main/java/io/a2a/http/A2AHttpClient.java (100%) rename {core => client}/src/main/java/io/a2a/http/A2AHttpResponse.java (100%) rename {core => client}/src/main/java/io/a2a/http/JdkA2AHttpClient.java (100%) create mode 100644 client/src/main/resources/META-INF/beans.xml rename {core => client}/src/test/java/io/a2a/client/A2ACardResolverTest.java (100%) rename {core => client}/src/test/java/io/a2a/client/A2AClientStreamingTest.java (100%) rename {core => client}/src/test/java/io/a2a/client/A2AClientTest.java (100%) rename {core => client}/src/test/java/io/a2a/client/JsonMessages.java (100%) rename {core => client}/src/test/java/io/a2a/client/JsonStreamingMessages.java (100%) rename {core => client}/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java (100%) create mode 100644 common/pom.xml rename {core => common}/src/main/java/io/a2a/util/Assert.java (100%) rename {core => common}/src/main/java/io/a2a/util/NotNull.java (100%) delete mode 100644 core/src/main/resources/META-INF/beans.xml delete mode 100644 core/src/test/java/io/a2a/util/AssertTest.java delete mode 100644 core/src/test/java/io/a2a/util/UtilsTest.java rename {core => spec}/pom.xml (69%) rename {core => spec}/src/main/java/io/a2a/spec/A2AClientError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/A2AClientHTTPError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/A2AClientJSONError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/A2AError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/A2AException.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/A2AServerException.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/APIKeySecurityScheme.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AgentCapabilities.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AgentCard.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AgentExtension.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AgentProvider.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AgentSkill.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/Artifact.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AuthenticationInfo.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/CancelTaskRequest.java (90%) rename {core => spec}/src/main/java/io/a2a/spec/CancelTaskResponse.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/DataPart.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/Event.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/EventKind.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/FileContent.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/FileContentDeserializer.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/FilePart.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/FileWithBytes.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/FileWithUri.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java (86%) rename {core => spec}/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/GetTaskRequest.java (92%) rename {core => spec}/src/main/java/io/a2a/spec/GetTaskResponse.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/IdJsonMappingException.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/IntegerJsonrpcId.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/InternalError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/InvalidAgentResponseError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/InvalidParamsError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/InvalidRequestError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONErrorResponse.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONParseError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCMessage.java (83%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCRequest.java (96%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java (82%) rename {core => spec}/src/main/java/io/a2a/spec/JSONRPCResponse.java (97%) rename {core => spec}/src/main/java/io/a2a/spec/JsonrpcId.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/Message.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/MessageSendConfiguration.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/MessageSendParams.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/MethodNotFoundError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java (80%) rename {core => spec}/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/OAuthFlows.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/Part.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/PasswordOAuthFlow.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/PushNotificationConfig.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/SecurityScheme.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/SendMessageRequest.java (92%) rename {core => spec}/src/main/java/io/a2a/spec/SendMessageResponse.java (96%) rename {core => spec}/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java (89%) rename {core => spec}/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java (96%) rename {core => spec}/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java (88%) rename {core => spec}/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/StreamingEventKind.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java (87%) rename {core => spec}/src/main/java/io/a2a/spec/StringJsonrpcId.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/Task.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskIdParams.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskNotCancelableError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskNotFoundError.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskQueryParams.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java (88%) rename {core => spec}/src/main/java/io/a2a/spec/TaskState.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskStatus.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/TextPart.java (100%) rename {core => spec}/src/main/java/io/a2a/spec/UnsupportedOperationError.java (100%) rename {core => spec}/src/main/java/io/a2a/util/Utils.java (100%) create mode 100644 spec/src/main/resources/META-INF/beans.xml rename {core => spec}/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java (100%) diff --git a/README.md b/README.md index 4e58db1d3..9339b08e5 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) - [Add a class that creates an A2A Agent Executor](#3-add-a-class-that-creates-an-a2a-agent-executor) - [Add an A2A Java SDK Server Maven dependency to your project](#4-add-an-a2a-java-sdk-server-maven-dependency-to-your-project) -### 1. Add the A2A Java SDK Core Maven dependency to your project +### 1. Add the A2A Java SDK Maven dependencies to your project > **Note**: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be > sure to check out the latest tag (you can see the tags [here](https://github.com/a2aproject/a2a-java/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. @@ -42,7 +42,12 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) ```xml io.a2a.sdk - a2a-java-sdk-core + a2a-java-sdk-client + 0.2.3 + + + io.a2a.sdk + a2a-java-sdk-server-common 0.2.3 ``` diff --git a/client/pom.xml b/client/pom.xml new file mode 100644 index 000000000..975fb2928 --- /dev/null +++ b/client/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + + io.a2a.sdk + a2a-java-sdk-parent + 0.2.4-SNAPSHOT + + a2a-java-sdk-client + + jar + + Java SDK A2A Client + Java SDK for the Agent2Agent Protocol (A2A) - Client + + + + ${project.groupId} + a2a-java-sdk-common + ${project.version} + + + + ${project.groupId} + a2a-java-sdk-spec + ${project.version} + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + + \ No newline at end of file diff --git a/core/src/main/java/io/a2a/spec/A2A.java b/client/src/main/java/io/a2a/A2A.java similarity index 79% rename from core/src/main/java/io/a2a/spec/A2A.java rename to client/src/main/java/io/a2a/A2A.java index 4f3d2381c..c8dca80e4 100644 --- a/core/src/main/java/io/a2a/spec/A2A.java +++ b/client/src/main/java/io/a2a/A2A.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a; import java.util.Collections; import java.util.Map; @@ -6,6 +6,11 @@ import io.a2a.client.A2ACardResolver; import io.a2a.http.A2AHttpClient; import io.a2a.http.JdkA2AHttpClient; +import io.a2a.spec.A2AClientError; +import io.a2a.spec.A2AClientJSONError; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Message; +import io.a2a.spec.TextPart; /** @@ -13,17 +18,6 @@ */ public class A2A { - public static final String CANCEL_TASK_METHOD = "tasks/cancel"; - public static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "tasks/pushNotificationConfig/get"; - public static final String GET_TASK_METHOD = "tasks/get"; - public static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "tasks/pushNotificationConfig/set"; - public static final String SEND_TASK_RESUBSCRIPTION_METHOD = "tasks/resubscribe"; - public static final String SEND_STREAMING_MESSAGE_METHOD = "message/stream"; - public static final String SEND_MESSAGE_METHOD = "message/send"; - - public static final String JSONRPC_VERSION = "2.0"; - - /** * Convert the given text to a user message. * @@ -133,16 +127,4 @@ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl, A2ACardResolver resolver = new A2ACardResolver(httpClient, agentUrl, relativeCardPath, authHeaders); return resolver.getAgentCard(); } - - protected static boolean isValidMethodName(String methodName) { - return methodName != null && (methodName.equals(CANCEL_TASK_METHOD) - || methodName.equals(GET_TASK_METHOD) - || methodName.equals(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD) - || methodName.equals(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD) - || methodName.equals(SEND_TASK_RESUBSCRIPTION_METHOD) - || methodName.equals(SEND_MESSAGE_METHOD) - || methodName.equals(SEND_STREAMING_MESSAGE_METHOD)); - - } - } diff --git a/core/src/main/java/io/a2a/client/A2ACardResolver.java b/client/src/main/java/io/a2a/client/A2ACardResolver.java similarity index 100% rename from core/src/main/java/io/a2a/client/A2ACardResolver.java rename to client/src/main/java/io/a2a/client/A2ACardResolver.java diff --git a/core/src/main/java/io/a2a/client/A2AClient.java b/client/src/main/java/io/a2a/client/A2AClient.java similarity index 94% rename from core/src/main/java/io/a2a/client/A2AClient.java rename to client/src/main/java/io/a2a/client/A2AClient.java index 3a35f0b67..ea08baea4 100644 --- a/core/src/main/java/io/a2a/client/A2AClient.java +++ b/client/src/main/java/io/a2a/client/A2AClient.java @@ -1,16 +1,6 @@ package io.a2a.client; -import static io.a2a.spec.A2A.CANCEL_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; -import static io.a2a.spec.A2A.JSONRPC_VERSION; -import static io.a2a.spec.A2A.SEND_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_STREAMING_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_TASK_RESUBSCRIPTION_METHOD; -import static io.a2a.spec.A2A.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; import static io.a2a.util.Assert.checkNotNullParam; -import static io.a2a.util.Utils.OBJECT_MAPPER; -import static io.a2a.util.Utils.unmarshalFrom; import java.io.IOException; import java.util.Map; @@ -24,7 +14,7 @@ import io.a2a.http.A2AHttpClient; import io.a2a.http.A2AHttpResponse; import io.a2a.http.JdkA2AHttpClient; -import io.a2a.spec.A2A; +import io.a2a.A2A; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.A2AServerException; @@ -36,6 +26,7 @@ import io.a2a.spec.GetTaskRequest; import io.a2a.spec.GetTaskResponse; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.JSONRPCMessage; import io.a2a.spec.JSONRPCResponse; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; @@ -49,6 +40,7 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskResubscriptionRequest; +import io.a2a.util.Utils; /** * An A2A client. @@ -158,8 +150,8 @@ public SendMessageResponse sendMessage(MessageSendParams messageSendParams) thro */ public SendMessageResponse sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException { SendMessageRequest.Builder sendMessageRequestBuilder = new SendMessageRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(SEND_MESSAGE_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(SendMessageRequest.METHOD) .params(messageSendParams); if (requestId != null) { @@ -210,8 +202,8 @@ public GetTaskResponse getTask(TaskQueryParams taskQueryParams) throws A2AServer */ public GetTaskResponse getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException { GetTaskRequest.Builder getTaskRequestBuilder = new GetTaskRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(GET_TASK_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetTaskRequest.METHOD) .params(taskQueryParams); if (requestId != null) { @@ -260,8 +252,8 @@ public CancelTaskResponse cancelTask(TaskIdParams taskIdParams) throws A2AServer */ public CancelTaskResponse cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException { CancelTaskRequest.Builder cancelTaskRequestBuilder = new CancelTaskRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(CANCEL_TASK_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(CancelTaskRequest.METHOD) .params(taskIdParams); if (requestId != null) { @@ -310,8 +302,8 @@ public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(TaskI */ public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String requestId, TaskIdParams taskIdParams) throws A2AServerException { GetTaskPushNotificationConfigRequest.Builder getTaskPushNotificationRequestBuilder = new GetTaskPushNotificationConfigRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetTaskPushNotificationConfigRequest.METHOD) .params(taskIdParams); if (requestId != null) { @@ -353,8 +345,8 @@ public SetTaskPushNotificationConfigResponse setTaskPushNotificationConfig(Strin public SetTaskPushNotificationConfigResponse setTaskPushNotificationConfig(String requestId, String taskId, PushNotificationConfig pushNotificationConfig) throws A2AServerException { SetTaskPushNotificationConfigRequest.Builder setTaskPushNotificationRequestBuilder = new SetTaskPushNotificationConfigRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(SetTaskPushNotificationConfigRequest.METHOD) .params(new TaskPushNotificationConfig(taskId, pushNotificationConfig)); if (requestId != null) { @@ -403,8 +395,8 @@ public void sendStreamingMessage(String requestId, MessageSendParams messageSend checkNotNullParam("failureHandler", failureHandler); SendStreamingMessageRequest.Builder sendStreamingMessageRequestBuilder = new SendStreamingMessageRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(SEND_STREAMING_MESSAGE_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(SendStreamingMessageRequest.METHOD) .params(messageSendParams); if (requestId != null) { @@ -462,8 +454,8 @@ public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consu checkNotNullParam("failureHandler", failureHandler); TaskResubscriptionRequest.Builder taskResubscriptionRequestBuilder = new TaskResubscriptionRequest.Builder() - .jsonrpc(JSONRPC_VERSION) - .method(SEND_TASK_RESUBSCRIPTION_METHOD) + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(TaskResubscriptionRequest.METHOD) .params(taskIdParams); if (requestId != null) { @@ -502,13 +494,13 @@ private A2AHttpClient.PostBuilder createPostBuilder(Object value) throws JsonPro return httpClient.createPost() .url(agentUrl) .addHeader("Content-Type", "application/json") - .body(OBJECT_MAPPER.writeValueAsString(value)); + .body(Utils.OBJECT_MAPPER.writeValueAsString(value)); } private T unmarshalResponse(String response, TypeReference typeReference) throws A2AServerException, JsonProcessingException { - T value = unmarshalFrom(response, typeReference); + T value = Utils.unmarshalFrom(response, typeReference); JSONRPCError error = value.getError(); if (error != null) { throw new A2AServerException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : "")); diff --git a/core/src/main/java/io/a2a/client/sse/SSEEventListener.java b/client/src/main/java/io/a2a/client/sse/SSEEventListener.java similarity index 100% rename from core/src/main/java/io/a2a/client/sse/SSEEventListener.java rename to client/src/main/java/io/a2a/client/sse/SSEEventListener.java diff --git a/core/src/main/java/io/a2a/http/A2AHttpClient.java b/client/src/main/java/io/a2a/http/A2AHttpClient.java similarity index 100% rename from core/src/main/java/io/a2a/http/A2AHttpClient.java rename to client/src/main/java/io/a2a/http/A2AHttpClient.java diff --git a/core/src/main/java/io/a2a/http/A2AHttpResponse.java b/client/src/main/java/io/a2a/http/A2AHttpResponse.java similarity index 100% rename from core/src/main/java/io/a2a/http/A2AHttpResponse.java rename to client/src/main/java/io/a2a/http/A2AHttpResponse.java diff --git a/core/src/main/java/io/a2a/http/JdkA2AHttpClient.java b/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java similarity index 100% rename from core/src/main/java/io/a2a/http/JdkA2AHttpClient.java rename to client/src/main/java/io/a2a/http/JdkA2AHttpClient.java diff --git a/client/src/main/resources/META-INF/beans.xml b/client/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/java/io/a2a/client/A2ACardResolverTest.java b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java similarity index 100% rename from core/src/test/java/io/a2a/client/A2ACardResolverTest.java rename to client/src/test/java/io/a2a/client/A2ACardResolverTest.java diff --git a/core/src/test/java/io/a2a/client/A2AClientStreamingTest.java b/client/src/test/java/io/a2a/client/A2AClientStreamingTest.java similarity index 100% rename from core/src/test/java/io/a2a/client/A2AClientStreamingTest.java rename to client/src/test/java/io/a2a/client/A2AClientStreamingTest.java diff --git a/core/src/test/java/io/a2a/client/A2AClientTest.java b/client/src/test/java/io/a2a/client/A2AClientTest.java similarity index 100% rename from core/src/test/java/io/a2a/client/A2AClientTest.java rename to client/src/test/java/io/a2a/client/A2AClientTest.java diff --git a/core/src/test/java/io/a2a/client/JsonMessages.java b/client/src/test/java/io/a2a/client/JsonMessages.java similarity index 100% rename from core/src/test/java/io/a2a/client/JsonMessages.java rename to client/src/test/java/io/a2a/client/JsonMessages.java diff --git a/core/src/test/java/io/a2a/client/JsonStreamingMessages.java b/client/src/test/java/io/a2a/client/JsonStreamingMessages.java similarity index 100% rename from core/src/test/java/io/a2a/client/JsonStreamingMessages.java rename to client/src/test/java/io/a2a/client/JsonStreamingMessages.java diff --git a/core/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java b/client/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java similarity index 100% rename from core/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java rename to client/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 000000000..3b6aabc36 --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + + io.a2a.sdk + a2a-java-sdk-parent + 0.2.4-SNAPSHOT + + a2a-java-sdk-common + + jar + + Java SDK A2A Common + Java SDK for the Agent2Agent Protocol (A2A) - Common + + + + org.slf4j + slf4j-api + + + + \ No newline at end of file diff --git a/core/src/main/java/io/a2a/util/Assert.java b/common/src/main/java/io/a2a/util/Assert.java similarity index 100% rename from core/src/main/java/io/a2a/util/Assert.java rename to common/src/main/java/io/a2a/util/Assert.java diff --git a/core/src/main/java/io/a2a/util/NotNull.java b/common/src/main/java/io/a2a/util/NotNull.java similarity index 100% rename from core/src/main/java/io/a2a/util/NotNull.java rename to common/src/main/java/io/a2a/util/NotNull.java diff --git a/core/src/main/resources/META-INF/beans.xml b/core/src/main/resources/META-INF/beans.xml deleted file mode 100644 index 9dfae34df..000000000 --- a/core/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/core/src/test/java/io/a2a/util/AssertTest.java b/core/src/test/java/io/a2a/util/AssertTest.java deleted file mode 100644 index 247764dac..000000000 --- a/core/src/test/java/io/a2a/util/AssertTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.a2a.util; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import org.junit.jupiter.api.Test; - -/** - * Tests for Assert utility class - */ -class AssertTest { - - @Test - void testCheckNotNullParam_WithValidValue() { - // Test normal case - String testValue = "test"; - String result = Assert.checkNotNullParam("testParam", testValue); - assertEquals(testValue, result); - } - - @Test - void testCheckNotNullParam_WithNullValue() { - // Test that null value throws exception - assertThrows(IllegalArgumentException.class, () -> { - Assert.checkNotNullParam("testParam", null); - }); - } - - @Test - void testCheckNotNullParam_WithNullName() { - // Test that null name parameter throws exception - assertThrows(IllegalArgumentException.class, () -> { - Assert.checkNotNullParam(null, "testValue"); - }); - } - - @Test - void testCheckNotNullParam_WithEmptyName() { - // Test empty string name parameter - String testValue = "test"; - String result = Assert.checkNotNullParam("", testValue); - assertEquals(testValue, result); - } - - @Test - void testIsNullOrStringOrInteger_WithNull() { - // Test null value - assertDoesNotThrow(() -> { - Assert.isNullOrStringOrInteger(null); - }); - } - - @Test - void testIsNullOrStringOrInteger_WithString() { - // Test String type - assertDoesNotThrow(() -> { - Assert.isNullOrStringOrInteger("test"); - }); - } - - @Test - void testIsNullOrStringOrInteger_WithInteger() { - // Test Integer type - assertDoesNotThrow(() -> { - Assert.isNullOrStringOrInteger(123); - }); - } - - @Test - void testIsNullOrStringOrInteger_WithLong() { - // Test that Long type throws exception - assertThrows(IllegalArgumentException.class, () -> { - Assert.isNullOrStringOrInteger(123L); - }); - } - - @Test - void testIsNullOrStringOrInteger_WithDouble() { - // Test that Double type throws exception - assertThrows(IllegalArgumentException.class, () -> { - Assert.isNullOrStringOrInteger(123.45); - }); - } - - @Test - void testIsNullOrStringOrInteger_WithBoolean() { - // Test that Boolean type throws exception - assertThrows(IllegalArgumentException.class, () -> { - Assert.isNullOrStringOrInteger(true); - }); - } - - @Test - void testIsNullOrStringOrInteger_WithObject() { - // Test that Object type throws exception - assertThrows(IllegalArgumentException.class, () -> { - Assert.isNullOrStringOrInteger(new Object()); - }); - } -} diff --git a/core/src/test/java/io/a2a/util/UtilsTest.java b/core/src/test/java/io/a2a/util/UtilsTest.java deleted file mode 100644 index a9f7953f3..000000000 --- a/core/src/test/java/io/a2a/util/UtilsTest.java +++ /dev/null @@ -1,169 +0,0 @@ -package io.a2a.util; - - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import com.fasterxml.jackson.core.type.TypeReference; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; - -/** - * Tests for Utils utility class - */ -class UtilsTest { - - @Test - void testDefaultIfNull_WithNullValue() { - // Test that null value returns default value - String result = Utils.defaultIfNull(null, "default"); - assertEquals("default", result); - } - - @Test - void testDefaultIfNull_WithNonNullValue() { - // Test that non-null value returns original value - String value = "test"; - String result = Utils.defaultIfNull(value, "default"); - assertEquals(value, result); - } - - @Test - void testDefaultIfNull_WithNullDefault() { - // Test case where default value is null - String value = "test"; - String result = Utils.defaultIfNull(value, null); - assertEquals(value, result); - } - - @Test - void testDefaultIfNull_WithBothNull() { - // Test case where both value and default are null - String result = Utils.defaultIfNull(null, null); - assertNull(result); - } - - @Test - void testDefaultIfNull_WithDifferentTypes() { - // Test with different types - Integer intValue = 123; - Integer result = Utils.defaultIfNull(intValue, 456); - assertEquals(intValue, result); - } - - @Test - void testUnmarshalFrom_WithValidJson() throws Exception { - // Test with valid JSON string - String json = "{\"name\":\"test\",\"value\":123}"; - TypeReference> typeRef = new TypeReference>() {}; - - Map result = Utils.unmarshalFrom(json, typeRef); - - assertNotNull(result); - assertEquals("test", result.get("name")); - assertEquals(123, result.get("value")); - } - - @Test - void testUnmarshalFrom_WithArrayJson() throws Exception { - // Test with array JSON - String json = "[\"item1\",\"item2\",\"item3\"]"; - TypeReference> typeRef = new TypeReference>() {}; - - List result = Utils.unmarshalFrom(json, typeRef); - - assertNotNull(result); - assertEquals(3, result.size()); - assertEquals("item1", result.get(0)); - assertEquals("item2", result.get(1)); - assertEquals("item3", result.get(2)); - } - - @Test - void testUnmarshalFrom_WithInvalidJson() { - // Test with invalid JSON string - String invalidJson = "{invalid json}"; - TypeReference> typeRef = new TypeReference>() {}; - - assertThrows(Exception.class, () -> { - Utils.unmarshalFrom(invalidJson, typeRef); - }); - } - - @Test - void testUnmarshalFrom_WithEmptyJson() throws Exception { - // Test with empty JSON object - String json = "{}"; - TypeReference> typeRef = new TypeReference>() {}; - - Map result = Utils.unmarshalFrom(json, typeRef); - - assertNotNull(result); - assertTrue(result.isEmpty()); - } - - @Test - void testRethrow_WithRuntimeException() { - // Test rethrowing RuntimeException - RuntimeException originalException = new RuntimeException("test exception"); - - assertThrows(RuntimeException.class, () -> { - Utils.rethrow(originalException); - }); - } - - @Test - void testRethrow_WithCheckedException() { - // Test rethrowing checked exception - Exception originalException = new Exception("test checked exception"); - - assertThrows(Exception.class, () -> { - Utils.rethrow(originalException); - }); - } - - @Test - void testRethrow_WithCustomException() { - // Test rethrowing custom exception - IllegalArgumentException originalException = new IllegalArgumentException("test illegal argument"); - - assertThrows(IllegalArgumentException.class, () -> { - Utils.rethrow(originalException); - }); - } - - @Test - void testObjectMapper_IsNotNull() { - // Test that OBJECT_MAPPER is not null - assertNotNull(Utils.OBJECT_MAPPER); - } - - @Test - void testObjectMapper_CanSerializeAndDeserialize() throws Exception { - // Test that OBJECT_MAPPER can serialize and deserialize properly - TestObject original = new TestObject("test", 123); - - String json = Utils.OBJECT_MAPPER.writeValueAsString(original); - TestObject deserialized = Utils.OBJECT_MAPPER.readValue(json, TestObject.class); - - assertEquals(original.name, deserialized.name); - assertEquals(original.value, deserialized.value); - } - - // Simple class for testing - static class TestObject { - public String name; - public int value; - - public TestObject() {} - - public TestObject(String name, int value) { - this.name = name; - this.value = value; - } - } -} diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 4626af575..9750da890 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -18,7 +18,7 @@ io.a2a.sdk - a2a-java-sdk-core + a2a-java-sdk-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index cea945492..45f202b96 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.a2a.client.A2AClient; -import io.a2a.spec.A2A; +import io.a2a.A2A; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index b7c70b08c..672d76655 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -28,7 +28,7 @@ io.a2a.sdk - a2a-java-sdk-core + a2a-java-sdk-client ${project.version} diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java index e5392ca45..1d7519b60 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java @@ -6,7 +6,7 @@ import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; -import io.a2a.spec.A2A; +import io.a2a.A2A; import io.a2a.spec.JSONRPCError; import io.a2a.spec.UnsupportedOperationError; diff --git a/pom.xml b/pom.xml index 4950c6bbd..2cf37a9c7 100644 --- a/pom.xml +++ b/pom.xml @@ -184,9 +184,10 @@ - - core sdk-server-common + common + spec + client sdk-jakarta sdk-quarkus tck diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index 91e6155b4..af290970e 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -55,7 +55,7 @@ ${project.groupId} - a2a-java-sdk-core + a2a-java-sdk-spec ${project.version} diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java index 59e332109..32e47e783 100644 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java +++ b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java @@ -1,17 +1,16 @@ package io.a2a.server.apps.jakarta; -import static io.a2a.spec.A2A.CANCEL_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; -import static io.a2a.spec.A2A.SEND_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_STREAMING_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_TASK_RESUBSCRIPTION_METHOD; -import static io.a2a.spec.A2A.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskRequest; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.TaskResubscriptionRequest; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.PreMatching; @@ -50,16 +49,16 @@ public void filter(ContainerRequestContext requestContext) { } private static boolean isStreamingRequest(String requestBody) { - return requestBody.contains(SEND_STREAMING_MESSAGE_METHOD) || - requestBody.contains(SEND_TASK_RESUBSCRIPTION_METHOD); + return requestBody.contains(SendStreamingMessageRequest.METHOD) || + requestBody.contains(TaskResubscriptionRequest.METHOD); } private static boolean isNonStreamingRequest(String requestBody) { - return requestBody.contains(GET_TASK_METHOD) || - requestBody.contains(CANCEL_TASK_METHOD) || - requestBody.contains(SEND_MESSAGE_METHOD) || - requestBody.contains(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD) || - requestBody.contains(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); + return requestBody.contains(GetTaskRequest.METHOD) || + requestBody.contains(CancelTaskRequest.METHOD) || + requestBody.contains(SendMessageRequest.METHOD) || + requestBody.contains(SetTaskPushNotificationConfigRequest.METHOD) || + requestBody.contains(GetTaskPushNotificationConfigRequest.METHOD); } private static void putAcceptHeader(ContainerRequestContext requestContext, String mediaType) { diff --git a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java index 159ac79bc..5bdb5225c 100644 --- a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java +++ b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java @@ -34,8 +34,10 @@ public JakartaA2AServerTest() { @Deployment public static WebArchive createTestArchive() throws IOException { final List prefixes = List.of( - "a2a-java-sdk-core", + "a2a-java-sdk-client", + "a2a-java-sdk-common", "a2a-java-sdk-server-common", + "a2a-java-sdk-spec", "jackson", "mutiny", "slf4j", diff --git a/sdk-quarkus/pom.xml b/sdk-quarkus/pom.xml index bacccc073..6e232d297 100644 --- a/sdk-quarkus/pom.xml +++ b/sdk-quarkus/pom.xml @@ -19,7 +19,7 @@ ${project.groupId} - a2a-java-sdk-core + a2a-java-sdk-spec ${project.version} diff --git a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 4246e965e..827cc7041 100644 --- a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -18,7 +18,6 @@ import com.fasterxml.jackson.core.io.JsonEOFException; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.requesthandlers.JSONRPCHandler; -import io.a2a.spec.A2A; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.GetTaskPushNotificationConfigRequest; @@ -214,16 +213,16 @@ private JSONRPCResponse generateErrorResponse(JSONRPCRequest request, JSON } private static boolean isStreamingRequest(String requestBody) { - return requestBody.contains(A2A.SEND_STREAMING_MESSAGE_METHOD) || - requestBody.contains(A2A.SEND_TASK_RESUBSCRIPTION_METHOD); + return requestBody.contains(SendStreamingMessageRequest.METHOD) || + requestBody.contains(TaskResubscriptionRequest.METHOD); } private static boolean isNonStreamingRequest(String requestBody) { - return requestBody.contains(A2A.GET_TASK_METHOD) || - requestBody.contains(A2A.CANCEL_TASK_METHOD) || - requestBody.contains(A2A.SEND_MESSAGE_METHOD) || - requestBody.contains(A2A.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD) || - requestBody.contains(A2A.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); + return requestBody.contains(GetTaskRequest.METHOD) || + requestBody.contains(CancelTaskRequest.METHOD) || + requestBody.contains(SendMessageRequest.METHOD) || + requestBody.contains(SetTaskPushNotificationConfigRequest.METHOD) || + requestBody.contains(GetTaskPushNotificationConfigRequest.METHOD); } static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 4cf02b5a6..848f943fe 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -19,7 +19,12 @@ ${project.groupId} - a2a-java-sdk-core + a2a-java-sdk-spec + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client ${project.version} diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java index 7a693d610..f43aec9cc 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -1,6 +1,5 @@ package io.a2a.server.events; - import java.util.concurrent.Flow; import io.a2a.spec.A2AServerException; diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 1ca4ca158..5d6cbec84 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -16,12 +16,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -import io.a2a.server.agentexecution.AgentExecutor; -import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.agentexecution.SimpleRequestContextBuilder; import io.a2a.server.events.EnhancedRunnable; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; @@ -46,6 +40,12 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.UnsupportedOperationError; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.agentexecution.SimpleRequestContextBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index a0b5a31bd..089dc7abe 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -6,9 +6,9 @@ import java.util.ArrayList; import java.util.List; -import io.a2a.spec.Event; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; +import io.a2a.spec.Event; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.Message; import io.a2a.spec.Part; diff --git a/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index 8e3f04af6..c9bb79061 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -1,5 +1,12 @@ package io.a2a.server.agentexecution; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; + import io.a2a.spec.InvalidParamsError; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; @@ -14,13 +21,6 @@ import java.util.List; import java.util.UUID; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; - public class RequestContextTest { @Test diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index d394db2bb..49913c0b6 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import io.a2a.spec.InternalError; import jakarta.enterprise.context.Dependent; import io.a2a.http.A2AHttpClient; diff --git a/core/pom.xml b/spec/pom.xml similarity index 69% rename from core/pom.xml rename to spec/pom.xml index 8aed247f4..450db7489 100644 --- a/core/pom.xml +++ b/spec/pom.xml @@ -9,14 +9,20 @@ a2a-java-sdk-parent 0.2.4-SNAPSHOT - a2a-java-sdk-core + a2a-java-sdk-spec jar - Java SDK A2A Core - Java SDK for the Agent2Agent Protocol (A2A) - Core + Java SDK A2A Spec + Java SDK for the Agent2Agent Protocol (A2A) - Spec + + ${project.groupId} + a2a-java-sdk-common + ${project.version} + + com.fasterxml.jackson.core jackson-databind @@ -25,21 +31,12 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 + org.junit.jupiter junit-jupiter-api test - - org.mockito - mockito-core - test - - - org.mock-server - mockserver-netty - test - \ No newline at end of file diff --git a/core/src/main/java/io/a2a/spec/A2AClientError.java b/spec/src/main/java/io/a2a/spec/A2AClientError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/A2AClientError.java rename to spec/src/main/java/io/a2a/spec/A2AClientError.java diff --git a/core/src/main/java/io/a2a/spec/A2AClientHTTPError.java b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/A2AClientHTTPError.java rename to spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java diff --git a/core/src/main/java/io/a2a/spec/A2AClientJSONError.java b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/A2AClientJSONError.java rename to spec/src/main/java/io/a2a/spec/A2AClientJSONError.java diff --git a/core/src/main/java/io/a2a/spec/A2AError.java b/spec/src/main/java/io/a2a/spec/A2AError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/A2AError.java rename to spec/src/main/java/io/a2a/spec/A2AError.java diff --git a/core/src/main/java/io/a2a/spec/A2AException.java b/spec/src/main/java/io/a2a/spec/A2AException.java similarity index 100% rename from core/src/main/java/io/a2a/spec/A2AException.java rename to spec/src/main/java/io/a2a/spec/A2AException.java diff --git a/core/src/main/java/io/a2a/spec/A2AServerException.java b/spec/src/main/java/io/a2a/spec/A2AServerException.java similarity index 100% rename from core/src/main/java/io/a2a/spec/A2AServerException.java rename to spec/src/main/java/io/a2a/spec/A2AServerException.java diff --git a/core/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java similarity index 100% rename from core/src/main/java/io/a2a/spec/APIKeySecurityScheme.java rename to spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java diff --git a/core/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AgentCapabilities.java rename to spec/src/main/java/io/a2a/spec/AgentCapabilities.java diff --git a/core/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AgentCard.java rename to spec/src/main/java/io/a2a/spec/AgentCard.java diff --git a/core/src/main/java/io/a2a/spec/AgentExtension.java b/spec/src/main/java/io/a2a/spec/AgentExtension.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AgentExtension.java rename to spec/src/main/java/io/a2a/spec/AgentExtension.java diff --git a/core/src/main/java/io/a2a/spec/AgentProvider.java b/spec/src/main/java/io/a2a/spec/AgentProvider.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AgentProvider.java rename to spec/src/main/java/io/a2a/spec/AgentProvider.java diff --git a/core/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AgentSkill.java rename to spec/src/main/java/io/a2a/spec/AgentSkill.java diff --git a/core/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java similarity index 100% rename from core/src/main/java/io/a2a/spec/Artifact.java rename to spec/src/main/java/io/a2a/spec/Artifact.java diff --git a/core/src/main/java/io/a2a/spec/AuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AuthenticationInfo.java rename to spec/src/main/java/io/a2a/spec/AuthenticationInfo.java diff --git a/core/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java similarity index 100% rename from core/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java rename to spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java diff --git a/core/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java similarity index 90% rename from core/src/main/java/io/a2a/spec/CancelTaskRequest.java rename to spec/src/main/java/io/a2a/spec/CancelTaskRequest.java index 3349ceb5c..39c370ae3 100644 --- a/core/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.CANCEL_TASK_METHOD; -import static io.a2a.spec.A2A.JSONRPC_VERSION; import static io.a2a.util.Utils.defaultIfNull; import java.util.UUID; @@ -20,6 +18,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class CancelTaskRequest extends NonStreamingJSONRPCRequest { + public static final String METHOD = "tasks/cancel"; + @JsonCreator public CancelTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { @@ -27,7 +27,7 @@ public CancelTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty( throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(CANCEL_TASK_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid CancelTaskRequest method"); } Assert.checkNotNullParam("params", params); @@ -39,13 +39,13 @@ public CancelTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty( } public CancelTaskRequest(Object id, TaskIdParams params) { - this(null, id, CANCEL_TASK_METHOD, params); + this(null, id, METHOD, params); } public static class Builder { private String jsonrpc; private Object id; - private String method = CANCEL_TASK_METHOD; + private String method = METHOD; private TaskIdParams params; public CancelTaskRequest.Builder jsonrpc(String jsonrpc) { diff --git a/core/src/main/java/io/a2a/spec/CancelTaskResponse.java b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java similarity index 100% rename from core/src/main/java/io/a2a/spec/CancelTaskResponse.java rename to spec/src/main/java/io/a2a/spec/CancelTaskResponse.java diff --git a/core/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java similarity index 100% rename from core/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java rename to spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java diff --git a/core/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java rename to spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java diff --git a/core/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java similarity index 100% rename from core/src/main/java/io/a2a/spec/DataPart.java rename to spec/src/main/java/io/a2a/spec/DataPart.java diff --git a/core/src/main/java/io/a2a/spec/Event.java b/spec/src/main/java/io/a2a/spec/Event.java similarity index 100% rename from core/src/main/java/io/a2a/spec/Event.java rename to spec/src/main/java/io/a2a/spec/Event.java diff --git a/core/src/main/java/io/a2a/spec/EventKind.java b/spec/src/main/java/io/a2a/spec/EventKind.java similarity index 100% rename from core/src/main/java/io/a2a/spec/EventKind.java rename to spec/src/main/java/io/a2a/spec/EventKind.java diff --git a/core/src/main/java/io/a2a/spec/FileContent.java b/spec/src/main/java/io/a2a/spec/FileContent.java similarity index 100% rename from core/src/main/java/io/a2a/spec/FileContent.java rename to spec/src/main/java/io/a2a/spec/FileContent.java diff --git a/core/src/main/java/io/a2a/spec/FileContentDeserializer.java b/spec/src/main/java/io/a2a/spec/FileContentDeserializer.java similarity index 100% rename from core/src/main/java/io/a2a/spec/FileContentDeserializer.java rename to spec/src/main/java/io/a2a/spec/FileContentDeserializer.java diff --git a/core/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java similarity index 100% rename from core/src/main/java/io/a2a/spec/FilePart.java rename to spec/src/main/java/io/a2a/spec/FilePart.java diff --git a/core/src/main/java/io/a2a/spec/FileWithBytes.java b/spec/src/main/java/io/a2a/spec/FileWithBytes.java similarity index 100% rename from core/src/main/java/io/a2a/spec/FileWithBytes.java rename to spec/src/main/java/io/a2a/spec/FileWithBytes.java diff --git a/core/src/main/java/io/a2a/spec/FileWithUri.java b/spec/src/main/java/io/a2a/spec/FileWithUri.java similarity index 100% rename from core/src/main/java/io/a2a/spec/FileWithUri.java rename to spec/src/main/java/io/a2a/spec/FileWithUri.java diff --git a/core/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java similarity index 86% rename from core/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java rename to spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index ba84b0231..ab39015bb 100644 --- a/core/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -1,17 +1,13 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; -import static io.a2a.spec.A2A.JSONRPC_VERSION; -import static io.a2a.util.Utils.defaultIfNull; - -import java.util.UUID; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; +import io.a2a.util.Utils; + +import java.util.UUID; /** * A get task push notification request. @@ -20,6 +16,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + public static final String METHOD = "tasks/pushNotificationConfig/get"; + @JsonCreator public GetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { @@ -27,18 +25,18 @@ public GetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String json throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid GetTaskPushNotificationRequest method"); } Assert.isNullOrStringOrInteger(id); - this.jsonrpc = defaultIfNull(jsonrpc, JSONRPC_VERSION); + this.jsonrpc = Utils.defaultIfNull(jsonrpc, JSONRPC_VERSION); this.id = id; this.method = method; this.params = params; } public GetTaskPushNotificationConfigRequest(String id, TaskIdParams params) { - this(null, id, GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, params); + this(null, id, METHOD, params); } public static class Builder { diff --git a/core/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java similarity index 100% rename from core/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java rename to spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java diff --git a/core/src/main/java/io/a2a/spec/GetTaskRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java similarity index 92% rename from core/src/main/java/io/a2a/spec/GetTaskRequest.java rename to spec/src/main/java/io/a2a/spec/GetTaskRequest.java index 6c62cc265..f31237af0 100644 --- a/core/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.GET_TASK_METHOD; -import static io.a2a.spec.A2A.JSONRPC_VERSION; import static io.a2a.util.Utils.defaultIfNull; import java.util.UUID; @@ -20,6 +18,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskRequest extends NonStreamingJSONRPCRequest { + public static final String METHOD = "tasks/get"; + @JsonCreator public GetTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") TaskQueryParams params) { @@ -27,7 +27,7 @@ public GetTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(GET_TASK_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid GetTaskRequest method"); } Assert.checkNotNullParam("params", params); @@ -39,7 +39,7 @@ public GetTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id } public GetTaskRequest(Object id, TaskQueryParams params) { - this(null, id, GET_TASK_METHOD, params); + this(null, id, METHOD, params); } diff --git a/core/src/main/java/io/a2a/spec/GetTaskResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java similarity index 100% rename from core/src/main/java/io/a2a/spec/GetTaskResponse.java rename to spec/src/main/java/io/a2a/spec/GetTaskResponse.java diff --git a/core/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java similarity index 100% rename from core/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java rename to spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java diff --git a/core/src/main/java/io/a2a/spec/IdJsonMappingException.java b/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java similarity index 100% rename from core/src/main/java/io/a2a/spec/IdJsonMappingException.java rename to spec/src/main/java/io/a2a/spec/IdJsonMappingException.java diff --git a/core/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java similarity index 100% rename from core/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java rename to spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java diff --git a/core/src/main/java/io/a2a/spec/IntegerJsonrpcId.java b/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java similarity index 100% rename from core/src/main/java/io/a2a/spec/IntegerJsonrpcId.java rename to spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java diff --git a/core/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/InternalError.java rename to spec/src/main/java/io/a2a/spec/InternalError.java diff --git a/core/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/InvalidAgentResponseError.java rename to spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java diff --git a/core/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/InvalidParamsError.java rename to spec/src/main/java/io/a2a/spec/InvalidParamsError.java diff --git a/core/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java b/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java similarity index 100% rename from core/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java rename to spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java diff --git a/core/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/InvalidRequestError.java rename to spec/src/main/java/io/a2a/spec/InvalidRequestError.java diff --git a/core/src/main/java/io/a2a/spec/JSONErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JSONErrorResponse.java rename to spec/src/main/java/io/a2a/spec/JSONErrorResponse.java diff --git a/core/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JSONParseError.java rename to spec/src/main/java/io/a2a/spec/JSONParseError.java index 244ae39ec..2e3e96dbe 100644 --- a/core/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -1,12 +1,12 @@ package io.a2a.spec; -import static io.a2a.util.Utils.defaultIfNull; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import static io.a2a.util.Utils.defaultIfNull; + @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class JSONParseError extends JSONRPCError implements A2AError { diff --git a/core/src/main/java/io/a2a/spec/JSONRPCError.java b/spec/src/main/java/io/a2a/spec/JSONRPCError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JSONRPCError.java rename to spec/src/main/java/io/a2a/spec/JSONRPCError.java diff --git a/core/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java rename to spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java diff --git a/core/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java rename to spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java diff --git a/core/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java rename to spec/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java diff --git a/core/src/main/java/io/a2a/spec/JSONRPCMessage.java b/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java similarity index 83% rename from core/src/main/java/io/a2a/spec/JSONRPCMessage.java rename to spec/src/main/java/io/a2a/spec/JSONRPCMessage.java index 95cadfd94..20d3a117a 100644 --- a/core/src/main/java/io/a2a/spec/JSONRPCMessage.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java @@ -5,6 +5,8 @@ */ public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCResponse { + String JSONRPC_VERSION = "2.0"; + String getJsonrpc(); Object getId(); diff --git a/core/src/main/java/io/a2a/spec/JSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java similarity index 96% rename from core/src/main/java/io/a2a/spec/JSONRPCRequest.java rename to spec/src/main/java/io/a2a/spec/JSONRPCRequest.java index f01318974..a88de90f1 100644 --- a/core/src/main/java/io/a2a/spec/JSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java @@ -1,6 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/core/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java similarity index 82% rename from core/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java rename to spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java index 2cdc61668..fe21ffab8 100644 --- a/core/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java @@ -1,17 +1,7 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.CANCEL_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; -import static io.a2a.spec.A2A.SEND_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_STREAMING_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_TASK_RESUBSCRIPTION_METHOD; -import static io.a2a.spec.A2A.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; -import static io.a2a.spec.A2A.isValidMethodName; import static io.a2a.util.Utils.OBJECT_MAPPER; -import java.io.IOException; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; @@ -19,6 +9,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; + public abstract class JSONRPCRequestDeserializerBase extends StdDeserializer> { public JSONRPCRequestDeserializerBase() { @@ -39,19 +31,19 @@ public JSONRPCRequest deserialize(JsonParser jsonParser, DeserializationConte JsonNode paramsNode = treeNode.get("params"); switch (method) { - case GET_TASK_METHOD: + case GetTaskRequest.METHOD: return new GetTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskQueryParams.class)); - case CANCEL_TASK_METHOD: + case CancelTaskRequest.METHOD: return new CancelTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD: + case SetTaskPushNotificationConfigRequest.METHOD: return new SetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskPushNotificationConfig.class)); - case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD: + case GetTaskPushNotificationConfigRequest.METHOD: return new GetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SEND_MESSAGE_METHOD: + case SendMessageRequest.METHOD: return new SendMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); - case SEND_TASK_RESUBSCRIPTION_METHOD: + case TaskResubscriptionRequest.METHOD: return new TaskResubscriptionRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SEND_STREAMING_MESSAGE_METHOD: + case SendStreamingMessageRequest.METHOD: return new SendStreamingMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); default: throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); @@ -71,7 +63,7 @@ protected T getAndValidateParams(JsonNode paramsNode, JsonParser jsonParser, protected String getAndValidateJsonrpc(JsonNode treeNode, JsonParser jsonParser) throws JsonMappingException { JsonNode jsonrpcNode = treeNode.get("jsonrpc"); - if (jsonrpcNode == null || ! jsonrpcNode.asText().equals(A2A.JSONRPC_VERSION)) { + if (jsonrpcNode == null || ! jsonrpcNode.asText().equals(JSONRPCMessage.JSONRPC_VERSION)) { throw new IdJsonMappingException("Invalid JSON-RPC protocol version", getIdIfPossible(treeNode, jsonParser)); } return jsonrpcNode.asText(); @@ -112,4 +104,15 @@ protected Object getIdIfPossible(JsonNode treeNode, JsonParser jsonParser) { return null; } } + + protected static boolean isValidMethodName(String methodName) { + return methodName != null && (methodName.equals(CancelTaskRequest.METHOD) + || methodName.equals(GetTaskRequest.METHOD) + || methodName.equals(GetTaskPushNotificationConfigRequest.METHOD) + || methodName.equals(SetTaskPushNotificationConfigRequest.METHOD) + || methodName.equals(TaskResubscriptionRequest.METHOD) + || methodName.equals(SendMessageRequest.METHOD) + || methodName.equals(SendStreamingMessageRequest.METHOD)); + + } } diff --git a/core/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java similarity index 97% rename from core/src/main/java/io/a2a/spec/JSONRPCResponse.java rename to spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 91b9443ee..1be348043 100644 --- a/core/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -1,6 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/core/src/main/java/io/a2a/spec/JsonrpcId.java b/spec/src/main/java/io/a2a/spec/JsonrpcId.java similarity index 100% rename from core/src/main/java/io/a2a/spec/JsonrpcId.java rename to spec/src/main/java/io/a2a/spec/JsonrpcId.java diff --git a/core/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java similarity index 100% rename from core/src/main/java/io/a2a/spec/Message.java rename to spec/src/main/java/io/a2a/spec/Message.java diff --git a/core/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java similarity index 100% rename from core/src/main/java/io/a2a/spec/MessageSendConfiguration.java rename to spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java diff --git a/core/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java similarity index 100% rename from core/src/main/java/io/a2a/spec/MessageSendParams.java rename to spec/src/main/java/io/a2a/spec/MessageSendParams.java diff --git a/core/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/MethodNotFoundError.java rename to spec/src/main/java/io/a2a/spec/MethodNotFoundError.java diff --git a/core/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java similarity index 100% rename from core/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java rename to spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java diff --git a/core/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java similarity index 100% rename from core/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java rename to spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java diff --git a/core/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java similarity index 80% rename from core/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java rename to spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java index a1db9f5c8..f2978b66f 100644 --- a/core/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java @@ -1,18 +1,12 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.CANCEL_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_METHOD; -import static io.a2a.spec.A2A.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; -import static io.a2a.spec.A2A.SEND_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; - -import java.io.IOException; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; + public class NonStreamingJSONRPCRequestDeserializer extends JSONRPCRequestDeserializerBase> { public NonStreamingJSONRPCRequestDeserializer() { @@ -33,19 +27,19 @@ public NonStreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserial JsonNode paramsNode = treeNode.get("params"); switch (method) { - case GET_TASK_METHOD: + case GetTaskRequest.METHOD: return new GetTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskQueryParams.class)); - case CANCEL_TASK_METHOD: + case CancelTaskRequest.METHOD: return new CancelTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD: + case SetTaskPushNotificationConfigRequest.METHOD: return new SetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskPushNotificationConfig.class)); - case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD: + case GetTaskPushNotificationConfigRequest.METHOD: return new GetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SEND_MESSAGE_METHOD: + case SendMessageRequest.METHOD: return new SendMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); default: diff --git a/core/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java similarity index 100% rename from core/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java rename to spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java diff --git a/core/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java similarity index 100% rename from core/src/main/java/io/a2a/spec/OAuthFlows.java rename to spec/src/main/java/io/a2a/spec/OAuthFlows.java diff --git a/core/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java similarity index 100% rename from core/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java rename to spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java diff --git a/core/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java similarity index 100% rename from core/src/main/java/io/a2a/spec/Part.java rename to spec/src/main/java/io/a2a/spec/Part.java diff --git a/core/src/main/java/io/a2a/spec/PasswordOAuthFlow.java b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java similarity index 100% rename from core/src/main/java/io/a2a/spec/PasswordOAuthFlow.java rename to spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java diff --git a/core/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java similarity index 100% rename from core/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java rename to spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java diff --git a/core/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java similarity index 100% rename from core/src/main/java/io/a2a/spec/PushNotificationConfig.java rename to spec/src/main/java/io/a2a/spec/PushNotificationConfig.java diff --git a/core/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java rename to spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java diff --git a/core/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java similarity index 100% rename from core/src/main/java/io/a2a/spec/SecurityScheme.java rename to spec/src/main/java/io/a2a/spec/SecurityScheme.java diff --git a/core/src/main/java/io/a2a/spec/SendMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java similarity index 92% rename from core/src/main/java/io/a2a/spec/SendMessageRequest.java rename to spec/src/main/java/io/a2a/spec/SendMessageRequest.java index 620335aa7..d31f364e4 100644 --- a/core/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; -import static io.a2a.spec.A2A.SEND_MESSAGE_METHOD; import static io.a2a.util.Utils.defaultIfNull; import java.util.UUID; @@ -20,6 +18,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class SendMessageRequest extends NonStreamingJSONRPCRequest { + public static final String METHOD = "message/send"; + @JsonCreator public SendMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") MessageSendParams params) { @@ -30,7 +30,7 @@ public SendMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(SEND_MESSAGE_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid SendMessageRequest method"); } Assert.checkNotNullParam("params", params); @@ -42,7 +42,7 @@ public SendMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty } public SendMessageRequest(Object id, MessageSendParams params) { - this(JSONRPC_VERSION, id, SEND_MESSAGE_METHOD, params); + this(JSONRPC_VERSION, id, METHOD, params); } public static class Builder { diff --git a/core/src/main/java/io/a2a/spec/SendMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java similarity index 96% rename from core/src/main/java/io/a2a/spec/SendMessageResponse.java rename to spec/src/main/java/io/a2a/spec/SendMessageResponse.java index 80b97e272..fa95bad36 100644 --- a/core/src/main/java/io/a2a/spec/SendMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java @@ -1,13 +1,11 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** diff --git a/core/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java similarity index 89% rename from core/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java rename to spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java index 6503f03e7..c4ebe8315 100644 --- a/core/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java @@ -1,18 +1,15 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; -import static io.a2a.spec.A2A.SEND_STREAMING_MESSAGE_METHOD; import static io.a2a.util.Utils.defaultIfNull; -import java.util.UUID; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; +import java.util.UUID; + /** * Used to initiate a task with streaming. */ @@ -20,6 +17,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class SendStreamingMessageRequest extends StreamingJSONRPCRequest { + public static final String METHOD = "message/stream"; + @JsonCreator public SendStreamingMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") MessageSendParams params) { @@ -27,7 +26,7 @@ public SendStreamingMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @Jso throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(SEND_STREAMING_MESSAGE_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid SendStreamingMessageRequest method"); } Assert.checkNotNullParam("params", params); @@ -39,13 +38,13 @@ public SendStreamingMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @Jso } public SendStreamingMessageRequest(Object id, MessageSendParams params) { - this(null, id, SEND_STREAMING_MESSAGE_METHOD, params); + this(null, id, METHOD, params); } public static class Builder { private String jsonrpc; private Object id; - private String method = SEND_STREAMING_MESSAGE_METHOD; + private String method = METHOD; private MessageSendParams params; public Builder jsonrpc(String jsonrpc) { diff --git a/core/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java similarity index 96% rename from core/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java rename to spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java index 3a1cc3553..b3597bfb6 100644 --- a/core/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java @@ -1,13 +1,11 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** diff --git a/core/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java similarity index 88% rename from core/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java rename to spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java index b042f6218..7d53083a1 100644 --- a/core/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; -import static io.a2a.spec.A2A.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; import static io.a2a.util.Utils.defaultIfNull; import java.util.UUID; @@ -20,6 +18,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + public static final String METHOD = "tasks/pushNotificationConfig/set"; + @JsonCreator public SetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") TaskPushNotificationConfig params) { @@ -27,7 +27,7 @@ public SetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String json throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid SetTaskPushNotificationRequest method"); } Assert.checkNotNullParam("params", params); @@ -39,13 +39,13 @@ public SetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String json } public SetTaskPushNotificationConfigRequest(String id, TaskPushNotificationConfig taskPushConfig) { - this(null, id, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, taskPushConfig); + this(null, id, METHOD, taskPushConfig); } public static class Builder { private String jsonrpc; private Object id; - private String method = SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; + private String method = METHOD; private TaskPushNotificationConfig params; public SetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { diff --git a/core/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java similarity index 100% rename from core/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java rename to spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java diff --git a/core/src/main/java/io/a2a/spec/StreamingEventKind.java b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java similarity index 100% rename from core/src/main/java/io/a2a/spec/StreamingEventKind.java rename to spec/src/main/java/io/a2a/spec/StreamingEventKind.java diff --git a/core/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java similarity index 100% rename from core/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java rename to spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java diff --git a/core/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java similarity index 87% rename from core/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java rename to spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java index 4360e52f7..236ad547a 100644 --- a/core/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.SEND_STREAMING_MESSAGE_METHOD; -import static io.a2a.spec.A2A.SEND_TASK_RESUBSCRIPTION_METHOD; - import java.io.IOException; import com.fasterxml.jackson.core.JsonParser; @@ -30,10 +27,10 @@ public StreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserializa JsonNode paramsNode = treeNode.get("params"); switch (method) { - case SEND_TASK_RESUBSCRIPTION_METHOD: + case TaskResubscriptionRequest.METHOD: return new TaskResubscriptionRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SEND_STREAMING_MESSAGE_METHOD: + case SendStreamingMessageRequest.METHOD: return new SendStreamingMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); default: diff --git a/core/src/main/java/io/a2a/spec/StringJsonrpcId.java b/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java similarity index 100% rename from core/src/main/java/io/a2a/spec/StringJsonrpcId.java rename to spec/src/main/java/io/a2a/spec/StringJsonrpcId.java diff --git a/core/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java similarity index 100% rename from core/src/main/java/io/a2a/spec/Task.java rename to spec/src/main/java/io/a2a/spec/Task.java diff --git a/core/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java rename to spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java diff --git a/core/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskIdParams.java rename to spec/src/main/java/io/a2a/spec/TaskIdParams.java diff --git a/core/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskNotCancelableError.java rename to spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java diff --git a/core/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskNotFoundError.java rename to spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index 61b1857c5..26ee264e3 100644 --- a/core/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -1,12 +1,12 @@ package io.a2a.spec; -import static io.a2a.util.Utils.defaultIfNull; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import static io.a2a.util.Utils.defaultIfNull; + @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotFoundError extends JSONRPCError { diff --git a/core/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java rename to spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java diff --git a/core/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskQueryParams.java rename to spec/src/main/java/io/a2a/spec/TaskQueryParams.java diff --git a/core/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java b/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java similarity index 88% rename from core/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java rename to spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java index 7380cddd1..de0c88c62 100644 --- a/core/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java +++ b/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java @@ -1,18 +1,15 @@ package io.a2a.spec; -import static io.a2a.spec.A2A.JSONRPC_VERSION; -import static io.a2a.spec.A2A.SEND_TASK_RESUBSCRIPTION_METHOD; import static io.a2a.util.Utils.defaultIfNull; -import java.util.UUID; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; +import java.util.UUID; + /** * Used to resubscribe to a task. */ @@ -20,6 +17,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class TaskResubscriptionRequest extends StreamingJSONRPCRequest { + public static final String METHOD = "tasks/resubscribe"; + @JsonCreator public TaskResubscriptionRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { @@ -27,7 +26,7 @@ public TaskResubscriptionRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonP throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); - if (! method.equals(SEND_TASK_RESUBSCRIPTION_METHOD)) { + if (! method.equals(METHOD)) { throw new IllegalArgumentException("Invalid TaskResubscriptionRequest method"); } Assert.checkNotNullParam("params", params); @@ -38,13 +37,13 @@ public TaskResubscriptionRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonP } public TaskResubscriptionRequest(Object id, TaskIdParams params) { - this(null, id, SEND_TASK_RESUBSCRIPTION_METHOD, params); + this(null, id, METHOD, params); } public static class Builder { private String jsonrpc; private Object id; - private String method = SEND_TASK_RESUBSCRIPTION_METHOD; + private String method = METHOD; private TaskIdParams params; public TaskResubscriptionRequest.Builder jsonrpc(String jsonrpc) { diff --git a/core/src/main/java/io/a2a/spec/TaskState.java b/spec/src/main/java/io/a2a/spec/TaskState.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskState.java rename to spec/src/main/java/io/a2a/spec/TaskState.java diff --git a/core/src/main/java/io/a2a/spec/TaskStatus.java b/spec/src/main/java/io/a2a/spec/TaskStatus.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskStatus.java rename to spec/src/main/java/io/a2a/spec/TaskStatus.java diff --git a/core/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java rename to spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java diff --git a/core/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java similarity index 100% rename from core/src/main/java/io/a2a/spec/TextPart.java rename to spec/src/main/java/io/a2a/spec/TextPart.java diff --git a/core/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java similarity index 100% rename from core/src/main/java/io/a2a/spec/UnsupportedOperationError.java rename to spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java diff --git a/core/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java similarity index 100% rename from core/src/main/java/io/a2a/util/Utils.java rename to spec/src/main/java/io/a2a/util/Utils.java diff --git a/spec/src/main/resources/META-INF/beans.xml b/spec/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java b/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java similarity index 100% rename from core/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java rename to spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java diff --git a/tck/pom.xml b/tck/pom.xml index b89071fc1..ceaf52f45 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -16,6 +16,11 @@ Server example to use with the A2A TCK + + io.a2a.sdk + a2a-java-sdk-spec + ${project.version} + io.a2a.sdk a2a-java-sdk-server-quarkus diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 866b379c0..394320f6c 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -20,7 +20,12 @@ ${project.groupId} - a2a-java-sdk-core + a2a-java-sdk-spec + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client ${project.version} From 2c5af01a10b41510e0db6e4a92d3990061b38484 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 7 Jul 2025 12:53:39 -0400 Subject: [PATCH 027/493] fix: Fix dependency for client example (#167) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Follows up on https://github.com/a2aproject/a2a-java/pull/149 --- .../main/java/io/a2a/examples/helloworld/HelloWorldRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index b87b14ce3..33064a9e9 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.a2a.sdk:a2a-java-sdk-core:0.2.4-SNAPSHOT +//DEPS io.a2a.sdk:a2a-java-sdk-client:0.2.4-SNAPSHOT //SOURCES HelloWorldClient.java /** From 3e152791501113717eec517d554f3568d20d9861 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 8 Jul 2025 05:45:12 -0400 Subject: [PATCH 028/493] fix: Update the README following the module re-organization (#169) # Description This PR updates the README following the module re-org changes. - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- README.md | 54 +++++++++++++++++++++-------------------------------- tck/pom.xml | 5 ----- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 9339b08e5..591611e48 100644 --- a/README.md +++ b/README.md @@ -29,26 +29,34 @@ More examples will be added soon. The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A). To run your agentic Java application as an A2A server, simply follow the steps below. -- [Add the A2A Java SDK Core Maven dependency to your project](#1-add-the-a2a-java-sdk-core-maven-dependency-to-your-project) +- [Add an A2A Java SDK Server Maven dependency to your project](#1-add-an-a2a-java-sdk-server-maven-dependency-to-your-project) - [Add a class that creates an A2A Agent Card](#2-add-a-class-that-creates-an-a2a-agent-card) - [Add a class that creates an A2A Agent Executor](#3-add-a-class-that-creates-an-a2a-agent-executor) -- [Add an A2A Java SDK Server Maven dependency to your project](#4-add-an-a2a-java-sdk-server-maven-dependency-to-your-project) -### 1. Add the A2A Java SDK Maven dependencies to your project +### 1. Add an A2A Java SDK Server Maven dependency to your project -> **Note**: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be -> sure to check out the latest tag (you can see the tags [here](https://github.com/a2aproject/a2a-java/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. +Adding a dependency on an A2A Java SDK Server will provide access to the core classes that make up the A2A specification +and allow you to run your agentic Java application as an A2A server agent. + +The A2A Java SDK provides two A2A server endpoint implementations, one based on Jakarta REST (`a2a-java-sdk-server-jakarta`) and one based on Quarkus Reactive Routes (`a2a-java-sdk-server-quarkus`). You can choose the one that best fits your application. + +Add **one** of the following dependencies to your project: ```xml io.a2a.sdk - a2a-java-sdk-client - 0.2.3 + a2a-java-sdk-server-jakarta + ${io.a2a.sdk.version} +``` + +OR + +```xml io.a2a.sdk - a2a-java-sdk-server-common - 0.2.3 + a2a-java-sdk-server-quarkus + ${io.a2a.sdk.version} ``` @@ -185,39 +193,19 @@ public class WeatherAgentExecutorProducer { } ``` -### 4. Add an A2A Java SDK Server Maven dependency to your project - -> **Note**: The A2A Java SDK isn't available yet in Maven Central but will be soon. For now, be -> sure to check out the latest tag (you can see the tags [here](https://github.com/a2aproject/a2a-java/tags)), build from the tag, and reference that version below. For example, if the latest tag is `0.2.3`, you can use the following dependency. - -Adding a dependency on an A2A Java SDK Server will allow you to run your agentic Java application as an A2A server. - -The A2A Java SDK provides two A2A server endpoint implementations, one based on Jakarta REST (`a2a-java-sdk-server-jakarta`) and one based on Quarkus Reactive Routes (`a2a-java-sdk-server-quarkus`). You can choose the one that best fits your application. - -Add **one** of the following dependencies to your project: - -```xml - - io.a2a.sdk - a2a-java-sdk-server-jakarta - ${io.a2a.sdk.version} - -``` +## A2A Client -OR +The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. +To make use of the Java `A2AClient`, simply add the following dependency: ```xml io.a2a.sdk - a2a-java-sdk-server-quarkus + a2a-java-sdk-client ${io.a2a.sdk.version} ``` -## A2A Client - -The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. - ### Sample Usage #### Create an A2A client diff --git a/tck/pom.xml b/tck/pom.xml index ceaf52f45..b89071fc1 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -16,11 +16,6 @@ Server example to use with the A2A TCK - - io.a2a.sdk - a2a-java-sdk-spec - ${project.version} - io.a2a.sdk a2a-java-sdk-server-quarkus From 60203cb17666b06c0d6fbf71abeeb2fc5eac4350 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 8 Jul 2025 14:31:54 +0100 Subject: [PATCH 029/493] chore: Get ready to deploy to Maven Central using temporary groupId (#168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- README.md | 15 ++- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 6 +- .../examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 6 +- examples/helloworld/server/pom.xml | 4 +- pom.xml | 124 ++++++++++++++++-- sdk-jakarta/pom.xml | 2 +- sdk-quarkus/pom.xml | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 1 - sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 4 +- tests/server-common/pom.xml | 2 +- 15 files changed, 145 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 591611e48..5460f910b 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,13 @@ The A2A Java SDK provides two A2A server endpoint implementations, one based on Add **one** of the following dependencies to your project: +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* + ```xml - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-server-jakarta + ${io.a2a.sdk.version} ``` @@ -54,8 +57,9 @@ OR ```xml - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-server-quarkus + ${io.a2a.sdk.version} ``` @@ -198,10 +202,15 @@ public class WeatherAgentExecutorProducer { The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. To make use of the Java `A2AClient`, simply add the following dependency: +---- +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* +---- + ```xml - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-client + ${io.a2a.sdk.version} ``` diff --git a/client/pom.xml b/client/pom.xml index 975fb2928..3a0810318 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT diff --git a/common/pom.xml b/common/pom.xml index 3b6aabc36..faea6f033 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 9750da890..641d1012d 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-examples-parent 0.2.4-SNAPSHOT @@ -17,7 +17,7 @@ - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-client @@ -27,14 +27,12 @@ org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 33064a9e9..10544f43c 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.a2a.sdk:a2a-java-sdk-client:0.2.4-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.4-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 672d76655..de38c1ad1 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT ../../pom.xml @@ -27,12 +27,12 @@ import - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-client ${project.version} - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-server-quarkus ${project.version} diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 5e0b6c58e..03fcee860 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-examples-parent 0.2.4-SNAPSHOT @@ -17,7 +17,7 @@ - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-server-quarkus diff --git a/pom.xml b/pom.xml index 2cf37a9c7..620416977 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT @@ -13,10 +13,37 @@ Java SDK A2A Parent Java SDK for the Agent2Agent Protocol (A2A) + https://github.com/a2asdk/a2a-java-sdk + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + a2a-java + The a2a-java developers + a2aproject + https://github.com/a2aproject/a2a-java + + + + + scm:git:git://github.com/a2asdk/a2a-java-sdk.git + scm:git:ssh://github.com:a2asdk/a2a-java-sdk.git + https://github.com/a2asdk/a2a-java-sdk + + + UTF-8 3.11.0 + 3.4.2 + 3.3.1 3.1.2 + 0.8.0 2.17.0 4.1.0 2.0.1 @@ -33,6 +60,7 @@ true + 3.8.0 @@ -131,7 +159,7 @@ org.apache.maven.plugins maven-compiler-plugin - ${compiler-plugin.version} + ${maven-compiler-plugin.version} 17 17 @@ -142,13 +170,18 @@ maven-surefire-plugin - ${surefire-plugin.version} + ${maven-surefire-plugin.version} ${maven.home} + + org.apache.maven.plugins + maven-jar-plugin + 3.4.2 + io.quarkus quarkus-maven-plugin @@ -164,21 +197,56 @@ - + + org.sonatype.central + central-publishing-maven-plugin + ${sonatype-central-publishing-plugin.version} + true + + + central-a2asdk-temp + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + attach-javadocs + + jar + + + + org.apache.maven.plugins maven-compiler-plugin - ${maven-compiler-plugin.version} - - org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} + + + org.sonatype.central + central-publishing-maven-plugin @@ -194,4 +262,44 @@ examples/helloworld tests/server-common + + + + + release + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + + + \ No newline at end of file diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index af290970e..d01235686 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT diff --git a/sdk-quarkus/pom.xml b/sdk-quarkus/pom.xml index 6e232d297..1e2410278 100644 --- a/sdk-quarkus/pom.xml +++ b/sdk-quarkus/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT diff --git a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 827cc7041..7c45c0f9f 100644 --- a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -154,7 +154,6 @@ public AgentCard getAgentCard() { * Handles incoming GET requests to the authenticated extended agent card endpoint. * Returns the agent card in JSON format. * - * @return the authenticated extended agent card */ @Route(path = "/agent/authenticatedExtendedCard", methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) public void getAuthenticatedExtendedAgentCard(RoutingExchange re) { diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 848f943fe..7ae5e1835 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT diff --git a/spec/pom.xml b/spec/pom.xml index 450db7489..7de1871d2 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT diff --git a/tck/pom.xml b/tck/pom.xml index b89071fc1..45440c86e 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT @@ -17,7 +17,7 @@ - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-server-quarkus ${project.version} diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 394320f6c..6bdd88f5d 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -5,7 +5,7 @@ 4.0.0 - io.a2a.sdk + io.github.a2asdk a2a-java-sdk-parent 0.2.4-SNAPSHOT ../../pom.xml From 55a16fde092d882a702921410a0958e67e13c914 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 8 Jul 2025 15:08:26 +0100 Subject: [PATCH 030/493] chore: release 0.2.3.Alpha1 Release-As: 0.2.3.Alpha1 From a16db086803b43926d137e86b5e2df93288ae706 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 8 Jul 2025 17:24:37 +0100 Subject: [PATCH 031/493] chore: Prepare 0.2.3.Alpha1 release (#173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 40 ++++++++++--------- sdk-jakarta/pom.xml | 2 +- sdk-quarkus/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 13 files changed, 33 insertions(+), 31 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 3a0810318..b40e846bb 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index faea6f033..fc527942d 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 641d1012d..b3222a3d7 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 10544f43c..cad6fe2b5 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.4-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.3.Alpha2-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index de38c1ad1..9571ad591 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 03fcee860..6ed56b358 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index 620416977..eb4890269 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT pom @@ -43,6 +43,8 @@ 3.4.2 3.3.1 3.1.2 + 3.8.0 + 3.2.4 0.8.0 2.17.0 4.1.0 @@ -60,7 +62,6 @@ true - 3.8.0 @@ -233,6 +234,20 @@ + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + @@ -275,27 +290,14 @@ org.apache.maven.plugins maven-source-plugin - - - attach-sources - - jar-no-fork - - - - org.apache.maven.plugins maven-javadoc-plugin - - - attach-javadocs - - jar - - - + + + org.apache.maven.plugins + maven-gpg-plugin diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index d01235686..2cad1a6f6 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-server-jakarta diff --git a/sdk-quarkus/pom.xml b/sdk-quarkus/pom.xml index 1e2410278..19710cb76 100644 --- a/sdk-quarkus/pom.xml +++ b/sdk-quarkus/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-server-quarkus diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 7ae5e1835..3155cbc17 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index 7de1871d2..9c03159e7 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 45440c86e..f233ec605 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 6bdd88f5d..7ffdb2487 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.4-SNAPSHOT + 0.2.3.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common From abe92d8f531462b252f2875ba7b2b8b505b05e24 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 8 Jul 2025 17:39:43 +0100 Subject: [PATCH 032/493] chore: Prepare 0.2.3.Beta1 release (#174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../main/java/io/a2a/examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- sdk-jakarta/pom.xml | 2 +- sdk-quarkus/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index b40e846bb..64f983e42 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index fc527942d..9b3d81137 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index b3222a3d7..26f701934 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index cad6fe2b5..8583ef27a 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.3.Alpha2-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.3.Beta2-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 9571ad591..7c9cf8928 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 6ed56b358..9fce226f5 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index eb4890269..5508c0cba 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT pom diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml index 2cad1a6f6..394956c24 100644 --- a/sdk-jakarta/pom.xml +++ b/sdk-jakarta/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-server-jakarta diff --git a/sdk-quarkus/pom.xml b/sdk-quarkus/pom.xml index 19710cb76..daa9418f2 100644 --- a/sdk-quarkus/pom.xml +++ b/sdk-quarkus/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-server-quarkus diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 3155cbc17..f35b91280 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index 9c03159e7..cddec7a1c 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index f233ec605..0d2623c2f 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 7ffdb2487..0e205b547 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Alpha2-SNAPSHOT + 0.2.3.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common From 03ed17edf28d535134b8013b6f1ca9e911be6344 Mon Sep 17 00:00:00 2001 From: Stefano Maestri Date: Wed, 9 Jul 2025 21:41:29 +0200 Subject: [PATCH 033/493] fix: (run-tck): update TCK repository reference (#175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The a2a-tck repository has been moved from maeste/a2a-tck to a2aproject/a2a-tck. This commit updates the checkout action in the run-tck.yml workflow to point to the new repository location. # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .github/workflows/run-tck.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 726f2c29b..d2cba0890 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -11,7 +11,7 @@ on: env: # Tag of the TCK - TCK_VERSION: 0.2.3 + TCK_VERSION: v0.2.3 # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 @@ -29,7 +29,7 @@ jobs: - name: Checkout a2a-tck uses: actions/checkout@v4 with: - repository: maeste/a2a-tck + repository: a2aproject/a2a-tck path: tck/a2a-tck ref: ${{ env.TCK_VERSION }} - name: Set up JDK 17 From 8d31ddf2ae45457323d58c5861305d5e5215eb2a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 11 Jul 2025 15:48:14 +0100 Subject: [PATCH 034/493] feat: Externalise sdk-jakarta, add list of integrations and make Quarkus reference implementation (#178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #176 🦕 --- CONTRIBUTING_INTEGRATIONS.md | 15 + README.md | 29 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 3 +- reference-impl/README.md | 7 + {sdk-quarkus => reference-impl}/pom.xml | 6 +- .../server/apps/quarkus/A2AServerRoutes.java | 0 .../src/main/resources/META-INF/beans.xml | 0 .../apps/quarkus/QuarkusA2AServerTest.java | 0 .../src/test/resources/application.properties | 0 sdk-jakarta/pom.xml | 242 ----------------- .../server/apps/jakarta/A2ARequestFilter.java | 68 ----- .../apps/jakarta/A2AServerResource.java | 257 ------------------ .../src/main/resources/META-INF/beans.xml | 6 - sdk-jakarta/src/scripts/configure_logger.cli | 2 - .../apps/jakarta/JakartaA2AServerTest.java | 96 ------- .../server/apps/jakarta/RestApplication.java | 8 - .../src/test/resources/WEB-INF/web.xml | 12 - sdk-jakarta/src/test/resources/arquillian.xml | 19 -- .../src/test/resources/logging.properties | 29 -- tck/pom.xml | 2 +- 22 files changed, 43 insertions(+), 762 deletions(-) create mode 100644 CONTRIBUTING_INTEGRATIONS.md create mode 100644 reference-impl/README.md rename {sdk-quarkus => reference-impl}/pom.xml (92%) rename {sdk-quarkus => reference-impl}/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java (100%) rename {sdk-quarkus => reference-impl}/src/main/resources/META-INF/beans.xml (100%) rename {sdk-quarkus => reference-impl}/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java (100%) rename {sdk-quarkus => reference-impl}/src/test/resources/application.properties (100%) delete mode 100644 sdk-jakarta/pom.xml delete mode 100644 sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java delete mode 100644 sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java delete mode 100644 sdk-jakarta/src/main/resources/META-INF/beans.xml delete mode 100644 sdk-jakarta/src/scripts/configure_logger.cli delete mode 100644 sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java delete mode 100644 sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java delete mode 100644 sdk-jakarta/src/test/resources/WEB-INF/web.xml delete mode 100644 sdk-jakarta/src/test/resources/arquillian.xml delete mode 100644 sdk-jakarta/src/test/resources/logging.properties diff --git a/CONTRIBUTING_INTEGRATIONS.md b/CONTRIBUTING_INTEGRATIONS.md new file mode 100644 index 000000000..9fc06a520 --- /dev/null +++ b/CONTRIBUTING_INTEGRATIONS.md @@ -0,0 +1,15 @@ +# Contributing A2A SDK Integrations + +To add your A2A SDK Integration for your chosen runtime to the list of integrations in the [README](README.md#server-integrations), open a pull request adding it to the list. + +The pull request should contain a link to your project page. + +Then the project page itself needs to contain the following information as a minimum: + +* How to use the integration. + * Ideally there should be a sample demonstrating how to use it +* The integration should have tests, extending [AbstractA2AServerTest](tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java) +* The integration should pass the [TCK](https://github.com/a2aproject/a2a-tck), and make it obvious how to see that it has passed. +* Ideally, the integration should be deployed in Maven Central. If that is not possible, provide clear instructions for how to build it. + +If some of the above points are problematic, feel free to point that out in your pull request, and we can discuss further. For example, AbstractA2AServerTest is currently written with only the initial runtimes in mind, and might need some tweaking. \ No newline at end of file diff --git a/README.md b/README.md index 5460f910b..fe7d3bbc0 100644 --- a/README.md +++ b/README.md @@ -33,32 +33,23 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) - [Add a class that creates an A2A Agent Card](#2-add-a-class-that-creates-an-a2a-agent-card) - [Add a class that creates an A2A Agent Executor](#3-add-a-class-that-creates-an-a2a-agent-executor) -### 1. Add an A2A Java SDK Server Maven dependency to your project +### 1. Add the A2A Java SDK Server Maven dependency to your project Adding a dependency on an A2A Java SDK Server will provide access to the core classes that make up the A2A specification and allow you to run your agentic Java application as an A2A server agent. -The A2A Java SDK provides two A2A server endpoint implementations, one based on Jakarta REST (`a2a-java-sdk-server-jakarta`) and one based on Quarkus Reactive Routes (`a2a-java-sdk-server-quarkus`). You can choose the one that best fits your application. +The A2A Java SDK provides a [reference A2A server implementation](reference-impl/README.md) based on [Quarkus](https://quarkus.io) for use with our tests and examples. However, the project is designed in such a way that it is trivial to integrate with various Java runtimes. -Add **one** of the following dependencies to your project: +[Server Integrations](#server-integrations) contains a list of community contributed integrations of the server with various runtimes. You might be able to use one of these for your target runtime, or you can use them as inspiration to create your own. -> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* +To use the reference implementation add the following dependency to your project: -```xml - - io.github.a2asdk - a2a-java-sdk-server-jakarta - - ${io.a2a.sdk.version} - -``` - -OR +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* ```xml io.github.a2asdk - a2a-java-sdk-server-quarkus + a2a-java-reference-server ${io.a2a.sdk.version} @@ -372,5 +363,13 @@ This project is licensed under the terms of the [Apache 2.0 License](LICENSE). See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. +## Server Integrations +The following list contains community contributed integrations with various Java Runtimes. + +To contribute an integration, please see [CONTRIBUTING_INTEGRATIONS.md](CONTRIBUTING_INTEGRATIONS.md). + +* [reference-impl/README.md](reference-impl/README.md) - Reference implementation, based on Quarkus. +* https://github.com/wildfly-extras/a2a-java-sdk-server-jakarta - This integration is based on Jakarta EE, and should work in all runtimes supporting the [Jakarta EE Web Profile](https://jakarta.ee/specifications/webprofile/). + diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 7c9cf8928..3b7bd849d 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -33,7 +33,7 @@ io.github.a2asdk - a2a-java-sdk-server-quarkus + a2a-java-reference-server ${project.version} diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 9fce226f5..3f5f74a1e 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -18,7 +18,7 @@ io.github.a2asdk - a2a-java-sdk-server-quarkus + a2a-java-reference-server io.quarkus diff --git a/pom.xml b/pom.xml index 5508c0cba..9fd7cfeaf 100644 --- a/pom.xml +++ b/pom.xml @@ -271,8 +271,7 @@ common spec client - sdk-jakarta - sdk-quarkus + reference-impl tck examples/helloworld tests/server-common diff --git a/reference-impl/README.md b/reference-impl/README.md new file mode 100644 index 000000000..2a7f0f902 --- /dev/null +++ b/reference-impl/README.md @@ -0,0 +1,7 @@ +# A2A Java SDK Reference Server Integration + +This is a reference server for the A2A SDK for Java, that we use to run tests, as well as to demonstrate examples. + +It is based on [Quarkus](https://quarkus.io), and makes use of Quarkus's [Reactive Routes](https://quarkus.io/guides/reactive-routes). + +It is a great choice if you use Quarkus! \ No newline at end of file diff --git a/sdk-quarkus/pom.xml b/reference-impl/pom.xml similarity index 92% rename from sdk-quarkus/pom.xml rename to reference-impl/pom.xml index daa9418f2..15a62e8b0 100644 --- a/sdk-quarkus/pom.xml +++ b/reference-impl/pom.xml @@ -9,12 +9,12 @@ a2a-java-sdk-parent 0.2.3.Beta2-SNAPSHOT - a2a-java-sdk-server-quarkus + a2a-java-reference-server jar - Java A2A SDK for Quarkus - Java SDK for the Agent2Agent Protocol (A2A) - SDK - Quarkus + Java A2A Reference Server + Java SDK for the Agent2Agent Protocol (A2A) - A2A Reference Server (based on Quarkus) diff --git a/sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java similarity index 100% rename from sdk-quarkus/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java rename to reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java diff --git a/sdk-quarkus/src/main/resources/META-INF/beans.xml b/reference-impl/src/main/resources/META-INF/beans.xml similarity index 100% rename from sdk-quarkus/src/main/resources/META-INF/beans.xml rename to reference-impl/src/main/resources/META-INF/beans.xml diff --git a/sdk-quarkus/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java similarity index 100% rename from sdk-quarkus/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java rename to reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java diff --git a/sdk-quarkus/src/test/resources/application.properties b/reference-impl/src/test/resources/application.properties similarity index 100% rename from sdk-quarkus/src/test/resources/application.properties rename to reference-impl/src/test/resources/application.properties diff --git a/sdk-jakarta/pom.xml b/sdk-jakarta/pom.xml deleted file mode 100644 index 394956c24..000000000 --- a/sdk-jakarta/pom.xml +++ /dev/null @@ -1,242 +0,0 @@ - - - 4.0.0 - - - io.github.a2asdk - a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT - - a2a-java-sdk-server-jakarta - - jar - - Java A2A SDK for Jakarta - Java SDK for the Agent2Agent Protocol (A2A) - SDK - Jakarta - - 5.1.0.Beta11 - 1.10.0.Final - - 1.2.6 - 10.0.0.Final - 3.3.4 - ${project.build.directory}${file.separator}wildfly - - 8787 - - - - - org.jboss.shrinkwrap - shrinkwrap-bom - ${version.org.jboss.shrinkwrap.shrinkwrap} - pom - import - - - org.jboss.arquillian - arquillian-bom - ${version.org.jboss.arquillian} - pom - import - - - org.wildfly.arquillian - wildfly-arquillian-bom - ${version.org.wildfly.arquillian} - pom - import - - - - - - ${project.groupId} - a2a-java-sdk-spec - ${project.version} - - - ${project.groupId} - a2a-java-sdk-server-common - ${project.version} - - - ${project.groupId} - a2a-java-sdk-tests-server-common - ${project.version} - provided - - - ${project.groupId} - a2a-java-sdk-tests-server-common - test-jar - test - ${project.version} - - - com.fasterxml.jackson.core - jackson-databind - provided - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - - - jakarta.inject - jakarta.inject-api - provided - - - jakarta.json - jakarta.json-api - provided - - - jakarta.ws.rs - jakarta.ws.rs-api - provided - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - test - - - io.rest-assured - rest-assured - test - - - org.jboss.arquillian.junit5 - arquillian-junit5-container - test - - - org.wildfly.arquillian - wildfly-arquillian-container-managed - test - - - org.jboss.shrinkwrap - shrinkwrap-api - test - - - org.junit.jupiter - junit-jupiter - test - - - org.jboss.threads - jboss-threads - 3.9.1 - test - - - org.hamcrest - hamcrest - test - - - - - - org.wildfly.glow - wildfly-glow-arquillian-plugin - 1.4.1.Final - - - - org.wildfly - wildfly-galleon-pack - 36.0.1.Final - - - standalone.xml - - - - scan - - scan - - test-compile - - - - - org.wildfly.plugins - wildfly-maven-plugin - 5.1.3.Final - - ${project.build.directory}/glow-scan/provisioning.xml - ${jboss.home} - ${jboss.home} - - - - - - - - - - - test-provisioning - - package - - test-compile - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.5.3 - - - ${jboss.home} - arquillian.xml - ${arquillian.java.vm.args} - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.8.1 - - ${project.build.directory}/lib - - - - copy - generate-test-resources - - copy-dependencies - - - test - provided - - - - - - - - - debug.profile - debug - - -agentlib:jdwp=transport=dt_socket,address=*:${server.debug.port},server=y,suspend=y - - - - \ No newline at end of file diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java deleted file mode 100644 index 32e47e783..000000000 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2ARequestFilter.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.a2a.server.apps.jakarta; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.TaskResubscriptionRequest; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.ContainerRequestFilter; -import jakarta.ws.rs.container.PreMatching; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.ext.Provider; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Provider -@PreMatching -public class A2ARequestFilter implements ContainerRequestFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(A2ARequestFilter.class); - - @Override - public void filter(ContainerRequestContext requestContext) { - if (requestContext.getMethod().equals("POST") && requestContext.hasEntity()) { - try (InputStream entityInputStream = requestContext.getEntityStream()) { - byte[] requestBodyBytes = entityInputStream.readAllBytes(); - String requestBody = new String(requestBodyBytes); - // ensure the request is treated as a streaming request or a non-streaming request - // based on the method in the request body - if (isStreamingRequest(requestBody)) { - LOGGER.debug("Handling request as streaming: {}", requestBody); - putAcceptHeader(requestContext, MediaType.SERVER_SENT_EVENTS); - } else if (isNonStreamingRequest(requestBody)) { - LOGGER.debug("Handling request as non-streaming: {}", requestBody); - putAcceptHeader(requestContext, MediaType.APPLICATION_JSON); - } - // reset the entity stream - requestContext.setEntityStream(new ByteArrayInputStream(requestBodyBytes)); - } catch(IOException e){ - throw new RuntimeException("Unable to read the request body"); - } - } - } - - private static boolean isStreamingRequest(String requestBody) { - return requestBody.contains(SendStreamingMessageRequest.METHOD) || - requestBody.contains(TaskResubscriptionRequest.METHOD); - } - - private static boolean isNonStreamingRequest(String requestBody) { - return requestBody.contains(GetTaskRequest.METHOD) || - requestBody.contains(CancelTaskRequest.METHOD) || - requestBody.contains(SendMessageRequest.METHOD) || - requestBody.contains(SetTaskPushNotificationConfigRequest.METHOD) || - requestBody.contains(GetTaskPushNotificationConfigRequest.METHOD); - } - - private static void putAcceptHeader(ContainerRequestContext requestContext, String mediaType) { - requestContext.getHeaders().putSingle("Accept", mediaType); - } - -} \ No newline at end of file diff --git a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java b/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java deleted file mode 100644 index 1e4db8c39..000000000 --- a/sdk-jakarta/src/main/java/io/a2a/server/apps/jakarta/A2AServerResource.java +++ /dev/null @@ -1,257 +0,0 @@ -package io.a2a.server.apps.jakarta; - -import java.util.concurrent.Executor; -import java.util.concurrent.Flow; - -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Context; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.ext.ExceptionMapper; -import jakarta.ws.rs.ext.Provider; -import jakarta.ws.rs.sse.Sse; -import jakarta.ws.rs.sse.SseEventSink; - -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; -import io.a2a.server.ExtendedAgentCard; -import io.a2a.server.requesthandlers.JSONRPCHandler; -import io.a2a.server.util.async.Internal; -import io.a2a.spec.AgentCard; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.IdJsonMappingException; -import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.InvalidParamsJsonMappingException; -import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONErrorResponse; -import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.JSONRPCErrorResponse; -import io.a2a.spec.JSONRPCRequest; -import io.a2a.spec.JSONRPCResponse; -import io.a2a.spec.MethodNotFoundError; -import io.a2a.spec.MethodNotFoundJsonMappingException; -import io.a2a.spec.NonStreamingJSONRPCRequest; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.StreamingJSONRPCRequest; -import io.a2a.spec.TaskResubscriptionRequest; -import io.a2a.spec.UnsupportedOperationError; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Path("/") -public class A2AServerResource { - - private static final Logger LOGGER = LoggerFactory.getLogger(A2AServerResource.class); - - @Inject - JSONRPCHandler jsonRpcHandler; - - @Inject - @ExtendedAgentCard - Instance extendedAgentCard; - - // Hook so testing can wait until the async Subscription is subscribed. - private static volatile Runnable streamingIsSubscribedRunnable; - - @Inject - @Internal - Executor executor; - - /** - * Handles incoming POST requests to the main A2A endpoint. Dispatches the - * request to the appropriate JSON-RPC handler method and returns the response. - * - * @param request the JSON-RPC request - * @return the JSON-RPC response which may be an error response - */ - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public JSONRPCResponse handleNonStreamingRequests(NonStreamingJSONRPCRequest request) { - LOGGER.debug("Handling non-streaming request"); - try { - return processNonStreamingRequest(request); - } finally { - LOGGER.debug("Completed non-streaming request"); - } - } - - /** - * Handles incoming POST requests to the main A2A endpoint that involve Server-Sent Events (SSE). - * Dispatches the request to the appropriate JSON-RPC handler method and returns the response. - */ - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.SERVER_SENT_EVENTS) - public void handleStreamingRequests(StreamingJSONRPCRequest request, @Context SseEventSink sseEventSink, @Context Sse sse) { - LOGGER.debug("Handling streaming request"); - executor.execute(() -> processStreamingRequest(request, sseEventSink, sse)); - LOGGER.debug("Submitted streaming request for async processing"); - } - - /** - * Handles incoming GET requests to the agent card endpoint. - * Returns the agent card in JSON format. - * - * @return the agent card - */ - @GET - @Path("/.well-known/agent.json") - @Produces(MediaType.APPLICATION_JSON) - public AgentCard getAgentCard() { - return jsonRpcHandler.getAgentCard(); - } - - /** - * Handles incoming GET requests to the authenticated extended agent card endpoint. - * Returns the agent card in JSON format. - * - * @return the authenticated extended agent card - */ - @GET - @Path("/agent/authenticatedExtendedCard") - @Produces(MediaType.APPLICATION_JSON) - public Response getAuthenticatedExtendedAgentCard() { - // TODO need to add authentication for this endpoint - // https://github.com/a2aproject/a2a-java/issues/77 - if (! jsonRpcHandler.getAgentCard().supportsAuthenticatedExtendedCard()) { - JSONErrorResponse errorResponse = new JSONErrorResponse("Extended agent card not supported or not enabled."); - return Response.status(Response.Status.NOT_FOUND) - .entity(errorResponse).build(); - } - if (! extendedAgentCard.isResolvable()) { - JSONErrorResponse errorResponse = new JSONErrorResponse("Authenticated extended agent card is supported but not configured on the server."); - return Response.status(Response.Status.NOT_FOUND) - .entity(errorResponse).build(); - } - return Response.ok(extendedAgentCard.get()) - .type(MediaType.APPLICATION_JSON) - .build(); - } - - private JSONRPCResponse processNonStreamingRequest(NonStreamingJSONRPCRequest request) { - if (request instanceof GetTaskRequest) { - return jsonRpcHandler.onGetTask((GetTaskRequest) request); - } else if (request instanceof CancelTaskRequest) { - return jsonRpcHandler.onCancelTask((CancelTaskRequest) request); - } else if (request instanceof SetTaskPushNotificationConfigRequest) { - return jsonRpcHandler.setPushNotification((SetTaskPushNotificationConfigRequest) request); - } else if (request instanceof GetTaskPushNotificationConfigRequest) { - return jsonRpcHandler.getPushNotification((GetTaskPushNotificationConfigRequest) request); - } else if (request instanceof SendMessageRequest) { - return jsonRpcHandler.onMessageSend((SendMessageRequest) request); - } else { - return generateErrorResponse(request, new UnsupportedOperationError()); - } - } - - private void processStreamingRequest(StreamingJSONRPCRequest request, SseEventSink sseEventSink, Sse sse) { - Flow.Publisher> publisher; - if (request instanceof SendStreamingMessageRequest) { - publisher = jsonRpcHandler.onMessageSendStream((SendStreamingMessageRequest) request); - handleStreamingResponse(publisher, sseEventSink, sse); - } else if (request instanceof TaskResubscriptionRequest) { - publisher = jsonRpcHandler.onResubscribeToTask((TaskResubscriptionRequest) request); - handleStreamingResponse(publisher, sseEventSink, sse); - } - } - - private void handleStreamingResponse(Flow.Publisher> publisher, SseEventSink sseEventSink, Sse sse) { - publisher.subscribe(new Flow.Subscriber>() { - private Flow.Subscription subscription; - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - subscription.request(Long.MAX_VALUE); - // Notify tests that we are subscribed - Runnable runnable = streamingIsSubscribedRunnable; - if (runnable != null) { - runnable.run(); - } - } - - @Override - public void onNext(JSONRPCResponse item) { - - sseEventSink.send(sse.newEventBuilder() - .mediaType(MediaType.APPLICATION_JSON_TYPE) - .data(item) - .build()); - } - - @Override - public void onError(Throwable throwable) { - // TODO - sseEventSink.close(); - } - - @Override - public void onComplete() { - sseEventSink.close(); - } - }); - } - - private JSONRPCResponse generateErrorResponse(JSONRPCRequest request, JSONRPCError error) { - return new JSONRPCErrorResponse(request.getId(), error); - } - - static void setStreamingIsSubscribedRunnable(Runnable streamingIsSubscribedRunnable) { - A2AServerResource.streamingIsSubscribedRunnable = streamingIsSubscribedRunnable; - } - - @Provider - public static class JsonParseExceptionMapper implements ExceptionMapper { - - public JsonParseExceptionMapper() { - } - - @Override - public Response toResponse(JsonParseException exception) { - // parse error, not possible to determine the request id - return Response.ok(new JSONRPCErrorResponse(new JSONParseError())).type(MediaType.APPLICATION_JSON).build(); - } - - } - - @Provider - public static class JsonMappingExceptionMapper implements ExceptionMapper { - - public JsonMappingExceptionMapper(){ - } - - @Override - public Response toResponse(JsonMappingException exception) { - if (exception.getCause() instanceof JsonParseException) { - return Response.ok(new JSONRPCErrorResponse(new JSONParseError())).type(MediaType.APPLICATION_JSON).build(); - } else if (exception instanceof MethodNotFoundJsonMappingException) { - Object id = ((MethodNotFoundJsonMappingException) exception).getId(); - return Response.ok(new JSONRPCErrorResponse(id, new MethodNotFoundError())) - .type(MediaType.APPLICATION_JSON).build(); - } else if (exception instanceof InvalidParamsJsonMappingException) { - Object id = ((InvalidParamsJsonMappingException) exception).getId(); - return Response.ok(new JSONRPCErrorResponse(id, new InvalidParamsError())) - .type(MediaType.APPLICATION_JSON).build(); - } else if (exception instanceof IdJsonMappingException) { - Object id = ((IdJsonMappingException) exception).getId(); - return Response.ok(new JSONRPCErrorResponse(id, new InvalidRequestError())) - .type(MediaType.APPLICATION_JSON).build(); - } - // not possible to determine the request id - return Response.ok(new JSONRPCErrorResponse(new InvalidRequestError())).type(MediaType.APPLICATION_JSON).build(); - } - - } -} \ No newline at end of file diff --git a/sdk-jakarta/src/main/resources/META-INF/beans.xml b/sdk-jakarta/src/main/resources/META-INF/beans.xml deleted file mode 100644 index 9dfae34df..000000000 --- a/sdk-jakarta/src/main/resources/META-INF/beans.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/sdk-jakarta/src/scripts/configure_logger.cli b/sdk-jakarta/src/scripts/configure_logger.cli deleted file mode 100644 index a45fb245d..000000000 --- a/sdk-jakarta/src/scripts/configure_logger.cli +++ /dev/null @@ -1,2 +0,0 @@ -/subsystem=logging/logger=org.jboss.weld:add(level=DEBUG) -/subsystem=logging/logger=io.a2a:add(level=DEBUG) \ No newline at end of file diff --git a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java deleted file mode 100644 index 5bdb5225c..000000000 --- a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/JakartaA2AServerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.a2a.server.apps.jakarta; - - - -import io.a2a.server.apps.common.AbstractA2AServerTest; -import io.a2a.server.apps.common.AgentCardProducer; -import io.a2a.server.apps.common.AgentExecutorProducer; -import io.a2a.server.events.InMemoryQueueManager; -import io.a2a.server.tasks.TaskStore; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.junit5.container.annotation.ArquillianTest; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.WebArchive; - -@ArquillianTest -@ApplicationScoped -public class JakartaA2AServerTest extends AbstractA2AServerTest { - - public JakartaA2AServerTest() { - super(8080); - } - - @Deployment - public static WebArchive createTestArchive() throws IOException { - final List prefixes = List.of( - "a2a-java-sdk-client", - "a2a-java-sdk-common", - "a2a-java-sdk-server-common", - "a2a-java-sdk-spec", - "jackson", - "mutiny", - "slf4j", - "rest-assured", - "groovy", - "http", - "commons", - "xml-path", - "json-path", - "hamcrest" - ); - List libraries = new ArrayList<>(); - try (DirectoryStream stream = Files.newDirectoryStream(Paths.get("target").resolve("lib"))) { - for (Path file : stream) { - String fileName = file.getFileName().toString(); - if (prefixes.stream().anyMatch(fileName::startsWith)) { - libraries.add(file.toFile()); - } - } - } - WebArchive archive = ShrinkWrap.create(WebArchive.class, "ROOT.war") - .addAsLibraries(libraries.toArray(new File[libraries.size()])) - .addClass(AbstractA2AServerTest.class) - .addClass(AgentCardProducer.class) - .addClass(AgentExecutorProducer.class) - .addClass(JakartaA2AServerTest.class) - .addClass(A2ARequestFilter.class) - .addClass(A2AServerResource.class) - .addClass(RestApplication.class) - .addAsManifestResource("META-INF/beans.xml", "beans.xml") - .addAsWebInfResource("META-INF/beans.xml", "beans.xml") - .addAsWebInfResource("WEB-INF/web.xml", "web.xml"); - return archive; - } - - @Inject - TaskStore taskStore; - - @Inject - InMemoryQueueManager queueManager; - - @Override - protected TaskStore getTaskStore() { - return taskStore; - } - - @Override - protected InMemoryQueueManager getQueueManager() { - return queueManager; - } - - @Override - protected void setStreamingSubscribedRunnable(Runnable runnable) { - A2AServerResource.setStreamingIsSubscribedRunnable(runnable); - } -} diff --git a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java b/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java deleted file mode 100644 index e2fcf6af3..000000000 --- a/sdk-jakarta/src/test/java/io/a2a/server/apps/jakarta/RestApplication.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.a2a.server.apps.jakarta; - -import jakarta.ws.rs.ApplicationPath; -import jakarta.ws.rs.core.Application; - -@ApplicationPath("/") -public class RestApplication extends Application { -} diff --git a/sdk-jakarta/src/test/resources/WEB-INF/web.xml b/sdk-jakarta/src/test/resources/WEB-INF/web.xml deleted file mode 100644 index 2678fbc83..000000000 --- a/sdk-jakarta/src/test/resources/WEB-INF/web.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - 30 - - - diff --git a/sdk-jakarta/src/test/resources/arquillian.xml b/sdk-jakarta/src/test/resources/arquillian.xml deleted file mode 100644 index 804afef5d..000000000 --- a/sdk-jakarta/src/test/resources/arquillian.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - ${basedir}/target/wildfly - - ${arquillian.java.vm.args} - true - - - - diff --git a/sdk-jakarta/src/test/resources/logging.properties b/sdk-jakarta/src/test/resources/logging.properties deleted file mode 100644 index 17885c831..000000000 --- a/sdk-jakarta/src/test/resources/logging.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright The WildFly Authors -# SPDX-License-Identifier: Apache-2.0 -# - -# Additional logger names to configure (root logger is always configured) -loggers=sun.rmi,org.jboss.shrinkwrap,org.apache.http.wire -logger.org.jboss.shrinkwrap.level=INFO -logger.sun.rmi.level=WARNING -logger.org.apache.http.wire.level=WARN - -# Root logger level -logger.level=WARN - -# Root logger handlers -logger.handlers=FILE - -# File handler configuration -handler.FILE=org.jboss.logmanager.handlers.FileHandler -handler.FILE.properties=autoFlush,append,fileName -handler.FILE.autoFlush=true -handler.FILE.fileName=./target/test.log -handler.FILE.formatter=PATTERN -handler.FILE.append=true - -# Formatter pattern configuration -formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter -formatter.PATTERN.properties=pattern -formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n diff --git a/tck/pom.xml b/tck/pom.xml index 0d2623c2f..6ebe03b10 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -18,7 +18,7 @@ io.github.a2asdk - a2a-java-sdk-server-quarkus + a2a-java-reference-server ${project.version} From d121d5c325239ed0dfe44d7a61a247d63151ae71 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 11 Jul 2025 16:18:31 -0400 Subject: [PATCH 035/493] fix: Incorporate latest feedback from https://github.com/a2aproject/a2a-java/pull/138 (#182) # Description This PR incorporates the latest feedback from https://github.com/a2aproject/a2a-java/pull/138. - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- .../java/io/a2a/client/A2ACardResolver.java | 27 ++++++++++------- .../io/a2a/client/A2ACardResolverTest.java | 30 ++++++++++++------- .../server/apps/quarkus/A2AServerRoutes.java | 19 ++++++------ .../io/a2a/server/tasks/ResultAggregator.java | 7 ++--- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/client/src/main/java/io/a2a/client/A2ACardResolver.java b/client/src/main/java/io/a2a/client/A2ACardResolver.java index 1266f7219..88d1e351f 100644 --- a/client/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/client/src/main/java/io/a2a/client/A2ACardResolver.java @@ -3,6 +3,8 @@ import static io.a2a.util.Utils.unmarshalFrom; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; @@ -18,14 +20,16 @@ public class A2ACardResolver { private final String url; private final Map authHeaders; - static String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent.json"; + private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent.json"; + + private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; - static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; /** * @param httpClient the http client to use * @param baseUrl the base URL for the agent whose agent card we want to retrieve + * @throws A2AClientError if the URL for the agent is invalid */ - public A2ACardResolver(A2AHttpClient httpClient, String baseUrl) { + public A2ACardResolver(A2AHttpClient httpClient, String baseUrl) throws A2AClientError { this(httpClient, baseUrl, null, null); } @@ -34,8 +38,9 @@ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl) { * @param baseUrl the base URL for the agent whose agent card we want to retrieve * @param agentCardPath optional path to the agent card endpoint relative to the base * agent URL, defaults to ".well-known/agent.json" + * @throws A2AClientError if the URL for the agent is invalid */ - public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCardPath) { + public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCardPath) throws A2AClientError { this(httpClient, baseUrl, agentCardPath, null); } @@ -45,17 +50,17 @@ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCar * @param agentCardPath optional path to the agent card endpoint relative to the base * agent URL, defaults to ".well-known/agent.json" * @param authHeaders the HTTP authentication headers to use. May be {@code null} + * @throws A2AClientError if the URL for the agent is invalid */ - public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCardPath, Map authHeaders) { + public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCardPath, + Map authHeaders) throws A2AClientError { this.httpClient = httpClient; - if (!baseUrl.endsWith("/")) { - baseUrl += "/"; - } agentCardPath = agentCardPath == null || agentCardPath.isEmpty() ? DEFAULT_AGENT_CARD_PATH : agentCardPath; - if (agentCardPath.startsWith("/")) { - agentCardPath = agentCardPath.substring(1); + try { + this.url = new URI(baseUrl).resolve(agentCardPath).toString(); + } catch (URISyntaxException e) { + throw new A2AClientError("Invalid agent URL", e); } - this.url = baseUrl + agentCardPath; this.authHeaders = authHeaders; } diff --git a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java index 8265b9514..8d9ff0f5b 100644 --- a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java +++ b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java @@ -1,6 +1,5 @@ package io.a2a.client; -import static io.a2a.client.A2ACardResolver.AGENT_CARD_TYPE_REFERENCE; import static io.a2a.util.Utils.OBJECT_MAPPER; import static io.a2a.util.Utils.unmarshalFrom; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -11,6 +10,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.http.A2AHttpClient; import io.a2a.http.A2AHttpResponse; import io.a2a.spec.A2AClientError; @@ -19,6 +19,10 @@ import org.junit.jupiter.api.Test; public class A2ACardResolverTest { + + private static final String AGENT_CARD_PATH = "/.well-known/agent.json"; + private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; + @Test public void testConstructorStripsSlashes() throws Exception { TestHttpClient client = new TestHttpClient(); @@ -27,33 +31,37 @@ public void testConstructorStripsSlashes() throws Exception { A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/"); AgentCard card = resolver.getAgentCard(); - assertEquals("http://example.com" + A2ACardResolver.DEFAULT_AGENT_CARD_PATH, client.url); + assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); resolver = new A2ACardResolver(client, "http://example.com"); card = resolver.getAgentCard(); - assertEquals("http://example.com" + A2ACardResolver.DEFAULT_AGENT_CARD_PATH, client.url); + assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); - resolver = new A2ACardResolver(client, "http://example.com/", A2ACardResolver.DEFAULT_AGENT_CARD_PATH); + // baseUrl with trailing slash, agentCardParth with leading slash + resolver = new A2ACardResolver(client, "http://example.com/", AGENT_CARD_PATH); card = resolver.getAgentCard(); - assertEquals("http://example.com" + A2ACardResolver.DEFAULT_AGENT_CARD_PATH, client.url); + assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); - resolver = new A2ACardResolver(client, "http://example.com", A2ACardResolver.DEFAULT_AGENT_CARD_PATH); + // baseUrl without trailing slash, agentCardPath with leading slash + resolver = new A2ACardResolver(client, "http://example.com", AGENT_CARD_PATH); card = resolver.getAgentCard(); - assertEquals("http://example.com" + A2ACardResolver.DEFAULT_AGENT_CARD_PATH, client.url); + assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); - resolver = new A2ACardResolver(client, "http://example.com/", A2ACardResolver.DEFAULT_AGENT_CARD_PATH.substring(0)); + // baseUrl with trailing slash, agentCardPath without leading slash + resolver = new A2ACardResolver(client, "http://example.com/", AGENT_CARD_PATH.substring(1)); card = resolver.getAgentCard(); - assertEquals("http://example.com" + A2ACardResolver.DEFAULT_AGENT_CARD_PATH, client.url); + assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); - resolver = new A2ACardResolver(client, "http://example.com", A2ACardResolver.DEFAULT_AGENT_CARD_PATH.substring(0)); + // baseUrl without trailing slash, agentCardPath without leading slash + resolver = new A2ACardResolver(client, "http://example.com", AGENT_CARD_PATH.substring(1)); card = resolver.getAgentCard(); - assertEquals("http://example.com" + A2ACardResolver.DEFAULT_AGENT_CARD_PATH, client.url); + assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); } diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 7c45c0f9f..acc363868 100644 --- a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -8,6 +8,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; +import com.fasterxml.jackson.databind.JsonNode; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import jakarta.inject.Singleton; @@ -212,16 +213,14 @@ private JSONRPCResponse generateErrorResponse(JSONRPCRequest request, JSON } private static boolean isStreamingRequest(String requestBody) { - return requestBody.contains(SendStreamingMessageRequest.METHOD) || - requestBody.contains(TaskResubscriptionRequest.METHOD); - } - - private static boolean isNonStreamingRequest(String requestBody) { - return requestBody.contains(GetTaskRequest.METHOD) || - requestBody.contains(CancelTaskRequest.METHOD) || - requestBody.contains(SendMessageRequest.METHOD) || - requestBody.contains(SetTaskPushNotificationConfigRequest.METHOD) || - requestBody.contains(GetTaskPushNotificationConfigRequest.METHOD); + try { + JsonNode node = Utils.OBJECT_MAPPER.readTree(requestBody); + JsonNode method = node != null ? node.get("method") : null; + return method != null && (SendStreamingMessageRequest.METHOD.equals(method.asText()) + || TaskResubscriptionRequest.METHOD.equals(method.asText())); + } catch (Exception e) { + return false; + } } static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index 5211d1deb..ad9ed9124 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -4,6 +4,7 @@ import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import static io.a2a.server.util.async.AsyncUtils.processor; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -100,11 +101,7 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer) // new request is expected in order for the agent to make progress, // so the agent should exit. - // TODO There is the following line in the Python code I don't totally get - // asyncio.create_task(self._continue_consuming(event_stream)) - // I think it means the continueConsuming() call should be done in another thread - continueConsuming(all); - + CompletableFuture.runAsync(() -> continueConsuming(all)); interrupted.set(true); return false; } From c3e7f9e49c91f0db611953c81de51172d7335de2 Mon Sep 17 00:00:00 2001 From: ChenKangQiang Date: Mon, 14 Jul 2025 22:24:56 +0800 Subject: [PATCH 036/493] fix: client sse (#177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix client send stream message without set text/event-stream. # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [ ] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [ ] Ensure the tests pass - [ ] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Co-authored-by: chenkangqiang --- client/src/main/java/io/a2a/http/JdkA2AHttpClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java b/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java index e3b5c0c66..c3d5907a2 100644 --- a/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java +++ b/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java @@ -141,7 +141,7 @@ public CompletableFuture getAsyncSSE( Consumer messageConsumer, Consumer errorConsumer, Runnable completeRunnable) throws IOException, InterruptedException { - HttpRequest request = createRequestBuilder(false) + HttpRequest request = createRequestBuilder(true) .build(); return super.asyncRequest(request, messageConsumer, errorConsumer, completeRunnable); } @@ -180,7 +180,7 @@ public CompletableFuture postAsyncSSE( Consumer messageConsumer, Consumer errorConsumer, Runnable completeRunnable) throws IOException, InterruptedException { - HttpRequest request = createRequestBuilder(false) + HttpRequest request = createRequestBuilder(true) .build(); return super.asyncRequest(request, messageConsumer, errorConsumer, completeRunnable); } From f902f1800bc06c8681de0e447a3e08ce732f99b9 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 14 Jul 2025 17:17:06 +0100 Subject: [PATCH 037/493] feat: Rework tests so they don't rely on running in container (#185) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description While running the tests in a container is possible in Quarkus, and in WildFly (with Arquillian), this might not be possible for all Java runtimes. Hence, this PR changes the tests so the interactions with the TaskStore, QueueManager, and being notified about the subscription to a streaming request happening. Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../server/apps/quarkus/A2AServerRoutes.java | 5 +- .../server/apps/quarkus/A2ATestRoutes.java | 149 +++++++++++ .../apps/quarkus/QuarkusA2AServerTest.java | 25 -- .../apps/common/AbstractA2AServerTest.java | 231 ++++++++++++++---- .../a2a/server/apps/common/TestUtilsBean.java | 50 ++++ 5 files changed, 389 insertions(+), 71 deletions(-) create mode 100644 reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java create mode 100644 tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index acc363868..1500dfe4c 100644 --- a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -8,7 +8,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; -import com.fasterxml.jackson.databind.JsonNode; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import jakarta.inject.Singleton; @@ -17,8 +16,10 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; +import com.fasterxml.jackson.databind.JsonNode; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.requesthandlers.JSONRPCHandler; +import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.GetTaskPushNotificationConfigRequest; @@ -44,7 +45,6 @@ import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; -import io.a2a.server.util.async.Internal; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; @@ -69,6 +69,7 @@ public class A2AServerRoutes { Instance extendedAgentCard; // Hook so testing can wait until the MultiSseSupport is subscribed. + // Without this we get intermittent failures private static volatile Runnable streamingMultiSseSupportSubscribedRunnable; @Inject diff --git a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java new file mode 100644 index 000000000..bc6a342a6 --- /dev/null +++ b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java @@ -0,0 +1,149 @@ +package io.a2a.server.apps.quarkus; + +import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; + +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import io.a2a.server.apps.common.TestUtilsBean; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.util.Utils; +import io.quarkus.vertx.web.Body; +import io.quarkus.vertx.web.Param; +import io.quarkus.vertx.web.Route; +import io.vertx.ext.web.RoutingContext; + +/** + * Exposes the {@link TestUtilsBean} via REST using Quarkus Reactive Routes + */ +@Singleton +public class A2ATestRoutes { + @Inject + TestUtilsBean testUtilsBean; + + @Inject + A2AServerRoutes a2AServerRoutes; + + AtomicInteger streamingSubscribedCount = new AtomicInteger(0); + + @PostConstruct + public void init() { + A2AServerRoutes.setStreamingMultiSseSupportSubscribedRunnable(() -> streamingSubscribedCount.incrementAndGet()); + } + + + @Route(path = "/test/task", methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void saveTask(@Body String body, RoutingContext rc) { + try { + Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class); + testUtilsBean.saveTask(task); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.GET}, produces = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void getTask(@Param String taskId, RoutingContext rc) { + try { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + rc.response() + .setStatusCode(200) + .putHeader(CONTENT_TYPE, APPLICATION_JSON) + .end(Utils.OBJECT_MAPPER.writeValueAsString(task)); + + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) + public void deleteTask(@Param String taskId, RoutingContext rc) { + try { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + testUtilsBean.deleteTask(taskId); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/queue/ensure/:taskId", methods = {Route.HttpMethod.POST}) + public void ensureTaskQueue(@Param String taskId, RoutingContext rc) { + try { + testUtilsBean.ensureQueue(taskId); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/queue/enqueueTaskStatusUpdateEvent/:taskId", methods = {Route.HttpMethod.POST}) + public void enqueueTaskStatusUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { + + try { + TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class); + testUtilsBean.enqueueEvent(taskId, event); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/queue/enqueueTaskArtifactUpdateEvent/:taskId", methods = {Route.HttpMethod.POST}) + public void enqueueTaskArtifactUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { + + try { + TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class); + testUtilsBean.enqueueEvent(taskId, event); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/streamingSubscribedCount", methods = {Route.HttpMethod.GET}, produces = {TEXT_PLAIN}) + public void getStreamingSubscribedCount(RoutingContext rc) { + rc.response() + .setStatusCode(200) + .end(String.valueOf(streamingSubscribedCount.get())); + } + + private void errorResponse(Throwable t, RoutingContext rc) { + t.printStackTrace(); + rc.response() + .setStatusCode(500) + .putHeader(CONTENT_TYPE, TEXT_PLAIN) + .end(); + } + +} diff --git a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java index dab1954ba..f9ed48643 100644 --- a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java +++ b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java @@ -1,37 +1,12 @@ package io.a2a.server.apps.quarkus; -import jakarta.inject.Inject; - import io.a2a.server.apps.common.AbstractA2AServerTest; -import io.a2a.server.events.InMemoryQueueManager; -import io.a2a.server.tasks.TaskStore; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest public class QuarkusA2AServerTest extends AbstractA2AServerTest { - @Inject - TaskStore taskStore; - - @Inject - InMemoryQueueManager queueManager; - public QuarkusA2AServerTest() { super(8081); } - - @Override - protected TaskStore getTaskStore() { - return taskStore; - } - - @Override - protected InMemoryQueueManager getQueueManager() { - return queueManager; - } - - @Override - protected void setStreamingSubscribedRunnable(Runnable runnable) { - A2AServerRoutes.setStreamingMultiSseSupportSubscribedRunnable(runnable); - } } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 0992a157f..3a20660e8 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -9,24 +9,25 @@ import static org.wildfly.common.Assert.assertTrue; import java.io.EOFException; +import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import jakarta.ws.rs.core.MediaType; import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.server.events.InMemoryQueueManager; -import io.a2a.server.tasks.TaskStore; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskRequest; @@ -71,6 +72,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +/** + * This test requires doing some work on the server to add/get/delete tasks, and enqueue events. This is exposed via REST, + * which delegates to {@link TestUtilsBean}. + */ public abstract class AbstractA2AServerTest { private static final Task MINIMAL_TASK = new Task.Builder() @@ -102,6 +107,7 @@ public abstract class AbstractA2AServerTest { .role(Message.Role.AGENT) .parts(new TextPart("test message")) .build(); + public static final String APPLICATION_JSON = "application/json"; private final int serverPort; @@ -110,16 +116,30 @@ protected AbstractA2AServerTest(int serverPort) { } @Test - public void testGetTaskSuccess() { + public void testTaskStoreMethodsSanityTest() throws Exception { + Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); + saveTaskInTaskStore(task); + Task saved = getTaskFromTaskStore(task.getId()); + assertEquals(task.getId(), saved.getId()); + assertEquals(task.getContextId(), saved.getContextId()); + assertEquals(task.getStatus().state(), saved.getStatus().state()); + + deleteTaskInTaskStore(task.getId()); + Task saved2 = getTaskFromTaskStore(task.getId()); + assertNull(saved2); + } + + @Test + public void testGetTaskSuccess() throws Exception { testGetTask(); } - private void testGetTask() { + private void testGetTask() throws Exception { testGetTask(null); } - private void testGetTask(String mediaType) { - getTaskStore().save(MINIMAL_TASK); + private void testGetTask(String mediaType) throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); try { GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); RequestSpecification requestSpecification = RestAssured.given() @@ -140,15 +160,14 @@ private void testGetTask(String mediaType) { assertEquals("session-xyz", response.getResult().getContextId()); assertEquals(TaskState.SUBMITTED, response.getResult().getStatus().state()); assertNull(response.getError()); - } catch (Exception e) { } finally { - getTaskStore().delete(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @Test - public void testGetTaskNotFound() { - assertTrue(getTaskStore().get("non-existent-task") == null); + public void testGetTaskNotFound() throws Exception { + assertTrue(getTaskFromTaskStore("non-existent-task") == null); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams("non-existent-task")); GetTaskResponse response = given() .contentType(MediaType.APPLICATION_JSON) @@ -167,8 +186,8 @@ public void testGetTaskNotFound() { } @Test - public void testCancelTaskSuccess() { - getTaskStore().save(CANCEL_TASK); + public void testCancelTaskSuccess() throws Exception { + saveTaskInTaskStore(CANCEL_TASK); try { CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK.getId())); CancelTaskResponse response = given() @@ -188,13 +207,13 @@ public void testCancelTaskSuccess() { assertEquals(TaskState.CANCELED, task.getStatus().state()); } catch (Exception e) { } finally { - getTaskStore().delete(CANCEL_TASK.getId()); + deleteTaskInTaskStore(CANCEL_TASK.getId()); } } @Test - public void testCancelTaskNotSupported() { - getTaskStore().save(CANCEL_TASK_NOT_SUPPORTED); + public void testCancelTaskNotSupported() throws Exception { + saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); try { CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); CancelTaskResponse response = given() @@ -213,7 +232,7 @@ public void testCancelTaskNotSupported() { assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); } catch (Exception e) { } finally { - getTaskStore().delete(CANCEL_TASK_NOT_SUPPORTED.getId()); + deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); } } @@ -238,8 +257,8 @@ public void testCancelTaskNotFound() { } @Test - public void testSendMessageNewMessageSuccess() { - assertTrue(getTaskStore().get(MINIMAL_TASK.getId()) == null); + public void testSendMessageNewMessageSuccess() throws Exception { + assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) @@ -264,8 +283,8 @@ public void testSendMessageNewMessageSuccess() { } @Test - public void testSendMessageExistingTaskSuccess() { - getTaskStore().save(MINIMAL_TASK); + public void testSendMessageExistingTaskSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); try { Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) @@ -290,13 +309,13 @@ public void testSendMessageExistingTaskSuccess() { assertEquals("test message", ((TextPart) part).getText()); } catch (Exception e) { } finally { - getTaskStore().delete(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @Test - public void testSetPushNotificationSuccess() { - getTaskStore().save(MINIMAL_TASK); + public void testSetPushNotificationSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( @@ -318,13 +337,13 @@ public void testSetPushNotificationSuccess() { assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (Exception e) { } finally { - getTaskStore().delete(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @Test - public void testGetPushNotificationSuccess() { - getTaskStore().save(MINIMAL_TASK); + public void testGetPushNotificationSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( @@ -360,7 +379,7 @@ public void testGetPushNotificationSuccess() { assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (Exception e) { } finally { - getTaskStore().delete(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @@ -542,14 +561,14 @@ public void testInvalidJSONRPCRequestNonExistentMethod() { } @Test - public void testNonStreamingMethodWithAcceptHeader() { + public void testNonStreamingMethodWithAcceptHeader() throws Exception { testGetTask(MediaType.APPLICATION_JSON); } @Test - public void testSendMessageStreamExistingTaskSuccess() { - getTaskStore().save(MINIMAL_TASK); + public void testSendMessageStreamExistingTaskSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); try { Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) @@ -598,20 +617,20 @@ public void testSendMessageStreamExistingTaskSuccess() { Assertions.assertNull(errorRef.get()); } catch (Exception e) { } finally { - getTaskStore().delete(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @Test public void testResubscribeExistingTaskSuccess() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); - getTaskStore().save(MINIMAL_TASK); + saveTaskInTaskStore(MINIMAL_TASK); try { // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap // does not work because after the message is sent, the queue becomes null but task resubscription // requires the queue to still be active - getQueueManager().createOrTap(MINIMAL_TASK.getId()); + ensureQueueForTask(MINIMAL_TASK.getId()); CountDownLatch taskResubscriptionRequestSent = new CountDownLatch(1); CountDownLatch taskResubscriptionResponseReceived = new CountDownLatch(2); @@ -622,7 +641,8 @@ public void testResubscribeExistingTaskSuccess() throws Exception { TaskResubscriptionRequest taskResubscriptionRequest = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); // Count down the latch when the MultiSseSupport on the server has started subscribing - setStreamingSubscribedRunnable(taskResubscriptionRequestSent::countDown); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> taskResubscriptionRequestSent.countDown()); CompletableFuture>> responseFuture = initialiseStreamingRequest(taskResubscriptionRequest, null); @@ -631,7 +651,6 @@ public void testResubscribeExistingTaskSuccess() throws Exception { responseFuture.thenAccept(response -> { if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); throw new IllegalStateException("Status code was " + response.statusCode()); } try { @@ -682,7 +701,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { .build()); for (Event event : events) { - getQueueManager().get(MINIMAL_TASK.getId()).enqueueEvent(event); + enqueueEventOnServer(event); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -710,8 +729,8 @@ public void testResubscribeExistingTaskSuccess() throws Exception { assertEquals(TaskState.COMPLETED, taskStatusUpdateEvent.getStatus().state()); assertNotNull(taskStatusUpdateEvent.getStatus().timestamp()); } finally { - setStreamingSubscribedRunnable(null); - getTaskStore().delete(MINIMAL_TASK.getId()); + //setStreamingSubscribedRunnable(null); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); executorService.shutdown(); if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { executorService.shutdownNow(); @@ -860,10 +879,9 @@ private CompletableFuture>> initialiseStreamingReque // Create the request HttpRequest.Builder builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + - "/")) + .uri(URI.create("http://localhost:" + serverPort + "/")) .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) - .header("Content-Type", "application/json"); + .header("Content-Type", APPLICATION_JSON); if (mediaType != null) { builder.header("Accept", mediaType); } @@ -874,11 +892,136 @@ private CompletableFuture>> initialiseStreamingReque return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); } - protected abstract TaskStore getTaskStore(); + protected void saveTaskInTaskStore(Task task) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/task")) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(task))) + .header("Content-Type", APPLICATION_JSON) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Creating task failed! " + response.body()); + } + } + + protected Task getTaskFromTaskStore(String taskId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() == 404) { + return null; + } + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Creating task failed! " + response.body()); + } + return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); + } + + protected void deleteTaskInTaskStore(String taskId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(("http://localhost:" + serverPort + "/test/task/" + taskId))) + .DELETE() + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); + } + } + + protected void ensureQueueForTask(String taskId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/queue/ensure/" + taskId)) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); + } + } + + protected void enqueueEventOnServer(Event event) throws Exception { + String path; + if (event instanceof TaskArtifactUpdateEvent e) { + path = "test/queue/enqueueTaskArtifactUpdateEvent/" + e.getTaskId(); + } else if (event instanceof TaskStatusUpdateEvent e) { + path = "test/queue/enqueueTaskStatusUpdateEvent/" + e.getTaskId(); + } else { + throw new RuntimeException("Unknown event type " + event.getClass() + ". If you need the ability to" + + " handle more types, please add the REST endpoints."); + } + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/" + path)) + .header("Content-Type", APPLICATION_JSON) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(event))) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Queueing event failed!" + response.body()); + } + } - protected abstract InMemoryQueueManager getQueueManager(); + private CompletableFuture awaitStreamingSubscription() { + int cnt = getStreamingSubscribedCount(); + AtomicInteger initialCount = new AtomicInteger(cnt); - protected abstract void setStreamingSubscribedRunnable(Runnable runnable); + return CompletableFuture.runAsync(() -> { + try { + boolean done = false; + long end = System.currentTimeMillis() + 15000; + while (System.currentTimeMillis() < end) { + int count = getStreamingSubscribedCount(); + if (count > initialCount.get()) { + done = true; + break; + } + Thread.sleep(500); + } + if (!done) { + throw new RuntimeException("Timed out waiting for subscription"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted"); + } + }); + } + + private int getStreamingSubscribedCount() { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/streamingSubscribedCount")) + .GET() + .build(); + try { + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + String body = response.body().trim(); + return Integer.parseInt(body); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } private static class BreakException extends RuntimeException { diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java new file mode 100644 index 000000000..8cd333a55 --- /dev/null +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java @@ -0,0 +1,50 @@ +package io.a2a.server.apps.common; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.Response; + +import io.a2a.server.events.QueueManager; +import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.Event; +import io.a2a.spec.Task; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Contains utilities to interact with the server side for the tests. + * The intent for this bean is to be exposed via REST. + * + *

There is a Quarkus implementation in {@code A2ATestRoutes} which shows the contract for how to + * expose it via REST. For other REST frameworks, you will need to provide an implementation that works in a similar + * way to {@code A2ATestRoutes}.

+ */ +@ApplicationScoped +public class TestUtilsBean { + + @Inject + TaskStore taskStore; + + @Inject + QueueManager queueManager; + + public void saveTask(Task task) { + taskStore.save(task); + } + + public Task getTask(String taskId) { + return taskStore.get(taskId); + } + + public void deleteTask(String taskId) { + taskStore.delete(taskId); + } + + public void ensureQueue(String taskId) { + queueManager.createOrTap(taskId); + } + + public void enqueueEvent(String taskId, Event event) { + queueManager.get(taskId).enqueueEvent(event); + } +} From d726d0fe32ee4fc2f32d96eb7852c0c879b2190d Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 17 Jul 2025 15:20:44 +0100 Subject: [PATCH 038/493] chore: Clean up AbstractA2AServerTest (#187) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../io/a2a/server/apps/common/AbstractA2AServerTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 3a20660e8..8624f8e27 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -71,6 +71,7 @@ import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; /** * This test requires doing some work on the server to add/get/delete tasks, and enqueue events. This is exposed via REST, @@ -622,6 +623,7 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { } @Test + @Timeout(value = 3, unit = TimeUnit.MINUTES) public void testResubscribeExistingTaskSuccess() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); saveTaskInTaskStore(MINIMAL_TASK); @@ -904,7 +906,7 @@ protected void saveTaskInTaskStore(Task task) throws Exception { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Creating task failed! " + response.body()); +throw new RuntimeException(String.format("Saving task failed! Status: %d, Body: %s", response.statusCode(), response.body())); } } @@ -922,7 +924,7 @@ protected Task getTaskFromTaskStore(String taskId) throws Exception { return null; } if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Creating task failed! " + response.body()); +throw new RuntimeException(String.format("Getting task failed! Status: %d, Body: %s", response.statusCode(), response.body())); } return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); } @@ -951,7 +953,7 @@ protected void ensureQueueForTask(String taskId) throws Exception { .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); +throw new RuntimeException(String.format("Ensuring queue failed! Status: %d, Body: %s", response.statusCode(), response.body())); } } From 93d6bae9e7b1565c5a0083597f53e650816984cb Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 17 Jul 2025 15:15:21 -0400 Subject: [PATCH 039/493] feat: Implement the spec updates for v0.2.4 and v0.2.5 (#192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This PR brings in the updates required to align with the A2A v0.2.4 and v0.2.5 spec versions. - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes #183 🦕 --- .github/workflows/run-tck.yml | 2 +- README.md | 32 ++- client/pom.xml | 2 +- .../main/java/io/a2a/client/A2AClient.java | 189 +++++++++++-- .../java/io/a2a/client/A2AClientTest.java | 5 +- .../test/java/io/a2a/client/JsonMessages.java | 6 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- .../helloworld/AgentCardProducer.java | 1 + pom.xml | 2 +- reference-impl/pom.xml | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 10 +- .../server/apps/quarkus/A2ATestRoutes.java | 39 +++ sdk-server-common/pom.xml | 2 +- .../DefaultRequestHandler.java | 95 +++++-- .../requesthandlers/JSONRPCHandler.java | 51 +++- .../requesthandlers/RequestHandler.java | 10 +- .../tasks/BasePushNotificationSender.java | 96 +++++++ .../InMemoryPushNotificationConfigStore.java | 77 ++++++ .../server/tasks/InMemoryPushNotifier.java | 78 ------ .../tasks/PushNotificationConfigStore.java | 33 +++ .../server/tasks/PushNotificationSender.java | 15 + .../io/a2a/server/tasks/PushNotifier.java | 14 - .../requesthandlers/JSONRPCHandlerTest.java | 233 ++++++++++++++-- spec/pom.xml | 2 +- spec/src/main/java/io/a2a/spec/AgentCard.java | 25 +- .../main/java/io/a2a/spec/AgentInterface.java | 18 ++ .../java/io/a2a/spec/CancelTaskResponse.java | 2 +- ...eleteTaskPushNotificationConfigParams.java | 50 ++++ ...leteTaskPushNotificationConfigRequest.java | 77 ++++++ ...eteTaskPushNotificationConfigResponse.java | 32 +++ .../GetTaskPushNotificationConfigParams.java | 53 ++++ .../GetTaskPushNotificationConfigRequest.java | 10 +- ...GetTaskPushNotificationConfigResponse.java | 2 +- .../java/io/a2a/spec/GetTaskResponse.java | 2 +- .../io/a2a/spec/JSONRPCErrorResponse.java | 2 +- .../spec/JSONRPCRequestDeserializerBase.java | 36 +-- .../java/io/a2a/spec/JSONRPCResponse.java | 7 +- .../spec/JSONRPCVoidResponseSerializer.java | 34 +++ .../ListTaskPushNotificationConfigParams.java | 24 ++ ...ListTaskPushNotificationConfigRequest.java | 77 ++++++ ...istTaskPushNotificationConfigResponse.java | 32 +++ .../a2a/spec/NonStreamingJSONRPCRequest.java | 2 +- ...onStreamingJSONRPCRequestDeserializer.java | 12 +- .../io/a2a/spec/PushNotificationConfig.java | 10 + .../PushNotificationNotSupportedError.java | 4 + .../java/io/a2a/spec/SendMessageResponse.java | 6 +- .../spec/SendStreamingMessageResponse.java | 6 +- ...SetTaskPushNotificationConfigResponse.java | 4 +- tck/pom.xml | 2 +- .../io/a2a/tck/server/AgentCardProducer.java | 1 + tests/server-common/pom.xml | 2 +- .../apps/common/AbstractA2AServerTest.java | 258 +++++++++++++++++- .../server/apps/common/AgentCardProducer.java | 1 + .../a2a/server/apps/common/TestUtilsBean.java | 16 +- 58 files changed, 1560 insertions(+), 253 deletions(-) create mode 100644 sdk-server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java create mode 100644 sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java delete mode 100644 sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java create mode 100644 sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java create mode 100644 sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java delete mode 100644 sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotifier.java create mode 100644 spec/src/main/java/io/a2a/spec/AgentInterface.java create mode 100644 spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java create mode 100644 spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java create mode 100644 spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java create mode 100644 spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java create mode 100644 spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java create mode 100644 spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java create mode 100644 spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java create mode 100644 spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index d2cba0890..291802da4 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -11,7 +11,7 @@ on: env: # Tag of the TCK - TCK_VERSION: v0.2.3 + TCK_VERSION: v0.2.5 # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 diff --git a/README.md b/README.md index fe7d3bbc0..2186dde3b 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ public class WeatherAgentCardProducer { .tags(Collections.singletonList("weather")) .examples(List.of("weather in LA, CA")) .build())) + .protocolVersion("0.2.5") .build(); } } @@ -255,18 +256,21 @@ Map metadata = ... CancelTaskResponse response = client.cancelTask(new TaskIdParams("task-1234", metadata)); ``` -#### Get the push notification configuration for a task +#### Get a push notification configuration for a task ```java // Get task push notification configuration GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("task-1234"); -// You can also specify additional properties using a map +// The push notification configuration ID can also be optionally specified +GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("task-1234", "config-4567"); + +// Additional properties can be specified using a map Map metadata = ... -GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig(new TaskIdParams("task-1234", metadata)); +GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig(new GetTaskPushNotificationConfigParams("task-1234", "config-1234", metadata)); ``` -#### Set the push notification configuration for a task +#### Set a push notification configuration for a task ```java // Set task push notification configuration @@ -277,6 +281,26 @@ PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Build SetTaskPushNotificationResponse response = client.setTaskPushNotificationConfig("task-1234", pushNotificationConfig); ``` +#### List the push notification configurations for a task + +```java +ListTaskPushNotificationConfigResponse response = client.listTaskPushNotificationConfig("task-1234"); + +// Additional properties can be specified using a map +Map metadata = ... +ListTaskPushNotificationConfigResponse response = client.listTaskPushNotificationConfig(new ListTaskPushNotificationConfigParams("task-123", metadata)); +``` + +#### Delete a push notification configuration for a task + +```java +DeleteTaskPushNotificationConfigResponse response = client.deleteTaskPushNotificationConfig("task-1234", "config-4567"); + +// Additional properties can be specified using a map +Map metadata = ... +DeleteTaskPushNotificationConfigResponse response = client.deleteTaskPushNotificationConfig(new DeleteTaskPushNotificationConfigParams("task-1234", "config-4567", metadata)); +``` + #### Send a streaming message ```java diff --git a/client/pom.xml b/client/pom.xml index 64f983e42..9ec3b9f5b 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-sdk-client diff --git a/client/src/main/java/io/a2a/client/A2AClient.java b/client/src/main/java/io/a2a/client/A2AClient.java index ea08baea4..80f8c401c 100644 --- a/client/src/main/java/io/a2a/client/A2AClient.java +++ b/client/src/main/java/io/a2a/client/A2AClient.java @@ -21,6 +21,10 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; +import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; @@ -28,6 +32,9 @@ import io.a2a.spec.JSONRPCError; import io.a2a.spec.JSONRPCMessage; import io.a2a.spec.JSONRPCResponse; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SendMessageRequest; @@ -52,6 +59,8 @@ public class A2AClient { private static final TypeReference CANCEL_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private final A2AHttpClient httpClient; private final String agentUrl; private AgentCard agentCard; @@ -164,7 +173,7 @@ public SendMessageResponse sendMessage(String requestId, MessageSendParams messa String httpResponseBody = sendPostRequest(sendMessageRequest); return unmarshalResponse(httpResponseBody, SEND_MESSAGE_RESPONSE_REFERENCE); } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to send message: " + e); + throw new A2AServerException("Failed to send message: " + e, e.getCause()); } } @@ -216,7 +225,7 @@ public GetTaskResponse getTask(String requestId, TaskQueryParams taskQueryParams String httpResponseBody = sendPostRequest(getTaskRequest); return unmarshalResponse(httpResponseBody, GET_TASK_RESPONSE_REFERENCE); } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get task: " + e); + throw new A2AServerException("Failed to get task: " + e, e.getCause()); } } @@ -266,45 +275,57 @@ public CancelTaskResponse cancelTask(String requestId, TaskIdParams taskIdParams String httpResponseBody = sendPostRequest(cancelTaskRequest); return unmarshalResponse(httpResponseBody, CANCEL_TASK_RESPONSE_REFERENCE); } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to cancel task: " + e); + throw new A2AServerException("Failed to cancel task: " + e, e.getCause()); } } /** * Get the push notification configuration for a task. * - * @param id the task ID + * @param taskId the task ID + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String taskId) throws A2AServerException { + return getTaskPushNotificationConfig(null, new GetTaskPushNotificationConfigParams(taskId)); + } + + /** + * Get the push notification configuration for a task. + * + * @param taskId the task ID + * @param pushNotificationConfigId the push notification configuration ID * @return the response containing the push notification configuration * @throws A2AServerException if getting the push notification configuration fails for any reason */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String id) throws A2AServerException { - return getTaskPushNotificationConfig(null, new TaskIdParams(id)); + public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String taskId, String pushNotificationConfigId) throws A2AServerException { + return getTaskPushNotificationConfig(null, new GetTaskPushNotificationConfigParams(taskId, pushNotificationConfigId)); } /** * Get the push notification configuration for a task. * - * @param taskIdParams the params for the task + * @param getTaskPushNotificationConfigParams the params for the task * @return the response containing the push notification configuration * @throws A2AServerException if getting the push notification configuration fails for any reason */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(TaskIdParams taskIdParams) throws A2AServerException { - return getTaskPushNotificationConfig(null, taskIdParams); + public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { + return getTaskPushNotificationConfig(null, getTaskPushNotificationConfigParams); } /** * Get the push notification configuration for a task. * * @param requestId the request ID to use - * @param taskIdParams the params for the task + * @param getTaskPushNotificationConfigParams the params for the task * @return the response containing the push notification configuration * @throws A2AServerException if getting the push notification configuration fails for any reason */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String requestId, TaskIdParams taskIdParams) throws A2AServerException { + public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { GetTaskPushNotificationConfigRequest.Builder getTaskPushNotificationRequestBuilder = new GetTaskPushNotificationConfigRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .method(GetTaskPushNotificationConfigRequest.METHOD) - .params(taskIdParams); + .params(getTaskPushNotificationConfigParams); if (requestId != null) { getTaskPushNotificationRequestBuilder.id(requestId); @@ -316,7 +337,7 @@ public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(Strin String httpResponseBody = sendPostRequest(getTaskPushNotificationRequest); return unmarshalResponse(httpResponseBody, GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get task push notification config: " + e); + throw new A2AServerException("Failed to get task push notification config: " + e, e.getCause()); } } @@ -359,7 +380,137 @@ public SetTaskPushNotificationConfigResponse setTaskPushNotificationConfig(Strin String httpResponseBody = sendPostRequest(setTaskPushNotificationRequest); return unmarshalResponse(httpResponseBody, SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to set task push notification config: " + e); + throw new A2AServerException("Failed to set task push notification config: " + e, e.getCause()); + } + } + + /** + * Retrieves the push notification configurations for a specified task. + * + * @param requestId the request ID to use + * @param taskId the task ID to use + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(String requestId, String taskId) throws A2AServerException { + return listTaskPushNotificationConfig(requestId, new ListTaskPushNotificationConfigParams(taskId)); + } + + /** + * Retrieves the push notification configurations for a specified task. + * + * @param taskId the task ID to use + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(String taskId) throws A2AServerException { + return listTaskPushNotificationConfig(null, new ListTaskPushNotificationConfigParams(taskId)); + } + + /** + * Retrieves the push notification configurations for a specified task. + * + * @param listTaskPushNotificationConfigParams the params for retrieving the push notification configuration + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { + return listTaskPushNotificationConfig(null, listTaskPushNotificationConfigParams); + } + + /** + * Retrieves the push notification configurations for a specified task. + * + * @param requestId the request ID to use + * @param listTaskPushNotificationConfigParams the params for retrieving the push notification configuration + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(String requestId, + ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { + ListTaskPushNotificationConfigRequest.Builder listTaskPushNotificationRequestBuilder = new ListTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(ListTaskPushNotificationConfigRequest.METHOD) + .params(listTaskPushNotificationConfigParams); + + if (requestId != null) { + listTaskPushNotificationRequestBuilder.id(requestId); + } + + ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = listTaskPushNotificationRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(listTaskPushNotificationRequest); + return unmarshalResponse(httpResponseBody, LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to list task push notification config: " + e, e.getCause()); + } + } + + /** + * Delete the push notification configuration for a specified task. + * + * @param requestId the request ID to use + * @param taskId the task ID + * @param pushNotificationConfigId the push notification config ID + * @return the response + * @throws A2AServerException if deleting the push notification configuration fails for any reason + */ + public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(String requestId, String taskId, + String pushNotificationConfigId) throws A2AServerException { + return deleteTaskPushNotificationConfig(requestId, new DeleteTaskPushNotificationConfigParams(taskId, pushNotificationConfigId)); + } + + /** + * Delete the push notification configuration for a specified task. + * + * @param taskId the task ID + * @param pushNotificationConfigId the push notification config ID + * @return the response + * @throws A2AServerException if deleting the push notification configuration fails for any reason + */ + public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(String taskId, + String pushNotificationConfigId) throws A2AServerException { + return deleteTaskPushNotificationConfig(null, new DeleteTaskPushNotificationConfigParams(taskId, pushNotificationConfigId)); + } + + /** + * Delete the push notification configuration for a specified task. + * + * @param deleteTaskPushNotificationConfigParams the params for deleting the push notification configuration + * @return the response + * @throws A2AServerException if deleting the push notification configuration fails for any reason + */ + public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { + return deleteTaskPushNotificationConfig(null, deleteTaskPushNotificationConfigParams); + } + + /** + * Delete the push notification configuration for a specified task. + * + * @param requestId the request ID to use + * @param deleteTaskPushNotificationConfigParams the params for deleting the push notification configuration + * @return the response + * @throws A2AServerException if deleting the push notification configuration fails for any reason + */ + public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(String requestId, + DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { + DeleteTaskPushNotificationConfigRequest.Builder deleteTaskPushNotificationRequestBuilder = new DeleteTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(DeleteTaskPushNotificationConfigRequest.METHOD) + .params(deleteTaskPushNotificationConfigParams); + + if (requestId != null) { + deleteTaskPushNotificationRequestBuilder.id(requestId); + } + + DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = deleteTaskPushNotificationRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(deleteTaskPushNotificationRequest); + return unmarshalResponse(httpResponseBody, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to delete task push notification config: " + e, e.getCause()); } } @@ -416,9 +567,9 @@ public void sendStreamingMessage(String requestId, MessageSendParams messageSend })); } catch (IOException e) { - throw new A2AServerException("Failed to send streaming message request: " + e); + throw new A2AServerException("Failed to send streaming message request: " + e, e.getCause()); } catch (InterruptedException e) { - throw new A2AServerException("Send streaming message request timed out: " + e); + throw new A2AServerException("Send streaming message request timed out: " + e, e.getCause()); } } @@ -475,9 +626,9 @@ public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consu })); } catch (IOException e) { - throw new A2AServerException("Failed to send task resubscription request: " + e); + throw new A2AServerException("Failed to send task resubscription request: " + e, e.getCause()); } catch (InterruptedException e) { - throw new A2AServerException("Task resubscription request timed out: " + e); + throw new A2AServerException("Task resubscription request timed out: " + e, e.getCause()); } } @@ -503,7 +654,7 @@ private T unmarshalResponse(String response, TypeRef T value = Utils.unmarshalFrom(response, typeReference); JSONRPCError error = value.getError(); if (error != null) { - throw new A2AServerException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : "")); + throw new A2AServerException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); } return value; } diff --git a/client/src/test/java/io/a2a/client/A2AClientTest.java b/client/src/test/java/io/a2a/client/A2AClientTest.java index e99734ff1..eda03e02a 100644 --- a/client/src/test/java/io/a2a/client/A2AClientTest.java +++ b/client/src/test/java/io/a2a/client/A2AClientTest.java @@ -46,6 +46,7 @@ import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskResponse; import io.a2a.spec.Message; @@ -328,7 +329,7 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception { A2AClient client = new A2AClient("http://localhost:4001"); GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("1", - new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64", new HashMap<>())); + new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", null, new HashMap<>())); assertEquals("2.0", response.getJsonrpc()); assertEquals(1, response.getId()); assertInstanceOf(TaskPushNotificationConfig.class, response.getResult()); @@ -442,6 +443,7 @@ public void testA2AClientGetAgentCard() throws Exception { assertEquals(outputModes, skills.get(1).outputModes()); assertTrue(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); + assertEquals("0.2.5", agentCard.protocolVersion()); } @Test @@ -514,6 +516,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertEquals(List.of("extended"), skills.get(2).tags()); assertTrue(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); + assertEquals("0.2.5", agentCard.protocolVersion()); } @Test diff --git a/client/src/test/java/io/a2a/client/JsonMessages.java b/client/src/test/java/io/a2a/client/JsonMessages.java index c7ebd7780..fecf216d0 100644 --- a/client/src/test/java/io/a2a/client/JsonMessages.java +++ b/client/src/test/java/io/a2a/client/JsonMessages.java @@ -67,7 +67,8 @@ public class JsonMessages { ] } ], - "supportsAuthenticatedExtendedCard": true + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" }"""; static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """ @@ -137,7 +138,8 @@ public class JsonMessages { "tags": ["extended"] } ], - "supportsAuthenticatedExtendedCard": true + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" }"""; diff --git a/common/pom.xml b/common/pom.xml index 9b3d81137..254a6da14 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 26f701934..3584a4202 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 8583ef27a..597113380 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.3.Beta2-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5.Beta2-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 3b7bd849d..8253fcc83 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 3f5f74a1e..979b4bf99 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index f07a6e527..06450c935 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -37,6 +37,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) + .protocolVersion("0.2.5") .build(); } } diff --git a/pom.xml b/pom.xml index 9fd7cfeaf..c7e409f2a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index 15a62e8b0..c2397b698 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-reference-server diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 1500dfe4c..81a0df3b3 100644 --- a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -22,6 +22,7 @@ import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskRequest; import io.a2a.spec.IdJsonMappingException; @@ -35,6 +36,7 @@ import io.a2a.spec.JSONRPCErrorResponse; import io.a2a.spec.JSONRPCRequest; import io.a2a.spec.JSONRPCResponse; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.MethodNotFoundJsonMappingException; import io.a2a.spec.NonStreamingJSONRPCRequest; @@ -187,11 +189,15 @@ private JSONRPCResponse processNonStreamingRequest(NonStreamingJSONRPCRequest } else if (request instanceof CancelTaskRequest) { return jsonRpcHandler.onCancelTask((CancelTaskRequest) request); } else if (request instanceof SetTaskPushNotificationConfigRequest) { - return jsonRpcHandler.setPushNotification((SetTaskPushNotificationConfigRequest) request); + return jsonRpcHandler.setPushNotificationConfig((SetTaskPushNotificationConfigRequest) request); } else if (request instanceof GetTaskPushNotificationConfigRequest) { - return jsonRpcHandler.getPushNotification((GetTaskPushNotificationConfigRequest) request); + return jsonRpcHandler.getPushNotificationConfig((GetTaskPushNotificationConfigRequest) request); } else if (request instanceof SendMessageRequest) { return jsonRpcHandler.onMessageSend((SendMessageRequest) request); + } else if (request instanceof ListTaskPushNotificationConfigRequest) { + return jsonRpcHandler.listPushNotificationConfig((ListTaskPushNotificationConfigRequest) request); + } else if (request instanceof DeleteTaskPushNotificationConfigRequest) { + return jsonRpcHandler.deletePushNotificationConfig((DeleteTaskPushNotificationConfigRequest) request); } else { return generateErrorResponse(request, new UnsupportedOperationError()); } diff --git a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java index bc6a342a6..5af126bf5 100644 --- a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java +++ b/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java @@ -11,6 +11,7 @@ import jakarta.inject.Singleton; import io.a2a.server.apps.common.TestUtilsBean; +import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; @@ -138,6 +139,44 @@ public void getStreamingSubscribedCount(RoutingContext rc) { .end(String.valueOf(streamingSubscribedCount.get())); } + @Route(path = "/test/task/:taskId/config/:configId", methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) + public void deleteTaskPushNotificationConfig(@Param String taskId, @Param String configId, RoutingContext rc) { + try { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + testUtilsBean.deleteTaskPushNotificationConfig(taskId, configId); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) + public void saveTaskPushNotificationConfig(@Param String taskId, @Body String body, RoutingContext rc) { + try { + PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class); + if (notificationConfig == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + testUtilsBean.saveTaskPushNotificationConfig(taskId, notificationConfig); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + private void errorResponse(Throwable t, RoutingContext rc) { t.printStackTrace(); rc.response() diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index f35b91280..27b38fe99 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-sdk-server-common diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 5d6cbec84..b4dbb2feb 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -16,20 +16,27 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.agentexecution.SimpleRequestContextBuilder; import io.a2a.server.events.EnhancedRunnable; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; import io.a2a.server.events.QueueManager; import io.a2a.server.events.TaskQueueExistsException; -import io.a2a.server.tasks.PushNotifier; +import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.server.tasks.PushNotificationSender; import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; import io.a2a.server.util.async.Internal; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.Event; import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; @@ -43,9 +50,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import io.a2a.server.agentexecution.AgentExecutor; -import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.agentexecution.SimpleRequestContextBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +61,8 @@ public class DefaultRequestHandler implements RequestHandler { private final AgentExecutor agentExecutor; private final TaskStore taskStore; private final QueueManager queueManager; - private final PushNotifier pushNotifier; + private final PushNotificationConfigStore pushConfigStore; + private final PushNotificationSender pushSender; private final Supplier requestContextBuilder; private final ConcurrentMap> runningAgents = new ConcurrentHashMap<>(); @@ -66,11 +71,13 @@ public class DefaultRequestHandler implements RequestHandler { @Inject public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, - QueueManager queueManager, PushNotifier pushNotifier, @Internal Executor executor) { + QueueManager queueManager, PushNotificationConfigStore pushConfigStore, + PushNotificationSender pushSender, @Internal Executor executor) { this.agentExecutor = agentExecutor; this.taskStore = taskStore; this.queueManager = queueManager; - this.pushNotifier = pushNotifier; + this.pushConfigStore = pushConfigStore; + this.pushSender = pushSender; this.executor = executor; // TODO In Python this is also a constructor parameter defaulting to this SimpleRequestContextBuilder // implementation if the parameter is null. Skip that for now, since otherwise I get CDI errors, and @@ -226,20 +233,20 @@ public Flow.Publisher onMessageSendStream(MessageSendParams } catch (TaskQueueExistsException e) { // TODO Log } - if (pushNotifier != null && + if (pushConfigStore != null && params.configuration() != null && params.configuration().pushNotification() != null) { - pushNotifier.setInfo( + pushConfigStore.setInfo( createdTask.getId(), params.configuration().pushNotification()); } } - if (pushNotifier != null && taskId.get() != null) { + if (pushSender != null && taskId.get() != null) { EventKind latest = resultAggregator.getCurrentResult(); if (latest instanceof Task latestTask) { - pushNotifier.sendNotification(latestTask); + pushSender.sendNotification(latestTask); } } @@ -254,7 +261,7 @@ public Flow.Publisher onMessageSendStream(MessageSendParams @Override public TaskPushNotificationConfig onSetTaskPushNotificationConfig(TaskPushNotificationConfig params) throws JSONRPCError { - if (pushNotifier == null) { + if (pushConfigStore == null) { throw new UnsupportedOperationError(); } Task task = taskStore.get(params.taskId()); @@ -262,14 +269,14 @@ public TaskPushNotificationConfig onSetTaskPushNotificationConfig(TaskPushNotifi throw new TaskNotFoundError(); } - pushNotifier.setInfo(params.taskId(), params.pushNotificationConfig()); + pushConfigStore.setInfo(params.taskId(), params.pushNotificationConfig()); return params; } @Override - public TaskPushNotificationConfig onGetTaskPushNotificationConfig(TaskIdParams params) throws JSONRPCError { - if (pushNotifier == null) { + public TaskPushNotificationConfig onGetTaskPushNotificationConfig(GetTaskPushNotificationConfigParams params) throws JSONRPCError { + if (pushConfigStore == null) { throw new UnsupportedOperationError(); } Task task = taskStore.get(params.id()); @@ -277,12 +284,24 @@ public TaskPushNotificationConfig onGetTaskPushNotificationConfig(TaskIdParams p throw new TaskNotFoundError(); } - PushNotificationConfig pushNotificationConfig = pushNotifier.getInfo(params.id()); - if (pushNotificationConfig == null) { + List pushNotificationConfigList = pushConfigStore.getInfo(params.id()); + if (pushNotificationConfigList == null || pushNotificationConfigList.isEmpty()) { throw new InternalError("No push notification config found"); } - return new TaskPushNotificationConfig(params.id(), pushNotificationConfig); + return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, params.pushNotificationConfigId())); + } + + private PushNotificationConfig getPushNotificationConfig(List notificationConfigList, + String configId) { + if (configId != null) { + for (PushNotificationConfig notificationConfig : notificationConfigList) { + if (configId.equals(notificationConfig.id())) { + return notificationConfig; + } + } + } + return notificationConfigList.get(0); } @Override @@ -305,8 +324,44 @@ public Flow.Publisher onResubscribeToTask(TaskIdParams param return convertingProcessor(results, e -> (StreamingEventKind) e); } + @Override + public List onListTaskPushNotificationConfig(ListTaskPushNotificationConfigParams params) throws JSONRPCError { + if (pushConfigStore == null) { + throw new UnsupportedOperationError(); + } + + Task task = taskStore.get(params.id()); + if (task == null) { + throw new TaskNotFoundError(); + } + + List pushNotificationConfigList = pushConfigStore.getInfo(params.id()); + List taskPushNotificationConfigList = new ArrayList<>(); + if (pushNotificationConfigList != null) { + for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) { + TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig); + taskPushNotificationConfigList.add(taskPushNotificationConfig); + } + } + return taskPushNotificationConfigList; + } + + @Override + public void onDeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigParams params) { + if (pushConfigStore == null) { + throw new UnsupportedOperationError(); + } + + Task task = taskStore.get(params.id()); + if (task == null) { + throw new TaskNotFoundError(); + } + + pushConfigStore.deleteInfo(params.id(), params.pushNotificationConfigId()); + } + private boolean shouldAddPushInfo(MessageSendParams params) { - return pushNotifier != null && params.configuration() != null && params.configuration().pushNotification() != null; + return pushConfigStore != null && params.configuration() != null && params.configuration().pushNotification() != null; } private EnhancedRunnable registerAndExecuteAgentAsync(String taskId, RequestContext requestContext, EventQueue queue) { @@ -357,7 +412,7 @@ private MessageSendSetup initMessageSend(MessageSendParams params) { if (shouldAddPushInfo(params)) { LOGGER.debug("Adding push info"); - pushNotifier.setInfo(task.getId(), params.configuration().pushNotification()); + pushConfigStore.setInfo(task.getId(), params.configuration().pushNotification()); } } diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java index fac014b0e..6a394e023 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java @@ -1,16 +1,18 @@ package io.a2a.server.requesthandlers; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; - -import java.util.concurrent.Flow; - import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import java.util.List; +import java.util.concurrent.Flow; + import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; @@ -19,6 +21,9 @@ import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.PushNotificationNotSupportedError; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; import io.a2a.spec.SendStreamingMessageRequest; @@ -113,7 +118,11 @@ public Flow.Publisher onResubscribeToTask(TaskResu } } - public GetTaskPushNotificationConfigResponse getPushNotification(GetTaskPushNotificationConfigRequest request) { + public GetTaskPushNotificationConfigResponse getPushNotificationConfig(GetTaskPushNotificationConfigRequest request) { + if (!agentCard.capabilities().pushNotifications()) { + return new GetTaskPushNotificationConfigResponse(request.getId(), + new PushNotificationNotSupportedError()); + } try { TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(request.getParams()); return new GetTaskPushNotificationConfigResponse(request.getId(), config); @@ -124,10 +133,10 @@ public GetTaskPushNotificationConfigResponse getPushNotification(GetTaskPushNoti } } - public SetTaskPushNotificationConfigResponse setPushNotification(SetTaskPushNotificationConfigRequest request) { + public SetTaskPushNotificationConfigResponse setPushNotificationConfig(SetTaskPushNotificationConfigRequest request) { if (!agentCard.capabilities().pushNotifications()) { return new SetTaskPushNotificationConfigResponse(request.getId(), - new InvalidRequestError("Push notifications are not supported by the agent")); + new PushNotificationNotSupportedError()); } try { TaskPushNotificationConfig config = requestHandler.onSetTaskPushNotificationConfig(request.getParams()); @@ -150,6 +159,36 @@ public GetTaskResponse onGetTask(GetTaskRequest request) { } } + public ListTaskPushNotificationConfigResponse listPushNotificationConfig(ListTaskPushNotificationConfigRequest request) { + if ( !agentCard.capabilities().pushNotifications()) { + return new ListTaskPushNotificationConfigResponse(request.getId(), + new PushNotificationNotSupportedError()); + } + try { + List pushNotificationConfigList = requestHandler.onListTaskPushNotificationConfig(request.getParams()); + return new ListTaskPushNotificationConfigResponse(request.getId(), pushNotificationConfigList); + } catch (JSONRPCError e) { + return new ListTaskPushNotificationConfigResponse(request.getId(), e); + } catch (Throwable t) { + return new ListTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); + } + } + + public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig(DeleteTaskPushNotificationConfigRequest request) { + if ( !agentCard.capabilities().pushNotifications()) { + return new DeleteTaskPushNotificationConfigResponse(request.getId(), + new PushNotificationNotSupportedError()); + } + try { + requestHandler.onDeleteTaskPushNotificationConfig(request.getParams()); + return new DeleteTaskPushNotificationConfigResponse(request.getId()); + } catch (JSONRPCError e) { + return new DeleteTaskPushNotificationConfigResponse(request.getId(), e); + } catch (Throwable t) { + return new DeleteTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); + } + } + public AgentCard getAgentCard() { return agentCard; } diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index e2902bca0..66d07b55b 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -1,9 +1,13 @@ package io.a2a.server.requesthandlers; +import java.util.List; import java.util.concurrent.Flow; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.MessageSendParams; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -22,7 +26,11 @@ public interface RequestHandler { TaskPushNotificationConfig onSetTaskPushNotificationConfig(TaskPushNotificationConfig params) throws JSONRPCError; - TaskPushNotificationConfig onGetTaskPushNotificationConfig(TaskIdParams params) throws JSONRPCError; + TaskPushNotificationConfig onGetTaskPushNotificationConfig(GetTaskPushNotificationConfigParams params) throws JSONRPCError; Flow.Publisher onResubscribeToTask(TaskIdParams params) throws JSONRPCError; + + List onListTaskPushNotificationConfig(ListTaskPushNotificationConfigParams params) throws JSONRPCError; + + void onDeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigParams params) throws JSONRPCError; } diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java new file mode 100644 index 000000000..33ac4445c --- /dev/null +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -0,0 +1,96 @@ +package io.a2a.server.tasks; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.a2a.http.A2AHttpClient; +import io.a2a.http.JdkA2AHttpClient; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.util.Utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ApplicationScoped +public class BasePushNotificationSender implements PushNotificationSender { + + private static final Logger LOGGER = LoggerFactory.getLogger(BasePushNotificationSender.class); + + private final A2AHttpClient httpClient; + private final PushNotificationConfigStore configStore; + + @Inject + public BasePushNotificationSender(PushNotificationConfigStore configStore) { + this.httpClient = new JdkA2AHttpClient(); + this.configStore = configStore; + } + + public BasePushNotificationSender(PushNotificationConfigStore configStore, A2AHttpClient httpClient) { + this.configStore = configStore; + this.httpClient = httpClient; + } + + @Override + public void sendNotification(Task task) { + List pushConfigs = configStore.getInfo(task.getId()); + if (pushConfigs == null || pushConfigs.isEmpty()) { + return; + } + + List> dispatchResults = pushConfigs + .stream() + .map(pushConfig -> dispatch(task, pushConfig)) + .toList(); + CompletableFuture allFutures = CompletableFuture.allOf(dispatchResults.toArray(new CompletableFuture[0])); + CompletableFuture dispatchResult = allFutures.thenApply(v -> dispatchResults.stream() + .allMatch(CompletableFuture::join)); + try { + boolean allSent = dispatchResult.get(); + if (! allSent) { + LOGGER.warn("Some push notifications failed to send for taskId: " + task.getId()); + } + } catch (InterruptedException | ExecutionException e) { + LOGGER.warn("Some push notifications failed to send for taskId " + task.getId() + ": {}", e.getMessage(), e); + } + } + + private CompletableFuture dispatch(Task task, PushNotificationConfig pushInfo) { + return CompletableFuture.supplyAsync(() -> dispatchNotification(task, pushInfo)); + } + + private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo) { + String url = pushInfo.url(); + + // TODO auth + + String body; + try { + body = Utils.OBJECT_MAPPER.writeValueAsString(task); + } catch (JsonProcessingException e) { + LOGGER.debug("Error writing value as string: {}", e.getMessage(), e); + return false; + } catch (Throwable throwable) { + LOGGER.debug("Error writing value as string: {}", throwable.getMessage(), throwable); + return false; + } + + try { + httpClient.createPost() + .url(url) + .body(body) + .post(); + } catch (IOException | InterruptedException e) { + LOGGER.debug("Error pushing data to " + url + ": {}", e.getMessage(), e); + return false; + } + return true; + } +} diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java new file mode 100644 index 000000000..e66fc1669 --- /dev/null +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -0,0 +1,77 @@ +package io.a2a.server.tasks; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import io.a2a.spec.PushNotificationConfig; + +/** + * In-memory implementation of the PushNotificationConfigStore interface. + * + * Stores push notification configurations in memory + */ +@ApplicationScoped +public class InMemoryPushNotificationConfigStore implements PushNotificationConfigStore { + + private final Map> pushNotificationInfos = Collections.synchronizedMap(new HashMap<>()); + + @Inject + public InMemoryPushNotificationConfigStore() { + } + + @Override + public void setInfo(String taskId, PushNotificationConfig notificationConfig) { + List notificationConfigList = pushNotificationInfos.getOrDefault(taskId, new ArrayList<>()); + PushNotificationConfig.Builder builder = new PushNotificationConfig.Builder(notificationConfig); + if (notificationConfig.id() == null) { + builder.id(taskId); + } + notificationConfig = builder.build(); + + Iterator notificationConfigIterator = notificationConfigList.iterator(); + while (notificationConfigIterator.hasNext()) { + PushNotificationConfig config = notificationConfigIterator.next(); + if (config.id().equals(notificationConfig.id())) { + notificationConfigIterator.remove(); + break; + } + } + notificationConfigList.add(notificationConfig); + pushNotificationInfos.put(taskId, notificationConfigList); + } + + @Override + public List getInfo(String taskId) { + return pushNotificationInfos.get(taskId); + } + + @Override + public void deleteInfo(String taskId, String configId) { + if (configId == null) { + configId = taskId; + } + List notificationConfigList = pushNotificationInfos.get(taskId); + if (notificationConfigList == null || notificationConfigList.isEmpty()) { + return; + } + + Iterator notificationConfigIterator = notificationConfigList.iterator(); + while (notificationConfigIterator.hasNext()) { + PushNotificationConfig config = notificationConfigIterator.next(); + if (configId.equals(config.id())) { + notificationConfigIterator.remove(); + break; + } + } + if (notificationConfigList.isEmpty()) { + pushNotificationInfos.remove(taskId); + } + } +} diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java deleted file mode 100644 index 6fb1fb39a..000000000 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotifier.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.a2a.server.tasks; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.JdkA2AHttpClient; -import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.Task; -import io.a2a.util.Utils; - -@ApplicationScoped -public class InMemoryPushNotifier implements PushNotifier { - private final A2AHttpClient httpClient; - private final Map pushNotificationInfos = Collections.synchronizedMap(new HashMap<>()); - - @Inject - public InMemoryPushNotifier() { - this.httpClient = new JdkA2AHttpClient(); - } - - public InMemoryPushNotifier(A2AHttpClient httpClient) { - this.httpClient = httpClient; - } - - @Override - public void setInfo(String taskId, PushNotificationConfig notificationConfig) { - pushNotificationInfos.put(taskId, notificationConfig); - } - - @Override - public PushNotificationConfig getInfo(String taskId) { - return pushNotificationInfos.get(taskId); - } - - @Override - public void deleteInfo(String taskId) { - pushNotificationInfos.remove(taskId); - } - - @Override - public void sendNotification(Task task) { - PushNotificationConfig pushInfo = pushNotificationInfos.get(task.getId()); - if (pushInfo == null) { - return; - } - String url = pushInfo.url(); - - // TODO auth - - String body; - try { - body = Utils.OBJECT_MAPPER.writeValueAsString(task); - } catch (JsonProcessingException e) { - e.printStackTrace(); - throw new RuntimeException("Error writing value as string: " + e.getMessage(), e); - } catch (Throwable throwable) { - throwable.printStackTrace(); - throw new RuntimeException("Error writing value as string: " + throwable.getMessage(), throwable); - } - - try { - httpClient.createPost() - .url(url) - .body(body) - .post(); - } catch (IOException | InterruptedException e) { - throw new RuntimeException("Error pushing data to " + url + ": " + e.getMessage(), e); - } - - } -} diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java new file mode 100644 index 000000000..68f132620 --- /dev/null +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java @@ -0,0 +1,33 @@ +package io.a2a.server.tasks; + +import java.util.List; + +import io.a2a.spec.PushNotificationConfig; + +/** + * Interface for storing and retrieving push notification configurations for tasks. + */ +public interface PushNotificationConfigStore { + + /** + * Sets or updates the push notification configuration for a task. + * @param taskId the task ID + * @param notificationConfig the push notification configuration + */ + void setInfo(String taskId, PushNotificationConfig notificationConfig); + + /** + * Retrieves the push notification configuration for a task. + * @param taskId the task ID + * @return the push notification configurations for a task + */ + List getInfo(String taskId); + + /** + * Deletes the push notification configuration for a task. + * @param taskId the task ID + * @param configId the push notification configuration + */ + void deleteInfo(String taskId, String configId); + +} diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java new file mode 100644 index 000000000..81d577f46 --- /dev/null +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java @@ -0,0 +1,15 @@ +package io.a2a.server.tasks; + +import io.a2a.spec.Task; + +/** + * Interface for sending push notifications for tasks. + */ +public interface PushNotificationSender { + + /** + * Sends a push notification containing the latest task state. + * @param task the task + */ + void sendNotification(Task task); +} diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotifier.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotifier.java deleted file mode 100644 index 2dfc7dff0..000000000 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotifier.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.a2a.server.tasks; - -import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.Task; - -public interface PushNotifier { - void setInfo(String taskId, PushNotificationConfig notificationConfig); - - PushNotificationConfig getInfo(String taskId); - - void deleteInfo(String taskId); - - void sendNotification(Task task); -} diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index 49913c0b6..80eaacf9e 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import jakarta.enterprise.context.Dependent; import java.io.IOException; import java.util.ArrayList; @@ -21,9 +22,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import io.a2a.spec.InternalError; -import jakarta.enterprise.context.Dependent; - import io.a2a.http.A2AHttpClient; import io.a2a.http.A2AHttpResponse; import io.a2a.server.agentexecution.AgentExecutor; @@ -31,9 +29,11 @@ import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; import io.a2a.server.events.InMemoryQueueManager; -import io.a2a.server.tasks.InMemoryPushNotifier; +import io.a2a.server.tasks.BasePushNotificationSender; +import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; import io.a2a.server.tasks.InMemoryTaskStore; -import io.a2a.server.tasks.PushNotifier; +import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.server.tasks.PushNotificationSender; import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskStore; import io.a2a.server.tasks.TaskUpdater; @@ -42,7 +42,11 @@ import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; +import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; @@ -50,9 +54,13 @@ import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.PushNotificationNotSupportedError; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; import io.a2a.spec.SendStreamingMessageRequest; @@ -75,6 +83,7 @@ import io.a2a.util.Utils; import io.quarkus.arc.profile.IfBuildProfile; import mutiny.zero.ZeroPublisher; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -130,9 +139,10 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC taskStore = new InMemoryTaskStore(); queueManager = new InMemoryQueueManager(); httpClient = new TestHttpClient(); - PushNotifier pushNotifier = new InMemoryPushNotifier(httpClient); + PushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); + PushNotificationSender pushSender = new BasePushNotificationSender(pushConfigStore, httpClient); - requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, pushNotifier, internalExecutor); + requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, pushConfigStore, pushSender, internalExecutor); } @AfterEach @@ -612,7 +622,7 @@ public void onComplete() { @Test - public void testSetPushNotificationSuccess() { + public void testSetPushNotificationConfigSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); taskStore.save(MINIMAL_TASK); @@ -620,12 +630,12 @@ public void testSetPushNotificationSuccess() { new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse response = handler.setPushNotification(request); + SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request); assertSame(taskPushConfig, response.getResult()); } @Test - public void testGetPushNotificationSuccess() { + public void testGetPushNotificationConfigSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { @@ -638,13 +648,15 @@ public void testGetPushNotificationSuccess() { MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotification(request); + handler.setPushNotificationConfig(request); GetTaskPushNotificationConfigRequest getRequest = - new GetTaskPushNotificationConfigRequest("111", new TaskIdParams(MINIMAL_TASK.getId())); - GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotification(getRequest); + new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest); - assertEquals(taskPushConfig, getResponse.getResult()); + TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), + new PushNotificationConfig.Builder().id(MINIMAL_TASK.getId()).url("http://example.com").build()); + assertEquals(expectedConfig, getResponse.getResult()); } @Test @@ -681,7 +693,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); - SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotification(stpnRequest); + SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest); assertNull(stpnResponse.getError()); Message msg = new Message.Builder(MESSAGE) @@ -1036,24 +1048,23 @@ public void testPushNotificationsNotSupportedError() { SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest.Builder() .params(config) .build(); - SetTaskPushNotificationConfigResponse response = handler.setPushNotification(request); - assertInstanceOf(InvalidRequestError.class, response.getError()); - assertEquals("Push notifications are not supported by the agent", response.getError().getMessage()); + SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request); + assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); } @Test - public void testOnGetPushNotificationNoPushNotifier() { + public void testOnGetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, internalExecutor); + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); taskStore.save(MINIMAL_TASK); GetTaskPushNotificationConfigRequest request = - new GetTaskPushNotificationConfigRequest("id", new TaskIdParams(MINIMAL_TASK.getId())); - GetTaskPushNotificationConfigResponse response = handler.getPushNotification(request); + new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request); assertNotNull(response.getError()); assertInstanceOf(UnsupportedOperationError.class, response.getError()); @@ -1061,10 +1072,10 @@ public void testOnGetPushNotificationNoPushNotifier() { } @Test - public void testOnSetPushNotificationNoPushNotifier() { + public void testOnSetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, internalExecutor); + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); @@ -1080,7 +1091,7 @@ public void testOnSetPushNotificationNoPushNotifier() { SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest.Builder() .params(config) .build(); - SetTaskPushNotificationConfigResponse response = handler.setPushNotification(request); + SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request); assertInstanceOf(UnsupportedOperationError.class, response.getError()); assertEquals("This operation is not supported", response.getError().getMessage()); @@ -1153,7 +1164,7 @@ public void testDefaultRequestHandlerWithCustomComponents() { @Test public void testOnMessageSendErrorHandling() { DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, internalExecutor); + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); @@ -1241,6 +1252,175 @@ public void onComplete() { assertInstanceOf(InternalError.class, results.get(0).getError()); } + @Test + public void testListPushNotificationConfig() { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build()); + SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + handler.setPushNotificationConfig(request); + + ListTaskPushNotificationConfigRequest listRequest = + new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + + assertEquals("111", listResponse.getId()); + assertEquals(1, listResponse.getResult().size()); + assertEquals(taskPushConfig, listResponse.getResult().get(0)); + } + + @Test + public void testListPushNotificationConfigNotSupported() { + AgentCard card = createAgentCard(true, false, true); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build()); + SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + handler.setPushNotificationConfig(request); + + ListTaskPushNotificationConfigRequest listRequest = + new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + + assertEquals("111", listResponse.getId()); + assertNull(listResponse.getResult()); + assertInstanceOf(PushNotificationNotSupportedError.class, listResponse.getError()); + } + + @Test + public void testListPushNotificationConfigNoPushConfigStore() { + DefaultRequestHandler requestHandler = + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + ListTaskPushNotificationConfigRequest listRequest = + new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + + assertEquals("111", listResponse.getId()); + assertNull(listResponse.getResult()); + assertInstanceOf(UnsupportedOperationError.class, listResponse.getError()); + } + + @Test + public void testListPushNotificationConfigTaskNotFound() { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + ListTaskPushNotificationConfigRequest listRequest = + new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + + assertEquals("111", listResponse.getId()); + assertNull(listResponse.getResult()); + assertInstanceOf(TaskNotFoundError.class, listResponse.getError()); + } + + @Test + public void testDeletePushNotificationConfig() { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build()); + SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + handler.setPushNotificationConfig(request); + + DeleteTaskPushNotificationConfigRequest deleteRequest = + new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest); + + assertEquals("111", deleteResponse.getId()); + assertNull(deleteResponse.getError()); + assertNull(deleteResponse.getResult()); + } + + @Test + public void testDeletePushNotificationConfigNotSupported() { + AgentCard card = createAgentCard(true, false, true); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build()); + SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + handler.setPushNotificationConfig(request); + + DeleteTaskPushNotificationConfigRequest deleteRequest = + new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest); + + assertEquals("111", deleteResponse.getId()); + assertNull(deleteResponse.getResult()); + assertInstanceOf(PushNotificationNotSupportedError.class, deleteResponse.getError()); + } + + @Test + public void testDeletePushNotificationConfigNoPushConfigStore() { + DefaultRequestHandler requestHandler = + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build()); + SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + handler.setPushNotificationConfig(request); + + DeleteTaskPushNotificationConfigRequest deleteRequest = + new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest); + + assertEquals("111", deleteResponse.getId()); + assertNull(deleteResponse.getResult()); + assertInstanceOf(UnsupportedOperationError.class, deleteResponse.getError()); + } + private static AgentCard createAgentCard(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory) { return new AgentCard.Builder() .name("test-card") @@ -1256,6 +1436,7 @@ private static AgentCard createAgentCard(boolean streaming, boolean pushNotifica .defaultInputModes(new ArrayList<>()) .defaultOutputModes(new ArrayList<>()) .skills(new ArrayList<>()) + .protocolVersion("0.2.5") .build(); } diff --git a/spec/pom.xml b/spec/pom.xml index cddec7a1c..c2decdd38 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-java-sdk-spec diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index 8429b16f5..e394fb626 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -17,7 +17,8 @@ public record AgentCard(String name, String description, String url, AgentProvid String version, String documentationUrl, AgentCapabilities capabilities, List defaultInputModes, List defaultOutputModes, List skills, boolean supportsAuthenticatedExtendedCard, Map securitySchemes, - List>> security, String iconUrl) { + List>> security, String iconUrl, List additionalInterfaces, + String preferredTransport, String protocolVersion) { private static final String TEXT_MODE = "text"; @@ -30,6 +31,7 @@ public record AgentCard(String name, String description, String url, AgentProvid Assert.checkNotNullParam("skills", skills); Assert.checkNotNullParam("url", url); Assert.checkNotNullParam("version", version); + Assert.checkNotNullParam("protocolVersion", protocolVersion); } public static class Builder { @@ -47,6 +49,9 @@ public static class Builder { private Map securitySchemes; private List>> security; private String iconUrl; + private List additionalInterfaces; + String preferredTransport; + String protocolVersion; public Builder name(String name) { this.name = name; @@ -118,10 +123,26 @@ public Builder iconUrl(String iconUrl) { return this; } + public Builder additionalInterfaces(List additionalInterfaces) { + this.additionalInterfaces = additionalInterfaces; + return this; + } + + public Builder preferredTransport(String preferredTransport) { + this.preferredTransport = preferredTransport; + return this; + } + + public Builder protocolVersion(String protocolVersion) { + this.protocolVersion = protocolVersion; + return this; + } + public AgentCard build() { return new AgentCard(name, description, url, provider, version, documentationUrl, capabilities, defaultInputModes, defaultOutputModes, skills, - supportsAuthenticatedExtendedCard, securitySchemes, security, iconUrl); + supportsAuthenticatedExtendedCard, securitySchemes, security, iconUrl, + additionalInterfaces, preferredTransport, protocolVersion); } } } diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java new file mode 100644 index 000000000..ab2b7307d --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -0,0 +1,18 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.a2a.util.Assert; + +/** + * Provides a declaration of the target url and the supported transport to interact with the agent. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AgentInterface(String transport, String url) { + + public AgentInterface { + Assert.checkNotNullParam("transport", transport); + Assert.checkNotNullParam("url", url); + } +} diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java index 02bd63461..9ef775118 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java @@ -15,7 +15,7 @@ public final class CancelTaskResponse extends JSONRPCResponse { @JsonCreator public CancelTaskResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("result") Task result, @JsonProperty("error") JSONRPCError error) { - super(jsonrpc, id, result, error); + super(jsonrpc, id, result, error, Task.class); } public CancelTaskResponse(Object id, JSONRPCError error) { diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java new file mode 100644 index 000000000..a64421a4c --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -0,0 +1,50 @@ +package io.a2a.spec; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.a2a.util.Assert; + +/** + * Parameters for removing pushNotificationConfiguration associated with a Task. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, Map metadata) { + + public DeleteTaskPushNotificationConfigParams { + Assert.checkNotNullParam("id", id); + Assert.checkNotNullParam("pushNotificationConfigId", pushNotificationConfigId); + } + + public DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId) { + this(id, pushNotificationConfigId, null); + } + + public static class Builder { + String id; + String pushNotificationConfigId; + Map metadata; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder pushNotificationConfigId(String pushNotificationConfigId) { + this.pushNotificationConfigId = pushNotificationConfigId; + return this; + } + + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public DeleteTaskPushNotificationConfigParams build() { + return new DeleteTaskPushNotificationConfigParams(id, pushNotificationConfigId, metadata); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java new file mode 100644 index 000000000..99f50ebfd --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -0,0 +1,77 @@ +package io.a2a.spec; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.a2a.util.Assert; +import io.a2a.util.Utils; + +/** + * A delete task push notification config request. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class DeleteTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + + public static final String METHOD = "tasks/pushNotificationConfig/delete"; + + @JsonCreator + public DeleteTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("method") String method, + @JsonProperty("params") DeleteTaskPushNotificationConfigParams params) { + if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { + throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); + } + Assert.checkNotNullParam("method", method); + if (! method.equals(METHOD)) { + throw new IllegalArgumentException("Invalid DeleteTaskPushNotificationConfigRequest method"); + } + Assert.isNullOrStringOrInteger(id); + this.jsonrpc = Utils.defaultIfNull(jsonrpc, JSONRPC_VERSION); + this.id = id; + this.method = method; + this.params = params; + } + + public DeleteTaskPushNotificationConfigRequest(String id, DeleteTaskPushNotificationConfigParams params) { + this(null, id, METHOD, params); + } + + public static class Builder { + private String jsonrpc; + private Object id; + private String method; + private DeleteTaskPushNotificationConfigParams params; + + public Builder jsonrpc(String jsonrpc) { + this.jsonrpc = jsonrpc; + return this; + } + + public Builder id(Object id) { + this.id = id; + return this; + } + + public Builder method(String method) { + this.method = method; + return this; + } + + public Builder params(DeleteTaskPushNotificationConfigParams params) { + this.params = params; + return this; + } + + public DeleteTaskPushNotificationConfigRequest build() { + if (id == null) { + id = UUID.randomUUID().toString(); + } + return new DeleteTaskPushNotificationConfigRequest(jsonrpc, id, method, params); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java new file mode 100644 index 000000000..0f65b5ad5 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java @@ -0,0 +1,32 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +/** + * A response for a delete task push notification config request. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonSerialize(using = JSONRPCVoidResponseSerializer.class) +public final class DeleteTaskPushNotificationConfigResponse extends JSONRPCResponse { + + @JsonCreator + public DeleteTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("result") Void result, + @JsonProperty("error") JSONRPCError error) { + super(jsonrpc, id, result, error, Void.class); + } + + public DeleteTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { + this(null, id, null, error); + } + + public DeleteTaskPushNotificationConfigResponse(Object id) { + this(null, id, null, null); + } + +} diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java new file mode 100644 index 000000000..d8952f87d --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -0,0 +1,53 @@ +package io.a2a.spec; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.a2a.util.Assert; + +/** + * Parameters for fetching a pushNotificationConfiguration associated with a Task. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record GetTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, Map metadata) { + + public GetTaskPushNotificationConfigParams { + Assert.checkNotNullParam("id", id); + } + + public GetTaskPushNotificationConfigParams(String id) { + this(id, null, null); + } + + public GetTaskPushNotificationConfigParams(String id, String pushNotificationConfigId) { + this(id, pushNotificationConfigId, null); + } + + public static class Builder { + String id; + String pushNotificationConfigId; + Map metadata; + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder pushNotificationConfigId(String pushNotificationConfigId) { + this.pushNotificationConfigId = pushNotificationConfigId; + return this; + } + + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public GetTaskPushNotificationConfigParams build() { + return new GetTaskPushNotificationConfigParams(id, pushNotificationConfigId, metadata); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index ab39015bb..b353e0cc8 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -14,13 +14,13 @@ */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { +public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "tasks/pushNotificationConfig/get"; @JsonCreator public GetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { + @JsonProperty("method") String method, @JsonProperty("params") GetTaskPushNotificationConfigParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } @@ -35,7 +35,7 @@ public GetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String json this.params = params; } - public GetTaskPushNotificationConfigRequest(String id, TaskIdParams params) { + public GetTaskPushNotificationConfigRequest(String id, GetTaskPushNotificationConfigParams params) { this(null, id, METHOD, params); } @@ -43,7 +43,7 @@ public static class Builder { private String jsonrpc; private Object id; private String method; - private TaskIdParams params; + private GetTaskPushNotificationConfigParams params; public GetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; @@ -60,7 +60,7 @@ public GetTaskPushNotificationConfigRequest.Builder method(String method) { return this; } - public GetTaskPushNotificationConfigRequest.Builder params(TaskIdParams params) { + public GetTaskPushNotificationConfigRequest.Builder params(GetTaskPushNotificationConfigParams params) { this.params = params; return this; } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java index c4340188f..116799a9e 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java @@ -16,7 +16,7 @@ public final class GetTaskPushNotificationConfigResponse extends JSONRPCResponse public GetTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("result") TaskPushNotificationConfig result, @JsonProperty("error") JSONRPCError error) { - super(jsonrpc, id, result, error); + super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } public GetTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { diff --git a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java index e51cb66aa..0d27a8e68 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java @@ -15,7 +15,7 @@ public final class GetTaskResponse extends JSONRPCResponse { @JsonCreator public GetTaskResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("result") Task result, @JsonProperty("error") JSONRPCError error) { - super(jsonrpc, id, result, error); + super(jsonrpc, id, result, error, Task.class); } public GetTaskResponse(Object id, JSONRPCError error) { diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java index 95ac5d341..ea7846655 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java @@ -17,7 +17,7 @@ public final class JSONRPCErrorResponse extends JSONRPCResponse { @JsonCreator public JSONRPCErrorResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("result") Void result, @JsonProperty("error") JSONRPCError error) { - super(jsonrpc, id, result, error); + super(jsonrpc, id, result, error, Void.class); Assert.checkNotNullParam("error", error); } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java index fe21ffab8..cf0134efe 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java @@ -4,13 +4,10 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import java.io.IOException; - public abstract class JSONRPCRequestDeserializerBase extends StdDeserializer> { public JSONRPCRequestDeserializerBase() { @@ -21,35 +18,6 @@ public JSONRPCRequestDeserializerBase(Class vc) { super(vc); } - @Override - public JSONRPCRequest deserialize(JsonParser jsonParser, DeserializationContext context) - throws IOException, JsonProcessingException { - JsonNode treeNode = jsonParser.getCodec().readTree(jsonParser); - String jsonrpc = getAndValidateJsonrpc(treeNode, jsonParser); - String method = getAndValidateMethod(treeNode, jsonParser); - Object id = getAndValidateId(treeNode, jsonParser); - JsonNode paramsNode = treeNode.get("params"); - - switch (method) { - case GetTaskRequest.METHOD: - return new GetTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskQueryParams.class)); - case CancelTaskRequest.METHOD: - return new CancelTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SetTaskPushNotificationConfigRequest.METHOD: - return new SetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskPushNotificationConfig.class)); - case GetTaskPushNotificationConfigRequest.METHOD: - return new GetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SendMessageRequest.METHOD: - return new SendMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); - case TaskResubscriptionRequest.METHOD: - return new TaskResubscriptionRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SendStreamingMessageRequest.METHOD: - return new SendStreamingMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); - default: - throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); - } - } - protected T getAndValidateParams(JsonNode paramsNode, JsonParser jsonParser, JsonNode node, Class paramsType) throws JsonMappingException { if (paramsNode == null) { return null; @@ -112,7 +80,9 @@ protected static boolean isValidMethodName(String methodName) { || methodName.equals(SetTaskPushNotificationConfigRequest.METHOD) || methodName.equals(TaskResubscriptionRequest.METHOD) || methodName.equals(SendMessageRequest.METHOD) - || methodName.equals(SendStreamingMessageRequest.METHOD)); + || methodName.equals(SendStreamingMessageRequest.METHOD) + || methodName.equals(ListTaskPushNotificationConfigRequest.METHOD) + || methodName.equals(DeleteTaskPushNotificationConfigRequest.METHOD)); } } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 1be348043..3a382b1a7 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -2,6 +2,7 @@ import static io.a2a.util.Utils.defaultIfNull; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -14,7 +15,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits SendStreamingMessageResponse, GetTaskResponse, CancelTaskResponse, SetTaskPushNotificationConfigResponse, GetTaskPushNotificationConfigResponse, - SendMessageResponse, JSONRPCErrorResponse { + SendMessageResponse, DeleteTaskPushNotificationConfigResponse, ListTaskPushNotificationConfigResponse, JSONRPCErrorResponse { protected String jsonrpc; protected Object id; @@ -24,14 +25,14 @@ public abstract sealed class JSONRPCResponse implements JSONRPCMessage permit public JSONRPCResponse() { } - public JSONRPCResponse(String jsonrpc, Object id, T result, JSONRPCError error) { + public JSONRPCResponse(String jsonrpc, Object id, T result, JSONRPCError error, Class resultType) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } if (error != null && result != null) { throw new IllegalArgumentException("Invalid JSON-RPC error response"); } - if (error == null && result == null) { + if (error == null && result == null && ! Void.class.equals(resultType)) { throw new IllegalArgumentException("Invalid JSON-RPC success response"); } Assert.isNullOrStringOrInteger(id); diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java new file mode 100644 index 000000000..1d5049410 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java @@ -0,0 +1,34 @@ +package io.a2a.spec; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +public class JSONRPCVoidResponseSerializer extends StdSerializer> { + + private static final JSONRPCErrorSerializer JSON_RPC_ERROR_SERIALIZER = new JSONRPCErrorSerializer(); + + public JSONRPCVoidResponseSerializer() { + this(null); + } + + public JSONRPCVoidResponseSerializer(Class> vc) { + super(vc); + } + + @Override + public void serialize(JSONRPCResponse value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + gen.writeStringField("jsonrpc", value.getJsonrpc()); + gen.writeObjectField("id", value.getId()); + if (value.getError() != null) { + gen.writeFieldName("error"); + JSON_RPC_ERROR_SERIALIZER.serialize(value.getError(), gen, provider); + } else { + gen.writeNullField("result"); + } + gen.writeEndObject(); + } +} diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java new file mode 100644 index 000000000..5ebb12f76 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -0,0 +1,24 @@ +package io.a2a.spec; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.a2a.util.Assert; + +/** + * Parameters for getting list of pushNotificationConfigurations associated with a Task. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListTaskPushNotificationConfigParams(String id, Map metadata) { + + public ListTaskPushNotificationConfigParams { + Assert.checkNotNullParam("id", id); + } + + public ListTaskPushNotificationConfigParams(String id) { + this(id, null); + } +} diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java new file mode 100644 index 000000000..90ba0f1f5 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java @@ -0,0 +1,77 @@ +package io.a2a.spec; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.a2a.util.Assert; +import io.a2a.util.Utils; + +/** + * A list task push notification config request. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ListTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + + public static final String METHOD = "tasks/pushNotificationConfig/list"; + + @JsonCreator + public ListTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("method") String method, + @JsonProperty("params") ListTaskPushNotificationConfigParams params) { + if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { + throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); + } + Assert.checkNotNullParam("method", method); + if (! method.equals(METHOD)) { + throw new IllegalArgumentException("Invalid ListTaskPushNotificationConfigRequest method"); + } + Assert.isNullOrStringOrInteger(id); + this.jsonrpc = Utils.defaultIfNull(jsonrpc, JSONRPC_VERSION); + this.id = id; + this.method = method; + this.params = params; + } + + public ListTaskPushNotificationConfigRequest(String id, ListTaskPushNotificationConfigParams params) { + this(null, id, METHOD, params); + } + + public static class Builder { + private String jsonrpc; + private Object id; + private String method; + private ListTaskPushNotificationConfigParams params; + + public Builder jsonrpc(String jsonrpc) { + this.jsonrpc = jsonrpc; + return this; + } + + public Builder id(Object id) { + this.id = id; + return this; + } + + public Builder method(String method) { + this.method = method; + return this; + } + + public Builder params(ListTaskPushNotificationConfigParams params) { + this.params = params; + return this; + } + + public ListTaskPushNotificationConfigRequest build() { + if (id == null) { + id = UUID.randomUUID().toString(); + } + return new ListTaskPushNotificationConfigRequest(jsonrpc, id, method, params); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java new file mode 100644 index 000000000..cc610416e --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java @@ -0,0 +1,32 @@ +package io.a2a.spec; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A response for a list task push notification config request. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ListTaskPushNotificationConfigResponse extends JSONRPCResponse> { + + @JsonCreator + public ListTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("result") List result, + @JsonProperty("error") JSONRPCError error) { + super(jsonrpc, id, result, error, (Class>) (Class) List.class); + } + + public ListTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { + this(null, id, null, error); + } + + public ListTaskPushNotificationConfigResponse(Object id, List result) { + this(null, id, result, null); + } + +} diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java index f79800215..1c0a696e7 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java @@ -12,5 +12,5 @@ @JsonDeserialize(using = NonStreamingJSONRPCRequestDeserializer.class) public abstract sealed class NonStreamingJSONRPCRequest extends JSONRPCRequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, - SendMessageRequest { + SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest { } diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java index f2978b66f..c97c524c5 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java @@ -1,12 +1,12 @@ package io.a2a.spec; +import java.io.IOException; + import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; -import java.io.IOException; - public class NonStreamingJSONRPCRequestDeserializer extends JSONRPCRequestDeserializerBase> { public NonStreamingJSONRPCRequestDeserializer() { @@ -38,10 +38,16 @@ public NonStreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserial getAndValidateParams(paramsNode, jsonParser, treeNode, TaskPushNotificationConfig.class)); case GetTaskPushNotificationConfigRequest.METHOD: return new GetTaskPushNotificationConfigRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); + getAndValidateParams(paramsNode, jsonParser, treeNode, GetTaskPushNotificationConfigParams.class)); case SendMessageRequest.METHOD: return new SendMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); + case ListTaskPushNotificationConfigRequest.METHOD: + return new ListTaskPushNotificationConfigRequest(jsonrpc, id, method, + getAndValidateParams(paramsNode, jsonParser, treeNode, ListTaskPushNotificationConfigParams.class)); + case DeleteTaskPushNotificationConfigRequest.METHOD: + return new DeleteTaskPushNotificationConfigRequest(jsonrpc, id, method, + getAndValidateParams(paramsNode, jsonParser, treeNode, DeleteTaskPushNotificationConfigParams.class)); default: throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); } diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index 19e9d3491..34270637f 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -21,6 +21,16 @@ public static class Builder { private PushNotificationAuthenticationInfo authentication; private String id; + public Builder() { + } + + public Builder(PushNotificationConfig notificationConfig) { + this.url = notificationConfig.url; + this.token = notificationConfig.token; + this.authentication = notificationConfig.authentication; + this.id = notificationConfig.id; + } + public Builder url(String url) { this.url = url; return this; diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index b97094cc0..d639b7bab 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -13,6 +13,10 @@ public class PushNotificationNotSupportedError extends JSONRPCError { public final static Integer DEFAULT_CODE = -32003; + public PushNotificationNotSupportedError() { + this(null, null, null); + } + @JsonCreator public PushNotificationNotSupportedError( @JsonProperty("code") Integer code, diff --git a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java index fa95bad36..901beba90 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java @@ -18,11 +18,7 @@ public final class SendMessageResponse extends JSONRPCResponse { @JsonCreator public SendMessageResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("result") EventKind result, @JsonProperty("error") JSONRPCError error) { - this.jsonrpc = defaultIfNull(jsonrpc, JSONRPC_VERSION); - Assert.isNullOrStringOrInteger(id); - this.id = id; - this.result = result; - this.error = error; + super(jsonrpc, id, result, error, EventKind.class); } public SendMessageResponse(Object id, EventKind result) { diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java index b3597bfb6..f3bcb9676 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java @@ -18,11 +18,7 @@ public final class SendStreamingMessageResponse extends JSONRPCResponse io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT a2a-tck-server diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 7abf29a12..610443ac8 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -37,6 +37,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) + .protocolVersion("0.2.5") .build(); } } diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 0e205b547..a66e91d08 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.3.Beta2-SNAPSHOT + 0.2.5.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 8624f8e27..3671da130 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; import static org.wildfly.common.Assert.assertNotNull; import static org.wildfly.common.Assert.assertTrue; @@ -28,11 +29,16 @@ import jakarta.ws.rs.core.MediaType; import com.fasterxml.jackson.core.JsonProcessingException; + +import io.a2a.client.A2AClient; +import io.a2a.spec.A2AServerException; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; +import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; @@ -42,6 +48,7 @@ import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.JSONRPCErrorResponse; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.MethodNotFoundError; @@ -69,6 +76,7 @@ import io.a2a.util.Utils; import io.restassured.RestAssured; import io.restassured.specification.RequestSpecification; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -111,9 +119,11 @@ public abstract class AbstractA2AServerTest { public static final String APPLICATION_JSON = "application/json"; private final int serverPort; + private A2AClient client; protected AbstractA2AServerTest(int serverPort) { this.serverPort = serverPort; + this.client = new A2AClient("http://localhost:" + serverPort); } @Test @@ -338,6 +348,7 @@ public void testSetPushNotificationSuccess() throws Exception { assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (Exception e) { } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @@ -363,7 +374,7 @@ public void testGetPushNotificationSuccess() throws Exception { assertNotNull(setTaskPushNotificationResponse); GetTaskPushNotificationConfigRequest request = - new GetTaskPushNotificationConfigRequest("111", new TaskIdParams(MINIMAL_TASK.getId())); + new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); GetTaskPushNotificationConfigResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(request) @@ -380,6 +391,7 @@ public void testGetPushNotificationSuccess() throws Exception { assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (Exception e) { } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @@ -842,6 +854,220 @@ private void testSendStreamingMessage(String mediaType) throws Exception { } + @Test + public void testListPushNotificationConfigWithConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + PushNotificationConfig notificationConfig1 = + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config1") + .build(); + PushNotificationConfig notificationConfig2 = + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config2") + .build(); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + + try { + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); + assertEquals("111", listResponse.getId()); + assertEquals(2, listResponse.getResult().size()); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1), listResponse.getResult().get(0)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2), listResponse.getResult().get(1)); + } catch (Exception e) { + fail(); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testListPushNotificationConfigWithoutConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + PushNotificationConfig notificationConfig1 = + new PushNotificationConfig.Builder() + .url("http://1.example.com") + .build(); + PushNotificationConfig notificationConfig2 = + new PushNotificationConfig.Builder() + .url("http://2.example.com") + .build(); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + + // will overwrite the previous one + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + try { + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); + assertEquals("111", listResponse.getId()); + assertEquals(1, listResponse.getResult().size()); + + PushNotificationConfig expectedNotificationConfig = new PushNotificationConfig.Builder() + .url("http://2.example.com") + .id(MINIMAL_TASK.getId()) + .build(); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), expectedNotificationConfig), + listResponse.getResult().get(0)); + } catch (Exception e) { + fail(); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testListPushNotificationConfigTaskNotFound() { + try { + client.listTaskPushNotificationConfig("111", "non-existent-task"); + fail(); + } catch (A2AServerException e) { + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } + } + + @Test + public void testListPushNotificationConfigEmptyList() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); + assertEquals("111", listResponse.getId()); + assertEquals(0, listResponse.getResult().size()); + } catch (Exception e) { + fail(); + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testDeletePushNotificationConfigWithValidConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + saveTaskInTaskStore(new Task.Builder() + .id("task-456") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build()); + + PushNotificationConfig notificationConfig1 = + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config1") + .build(); + PushNotificationConfig notificationConfig2 = + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config2") + .build(); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + savePushNotificationConfigInStore("task-456", notificationConfig1); + + try { + // specify the config ID to delete + DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), + "config1"); + assertNull(deleteResponse.getError()); + assertNull(deleteResponse.getResult()); + + // should now be 1 left + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); + assertEquals(1, listResponse.getResult().size()); + + // should remain unchanged, this is a different task + listResponse = client.listTaskPushNotificationConfig("task-456"); + assertEquals(1, listResponse.getResult().size()); + } catch (Exception e) { + fail(); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deletePushNotificationConfigInStore("task-456", "config1"); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore("task-456"); + } + } + + @Test + public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + PushNotificationConfig notificationConfig1 = + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config1") + .build(); + PushNotificationConfig notificationConfig2 = + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config2") + .build(); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + + try { + DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), + "non-existent-config-id"); + assertNull(deleteResponse.getError()); + assertNull(deleteResponse.getResult()); + + // should remain unchanged + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); + assertEquals(2, listResponse.getResult().size()); + } catch (Exception e) { + fail(); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testDeletePushNotificationConfigTaskNotFound() { + try { + client.deleteTaskPushNotificationConfig("non-existent-task", "non-existent-config-id"); + fail(); + } catch (A2AServerException e) { + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } + } + + @Test + public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + PushNotificationConfig notificationConfig1 = + new PushNotificationConfig.Builder() + .url("http://1.example.com") + .build(); + PushNotificationConfig notificationConfig2 = + new PushNotificationConfig.Builder() + .url("http://2.example.com") + .build(); + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + + // this one will overwrite the previous one + savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + + try { + DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), + MINIMAL_TASK.getId()); + assertNull(deleteResponse.getError()); + assertNull(deleteResponse.getResult()); + + // should now be 0 + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); + assertEquals(0, listResponse.getResult().size()); + } catch (Exception e) { + fail(); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { line = extractSseData(line); if (line != null) { @@ -1025,6 +1251,36 @@ private int getStreamingSubscribedCount() { } } + protected void deletePushNotificationConfigInStore(String taskId, String configId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(("http://localhost:" + serverPort + "/test/task/" + taskId + "/config/" + configId))) + .DELETE() + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); + } + } + + protected void savePushNotificationConfigInStore(String taskId, PushNotificationConfig notificationConfig) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(notificationConfig))) + .header("Content-Type", APPLICATION_JSON) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Creating task push notification config failed! " + response.body()); + } + } + private static class BreakException extends RuntimeException { } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index f68f44967..3354c1522 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -32,6 +32,7 @@ public AgentCard agentCard() { .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(new ArrayList<>()) + .protocolVersion("0.2.5") .build(); } } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java index 8cd333a55..c65766dd8 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java @@ -2,14 +2,13 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.core.Response; import io.a2a.server.events.QueueManager; +import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.server.tasks.TaskStore; import io.a2a.spec.Event; +import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; -import io.quarkus.arc.profile.IfBuildProfile; /** * Contains utilities to interact with the server side for the tests. @@ -28,6 +27,9 @@ public class TestUtilsBean { @Inject QueueManager queueManager; + @Inject + PushNotificationConfigStore pushNotificationConfigStore; + public void saveTask(Task task) { taskStore.save(task); } @@ -47,4 +49,12 @@ public void ensureQueue(String taskId) { public void enqueueEvent(String taskId, Event event) { queueManager.get(taskId).enqueueEvent(event); } + + public void deleteTaskPushNotificationConfig(String taskId, String configId) { + pushNotificationConfigStore.deleteInfo(taskId, configId); + } + + public void saveTaskPushNotificationConfig(String taskId, PushNotificationConfig notificationConfig) { + pushNotificationConfigStore.setInfo(taskId, notificationConfig); + } } From da6e49d422b912dbd406b5580f56da7868713815 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 18 Jul 2025 11:23:01 -0400 Subject: [PATCH 040/493] fix: Fix JSONRPCVoidResponseSerializer constructor (#193) # Description - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) --- .../java/io/a2a/spec/JSONRPCVoidResponseSerializer.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java index 1d5049410..200bc4cd4 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java @@ -5,17 +5,15 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.fasterxml.jackson.databind.type.TypeFactory; public class JSONRPCVoidResponseSerializer extends StdSerializer> { private static final JSONRPCErrorSerializer JSON_RPC_ERROR_SERIALIZER = new JSONRPCErrorSerializer(); public JSONRPCVoidResponseSerializer() { - this(null); - } - - public JSONRPCVoidResponseSerializer(Class> vc) { - super(vc); + super(TypeFactory.defaultInstance().constructParametricType(JSONRPCResponse.class, + Void.class)); } @Override From b420b5297e8de16682a564c70e16ef48a013a025 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 18 Jul 2025 13:27:46 -0400 Subject: [PATCH 041/493] fix: Remove unnecessary check in the tck AgentExecutor implementation (#194) # Description **NOTE:** This will fail the TCK tests until https://github.com/a2aproject/a2a-tck/pull/34 is included in a tag Depends on https://github.com/a2aproject/a2a-tck/pull/34 - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) --- .../main/java/io/a2a/tck/server/AgentExecutorProducer.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index 592546591..4f36e4e5b 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -29,10 +29,6 @@ private static class FireAndForgetAgentExecutor implements AgentExecutor { public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { Task task = context.getTask(); - if (context.getMessage().getTaskId() != null && task == null && context.getMessage().getTaskId().startsWith("non-existent")) { - throw new TaskNotFoundError(); - } - if (task == null) { task = new Task.Builder() .id(context.getTaskId()) From bc3c8cb87e87e7e8c59df58a77cbcb22ecdc4f6f Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 21 Jul 2025 12:16:52 +0100 Subject: [PATCH 042/493] chore: Release 0.2.5.Beta1 and move to next snapshot (#196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference-impl/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 9ec3b9f5b..80c9ee5d0 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index 254a6da14..aca4ffe4d 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 3584a4202..f8246b477 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 8253fcc83..ff6f3f837 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 979b4bf99..4902c702d 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index c7e409f2a..aa7122fa5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index c2397b698..46c861d09 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-reference-server diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 27b38fe99..3ba5429e9 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index c2decdd38..e779c14c1 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 0e9ebf563..becf3fcf9 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index a66e91d08..abdff0109 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta1-SNAPSHOT + 0.2.5.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common From 6f5c8bec72dca613943fe54a1e6056518fa665df Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 21 Jul 2025 16:53:34 +0100 Subject: [PATCH 043/493] chore: automate releasing to Maven central when a tag happens. (#197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../workflows/release-to-maven-central.yml | 52 +++++++++++++++++++ pom.xml | 4 ++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/release-to-maven-central.yml diff --git a/.github/workflows/release-to-maven-central.yml b/.github/workflows/release-to-maven-central.yml new file mode 100644 index 000000000..c0f5a9724 --- /dev/null +++ b/.github/workflows/release-to-maven-central.yml @@ -0,0 +1,52 @@ +name: Publish release to Maven Central + +on: + push: + tags: + - 'v?[0-9]+.[0-9]+.[0-9]+*' # Trigger on tags like v1.0.0, 1.2.3, v1.2.3.Alpha1 etc. + +jobs: + publish: + # Only run this job for the main repository, not for forks + if: github.repository == 'a2aproject/a2a-java' + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Use secrets to import GPG key + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} + passphrase: ${{ secrets.GPG_SIGNING_PASSPHRASE }} + + # Create settings.xml for Maven since it needs the 'central-a2asdk-temp' server. + # Populate wqith username and password from secrets + - name: Create settings.xml + run: | + mkdir -p ~/.m2 + echo "central-a2asdk-temp${{ secrets.CENTRAL_TOKEN_USERNAME }}${{ secrets.CENTRAL_TOKEN_PASSWORD }}" > ~/.m2/settings.xml + + # Deploy to Maven Central + # -s uses the settings file we created. + - name: Publish to Maven Central + run: > + mvn -B deploy + -s ~/.m2/settings.xml + -P release + -DskipTests + -Drelease.auto.publish=true + env: + # GPG passphrase is set as an environment variable for the gpg plugin to use + GPG_PASSPHRASE: ${{ secrets.GPG_SIGNING_PASSPHRASE }} diff --git a/pom.xml b/pom.xml index aa7122fa5..48564f77a 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,9 @@ true + + + false @@ -206,6 +209,7 @@ central-a2asdk-temp + ${release.auto.publish} From b5c08bdf8d319211947cbdd75c17216e84379a87 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 21 Jul 2025 16:56:15 +0100 Subject: [PATCH 044/493] Prepare 0.2.5.Beta2 so we can check the release to maven job --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../main/java/io/a2a/examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference-impl/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 80c9ee5d0..f6edfb553 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index aca4ffe4d..4dadef7bb 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index f8246b477..5a26715ae 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 597113380..0ff714cdc 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5.Beta2-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5.Beta2 //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index ff6f3f837..3f374a197 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 4902c702d..72f513a30 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index 48564f77a..2afbb1133 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index 46c861d09..00e66df56 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-reference-server diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 3ba5429e9..e3e0ba6ec 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index e779c14c1..4c4d6a505 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index becf3fcf9..bcb6a3048 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index abdff0109..0ecce7bfa 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2-SNAPSHOT + 0.2.5.Beta2 ../../pom.xml a2a-java-sdk-tests-server-common From b568d03496515d49ad57d75cc92fdad2100dd0ac Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 21 Jul 2025 16:57:24 +0100 Subject: [PATCH 045/493] Bump to next SNAPSHOT --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../main/java/io/a2a/examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference-impl/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index f6edfb553..6c3e60d82 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index 4dadef7bb..2974014c9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 5a26715ae..cbde92e12 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 0ff714cdc..91f2129eb 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5.Beta2 +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5.Beta3-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 3f374a197..8d7cd19f9 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 72f513a30..41dbf0582 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index 2afbb1133..c1cc446e7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index 00e66df56..71b4fa067 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-reference-server diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index e3e0ba6ec..eb5b140fe 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index 4c4d6a505..fb3a4358e 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index bcb6a3048..a2694a183 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 0ecce7bfa..69281e341 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta2 + 0.2.5.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common From 865ee63cfde8668fab33b014b4fa9a1571d9b9c5 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 21 Jul 2025 17:09:12 +0100 Subject: [PATCH 046/493] chore: make workflows manually runnable (#195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .github/workflows/build-and-test.yml | 1 + .github/workflows/run-tck.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9dec62a96..bdc097791 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -4,6 +4,7 @@ on: # Handle all branches for now push: pull_request: + workflow_dispatch: # Only run the latest job concurrency: diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 291802da4..7793157dc 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -8,6 +8,7 @@ on: pull_request: branches: - main + workflow_dispatch: env: # Tag of the TCK From 483760f14dfde17d59c4f79fc7893852065e4ec9 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 21 Jul 2025 13:50:52 -0400 Subject: [PATCH 047/493] chore: release 0.2.5 Release-As: 0.2.5 From c6627e45043c989797a83dc7fee5a6a286aaa4b6 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 21 Jul 2025 14:38:09 -0400 Subject: [PATCH 048/493] chore: Next is 0.2.5 --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference-impl/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 6c3e60d82..f7c9471c9 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index 2974014c9..210e18dee 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index cbde92e12..b9b6bedd7 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 91f2129eb..1069407e7 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5.Beta3-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5 //SOURCES HelloWorldClient.java /** @@ -19,4 +19,4 @@ public class HelloWorldRunner { public static void main(String[] args) { io.a2a.examples.helloworld.HelloWorldClient.main(args); } -} \ No newline at end of file +} diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 8d7cd19f9..6ed40846c 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 41dbf0582..d4ff8684b 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index c1cc446e7..8c0ffbdee 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index 71b4fa067..77da86040 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-reference-server diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index eb5b140fe..03a90a5b6 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index fb3a4358e..2ade9652a 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index a2694a183..4eeb8ed99 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 69281e341..3d27c6e8f 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5.Beta3-SNAPSHOT + 0.2.5-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common From d34844d15df5833e25822c74069acb636f502157 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 21 Jul 2025 14:40:40 -0400 Subject: [PATCH 049/493] chore: release 0.2.5 Release-As: 0.2.5 From f4ea4ba27000f9d5f52371ac520dc27a58d0c5f2 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 21 Jul 2025 14:54:00 -0400 Subject: [PATCH 050/493] chore: Release 0.2.5 --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference-impl/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index f7c9471c9..63ce3e982 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index 210e18dee..41f41e4a6 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index b9b6bedd7..fb0b8e09a 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-sdk-examples-client diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 6ed40846c..9e63e94e3 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index d4ff8684b..214966899 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index 8c0ffbdee..dc4dd3602 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index 77da86040..7fc1289aa 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-reference-server diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 03a90a5b6..863c5dbb1 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index 2ade9652a..ab6b8016f 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 4eeb8ed99..b454a6d71 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 3d27c6e8f..26263e201 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5-SNAPSHOT + 0.2.5 ../../pom.xml a2a-java-sdk-tests-server-common From 6caee8e875a56525a1af61a28a9cfd8d9e89b8e6 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 21 Jul 2025 14:55:14 -0400 Subject: [PATCH 051/493] chore: Next is 0.2.6.Beta1-SNAPSHOT --- client/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../main/java/io/a2a/examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference-impl/pom.xml | 2 +- sdk-server-common/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 63ce3e982..acae086f4 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-sdk-client diff --git a/common/pom.xml b/common/pom.xml index 41f41e4a6..e4c01920e 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index fb0b8e09a..8edb46cb6 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 1069407e7..144972238 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.5 +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.6.Beta1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 9e63e94e3..7900d3cef 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 214966899..ee7c5324b 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index dc4dd3602..e34d09477 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT pom diff --git a/reference-impl/pom.xml b/reference-impl/pom.xml index 7fc1289aa..b128aa3f0 100644 --- a/reference-impl/pom.xml +++ b/reference-impl/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-reference-server diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 863c5dbb1..8fc372323 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec/pom.xml b/spec/pom.xml index ab6b8016f..ce67def18 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index b454a6d71..42c25a6db 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 26263e201..831dcc86c 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.5 + 0.2.6.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common From 65f4c9372267bfcfd119f763a22d8a0c891105a9 Mon Sep 17 00:00:00 2001 From: Stefano Maestri Date: Fri, 25 Jul 2025 15:02:45 +0200 Subject: [PATCH 052/493] Fix: this is a fix for a2a-tck issue 36 (#202) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [ x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [ x] Ensure the tests pass - [ x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../java/io/a2a/tck/server/AgentExecutorProducer.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index 4f36e4e5b..8d2cee335 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -39,12 +39,21 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP eventQueue.enqueueEvent(task); } + if (context.getMessage().getMessageId().startsWith("test-resubscribe-message-id")) { + int timeoutMs = Integer.parseInt(System.getenv().getOrDefault("RESUBSCRIBE_TIMEOUT_MS", "3000")); + System.out.println("====> task id starts with test-resubscribe-message-id, sleeping for " + timeoutMs + " ms"); + try { + Thread.sleep(timeoutMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } TaskUpdater updater = new TaskUpdater(context, eventQueue); // Immediately set to WORKING state updater.startWork(); System.out.println("====> task set to WORKING, starting background execution"); - + // Method returns immediately - task continues in background System.out.println("====> execute() method returning immediately, task running in background"); } From ea56419d5cf13ac46a4ba49066ca655767582004 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 28 Jul 2025 17:10:09 +0100 Subject: [PATCH 053/493] feat: Implement ServerCallContext (#206) Port the ServerCallContext from Python --- .../server/apps/quarkus/A2AServerRoutes.java | 92 ++++++++++---- .../apps/quarkus/CallContextFactory.java | 8 ++ .../java/io/a2a/server/ServerCallContext.java | 23 +++- .../server/agentexecution/RequestContext.java | 18 ++- .../a2a/server/auth/UnauthenticatedUser.java | 6 + .../DefaultRequestHandler.java | 33 +++-- .../requesthandlers/JSONRPCHandler.java | 49 +++++--- .../requesthandlers/RequestHandler.java | 37 ++++-- .../agentexecution/RequestContextTest.java | 36 +++--- .../requesthandlers/JSONRPCHandlerTest.java | 115 ++++++++++-------- 10 files changed, 284 insertions(+), 133 deletions(-) create mode 100644 reference-impl/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 81a0df3b3..988736c91 100644 --- a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -3,6 +3,9 @@ import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicLong; @@ -18,6 +21,9 @@ import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; import io.a2a.server.ExtendedAgentCard; +import io.a2a.server.ServerCallContext; +import io.a2a.server.auth.UnauthenticatedUser; +import io.a2a.server.auth.User; import io.a2a.server.requesthandlers.JSONRPCHandler; import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; @@ -78,9 +84,13 @@ public class A2AServerRoutes { @Internal Executor executor; + @Inject + Instance callContextFactory; + @Route(path = "/", methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { boolean streaming = false; + ServerCallContext context = createCallContext(rc); JSONRPCResponse nonStreamingResponse = null; Multi> streamingResponse = null; JSONRPCErrorResponse error = null; @@ -89,10 +99,10 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { if (isStreamingRequest(body)) { streaming = true; StreamingJSONRPCRequest request = Utils.OBJECT_MAPPER.readValue(body, StreamingJSONRPCRequest.class); - streamingResponse = processStreamingRequest(request); + streamingResponse = processStreamingRequest(request, context); } else { NonStreamingJSONRPCRequest request = Utils.OBJECT_MAPPER.readValue(body, NonStreamingJSONRPCRequest.class); - nonStreamingResponse = processNonStreamingRequest(request); + nonStreamingResponse = processNonStreamingRequest(request, context); } } catch (JsonProcessingException e) { error = handleError(e); @@ -183,32 +193,34 @@ public void getAuthenticatedExtendedAgentCard(RoutingExchange re) { } } - private JSONRPCResponse processNonStreamingRequest(NonStreamingJSONRPCRequest request) { - if (request instanceof GetTaskRequest) { - return jsonRpcHandler.onGetTask((GetTaskRequest) request); - } else if (request instanceof CancelTaskRequest) { - return jsonRpcHandler.onCancelTask((CancelTaskRequest) request); - } else if (request instanceof SetTaskPushNotificationConfigRequest) { - return jsonRpcHandler.setPushNotificationConfig((SetTaskPushNotificationConfigRequest) request); - } else if (request instanceof GetTaskPushNotificationConfigRequest) { - return jsonRpcHandler.getPushNotificationConfig((GetTaskPushNotificationConfigRequest) request); - } else if (request instanceof SendMessageRequest) { - return jsonRpcHandler.onMessageSend((SendMessageRequest) request); - } else if (request instanceof ListTaskPushNotificationConfigRequest) { - return jsonRpcHandler.listPushNotificationConfig((ListTaskPushNotificationConfigRequest) request); - } else if (request instanceof DeleteTaskPushNotificationConfigRequest) { - return jsonRpcHandler.deletePushNotificationConfig((DeleteTaskPushNotificationConfigRequest) request); + private JSONRPCResponse processNonStreamingRequest( + NonStreamingJSONRPCRequest request, ServerCallContext context) { + if (request instanceof GetTaskRequest req) { + return jsonRpcHandler.onGetTask(req, context); + } else if (request instanceof CancelTaskRequest req) { + return jsonRpcHandler.onCancelTask(req, context); + } else if (request instanceof SetTaskPushNotificationConfigRequest req) { + return jsonRpcHandler.setPushNotificationConfig(req, context); + } else if (request instanceof GetTaskPushNotificationConfigRequest req) { + return jsonRpcHandler.getPushNotificationConfig(req, context); + } else if (request instanceof SendMessageRequest req) { + return jsonRpcHandler.onMessageSend(req, context); + } else if (request instanceof ListTaskPushNotificationConfigRequest req) { + return jsonRpcHandler.listPushNotificationConfig(req, context); + } else if (request instanceof DeleteTaskPushNotificationConfigRequest req) { + return jsonRpcHandler.deletePushNotificationConfig(req, context); } else { return generateErrorResponse(request, new UnsupportedOperationError()); } } - private Multi> processStreamingRequest(JSONRPCRequest request) { + private Multi> processStreamingRequest( + JSONRPCRequest request, ServerCallContext context) { Flow.Publisher> publisher; - if (request instanceof SendStreamingMessageRequest) { - publisher = jsonRpcHandler.onMessageSendStream((SendStreamingMessageRequest) request); - } else if (request instanceof TaskResubscriptionRequest) { - publisher = jsonRpcHandler.onResubscribeToTask((TaskResubscriptionRequest) request); + if (request instanceof SendStreamingMessageRequest req) { + publisher = jsonRpcHandler.onMessageSendStream(req, context); + } else if (request instanceof TaskResubscriptionRequest req) { + publisher = jsonRpcHandler.onResubscribeToTask(req, context); } else { return Multi.createFrom().item(generateErrorResponse(request, new UnsupportedOperationError())); } @@ -234,6 +246,42 @@ static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { streamingMultiSseSupportSubscribedRunnable = runnable; } + private ServerCallContext createCallContext(RoutingContext rc) { + + if (callContextFactory.isUnsatisfied()) { + User user; + if (rc.user() == null) { + user = UnauthenticatedUser.INSTANCE; + } else { + user = new User() { + @Override + public boolean isAuthenticated() { + return rc.userContext().authenticated(); + } + + @Override + public String getUsername() { + return rc.user().subject(); + } + }; + } + Map state = new HashMap<>(); + // TODO Python's impl has + // state['auth'] = request.auth + // in jsonrpc_app.py. Figure out what this maps to in what Vert.X gives us + + Map headers = new HashMap<>(); + Set headerNames = rc.request().headers().names(); + headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); + state.put("headers", headers); + + return new ServerCallContext(user, state); + } else { + CallContextFactory builder = callContextFactory.get(); + return builder.build(rc); + } + } + // Port of import io.quarkus.vertx.web.runtime.MultiSseSupport, which is considered internal API private static class MultiSseSupport { diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java new file mode 100644 index 000000000..d40bc65f0 --- /dev/null +++ b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java @@ -0,0 +1,8 @@ +package io.a2a.server.apps.quarkus; + +import io.a2a.server.ServerCallContext; +import io.vertx.ext.web.RoutingContext; + +public interface CallContextFactory { + ServerCallContext build(RoutingContext rc); +} diff --git a/sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java b/sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java index 70fb344df..558f01eda 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java +++ b/sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java @@ -1,5 +1,26 @@ package io.a2a.server; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import io.a2a.server.auth.User; + public class ServerCallContext { - // TODO port the fields + // TODO Not totally sure yet about these field types + private final Map modelConfig = new ConcurrentHashMap<>(); + private final Map state; + private final User user; + + public ServerCallContext(User user, Map state) { + this.user = user; + this.state = state; + } + + public Map getState() { + return state; + } + + public User getUser() { + return user; + } } diff --git a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index bac7673a1..585b4fce4 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -22,13 +22,21 @@ public class RequestContext { private String contextId; private Task task; private List relatedTasks; - - public RequestContext(MessageSendParams params, String taskId, String contextId, Task task, List relatedTasks) throws InvalidParamsError { + private final ServerCallContext callContext; + + public RequestContext( + MessageSendParams params, + String taskId, + String contextId, + Task task, + List relatedTasks, + ServerCallContext callContext) throws InvalidParamsError { this.params = params; this.taskId = taskId; this.contextId = contextId; this.task = task; this.relatedTasks = relatedTasks == null ? new ArrayList<>() : relatedTasks; + this.callContext = callContext; // if the taskId and contextId were specified, they must match the params if (params != null) { @@ -73,6 +81,10 @@ public MessageSendConfiguration getConfiguration() { return params != null ? params.configuration() : null; } + public ServerCallContext getCallContext() { + return callContext; + } + public String getUserInput(String delimiter) { if (params == null) { return ""; @@ -187,7 +199,7 @@ public ServerCallContext getServerCallContext() { } public RequestContext build() { - return new RequestContext(params, taskId, contextId, task, relatedTasks); + return new RequestContext(params, taskId, contextId, task, relatedTasks, serverCallContext); } } diff --git a/sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java b/sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java index e4ec7a69c..9988ebbcf 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java +++ b/sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java @@ -1,6 +1,12 @@ package io.a2a.server.auth; public class UnauthenticatedUser implements User { + + public static UnauthenticatedUser INSTANCE = new UnauthenticatedUser(); + + private UnauthenticatedUser() { + } + @Override public boolean isAuthenticated() { return false; diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index b4dbb2feb..e79a97201 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -16,6 +16,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.agentexecution.SimpleRequestContextBuilder; @@ -87,7 +88,7 @@ public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, } @Override - public Task onGetTask(TaskQueryParams params) throws JSONRPCError { + public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws JSONRPCError { LOGGER.debug("onGetTask {}", params.id()); Task task = taskStore.get(params.id()); if (task == null) { @@ -114,7 +115,7 @@ public Task onGetTask(TaskQueryParams params) throws JSONRPCError { } @Override - public Task onCancelTask(TaskIdParams params) throws JSONRPCError { + public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws JSONRPCError { Task task = taskStore.get(params.id()); if (task == null) { throw new TaskNotFoundError(); @@ -136,6 +137,7 @@ public Task onCancelTask(TaskIdParams params) throws JSONRPCError { .setTaskId(task.getId()) .setContextId(task.getContextId()) .setTask(task) + .setServerCallContext(context) .build(), queue); @@ -152,9 +154,9 @@ public Task onCancelTask(TaskIdParams params) throws JSONRPCError { } @Override - public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { + public EventKind onMessageSend(MessageSendParams params, ServerCallContext context) throws JSONRPCError { LOGGER.debug("onMessageSend - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); - MessageSendSetup mss = initMessageSend(params); + MessageSendSetup mss = initMessageSend(params, context); String taskId = mss.requestContext.getTaskId(); LOGGER.debug("Request context taskId: {}", taskId); @@ -200,9 +202,10 @@ public EventKind onMessageSend(MessageSendParams params) throws JSONRPCError { } @Override - public Flow.Publisher onMessageSendStream(MessageSendParams params) throws JSONRPCError { + public Flow.Publisher onMessageSendStream( + MessageSendParams params, ServerCallContext context) throws JSONRPCError { LOGGER.debug("onMessageSendStream - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); - MessageSendSetup mss = initMessageSend(params); + MessageSendSetup mss = initMessageSend(params, context); AtomicReference taskId = new AtomicReference<>(mss.requestContext.getTaskId()); EventQueue queue = queueManager.createOrTap(taskId.get()); @@ -260,7 +263,8 @@ public Flow.Publisher onMessageSendStream(MessageSendParams } @Override - public TaskPushNotificationConfig onSetTaskPushNotificationConfig(TaskPushNotificationConfig params) throws JSONRPCError { + public TaskPushNotificationConfig onSetTaskPushNotificationConfig( + TaskPushNotificationConfig params, ServerCallContext context) throws JSONRPCError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } @@ -275,7 +279,8 @@ public TaskPushNotificationConfig onSetTaskPushNotificationConfig(TaskPushNotifi } @Override - public TaskPushNotificationConfig onGetTaskPushNotificationConfig(GetTaskPushNotificationConfigParams params) throws JSONRPCError { + public TaskPushNotificationConfig onGetTaskPushNotificationConfig( + GetTaskPushNotificationConfigParams params, ServerCallContext context) throws JSONRPCError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } @@ -305,7 +310,8 @@ private PushNotificationConfig getPushNotificationConfig(List onResubscribeToTask(TaskIdParams params) throws JSONRPCError { + public Flow.Publisher onResubscribeToTask( + TaskIdParams params, ServerCallContext context) throws JSONRPCError { Task task = taskStore.get(params.id()); if (task == null) { throw new TaskNotFoundError(); @@ -325,7 +331,8 @@ public Flow.Publisher onResubscribeToTask(TaskIdParams param } @Override - public List onListTaskPushNotificationConfig(ListTaskPushNotificationConfigParams params) throws JSONRPCError { + public List onListTaskPushNotificationConfig( + ListTaskPushNotificationConfigParams params, ServerCallContext context) throws JSONRPCError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } @@ -347,7 +354,8 @@ public List onListTaskPushNotificationConfig(ListTas } @Override - public void onDeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigParams params) { + public void onDeleteTaskPushNotificationConfig( + DeleteTaskPushNotificationConfigParams params, ServerCallContext context) { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } @@ -398,7 +406,7 @@ private void cleanupProducer(String taskId) { }); } - private MessageSendSetup initMessageSend(MessageSendParams params) { + private MessageSendSetup initMessageSend(MessageSendParams params, ServerCallContext context) { TaskManager taskManager = new TaskManager( params.message().getTaskId(), params.message().getContextId(), @@ -421,6 +429,7 @@ private MessageSendSetup initMessageSend(MessageSendParams params) { .setTaskId(task == null ? null : task.getId()) .setContextId(params.message().getContextId()) .setTask(task) + .setServerCallContext(context) .build(); return new MessageSendSetup(taskManager, task, requestContext); } diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java index 6a394e023..fb120f981 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java @@ -2,12 +2,14 @@ import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import java.util.List; import java.util.concurrent.Flow; import io.a2a.server.PublicAgentCard; +import io.a2a.server.ServerCallContext; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; @@ -52,9 +54,9 @@ public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, RequestHandler reque this.requestHandler = requestHandler; } - public SendMessageResponse onMessageSend(SendMessageRequest request) { + public SendMessageResponse onMessageSend(SendMessageRequest request, ServerCallContext context) { try { - EventKind taskOrMessage = requestHandler.onMessageSend(request.getParams()); + EventKind taskOrMessage = requestHandler.onMessageSend(request.getParams(), context); return new SendMessageResponse(request.getId(), taskOrMessage); } catch (JSONRPCError e) { return new SendMessageResponse(request.getId(), e); @@ -64,7 +66,8 @@ public SendMessageResponse onMessageSend(SendMessageRequest request) { } - public Flow.Publisher onMessageSendStream(SendStreamingMessageRequest request) { + public Flow.Publisher onMessageSendStream( + SendStreamingMessageRequest request, ServerCallContext context) { if (!agentCard.capabilities().streaming()) { return ZeroPublisher.fromItems( new SendStreamingMessageResponse( @@ -73,7 +76,8 @@ public Flow.Publisher onMessageSendStream(SendStre } try { - Flow.Publisher publisher = requestHandler.onMessageSendStream(request.getParams()); + Flow.Publisher publisher = + requestHandler.onMessageSendStream(request.getParams(), context); // We can't use the convertingProcessor convenience method since that propagates any errors as an error handled // via Subscriber.onError() rather than as part of the SendStreamingResponse payload return convertToSendStreamingMessageResponse(request.getId(), publisher); @@ -84,9 +88,9 @@ public Flow.Publisher onMessageSendStream(SendStre } } - public CancelTaskResponse onCancelTask(CancelTaskRequest request) { + public CancelTaskResponse onCancelTask(CancelTaskRequest request, ServerCallContext context) { try { - Task task = requestHandler.onCancelTask(request.getParams()); + Task task = requestHandler.onCancelTask(request.getParams(), context); if (task != null) { return new CancelTaskResponse(request.getId(), task); } @@ -98,7 +102,8 @@ public CancelTaskResponse onCancelTask(CancelTaskRequest request) { } } - public Flow.Publisher onResubscribeToTask(TaskResubscriptionRequest request) { + public Flow.Publisher onResubscribeToTask( + TaskResubscriptionRequest request, ServerCallContext context) { if (!agentCard.capabilities().streaming()) { return ZeroPublisher.fromItems( new SendStreamingMessageResponse( @@ -107,7 +112,8 @@ public Flow.Publisher onResubscribeToTask(TaskResu } try { - Flow.Publisher publisher = requestHandler.onResubscribeToTask(request.getParams()); + Flow.Publisher publisher = + requestHandler.onResubscribeToTask(request.getParams(), context); // We can't use the convertingProcessor convenience method since that propagates any errors as an error handled // via Subscriber.onError() rather than as part of the SendStreamingResponse payload return convertToSendStreamingMessageResponse(request.getId(), publisher); @@ -118,13 +124,15 @@ public Flow.Publisher onResubscribeToTask(TaskResu } } - public GetTaskPushNotificationConfigResponse getPushNotificationConfig(GetTaskPushNotificationConfigRequest request) { + public GetTaskPushNotificationConfigResponse getPushNotificationConfig( + GetTaskPushNotificationConfigRequest request, ServerCallContext context) { if (!agentCard.capabilities().pushNotifications()) { return new GetTaskPushNotificationConfigResponse(request.getId(), new PushNotificationNotSupportedError()); } try { - TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(request.getParams()); + TaskPushNotificationConfig config = + requestHandler.onGetTaskPushNotificationConfig(request.getParams(), context); return new GetTaskPushNotificationConfigResponse(request.getId(), config); } catch (JSONRPCError e) { return new GetTaskPushNotificationConfigResponse(request.getId().toString(), e); @@ -133,13 +141,15 @@ public GetTaskPushNotificationConfigResponse getPushNotificationConfig(GetTaskPu } } - public SetTaskPushNotificationConfigResponse setPushNotificationConfig(SetTaskPushNotificationConfigRequest request) { + public SetTaskPushNotificationConfigResponse setPushNotificationConfig( + SetTaskPushNotificationConfigRequest request, ServerCallContext context) { if (!agentCard.capabilities().pushNotifications()) { return new SetTaskPushNotificationConfigResponse(request.getId(), new PushNotificationNotSupportedError()); } try { - TaskPushNotificationConfig config = requestHandler.onSetTaskPushNotificationConfig(request.getParams()); + TaskPushNotificationConfig config = + requestHandler.onSetTaskPushNotificationConfig(request.getParams(), context); return new SetTaskPushNotificationConfigResponse(request.getId().toString(), config); } catch (JSONRPCError e) { return new SetTaskPushNotificationConfigResponse(request.getId(), e); @@ -148,9 +158,9 @@ public SetTaskPushNotificationConfigResponse setPushNotificationConfig(SetTaskPu } } - public GetTaskResponse onGetTask(GetTaskRequest request) { + public GetTaskResponse onGetTask(GetTaskRequest request, ServerCallContext context) { try { - Task task = requestHandler.onGetTask(request.getParams()); + Task task = requestHandler.onGetTask(request.getParams(), context); return new GetTaskResponse(request.getId(), task); } catch (JSONRPCError e) { return new GetTaskResponse(request.getId(), e); @@ -159,13 +169,15 @@ public GetTaskResponse onGetTask(GetTaskRequest request) { } } - public ListTaskPushNotificationConfigResponse listPushNotificationConfig(ListTaskPushNotificationConfigRequest request) { + public ListTaskPushNotificationConfigResponse listPushNotificationConfig( + ListTaskPushNotificationConfigRequest request, ServerCallContext context) { if ( !agentCard.capabilities().pushNotifications()) { return new ListTaskPushNotificationConfigResponse(request.getId(), new PushNotificationNotSupportedError()); } try { - List pushNotificationConfigList = requestHandler.onListTaskPushNotificationConfig(request.getParams()); + List pushNotificationConfigList = + requestHandler.onListTaskPushNotificationConfig(request.getParams(), context); return new ListTaskPushNotificationConfigResponse(request.getId(), pushNotificationConfigList); } catch (JSONRPCError e) { return new ListTaskPushNotificationConfigResponse(request.getId(), e); @@ -174,13 +186,14 @@ public ListTaskPushNotificationConfigResponse listPushNotificationConfig(ListTas } } - public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig(DeleteTaskPushNotificationConfigRequest request) { + public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig( + DeleteTaskPushNotificationConfigRequest request, ServerCallContext context) { if ( !agentCard.capabilities().pushNotifications()) { return new DeleteTaskPushNotificationConfigResponse(request.getId(), new PushNotificationNotSupportedError()); } try { - requestHandler.onDeleteTaskPushNotificationConfig(request.getParams()); + requestHandler.onDeleteTaskPushNotificationConfig(request.getParams(), context); return new DeleteTaskPushNotificationConfigResponse(request.getId()); } catch (JSONRPCError e) { return new DeleteTaskPushNotificationConfigResponse(request.getId(), e); diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index 66d07b55b..e45bc3c62 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.concurrent.Flow; +import io.a2a.server.ServerCallContext; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; @@ -16,21 +17,39 @@ import io.a2a.spec.TaskQueryParams; public interface RequestHandler { - Task onGetTask(TaskQueryParams params) throws JSONRPCError; + Task onGetTask( + TaskQueryParams params, + ServerCallContext context) throws JSONRPCError; - Task onCancelTask(TaskIdParams params) throws JSONRPCError; + Task onCancelTask( + TaskIdParams params, + ServerCallContext context) throws JSONRPCError; - EventKind onMessageSend(MessageSendParams params) throws JSONRPCError; + EventKind onMessageSend( + MessageSendParams params, + ServerCallContext context) throws JSONRPCError; - Flow.Publisher onMessageSendStream(MessageSendParams params) throws JSONRPCError; + Flow.Publisher onMessageSendStream( + MessageSendParams params, + ServerCallContext context) throws JSONRPCError; - TaskPushNotificationConfig onSetTaskPushNotificationConfig(TaskPushNotificationConfig params) throws JSONRPCError; + TaskPushNotificationConfig onSetTaskPushNotificationConfig( + TaskPushNotificationConfig params, + ServerCallContext context) throws JSONRPCError; - TaskPushNotificationConfig onGetTaskPushNotificationConfig(GetTaskPushNotificationConfigParams params) throws JSONRPCError; + TaskPushNotificationConfig onGetTaskPushNotificationConfig( + GetTaskPushNotificationConfigParams params, + ServerCallContext context) throws JSONRPCError; - Flow.Publisher onResubscribeToTask(TaskIdParams params) throws JSONRPCError; + Flow.Publisher onResubscribeToTask( + TaskIdParams params, + ServerCallContext context) throws JSONRPCError; - List onListTaskPushNotificationConfig(ListTaskPushNotificationConfigParams params) throws JSONRPCError; + List onListTaskPushNotificationConfig( + ListTaskPushNotificationConfigParams params, + ServerCallContext context) throws JSONRPCError; - void onDeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigParams params) throws JSONRPCError; + void onDeleteTaskPushNotificationConfig( + DeleteTaskPushNotificationConfigParams params, + ServerCallContext context) throws JSONRPCError; } diff --git a/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index c9bb79061..081cc873a 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -25,7 +25,7 @@ public class RequestContextTest { @Test public void testInitWithoutParams() { - RequestContext context = new RequestContext(null, null, null, null, null); + RequestContext context = new RequestContext(null, null, null, null, null, null); assertNull(context.getMessage()); assertNull(context.getTaskId()); assertNull(context.getContextId()); @@ -46,7 +46,7 @@ public void testInitWithParamsNoIds() { .thenReturn(taskId) .thenReturn(contextId); - RequestContext context = new RequestContext(mockParams, null, null, null, null); + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(mockParams.message(), context.getMessage()); assertEquals(taskId.toString(), context.getTaskId()); @@ -62,7 +62,7 @@ public void testInitWithTaskId() { var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, taskId, null, null, null); + RequestContext context = new RequestContext(mockParams, taskId, null, null, null, null); assertEquals(taskId, context.getTaskId()); assertEquals(taskId, mockParams.message().getTaskId()); @@ -73,7 +73,7 @@ public void testInitWithContextId() { String contextId = "context-456"; var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, null, contextId, null, null); + RequestContext context = new RequestContext(mockParams, null, contextId, null, null, null); assertEquals(contextId, context.getContextId()); assertEquals(contextId, mockParams.message().getContextId()); @@ -85,7 +85,7 @@ public void testInitWithBothIds() { String contextId = "context-456"; var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null); + RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null, null); assertEquals(taskId, context.getTaskId()); assertEquals(taskId, mockParams.message().getTaskId()); @@ -99,14 +99,14 @@ public void testInitWithTask() { var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, null, null, mockTask, null); + RequestContext context = new RequestContext(mockParams, null, null, mockTask, null, null); assertEquals(mockTask, context.getTask()); } @Test public void testGetUserInputNoParams() { - RequestContext context = new RequestContext(null, null, null, null, null); + RequestContext context = new RequestContext(null, null, null, null, null, null); assertEquals("", context.getUserInput(null)); } @@ -114,7 +114,7 @@ public void testGetUserInputNoParams() { public void testAttachRelatedTask() { var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); - RequestContext context = new RequestContext(null, null, null, null, null); + RequestContext context = new RequestContext(null, null, null, null, null, null); assertEquals(0, context.getRelatedTasks().size()); context.attachRelatedTask(mockTask); @@ -133,7 +133,7 @@ public void testCheckOrGenerateTaskIdWithExistingTaskId() { var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, null, null, null, null); + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(existingId, context.getTaskId()); assertEquals(existingId, mockParams.message().getTaskId()); @@ -146,7 +146,7 @@ public void testCheckOrGenerateContextIdWithExistingContextId() { var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, null, null, null, null); + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(existingId, context.getContextId()); assertEquals(existingId, mockParams.message().getContextId()); @@ -159,7 +159,7 @@ public void testInitRaisesErrorOnTaskIdMismatch() { var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> - new RequestContext(mockParams, "wrong-task-id", null, mockTask, null)); + new RequestContext(mockParams, "wrong-task-id", null, mockTask, null, null)); assertTrue(error.getMessage().contains("bad task id")); } @@ -171,7 +171,7 @@ public void testInitRaisesErrorOnContextIdMismatch() { var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> - new RequestContext(mockParams, mockTask.getId(), "wrong-context-id", mockTask, null)); + new RequestContext(mockParams, mockTask.getId(), "wrong-context-id", mockTask, null, null)); assertTrue(error.getMessage().contains("bad context id")); } @@ -184,7 +184,7 @@ public void testWithRelatedTasksProvided() { relatedTasks.add(mockTask); relatedTasks.add(mock(Task.class)); - RequestContext context = new RequestContext(null, null, null, null, relatedTasks); + RequestContext context = new RequestContext(null, null, null, null, relatedTasks, null); assertEquals(relatedTasks, context.getRelatedTasks()); assertEquals(2, context.getRelatedTasks().size()); @@ -192,7 +192,7 @@ public void testWithRelatedTasksProvided() { @Test public void testMessagePropertyWithoutParams() { - RequestContext context = new RequestContext(null, null, null, null, null); + RequestContext context = new RequestContext(null, null, null, null, null, null); assertNull(context.getMessage()); } @@ -201,7 +201,7 @@ public void testMessagePropertyWithParams() { var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, null, null, null, null); + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(mockParams.message(), context.getMessage()); } @@ -214,7 +214,7 @@ public void testInitWithExistingIdsInMessage() { .taskId(existingTaskId).contextId(existingContextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - RequestContext context = new RequestContext(mockParams, null, null, null, null); + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(existingTaskId, context.getTaskId()); assertEquals(existingContextId, context.getContextId()); @@ -227,7 +227,7 @@ public void testInitWithTaskIdAndExistingTaskIdMatch() { var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); - RequestContext context = new RequestContext(mockParams, mockTask.getId(), null, mockTask, null); + RequestContext context = new RequestContext(mockParams, mockTask.getId(), null, mockTask, null, null); assertEquals(mockTask.getId(), context.getTaskId()); assertEquals(mockTask, context.getTask()); @@ -240,7 +240,7 @@ public void testInitWithContextIdAndExistingContextIdMatch() { var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); - RequestContext context = new RequestContext(mockParams, mockTask.getId(), mockTask.getContextId(), mockTask, null); + RequestContext context = new RequestContext(mockParams, mockTask.getId(), mockTask.getContextId(), mockTask, null, null); assertEquals(mockTask.getContextId(), context.getContextId()); assertEquals(mockTask, context.getTask()); diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index 80eaacf9e..f67fa87cf 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -7,12 +7,12 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import jakarta.enterprise.context.Dependent; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -22,10 +22,14 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import jakarta.enterprise.context.Dependent; + import io.a2a.http.A2AHttpClient; import io.a2a.http.A2AHttpResponse; +import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; import io.a2a.server.events.InMemoryQueueManager; @@ -83,7 +87,6 @@ import io.a2a.util.Utils; import io.quarkus.arc.profile.IfBuildProfile; import mutiny.zero.ZeroPublisher; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -117,6 +120,8 @@ public class JSONRPCHandlerTest { private final Executor internalExecutor = Executors.newCachedThreadPool(); + private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar")); + @BeforeEach public void init() { @@ -156,7 +161,7 @@ public void testOnGetTaskSuccess() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); taskStore.save(MINIMAL_TASK); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); - GetTaskResponse response = handler.onGetTask(request); + GetTaskResponse response = handler.onGetTask(request, callContext); assertEquals(request.getId(), response.getId()); assertSame(MINIMAL_TASK, response.getResult()); assertNull(response.getError()); @@ -166,7 +171,7 @@ public void testOnGetTaskSuccess() throws Exception { public void testOnGetTaskNotFound() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); - GetTaskResponse response = handler.onGetTask(request); + GetTaskResponse response = handler.onGetTask(request, callContext); assertEquals(request.getId(), response.getId()); assertInstanceOf(TaskNotFoundError.class, response.getError()); assertNull(response.getResult()); @@ -187,7 +192,7 @@ public void testOnCancelTaskSuccess() throws Exception { }; CancelTaskRequest request = new CancelTaskRequest("111", new TaskIdParams(MINIMAL_TASK.getId())); - CancelTaskResponse response = handler.onCancelTask(request); + CancelTaskResponse response = handler.onCancelTask(request, callContext); assertNull(response.getError()); assertEquals(request.getId(), response.getId()); @@ -207,7 +212,7 @@ public void testOnCancelTaskNotSupported() { }; CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - CancelTaskResponse response = handler.onCancelTask(request); + CancelTaskResponse response = handler.onCancelTask(request, callContext); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); assertInstanceOf(UnsupportedOperationError.class, response.getError()); @@ -217,7 +222,7 @@ public void testOnCancelTaskNotSupported() { public void testOnCancelTaskNotFound() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - CancelTaskResponse response = handler.onCancelTask(request); + CancelTaskResponse response = handler.onCancelTask(request, callContext); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); assertInstanceOf(TaskNotFoundError.class, response.getError()); @@ -234,7 +239,7 @@ public void testOnMessageNewMessageSuccess() { .contextId(MINIMAL_TASK.getContextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response = handler.onMessageSend(request); + SendMessageResponse response = handler.onMessageSend(request, callContext); assertNull(response.getError()); // The Python implementation returns a Task here, but then again they are using hardcoded mocks and // bypassing the whole EventQueue. @@ -259,7 +264,7 @@ public void testOnMessageNewMessageSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ - response = handler.onMessageSend(request); + response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); assertSame(MINIMAL_TASK, response.getResult()); @@ -277,7 +282,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() { .contextId(MINIMAL_TASK.getContextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response = handler.onMessageSend(request); + SendMessageResponse response = handler.onMessageSend(request, callContext); assertNull(response.getError()); // The Python implementation returns a Task here, but then again they are using hardcoded mocks and // bypassing the whole EventQueue. @@ -303,7 +308,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ - response = handler.onMessageSend(request); + response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); assertSame(MINIMAL_TASK, response.getResult()); @@ -324,7 +329,7 @@ public void testOnMessageError() { .build(); SendMessageRequest request = new SendMessageRequest( "1", new MessageSendParams(message, null, null)); - SendMessageResponse response = handler.onMessageSend(request); + SendMessageResponse response = handler.onMessageSend(request, callContext); assertInstanceOf(UnsupportedOperationError.class, response.getError()); assertNull(response.getResult()); } @@ -343,7 +348,7 @@ public void testOnMessageErrorMocks() { EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromItems(new UnsupportedOperationError())).when(mock).consumeAll();})){ - response = handler.onMessageSend(request); + response = handler.onMessageSend(request, callContext); } assertInstanceOf(UnsupportedOperationError.class, response.getError()); @@ -364,7 +369,7 @@ public void testOnMessageStreamNewMessageSuccess() { SendStreamingMessageRequest request = new SendStreamingMessageRequest( "1", new MessageSendParams(message, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); List results = new ArrayList<>(); CountDownLatch latch = new CountDownLatch(1); @@ -438,7 +443,7 @@ public void testOnMessageStreamNewMessageSuccessMocks() { EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ - response = handler.onMessageSendStream(request); + response = handler.onMessageSendStream(request, callContext); } List results = new ArrayList<>(); @@ -492,7 +497,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception SendStreamingMessageRequest request = new SendStreamingMessageRequest( "1", new MessageSendParams(message, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); // This Publisher never completes so we subscribe in a new thread. // I _think_ that is as expected, and testOnMessageStreamNewMessageSendPushNotificationSuccess seems @@ -583,7 +588,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ - response = handler.onMessageSendStream(request); + response = handler.onMessageSendStream(request, callContext); } List results = new ArrayList<>(); @@ -630,7 +635,7 @@ public void testSetPushNotificationConfigSuccess() { new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request); + SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); assertSame(taskPushConfig, response.getResult()); } @@ -648,11 +653,11 @@ public void testGetPushNotificationConfigSuccess() { MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotificationConfig(request); + handler.setPushNotificationConfig(request, callContext); GetTaskPushNotificationConfigRequest getRequest = new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest); + GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest, callContext); TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id(MINIMAL_TASK.getId()).url("http://example.com").build()); @@ -693,14 +698,14 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); - SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest); + SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); assertNull(stpnResponse.getError()); Message msg = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(msg, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); final List results = Collections.synchronizedList(new ArrayList<>()); final AtomicReference subscriptionRef = new AtomicReference<>(); @@ -777,7 +782,7 @@ public void testOnResubscribeExistingTaskSuccess() { }; TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - Flow.Publisher response = handler.onResubscribeToTask(request); + Flow.Publisher response = handler.onResubscribeToTask(request, callContext); // We need to send some events in order for those to end up in the queue Message message = new Message.Builder() @@ -787,7 +792,9 @@ public void testOnResubscribeExistingTaskSuccess() { .parts(new TextPart("text")) .build(); SendMessageResponse smr = - handler.onMessageSend(new SendMessageRequest("1", new MessageSendParams(message, null, null))); + handler.onMessageSend( + new SendMessageRequest("1", new MessageSendParams(message, null, null)), + callContext); assertNull(smr.getError()); @@ -853,7 +860,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ - response = handler.onResubscribeToTask(request); + response = handler.onResubscribeToTask(request, callContext); } List results = new ArrayList<>(); @@ -898,7 +905,7 @@ public void testOnResubscribeNoExistingTaskError() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - Flow.Publisher response = handler.onResubscribeToTask(request); + Flow.Publisher response = handler.onResubscribeToTask(request, callContext); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -946,7 +953,7 @@ public void testStreamingNotSupportedError() { .message(MESSAGE) .build()) .build(); - Flow.Publisher response = handler.onMessageSendStream(request); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -992,7 +999,7 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() { JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - Flow.Publisher response = handler.onResubscribeToTask(request); + Flow.Publisher response = handler.onResubscribeToTask(request, callContext); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -1048,7 +1055,7 @@ public void testPushNotificationsNotSupportedError() { SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest.Builder() .params(config) .build(); - SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request); + SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); } @@ -1064,7 +1071,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { GetTaskPushNotificationConfigRequest request = new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request); + GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request, callContext); assertNotNull(response.getError()); assertInstanceOf(UnsupportedOperationError.class, response.getError()); @@ -1091,7 +1098,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest.Builder() .params(config) .build(); - SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request); + SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); assertInstanceOf(UnsupportedOperationError.class, response.getError()); assertEquals("This operation is not supported", response.getError().getMessage()); @@ -1100,12 +1107,13 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { @Test public void testOnMessageSendInternalError() { DefaultRequestHandler mocked = Mockito.mock(DefaultRequestHandler.class); - Mockito.doThrow(new InternalError("Internal Error")).when(mocked).onMessageSend(Mockito.any(MessageSendParams.class)); + Mockito.doThrow(new InternalError("Internal Error")).when(mocked) + .onMessageSend(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); JSONRPCHandler handler = new JSONRPCHandler(CARD, mocked); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); - SendMessageResponse response = handler.onMessageSend(request); + SendMessageResponse response = handler.onMessageSend(request, callContext); assertInstanceOf(InternalError.class, response.getError()); } @@ -1113,12 +1121,13 @@ public void testOnMessageSendInternalError() { @Test public void testOnMessageStreamInternalError() { DefaultRequestHandler mocked = Mockito.mock(DefaultRequestHandler.class); - Mockito.doThrow(new InternalError("Internal Error")).when(mocked).onMessageSendStream(Mockito.any(MessageSendParams.class)); + Mockito.doThrow(new InternalError("Internal Error")).when(mocked) + .onMessageSendStream(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); JSONRPCHandler handler = new JSONRPCHandler(CARD, mocked); SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); List results = new ArrayList<>(); @@ -1184,7 +1193,7 @@ public void testOnMessageSendErrorHandling() { Mockito.doThrow( new UnsupportedOperationError()) .when(mock).consumeAndBreakOnInterrupt(Mockito.any(EventConsumer.class)))){ - response = handler.onMessageSend(request); + response = handler.onMessageSend(request, callContext); } assertInstanceOf(UnsupportedOperationError.class, response.getError()); @@ -1201,7 +1210,7 @@ public void testOnMessageSendTaskIdMismatch() { }); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); - SendMessageResponse response = handler.onMessageSend(request); + SendMessageResponse response = handler.onMessageSend(request, callContext); assertInstanceOf(InternalError.class, response.getError()); } @@ -1216,7 +1225,7 @@ public void testOnMessageStreamTaskIdMismatch() { }); SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -1267,11 +1276,11 @@ public void testListPushNotificationConfig() { .id(MINIMAL_TASK.getId()) .build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotificationConfig(request); + handler.setPushNotificationConfig(request, callContext); ListTaskPushNotificationConfigRequest listRequest = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertEquals(1, listResponse.getResult().size()); @@ -1294,11 +1303,12 @@ public void testListPushNotificationConfigNotSupported() { .id(MINIMAL_TASK.getId()) .build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotificationConfig(request); + handler.setPushNotificationConfig(request, callContext); ListTaskPushNotificationConfigRequest listRequest = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + ListTaskPushNotificationConfigResponse listResponse = + handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertNull(listResponse.getResult()); @@ -1317,7 +1327,8 @@ public void testListPushNotificationConfigNoPushConfigStore() { ListTaskPushNotificationConfigRequest listRequest = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + ListTaskPushNotificationConfigResponse listResponse = + handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertNull(listResponse.getResult()); @@ -1333,7 +1344,8 @@ public void testListPushNotificationConfigTaskNotFound() { ListTaskPushNotificationConfigRequest listRequest = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest); + ListTaskPushNotificationConfigResponse listResponse = + handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertNull(listResponse.getResult()); @@ -1355,11 +1367,12 @@ public void testDeletePushNotificationConfig() { .id(MINIMAL_TASK.getId()) .build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotificationConfig(request); + handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); - DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest); + DeleteTaskPushNotificationConfigResponse deleteResponse = + handler.deletePushNotificationConfig(deleteRequest, callContext); assertEquals("111", deleteResponse.getId()); assertNull(deleteResponse.getError()); @@ -1382,11 +1395,12 @@ public void testDeletePushNotificationConfigNotSupported() { .id(MINIMAL_TASK.getId()) .build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotificationConfig(request); + handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); - DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest); + DeleteTaskPushNotificationConfigResponse deleteResponse = + handler.deletePushNotificationConfig(deleteRequest, callContext); assertEquals("111", deleteResponse.getId()); assertNull(deleteResponse.getResult()); @@ -1410,11 +1424,12 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { .id(MINIMAL_TASK.getId()) .build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - handler.setPushNotificationConfig(request); + handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); - DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest); + DeleteTaskPushNotificationConfigResponse deleteResponse = + handler.deletePushNotificationConfig(deleteRequest, callContext); assertEquals("111", deleteResponse.getId()); assertNull(deleteResponse.getResult()); From 76cc03aeeb8214b837867d7dd6eb943ddd256a1a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 29 Jul 2025 09:21:50 +0100 Subject: [PATCH 054/493] chore: add logging to TaskManager (#205) # Description I noticed while reviewing #181 and looking at the Python implementation that we don't have logging in the TaskManager. We decided on the logging strategy after the initial code was written. This PR addresses this omission. --- .../main/java/io/a2a/server/tasks/TaskManager.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 089dc7abe..cebddf85a 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -16,8 +16,13 @@ import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class TaskManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskManager.class); + private volatile String taskId; private volatile String contextId; private final TaskStore taskStore; @@ -101,16 +106,18 @@ Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { // This represents the first chunk for this artifact index if (existingArtifactIndex >= 0) { // Replace the existing artifact entirely with the new artifact + LOGGER.debug("Replacing artifact at id {} for task {}", artifactId, taskId); artifacts.set(existingArtifactIndex, newArtifact); } else { // Append the new artifact since no artifact with this id/index exists yet + LOGGER.debug("Adding artifact at id {} for task {}", artifactId, taskId); artifacts.add(newArtifact); } } else if (existingArtifact != null) { // Append new parts to the existing artifact's parts list // Do this to a copy - + LOGGER.debug("Appending parts to artifact id {} for task {}", artifactId, taskId); List> parts = new ArrayList<>(existingArtifact.parts()); parts.addAll(newArtifact.parts()); Artifact updated = new Artifact.Builder(existingArtifact) @@ -120,6 +127,9 @@ Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { } else { // We received a chunk to append, but we don't have an existing artifact. // We will ignore this chunk + LOGGER.warn( + "Received append=true for nonexistent artifact index for artifact {} in task {}. Ignoring chunk.", + artifactId, taskId); } task = new Task.Builder(task) From 4b11c0013fa33fbf5b4b1cb8f3e7c7baea4d0297 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 4 Aug 2025 10:38:00 -0400 Subject: [PATCH 055/493] feat: gRPC transport implementation (#211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains: * Utility classes for converting from spec classes and gRPC classes and vice versa * An initial gRPC client implementation * An initial gRPC request handler implementation * Tests for the gRPC request handler implementation * gRPC server integration - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes https://github.com/a2aproject/a2a-java/issues/210 🦕 --------- Co-authored-by: Kabir Khan --- client/pom.xml | 6 +- .../java/io/a2a/client/A2AGrpcClient.java | 200 + .../io/a2a/client/sse/SSEStreamObserver.java | 57 + grpc/pom.xml | 39 + grpc/src/main/java/io/a2a/grpc/A2A.java | 836 ++++ .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 1327 +++++ .../io/a2a/grpc/APIKeySecurityScheme.java | 858 ++++ .../grpc/APIKeySecuritySchemeOrBuilder.java | 72 + .../java/io/a2a/grpc/AgentCapabilities.java | 986 ++++ .../a2a/grpc/AgentCapabilitiesOrBuilder.java | 76 + grpc/src/main/java/io/a2a/grpc/AgentCard.java | 4410 +++++++++++++++++ .../java/io/a2a/grpc/AgentCardOrBuilder.java | 522 ++ .../main/java/io/a2a/grpc/AgentExtension.java | 1044 ++++ .../io/a2a/grpc/AgentExtensionOrBuilder.java | 94 + .../main/java/io/a2a/grpc/AgentInterface.java | 716 +++ .../io/a2a/grpc/AgentInterfaceOrBuilder.java | 56 + .../main/java/io/a2a/grpc/AgentProvider.java | 716 +++ .../io/a2a/grpc/AgentProviderOrBuilder.java | 56 + .../src/main/java/io/a2a/grpc/AgentSkill.java | 1897 +++++++ .../java/io/a2a/grpc/AgentSkillOrBuilder.java | 254 + grpc/src/main/java/io/a2a/grpc/Artifact.java | 1799 +++++++ .../java/io/a2a/grpc/ArtifactOrBuilder.java | 184 + .../java/io/a2a/grpc/AuthenticationInfo.java | 779 +++ .../a2a/grpc/AuthenticationInfoOrBuilder.java | 73 + .../a2a/grpc/AuthorizationCodeOAuthFlow.java | 1213 +++++ .../AuthorizationCodeOAuthFlowOrBuilder.java | 137 + .../java/io/a2a/grpc/CancelTaskRequest.java | 530 ++ .../a2a/grpc/CancelTaskRequestOrBuilder.java | 32 + .../a2a/grpc/ClientCredentialsOAuthFlow.java | 1042 ++++ .../ClientCredentialsOAuthFlowOrBuilder.java | 115 + ...eateTaskPushNotificationConfigRequest.java | 866 ++++ ...ushNotificationConfigRequestOrBuilder.java | 61 + grpc/src/main/java/io/a2a/grpc/DataPart.java | 567 +++ .../java/io/a2a/grpc/DataPartOrBuilder.java | 27 + ...leteTaskPushNotificationConfigRequest.java | 530 ++ ...ushNotificationConfigRequestOrBuilder.java | 32 + grpc/src/main/java/io/a2a/grpc/FilePart.java | 855 ++++ .../java/io/a2a/grpc/FilePartOrBuilder.java | 54 + .../java/io/a2a/grpc/GetAgentCardRequest.java | 367 ++ .../grpc/GetAgentCardRequestOrBuilder.java | 12 + .../GetTaskPushNotificationConfigRequest.java | 530 ++ ...ushNotificationConfigRequestOrBuilder.java | 32 + .../main/java/io/a2a/grpc/GetTaskRequest.java | 596 +++ .../io/a2a/grpc/GetTaskRequestOrBuilder.java | 38 + .../io/a2a/grpc/HTTPAuthSecurityScheme.java | 893 ++++ .../grpc/HTTPAuthSecuritySchemeOrBuilder.java | 82 + .../java/io/a2a/grpc/ImplicitOAuthFlow.java | 1042 ++++ .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java | 115 + ...ListTaskPushNotificationConfigRequest.java | 819 +++ ...ushNotificationConfigRequestOrBuilder.java | 74 + ...istTaskPushNotificationConfigResponse.java | 891 ++++ ...shNotificationConfigResponseOrBuilder.java | 58 + grpc/src/main/java/io/a2a/grpc/Message.java | 1983 ++++++++ .../java/io/a2a/grpc/MessageOrBuilder.java | 217 + .../io/a2a/grpc/OAuth2SecurityScheme.java | 771 +++ .../grpc/OAuth2SecuritySchemeOrBuilder.java | 59 + .../src/main/java/io/a2a/grpc/OAuthFlows.java | 1273 +++++ .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java | 74 + .../a2a/grpc/OpenIdConnectSecurityScheme.java | 701 +++ .../OpenIdConnectSecuritySchemeOrBuilder.java | 54 + grpc/src/main/java/io/a2a/grpc/Part.java | 1042 ++++ .../main/java/io/a2a/grpc/PartOrBuilder.java | 61 + .../java/io/a2a/grpc/PasswordOAuthFlow.java | 1042 ++++ .../a2a/grpc/PasswordOAuthFlowOrBuilder.java | 115 + .../io/a2a/grpc/PushNotificationConfig.java | 1107 +++++ .../grpc/PushNotificationConfigOrBuilder.java | 99 + grpc/src/main/java/io/a2a/grpc/Role.java | 150 + grpc/src/main/java/io/a2a/grpc/Security.java | 672 +++ .../java/io/a2a/grpc/SecurityOrBuilder.java | 46 + .../main/java/io/a2a/grpc/SecurityScheme.java | 1273 +++++ .../io/a2a/grpc/SecuritySchemeOrBuilder.java | 74 + .../io/a2a/grpc/SendMessageConfiguration.java | 1037 ++++ .../SendMessageConfigurationOrBuilder.java | 104 + .../java/io/a2a/grpc/SendMessageRequest.java | 937 ++++ .../a2a/grpc/SendMessageRequestOrBuilder.java | 57 + .../java/io/a2a/grpc/SendMessageResponse.java | 865 ++++ .../grpc/SendMessageResponseOrBuilder.java | 44 + .../main/java/io/a2a/grpc/StreamResponse.java | 1297 +++++ .../io/a2a/grpc/StreamResponseOrBuilder.java | 74 + .../src/main/java/io/a2a/grpc/StringList.java | 563 +++ .../java/io/a2a/grpc/StringListOrBuilder.java | 37 + grpc/src/main/java/io/a2a/grpc/Task.java | 2114 ++++++++ .../io/a2a/grpc/TaskArtifactUpdateEvent.java | 1344 +++++ .../TaskArtifactUpdateEventOrBuilder.java | 126 + .../main/java/io/a2a/grpc/TaskOrBuilder.java | 204 + .../a2a/grpc/TaskPushNotificationConfig.java | 723 +++ .../TaskPushNotificationConfigOrBuilder.java | 47 + grpc/src/main/java/io/a2a/grpc/TaskState.java | 268 + .../src/main/java/io/a2a/grpc/TaskStatus.java | 980 ++++ .../java/io/a2a/grpc/TaskStatusOrBuilder.java | 88 + .../io/a2a/grpc/TaskStatusUpdateEvent.java | 1261 +++++ .../grpc/TaskStatusUpdateEventOrBuilder.java | 116 + .../io/a2a/grpc/TaskSubscriptionRequest.java | 530 ++ .../TaskSubscriptionRequestOrBuilder.java | 32 + .../java/io/a2a/grpc/utils/ProtoUtils.java | 871 ++++ grpc/src/main/resources/META-INF/beans.xml | 6 + pom.xml | 29 +- reference-grpc/pom.xml | 79 + .../grpc/quarkus/QuarkusGrpcHandler.java | 27 + .../src/main/resources/application.properties | 5 + .../server/grpc/quarkus/A2ATestResource.java | 132 + .../grpc/quarkus/QuarkusA2AGrpcTest.java | 1245 +++++ sdk-server-common/pom.xml | 10 + .../requesthandlers/CallContextFactory.java | 8 + .../server/requesthandlers/GrpcHandler.java | 394 ++ .../AbstractA2ARequestHandlerTest.java | 188 + .../requesthandlers/GrpcHandlerTest.java | 806 +++ .../requesthandlers/JSONRPCHandlerTest.java | 172 +- .../src/test/resources/META-INF/beans.xml | 3 +- 109 files changed, 56119 insertions(+), 184 deletions(-) create mode 100644 client/src/main/java/io/a2a/client/A2AGrpcClient.java create mode 100644 client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java create mode 100644 grpc/pom.xml create mode 100644 grpc/src/main/java/io/a2a/grpc/A2A.java create mode 100644 grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java create mode 100644 grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java create mode 100644 grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentCard.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentExtension.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentInterface.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentProvider.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentSkill.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/Artifact.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java create mode 100644 grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/DataPart.java create mode 100644 grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/FilePart.java create mode 100644 grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java create mode 100644 grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java create mode 100644 grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/Message.java create mode 100644 grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java create mode 100644 grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/OAuthFlows.java create mode 100644 grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java create mode 100644 grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/Part.java create mode 100644 grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java create mode 100644 grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java create mode 100644 grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/Role.java create mode 100644 grpc/src/main/java/io/a2a/grpc/Security.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SecurityScheme.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java create mode 100644 grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/StreamResponse.java create mode 100644 grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/StringList.java create mode 100644 grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/Task.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskState.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskStatus.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java create mode 100644 grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java create mode 100644 grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java create mode 100644 grpc/src/main/resources/META-INF/beans.xml create mode 100644 reference-grpc/pom.xml create mode 100644 reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java create mode 100644 reference-grpc/src/main/resources/application.properties create mode 100644 reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java create mode 100644 reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java create mode 100644 sdk-server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java create mode 100644 sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java create mode 100644 sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java create mode 100644 sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java diff --git a/client/pom.xml b/client/pom.xml index acae086f4..d593b54ef 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -22,7 +22,11 @@ a2a-java-sdk-common ${project.version}
- + + ${project.groupId} + a2a-java-sdk-grpc + ${project.version} + ${project.groupId} a2a-java-sdk-spec diff --git a/client/src/main/java/io/a2a/client/A2AGrpcClient.java b/client/src/main/java/io/a2a/client/A2AGrpcClient.java new file mode 100644 index 000000000..661adc228 --- /dev/null +++ b/client/src/main/java/io/a2a/client/A2AGrpcClient.java @@ -0,0 +1,200 @@ +package io.a2a.client; + +import static io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub; +import static io.a2a.grpc.A2AServiceGrpc.A2AServiceStub; +import static io.a2a.grpc.utils.ProtoUtils.FromProto; +import static io.a2a.grpc.utils.ProtoUtils.ToProto; +import static io.a2a.util.Assert.checkNotNullParam; + +import java.util.function.Consumer; + +import io.a2a.client.sse.SSEStreamObserver; +import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.CancelTaskRequest; +import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskRequest; +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.spec.A2AServerException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.grpc.Channel; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; + +/** + * A2A Client for interacting with an A2A agent via gRPC. + */ +public class A2AGrpcClient { + + private A2AServiceBlockingV2Stub blockingStub; + private A2AServiceStub asyncStub; + private AgentCard agentCard; + + /** + * Create an A2A client for interacting with an A2A agent via gRPC. + * + * @param channel the gRPC channel + * @param agentCard the agent card for the A2A server this client will be communicating with + */ + public A2AGrpcClient(Channel channel, AgentCard agentCard) { + checkNotNullParam("channel", channel); + checkNotNullParam("agentCard", agentCard); + this.asyncStub = A2AServiceGrpc.newStub(channel); + this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); + this.agentCard = agentCard; + } + + /** + * Send a message to the remote agent. + * + * @param messageSendParams the parameters for the message to be sent + * @return the response, may be a message or a task + * @throws A2AServerException if sending the message fails for any reason + */ + public EventKind sendMessage(MessageSendParams messageSendParams) throws A2AServerException { + SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); + try { + SendMessageResponse response = blockingStub.sendMessage(request); + if (response.hasMsg()) { + return FromProto.message(response.getMsg()); + } else if (response.hasTask()) { + return FromProto.task(response.getTask()); + } else { + throw new A2AServerException("Server response did not contain a message or task"); + } + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to send message: " + e, e); + } + } + + /** + * Retrieves the current state and history of a specific task. + * + * @param taskQueryParams the params for the task to be queried + * @return the task + * @throws A2AServerException if retrieving the task fails for any reason + */ + public Task getTask(TaskQueryParams taskQueryParams) throws A2AServerException { + GetTaskRequest.Builder requestBuilder = GetTaskRequest.newBuilder(); + requestBuilder.setName("tasks/" + taskQueryParams.id()); + if (taskQueryParams.historyLength() != null) { + requestBuilder.setHistoryLength(taskQueryParams.historyLength()); + } + GetTaskRequest getTaskRequest = requestBuilder.build(); + try { + return FromProto.task(blockingStub.getTask(getTaskRequest)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to get task: " + e, e); + } + } + + /** + * Cancel a task that was previously submitted to the A2A server. + * + * @param taskIdParams the params for the task to be cancelled + * @return the updated task + * @throws A2AServerException if cancelling the task fails for any reason + */ + public Task cancelTask(TaskIdParams taskIdParams) throws A2AServerException { + CancelTaskRequest cancelTaskRequest = CancelTaskRequest.newBuilder() + .setName("tasks/" + taskIdParams.id()) + .build(); + try { + return FromProto.task(blockingStub.cancelTask(cancelTaskRequest)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to cancel task: " + e, e); + } + } + + /** + * Set push notification configuration for a task. + * + * @param taskPushNotificationConfig the task push notification configuration + * @return the task push notification config + * @throws A2AServerException if setting the push notification configuration fails for any reason + */ + public TaskPushNotificationConfig setTaskPushNotificationConfig(TaskPushNotificationConfig taskPushNotificationConfig) throws A2AServerException { + String configId = taskPushNotificationConfig.pushNotificationConfig().id(); + CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + taskPushNotificationConfig.taskId()) + .setConfig(ToProto.taskPushNotificationConfig(taskPushNotificationConfig)) + .setConfigId(configId == null ? "" : configId) + .build(); + try { + return FromProto.taskPushNotificationConfig(blockingStub.createTaskPushNotificationConfig(request)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to set the task push notification config: " + e, e); + } + } + + /** + * Get the push notification configuration for a task. + * + * @param getTaskPushNotificationConfigParams the params for the task + * @return the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + public TaskPushNotificationConfig getTaskPushNotificationConfig(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { + GetTaskPushNotificationConfigRequest getTaskPushNotificationConfigRequest = GetTaskPushNotificationConfigRequest.newBuilder() + .setName(getTaskPushNotificationConfigName(getTaskPushNotificationConfigParams)) + .build(); + try { + return FromProto.taskPushNotificationConfig(blockingStub.getTaskPushNotificationConfig(getTaskPushNotificationConfigRequest)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to get the task push notification config: " + e, e); + } + } + + /** + * Send a streaming message request to the remote agent. + * + * @param messageSendParams the parameters for the message to be sent + * @param eventHandler a consumer that will be invoked for each event received from the remote agent + * @param errorHandler a consumer that will be invoked if an error occurs + * @throws A2AServerException if sending the streaming message fails for any reason + */ + public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer eventHandler, + Consumer errorHandler) throws A2AServerException { + SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); + StreamObserver streamObserver = new SSEStreamObserver(eventHandler, errorHandler); + try { + asyncStub.sendStreamingMessage(request, streamObserver); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to send streaming message: " + e, e); + } + } + + private SendMessageRequest createGrpcSendMessageRequestFromMessageSendParams(MessageSendParams messageSendParams) { + SendMessageRequest.Builder builder = SendMessageRequest.newBuilder(); + builder.setRequest(ToProto.message(messageSendParams.message())); + if (messageSendParams.configuration() != null) { + builder.setConfiguration(ToProto.messageSendConfiguration(messageSendParams.configuration())); + } + if (messageSendParams.metadata() != null) { + builder.setMetadata(ToProto.struct(messageSendParams.metadata())); + } + return builder.build(); + } + + private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) { + StringBuilder name = new StringBuilder(); + name.append("tasks/"); + name.append(getTaskPushNotificationConfigParams.id()); + if (getTaskPushNotificationConfigParams.pushNotificationConfigId() != null) { + name.append("/pushNotificationConfigs/"); + name.append(getTaskPushNotificationConfigParams.pushNotificationConfigId()); + } + return name.toString(); + } +} diff --git a/client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java b/client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java new file mode 100644 index 000000000..adc721f42 --- /dev/null +++ b/client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java @@ -0,0 +1,57 @@ +package io.a2a.client.sse; + + +import static io.a2a.grpc.utils.ProtoUtils.FromProto; + +import java.util.function.Consumer; +import java.util.logging.Logger; + +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.StreamingEventKind; +import io.grpc.stub.StreamObserver; + +public class SSEStreamObserver implements StreamObserver { + + private static final Logger log = Logger.getLogger(SSEStreamObserver.class.getName()); + private final Consumer eventHandler; + private final Consumer errorHandler; + + public SSEStreamObserver(Consumer eventHandler, Consumer errorHandler) { + this.eventHandler = eventHandler; + this.errorHandler = errorHandler; + } + + @Override + public void onNext(StreamResponse response) { + StreamingEventKind event; + switch (response.getPayloadCase()) { + case MSG: + event = FromProto.message(response.getMsg()); + break; + case TASK: + event = FromProto.task(response.getTask()); + break; + case STATUS_UPDATE: + event = FromProto.taskStatusUpdateEvent(response.getStatusUpdate()); + break; + case ARTIFACT_UPDATE: + event = FromProto.taskArtifactUpdateEvent(response.getArtifactUpdate()); + break; + default: + log.warning("Invalid stream response " + response.getPayloadCase()); + errorHandler.accept(new IllegalStateException("Invalid stream response from server: " + response.getPayloadCase())); + return; + } + eventHandler.accept(event); + } + + @Override + public void onError(Throwable t) { + errorHandler.accept(t); + } + + @Override + public void onCompleted() { + // done + } +} diff --git a/grpc/pom.xml b/grpc/pom.xml new file mode 100644 index 000000000..a8e7d634e --- /dev/null +++ b/grpc/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + + a2a-java-sdk-grpc + + jar + + Java SDK A2A gRPC + Java SDK for the Agent2Agent Protocol (A2A) - gRPC + + + + io.github.a2asdk + a2a-java-sdk-spec + ${project.version} + + + com.google.protobuf + protobuf-java + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + + diff --git a/grpc/src/main/java/io/a2a/grpc/A2A.java b/grpc/src/main/java/io/a2a/grpc/A2A.java new file mode 100644 index 000000000..1b38002d7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/A2A.java @@ -0,0 +1,836 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public final class A2A { + private A2A() {} + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + A2A.class.getName()); + } + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_SendMessageConfiguration_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_SendMessageConfiguration_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_Task_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_Task_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_TaskStatus_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_TaskStatus_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_Part_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_Part_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_FilePart_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_FilePart_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_DataPart_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_DataPart_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_Message_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_Message_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_Artifact_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_Artifact_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_TaskStatusUpdateEvent_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_TaskArtifactUpdateEvent_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_PushNotificationConfig_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_PushNotificationConfig_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AuthenticationInfo_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AuthenticationInfo_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentInterface_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentInterface_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentCard_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentCard_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentProvider_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentProvider_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentCapabilities_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentExtension_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentExtension_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentSkill_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentSkill_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_TaskPushNotificationConfig_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_StringList_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_StringList_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_Security_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_Security_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_Security_SchemesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_Security_SchemesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_SecurityScheme_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_SecurityScheme_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_APIKeySecurityScheme_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_OAuth2SecurityScheme_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_OAuthFlows_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_OAuthFlows_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_SendMessageRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_GetTaskRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_CancelTaskRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_GetAgentCardRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_SendMessageResponse_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_StreamResponse_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_StreamResponse_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\ta2a.proto\022\006a2a.v1\032\034google/api/annotati" + + "ons.proto\032\027google/api/client.proto\032\037goog" + + "le/api/field_behavior.proto\032\033google/prot" + + "obuf/empty.proto\032\034google/protobuf/struct" + + ".proto\032\037google/protobuf/timestamp.proto\"" + + "\336\001\n\030SendMessageConfiguration\0222\n\025accepted" + + "_output_modes\030\001 \003(\tR\023acceptedOutputModes" + + "\022K\n\021push_notification\030\002 \001(\0132\036.a2a.v1.Pus" + + "hNotificationConfigR\020pushNotification\022%\n" + + "\016history_length\030\003 \001(\005R\rhistoryLength\022\032\n\010" + + "blocking\030\004 \001(\010R\010blocking\"\361\001\n\004Task\022\016\n\002id\030" + + "\001 \001(\tR\002id\022\035\n\ncontext_id\030\002 \001(\tR\tcontextId" + + "\022*\n\006status\030\003 \001(\0132\022.a2a.v1.TaskStatusR\006st" + + "atus\022.\n\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifac" + + "tR\tartifacts\022)\n\007history\030\005 \003(\0132\017.a2a.v1.M" + + "essageR\007history\0223\n\010metadata\030\006 \001(\0132\027.goog" + + "le.protobuf.StructR\010metadata\"\231\001\n\nTaskSta" + + "tus\022\'\n\005state\030\001 \001(\0162\021.a2a.v1.TaskStateR\005s" + + "tate\022(\n\006update\030\002 \001(\0132\017.a2a.v1.MessageR\007m" + + "essage\0228\n\ttimestamp\030\003 \001(\0132\032.google.proto" + + "buf.TimestampR\ttimestamp\"t\n\004Part\022\024\n\004text" + + "\030\001 \001(\tH\000R\004text\022&\n\004file\030\002 \001(\0132\020.a2a.v1.Fi" + + "lePartH\000R\004file\022&\n\004data\030\003 \001(\0132\020.a2a.v1.Da" + + "taPartH\000R\004dataB\006\n\004part\"\177\n\010FilePart\022$\n\rfi" + + "le_with_uri\030\001 \001(\tH\000R\013fileWithUri\022(\n\017file" + + "_with_bytes\030\002 \001(\014H\000R\rfileWithBytes\022\033\n\tmi" + + "me_type\030\003 \001(\tR\010mimeTypeB\006\n\004file\"7\n\010DataP" + + "art\022+\n\004data\030\001 \001(\0132\027.google.protobuf.Stru" + + "ctR\004data\"\377\001\n\007Message\022\035\n\nmessage_id\030\001 \001(\t" + + "R\tmessageId\022\035\n\ncontext_id\030\002 \001(\tR\tcontext" + + "Id\022\027\n\007task_id\030\003 \001(\tR\006taskId\022 \n\004role\030\004 \001(" + + "\0162\014.a2a.v1.RoleR\004role\022&\n\007content\030\005 \003(\0132\014" + + ".a2a.v1.PartR\007content\0223\n\010metadata\030\006 \001(\0132" + + "\027.google.protobuf.StructR\010metadata\022\036\n\nex" + + "tensions\030\007 \003(\tR\nextensions\"\332\001\n\010Artifact\022" + + "\037\n\013artifact_id\030\001 \001(\tR\nartifactId\022\022\n\004name" + + "\030\003 \001(\tR\004name\022 \n\013description\030\004 \001(\tR\013descr" + + "iption\022\"\n\005parts\030\005 \003(\0132\014.a2a.v1.PartR\005par" + + "ts\0223\n\010metadata\030\006 \001(\0132\027.google.protobuf.S" + + "tructR\010metadata\022\036\n\nextensions\030\007 \003(\tR\next" + + "ensions\"\306\001\n\025TaskStatusUpdateEvent\022\027\n\007tas" + + "k_id\030\001 \001(\tR\006taskId\022\035\n\ncontext_id\030\002 \001(\tR\t" + + "contextId\022*\n\006status\030\003 \001(\0132\022.a2a.v1.TaskS" + + "tatusR\006status\022\024\n\005final\030\004 \001(\010R\005final\0223\n\010m" + + "etadata\030\005 \001(\0132\027.google.protobuf.StructR\010" + + "metadata\"\353\001\n\027TaskArtifactUpdateEvent\022\027\n\007" + + "task_id\030\001 \001(\tR\006taskId\022\035\n\ncontext_id\030\002 \001(" + + "\tR\tcontextId\022,\n\010artifact\030\003 \001(\0132\020.a2a.v1." + + "ArtifactR\010artifact\022\026\n\006append\030\004 \001(\010R\006appe" + + "nd\022\035\n\nlast_chunk\030\005 \001(\010R\tlastChunk\0223\n\010met" + + "adata\030\006 \001(\0132\027.google.protobuf.StructR\010me" + + "tadata\"\224\001\n\026PushNotificationConfig\022\016\n\002id\030" + + "\001 \001(\tR\002id\022\020\n\003url\030\002 \001(\tR\003url\022\024\n\005token\030\003 \001" + + "(\tR\005token\022B\n\016authentication\030\004 \001(\0132\032.a2a." + + "v1.AuthenticationInfoR\016authentication\"P\n" + + "\022AuthenticationInfo\022\030\n\007schemes\030\001 \003(\tR\007sc" + + "hemes\022 \n\013credentials\030\002 \001(\tR\013credentials\"" + + "@\n\016AgentInterface\022\020\n\003url\030\001 \001(\tR\003url\022\034\n\tt" + + "ransport\030\002 \001(\tR\ttransport\"\361\006\n\tAgentCard\022" + + ")\n\020protocol_version\030\020 \001(\tR\017protocolVersi" + + "on\022\022\n\004name\030\001 \001(\tR\004name\022 \n\013description\030\002 " + + "\001(\tR\013description\022\020\n\003url\030\003 \001(\tR\003url\022/\n\023pr" + + "eferred_transport\030\016 \001(\tR\022preferredTransp" + + "ort\022K\n\025additional_interfaces\030\017 \003(\0132\026.a2a" + + ".v1.AgentInterfaceR\024additionalInterfaces" + + "\0221\n\010provider\030\004 \001(\0132\025.a2a.v1.AgentProvide" + + "rR\010provider\022\030\n\007version\030\005 \001(\tR\007version\022+\n" + + "\021documentation_url\030\006 \001(\tR\020documentationU" + + "rl\022=\n\014capabilities\030\007 \001(\0132\031.a2a.v1.AgentC" + + "apabilitiesR\014capabilities\022Q\n\020security_sc" + + "hemes\030\010 \003(\0132&.a2a.v1.AgentCard.SecurityS" + + "chemesEntryR\017securitySchemes\022,\n\010security" + + "\030\t \003(\0132\020.a2a.v1.SecurityR\010security\022.\n\023de" + + "fault_input_modes\030\n \003(\tR\021defaultInputMod" + + "es\0220\n\024default_output_modes\030\013 \003(\tR\022defaul" + + "tOutputModes\022*\n\006skills\030\014 \003(\0132\022.a2a.v1.Ag" + + "entSkillR\006skills\022O\n$supports_authenticat" + + "ed_extended_card\030\r \001(\010R!supportsAuthenti" + + "catedExtendedCard\032Z\n\024SecuritySchemesEntr" + + "y\022\020\n\003key\030\001 \001(\tR\003key\022,\n\005value\030\002 \001(\0132\026.a2a" + + ".v1.SecuritySchemeR\005value:\0028\001\"E\n\rAgentPr" + + "ovider\022\020\n\003url\030\001 \001(\tR\003url\022\"\n\014organization" + + "\030\002 \001(\tR\014organization\"\230\001\n\021AgentCapabiliti" + + "es\022\034\n\tstreaming\030\001 \001(\010R\tstreaming\022-\n\022push" + + "_notifications\030\002 \001(\010R\021pushNotifications\022" + + "6\n\nextensions\030\003 \003(\0132\026.a2a.v1.AgentExtens" + + "ionR\nextensions\"\221\001\n\016AgentExtension\022\020\n\003ur" + + "i\030\001 \001(\tR\003uri\022 \n\013description\030\002 \001(\tR\013descr" + + "iption\022\032\n\010required\030\003 \001(\010R\010required\022/\n\006pa" + + "rams\030\004 \001(\0132\027.google.protobuf.StructR\006par" + + "ams\"\306\001\n\nAgentSkill\022\016\n\002id\030\001 \001(\tR\002id\022\022\n\004na" + + "me\030\002 \001(\tR\004name\022 \n\013description\030\003 \001(\tR\013des" + + "cription\022\022\n\004tags\030\004 \003(\tR\004tags\022\032\n\010examples" + + "\030\005 \003(\tR\010examples\022\037\n\013input_modes\030\006 \003(\tR\ni" + + "nputModes\022!\n\014output_modes\030\007 \003(\tR\013outputM" + + "odes\"\212\001\n\032TaskPushNotificationConfig\022\022\n\004n" + + "ame\030\001 \001(\tR\004name\022X\n\030push_notification_con" + + "fig\030\002 \001(\0132\036.a2a.v1.PushNotificationConfi" + + "gR\026pushNotificationConfig\" \n\nStringList\022" + + "\022\n\004list\030\001 \003(\tR\004list\"\223\001\n\010Security\0227\n\007sche" + + "mes\030\001 \003(\0132\035.a2a.v1.Security.SchemesEntry" + + "R\007schemes\032N\n\014SchemesEntry\022\020\n\003key\030\001 \001(\tR\003" + + "key\022(\n\005value\030\002 \001(\0132\022.a2a.v1.StringListR\005" + + "value:\0028\001\"\221\003\n\016SecurityScheme\022U\n\027api_key_" + + "security_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySec" + + "uritySchemeH\000R\024apiKeySecurityScheme\022[\n\031h" + + "ttp_auth_security_scheme\030\002 \001(\0132\036.a2a.v1." + + "HTTPAuthSecuritySchemeH\000R\026httpAuthSecuri" + + "tyScheme\022T\n\026oauth2_security_scheme\030\003 \001(\013" + + "2\034.a2a.v1.OAuth2SecuritySchemeH\000R\024oauth2" + + "SecurityScheme\022k\n\037open_id_connect_securi" + + "ty_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConnectSe" + + "curitySchemeH\000R\033openIdConnectSecuritySch" + + "emeB\010\n\006scheme\"h\n\024APIKeySecurityScheme\022 \n" + + "\013description\030\001 \001(\tR\013description\022\032\n\010locat" + + "ion\030\002 \001(\tR\010location\022\022\n\004name\030\003 \001(\tR\004name\"" + + "w\n\026HTTPAuthSecurityScheme\022 \n\013description" + + "\030\001 \001(\tR\013description\022\026\n\006scheme\030\002 \001(\tR\006sch" + + "eme\022#\n\rbearer_format\030\003 \001(\tR\014bearerFormat" + + "\"b\n\024OAuth2SecurityScheme\022 \n\013description\030" + + "\001 \001(\tR\013description\022(\n\005flows\030\002 \001(\0132\022.a2a." + + "v1.OAuthFlowsR\005flows\"n\n\033OpenIdConnectSec" + + "urityScheme\022 \n\013description\030\001 \001(\tR\013descri" + + "ption\022-\n\023open_id_connect_url\030\002 \001(\tR\020open" + + "IdConnectUrl\"\260\002\n\nOAuthFlows\022S\n\022authoriza" + + "tion_code\030\001 \001(\0132\".a2a.v1.AuthorizationCo" + + "deOAuthFlowH\000R\021authorizationCode\022S\n\022clie" + + "nt_credentials\030\002 \001(\0132\".a2a.v1.ClientCred" + + "entialsOAuthFlowH\000R\021clientCredentials\0227\n" + + "\010implicit\030\003 \001(\0132\031.a2a.v1.ImplicitOAuthFl" + + "owH\000R\010implicit\0227\n\010password\030\004 \001(\0132\031.a2a.v" + + "1.PasswordOAuthFlowH\000R\010passwordB\006\n\004flow\"" + + "\212\002\n\032AuthorizationCodeOAuthFlow\022+\n\021author" + + "ization_url\030\001 \001(\tR\020authorizationUrl\022\033\n\tt" + + "oken_url\030\002 \001(\tR\010tokenUrl\022\037\n\013refresh_url\030" + + "\003 \001(\tR\nrefreshUrl\022F\n\006scopes\030\004 \003(\0132..a2a." + + "v1.AuthorizationCodeOAuthFlow.ScopesEntr" + + "yR\006scopes\0329\n\013ScopesEntry\022\020\n\003key\030\001 \001(\tR\003k" + + "ey\022\024\n\005value\030\002 \001(\tR\005value:\0028\001\"\335\001\n\032ClientC" + + "redentialsOAuthFlow\022\033\n\ttoken_url\030\001 \001(\tR\010" + + "tokenUrl\022\037\n\013refresh_url\030\002 \001(\tR\nrefreshUr" + + "l\022F\n\006scopes\030\003 \003(\0132..a2a.v1.ClientCredent" + + "ialsOAuthFlow.ScopesEntryR\006scopes\0329\n\013Sco" + + "pesEntry\022\020\n\003key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001(" + + "\tR\005value:\0028\001\"\333\001\n\021ImplicitOAuthFlow\022+\n\021au" + + "thorization_url\030\001 \001(\tR\020authorizationUrl\022" + + "\037\n\013refresh_url\030\002 \001(\tR\nrefreshUrl\022=\n\006scop" + + "es\030\003 \003(\0132%.a2a.v1.ImplicitOAuthFlow.Scop" + + "esEntryR\006scopes\0329\n\013ScopesEntry\022\020\n\003key\030\001 " + + "\001(\tR\003key\022\024\n\005value\030\002 \001(\tR\005value:\0028\001\"\313\001\n\021P" + + "asswordOAuthFlow\022\033\n\ttoken_url\030\001 \001(\tR\010tok" + + "enUrl\022\037\n\013refresh_url\030\002 \001(\tR\nrefreshUrl\022=" + + "\n\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuthFlo" + + "w.ScopesEntryR\006scopes\0329\n\013ScopesEntry\022\020\n\003" + + "key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001(\tR\005value:\0028\001" + + "\"\301\001\n\022SendMessageRequest\022.\n\007request\030\001 \001(\013" + + "2\017.a2a.v1.MessageB\003\340A\002R\007request\022F\n\rconfi" + + "guration\030\002 \001(\0132 .a2a.v1.SendMessageConfi" + + "gurationR\rconfiguration\0223\n\010metadata\030\003 \001(" + + "\0132\027.google.protobuf.StructR\010metadata\"P\n\016" + + "GetTaskRequest\022\027\n\004name\030\001 \001(\tB\003\340A\002R\004name\022" + + "%\n\016history_length\030\002 \001(\005R\rhistoryLength\"\'" + + "\n\021CancelTaskRequest\022\022\n\004name\030\001 \001(\tR\004name\"" + + ":\n$GetTaskPushNotificationConfigRequest\022" + + "\022\n\004name\030\001 \001(\tR\004name\"=\n\'DeleteTaskPushNot" + + "ificationConfigRequest\022\022\n\004name\030\001 \001(\tR\004na" + + "me\"\251\001\n\'CreateTaskPushNotificationConfigR" + + "equest\022\033\n\006parent\030\001 \001(\tB\003\340A\002R\006parent\022 \n\tc" + + "onfig_id\030\002 \001(\tB\003\340A\002R\010configId\022?\n\006config\030" + + "\003 \001(\0132\".a2a.v1.TaskPushNotificationConfi" + + "gB\003\340A\002R\006config\"-\n\027TaskSubscriptionReques" + + "t\022\022\n\004name\030\001 \001(\tR\004name\"{\n%ListTaskPushNot" + + "ificationConfigRequest\022\026\n\006parent\030\001 \001(\tR\006" + + "parent\022\033\n\tpage_size\030\002 \001(\005R\010pageSize\022\035\n\np" + + "age_token\030\003 \001(\tR\tpageToken\"\025\n\023GetAgentCa" + + "rdRequest\"m\n\023SendMessageResponse\022\"\n\004task" + + "\030\001 \001(\0132\014.a2a.v1.TaskH\000R\004task\022\'\n\003msg\030\002 \001(" + + "\0132\017.a2a.v1.MessageH\000R\007messageB\t\n\007payload" + + "\"\372\001\n\016StreamResponse\022\"\n\004task\030\001 \001(\0132\014.a2a." + + "v1.TaskH\000R\004task\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.Me" + + "ssageH\000R\007message\022D\n\rstatus_update\030\003 \001(\0132" + + "\035.a2a.v1.TaskStatusUpdateEventH\000R\014status" + + "Update\022J\n\017artifact_update\030\004 \001(\0132\037.a2a.v1" + + ".TaskArtifactUpdateEventH\000R\016artifactUpda" + + "teB\t\n\007payload\"\216\001\n&ListTaskPushNotificati" + + "onConfigResponse\022<\n\007configs\030\001 \003(\0132\".a2a." + + "v1.TaskPushNotificationConfigR\007configs\022&" + + "\n\017next_page_token\030\002 \001(\tR\rnextPageToken*\372" + + "\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020\000" + + "\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STATE" + + "_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n\021" + + "TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCEL" + + "LED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n\023" + + "TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUTH" + + "_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020" + + "\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\272\n\n\nA2A" + + "Service\022c\n\013SendMessage\022\032.a2a.v1.SendMess" + + "ageRequest\032\033.a2a.v1.SendMessageResponse\"" + + "\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024SendStre" + + "amingMessage\022\032.a2a.v1.SendMessageRequest" + + "\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"\022/v1/me" + + "ssage:stream:\001*0\001\022R\n\007GetTask\022\026.a2a.v1.Ge" + + "tTaskRequest\032\014.a2a.v1.Task\"!\332A\004name\202\323\344\223\002" + + "\024\022\022/v1/{name=tasks/*}\022[\n\nCancelTask\022\031.a2" + + "a.v1.CancelTaskRequest\032\014.a2a.v1.Task\"$\202\323" + + "\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*\022s\n\020Ta" + + "skSubscription\022\037.a2a.v1.TaskSubscription" + + "Request\032\026.a2a.v1.StreamResponse\"$\202\323\344\223\002\036\022" + + "\034/v1/{name=tasks/*}:subscribe0\001\022\304\001\n Crea" + + "teTaskPushNotificationConfig\022/.a2a.v1.Cr" + + "eateTaskPushNotificationConfigRequest\032\"." + + "a2a.v1.TaskPushNotificationConfig\"K\332A\rpa" + + "rent,config\202\323\344\223\0025\"+/v1/{parent=task/*/pu" + + "shNotificationConfigs}:\006config\022\256\001\n\035GetTa" + + "skPushNotificationConfig\022,.a2a.v1.GetTas" + + "kPushNotificationConfigRequest\032\".a2a.v1." + + "TaskPushNotificationConfig\";\332A\004name\202\323\344\223\002" + + ".\022,/v1/{name=tasks/*/pushNotificationCon" + + "figs/*}\022\276\001\n\036ListTaskPushNotificationConf" + + "ig\022-.a2a.v1.ListTaskPushNotificationConf" + + "igRequest\032..a2a.v1.ListTaskPushNotificat" + + "ionConfigResponse\"=\332A\006parent\202\323\344\223\002.\022,/v1/" + + "{parent=tasks/*}/pushNotificationConfigs" + + "\022P\n\014GetAgentCard\022\033.a2a.v1.GetAgentCardRe" + + "quest\032\021.a2a.v1.AgentCard\"\020\202\323\344\223\002\n\022\010/v1/ca" + + "rd\022\250\001\n DeleteTaskPushNotificationConfig\022" + + "/.a2a.v1.DeleteTaskPushNotificationConfi" + + "gRequest\032\026.google.protobuf.Empty\";\332A\004nam" + + "e\202\323\344\223\002.*,/v1/{name=tasks/*/pushNotificat" + + "ionConfigs/*}B7\n\013io.a2a.grpcB\003A2AP\001Z\030goo" + + "gle.golang.org/a2a/v1\252\002\006A2a.V1b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + com.google.api.AnnotationsProto.getDescriptor(), + com.google.api.ClientProto.getDescriptor(), + com.google.api.FieldBehaviorProto.getDescriptor(), + com.google.protobuf.EmptyProto.getDescriptor(), + com.google.protobuf.StructProto.getDescriptor(), + com.google.protobuf.TimestampProto.getDescriptor(), + }); + internal_static_a2a_v1_SendMessageConfiguration_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_a2a_v1_SendMessageConfiguration_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_SendMessageConfiguration_descriptor, + new java.lang.String[] { "AcceptedOutputModes", "PushNotification", "HistoryLength", "Blocking", }); + internal_static_a2a_v1_Task_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_a2a_v1_Task_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_Task_descriptor, + new java.lang.String[] { "Id", "ContextId", "Status", "Artifacts", "History", "Metadata", }); + internal_static_a2a_v1_TaskStatus_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_a2a_v1_TaskStatus_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_TaskStatus_descriptor, + new java.lang.String[] { "State", "Update", "Timestamp", }); + internal_static_a2a_v1_Part_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_a2a_v1_Part_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_Part_descriptor, + new java.lang.String[] { "Text", "File", "Data", "Part", }); + internal_static_a2a_v1_FilePart_descriptor = + getDescriptor().getMessageTypes().get(4); + internal_static_a2a_v1_FilePart_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_FilePart_descriptor, + new java.lang.String[] { "FileWithUri", "FileWithBytes", "MimeType", "File", }); + internal_static_a2a_v1_DataPart_descriptor = + getDescriptor().getMessageTypes().get(5); + internal_static_a2a_v1_DataPart_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_DataPart_descriptor, + new java.lang.String[] { "Data", }); + internal_static_a2a_v1_Message_descriptor = + getDescriptor().getMessageTypes().get(6); + internal_static_a2a_v1_Message_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_Message_descriptor, + new java.lang.String[] { "MessageId", "ContextId", "TaskId", "Role", "Content", "Metadata", "Extensions", }); + internal_static_a2a_v1_Artifact_descriptor = + getDescriptor().getMessageTypes().get(7); + internal_static_a2a_v1_Artifact_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_Artifact_descriptor, + new java.lang.String[] { "ArtifactId", "Name", "Description", "Parts", "Metadata", "Extensions", }); + internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor = + getDescriptor().getMessageTypes().get(8); + internal_static_a2a_v1_TaskStatusUpdateEvent_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor, + new java.lang.String[] { "TaskId", "ContextId", "Status", "Final", "Metadata", }); + internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor = + getDescriptor().getMessageTypes().get(9); + internal_static_a2a_v1_TaskArtifactUpdateEvent_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor, + new java.lang.String[] { "TaskId", "ContextId", "Artifact", "Append", "LastChunk", "Metadata", }); + internal_static_a2a_v1_PushNotificationConfig_descriptor = + getDescriptor().getMessageTypes().get(10); + internal_static_a2a_v1_PushNotificationConfig_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_PushNotificationConfig_descriptor, + new java.lang.String[] { "Id", "Url", "Token", "Authentication", }); + internal_static_a2a_v1_AuthenticationInfo_descriptor = + getDescriptor().getMessageTypes().get(11); + internal_static_a2a_v1_AuthenticationInfo_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AuthenticationInfo_descriptor, + new java.lang.String[] { "Schemes", "Credentials", }); + internal_static_a2a_v1_AgentInterface_descriptor = + getDescriptor().getMessageTypes().get(12); + internal_static_a2a_v1_AgentInterface_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentInterface_descriptor, + new java.lang.String[] { "Url", "Transport", }); + internal_static_a2a_v1_AgentCard_descriptor = + getDescriptor().getMessageTypes().get(13); + internal_static_a2a_v1_AgentCard_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentCard_descriptor, + new java.lang.String[] { "ProtocolVersion", "Name", "Description", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", }); + internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor = + internal_static_a2a_v1_AgentCard_descriptor.getNestedTypes().get(0); + internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_AgentProvider_descriptor = + getDescriptor().getMessageTypes().get(14); + internal_static_a2a_v1_AgentProvider_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentProvider_descriptor, + new java.lang.String[] { "Url", "Organization", }); + internal_static_a2a_v1_AgentCapabilities_descriptor = + getDescriptor().getMessageTypes().get(15); + internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentCapabilities_descriptor, + new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", }); + internal_static_a2a_v1_AgentExtension_descriptor = + getDescriptor().getMessageTypes().get(16); + internal_static_a2a_v1_AgentExtension_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentExtension_descriptor, + new java.lang.String[] { "Uri", "Description", "Required", "Params", }); + internal_static_a2a_v1_AgentSkill_descriptor = + getDescriptor().getMessageTypes().get(17); + internal_static_a2a_v1_AgentSkill_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentSkill_descriptor, + new java.lang.String[] { "Id", "Name", "Description", "Tags", "Examples", "InputModes", "OutputModes", }); + internal_static_a2a_v1_TaskPushNotificationConfig_descriptor = + getDescriptor().getMessageTypes().get(18); + internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_TaskPushNotificationConfig_descriptor, + new java.lang.String[] { "Name", "PushNotificationConfig", }); + internal_static_a2a_v1_StringList_descriptor = + getDescriptor().getMessageTypes().get(19); + internal_static_a2a_v1_StringList_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_StringList_descriptor, + new java.lang.String[] { "List", }); + internal_static_a2a_v1_Security_descriptor = + getDescriptor().getMessageTypes().get(20); + internal_static_a2a_v1_Security_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_Security_descriptor, + new java.lang.String[] { "Schemes", }); + internal_static_a2a_v1_Security_SchemesEntry_descriptor = + internal_static_a2a_v1_Security_descriptor.getNestedTypes().get(0); + internal_static_a2a_v1_Security_SchemesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_Security_SchemesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_SecurityScheme_descriptor = + getDescriptor().getMessageTypes().get(21); + internal_static_a2a_v1_SecurityScheme_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_SecurityScheme_descriptor, + new java.lang.String[] { "ApiKeySecurityScheme", "HttpAuthSecurityScheme", "Oauth2SecurityScheme", "OpenIdConnectSecurityScheme", "Scheme", }); + internal_static_a2a_v1_APIKeySecurityScheme_descriptor = + getDescriptor().getMessageTypes().get(22); + internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_APIKeySecurityScheme_descriptor, + new java.lang.String[] { "Description", "Location", "Name", }); + internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor = + getDescriptor().getMessageTypes().get(23); + internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor, + new java.lang.String[] { "Description", "Scheme", "BearerFormat", }); + internal_static_a2a_v1_OAuth2SecurityScheme_descriptor = + getDescriptor().getMessageTypes().get(24); + internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_OAuth2SecurityScheme_descriptor, + new java.lang.String[] { "Description", "Flows", }); + internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor = + getDescriptor().getMessageTypes().get(25); + internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor, + new java.lang.String[] { "Description", "OpenIdConnectUrl", }); + internal_static_a2a_v1_OAuthFlows_descriptor = + getDescriptor().getMessageTypes().get(26); + internal_static_a2a_v1_OAuthFlows_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_OAuthFlows_descriptor, + new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "Implicit", "Password", "Flow", }); + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor = + getDescriptor().getMessageTypes().get(27); + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor, + new java.lang.String[] { "AuthorizationUrl", "TokenUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor.getNestedTypes().get(0); + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor = + getDescriptor().getMessageTypes().get(28); + internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor, + new java.lang.String[] { "TokenUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor.getNestedTypes().get(0); + internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor = + getDescriptor().getMessageTypes().get(29); + internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor, + new java.lang.String[] { "AuthorizationUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor.getNestedTypes().get(0); + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_PasswordOAuthFlow_descriptor = + getDescriptor().getMessageTypes().get(30); + internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_PasswordOAuthFlow_descriptor, + new java.lang.String[] { "TokenUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_PasswordOAuthFlow_descriptor.getNestedTypes().get(0); + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_SendMessageRequest_descriptor = + getDescriptor().getMessageTypes().get(31); + internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_SendMessageRequest_descriptor, + new java.lang.String[] { "Request", "Configuration", "Metadata", }); + internal_static_a2a_v1_GetTaskRequest_descriptor = + getDescriptor().getMessageTypes().get(32); + internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_GetTaskRequest_descriptor, + new java.lang.String[] { "Name", "HistoryLength", }); + internal_static_a2a_v1_CancelTaskRequest_descriptor = + getDescriptor().getMessageTypes().get(33); + internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_CancelTaskRequest_descriptor, + new java.lang.String[] { "Name", }); + internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor = + getDescriptor().getMessageTypes().get(34); + internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor, + new java.lang.String[] { "Name", }); + internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor = + getDescriptor().getMessageTypes().get(35); + internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, + new java.lang.String[] { "Name", }); + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor = + getDescriptor().getMessageTypes().get(36); + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor, + new java.lang.String[] { "Parent", "ConfigId", "Config", }); + internal_static_a2a_v1_TaskSubscriptionRequest_descriptor = + getDescriptor().getMessageTypes().get(37); + internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_TaskSubscriptionRequest_descriptor, + new java.lang.String[] { "Name", }); + internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor = + getDescriptor().getMessageTypes().get(38); + internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor, + new java.lang.String[] { "Parent", "PageSize", "PageToken", }); + internal_static_a2a_v1_GetAgentCardRequest_descriptor = + getDescriptor().getMessageTypes().get(39); + internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_GetAgentCardRequest_descriptor, + new java.lang.String[] { }); + internal_static_a2a_v1_SendMessageResponse_descriptor = + getDescriptor().getMessageTypes().get(40); + internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_SendMessageResponse_descriptor, + new java.lang.String[] { "Task", "Msg", "Payload", }); + internal_static_a2a_v1_StreamResponse_descriptor = + getDescriptor().getMessageTypes().get(41); + internal_static_a2a_v1_StreamResponse_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_StreamResponse_descriptor, + new java.lang.String[] { "Task", "Msg", "StatusUpdate", "ArtifactUpdate", "Payload", }); + internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor = + getDescriptor().getMessageTypes().get(42); + internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor, + new java.lang.String[] { "Configs", "NextPageToken", }); + descriptor.resolveAllFeaturesImmutable(); + com.google.api.AnnotationsProto.getDescriptor(); + com.google.api.ClientProto.getDescriptor(); + com.google.api.FieldBehaviorProto.getDescriptor(); + com.google.protobuf.EmptyProto.getDescriptor(); + com.google.protobuf.StructProto.getDescriptor(); + com.google.protobuf.TimestampProto.getDescriptor(); + com.google.protobuf.ExtensionRegistry registry = + com.google.protobuf.ExtensionRegistry.newInstance(); + registry.add(com.google.api.FieldBehaviorProto.fieldBehavior); + registry.add(com.google.api.AnnotationsProto.http); + registry.add(com.google.api.ClientProto.methodSignature); + com.google.protobuf.Descriptors.FileDescriptor + .internalUpdateFileDescriptor(descriptor, registry); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java new file mode 100644 index 000000000..4ec3a9ac0 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -0,0 +1,1327 @@ +package io.a2a.grpc; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + *
+ * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+ * different shape than the JSONRPC version to better conform to AIP-127,
+ * where appropriate. The nouns are AgentCard, Message, Task and
+ * TaskPushNotificationConfig.
+ * - Messages are not a standard resource so there is no get/delete/update/list
+ *   interface, only a send and stream custom methods.
+ * - Tasks have a get interface and custom cancel and subscribe methods.
+ * - TaskPushNotificationConfig are a resource whose parent is a task.
+ *   They have get, list and create methods.
+ * - AgentCard is a static resource with only a get method.
+ * fields are not present as they don't comply with AIP rules, and the
+ * optional history_length on the get task method is not present as it also
+ * violates AIP-127 and AIP-131.
+ * 
+ */ +// Temporarily comment this out until https://github.com/grpc/grpc-java/pull/12080 is included in a release +//@javax.annotation.Generated( +// value = "by gRPC proto compiler (version 1.73.0)", +// comments = "Source: a2a.proto") +@io.grpc.stub.annotations.GrpcGenerated +public final class A2AServiceGrpc { + + private A2AServiceGrpc() {} + + public static final java.lang.String SERVICE_NAME = "a2a.v1.A2AService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getSendMessageMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "SendMessage", + requestType = io.a2a.grpc.SendMessageRequest.class, + responseType = io.a2a.grpc.SendMessageResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getSendMessageMethod() { + io.grpc.MethodDescriptor getSendMessageMethod; + if ((getSendMessageMethod = A2AServiceGrpc.getSendMessageMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getSendMessageMethod = A2AServiceGrpc.getSendMessageMethod) == null) { + A2AServiceGrpc.getSendMessageMethod = getSendMessageMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendMessage")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.SendMessageRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.SendMessageResponse.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("SendMessage")) + .build(); + } + } + } + return getSendMessageMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendStreamingMessageMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "SendStreamingMessage", + requestType = io.a2a.grpc.SendMessageRequest.class, + responseType = io.a2a.grpc.StreamResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getSendStreamingMessageMethod() { + io.grpc.MethodDescriptor getSendStreamingMessageMethod; + if ((getSendStreamingMessageMethod = A2AServiceGrpc.getSendStreamingMessageMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getSendStreamingMessageMethod = A2AServiceGrpc.getSendStreamingMessageMethod) == null) { + A2AServiceGrpc.getSendStreamingMessageMethod = getSendStreamingMessageMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendStreamingMessage")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.SendMessageRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.StreamResponse.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("SendStreamingMessage")) + .build(); + } + } + } + return getSendStreamingMessageMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetTaskMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetTask", + requestType = io.a2a.grpc.GetTaskRequest.class, + responseType = io.a2a.grpc.Task.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetTaskMethod() { + io.grpc.MethodDescriptor getGetTaskMethod; + if ((getGetTaskMethod = A2AServiceGrpc.getGetTaskMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getGetTaskMethod = A2AServiceGrpc.getGetTaskMethod) == null) { + A2AServiceGrpc.getGetTaskMethod = getGetTaskMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetTask")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.GetTaskRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.Task.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("GetTask")) + .build(); + } + } + } + return getGetTaskMethod; + } + + private static volatile io.grpc.MethodDescriptor getCancelTaskMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "CancelTask", + requestType = io.a2a.grpc.CancelTaskRequest.class, + responseType = io.a2a.grpc.Task.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getCancelTaskMethod() { + io.grpc.MethodDescriptor getCancelTaskMethod; + if ((getCancelTaskMethod = A2AServiceGrpc.getCancelTaskMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getCancelTaskMethod = A2AServiceGrpc.getCancelTaskMethod) == null) { + A2AServiceGrpc.getCancelTaskMethod = getCancelTaskMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "CancelTask")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.CancelTaskRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.Task.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("CancelTask")) + .build(); + } + } + } + return getCancelTaskMethod; + } + + private static volatile io.grpc.MethodDescriptor getTaskSubscriptionMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "TaskSubscription", + requestType = io.a2a.grpc.TaskSubscriptionRequest.class, + responseType = io.a2a.grpc.StreamResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getTaskSubscriptionMethod() { + io.grpc.MethodDescriptor getTaskSubscriptionMethod; + if ((getTaskSubscriptionMethod = A2AServiceGrpc.getTaskSubscriptionMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getTaskSubscriptionMethod = A2AServiceGrpc.getTaskSubscriptionMethod) == null) { + A2AServiceGrpc.getTaskSubscriptionMethod = getTaskSubscriptionMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "TaskSubscription")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.TaskSubscriptionRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.StreamResponse.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("TaskSubscription")) + .build(); + } + } + } + return getTaskSubscriptionMethod; + } + + private static volatile io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "CreateTaskPushNotificationConfig", + requestType = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, + responseType = io.a2a.grpc.TaskPushNotificationConfig.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod() { + io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod; + if ((getCreateTaskPushNotificationConfigMethod = A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getCreateTaskPushNotificationConfigMethod = A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod) == null) { + A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod = getCreateTaskPushNotificationConfigMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "CreateTaskPushNotificationConfig")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("CreateTaskPushNotificationConfig")) + .build(); + } + } + } + return getCreateTaskPushNotificationConfigMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetTaskPushNotificationConfigMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetTaskPushNotificationConfig", + requestType = io.a2a.grpc.GetTaskPushNotificationConfigRequest.class, + responseType = io.a2a.grpc.TaskPushNotificationConfig.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetTaskPushNotificationConfigMethod() { + io.grpc.MethodDescriptor getGetTaskPushNotificationConfigMethod; + if ((getGetTaskPushNotificationConfigMethod = A2AServiceGrpc.getGetTaskPushNotificationConfigMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getGetTaskPushNotificationConfigMethod = A2AServiceGrpc.getGetTaskPushNotificationConfigMethod) == null) { + A2AServiceGrpc.getGetTaskPushNotificationConfigMethod = getGetTaskPushNotificationConfigMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetTaskPushNotificationConfig")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.GetTaskPushNotificationConfigRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("GetTaskPushNotificationConfig")) + .build(); + } + } + } + return getGetTaskPushNotificationConfigMethod; + } + + private static volatile io.grpc.MethodDescriptor getListTaskPushNotificationConfigMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "ListTaskPushNotificationConfig", + requestType = io.a2a.grpc.ListTaskPushNotificationConfigRequest.class, + responseType = io.a2a.grpc.ListTaskPushNotificationConfigResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getListTaskPushNotificationConfigMethod() { + io.grpc.MethodDescriptor getListTaskPushNotificationConfigMethod; + if ((getListTaskPushNotificationConfigMethod = A2AServiceGrpc.getListTaskPushNotificationConfigMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getListTaskPushNotificationConfigMethod = A2AServiceGrpc.getListTaskPushNotificationConfigMethod) == null) { + A2AServiceGrpc.getListTaskPushNotificationConfigMethod = getListTaskPushNotificationConfigMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ListTaskPushNotificationConfig")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.ListTaskPushNotificationConfigRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.ListTaskPushNotificationConfigResponse.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("ListTaskPushNotificationConfig")) + .build(); + } + } + } + return getListTaskPushNotificationConfigMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetAgentCardMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetAgentCard", + requestType = io.a2a.grpc.GetAgentCardRequest.class, + responseType = io.a2a.grpc.AgentCard.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetAgentCardMethod() { + io.grpc.MethodDescriptor getGetAgentCardMethod; + if ((getGetAgentCardMethod = A2AServiceGrpc.getGetAgentCardMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getGetAgentCardMethod = A2AServiceGrpc.getGetAgentCardMethod) == null) { + A2AServiceGrpc.getGetAgentCardMethod = getGetAgentCardMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetAgentCard")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.GetAgentCardRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.AgentCard.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("GetAgentCard")) + .build(); + } + } + } + return getGetAgentCardMethod; + } + + private static volatile io.grpc.MethodDescriptor getDeleteTaskPushNotificationConfigMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "DeleteTaskPushNotificationConfig", + requestType = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.class, + responseType = com.google.protobuf.Empty.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getDeleteTaskPushNotificationConfigMethod() { + io.grpc.MethodDescriptor getDeleteTaskPushNotificationConfigMethod; + if ((getDeleteTaskPushNotificationConfigMethod = A2AServiceGrpc.getDeleteTaskPushNotificationConfigMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getDeleteTaskPushNotificationConfigMethod = A2AServiceGrpc.getDeleteTaskPushNotificationConfigMethod) == null) { + A2AServiceGrpc.getDeleteTaskPushNotificationConfigMethod = getDeleteTaskPushNotificationConfigMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "DeleteTaskPushNotificationConfig")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + com.google.protobuf.Empty.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("DeleteTaskPushNotificationConfig")) + .build(); + } + } + } + return getDeleteTaskPushNotificationConfigMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static A2AServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public A2AServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceStub(channel, callOptions); + } + }; + return A2AServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports all types of calls on the service + */ + public static A2AServiceBlockingV2Stub newBlockingV2Stub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public A2AServiceBlockingV2Stub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceBlockingV2Stub(channel, callOptions); + } + }; + return A2AServiceBlockingV2Stub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static A2AServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public A2AServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceBlockingStub(channel, callOptions); + } + }; + return A2AServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static A2AServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public A2AServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceFutureStub(channel, callOptions); + } + }; + return A2AServiceFutureStub.newStub(factory, channel); + } + + /** + *
+   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+   * different shape than the JSONRPC version to better conform to AIP-127,
+   * where appropriate. The nouns are AgentCard, Message, Task and
+   * TaskPushNotificationConfig.
+   * - Messages are not a standard resource so there is no get/delete/update/list
+   *   interface, only a send and stream custom methods.
+   * - Tasks have a get interface and custom cancel and subscribe methods.
+   * - TaskPushNotificationConfig are a resource whose parent is a task.
+   *   They have get, list and create methods.
+   * - AgentCard is a static resource with only a get method.
+   * fields are not present as they don't comply with AIP rules, and the
+   * optional history_length on the get task method is not present as it also
+   * violates AIP-127 and AIP-131.
+   * 
+ */ + public interface AsyncService { + + /** + *
+     * Send a message to the agent. This is a blocking call that will return the
+     * task once it is completed, or a LRO if requested.
+     * 
+ */ + default void sendMessage(io.a2a.grpc.SendMessageRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSendMessageMethod(), responseObserver); + } + + /** + *
+     * SendStreamingMessage is a streaming call that will return a stream of
+     * task update events until the Task is in an interrupted or terminal state.
+     * 
+ */ + default void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSendStreamingMessageMethod(), responseObserver); + } + + /** + *
+     * Get the current state of a task from the agent.
+     * 
+ */ + default void getTask(io.a2a.grpc.GetTaskRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetTaskMethod(), responseObserver); + } + + /** + *
+     * Cancel a task from the agent. If supported one should expect no
+     * more task updates for the task.
+     * 
+ */ + default void cancelTask(io.a2a.grpc.CancelTaskRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCancelTaskMethod(), responseObserver); + } + + /** + *
+     * TaskSubscription is a streaming call that will return a stream of task
+     * update events. This attaches the stream to an existing in process task.
+     * If the task is complete the stream will return the completed task (like
+     * GetTask) and close the stream.
+     * 
+ */ + default void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getTaskSubscriptionMethod(), responseObserver); + } + + /** + *
+     * Set a push notification config for a task.
+     * 
+ */ + default void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCreateTaskPushNotificationConfigMethod(), responseObserver); + } + + /** + *
+     * Get a push notification config for a task.
+     * 
+ */ + default void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetTaskPushNotificationConfigMethod(), responseObserver); + } + + /** + *
+     * Get a list of push notifications configured for a task.
+     * 
+ */ + default void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getListTaskPushNotificationConfigMethod(), responseObserver); + } + + /** + *
+     * GetAgentCard returns the agent card for the agent.
+     * 
+ */ + default void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetAgentCardMethod(), responseObserver); + } + + /** + *
+     * Delete a push notification config for a task.
+     * 
+ */ + default void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getDeleteTaskPushNotificationConfigMethod(), responseObserver); + } + } + + /** + * Base class for the server implementation of the service A2AService. + *
+   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+   * different shape than the JSONRPC version to better conform to AIP-127,
+   * where appropriate. The nouns are AgentCard, Message, Task and
+   * TaskPushNotificationConfig.
+   * - Messages are not a standard resource so there is no get/delete/update/list
+   *   interface, only a send and stream custom methods.
+   * - Tasks have a get interface and custom cancel and subscribe methods.
+   * - TaskPushNotificationConfig are a resource whose parent is a task.
+   *   They have get, list and create methods.
+   * - AgentCard is a static resource with only a get method.
+   * fields are not present as they don't comply with AIP rules, and the
+   * optional history_length on the get task method is not present as it also
+   * violates AIP-127 and AIP-131.
+   * 
+ */ + public static abstract class A2AServiceImplBase + implements io.grpc.BindableService, AsyncService { + + @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { + return A2AServiceGrpc.bindService(this); + } + } + + /** + * A stub to allow clients to do asynchronous rpc calls to service A2AService. + *
+   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+   * different shape than the JSONRPC version to better conform to AIP-127,
+   * where appropriate. The nouns are AgentCard, Message, Task and
+   * TaskPushNotificationConfig.
+   * - Messages are not a standard resource so there is no get/delete/update/list
+   *   interface, only a send and stream custom methods.
+   * - Tasks have a get interface and custom cancel and subscribe methods.
+   * - TaskPushNotificationConfig are a resource whose parent is a task.
+   *   They have get, list and create methods.
+   * - AgentCard is a static resource with only a get method.
+   * fields are not present as they don't comply with AIP rules, and the
+   * optional history_length on the get task method is not present as it also
+   * violates AIP-127 and AIP-131.
+   * 
+ */ + public static final class A2AServiceStub + extends io.grpc.stub.AbstractAsyncStub { + private A2AServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected A2AServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceStub(channel, callOptions); + } + + /** + *
+     * Send a message to the agent. This is a blocking call that will return the
+     * task once it is completed, or a LRO if requested.
+     * 
+ */ + public void sendMessage(io.a2a.grpc.SendMessageRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getSendMessageMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * SendStreamingMessage is a streaming call that will return a stream of
+     * task update events until the Task is in an interrupted or terminal state.
+     * 
+ */ + public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getSendStreamingMessageMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * Get the current state of a task from the agent.
+     * 
+ */ + public void getTask(io.a2a.grpc.GetTaskRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetTaskMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * Cancel a task from the agent. If supported one should expect no
+     * more task updates for the task.
+     * 
+ */ + public void cancelTask(io.a2a.grpc.CancelTaskRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getCancelTaskMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * TaskSubscription is a streaming call that will return a stream of task
+     * update events. This attaches the stream to an existing in process task.
+     * If the task is complete the stream will return the completed task (like
+     * GetTask) and close the stream.
+     * 
+ */ + public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getTaskSubscriptionMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * Set a push notification config for a task.
+     * 
+ */ + public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getCreateTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * Get a push notification config for a task.
+     * 
+ */ + public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * Get a list of push notifications configured for a task.
+     * 
+ */ + public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getListTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * GetAgentCard returns the agent card for the agent.
+     * 
+ */ + public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetAgentCardMethod(), getCallOptions()), request, responseObserver); + } + + /** + *
+     * Delete a push notification config for a task.
+     * 
+ */ + public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getDeleteTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); + } + } + + /** + * A stub to allow clients to do synchronous rpc calls to service A2AService. + *
+   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+   * different shape than the JSONRPC version to better conform to AIP-127,
+   * where appropriate. The nouns are AgentCard, Message, Task and
+   * TaskPushNotificationConfig.
+   * - Messages are not a standard resource so there is no get/delete/update/list
+   *   interface, only a send and stream custom methods.
+   * - Tasks have a get interface and custom cancel and subscribe methods.
+   * - TaskPushNotificationConfig are a resource whose parent is a task.
+   *   They have get, list and create methods.
+   * - AgentCard is a static resource with only a get method.
+   * fields are not present as they don't comply with AIP rules, and the
+   * optional history_length on the get task method is not present as it also
+   * violates AIP-127 and AIP-131.
+   * 
+ */ + public static final class A2AServiceBlockingV2Stub + extends io.grpc.stub.AbstractBlockingStub { + private A2AServiceBlockingV2Stub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected A2AServiceBlockingV2Stub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceBlockingV2Stub(channel, callOptions); + } + + /** + *
+     * Send a message to the agent. This is a blocking call that will return the
+     * task once it is completed, or a LRO if requested.
+     * 
+ */ + public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getSendMessageMethod(), getCallOptions(), request); + } + + /** + *
+     * SendStreamingMessage is a streaming call that will return a stream of
+     * task update events until the Task is in an interrupted or terminal state.
+     * 
+ */ + @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918") + public io.grpc.stub.BlockingClientCall + sendStreamingMessage(io.a2a.grpc.SendMessageRequest request) { + return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall( + getChannel(), getSendStreamingMessageMethod(), getCallOptions(), request); + } + + /** + *
+     * Get the current state of a task from the agent.
+     * 
+ */ + public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetTaskMethod(), getCallOptions(), request); + } + + /** + *
+     * Cancel a task from the agent. If supported one should expect no
+     * more task updates for the task.
+     * 
+ */ + public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getCancelTaskMethod(), getCallOptions(), request); + } + + /** + *
+     * TaskSubscription is a streaming call that will return a stream of task
+     * update events. This attaches the stream to an existing in process task.
+     * If the task is complete the stream will return the completed task (like
+     * GetTask) and close the stream.
+     * 
+ */ + @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918") + public io.grpc.stub.BlockingClientCall + taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request) { + return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall( + getChannel(), getTaskSubscriptionMethod(), getCallOptions(), request); + } + + /** + *
+     * Set a push notification config for a task.
+     * 
+ */ + public io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getCreateTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + + /** + *
+     * Get a push notification config for a task.
+     * 
+ */ + public io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + + /** + *
+     * Get a list of push notifications configured for a task.
+     * 
+ */ + public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getListTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + + /** + *
+     * GetAgentCard returns the agent card for the agent.
+     * 
+ */ + public io.a2a.grpc.AgentCard getAgentCard(io.a2a.grpc.GetAgentCardRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetAgentCardMethod(), getCallOptions(), request); + } + + /** + *
+     * Delete a push notification config for a task.
+     * 
+ */ + public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getDeleteTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + } + + /** + * A stub to allow clients to do limited synchronous rpc calls to service A2AService. + *
+   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+   * different shape than the JSONRPC version to better conform to AIP-127,
+   * where appropriate. The nouns are AgentCard, Message, Task and
+   * TaskPushNotificationConfig.
+   * - Messages are not a standard resource so there is no get/delete/update/list
+   *   interface, only a send and stream custom methods.
+   * - Tasks have a get interface and custom cancel and subscribe methods.
+   * - TaskPushNotificationConfig are a resource whose parent is a task.
+   *   They have get, list and create methods.
+   * - AgentCard is a static resource with only a get method.
+   * fields are not present as they don't comply with AIP rules, and the
+   * optional history_length on the get task method is not present as it also
+   * violates AIP-127 and AIP-131.
+   * 
+ */ + public static final class A2AServiceBlockingStub + extends io.grpc.stub.AbstractBlockingStub { + private A2AServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected A2AServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceBlockingStub(channel, callOptions); + } + + /** + *
+     * Send a message to the agent. This is a blocking call that will return the
+     * task once it is completed, or a LRO if requested.
+     * 
+ */ + public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getSendMessageMethod(), getCallOptions(), request); + } + + /** + *
+     * SendStreamingMessage is a streaming call that will return a stream of
+     * task update events until the Task is in an interrupted or terminal state.
+     * 
+ */ + public java.util.Iterator sendStreamingMessage( + io.a2a.grpc.SendMessageRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getSendStreamingMessageMethod(), getCallOptions(), request); + } + + /** + *
+     * Get the current state of a task from the agent.
+     * 
+ */ + public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetTaskMethod(), getCallOptions(), request); + } + + /** + *
+     * Cancel a task from the agent. If supported one should expect no
+     * more task updates for the task.
+     * 
+ */ + public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getCancelTaskMethod(), getCallOptions(), request); + } + + /** + *
+     * TaskSubscription is a streaming call that will return a stream of task
+     * update events. This attaches the stream to an existing in process task.
+     * If the task is complete the stream will return the completed task (like
+     * GetTask) and close the stream.
+     * 
+ */ + public java.util.Iterator taskSubscription( + io.a2a.grpc.TaskSubscriptionRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getTaskSubscriptionMethod(), getCallOptions(), request); + } + + /** + *
+     * Set a push notification config for a task.
+     * 
+ */ + public io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getCreateTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + + /** + *
+     * Get a push notification config for a task.
+     * 
+ */ + public io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + + /** + *
+     * Get a list of push notifications configured for a task.
+     * 
+ */ + public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getListTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + + /** + *
+     * GetAgentCard returns the agent card for the agent.
+     * 
+ */ + public io.a2a.grpc.AgentCard getAgentCard(io.a2a.grpc.GetAgentCardRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetAgentCardMethod(), getCallOptions(), request); + } + + /** + *
+     * Delete a push notification config for a task.
+     * 
+ */ + public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getDeleteTaskPushNotificationConfigMethod(), getCallOptions(), request); + } + } + + /** + * A stub to allow clients to do ListenableFuture-style rpc calls to service A2AService. + *
+   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
+   * different shape than the JSONRPC version to better conform to AIP-127,
+   * where appropriate. The nouns are AgentCard, Message, Task and
+   * TaskPushNotificationConfig.
+   * - Messages are not a standard resource so there is no get/delete/update/list
+   *   interface, only a send and stream custom methods.
+   * - Tasks have a get interface and custom cancel and subscribe methods.
+   * - TaskPushNotificationConfig are a resource whose parent is a task.
+   *   They have get, list and create methods.
+   * - AgentCard is a static resource with only a get method.
+   * fields are not present as they don't comply with AIP rules, and the
+   * optional history_length on the get task method is not present as it also
+   * violates AIP-127 and AIP-131.
+   * 
+ */ + public static final class A2AServiceFutureStub + extends io.grpc.stub.AbstractFutureStub { + private A2AServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected A2AServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new A2AServiceFutureStub(channel, callOptions); + } + + /** + *
+     * Send a message to the agent. This is a blocking call that will return the
+     * task once it is completed, or a LRO if requested.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture sendMessage( + io.a2a.grpc.SendMessageRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getSendMessageMethod(), getCallOptions()), request); + } + + /** + *
+     * Get the current state of a task from the agent.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture getTask( + io.a2a.grpc.GetTaskRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetTaskMethod(), getCallOptions()), request); + } + + /** + *
+     * Cancel a task from the agent. If supported one should expect no
+     * more task updates for the task.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture cancelTask( + io.a2a.grpc.CancelTaskRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getCancelTaskMethod(), getCallOptions()), request); + } + + /** + *
+     * Set a push notification config for a task.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture createTaskPushNotificationConfig( + io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getCreateTaskPushNotificationConfigMethod(), getCallOptions()), request); + } + + /** + *
+     * Get a push notification config for a task.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture getTaskPushNotificationConfig( + io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetTaskPushNotificationConfigMethod(), getCallOptions()), request); + } + + /** + *
+     * Get a list of push notifications configured for a task.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture listTaskPushNotificationConfig( + io.a2a.grpc.ListTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getListTaskPushNotificationConfigMethod(), getCallOptions()), request); + } + + /** + *
+     * GetAgentCard returns the agent card for the agent.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture getAgentCard( + io.a2a.grpc.GetAgentCardRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetAgentCardMethod(), getCallOptions()), request); + } + + /** + *
+     * Delete a push notification config for a task.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture deleteTaskPushNotificationConfig( + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getDeleteTaskPushNotificationConfigMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_SEND_MESSAGE = 0; + private static final int METHODID_SEND_STREAMING_MESSAGE = 1; + private static final int METHODID_GET_TASK = 2; + private static final int METHODID_CANCEL_TASK = 3; + private static final int METHODID_TASK_SUBSCRIPTION = 4; + private static final int METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG = 5; + private static final int METHODID_GET_TASK_PUSH_NOTIFICATION_CONFIG = 6; + private static final int METHODID_LIST_TASK_PUSH_NOTIFICATION_CONFIG = 7; + private static final int METHODID_GET_AGENT_CARD = 8; + private static final int METHODID_DELETE_TASK_PUSH_NOTIFICATION_CONFIG = 9; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final AsyncService serviceImpl; + private final int methodId; + + MethodHandlers(AsyncService serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_SEND_MESSAGE: + serviceImpl.sendMessage((io.a2a.grpc.SendMessageRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_SEND_STREAMING_MESSAGE: + serviceImpl.sendStreamingMessage((io.a2a.grpc.SendMessageRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_TASK: + serviceImpl.getTask((io.a2a.grpc.GetTaskRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_CANCEL_TASK: + serviceImpl.cancelTask((io.a2a.grpc.CancelTaskRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_TASK_SUBSCRIPTION: + serviceImpl.taskSubscription((io.a2a.grpc.TaskSubscriptionRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG: + serviceImpl.createTaskPushNotificationConfig((io.a2a.grpc.CreateTaskPushNotificationConfigRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_TASK_PUSH_NOTIFICATION_CONFIG: + serviceImpl.getTaskPushNotificationConfig((io.a2a.grpc.GetTaskPushNotificationConfigRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_LIST_TASK_PUSH_NOTIFICATION_CONFIG: + serviceImpl.listTaskPushNotificationConfig((io.a2a.grpc.ListTaskPushNotificationConfigRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_AGENT_CARD: + serviceImpl.getAgentCard((io.a2a.grpc.GetAgentCardRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_DELETE_TASK_PUSH_NOTIFICATION_CONFIG: + serviceImpl.deleteTaskPushNotificationConfig((io.a2a.grpc.DeleteTaskPushNotificationConfigRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + default: + throw new AssertionError(); + } + } + } + + public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getSendMessageMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.SendMessageRequest, + io.a2a.grpc.SendMessageResponse>( + service, METHODID_SEND_MESSAGE))) + .addMethod( + getSendStreamingMessageMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers< + io.a2a.grpc.SendMessageRequest, + io.a2a.grpc.StreamResponse>( + service, METHODID_SEND_STREAMING_MESSAGE))) + .addMethod( + getGetTaskMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.GetTaskRequest, + io.a2a.grpc.Task>( + service, METHODID_GET_TASK))) + .addMethod( + getCancelTaskMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.CancelTaskRequest, + io.a2a.grpc.Task>( + service, METHODID_CANCEL_TASK))) + .addMethod( + getTaskSubscriptionMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers< + io.a2a.grpc.TaskSubscriptionRequest, + io.a2a.grpc.StreamResponse>( + service, METHODID_TASK_SUBSCRIPTION))) + .addMethod( + getCreateTaskPushNotificationConfigMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.CreateTaskPushNotificationConfigRequest, + io.a2a.grpc.TaskPushNotificationConfig>( + service, METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG))) + .addMethod( + getGetTaskPushNotificationConfigMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.GetTaskPushNotificationConfigRequest, + io.a2a.grpc.TaskPushNotificationConfig>( + service, METHODID_GET_TASK_PUSH_NOTIFICATION_CONFIG))) + .addMethod( + getListTaskPushNotificationConfigMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.ListTaskPushNotificationConfigRequest, + io.a2a.grpc.ListTaskPushNotificationConfigResponse>( + service, METHODID_LIST_TASK_PUSH_NOTIFICATION_CONFIG))) + .addMethod( + getGetAgentCardMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.GetAgentCardRequest, + io.a2a.grpc.AgentCard>( + service, METHODID_GET_AGENT_CARD))) + .addMethod( + getDeleteTaskPushNotificationConfigMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest, + com.google.protobuf.Empty>( + service, METHODID_DELETE_TASK_PUSH_NOTIFICATION_CONFIG))) + .build(); + } + + private static abstract class A2AServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + A2AServiceBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return io.a2a.grpc.A2A.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("A2AService"); + } + } + + private static final class A2AServiceFileDescriptorSupplier + extends A2AServiceBaseDescriptorSupplier { + A2AServiceFileDescriptorSupplier() {} + } + + private static final class A2AServiceMethodDescriptorSupplier + extends A2AServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final java.lang.String methodName; + + A2AServiceMethodDescriptorSupplier(java.lang.String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (A2AServiceGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new A2AServiceFileDescriptorSupplier()) + .addMethod(getSendMessageMethod()) + .addMethod(getSendStreamingMessageMethod()) + .addMethod(getGetTaskMethod()) + .addMethod(getCancelTaskMethod()) + .addMethod(getTaskSubscriptionMethod()) + .addMethod(getCreateTaskPushNotificationConfigMethod()) + .addMethod(getGetTaskPushNotificationConfigMethod()) + .addMethod(getListTaskPushNotificationConfigMethod()) + .addMethod(getGetAgentCardMethod()) + .addMethod(getDeleteTaskPushNotificationConfigMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java b/grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java new file mode 100644 index 000000000..f2a9778d6 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java @@ -0,0 +1,858 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.APIKeySecurityScheme} + */ +@com.google.protobuf.Generated +public final class APIKeySecurityScheme extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.APIKeySecurityScheme) + APIKeySecuritySchemeOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + APIKeySecurityScheme.class.getName()); + } + // Use APIKeySecurityScheme.newBuilder() to construct. + private APIKeySecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private APIKeySecurityScheme() { + description_ = ""; + location_ = ""; + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_APIKeySecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.APIKeySecurityScheme.class, io.a2a.grpc.APIKeySecurityScheme.Builder.class); + } + + public static final int DESCRIPTION_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int LOCATION_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object location_ = ""; + /** + *
+   * Location of the API key, valid values are "query", "header", or "cookie"
+   * 
+ * + * string location = 2 [json_name = "location"]; + * @return The location. + */ + @java.lang.Override + public java.lang.String getLocation() { + java.lang.Object ref = location_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + location_ = s; + return s; + } + } + /** + *
+   * Location of the API key, valid values are "query", "header", or "cookie"
+   * 
+ * + * string location = 2 [json_name = "location"]; + * @return The bytes for location. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getLocationBytes() { + java.lang.Object ref = location_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + location_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NAME_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * Name of the header, query or cookie parameter to be used.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * Name of the header, query or cookie parameter to be used.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(location_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, location_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, name_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(location_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, location_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, name_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.APIKeySecurityScheme)) { + return super.equals(obj); + } + io.a2a.grpc.APIKeySecurityScheme other = (io.a2a.grpc.APIKeySecurityScheme) obj; + + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getLocation() + .equals(other.getLocation())) return false; + if (!getName() + .equals(other.getName())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + hash = (37 * hash) + LOCATION_FIELD_NUMBER; + hash = (53 * hash) + getLocation().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.APIKeySecurityScheme parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.APIKeySecurityScheme parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.APIKeySecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.APIKeySecurityScheme prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.APIKeySecurityScheme} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.APIKeySecurityScheme) + io.a2a.grpc.APIKeySecuritySchemeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_APIKeySecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.APIKeySecurityScheme.class, io.a2a.grpc.APIKeySecurityScheme.Builder.class); + } + + // Construct using io.a2a.grpc.APIKeySecurityScheme.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + description_ = ""; + location_ = ""; + name_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_APIKeySecurityScheme_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.APIKeySecurityScheme getDefaultInstanceForType() { + return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.APIKeySecurityScheme build() { + io.a2a.grpc.APIKeySecurityScheme result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.APIKeySecurityScheme buildPartial() { + io.a2a.grpc.APIKeySecurityScheme result = new io.a2a.grpc.APIKeySecurityScheme(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.APIKeySecurityScheme result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.description_ = description_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.location_ = location_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.name_ = name_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.APIKeySecurityScheme) { + return mergeFrom((io.a2a.grpc.APIKeySecurityScheme)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.APIKeySecurityScheme other) { + if (other == io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance()) return this; + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getLocation().isEmpty()) { + location_ = other.location_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000004; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + location_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object description_ = ""; + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object location_ = ""; + /** + *
+     * Location of the API key, valid values are "query", "header", or "cookie"
+     * 
+ * + * string location = 2 [json_name = "location"]; + * @return The location. + */ + public java.lang.String getLocation() { + java.lang.Object ref = location_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + location_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Location of the API key, valid values are "query", "header", or "cookie"
+     * 
+ * + * string location = 2 [json_name = "location"]; + * @return The bytes for location. + */ + public com.google.protobuf.ByteString + getLocationBytes() { + java.lang.Object ref = location_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + location_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Location of the API key, valid values are "query", "header", or "cookie"
+     * 
+ * + * string location = 2 [json_name = "location"]; + * @param value The location to set. + * @return This builder for chaining. + */ + public Builder setLocation( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + location_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Location of the API key, valid values are "query", "header", or "cookie"
+     * 
+ * + * string location = 2 [json_name = "location"]; + * @return This builder for chaining. + */ + public Builder clearLocation() { + location_ = getDefaultInstance().getLocation(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * Location of the API key, valid values are "query", "header", or "cookie"
+     * 
+ * + * string location = 2 [json_name = "location"]; + * @param value The bytes for location to set. + * @return This builder for chaining. + */ + public Builder setLocationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + location_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + *
+     * Name of the header, query or cookie parameter to be used.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Name of the header, query or cookie parameter to be used.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Name of the header, query or cookie parameter to be used.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Name of the header, query or cookie parameter to be used.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * Name of the header, query or cookie parameter to be used.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.APIKeySecurityScheme) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.APIKeySecurityScheme) + private static final io.a2a.grpc.APIKeySecurityScheme DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.APIKeySecurityScheme(); + } + + public static io.a2a.grpc.APIKeySecurityScheme getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public APIKeySecurityScheme parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.APIKeySecurityScheme getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java new file mode 100644 index 000000000..ec893f4f8 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java @@ -0,0 +1,72 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface APIKeySecuritySchemeOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.APIKeySecurityScheme) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * Location of the API key, valid values are "query", "header", or "cookie"
+   * 
+ * + * string location = 2 [json_name = "location"]; + * @return The location. + */ + java.lang.String getLocation(); + /** + *
+   * Location of the API key, valid values are "query", "header", or "cookie"
+   * 
+ * + * string location = 2 [json_name = "location"]; + * @return The bytes for location. + */ + com.google.protobuf.ByteString + getLocationBytes(); + + /** + *
+   * Name of the header, query or cookie parameter to be used.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * Name of the header, query or cookie parameter to be used.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java new file mode 100644 index 000000000..ca67660d7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -0,0 +1,986 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Defines the A2A feature set supported by the agent
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentCapabilities} + */ +@com.google.protobuf.Generated +public final class AgentCapabilities extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentCapabilities) + AgentCapabilitiesOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentCapabilities.class.getName()); + } + // Use AgentCapabilities.newBuilder() to construct. + private AgentCapabilities(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentCapabilities() { + extensions_ = java.util.Collections.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCapabilities_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentCapabilities.class, io.a2a.grpc.AgentCapabilities.Builder.class); + } + + public static final int STREAMING_FIELD_NUMBER = 1; + private boolean streaming_ = false; + /** + *
+   * If the agent will support streaming responses
+   * 
+ * + * bool streaming = 1 [json_name = "streaming"]; + * @return The streaming. + */ + @java.lang.Override + public boolean getStreaming() { + return streaming_; + } + + public static final int PUSH_NOTIFICATIONS_FIELD_NUMBER = 2; + private boolean pushNotifications_ = false; + /** + *
+   * If the agent can send push notifications to the clients webhook
+   * 
+ * + * bool push_notifications = 2 [json_name = "pushNotifications"]; + * @return The pushNotifications. + */ + @java.lang.Override + public boolean getPushNotifications() { + return pushNotifications_; + } + + public static final int EXTENSIONS_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private java.util.List extensions_; + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + @java.lang.Override + public java.util.List getExtensionsList() { + return extensions_; + } + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + @java.lang.Override + public java.util.List + getExtensionsOrBuilderList() { + return extensions_; + } + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + @java.lang.Override + public int getExtensionsCount() { + return extensions_.size(); + } + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentExtension getExtensions(int index) { + return extensions_.get(index); + } + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( + int index) { + return extensions_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (streaming_ != false) { + output.writeBool(1, streaming_); + } + if (pushNotifications_ != false) { + output.writeBool(2, pushNotifications_); + } + for (int i = 0; i < extensions_.size(); i++) { + output.writeMessage(3, extensions_.get(i)); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (streaming_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(1, streaming_); + } + if (pushNotifications_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, pushNotifications_); + } + for (int i = 0; i < extensions_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, extensions_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentCapabilities)) { + return super.equals(obj); + } + io.a2a.grpc.AgentCapabilities other = (io.a2a.grpc.AgentCapabilities) obj; + + if (getStreaming() + != other.getStreaming()) return false; + if (getPushNotifications() + != other.getPushNotifications()) return false; + if (!getExtensionsList() + .equals(other.getExtensionsList())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + STREAMING_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getStreaming()); + hash = (37 * hash) + PUSH_NOTIFICATIONS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getPushNotifications()); + if (getExtensionsCount() > 0) { + hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER; + hash = (53 * hash) + getExtensionsList().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentCapabilities parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCapabilities parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCapabilities parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentCapabilities parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentCapabilities parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentCapabilities parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentCapabilities prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Defines the A2A feature set supported by the agent
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentCapabilities} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentCapabilities) + io.a2a.grpc.AgentCapabilitiesOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCapabilities_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentCapabilities.class, io.a2a.grpc.AgentCapabilities.Builder.class); + } + + // Construct using io.a2a.grpc.AgentCapabilities.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + streaming_ = false; + pushNotifications_ = false; + if (extensionsBuilder_ == null) { + extensions_ = java.util.Collections.emptyList(); + } else { + extensions_ = null; + extensionsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCapabilities_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentCapabilities getDefaultInstanceForType() { + return io.a2a.grpc.AgentCapabilities.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentCapabilities build() { + io.a2a.grpc.AgentCapabilities result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentCapabilities buildPartial() { + io.a2a.grpc.AgentCapabilities result = new io.a2a.grpc.AgentCapabilities(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.AgentCapabilities result) { + if (extensionsBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0)) { + extensions_ = java.util.Collections.unmodifiableList(extensions_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.extensions_ = extensions_; + } else { + result.extensions_ = extensionsBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.AgentCapabilities result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.streaming_ = streaming_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.pushNotifications_ = pushNotifications_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentCapabilities) { + return mergeFrom((io.a2a.grpc.AgentCapabilities)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentCapabilities other) { + if (other == io.a2a.grpc.AgentCapabilities.getDefaultInstance()) return this; + if (other.getStreaming() != false) { + setStreaming(other.getStreaming()); + } + if (other.getPushNotifications() != false) { + setPushNotifications(other.getPushNotifications()); + } + if (extensionsBuilder_ == null) { + if (!other.extensions_.isEmpty()) { + if (extensions_.isEmpty()) { + extensions_ = other.extensions_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureExtensionsIsMutable(); + extensions_.addAll(other.extensions_); + } + onChanged(); + } + } else { + if (!other.extensions_.isEmpty()) { + if (extensionsBuilder_.isEmpty()) { + extensionsBuilder_.dispose(); + extensionsBuilder_ = null; + extensions_ = other.extensions_; + bitField0_ = (bitField0_ & ~0x00000004); + extensionsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetExtensionsFieldBuilder() : null; + } else { + extensionsBuilder_.addAllMessages(other.extensions_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + streaming_ = input.readBool(); + bitField0_ |= 0x00000001; + break; + } // case 8 + case 16: { + pushNotifications_ = input.readBool(); + bitField0_ |= 0x00000002; + break; + } // case 16 + case 26: { + io.a2a.grpc.AgentExtension m = + input.readMessage( + io.a2a.grpc.AgentExtension.parser(), + extensionRegistry); + if (extensionsBuilder_ == null) { + ensureExtensionsIsMutable(); + extensions_.add(m); + } else { + extensionsBuilder_.addMessage(m); + } + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private boolean streaming_ ; + /** + *
+     * If the agent will support streaming responses
+     * 
+ * + * bool streaming = 1 [json_name = "streaming"]; + * @return The streaming. + */ + @java.lang.Override + public boolean getStreaming() { + return streaming_; + } + /** + *
+     * If the agent will support streaming responses
+     * 
+ * + * bool streaming = 1 [json_name = "streaming"]; + * @param value The streaming to set. + * @return This builder for chaining. + */ + public Builder setStreaming(boolean value) { + + streaming_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * If the agent will support streaming responses
+     * 
+ * + * bool streaming = 1 [json_name = "streaming"]; + * @return This builder for chaining. + */ + public Builder clearStreaming() { + bitField0_ = (bitField0_ & ~0x00000001); + streaming_ = false; + onChanged(); + return this; + } + + private boolean pushNotifications_ ; + /** + *
+     * If the agent can send push notifications to the clients webhook
+     * 
+ * + * bool push_notifications = 2 [json_name = "pushNotifications"]; + * @return The pushNotifications. + */ + @java.lang.Override + public boolean getPushNotifications() { + return pushNotifications_; + } + /** + *
+     * If the agent can send push notifications to the clients webhook
+     * 
+ * + * bool push_notifications = 2 [json_name = "pushNotifications"]; + * @param value The pushNotifications to set. + * @return This builder for chaining. + */ + public Builder setPushNotifications(boolean value) { + + pushNotifications_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * If the agent can send push notifications to the clients webhook
+     * 
+ * + * bool push_notifications = 2 [json_name = "pushNotifications"]; + * @return This builder for chaining. + */ + public Builder clearPushNotifications() { + bitField0_ = (bitField0_ & ~0x00000002); + pushNotifications_ = false; + onChanged(); + return this; + } + + private java.util.List extensions_ = + java.util.Collections.emptyList(); + private void ensureExtensionsIsMutable() { + if (!((bitField0_ & 0x00000004) != 0)) { + extensions_ = new java.util.ArrayList(extensions_); + bitField0_ |= 0x00000004; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentExtension, io.a2a.grpc.AgentExtension.Builder, io.a2a.grpc.AgentExtensionOrBuilder> extensionsBuilder_; + + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public java.util.List getExtensionsList() { + if (extensionsBuilder_ == null) { + return java.util.Collections.unmodifiableList(extensions_); + } else { + return extensionsBuilder_.getMessageList(); + } + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public int getExtensionsCount() { + if (extensionsBuilder_ == null) { + return extensions_.size(); + } else { + return extensionsBuilder_.getCount(); + } + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public io.a2a.grpc.AgentExtension getExtensions(int index) { + if (extensionsBuilder_ == null) { + return extensions_.get(index); + } else { + return extensionsBuilder_.getMessage(index); + } + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder setExtensions( + int index, io.a2a.grpc.AgentExtension value) { + if (extensionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureExtensionsIsMutable(); + extensions_.set(index, value); + onChanged(); + } else { + extensionsBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder setExtensions( + int index, io.a2a.grpc.AgentExtension.Builder builderForValue) { + if (extensionsBuilder_ == null) { + ensureExtensionsIsMutable(); + extensions_.set(index, builderForValue.build()); + onChanged(); + } else { + extensionsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder addExtensions(io.a2a.grpc.AgentExtension value) { + if (extensionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureExtensionsIsMutable(); + extensions_.add(value); + onChanged(); + } else { + extensionsBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder addExtensions( + int index, io.a2a.grpc.AgentExtension value) { + if (extensionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureExtensionsIsMutable(); + extensions_.add(index, value); + onChanged(); + } else { + extensionsBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder addExtensions( + io.a2a.grpc.AgentExtension.Builder builderForValue) { + if (extensionsBuilder_ == null) { + ensureExtensionsIsMutable(); + extensions_.add(builderForValue.build()); + onChanged(); + } else { + extensionsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder addExtensions( + int index, io.a2a.grpc.AgentExtension.Builder builderForValue) { + if (extensionsBuilder_ == null) { + ensureExtensionsIsMutable(); + extensions_.add(index, builderForValue.build()); + onChanged(); + } else { + extensionsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder addAllExtensions( + java.lang.Iterable values) { + if (extensionsBuilder_ == null) { + ensureExtensionsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, extensions_); + onChanged(); + } else { + extensionsBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder clearExtensions() { + if (extensionsBuilder_ == null) { + extensions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + } else { + extensionsBuilder_.clear(); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public Builder removeExtensions(int index) { + if (extensionsBuilder_ == null) { + ensureExtensionsIsMutable(); + extensions_.remove(index); + onChanged(); + } else { + extensionsBuilder_.remove(index); + } + return this; + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public io.a2a.grpc.AgentExtension.Builder getExtensionsBuilder( + int index) { + return internalGetExtensionsFieldBuilder().getBuilder(index); + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( + int index) { + if (extensionsBuilder_ == null) { + return extensions_.get(index); } else { + return extensionsBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public java.util.List + getExtensionsOrBuilderList() { + if (extensionsBuilder_ != null) { + return extensionsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(extensions_); + } + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder() { + return internalGetExtensionsFieldBuilder().addBuilder( + io.a2a.grpc.AgentExtension.getDefaultInstance()); + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder( + int index) { + return internalGetExtensionsFieldBuilder().addBuilder( + index, io.a2a.grpc.AgentExtension.getDefaultInstance()); + } + /** + *
+     * Extensions supported by this agent.
+     * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + public java.util.List + getExtensionsBuilderList() { + return internalGetExtensionsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentExtension, io.a2a.grpc.AgentExtension.Builder, io.a2a.grpc.AgentExtensionOrBuilder> + internalGetExtensionsFieldBuilder() { + if (extensionsBuilder_ == null) { + extensionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentExtension, io.a2a.grpc.AgentExtension.Builder, io.a2a.grpc.AgentExtensionOrBuilder>( + extensions_, + ((bitField0_ & 0x00000004) != 0), + getParentForChildren(), + isClean()); + extensions_ = null; + } + return extensionsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCapabilities) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentCapabilities) + private static final io.a2a.grpc.AgentCapabilities DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentCapabilities(); + } + + public static io.a2a.grpc.AgentCapabilities getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentCapabilities parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentCapabilities getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java new file mode 100644 index 000000000..311841ae9 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java @@ -0,0 +1,76 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentCapabilitiesOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentCapabilities) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * If the agent will support streaming responses
+   * 
+ * + * bool streaming = 1 [json_name = "streaming"]; + * @return The streaming. + */ + boolean getStreaming(); + + /** + *
+   * If the agent can send push notifications to the clients webhook
+   * 
+ * + * bool push_notifications = 2 [json_name = "pushNotifications"]; + * @return The pushNotifications. + */ + boolean getPushNotifications(); + + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + java.util.List + getExtensionsList(); + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + io.a2a.grpc.AgentExtension getExtensions(int index); + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + int getExtensionsCount(); + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + java.util.List + getExtensionsOrBuilderList(); + /** + *
+   * Extensions supported by this agent.
+   * 
+ * + * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + */ + io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( + int index); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCard.java b/grpc/src/main/java/io/a2a/grpc/AgentCard.java new file mode 100644 index 000000000..8327b6376 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -0,0 +1,4410 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * AgentCard conveys key information:
+ * - Overall details (version, name, description, uses)
+ * - Skills; a set of actions/solutions the agent can perform
+ * - Default modalities/content types supported by the agent.
+ * - Authentication requirements
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentCard} + */ +@com.google.protobuf.Generated +public final class AgentCard extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentCard) + AgentCardOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentCard.class.getName()); + } + // Use AgentCard.newBuilder() to construct. + private AgentCard(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentCard() { + protocolVersion_ = ""; + name_ = ""; + description_ = ""; + url_ = ""; + preferredTransport_ = ""; + additionalInterfaces_ = java.util.Collections.emptyList(); + version_ = ""; + documentationUrl_ = ""; + security_ = java.util.Collections.emptyList(); + defaultInputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + defaultOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + skills_ = java.util.Collections.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCard_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 8: + return internalGetSecuritySchemes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCard_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentCard.class, io.a2a.grpc.AgentCard.Builder.class); + } + + private int bitField0_; + public static final int PROTOCOL_VERSION_FIELD_NUMBER = 16; + @SuppressWarnings("serial") + private volatile java.lang.Object protocolVersion_ = ""; + /** + *
+   * The version of the A2A protocol this agent supports.
+   * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return The protocolVersion. + */ + @java.lang.Override + public java.lang.String getProtocolVersion() { + java.lang.Object ref = protocolVersion_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protocolVersion_ = s; + return s; + } + } + /** + *
+   * The version of the A2A protocol this agent supports.
+   * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return The bytes for protocolVersion. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getProtocolVersionBytes() { + java.lang.Object ref = protocolVersion_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + protocolVersion_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * A human readable name for the agent.
+   * Example: "Recipe Agent"
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * A human readable name for the agent.
+   * Example: "Recipe Agent"
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DESCRIPTION_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * A description of the agent's domain of action/solution space.
+   * Example: "Agent that helps users with recipes and cooking."
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * A description of the agent's domain of action/solution space.
+   * Example: "Agent that helps users with recipes and cooking."
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int URL_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object url_ = ""; + /** + *
+   * A URL to the address the agent is hosted at. This represents the
+   * preferred endpoint as declared by the agent.
+   * 
+ * + * string url = 3 [json_name = "url"]; + * @return The url. + */ + @java.lang.Override + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } + } + /** + *
+   * A URL to the address the agent is hosted at. This represents the
+   * preferred endpoint as declared by the agent.
+   * 
+ * + * string url = 3 [json_name = "url"]; + * @return The bytes for url. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PREFERRED_TRANSPORT_FIELD_NUMBER = 14; + @SuppressWarnings("serial") + private volatile java.lang.Object preferredTransport_ = ""; + /** + *
+   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return The preferredTransport. + */ + @java.lang.Override + public java.lang.String getPreferredTransport() { + java.lang.Object ref = preferredTransport_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + preferredTransport_ = s; + return s; + } + } + /** + *
+   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return The bytes for preferredTransport. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getPreferredTransportBytes() { + java.lang.Object ref = preferredTransport_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + preferredTransport_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ADDITIONAL_INTERFACES_FIELD_NUMBER = 15; + @SuppressWarnings("serial") + private java.util.List additionalInterfaces_; + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + @java.lang.Override + public java.util.List getAdditionalInterfacesList() { + return additionalInterfaces_; + } + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + @java.lang.Override + public java.util.List + getAdditionalInterfacesOrBuilderList() { + return additionalInterfaces_; + } + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + @java.lang.Override + public int getAdditionalInterfacesCount() { + return additionalInterfaces_.size(); + } + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { + return additionalInterfaces_.get(index); + } + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + int index) { + return additionalInterfaces_.get(index); + } + + public static final int PROVIDER_FIELD_NUMBER = 4; + private io.a2a.grpc.AgentProvider provider_; + /** + *
+   * The service provider of the agent.
+   * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * @return Whether the provider field is set. + */ + @java.lang.Override + public boolean hasProvider() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The service provider of the agent.
+   * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * @return The provider. + */ + @java.lang.Override + public io.a2a.grpc.AgentProvider getProvider() { + return provider_ == null ? io.a2a.grpc.AgentProvider.getDefaultInstance() : provider_; + } + /** + *
+   * The service provider of the agent.
+   * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { + return provider_ == null ? io.a2a.grpc.AgentProvider.getDefaultInstance() : provider_; + } + + public static final int VERSION_FIELD_NUMBER = 5; + @SuppressWarnings("serial") + private volatile java.lang.Object version_ = ""; + /** + *
+   * The version of the agent.
+   * Example: "1.0.0"
+   * 
+ * + * string version = 5 [json_name = "version"]; + * @return The version. + */ + @java.lang.Override + public java.lang.String getVersion() { + java.lang.Object ref = version_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + version_ = s; + return s; + } + } + /** + *
+   * The version of the agent.
+   * Example: "1.0.0"
+   * 
+ * + * string version = 5 [json_name = "version"]; + * @return The bytes for version. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getVersionBytes() { + java.lang.Object ref = version_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + version_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DOCUMENTATION_URL_FIELD_NUMBER = 6; + @SuppressWarnings("serial") + private volatile java.lang.Object documentationUrl_ = ""; + /** + *
+   * A url to provide additional documentation about the agent.
+   * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return The documentationUrl. + */ + @java.lang.Override + public java.lang.String getDocumentationUrl() { + java.lang.Object ref = documentationUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + documentationUrl_ = s; + return s; + } + } + /** + *
+   * A url to provide additional documentation about the agent.
+   * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return The bytes for documentationUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDocumentationUrlBytes() { + java.lang.Object ref = documentationUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + documentationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CAPABILITIES_FIELD_NUMBER = 7; + private io.a2a.grpc.AgentCapabilities capabilities_; + /** + *
+   * A2A Capability set supported by the agent.
+   * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * @return Whether the capabilities field is set. + */ + @java.lang.Override + public boolean hasCapabilities() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * A2A Capability set supported by the agent.
+   * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * @return The capabilities. + */ + @java.lang.Override + public io.a2a.grpc.AgentCapabilities getCapabilities() { + return capabilities_ == null ? io.a2a.grpc.AgentCapabilities.getDefaultInstance() : capabilities_; + } + /** + *
+   * A2A Capability set supported by the agent.
+   * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { + return capabilities_ == null ? io.a2a.grpc.AgentCapabilities.getDefaultInstance() : capabilities_; + } + + public static final int SECURITY_SCHEMES_FIELD_NUMBER = 8; + private static final class SecuritySchemesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, io.a2a.grpc.SecurityScheme> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.MESSAGE, + io.a2a.grpc.SecurityScheme.getDefaultInstance()); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, io.a2a.grpc.SecurityScheme> securitySchemes_; + private com.google.protobuf.MapField + internalGetSecuritySchemes() { + if (securitySchemes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + SecuritySchemesDefaultEntryHolder.defaultEntry); + } + return securitySchemes_; + } + public int getSecuritySchemesCount() { + return internalGetSecuritySchemes().getMap().size(); + } + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public boolean containsSecuritySchemes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetSecuritySchemes().getMap().containsKey(key); + } + /** + * Use {@link #getSecuritySchemesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getSecuritySchemes() { + return getSecuritySchemesMap(); + } + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public java.util.Map getSecuritySchemesMap() { + return internalGetSecuritySchemes().getMap(); + } + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public /* nullable */ +io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( + java.lang.String key, + /* nullable */ +io.a2a.grpc.SecurityScheme defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetSecuritySchemes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetSecuritySchemes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + public static final int SECURITY_FIELD_NUMBER = 9; + @SuppressWarnings("serial") + private java.util.List security_; + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + @java.lang.Override + public java.util.List getSecurityList() { + return security_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + @java.lang.Override + public java.util.List + getSecurityOrBuilderList() { + return security_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + @java.lang.Override + public int getSecurityCount() { + return security_.size(); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + @java.lang.Override + public io.a2a.grpc.Security getSecurity(int index) { + return security_.get(index); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + @java.lang.Override + public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + int index) { + return security_.get(index); + } + + public static final int DEFAULT_INPUT_MODES_FIELD_NUMBER = 10; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList defaultInputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return A list containing the defaultInputModes. + */ + public com.google.protobuf.ProtocolStringList + getDefaultInputModesList() { + return defaultInputModes_; + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return The count of defaultInputModes. + */ + public int getDefaultInputModesCount() { + return defaultInputModes_.size(); + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index of the element to return. + * @return The defaultInputModes at the given index. + */ + public java.lang.String getDefaultInputModes(int index) { + return defaultInputModes_.get(index); + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index of the value to return. + * @return The bytes of the defaultInputModes at the given index. + */ + public com.google.protobuf.ByteString + getDefaultInputModesBytes(int index) { + return defaultInputModes_.getByteString(index); + } + + public static final int DEFAULT_OUTPUT_MODES_FIELD_NUMBER = 11; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList defaultOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return A list containing the defaultOutputModes. + */ + public com.google.protobuf.ProtocolStringList + getDefaultOutputModesList() { + return defaultOutputModes_; + } + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return The count of defaultOutputModes. + */ + public int getDefaultOutputModesCount() { + return defaultOutputModes_.size(); + } + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index of the element to return. + * @return The defaultOutputModes at the given index. + */ + public java.lang.String getDefaultOutputModes(int index) { + return defaultOutputModes_.get(index); + } + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index of the value to return. + * @return The bytes of the defaultOutputModes at the given index. + */ + public com.google.protobuf.ByteString + getDefaultOutputModesBytes(int index) { + return defaultOutputModes_.getByteString(index); + } + + public static final int SKILLS_FIELD_NUMBER = 12; + @SuppressWarnings("serial") + private java.util.List skills_; + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + @java.lang.Override + public java.util.List getSkillsList() { + return skills_; + } + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + @java.lang.Override + public java.util.List + getSkillsOrBuilderList() { + return skills_; + } + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + @java.lang.Override + public int getSkillsCount() { + return skills_.size(); + } + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentSkill getSkills(int index) { + return skills_.get(index); + } + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + @java.lang.Override + public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( + int index) { + return skills_.get(index); + } + + public static final int SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER = 13; + private boolean supportsAuthenticatedExtendedCard_ = false; + /** + *
+   * Whether the agent supports providing an extended agent card when
+   * the user is authenticated, i.e. is the card from .well-known
+   * different than the card from GetAgentCard.
+   * 
+ * + * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * @return The supportsAuthenticatedExtendedCard. + */ + @java.lang.Override + public boolean getSupportsAuthenticatedExtendedCard() { + return supportsAuthenticatedExtendedCard_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, url_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(4, getProvider()); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(version_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 5, version_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(documentationUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 6, documentationUrl_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeMessage(7, getCapabilities()); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetSecuritySchemes(), + SecuritySchemesDefaultEntryHolder.defaultEntry, + 8); + for (int i = 0; i < security_.size(); i++) { + output.writeMessage(9, security_.get(i)); + } + for (int i = 0; i < defaultInputModes_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 10, defaultInputModes_.getRaw(i)); + } + for (int i = 0; i < defaultOutputModes_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 11, defaultOutputModes_.getRaw(i)); + } + for (int i = 0; i < skills_.size(); i++) { + output.writeMessage(12, skills_.get(i)); + } + if (supportsAuthenticatedExtendedCard_ != false) { + output.writeBool(13, supportsAuthenticatedExtendedCard_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(preferredTransport_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 14, preferredTransport_); + } + for (int i = 0; i < additionalInterfaces_.size(); i++) { + output.writeMessage(15, additionalInterfaces_.get(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 16, protocolVersion_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, url_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, getProvider()); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(version_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(5, version_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(documentationUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(6, documentationUrl_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(7, getCapabilities()); + } + for (java.util.Map.Entry entry + : internalGetSecuritySchemes().getMap().entrySet()) { + com.google.protobuf.MapEntry + securitySchemes__ = SecuritySchemesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, securitySchemes__); + } + for (int i = 0; i < security_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(9, security_.get(i)); + } + { + int dataSize = 0; + for (int i = 0; i < defaultInputModes_.size(); i++) { + dataSize += computeStringSizeNoTag(defaultInputModes_.getRaw(i)); + } + size += dataSize; + size += 1 * getDefaultInputModesList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < defaultOutputModes_.size(); i++) { + dataSize += computeStringSizeNoTag(defaultOutputModes_.getRaw(i)); + } + size += dataSize; + size += 1 * getDefaultOutputModesList().size(); + } + for (int i = 0; i < skills_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(12, skills_.get(i)); + } + if (supportsAuthenticatedExtendedCard_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(13, supportsAuthenticatedExtendedCard_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(preferredTransport_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(14, preferredTransport_); + } + for (int i = 0; i < additionalInterfaces_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(15, additionalInterfaces_.get(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(16, protocolVersion_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentCard)) { + return super.equals(obj); + } + io.a2a.grpc.AgentCard other = (io.a2a.grpc.AgentCard) obj; + + if (!getProtocolVersion() + .equals(other.getProtocolVersion())) return false; + if (!getName() + .equals(other.getName())) return false; + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getUrl() + .equals(other.getUrl())) return false; + if (!getPreferredTransport() + .equals(other.getPreferredTransport())) return false; + if (!getAdditionalInterfacesList() + .equals(other.getAdditionalInterfacesList())) return false; + if (hasProvider() != other.hasProvider()) return false; + if (hasProvider()) { + if (!getProvider() + .equals(other.getProvider())) return false; + } + if (!getVersion() + .equals(other.getVersion())) return false; + if (!getDocumentationUrl() + .equals(other.getDocumentationUrl())) return false; + if (hasCapabilities() != other.hasCapabilities()) return false; + if (hasCapabilities()) { + if (!getCapabilities() + .equals(other.getCapabilities())) return false; + } + if (!internalGetSecuritySchemes().equals( + other.internalGetSecuritySchemes())) return false; + if (!getSecurityList() + .equals(other.getSecurityList())) return false; + if (!getDefaultInputModesList() + .equals(other.getDefaultInputModesList())) return false; + if (!getDefaultOutputModesList() + .equals(other.getDefaultOutputModesList())) return false; + if (!getSkillsList() + .equals(other.getSkillsList())) return false; + if (getSupportsAuthenticatedExtendedCard() + != other.getSupportsAuthenticatedExtendedCard()) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + PROTOCOL_VERSION_FIELD_NUMBER; + hash = (53 * hash) + getProtocolVersion().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + hash = (37 * hash) + URL_FIELD_NUMBER; + hash = (53 * hash) + getUrl().hashCode(); + hash = (37 * hash) + PREFERRED_TRANSPORT_FIELD_NUMBER; + hash = (53 * hash) + getPreferredTransport().hashCode(); + if (getAdditionalInterfacesCount() > 0) { + hash = (37 * hash) + ADDITIONAL_INTERFACES_FIELD_NUMBER; + hash = (53 * hash) + getAdditionalInterfacesList().hashCode(); + } + if (hasProvider()) { + hash = (37 * hash) + PROVIDER_FIELD_NUMBER; + hash = (53 * hash) + getProvider().hashCode(); + } + hash = (37 * hash) + VERSION_FIELD_NUMBER; + hash = (53 * hash) + getVersion().hashCode(); + hash = (37 * hash) + DOCUMENTATION_URL_FIELD_NUMBER; + hash = (53 * hash) + getDocumentationUrl().hashCode(); + if (hasCapabilities()) { + hash = (37 * hash) + CAPABILITIES_FIELD_NUMBER; + hash = (53 * hash) + getCapabilities().hashCode(); + } + if (!internalGetSecuritySchemes().getMap().isEmpty()) { + hash = (37 * hash) + SECURITY_SCHEMES_FIELD_NUMBER; + hash = (53 * hash) + internalGetSecuritySchemes().hashCode(); + } + if (getSecurityCount() > 0) { + hash = (37 * hash) + SECURITY_FIELD_NUMBER; + hash = (53 * hash) + getSecurityList().hashCode(); + } + if (getDefaultInputModesCount() > 0) { + hash = (37 * hash) + DEFAULT_INPUT_MODES_FIELD_NUMBER; + hash = (53 * hash) + getDefaultInputModesList().hashCode(); + } + if (getDefaultOutputModesCount() > 0) { + hash = (37 * hash) + DEFAULT_OUTPUT_MODES_FIELD_NUMBER; + hash = (53 * hash) + getDefaultOutputModesList().hashCode(); + } + if (getSkillsCount() > 0) { + hash = (37 * hash) + SKILLS_FIELD_NUMBER; + hash = (53 * hash) + getSkillsList().hashCode(); + } + hash = (37 * hash) + SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getSupportsAuthenticatedExtendedCard()); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentCard parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCard parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCard parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCard parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCard parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCard parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCard parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentCard parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentCard parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentCard parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentCard parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentCard parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentCard prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * AgentCard conveys key information:
+   * - Overall details (version, name, description, uses)
+   * - Skills; a set of actions/solutions the agent can perform
+   * - Default modalities/content types supported by the agent.
+   * - Authentication requirements
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentCard} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentCard) + io.a2a.grpc.AgentCardOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCard_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 8: + return internalGetSecuritySchemes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 8: + return internalGetMutableSecuritySchemes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCard_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentCard.class, io.a2a.grpc.AgentCard.Builder.class); + } + + // Construct using io.a2a.grpc.AgentCard.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetAdditionalInterfacesFieldBuilder(); + internalGetProviderFieldBuilder(); + internalGetCapabilitiesFieldBuilder(); + internalGetSecurityFieldBuilder(); + internalGetSkillsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + protocolVersion_ = ""; + name_ = ""; + description_ = ""; + url_ = ""; + preferredTransport_ = ""; + if (additionalInterfacesBuilder_ == null) { + additionalInterfaces_ = java.util.Collections.emptyList(); + } else { + additionalInterfaces_ = null; + additionalInterfacesBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000020); + provider_ = null; + if (providerBuilder_ != null) { + providerBuilder_.dispose(); + providerBuilder_ = null; + } + version_ = ""; + documentationUrl_ = ""; + capabilities_ = null; + if (capabilitiesBuilder_ != null) { + capabilitiesBuilder_.dispose(); + capabilitiesBuilder_ = null; + } + internalGetMutableSecuritySchemes().clear(); + if (securityBuilder_ == null) { + security_ = java.util.Collections.emptyList(); + } else { + security_ = null; + securityBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000800); + defaultInputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + defaultOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + if (skillsBuilder_ == null) { + skills_ = java.util.Collections.emptyList(); + } else { + skills_ = null; + skillsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00004000); + supportsAuthenticatedExtendedCard_ = false; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCard_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentCard getDefaultInstanceForType() { + return io.a2a.grpc.AgentCard.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentCard build() { + io.a2a.grpc.AgentCard result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentCard buildPartial() { + io.a2a.grpc.AgentCard result = new io.a2a.grpc.AgentCard(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) { + if (additionalInterfacesBuilder_ == null) { + if (((bitField0_ & 0x00000020) != 0)) { + additionalInterfaces_ = java.util.Collections.unmodifiableList(additionalInterfaces_); + bitField0_ = (bitField0_ & ~0x00000020); + } + result.additionalInterfaces_ = additionalInterfaces_; + } else { + result.additionalInterfaces_ = additionalInterfacesBuilder_.build(); + } + if (securityBuilder_ == null) { + if (((bitField0_ & 0x00000800) != 0)) { + security_ = java.util.Collections.unmodifiableList(security_); + bitField0_ = (bitField0_ & ~0x00000800); + } + result.security_ = security_; + } else { + result.security_ = securityBuilder_.build(); + } + if (skillsBuilder_ == null) { + if (((bitField0_ & 0x00004000) != 0)) { + skills_ = java.util.Collections.unmodifiableList(skills_); + bitField0_ = (bitField0_ & ~0x00004000); + } + result.skills_ = skills_; + } else { + result.skills_ = skillsBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.AgentCard result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.protocolVersion_ = protocolVersion_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.name_ = name_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.description_ = description_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.url_ = url_; + } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.preferredTransport_ = preferredTransport_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000040) != 0)) { + result.provider_ = providerBuilder_ == null + ? provider_ + : providerBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000080) != 0)) { + result.version_ = version_; + } + if (((from_bitField0_ & 0x00000100) != 0)) { + result.documentationUrl_ = documentationUrl_; + } + if (((from_bitField0_ & 0x00000200) != 0)) { + result.capabilities_ = capabilitiesBuilder_ == null + ? capabilities_ + : capabilitiesBuilder_.build(); + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000400) != 0)) { + result.securitySchemes_ = internalGetSecuritySchemes().build(SecuritySchemesDefaultEntryHolder.defaultEntry); + } + if (((from_bitField0_ & 0x00001000) != 0)) { + defaultInputModes_.makeImmutable(); + result.defaultInputModes_ = defaultInputModes_; + } + if (((from_bitField0_ & 0x00002000) != 0)) { + defaultOutputModes_.makeImmutable(); + result.defaultOutputModes_ = defaultOutputModes_; + } + if (((from_bitField0_ & 0x00008000) != 0)) { + result.supportsAuthenticatedExtendedCard_ = supportsAuthenticatedExtendedCard_; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentCard) { + return mergeFrom((io.a2a.grpc.AgentCard)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentCard other) { + if (other == io.a2a.grpc.AgentCard.getDefaultInstance()) return this; + if (!other.getProtocolVersion().isEmpty()) { + protocolVersion_ = other.protocolVersion_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (!other.getUrl().isEmpty()) { + url_ = other.url_; + bitField0_ |= 0x00000008; + onChanged(); + } + if (!other.getPreferredTransport().isEmpty()) { + preferredTransport_ = other.preferredTransport_; + bitField0_ |= 0x00000010; + onChanged(); + } + if (additionalInterfacesBuilder_ == null) { + if (!other.additionalInterfaces_.isEmpty()) { + if (additionalInterfaces_.isEmpty()) { + additionalInterfaces_ = other.additionalInterfaces_; + bitField0_ = (bitField0_ & ~0x00000020); + } else { + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.addAll(other.additionalInterfaces_); + } + onChanged(); + } + } else { + if (!other.additionalInterfaces_.isEmpty()) { + if (additionalInterfacesBuilder_.isEmpty()) { + additionalInterfacesBuilder_.dispose(); + additionalInterfacesBuilder_ = null; + additionalInterfaces_ = other.additionalInterfaces_; + bitField0_ = (bitField0_ & ~0x00000020); + additionalInterfacesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetAdditionalInterfacesFieldBuilder() : null; + } else { + additionalInterfacesBuilder_.addAllMessages(other.additionalInterfaces_); + } + } + } + if (other.hasProvider()) { + mergeProvider(other.getProvider()); + } + if (!other.getVersion().isEmpty()) { + version_ = other.version_; + bitField0_ |= 0x00000080; + onChanged(); + } + if (!other.getDocumentationUrl().isEmpty()) { + documentationUrl_ = other.documentationUrl_; + bitField0_ |= 0x00000100; + onChanged(); + } + if (other.hasCapabilities()) { + mergeCapabilities(other.getCapabilities()); + } + internalGetMutableSecuritySchemes().mergeFrom( + other.internalGetSecuritySchemes()); + bitField0_ |= 0x00000400; + if (securityBuilder_ == null) { + if (!other.security_.isEmpty()) { + if (security_.isEmpty()) { + security_ = other.security_; + bitField0_ = (bitField0_ & ~0x00000800); + } else { + ensureSecurityIsMutable(); + security_.addAll(other.security_); + } + onChanged(); + } + } else { + if (!other.security_.isEmpty()) { + if (securityBuilder_.isEmpty()) { + securityBuilder_.dispose(); + securityBuilder_ = null; + security_ = other.security_; + bitField0_ = (bitField0_ & ~0x00000800); + securityBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetSecurityFieldBuilder() : null; + } else { + securityBuilder_.addAllMessages(other.security_); + } + } + } + if (!other.defaultInputModes_.isEmpty()) { + if (defaultInputModes_.isEmpty()) { + defaultInputModes_ = other.defaultInputModes_; + bitField0_ |= 0x00001000; + } else { + ensureDefaultInputModesIsMutable(); + defaultInputModes_.addAll(other.defaultInputModes_); + } + onChanged(); + } + if (!other.defaultOutputModes_.isEmpty()) { + if (defaultOutputModes_.isEmpty()) { + defaultOutputModes_ = other.defaultOutputModes_; + bitField0_ |= 0x00002000; + } else { + ensureDefaultOutputModesIsMutable(); + defaultOutputModes_.addAll(other.defaultOutputModes_); + } + onChanged(); + } + if (skillsBuilder_ == null) { + if (!other.skills_.isEmpty()) { + if (skills_.isEmpty()) { + skills_ = other.skills_; + bitField0_ = (bitField0_ & ~0x00004000); + } else { + ensureSkillsIsMutable(); + skills_.addAll(other.skills_); + } + onChanged(); + } + } else { + if (!other.skills_.isEmpty()) { + if (skillsBuilder_.isEmpty()) { + skillsBuilder_.dispose(); + skillsBuilder_ = null; + skills_ = other.skills_; + bitField0_ = (bitField0_ & ~0x00004000); + skillsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetSkillsFieldBuilder() : null; + } else { + skillsBuilder_.addAllMessages(other.skills_); + } + } + } + if (other.getSupportsAuthenticatedExtendedCard() != false) { + setSupportsAuthenticatedExtendedCard(other.getSupportsAuthenticatedExtendedCard()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 10 + case 18: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 18 + case 26: { + url_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000008; + break; + } // case 26 + case 34: { + input.readMessage( + internalGetProviderFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000040; + break; + } // case 34 + case 42: { + version_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000080; + break; + } // case 42 + case 50: { + documentationUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000100; + break; + } // case 50 + case 58: { + input.readMessage( + internalGetCapabilitiesFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000200; + break; + } // case 58 + case 66: { + com.google.protobuf.MapEntry + securitySchemes__ = input.readMessage( + SecuritySchemesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableSecuritySchemes().ensureBuilderMap().put( + securitySchemes__.getKey(), securitySchemes__.getValue()); + bitField0_ |= 0x00000400; + break; + } // case 66 + case 74: { + io.a2a.grpc.Security m = + input.readMessage( + io.a2a.grpc.Security.parser(), + extensionRegistry); + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.add(m); + } else { + securityBuilder_.addMessage(m); + } + break; + } // case 74 + case 82: { + java.lang.String s = input.readStringRequireUtf8(); + ensureDefaultInputModesIsMutable(); + defaultInputModes_.add(s); + break; + } // case 82 + case 90: { + java.lang.String s = input.readStringRequireUtf8(); + ensureDefaultOutputModesIsMutable(); + defaultOutputModes_.add(s); + break; + } // case 90 + case 98: { + io.a2a.grpc.AgentSkill m = + input.readMessage( + io.a2a.grpc.AgentSkill.parser(), + extensionRegistry); + if (skillsBuilder_ == null) { + ensureSkillsIsMutable(); + skills_.add(m); + } else { + skillsBuilder_.addMessage(m); + } + break; + } // case 98 + case 104: { + supportsAuthenticatedExtendedCard_ = input.readBool(); + bitField0_ |= 0x00008000; + break; + } // case 104 + case 114: { + preferredTransport_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000010; + break; + } // case 114 + case 122: { + io.a2a.grpc.AgentInterface m = + input.readMessage( + io.a2a.grpc.AgentInterface.parser(), + extensionRegistry); + if (additionalInterfacesBuilder_ == null) { + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.add(m); + } else { + additionalInterfacesBuilder_.addMessage(m); + } + break; + } // case 122 + case 130: { + protocolVersion_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 130 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object protocolVersion_ = ""; + /** + *
+     * The version of the A2A protocol this agent supports.
+     * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return The protocolVersion. + */ + public java.lang.String getProtocolVersion() { + java.lang.Object ref = protocolVersion_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protocolVersion_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The version of the A2A protocol this agent supports.
+     * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return The bytes for protocolVersion. + */ + public com.google.protobuf.ByteString + getProtocolVersionBytes() { + java.lang.Object ref = protocolVersion_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + protocolVersion_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The version of the A2A protocol this agent supports.
+     * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @param value The protocolVersion to set. + * @return This builder for chaining. + */ + public Builder setProtocolVersion( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + protocolVersion_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The version of the A2A protocol this agent supports.
+     * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return This builder for chaining. + */ + public Builder clearProtocolVersion() { + protocolVersion_ = getDefaultInstance().getProtocolVersion(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The version of the A2A protocol this agent supports.
+     * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @param value The bytes for protocolVersion to set. + * @return This builder for chaining. + */ + public Builder setProtocolVersionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + protocolVersion_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + *
+     * A human readable name for the agent.
+     * Example: "Recipe Agent"
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A human readable name for the agent.
+     * Example: "Recipe Agent"
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A human readable name for the agent.
+     * Example: "Recipe Agent"
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A human readable name for the agent.
+     * Example: "Recipe Agent"
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * A human readable name for the agent.
+     * Example: "Recipe Agent"
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object description_ = ""; + /** + *
+     * A description of the agent's domain of action/solution space.
+     * Example: "Agent that helps users with recipes and cooking."
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A description of the agent's domain of action/solution space.
+     * Example: "Agent that helps users with recipes and cooking."
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A description of the agent's domain of action/solution space.
+     * Example: "Agent that helps users with recipes and cooking."
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * A description of the agent's domain of action/solution space.
+     * Example: "Agent that helps users with recipes and cooking."
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * A description of the agent's domain of action/solution space.
+     * Example: "Agent that helps users with recipes and cooking."
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private java.lang.Object url_ = ""; + /** + *
+     * A URL to the address the agent is hosted at. This represents the
+     * preferred endpoint as declared by the agent.
+     * 
+ * + * string url = 3 [json_name = "url"]; + * @return The url. + */ + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A URL to the address the agent is hosted at. This represents the
+     * preferred endpoint as declared by the agent.
+     * 
+ * + * string url = 3 [json_name = "url"]; + * @return The bytes for url. + */ + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A URL to the address the agent is hosted at. This represents the
+     * preferred endpoint as declared by the agent.
+     * 
+ * + * string url = 3 [json_name = "url"]; + * @param value The url to set. + * @return This builder for chaining. + */ + public Builder setUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + url_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * A URL to the address the agent is hosted at. This represents the
+     * preferred endpoint as declared by the agent.
+     * 
+ * + * string url = 3 [json_name = "url"]; + * @return This builder for chaining. + */ + public Builder clearUrl() { + url_ = getDefaultInstance().getUrl(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + *
+     * A URL to the address the agent is hosted at. This represents the
+     * preferred endpoint as declared by the agent.
+     * 
+ * + * string url = 3 [json_name = "url"]; + * @param value The bytes for url to set. + * @return This builder for chaining. + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + url_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + + private java.lang.Object preferredTransport_ = ""; + /** + *
+     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return The preferredTransport. + */ + public java.lang.String getPreferredTransport() { + java.lang.Object ref = preferredTransport_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + preferredTransport_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return The bytes for preferredTransport. + */ + public com.google.protobuf.ByteString + getPreferredTransportBytes() { + java.lang.Object ref = preferredTransport_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + preferredTransport_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @param value The preferredTransport to set. + * @return This builder for chaining. + */ + public Builder setPreferredTransport( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + preferredTransport_ = value; + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return This builder for chaining. + */ + public Builder clearPreferredTransport() { + preferredTransport_ = getDefaultInstance().getPreferredTransport(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + return this; + } + /** + *
+     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @param value The bytes for preferredTransport to set. + * @return This builder for chaining. + */ + public Builder setPreferredTransportBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + preferredTransport_ = value; + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + + private java.util.List additionalInterfaces_ = + java.util.Collections.emptyList(); + private void ensureAdditionalInterfacesIsMutable() { + if (!((bitField0_ & 0x00000020) != 0)) { + additionalInterfaces_ = new java.util.ArrayList(additionalInterfaces_); + bitField0_ |= 0x00000020; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> additionalInterfacesBuilder_; + + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public java.util.List getAdditionalInterfacesList() { + if (additionalInterfacesBuilder_ == null) { + return java.util.Collections.unmodifiableList(additionalInterfaces_); + } else { + return additionalInterfacesBuilder_.getMessageList(); + } + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public int getAdditionalInterfacesCount() { + if (additionalInterfacesBuilder_ == null) { + return additionalInterfaces_.size(); + } else { + return additionalInterfacesBuilder_.getCount(); + } + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { + if (additionalInterfacesBuilder_ == null) { + return additionalInterfaces_.get(index); + } else { + return additionalInterfacesBuilder_.getMessage(index); + } + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder setAdditionalInterfaces( + int index, io.a2a.grpc.AgentInterface value) { + if (additionalInterfacesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.set(index, value); + onChanged(); + } else { + additionalInterfacesBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder setAdditionalInterfaces( + int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { + if (additionalInterfacesBuilder_ == null) { + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.set(index, builderForValue.build()); + onChanged(); + } else { + additionalInterfacesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { + if (additionalInterfacesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.add(value); + onChanged(); + } else { + additionalInterfacesBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder addAdditionalInterfaces( + int index, io.a2a.grpc.AgentInterface value) { + if (additionalInterfacesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.add(index, value); + onChanged(); + } else { + additionalInterfacesBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder addAdditionalInterfaces( + io.a2a.grpc.AgentInterface.Builder builderForValue) { + if (additionalInterfacesBuilder_ == null) { + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.add(builderForValue.build()); + onChanged(); + } else { + additionalInterfacesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder addAdditionalInterfaces( + int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { + if (additionalInterfacesBuilder_ == null) { + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.add(index, builderForValue.build()); + onChanged(); + } else { + additionalInterfacesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder addAllAdditionalInterfaces( + java.lang.Iterable values) { + if (additionalInterfacesBuilder_ == null) { + ensureAdditionalInterfacesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, additionalInterfaces_); + onChanged(); + } else { + additionalInterfacesBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder clearAdditionalInterfaces() { + if (additionalInterfacesBuilder_ == null) { + additionalInterfaces_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000020); + onChanged(); + } else { + additionalInterfacesBuilder_.clear(); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public Builder removeAdditionalInterfaces(int index) { + if (additionalInterfacesBuilder_ == null) { + ensureAdditionalInterfacesIsMutable(); + additionalInterfaces_.remove(index); + onChanged(); + } else { + additionalInterfacesBuilder_.remove(index); + } + return this; + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public io.a2a.grpc.AgentInterface.Builder getAdditionalInterfacesBuilder( + int index) { + return internalGetAdditionalInterfacesFieldBuilder().getBuilder(index); + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + int index) { + if (additionalInterfacesBuilder_ == null) { + return additionalInterfaces_.get(index); } else { + return additionalInterfacesBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public java.util.List + getAdditionalInterfacesOrBuilderList() { + if (additionalInterfacesBuilder_ != null) { + return additionalInterfacesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(additionalInterfaces_); + } + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder() { + return internalGetAdditionalInterfacesFieldBuilder().addBuilder( + io.a2a.grpc.AgentInterface.getDefaultInstance()); + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( + int index) { + return internalGetAdditionalInterfacesFieldBuilder().addBuilder( + index, io.a2a.grpc.AgentInterface.getDefaultInstance()); + } + /** + *
+     * Announcement of additional supported transports. Client can use any of
+     * the supported transports.
+     * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + public java.util.List + getAdditionalInterfacesBuilderList() { + return internalGetAdditionalInterfacesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> + internalGetAdditionalInterfacesFieldBuilder() { + if (additionalInterfacesBuilder_ == null) { + additionalInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder>( + additionalInterfaces_, + ((bitField0_ & 0x00000020) != 0), + getParentForChildren(), + isClean()); + additionalInterfaces_ = null; + } + return additionalInterfacesBuilder_; + } + + private io.a2a.grpc.AgentProvider provider_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AgentProvider, io.a2a.grpc.AgentProvider.Builder, io.a2a.grpc.AgentProviderOrBuilder> providerBuilder_; + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * @return Whether the provider field is set. + */ + public boolean hasProvider() { + return ((bitField0_ & 0x00000040) != 0); + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * @return The provider. + */ + public io.a2a.grpc.AgentProvider getProvider() { + if (providerBuilder_ == null) { + return provider_ == null ? io.a2a.grpc.AgentProvider.getDefaultInstance() : provider_; + } else { + return providerBuilder_.getMessage(); + } + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + public Builder setProvider(io.a2a.grpc.AgentProvider value) { + if (providerBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + provider_ = value; + } else { + providerBuilder_.setMessage(value); + } + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + public Builder setProvider( + io.a2a.grpc.AgentProvider.Builder builderForValue) { + if (providerBuilder_ == null) { + provider_ = builderForValue.build(); + } else { + providerBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + public Builder mergeProvider(io.a2a.grpc.AgentProvider value) { + if (providerBuilder_ == null) { + if (((bitField0_ & 0x00000040) != 0) && + provider_ != null && + provider_ != io.a2a.grpc.AgentProvider.getDefaultInstance()) { + getProviderBuilder().mergeFrom(value); + } else { + provider_ = value; + } + } else { + providerBuilder_.mergeFrom(value); + } + if (provider_ != null) { + bitField0_ |= 0x00000040; + onChanged(); + } + return this; + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + public Builder clearProvider() { + bitField0_ = (bitField0_ & ~0x00000040); + provider_ = null; + if (providerBuilder_ != null) { + providerBuilder_.dispose(); + providerBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + public io.a2a.grpc.AgentProvider.Builder getProviderBuilder() { + bitField0_ |= 0x00000040; + onChanged(); + return internalGetProviderFieldBuilder().getBuilder(); + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { + if (providerBuilder_ != null) { + return providerBuilder_.getMessageOrBuilder(); + } else { + return provider_ == null ? + io.a2a.grpc.AgentProvider.getDefaultInstance() : provider_; + } + } + /** + *
+     * The service provider of the agent.
+     * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AgentProvider, io.a2a.grpc.AgentProvider.Builder, io.a2a.grpc.AgentProviderOrBuilder> + internalGetProviderFieldBuilder() { + if (providerBuilder_ == null) { + providerBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AgentProvider, io.a2a.grpc.AgentProvider.Builder, io.a2a.grpc.AgentProviderOrBuilder>( + getProvider(), + getParentForChildren(), + isClean()); + provider_ = null; + } + return providerBuilder_; + } + + private java.lang.Object version_ = ""; + /** + *
+     * The version of the agent.
+     * Example: "1.0.0"
+     * 
+ * + * string version = 5 [json_name = "version"]; + * @return The version. + */ + public java.lang.String getVersion() { + java.lang.Object ref = version_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + version_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The version of the agent.
+     * Example: "1.0.0"
+     * 
+ * + * string version = 5 [json_name = "version"]; + * @return The bytes for version. + */ + public com.google.protobuf.ByteString + getVersionBytes() { + java.lang.Object ref = version_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + version_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The version of the agent.
+     * Example: "1.0.0"
+     * 
+ * + * string version = 5 [json_name = "version"]; + * @param value The version to set. + * @return This builder for chaining. + */ + public Builder setVersion( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + version_ = value; + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + /** + *
+     * The version of the agent.
+     * Example: "1.0.0"
+     * 
+ * + * string version = 5 [json_name = "version"]; + * @return This builder for chaining. + */ + public Builder clearVersion() { + version_ = getDefaultInstance().getVersion(); + bitField0_ = (bitField0_ & ~0x00000080); + onChanged(); + return this; + } + /** + *
+     * The version of the agent.
+     * Example: "1.0.0"
+     * 
+ * + * string version = 5 [json_name = "version"]; + * @param value The bytes for version to set. + * @return This builder for chaining. + */ + public Builder setVersionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + version_ = value; + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + + private java.lang.Object documentationUrl_ = ""; + /** + *
+     * A url to provide additional documentation about the agent.
+     * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return The documentationUrl. + */ + public java.lang.String getDocumentationUrl() { + java.lang.Object ref = documentationUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + documentationUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A url to provide additional documentation about the agent.
+     * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return The bytes for documentationUrl. + */ + public com.google.protobuf.ByteString + getDocumentationUrlBytes() { + java.lang.Object ref = documentationUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + documentationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A url to provide additional documentation about the agent.
+     * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @param value The documentationUrl to set. + * @return This builder for chaining. + */ + public Builder setDocumentationUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + documentationUrl_ = value; + bitField0_ |= 0x00000100; + onChanged(); + return this; + } + /** + *
+     * A url to provide additional documentation about the agent.
+     * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return This builder for chaining. + */ + public Builder clearDocumentationUrl() { + documentationUrl_ = getDefaultInstance().getDocumentationUrl(); + bitField0_ = (bitField0_ & ~0x00000100); + onChanged(); + return this; + } + /** + *
+     * A url to provide additional documentation about the agent.
+     * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @param value The bytes for documentationUrl to set. + * @return This builder for chaining. + */ + public Builder setDocumentationUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + documentationUrl_ = value; + bitField0_ |= 0x00000100; + onChanged(); + return this; + } + + private io.a2a.grpc.AgentCapabilities capabilities_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AgentCapabilities, io.a2a.grpc.AgentCapabilities.Builder, io.a2a.grpc.AgentCapabilitiesOrBuilder> capabilitiesBuilder_; + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * @return Whether the capabilities field is set. + */ + public boolean hasCapabilities() { + return ((bitField0_ & 0x00000200) != 0); + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * @return The capabilities. + */ + public io.a2a.grpc.AgentCapabilities getCapabilities() { + if (capabilitiesBuilder_ == null) { + return capabilities_ == null ? io.a2a.grpc.AgentCapabilities.getDefaultInstance() : capabilities_; + } else { + return capabilitiesBuilder_.getMessage(); + } + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) { + if (capabilitiesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + capabilities_ = value; + } else { + capabilitiesBuilder_.setMessage(value); + } + bitField0_ |= 0x00000200; + onChanged(); + return this; + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + public Builder setCapabilities( + io.a2a.grpc.AgentCapabilities.Builder builderForValue) { + if (capabilitiesBuilder_ == null) { + capabilities_ = builderForValue.build(); + } else { + capabilitiesBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000200; + onChanged(); + return this; + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) { + if (capabilitiesBuilder_ == null) { + if (((bitField0_ & 0x00000200) != 0) && + capabilities_ != null && + capabilities_ != io.a2a.grpc.AgentCapabilities.getDefaultInstance()) { + getCapabilitiesBuilder().mergeFrom(value); + } else { + capabilities_ = value; + } + } else { + capabilitiesBuilder_.mergeFrom(value); + } + if (capabilities_ != null) { + bitField0_ |= 0x00000200; + onChanged(); + } + return this; + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + public Builder clearCapabilities() { + bitField0_ = (bitField0_ & ~0x00000200); + capabilities_ = null; + if (capabilitiesBuilder_ != null) { + capabilitiesBuilder_.dispose(); + capabilitiesBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() { + bitField0_ |= 0x00000200; + onChanged(); + return internalGetCapabilitiesFieldBuilder().getBuilder(); + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { + if (capabilitiesBuilder_ != null) { + return capabilitiesBuilder_.getMessageOrBuilder(); + } else { + return capabilities_ == null ? + io.a2a.grpc.AgentCapabilities.getDefaultInstance() : capabilities_; + } + } + /** + *
+     * A2A Capability set supported by the agent.
+     * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AgentCapabilities, io.a2a.grpc.AgentCapabilities.Builder, io.a2a.grpc.AgentCapabilitiesOrBuilder> + internalGetCapabilitiesFieldBuilder() { + if (capabilitiesBuilder_ == null) { + capabilitiesBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AgentCapabilities, io.a2a.grpc.AgentCapabilities.Builder, io.a2a.grpc.AgentCapabilitiesOrBuilder>( + getCapabilities(), + getParentForChildren(), + isClean()); + capabilities_ = null; + } + return capabilitiesBuilder_; + } + + private static final class SecuritySchemesConverter implements com.google.protobuf.MapFieldBuilder.Converter { + @java.lang.Override + public io.a2a.grpc.SecurityScheme build(io.a2a.grpc.SecuritySchemeOrBuilder val) { + if (val instanceof io.a2a.grpc.SecurityScheme) { return (io.a2a.grpc.SecurityScheme) val; } + return ((io.a2a.grpc.SecurityScheme.Builder) val).build(); + } + + @java.lang.Override + public com.google.protobuf.MapEntry defaultEntry() { + return SecuritySchemesDefaultEntryHolder.defaultEntry; + } + }; + private static final SecuritySchemesConverter securitySchemesConverter = new SecuritySchemesConverter(); + + private com.google.protobuf.MapFieldBuilder< + java.lang.String, io.a2a.grpc.SecuritySchemeOrBuilder, io.a2a.grpc.SecurityScheme, io.a2a.grpc.SecurityScheme.Builder> securitySchemes_; + private com.google.protobuf.MapFieldBuilder + internalGetSecuritySchemes() { + if (securitySchemes_ == null) { + return new com.google.protobuf.MapFieldBuilder<>(securitySchemesConverter); + } + return securitySchemes_; + } + private com.google.protobuf.MapFieldBuilder + internalGetMutableSecuritySchemes() { + if (securitySchemes_ == null) { + securitySchemes_ = new com.google.protobuf.MapFieldBuilder<>(securitySchemesConverter); + } + bitField0_ |= 0x00000400; + onChanged(); + return securitySchemes_; + } + public int getSecuritySchemesCount() { + return internalGetSecuritySchemes().ensureBuilderMap().size(); + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public boolean containsSecuritySchemes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetSecuritySchemes().ensureBuilderMap().containsKey(key); + } + /** + * Use {@link #getSecuritySchemesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getSecuritySchemes() { + return getSecuritySchemesMap(); + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public java.util.Map getSecuritySchemesMap() { + return internalGetSecuritySchemes().getImmutableMap(); + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public /* nullable */ +io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( + java.lang.String key, + /* nullable */ +io.a2a.grpc.SecurityScheme defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = internalGetMutableSecuritySchemes().ensureBuilderMap(); + return map.containsKey(key) ? securitySchemesConverter.build(map.get(key)) : defaultValue; + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + @java.lang.Override + public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = internalGetMutableSecuritySchemes().ensureBuilderMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return securitySchemesConverter.build(map.get(key)); + } + public Builder clearSecuritySchemes() { + bitField0_ = (bitField0_ & ~0x00000400); + internalGetMutableSecuritySchemes().clear(); + return this; + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + public Builder removeSecuritySchemes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableSecuritySchemes().ensureBuilderMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableSecuritySchemes() { + bitField0_ |= 0x00000400; + return internalGetMutableSecuritySchemes().ensureMessageMap(); + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + public Builder putSecuritySchemes( + java.lang.String key, + io.a2a.grpc.SecurityScheme value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableSecuritySchemes().ensureBuilderMap() + .put(key, value); + bitField0_ |= 0x00000400; + return this; + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + public Builder putAllSecuritySchemes( + java.util.Map values) { + for (java.util.Map.Entry e : values.entrySet()) { + if (e.getKey() == null || e.getValue() == null) { + throw new NullPointerException(); + } + } + internalGetMutableSecuritySchemes().ensureBuilderMap() + .putAll(values); + bitField0_ |= 0x00000400; + return this; + } + /** + *
+     * The security scheme details used for authenticating with this agent.
+     * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + public io.a2a.grpc.SecurityScheme.Builder putSecuritySchemesBuilderIfAbsent( + java.lang.String key) { + java.util.Map builderMap = internalGetMutableSecuritySchemes().ensureBuilderMap(); + io.a2a.grpc.SecuritySchemeOrBuilder entry = builderMap.get(key); + if (entry == null) { + entry = io.a2a.grpc.SecurityScheme.newBuilder(); + builderMap.put(key, entry); + } + if (entry instanceof io.a2a.grpc.SecurityScheme) { + entry = ((io.a2a.grpc.SecurityScheme) entry).toBuilder(); + builderMap.put(key, entry); + } + return (io.a2a.grpc.SecurityScheme.Builder) entry; + } + + private java.util.List security_ = + java.util.Collections.emptyList(); + private void ensureSecurityIsMutable() { + if (!((bitField0_ & 0x00000800) != 0)) { + security_ = new java.util.ArrayList(security_); + bitField0_ |= 0x00000800; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> securityBuilder_; + + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public java.util.List getSecurityList() { + if (securityBuilder_ == null) { + return java.util.Collections.unmodifiableList(security_); + } else { + return securityBuilder_.getMessageList(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public int getSecurityCount() { + if (securityBuilder_ == null) { + return security_.size(); + } else { + return securityBuilder_.getCount(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public io.a2a.grpc.Security getSecurity(int index) { + if (securityBuilder_ == null) { + return security_.get(index); + } else { + return securityBuilder_.getMessage(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder setSecurity( + int index, io.a2a.grpc.Security value) { + if (securityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSecurityIsMutable(); + security_.set(index, value); + onChanged(); + } else { + securityBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder setSecurity( + int index, io.a2a.grpc.Security.Builder builderForValue) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.set(index, builderForValue.build()); + onChanged(); + } else { + securityBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder addSecurity(io.a2a.grpc.Security value) { + if (securityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSecurityIsMutable(); + security_.add(value); + onChanged(); + } else { + securityBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder addSecurity( + int index, io.a2a.grpc.Security value) { + if (securityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSecurityIsMutable(); + security_.add(index, value); + onChanged(); + } else { + securityBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder addSecurity( + io.a2a.grpc.Security.Builder builderForValue) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.add(builderForValue.build()); + onChanged(); + } else { + securityBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder addSecurity( + int index, io.a2a.grpc.Security.Builder builderForValue) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.add(index, builderForValue.build()); + onChanged(); + } else { + securityBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder addAllSecurity( + java.lang.Iterable values) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, security_); + onChanged(); + } else { + securityBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder clearSecurity() { + if (securityBuilder_ == null) { + security_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000800); + onChanged(); + } else { + securityBuilder_.clear(); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public Builder removeSecurity(int index) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.remove(index); + onChanged(); + } else { + securityBuilder_.remove(index); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public io.a2a.grpc.Security.Builder getSecurityBuilder( + int index) { + return internalGetSecurityFieldBuilder().getBuilder(index); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + int index) { + if (securityBuilder_ == null) { + return security_.get(index); } else { + return securityBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public java.util.List + getSecurityOrBuilderList() { + if (securityBuilder_ != null) { + return securityBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(security_); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public io.a2a.grpc.Security.Builder addSecurityBuilder() { + return internalGetSecurityFieldBuilder().addBuilder( + io.a2a.grpc.Security.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public io.a2a.grpc.Security.Builder addSecurityBuilder( + int index) { + return internalGetSecurityFieldBuilder().addBuilder( + index, io.a2a.grpc.Security.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security requirements for contacting the agent.
+     * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + public java.util.List + getSecurityBuilderList() { + return internalGetSecurityFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> + internalGetSecurityFieldBuilder() { + if (securityBuilder_ == null) { + securityBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder>( + security_, + ((bitField0_ & 0x00000800) != 0), + getParentForChildren(), + isClean()); + security_ = null; + } + return securityBuilder_; + } + + private com.google.protobuf.LazyStringArrayList defaultInputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureDefaultInputModesIsMutable() { + if (!defaultInputModes_.isModifiable()) { + defaultInputModes_ = new com.google.protobuf.LazyStringArrayList(defaultInputModes_); + } + bitField0_ |= 0x00001000; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return A list containing the defaultInputModes. + */ + public com.google.protobuf.ProtocolStringList + getDefaultInputModesList() { + defaultInputModes_.makeImmutable(); + return defaultInputModes_; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return The count of defaultInputModes. + */ + public int getDefaultInputModesCount() { + return defaultInputModes_.size(); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index of the element to return. + * @return The defaultInputModes at the given index. + */ + public java.lang.String getDefaultInputModes(int index) { + return defaultInputModes_.get(index); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index of the value to return. + * @return The bytes of the defaultInputModes at the given index. + */ + public com.google.protobuf.ByteString + getDefaultInputModesBytes(int index) { + return defaultInputModes_.getByteString(index); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index to set the value at. + * @param value The defaultInputModes to set. + * @return This builder for chaining. + */ + public Builder setDefaultInputModes( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureDefaultInputModesIsMutable(); + defaultInputModes_.set(index, value); + bitField0_ |= 0x00001000; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param value The defaultInputModes to add. + * @return This builder for chaining. + */ + public Builder addDefaultInputModes( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureDefaultInputModesIsMutable(); + defaultInputModes_.add(value); + bitField0_ |= 0x00001000; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param values The defaultInputModes to add. + * @return This builder for chaining. + */ + public Builder addAllDefaultInputModes( + java.lang.Iterable values) { + ensureDefaultInputModesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, defaultInputModes_); + bitField0_ |= 0x00001000; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return This builder for chaining. + */ + public Builder clearDefaultInputModes() { + defaultInputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00001000);; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * The set of interaction modes that the agent supports across all skills.
+     * This can be overridden per skill. Defined as mime types.
+     * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param value The bytes of the defaultInputModes to add. + * @return This builder for chaining. + */ + public Builder addDefaultInputModesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureDefaultInputModesIsMutable(); + defaultInputModes_.add(value); + bitField0_ |= 0x00001000; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringArrayList defaultOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureDefaultOutputModesIsMutable() { + if (!defaultOutputModes_.isModifiable()) { + defaultOutputModes_ = new com.google.protobuf.LazyStringArrayList(defaultOutputModes_); + } + bitField0_ |= 0x00002000; + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return A list containing the defaultOutputModes. + */ + public com.google.protobuf.ProtocolStringList + getDefaultOutputModesList() { + defaultOutputModes_.makeImmutable(); + return defaultOutputModes_; + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return The count of defaultOutputModes. + */ + public int getDefaultOutputModesCount() { + return defaultOutputModes_.size(); + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index of the element to return. + * @return The defaultOutputModes at the given index. + */ + public java.lang.String getDefaultOutputModes(int index) { + return defaultOutputModes_.get(index); + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index of the value to return. + * @return The bytes of the defaultOutputModes at the given index. + */ + public com.google.protobuf.ByteString + getDefaultOutputModesBytes(int index) { + return defaultOutputModes_.getByteString(index); + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index to set the value at. + * @param value The defaultOutputModes to set. + * @return This builder for chaining. + */ + public Builder setDefaultOutputModes( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureDefaultOutputModesIsMutable(); + defaultOutputModes_.set(index, value); + bitField0_ |= 0x00002000; + onChanged(); + return this; + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param value The defaultOutputModes to add. + * @return This builder for chaining. + */ + public Builder addDefaultOutputModes( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureDefaultOutputModesIsMutable(); + defaultOutputModes_.add(value); + bitField0_ |= 0x00002000; + onChanged(); + return this; + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param values The defaultOutputModes to add. + * @return This builder for chaining. + */ + public Builder addAllDefaultOutputModes( + java.lang.Iterable values) { + ensureDefaultOutputModesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, defaultOutputModes_); + bitField0_ |= 0x00002000; + onChanged(); + return this; + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return This builder for chaining. + */ + public Builder clearDefaultOutputModes() { + defaultOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00002000);; + onChanged(); + return this; + } + /** + *
+     * The mime types supported as outputs from this agent.
+     * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param value The bytes of the defaultOutputModes to add. + * @return This builder for chaining. + */ + public Builder addDefaultOutputModesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureDefaultOutputModesIsMutable(); + defaultOutputModes_.add(value); + bitField0_ |= 0x00002000; + onChanged(); + return this; + } + + private java.util.List skills_ = + java.util.Collections.emptyList(); + private void ensureSkillsIsMutable() { + if (!((bitField0_ & 0x00004000) != 0)) { + skills_ = new java.util.ArrayList(skills_); + bitField0_ |= 0x00004000; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentSkill, io.a2a.grpc.AgentSkill.Builder, io.a2a.grpc.AgentSkillOrBuilder> skillsBuilder_; + + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public java.util.List getSkillsList() { + if (skillsBuilder_ == null) { + return java.util.Collections.unmodifiableList(skills_); + } else { + return skillsBuilder_.getMessageList(); + } + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public int getSkillsCount() { + if (skillsBuilder_ == null) { + return skills_.size(); + } else { + return skillsBuilder_.getCount(); + } + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public io.a2a.grpc.AgentSkill getSkills(int index) { + if (skillsBuilder_ == null) { + return skills_.get(index); + } else { + return skillsBuilder_.getMessage(index); + } + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder setSkills( + int index, io.a2a.grpc.AgentSkill value) { + if (skillsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSkillsIsMutable(); + skills_.set(index, value); + onChanged(); + } else { + skillsBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder setSkills( + int index, io.a2a.grpc.AgentSkill.Builder builderForValue) { + if (skillsBuilder_ == null) { + ensureSkillsIsMutable(); + skills_.set(index, builderForValue.build()); + onChanged(); + } else { + skillsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder addSkills(io.a2a.grpc.AgentSkill value) { + if (skillsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSkillsIsMutable(); + skills_.add(value); + onChanged(); + } else { + skillsBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder addSkills( + int index, io.a2a.grpc.AgentSkill value) { + if (skillsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSkillsIsMutable(); + skills_.add(index, value); + onChanged(); + } else { + skillsBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder addSkills( + io.a2a.grpc.AgentSkill.Builder builderForValue) { + if (skillsBuilder_ == null) { + ensureSkillsIsMutable(); + skills_.add(builderForValue.build()); + onChanged(); + } else { + skillsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder addSkills( + int index, io.a2a.grpc.AgentSkill.Builder builderForValue) { + if (skillsBuilder_ == null) { + ensureSkillsIsMutable(); + skills_.add(index, builderForValue.build()); + onChanged(); + } else { + skillsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder addAllSkills( + java.lang.Iterable values) { + if (skillsBuilder_ == null) { + ensureSkillsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, skills_); + onChanged(); + } else { + skillsBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder clearSkills() { + if (skillsBuilder_ == null) { + skills_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00004000); + onChanged(); + } else { + skillsBuilder_.clear(); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public Builder removeSkills(int index) { + if (skillsBuilder_ == null) { + ensureSkillsIsMutable(); + skills_.remove(index); + onChanged(); + } else { + skillsBuilder_.remove(index); + } + return this; + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public io.a2a.grpc.AgentSkill.Builder getSkillsBuilder( + int index) { + return internalGetSkillsFieldBuilder().getBuilder(index); + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( + int index) { + if (skillsBuilder_ == null) { + return skills_.get(index); } else { + return skillsBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public java.util.List + getSkillsOrBuilderList() { + if (skillsBuilder_ != null) { + return skillsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(skills_); + } + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder() { + return internalGetSkillsFieldBuilder().addBuilder( + io.a2a.grpc.AgentSkill.getDefaultInstance()); + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( + int index) { + return internalGetSkillsFieldBuilder().addBuilder( + index, io.a2a.grpc.AgentSkill.getDefaultInstance()); + } + /** + *
+     * Skills represent a unit of ability an agent can perform. This may
+     * somewhat abstract but represents a more focused set of actions that the
+     * agent is highly likely to succeed at.
+     * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + public java.util.List + getSkillsBuilderList() { + return internalGetSkillsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentSkill, io.a2a.grpc.AgentSkill.Builder, io.a2a.grpc.AgentSkillOrBuilder> + internalGetSkillsFieldBuilder() { + if (skillsBuilder_ == null) { + skillsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentSkill, io.a2a.grpc.AgentSkill.Builder, io.a2a.grpc.AgentSkillOrBuilder>( + skills_, + ((bitField0_ & 0x00004000) != 0), + getParentForChildren(), + isClean()); + skills_ = null; + } + return skillsBuilder_; + } + + private boolean supportsAuthenticatedExtendedCard_ ; + /** + *
+     * Whether the agent supports providing an extended agent card when
+     * the user is authenticated, i.e. is the card from .well-known
+     * different than the card from GetAgentCard.
+     * 
+ * + * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * @return The supportsAuthenticatedExtendedCard. + */ + @java.lang.Override + public boolean getSupportsAuthenticatedExtendedCard() { + return supportsAuthenticatedExtendedCard_; + } + /** + *
+     * Whether the agent supports providing an extended agent card when
+     * the user is authenticated, i.e. is the card from .well-known
+     * different than the card from GetAgentCard.
+     * 
+ * + * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * @param value The supportsAuthenticatedExtendedCard to set. + * @return This builder for chaining. + */ + public Builder setSupportsAuthenticatedExtendedCard(boolean value) { + + supportsAuthenticatedExtendedCard_ = value; + bitField0_ |= 0x00008000; + onChanged(); + return this; + } + /** + *
+     * Whether the agent supports providing an extended agent card when
+     * the user is authenticated, i.e. is the card from .well-known
+     * different than the card from GetAgentCard.
+     * 
+ * + * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * @return This builder for chaining. + */ + public Builder clearSupportsAuthenticatedExtendedCard() { + bitField0_ = (bitField0_ & ~0x00008000); + supportsAuthenticatedExtendedCard_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCard) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentCard) + private static final io.a2a.grpc.AgentCard DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentCard(); + } + + public static io.a2a.grpc.AgentCard getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentCard parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentCard getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java new file mode 100644 index 000000000..4de7e064d --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java @@ -0,0 +1,522 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentCardOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentCard) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The version of the A2A protocol this agent supports.
+   * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return The protocolVersion. + */ + java.lang.String getProtocolVersion(); + /** + *
+   * The version of the A2A protocol this agent supports.
+   * 
+ * + * string protocol_version = 16 [json_name = "protocolVersion"]; + * @return The bytes for protocolVersion. + */ + com.google.protobuf.ByteString + getProtocolVersionBytes(); + + /** + *
+   * A human readable name for the agent.
+   * Example: "Recipe Agent"
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * A human readable name for the agent.
+   * Example: "Recipe Agent"
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + *
+   * A description of the agent's domain of action/solution space.
+   * Example: "Agent that helps users with recipes and cooking."
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * A description of the agent's domain of action/solution space.
+   * Example: "Agent that helps users with recipes and cooking."
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * A URL to the address the agent is hosted at. This represents the
+   * preferred endpoint as declared by the agent.
+   * 
+ * + * string url = 3 [json_name = "url"]; + * @return The url. + */ + java.lang.String getUrl(); + /** + *
+   * A URL to the address the agent is hosted at. This represents the
+   * preferred endpoint as declared by the agent.
+   * 
+ * + * string url = 3 [json_name = "url"]; + * @return The bytes for url. + */ + com.google.protobuf.ByteString + getUrlBytes(); + + /** + *
+   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return The preferredTransport. + */ + java.lang.String getPreferredTransport(); + /** + *
+   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * 
+ * + * string preferred_transport = 14 [json_name = "preferredTransport"]; + * @return The bytes for preferredTransport. + */ + com.google.protobuf.ByteString + getPreferredTransportBytes(); + + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + java.util.List + getAdditionalInterfacesList(); + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index); + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + int getAdditionalInterfacesCount(); + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + java.util.List + getAdditionalInterfacesOrBuilderList(); + /** + *
+   * Announcement of additional supported transports. Client can use any of
+   * the supported transports.
+   * 
+ * + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + */ + io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + int index); + + /** + *
+   * The service provider of the agent.
+   * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * @return Whether the provider field is set. + */ + boolean hasProvider(); + /** + *
+   * The service provider of the agent.
+   * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * @return The provider. + */ + io.a2a.grpc.AgentProvider getProvider(); + /** + *
+   * The service provider of the agent.
+   * 
+ * + * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + */ + io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder(); + + /** + *
+   * The version of the agent.
+   * Example: "1.0.0"
+   * 
+ * + * string version = 5 [json_name = "version"]; + * @return The version. + */ + java.lang.String getVersion(); + /** + *
+   * The version of the agent.
+   * Example: "1.0.0"
+   * 
+ * + * string version = 5 [json_name = "version"]; + * @return The bytes for version. + */ + com.google.protobuf.ByteString + getVersionBytes(); + + /** + *
+   * A url to provide additional documentation about the agent.
+   * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return The documentationUrl. + */ + java.lang.String getDocumentationUrl(); + /** + *
+   * A url to provide additional documentation about the agent.
+   * 
+ * + * string documentation_url = 6 [json_name = "documentationUrl"]; + * @return The bytes for documentationUrl. + */ + com.google.protobuf.ByteString + getDocumentationUrlBytes(); + + /** + *
+   * A2A Capability set supported by the agent.
+   * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * @return Whether the capabilities field is set. + */ + boolean hasCapabilities(); + /** + *
+   * A2A Capability set supported by the agent.
+   * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * @return The capabilities. + */ + io.a2a.grpc.AgentCapabilities getCapabilities(); + /** + *
+   * A2A Capability set supported by the agent.
+   * 
+ * + * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + */ + io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder(); + + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + int getSecuritySchemesCount(); + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + boolean containsSecuritySchemes( + java.lang.String key); + /** + * Use {@link #getSecuritySchemesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getSecuritySchemes(); + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + java.util.Map + getSecuritySchemesMap(); + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + /* nullable */ +io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( + java.lang.String key, + /* nullable */ +io.a2a.grpc.SecurityScheme defaultValue); + /** + *
+   * The security scheme details used for authenticating with this agent.
+   * 
+ * + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + */ + io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( + java.lang.String key); + + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + java.util.List + getSecurityList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + io.a2a.grpc.Security getSecurity(int index); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + int getSecurityCount(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + java.util.List + getSecurityOrBuilderList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security requirements for contacting the agent.
+   * 
+ * + * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + */ + io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + int index); + + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return A list containing the defaultInputModes. + */ + java.util.List + getDefaultInputModesList(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @return The count of defaultInputModes. + */ + int getDefaultInputModesCount(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index of the element to return. + * @return The defaultInputModes at the given index. + */ + java.lang.String getDefaultInputModes(int index); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * The set of interaction modes that the agent supports across all skills.
+   * This can be overridden per skill. Defined as mime types.
+   * 
+ * + * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * @param index The index of the value to return. + * @return The bytes of the defaultInputModes at the given index. + */ + com.google.protobuf.ByteString + getDefaultInputModesBytes(int index); + + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return A list containing the defaultOutputModes. + */ + java.util.List + getDefaultOutputModesList(); + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @return The count of defaultOutputModes. + */ + int getDefaultOutputModesCount(); + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index of the element to return. + * @return The defaultOutputModes at the given index. + */ + java.lang.String getDefaultOutputModes(int index); + /** + *
+   * The mime types supported as outputs from this agent.
+   * 
+ * + * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * @param index The index of the value to return. + * @return The bytes of the defaultOutputModes at the given index. + */ + com.google.protobuf.ByteString + getDefaultOutputModesBytes(int index); + + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + java.util.List + getSkillsList(); + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + io.a2a.grpc.AgentSkill getSkills(int index); + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + int getSkillsCount(); + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + java.util.List + getSkillsOrBuilderList(); + /** + *
+   * Skills represent a unit of ability an agent can perform. This may
+   * somewhat abstract but represents a more focused set of actions that the
+   * agent is highly likely to succeed at.
+   * 
+ * + * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + */ + io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( + int index); + + /** + *
+   * Whether the agent supports providing an extended agent card when
+   * the user is authenticated, i.e. is the card from .well-known
+   * different than the card from GetAgentCard.
+   * 
+ * + * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * @return The supportsAuthenticatedExtendedCard. + */ + boolean getSupportsAuthenticatedExtendedCard(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AgentExtension.java b/grpc/src/main/java/io/a2a/grpc/AgentExtension.java new file mode 100644 index 000000000..04e298d8d --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentExtension.java @@ -0,0 +1,1044 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * A declaration of an extension supported by an Agent.
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentExtension} + */ +@com.google.protobuf.Generated +public final class AgentExtension extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentExtension) + AgentExtensionOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentExtension.class.getName()); + } + // Use AgentExtension.newBuilder() to construct. + private AgentExtension(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentExtension() { + uri_ = ""; + description_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentExtension_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentExtension_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentExtension.class, io.a2a.grpc.AgentExtension.Builder.class); + } + + private int bitField0_; + public static final int URI_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object uri_ = ""; + /** + *
+   * The URI of the extension.
+   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return The uri. + */ + @java.lang.Override + public java.lang.String getUri() { + java.lang.Object ref = uri_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + uri_ = s; + return s; + } + } + /** + *
+   * The URI of the extension.
+   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return The bytes for uri. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUriBytes() { + java.lang.Object ref = uri_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + uri_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DESCRIPTION_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * A description of how this agent uses this extension.
+   * Example: "Google OAuth 2.0 authentication"
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * A description of how this agent uses this extension.
+   * Example: "Google OAuth 2.0 authentication"
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REQUIRED_FIELD_NUMBER = 3; + private boolean required_ = false; + /** + *
+   * Whether the client must follow specific requirements of the extension.
+   * Example: false
+   * 
+ * + * bool required = 3 [json_name = "required"]; + * @return The required. + */ + @java.lang.Override + public boolean getRequired() { + return required_; + } + + public static final int PARAMS_FIELD_NUMBER = 4; + private com.google.protobuf.Struct params_; + /** + *
+   * Optional configuration for the extension.
+   * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + * @return Whether the params field is set. + */ + @java.lang.Override + public boolean hasParams() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Optional configuration for the extension.
+   * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + * @return The params. + */ + @java.lang.Override + public com.google.protobuf.Struct getParams() { + return params_ == null ? com.google.protobuf.Struct.getDefaultInstance() : params_; + } + /** + *
+   * Optional configuration for the extension.
+   * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getParamsOrBuilder() { + return params_ == null ? com.google.protobuf.Struct.getDefaultInstance() : params_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(uri_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, uri_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, description_); + } + if (required_ != false) { + output.writeBool(3, required_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(4, getParams()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(uri_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, uri_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, description_); + } + if (required_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(3, required_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, getParams()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentExtension)) { + return super.equals(obj); + } + io.a2a.grpc.AgentExtension other = (io.a2a.grpc.AgentExtension) obj; + + if (!getUri() + .equals(other.getUri())) return false; + if (!getDescription() + .equals(other.getDescription())) return false; + if (getRequired() + != other.getRequired()) return false; + if (hasParams() != other.hasParams()) return false; + if (hasParams()) { + if (!getParams() + .equals(other.getParams())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + URI_FIELD_NUMBER; + hash = (53 * hash) + getUri().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + hash = (37 * hash) + REQUIRED_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getRequired()); + if (hasParams()) { + hash = (37 * hash) + PARAMS_FIELD_NUMBER; + hash = (53 * hash) + getParams().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentExtension parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentExtension parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentExtension parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentExtension parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentExtension parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentExtension parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentExtension parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentExtension parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentExtension parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentExtension parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentExtension parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentExtension parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentExtension prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * A declaration of an extension supported by an Agent.
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentExtension} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentExtension) + io.a2a.grpc.AgentExtensionOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentExtension_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentExtension_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentExtension.class, io.a2a.grpc.AgentExtension.Builder.class); + } + + // Construct using io.a2a.grpc.AgentExtension.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetParamsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + uri_ = ""; + description_ = ""; + required_ = false; + params_ = null; + if (paramsBuilder_ != null) { + paramsBuilder_.dispose(); + paramsBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentExtension_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentExtension getDefaultInstanceForType() { + return io.a2a.grpc.AgentExtension.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentExtension build() { + io.a2a.grpc.AgentExtension result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentExtension buildPartial() { + io.a2a.grpc.AgentExtension result = new io.a2a.grpc.AgentExtension(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AgentExtension result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.uri_ = uri_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.description_ = description_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.required_ = required_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000008) != 0)) { + result.params_ = paramsBuilder_ == null + ? params_ + : paramsBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentExtension) { + return mergeFrom((io.a2a.grpc.AgentExtension)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentExtension other) { + if (other == io.a2a.grpc.AgentExtension.getDefaultInstance()) return this; + if (!other.getUri().isEmpty()) { + uri_ = other.uri_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.getRequired() != false) { + setRequired(other.getRequired()); + } + if (other.hasParams()) { + mergeParams(other.getParams()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + uri_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 24: { + required_ = input.readBool(); + bitField0_ |= 0x00000004; + break; + } // case 24 + case 34: { + input.readMessage( + internalGetParamsFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000008; + break; + } // case 34 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object uri_ = ""; + /** + *
+     * The URI of the extension.
+     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return The uri. + */ + public java.lang.String getUri() { + java.lang.Object ref = uri_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + uri_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The URI of the extension.
+     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return The bytes for uri. + */ + public com.google.protobuf.ByteString + getUriBytes() { + java.lang.Object ref = uri_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + uri_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The URI of the extension.
+     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * 
+ * + * string uri = 1 [json_name = "uri"]; + * @param value The uri to set. + * @return This builder for chaining. + */ + public Builder setUri( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + uri_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The URI of the extension.
+     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return This builder for chaining. + */ + public Builder clearUri() { + uri_ = getDefaultInstance().getUri(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The URI of the extension.
+     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * 
+ * + * string uri = 1 [json_name = "uri"]; + * @param value The bytes for uri to set. + * @return This builder for chaining. + */ + public Builder setUriBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + uri_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object description_ = ""; + /** + *
+     * A description of how this agent uses this extension.
+     * Example: "Google OAuth 2.0 authentication"
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A description of how this agent uses this extension.
+     * Example: "Google OAuth 2.0 authentication"
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A description of how this agent uses this extension.
+     * Example: "Google OAuth 2.0 authentication"
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A description of how this agent uses this extension.
+     * Example: "Google OAuth 2.0 authentication"
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * A description of how this agent uses this extension.
+     * Example: "Google OAuth 2.0 authentication"
+     * 
+ * + * string description = 2 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private boolean required_ ; + /** + *
+     * Whether the client must follow specific requirements of the extension.
+     * Example: false
+     * 
+ * + * bool required = 3 [json_name = "required"]; + * @return The required. + */ + @java.lang.Override + public boolean getRequired() { + return required_; + } + /** + *
+     * Whether the client must follow specific requirements of the extension.
+     * Example: false
+     * 
+ * + * bool required = 3 [json_name = "required"]; + * @param value The required to set. + * @return This builder for chaining. + */ + public Builder setRequired(boolean value) { + + required_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Whether the client must follow specific requirements of the extension.
+     * Example: false
+     * 
+ * + * bool required = 3 [json_name = "required"]; + * @return This builder for chaining. + */ + public Builder clearRequired() { + bitField0_ = (bitField0_ & ~0x00000004); + required_ = false; + onChanged(); + return this; + } + + private com.google.protobuf.Struct params_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> paramsBuilder_; + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + * @return Whether the params field is set. + */ + public boolean hasParams() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + * @return The params. + */ + public com.google.protobuf.Struct getParams() { + if (paramsBuilder_ == null) { + return params_ == null ? com.google.protobuf.Struct.getDefaultInstance() : params_; + } else { + return paramsBuilder_.getMessage(); + } + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + public Builder setParams(com.google.protobuf.Struct value) { + if (paramsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + params_ = value; + } else { + paramsBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + public Builder setParams( + com.google.protobuf.Struct.Builder builderForValue) { + if (paramsBuilder_ == null) { + params_ = builderForValue.build(); + } else { + paramsBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + public Builder mergeParams(com.google.protobuf.Struct value) { + if (paramsBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0) && + params_ != null && + params_ != com.google.protobuf.Struct.getDefaultInstance()) { + getParamsBuilder().mergeFrom(value); + } else { + params_ = value; + } + } else { + paramsBuilder_.mergeFrom(value); + } + if (params_ != null) { + bitField0_ |= 0x00000008; + onChanged(); + } + return this; + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + public Builder clearParams() { + bitField0_ = (bitField0_ & ~0x00000008); + params_ = null; + if (paramsBuilder_ != null) { + paramsBuilder_.dispose(); + paramsBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + public com.google.protobuf.Struct.Builder getParamsBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return internalGetParamsFieldBuilder().getBuilder(); + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + public com.google.protobuf.StructOrBuilder getParamsOrBuilder() { + if (paramsBuilder_ != null) { + return paramsBuilder_.getMessageOrBuilder(); + } else { + return params_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : params_; + } + } + /** + *
+     * Optional configuration for the extension.
+     * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetParamsFieldBuilder() { + if (paramsBuilder_ == null) { + paramsBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getParams(), + getParentForChildren(), + isClean()); + params_ = null; + } + return paramsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentExtension) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentExtension) + private static final io.a2a.grpc.AgentExtension DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentExtension(); + } + + public static io.a2a.grpc.AgentExtension getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentExtension parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentExtension getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java new file mode 100644 index 000000000..580b65020 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java @@ -0,0 +1,94 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentExtensionOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentExtension) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The URI of the extension.
+   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return The uri. + */ + java.lang.String getUri(); + /** + *
+   * The URI of the extension.
+   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * 
+ * + * string uri = 1 [json_name = "uri"]; + * @return The bytes for uri. + */ + com.google.protobuf.ByteString + getUriBytes(); + + /** + *
+   * A description of how this agent uses this extension.
+   * Example: "Google OAuth 2.0 authentication"
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * A description of how this agent uses this extension.
+   * Example: "Google OAuth 2.0 authentication"
+   * 
+ * + * string description = 2 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * Whether the client must follow specific requirements of the extension.
+   * Example: false
+   * 
+ * + * bool required = 3 [json_name = "required"]; + * @return The required. + */ + boolean getRequired(); + + /** + *
+   * Optional configuration for the extension.
+   * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + * @return Whether the params field is set. + */ + boolean hasParams(); + /** + *
+   * Optional configuration for the extension.
+   * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + * @return The params. + */ + com.google.protobuf.Struct getParams(); + /** + *
+   * Optional configuration for the extension.
+   * 
+ * + * .google.protobuf.Struct params = 4 [json_name = "params"]; + */ + com.google.protobuf.StructOrBuilder getParamsOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/grpc/src/main/java/io/a2a/grpc/AgentInterface.java new file mode 100644 index 000000000..78ad52d8e --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentInterface.java @@ -0,0 +1,716 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Defines additional transport information for the agent.
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentInterface} + */ +@com.google.protobuf.Generated +public final class AgentInterface extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentInterface) + AgentInterfaceOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentInterface.class.getName()); + } + // Use AgentInterface.newBuilder() to construct. + private AgentInterface(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentInterface() { + url_ = ""; + transport_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentInterface_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentInterface_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentInterface.class, io.a2a.grpc.AgentInterface.Builder.class); + } + + public static final int URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object url_ = ""; + /** + *
+   * The url this interface is found at.
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The url. + */ + @java.lang.Override + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } + } + /** + *
+   * The url this interface is found at.
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The bytes for url. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TRANSPORT_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object transport_ = ""; + /** + *
+   * The transport supported this url. This is an open form string, to be
+   * easily extended for many transport protocols. The core ones officially
+   * supported are JSONRPC, GRPC and HTTP+JSON.
+   * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return The transport. + */ + @java.lang.Override + public java.lang.String getTransport() { + java.lang.Object ref = transport_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + transport_ = s; + return s; + } + } + /** + *
+   * The transport supported this url. This is an open form string, to be
+   * easily extended for many transport protocols. The core ones officially
+   * supported are JSONRPC, GRPC and HTTP+JSON.
+   * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return The bytes for transport. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTransportBytes() { + java.lang.Object ref = transport_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + transport_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, url_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(transport_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, transport_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, url_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(transport_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, transport_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentInterface)) { + return super.equals(obj); + } + io.a2a.grpc.AgentInterface other = (io.a2a.grpc.AgentInterface) obj; + + if (!getUrl() + .equals(other.getUrl())) return false; + if (!getTransport() + .equals(other.getTransport())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + URL_FIELD_NUMBER; + hash = (53 * hash) + getUrl().hashCode(); + hash = (37 * hash) + TRANSPORT_FIELD_NUMBER; + hash = (53 * hash) + getTransport().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentInterface parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentInterface parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentInterface parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentInterface parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentInterface parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentInterface parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentInterface parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentInterface parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentInterface parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentInterface parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentInterface parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentInterface parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentInterface prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Defines additional transport information for the agent.
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentInterface} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentInterface) + io.a2a.grpc.AgentInterfaceOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentInterface_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentInterface_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentInterface.class, io.a2a.grpc.AgentInterface.Builder.class); + } + + // Construct using io.a2a.grpc.AgentInterface.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + url_ = ""; + transport_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentInterface_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentInterface getDefaultInstanceForType() { + return io.a2a.grpc.AgentInterface.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentInterface build() { + io.a2a.grpc.AgentInterface result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentInterface buildPartial() { + io.a2a.grpc.AgentInterface result = new io.a2a.grpc.AgentInterface(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AgentInterface result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.url_ = url_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.transport_ = transport_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentInterface) { + return mergeFrom((io.a2a.grpc.AgentInterface)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentInterface other) { + if (other == io.a2a.grpc.AgentInterface.getDefaultInstance()) return this; + if (!other.getUrl().isEmpty()) { + url_ = other.url_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getTransport().isEmpty()) { + transport_ = other.transport_; + bitField0_ |= 0x00000002; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + url_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + transport_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object url_ = ""; + /** + *
+     * The url this interface is found at.
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @return The url. + */ + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The url this interface is found at.
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @return The bytes for url. + */ + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The url this interface is found at.
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @param value The url to set. + * @return This builder for chaining. + */ + public Builder setUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + url_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The url this interface is found at.
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @return This builder for chaining. + */ + public Builder clearUrl() { + url_ = getDefaultInstance().getUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The url this interface is found at.
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @param value The bytes for url to set. + * @return This builder for chaining. + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + url_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object transport_ = ""; + /** + *
+     * The transport supported this url. This is an open form string, to be
+     * easily extended for many transport protocols. The core ones officially
+     * supported are JSONRPC, GRPC and HTTP+JSON.
+     * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return The transport. + */ + public java.lang.String getTransport() { + java.lang.Object ref = transport_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + transport_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The transport supported this url. This is an open form string, to be
+     * easily extended for many transport protocols. The core ones officially
+     * supported are JSONRPC, GRPC and HTTP+JSON.
+     * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return The bytes for transport. + */ + public com.google.protobuf.ByteString + getTransportBytes() { + java.lang.Object ref = transport_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + transport_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The transport supported this url. This is an open form string, to be
+     * easily extended for many transport protocols. The core ones officially
+     * supported are JSONRPC, GRPC and HTTP+JSON.
+     * 
+ * + * string transport = 2 [json_name = "transport"]; + * @param value The transport to set. + * @return This builder for chaining. + */ + public Builder setTransport( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + transport_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The transport supported this url. This is an open form string, to be
+     * easily extended for many transport protocols. The core ones officially
+     * supported are JSONRPC, GRPC and HTTP+JSON.
+     * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return This builder for chaining. + */ + public Builder clearTransport() { + transport_ = getDefaultInstance().getTransport(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The transport supported this url. This is an open form string, to be
+     * easily extended for many transport protocols. The core ones officially
+     * supported are JSONRPC, GRPC and HTTP+JSON.
+     * 
+ * + * string transport = 2 [json_name = "transport"]; + * @param value The bytes for transport to set. + * @return This builder for chaining. + */ + public Builder setTransportBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + transport_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentInterface) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentInterface) + private static final io.a2a.grpc.AgentInterface DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentInterface(); + } + + public static io.a2a.grpc.AgentInterface getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentInterface parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentInterface getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java new file mode 100644 index 000000000..22d5faf01 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java @@ -0,0 +1,56 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentInterfaceOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentInterface) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The url this interface is found at.
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The url. + */ + java.lang.String getUrl(); + /** + *
+   * The url this interface is found at.
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The bytes for url. + */ + com.google.protobuf.ByteString + getUrlBytes(); + + /** + *
+   * The transport supported this url. This is an open form string, to be
+   * easily extended for many transport protocols. The core ones officially
+   * supported are JSONRPC, GRPC and HTTP+JSON.
+   * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return The transport. + */ + java.lang.String getTransport(); + /** + *
+   * The transport supported this url. This is an open form string, to be
+   * easily extended for many transport protocols. The core ones officially
+   * supported are JSONRPC, GRPC and HTTP+JSON.
+   * 
+ * + * string transport = 2 [json_name = "transport"]; + * @return The bytes for transport. + */ + com.google.protobuf.ByteString + getTransportBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AgentProvider.java b/grpc/src/main/java/io/a2a/grpc/AgentProvider.java new file mode 100644 index 000000000..a166ddaa4 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentProvider.java @@ -0,0 +1,716 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Represents information about the service provider of an agent.
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentProvider} + */ +@com.google.protobuf.Generated +public final class AgentProvider extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentProvider) + AgentProviderOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentProvider.class.getName()); + } + // Use AgentProvider.newBuilder() to construct. + private AgentProvider(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentProvider() { + url_ = ""; + organization_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentProvider_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentProvider_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentProvider.class, io.a2a.grpc.AgentProvider.Builder.class); + } + + public static final int URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object url_ = ""; + /** + *
+   * The providers reference url
+   * Example: "https://ai.google.dev"
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The url. + */ + @java.lang.Override + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } + } + /** + *
+   * The providers reference url
+   * Example: "https://ai.google.dev"
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The bytes for url. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ORGANIZATION_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object organization_ = ""; + /** + *
+   * The providers organization name
+   * Example: "Google"
+   * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return The organization. + */ + @java.lang.Override + public java.lang.String getOrganization() { + java.lang.Object ref = organization_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + organization_ = s; + return s; + } + } + /** + *
+   * The providers organization name
+   * Example: "Google"
+   * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return The bytes for organization. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getOrganizationBytes() { + java.lang.Object ref = organization_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + organization_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, url_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(organization_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, organization_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, url_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(organization_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, organization_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentProvider)) { + return super.equals(obj); + } + io.a2a.grpc.AgentProvider other = (io.a2a.grpc.AgentProvider) obj; + + if (!getUrl() + .equals(other.getUrl())) return false; + if (!getOrganization() + .equals(other.getOrganization())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + URL_FIELD_NUMBER; + hash = (53 * hash) + getUrl().hashCode(); + hash = (37 * hash) + ORGANIZATION_FIELD_NUMBER; + hash = (53 * hash) + getOrganization().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentProvider parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentProvider parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentProvider parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentProvider parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentProvider parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentProvider parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentProvider parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentProvider parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentProvider parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentProvider parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentProvider parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentProvider parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentProvider prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Represents information about the service provider of an agent.
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentProvider} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentProvider) + io.a2a.grpc.AgentProviderOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentProvider_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentProvider_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentProvider.class, io.a2a.grpc.AgentProvider.Builder.class); + } + + // Construct using io.a2a.grpc.AgentProvider.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + url_ = ""; + organization_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentProvider_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentProvider getDefaultInstanceForType() { + return io.a2a.grpc.AgentProvider.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentProvider build() { + io.a2a.grpc.AgentProvider result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentProvider buildPartial() { + io.a2a.grpc.AgentProvider result = new io.a2a.grpc.AgentProvider(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AgentProvider result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.url_ = url_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.organization_ = organization_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentProvider) { + return mergeFrom((io.a2a.grpc.AgentProvider)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentProvider other) { + if (other == io.a2a.grpc.AgentProvider.getDefaultInstance()) return this; + if (!other.getUrl().isEmpty()) { + url_ = other.url_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getOrganization().isEmpty()) { + organization_ = other.organization_; + bitField0_ |= 0x00000002; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + url_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + organization_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object url_ = ""; + /** + *
+     * The providers reference url
+     * Example: "https://ai.google.dev"
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @return The url. + */ + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The providers reference url
+     * Example: "https://ai.google.dev"
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @return The bytes for url. + */ + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The providers reference url
+     * Example: "https://ai.google.dev"
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @param value The url to set. + * @return This builder for chaining. + */ + public Builder setUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + url_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The providers reference url
+     * Example: "https://ai.google.dev"
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @return This builder for chaining. + */ + public Builder clearUrl() { + url_ = getDefaultInstance().getUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The providers reference url
+     * Example: "https://ai.google.dev"
+     * 
+ * + * string url = 1 [json_name = "url"]; + * @param value The bytes for url to set. + * @return This builder for chaining. + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + url_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object organization_ = ""; + /** + *
+     * The providers organization name
+     * Example: "Google"
+     * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return The organization. + */ + public java.lang.String getOrganization() { + java.lang.Object ref = organization_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + organization_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The providers organization name
+     * Example: "Google"
+     * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return The bytes for organization. + */ + public com.google.protobuf.ByteString + getOrganizationBytes() { + java.lang.Object ref = organization_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + organization_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The providers organization name
+     * Example: "Google"
+     * 
+ * + * string organization = 2 [json_name = "organization"]; + * @param value The organization to set. + * @return This builder for chaining. + */ + public Builder setOrganization( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + organization_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The providers organization name
+     * Example: "Google"
+     * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return This builder for chaining. + */ + public Builder clearOrganization() { + organization_ = getDefaultInstance().getOrganization(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The providers organization name
+     * Example: "Google"
+     * 
+ * + * string organization = 2 [json_name = "organization"]; + * @param value The bytes for organization to set. + * @return This builder for chaining. + */ + public Builder setOrganizationBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + organization_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentProvider) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentProvider) + private static final io.a2a.grpc.AgentProvider DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentProvider(); + } + + public static io.a2a.grpc.AgentProvider getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentProvider parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentProvider getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java new file mode 100644 index 000000000..0a54dc18f --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java @@ -0,0 +1,56 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentProviderOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentProvider) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The providers reference url
+   * Example: "https://ai.google.dev"
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The url. + */ + java.lang.String getUrl(); + /** + *
+   * The providers reference url
+   * Example: "https://ai.google.dev"
+   * 
+ * + * string url = 1 [json_name = "url"]; + * @return The bytes for url. + */ + com.google.protobuf.ByteString + getUrlBytes(); + + /** + *
+   * The providers organization name
+   * Example: "Google"
+   * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return The organization. + */ + java.lang.String getOrganization(); + /** + *
+   * The providers organization name
+   * Example: "Google"
+   * 
+ * + * string organization = 2 [json_name = "organization"]; + * @return The bytes for organization. + */ + com.google.protobuf.ByteString + getOrganizationBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/grpc/src/main/java/io/a2a/grpc/AgentSkill.java new file mode 100644 index 000000000..c8d4dbf0b --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -0,0 +1,1897 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * AgentSkill represents a unit of action/solution that the agent can perform.
+ * One can think of this as a type of highly reliable solution that an agent
+ * can be tasked to provide. Agents have the autonomy to choose how and when
+ * to use specific skills, but clients should have confidence that if the
+ * skill is defined that unit of action can be reliably performed.
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentSkill} + */ +@com.google.protobuf.Generated +public final class AgentSkill extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentSkill) + AgentSkillOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentSkill.class.getName()); + } + // Use AgentSkill.newBuilder() to construct. + private AgentSkill(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentSkill() { + id_ = ""; + name_ = ""; + description_ = ""; + tags_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + examples_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + inputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + outputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentSkill_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentSkill_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentSkill.class, io.a2a.grpc.AgentSkill.Builder.class); + } + + public static final int ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + *
+   * Unique id of the skill within this agent.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + *
+   * Unique id of the skill within this agent.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NAME_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * A human readable name for the skill.
+   * 
+ * + * string name = 2 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * A human readable name for the skill.
+   * 
+ * + * string name = 2 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DESCRIPTION_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * A human (or llm) readable description of the skill
+   * details and behaviors.
+   * 
+ * + * string description = 3 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * A human (or llm) readable description of the skill
+   * details and behaviors.
+   * 
+ * + * string description = 3 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TAGS_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList tags_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return A list containing the tags. + */ + public com.google.protobuf.ProtocolStringList + getTagsList() { + return tags_; + } + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return The count of tags. + */ + public int getTagsCount() { + return tags_.size(); + } + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index of the element to return. + * @return The tags at the given index. + */ + public java.lang.String getTags(int index) { + return tags_.get(index); + } + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index of the value to return. + * @return The bytes of the tags at the given index. + */ + public com.google.protobuf.ByteString + getTagsBytes(int index) { + return tags_.getByteString(index); + } + + public static final int EXAMPLES_FIELD_NUMBER = 5; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList examples_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return A list containing the examples. + */ + public com.google.protobuf.ProtocolStringList + getExamplesList() { + return examples_; + } + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return The count of examples. + */ + public int getExamplesCount() { + return examples_.size(); + } + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index of the element to return. + * @return The examples at the given index. + */ + public java.lang.String getExamples(int index) { + return examples_.get(index); + } + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index of the value to return. + * @return The bytes of the examples at the given index. + */ + public com.google.protobuf.ByteString + getExamplesBytes(int index) { + return examples_.getByteString(index); + } + + public static final int INPUT_MODES_FIELD_NUMBER = 6; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList inputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return A list containing the inputModes. + */ + public com.google.protobuf.ProtocolStringList + getInputModesList() { + return inputModes_; + } + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return The count of inputModes. + */ + public int getInputModesCount() { + return inputModes_.size(); + } + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index of the element to return. + * @return The inputModes at the given index. + */ + public java.lang.String getInputModes(int index) { + return inputModes_.get(index); + } + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index of the value to return. + * @return The bytes of the inputModes at the given index. + */ + public com.google.protobuf.ByteString + getInputModesBytes(int index) { + return inputModes_.getByteString(index); + } + + public static final int OUTPUT_MODES_FIELD_NUMBER = 7; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList outputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return A list containing the outputModes. + */ + public com.google.protobuf.ProtocolStringList + getOutputModesList() { + return outputModes_; + } + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return The count of outputModes. + */ + public int getOutputModesCount() { + return outputModes_.size(); + } + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index of the element to return. + * @return The outputModes at the given index. + */ + public java.lang.String getOutputModes(int index) { + return outputModes_.get(index); + } + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index of the value to return. + * @return The bytes of the outputModes at the given index. + */ + public com.google.protobuf.ByteString + getOutputModesBytes(int index) { + return outputModes_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, name_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, description_); + } + for (int i = 0; i < tags_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, tags_.getRaw(i)); + } + for (int i = 0; i < examples_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 5, examples_.getRaw(i)); + } + for (int i = 0; i < inputModes_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 6, inputModes_.getRaw(i)); + } + for (int i = 0; i < outputModes_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 7, outputModes_.getRaw(i)); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, name_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, description_); + } + { + int dataSize = 0; + for (int i = 0; i < tags_.size(); i++) { + dataSize += computeStringSizeNoTag(tags_.getRaw(i)); + } + size += dataSize; + size += 1 * getTagsList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < examples_.size(); i++) { + dataSize += computeStringSizeNoTag(examples_.getRaw(i)); + } + size += dataSize; + size += 1 * getExamplesList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < inputModes_.size(); i++) { + dataSize += computeStringSizeNoTag(inputModes_.getRaw(i)); + } + size += dataSize; + size += 1 * getInputModesList().size(); + } + { + int dataSize = 0; + for (int i = 0; i < outputModes_.size(); i++) { + dataSize += computeStringSizeNoTag(outputModes_.getRaw(i)); + } + size += dataSize; + size += 1 * getOutputModesList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentSkill)) { + return super.equals(obj); + } + io.a2a.grpc.AgentSkill other = (io.a2a.grpc.AgentSkill) obj; + + if (!getId() + .equals(other.getId())) return false; + if (!getName() + .equals(other.getName())) return false; + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getTagsList() + .equals(other.getTagsList())) return false; + if (!getExamplesList() + .equals(other.getExamplesList())) return false; + if (!getInputModesList() + .equals(other.getInputModesList())) return false; + if (!getOutputModesList() + .equals(other.getOutputModesList())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + if (getTagsCount() > 0) { + hash = (37 * hash) + TAGS_FIELD_NUMBER; + hash = (53 * hash) + getTagsList().hashCode(); + } + if (getExamplesCount() > 0) { + hash = (37 * hash) + EXAMPLES_FIELD_NUMBER; + hash = (53 * hash) + getExamplesList().hashCode(); + } + if (getInputModesCount() > 0) { + hash = (37 * hash) + INPUT_MODES_FIELD_NUMBER; + hash = (53 * hash) + getInputModesList().hashCode(); + } + if (getOutputModesCount() > 0) { + hash = (37 * hash) + OUTPUT_MODES_FIELD_NUMBER; + hash = (53 * hash) + getOutputModesList().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentSkill parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentSkill parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentSkill parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentSkill parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentSkill parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentSkill parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentSkill parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentSkill parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentSkill parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentSkill parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentSkill parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentSkill parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentSkill prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * AgentSkill represents a unit of action/solution that the agent can perform.
+   * One can think of this as a type of highly reliable solution that an agent
+   * can be tasked to provide. Agents have the autonomy to choose how and when
+   * to use specific skills, but clients should have confidence that if the
+   * skill is defined that unit of action can be reliably performed.
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentSkill} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentSkill) + io.a2a.grpc.AgentSkillOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentSkill_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentSkill_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentSkill.class, io.a2a.grpc.AgentSkill.Builder.class); + } + + // Construct using io.a2a.grpc.AgentSkill.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = ""; + name_ = ""; + description_ = ""; + tags_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + examples_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + inputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + outputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentSkill_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentSkill getDefaultInstanceForType() { + return io.a2a.grpc.AgentSkill.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentSkill build() { + io.a2a.grpc.AgentSkill result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentSkill buildPartial() { + io.a2a.grpc.AgentSkill result = new io.a2a.grpc.AgentSkill(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AgentSkill result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.name_ = name_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.description_ = description_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + tags_.makeImmutable(); + result.tags_ = tags_; + } + if (((from_bitField0_ & 0x00000010) != 0)) { + examples_.makeImmutable(); + result.examples_ = examples_; + } + if (((from_bitField0_ & 0x00000020) != 0)) { + inputModes_.makeImmutable(); + result.inputModes_ = inputModes_; + } + if (((from_bitField0_ & 0x00000040) != 0)) { + outputModes_.makeImmutable(); + result.outputModes_ = outputModes_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentSkill) { + return mergeFrom((io.a2a.grpc.AgentSkill)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentSkill other) { + if (other == io.a2a.grpc.AgentSkill.getDefaultInstance()) return this; + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (!other.tags_.isEmpty()) { + if (tags_.isEmpty()) { + tags_ = other.tags_; + bitField0_ |= 0x00000008; + } else { + ensureTagsIsMutable(); + tags_.addAll(other.tags_); + } + onChanged(); + } + if (!other.examples_.isEmpty()) { + if (examples_.isEmpty()) { + examples_ = other.examples_; + bitField0_ |= 0x00000010; + } else { + ensureExamplesIsMutable(); + examples_.addAll(other.examples_); + } + onChanged(); + } + if (!other.inputModes_.isEmpty()) { + if (inputModes_.isEmpty()) { + inputModes_ = other.inputModes_; + bitField0_ |= 0x00000020; + } else { + ensureInputModesIsMutable(); + inputModes_.addAll(other.inputModes_); + } + onChanged(); + } + if (!other.outputModes_.isEmpty()) { + if (outputModes_.isEmpty()) { + outputModes_ = other.outputModes_; + bitField0_ |= 0x00000040; + } else { + ensureOutputModesIsMutable(); + outputModes_.addAll(other.outputModes_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 34: { + java.lang.String s = input.readStringRequireUtf8(); + ensureTagsIsMutable(); + tags_.add(s); + break; + } // case 34 + case 42: { + java.lang.String s = input.readStringRequireUtf8(); + ensureExamplesIsMutable(); + examples_.add(s); + break; + } // case 42 + case 50: { + java.lang.String s = input.readStringRequireUtf8(); + ensureInputModesIsMutable(); + inputModes_.add(s); + break; + } // case 50 + case 58: { + java.lang.String s = input.readStringRequireUtf8(); + ensureOutputModesIsMutable(); + outputModes_.add(s); + break; + } // case 58 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object id_ = ""; + /** + *
+     * Unique id of the skill within this agent.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Unique id of the skill within this agent.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Unique id of the skill within this agent.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Unique id of the skill within this agent.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Unique id of the skill within this agent.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + *
+     * A human readable name for the skill.
+     * 
+ * + * string name = 2 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A human readable name for the skill.
+     * 
+ * + * string name = 2 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A human readable name for the skill.
+     * 
+ * + * string name = 2 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A human readable name for the skill.
+     * 
+ * + * string name = 2 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * A human readable name for the skill.
+     * 
+ * + * string name = 2 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object description_ = ""; + /** + *
+     * A human (or llm) readable description of the skill
+     * details and behaviors.
+     * 
+ * + * string description = 3 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A human (or llm) readable description of the skill
+     * details and behaviors.
+     * 
+ * + * string description = 3 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A human (or llm) readable description of the skill
+     * details and behaviors.
+     * 
+ * + * string description = 3 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * A human (or llm) readable description of the skill
+     * details and behaviors.
+     * 
+ * + * string description = 3 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * A human (or llm) readable description of the skill
+     * details and behaviors.
+     * 
+ * + * string description = 3 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringArrayList tags_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureTagsIsMutable() { + if (!tags_.isModifiable()) { + tags_ = new com.google.protobuf.LazyStringArrayList(tags_); + } + bitField0_ |= 0x00000008; + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return A list containing the tags. + */ + public com.google.protobuf.ProtocolStringList + getTagsList() { + tags_.makeImmutable(); + return tags_; + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return The count of tags. + */ + public int getTagsCount() { + return tags_.size(); + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index of the element to return. + * @return The tags at the given index. + */ + public java.lang.String getTags(int index) { + return tags_.get(index); + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index of the value to return. + * @return The bytes of the tags at the given index. + */ + public com.google.protobuf.ByteString + getTagsBytes(int index) { + return tags_.getByteString(index); + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index to set the value at. + * @param value The tags to set. + * @return This builder for chaining. + */ + public Builder setTags( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureTagsIsMutable(); + tags_.set(index, value); + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param value The tags to add. + * @return This builder for chaining. + */ + public Builder addTags( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureTagsIsMutable(); + tags_.add(value); + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param values The tags to add. + * @return This builder for chaining. + */ + public Builder addAllTags( + java.lang.Iterable values) { + ensureTagsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, tags_); + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return This builder for chaining. + */ + public Builder clearTags() { + tags_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008);; + onChanged(); + return this; + } + /** + *
+     * A set of tags for the skill to enhance categorization/utilization.
+     * Example: ["cooking", "customer support", "billing"]
+     * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param value The bytes of the tags to add. + * @return This builder for chaining. + */ + public Builder addTagsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureTagsIsMutable(); + tags_.add(value); + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringArrayList examples_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureExamplesIsMutable() { + if (!examples_.isModifiable()) { + examples_ = new com.google.protobuf.LazyStringArrayList(examples_); + } + bitField0_ |= 0x00000010; + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return A list containing the examples. + */ + public com.google.protobuf.ProtocolStringList + getExamplesList() { + examples_.makeImmutable(); + return examples_; + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return The count of examples. + */ + public int getExamplesCount() { + return examples_.size(); + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index of the element to return. + * @return The examples at the given index. + */ + public java.lang.String getExamples(int index) { + return examples_.get(index); + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index of the value to return. + * @return The bytes of the examples at the given index. + */ + public com.google.protobuf.ByteString + getExamplesBytes(int index) { + return examples_.getByteString(index); + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index to set the value at. + * @param value The examples to set. + * @return This builder for chaining. + */ + public Builder setExamples( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureExamplesIsMutable(); + examples_.set(index, value); + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param value The examples to add. + * @return This builder for chaining. + */ + public Builder addExamples( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureExamplesIsMutable(); + examples_.add(value); + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param values The examples to add. + * @return This builder for chaining. + */ + public Builder addAllExamples( + java.lang.Iterable values) { + ensureExamplesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, examples_); + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return This builder for chaining. + */ + public Builder clearExamples() { + examples_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010);; + onChanged(); + return this; + } + /** + *
+     * A set of example queries that this skill is designed to address.
+     * These examples should help the caller to understand how to craft requests
+     * to the agent to achieve specific goals.
+     * Example: ["I need a recipe for bread"]
+     * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param value The bytes of the examples to add. + * @return This builder for chaining. + */ + public Builder addExamplesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureExamplesIsMutable(); + examples_.add(value); + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringArrayList inputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureInputModesIsMutable() { + if (!inputModes_.isModifiable()) { + inputModes_ = new com.google.protobuf.LazyStringArrayList(inputModes_); + } + bitField0_ |= 0x00000020; + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return A list containing the inputModes. + */ + public com.google.protobuf.ProtocolStringList + getInputModesList() { + inputModes_.makeImmutable(); + return inputModes_; + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return The count of inputModes. + */ + public int getInputModesCount() { + return inputModes_.size(); + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index of the element to return. + * @return The inputModes at the given index. + */ + public java.lang.String getInputModes(int index) { + return inputModes_.get(index); + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index of the value to return. + * @return The bytes of the inputModes at the given index. + */ + public com.google.protobuf.ByteString + getInputModesBytes(int index) { + return inputModes_.getByteString(index); + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index to set the value at. + * @param value The inputModes to set. + * @return This builder for chaining. + */ + public Builder setInputModes( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureInputModesIsMutable(); + inputModes_.set(index, value); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param value The inputModes to add. + * @return This builder for chaining. + */ + public Builder addInputModes( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureInputModesIsMutable(); + inputModes_.add(value); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param values The inputModes to add. + * @return This builder for chaining. + */ + public Builder addAllInputModes( + java.lang.Iterable values) { + ensureInputModesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, inputModes_); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return This builder for chaining. + */ + public Builder clearInputModes() { + inputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000020);; + onChanged(); + return this; + } + /** + *
+     * Possible input modalities supported.
+     * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param value The bytes of the inputModes to add. + * @return This builder for chaining. + */ + public Builder addInputModesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureInputModesIsMutable(); + inputModes_.add(value); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + + private com.google.protobuf.LazyStringArrayList outputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureOutputModesIsMutable() { + if (!outputModes_.isModifiable()) { + outputModes_ = new com.google.protobuf.LazyStringArrayList(outputModes_); + } + bitField0_ |= 0x00000040; + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return A list containing the outputModes. + */ + public com.google.protobuf.ProtocolStringList + getOutputModesList() { + outputModes_.makeImmutable(); + return outputModes_; + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return The count of outputModes. + */ + public int getOutputModesCount() { + return outputModes_.size(); + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index of the element to return. + * @return The outputModes at the given index. + */ + public java.lang.String getOutputModes(int index) { + return outputModes_.get(index); + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index of the value to return. + * @return The bytes of the outputModes at the given index. + */ + public com.google.protobuf.ByteString + getOutputModesBytes(int index) { + return outputModes_.getByteString(index); + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index to set the value at. + * @param value The outputModes to set. + * @return This builder for chaining. + */ + public Builder setOutputModes( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureOutputModesIsMutable(); + outputModes_.set(index, value); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param value The outputModes to add. + * @return This builder for chaining. + */ + public Builder addOutputModes( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureOutputModesIsMutable(); + outputModes_.add(value); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param values The outputModes to add. + * @return This builder for chaining. + */ + public Builder addAllOutputModes( + java.lang.Iterable values) { + ensureOutputModesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, outputModes_); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return This builder for chaining. + */ + public Builder clearOutputModes() { + outputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040);; + onChanged(); + return this; + } + /** + *
+     * Possible output modalities produced
+     * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param value The bytes of the outputModes to add. + * @return This builder for chaining. + */ + public Builder addOutputModesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureOutputModesIsMutable(); + outputModes_.add(value); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentSkill) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentSkill) + private static final io.a2a.grpc.AgentSkill DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentSkill(); + } + + public static io.a2a.grpc.AgentSkill getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentSkill parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentSkill getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java new file mode 100644 index 000000000..bfcb9d956 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -0,0 +1,254 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentSkillOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentSkill) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Unique id of the skill within this agent.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + java.lang.String getId(); + /** + *
+   * Unique id of the skill within this agent.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + com.google.protobuf.ByteString + getIdBytes(); + + /** + *
+   * A human readable name for the skill.
+   * 
+ * + * string name = 2 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * A human readable name for the skill.
+   * 
+ * + * string name = 2 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + *
+   * A human (or llm) readable description of the skill
+   * details and behaviors.
+   * 
+ * + * string description = 3 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * A human (or llm) readable description of the skill
+   * details and behaviors.
+   * 
+ * + * string description = 3 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return A list containing the tags. + */ + java.util.List + getTagsList(); + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @return The count of tags. + */ + int getTagsCount(); + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index of the element to return. + * @return The tags at the given index. + */ + java.lang.String getTags(int index); + /** + *
+   * A set of tags for the skill to enhance categorization/utilization.
+   * Example: ["cooking", "customer support", "billing"]
+   * 
+ * + * repeated string tags = 4 [json_name = "tags"]; + * @param index The index of the value to return. + * @return The bytes of the tags at the given index. + */ + com.google.protobuf.ByteString + getTagsBytes(int index); + + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return A list containing the examples. + */ + java.util.List + getExamplesList(); + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @return The count of examples. + */ + int getExamplesCount(); + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index of the element to return. + * @return The examples at the given index. + */ + java.lang.String getExamples(int index); + /** + *
+   * A set of example queries that this skill is designed to address.
+   * These examples should help the caller to understand how to craft requests
+   * to the agent to achieve specific goals.
+   * Example: ["I need a recipe for bread"]
+   * 
+ * + * repeated string examples = 5 [json_name = "examples"]; + * @param index The index of the value to return. + * @return The bytes of the examples at the given index. + */ + com.google.protobuf.ByteString + getExamplesBytes(int index); + + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return A list containing the inputModes. + */ + java.util.List + getInputModesList(); + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @return The count of inputModes. + */ + int getInputModesCount(); + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index of the element to return. + * @return The inputModes at the given index. + */ + java.lang.String getInputModes(int index); + /** + *
+   * Possible input modalities supported.
+   * 
+ * + * repeated string input_modes = 6 [json_name = "inputModes"]; + * @param index The index of the value to return. + * @return The bytes of the inputModes at the given index. + */ + com.google.protobuf.ByteString + getInputModesBytes(int index); + + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return A list containing the outputModes. + */ + java.util.List + getOutputModesList(); + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @return The count of outputModes. + */ + int getOutputModesCount(); + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index of the element to return. + * @return The outputModes at the given index. + */ + java.lang.String getOutputModes(int index); + /** + *
+   * Possible output modalities produced
+   * 
+ * + * repeated string output_modes = 7 [json_name = "outputModes"]; + * @param index The index of the value to return. + * @return The bytes of the outputModes at the given index. + */ + com.google.protobuf.ByteString + getOutputModesBytes(int index); +} diff --git a/grpc/src/main/java/io/a2a/grpc/Artifact.java b/grpc/src/main/java/io/a2a/grpc/Artifact.java new file mode 100644 index 000000000..3e50bbcf8 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/Artifact.java @@ -0,0 +1,1799 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Artifacts are the container for task completed results. These are similar
+ * to Messages but are intended to be the product of a task, as opposed to
+ * point-to-point communication.
+ * 
+ * + * Protobuf type {@code a2a.v1.Artifact} + */ +@com.google.protobuf.Generated +public final class Artifact extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.Artifact) + ArtifactOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + Artifact.class.getName()); + } + // Use Artifact.newBuilder() to construct. + private Artifact(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Artifact() { + artifactId_ = ""; + name_ = ""; + description_ = ""; + parts_ = java.util.Collections.emptyList(); + extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Artifact_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Artifact_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Artifact.class, io.a2a.grpc.Artifact.Builder.class); + } + + private int bitField0_; + public static final int ARTIFACT_ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object artifactId_ = ""; + /** + *
+   * Unique id for the artifact. It must be at least unique within a task.
+   * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return The artifactId. + */ + @java.lang.Override + public java.lang.String getArtifactId() { + java.lang.Object ref = artifactId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + artifactId_ = s; + return s; + } + } + /** + *
+   * Unique id for the artifact. It must be at least unique within a task.
+   * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return The bytes for artifactId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getArtifactIdBytes() { + java.lang.Object ref = artifactId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + artifactId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int NAME_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * A human readable name for the artifact.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * A human readable name for the artifact.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DESCRIPTION_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * A human readable description of the artifact, optional.
+   * 
+ * + * string description = 4 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * A human readable description of the artifact, optional.
+   * 
+ * + * string description = 4 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PARTS_FIELD_NUMBER = 5; + @SuppressWarnings("serial") + private java.util.List parts_; + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + @java.lang.Override + public java.util.List getPartsList() { + return parts_; + } + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + @java.lang.Override + public java.util.List + getPartsOrBuilderList() { + return parts_; + } + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + @java.lang.Override + public int getPartsCount() { + return parts_.size(); + } + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + @java.lang.Override + public io.a2a.grpc.Part getParts(int index) { + return parts_.get(index); + } + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + @java.lang.Override + public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( + int index) { + return parts_.get(index); + } + + public static final int METADATA_FIELD_NUMBER = 6; + private com.google.protobuf.Struct metadata_; + /** + *
+   * Optional metadata included with the artifact.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Optional metadata included with the artifact.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * Optional metadata included with the artifact.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + + public static final int EXTENSIONS_FIELD_NUMBER = 7; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return A list containing the extensions. + */ + public com.google.protobuf.ProtocolStringList + getExtensionsList() { + return extensions_; + } + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return The count of extensions. + */ + public int getExtensionsCount() { + return extensions_.size(); + } + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the element to return. + * @return The extensions at the given index. + */ + public java.lang.String getExtensions(int index) { + return extensions_.get(index); + } + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the value to return. + * @return The bytes of the extensions at the given index. + */ + public com.google.protobuf.ByteString + getExtensionsBytes(int index) { + return extensions_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(artifactId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, artifactId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, name_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, description_); + } + for (int i = 0; i < parts_.size(); i++) { + output.writeMessage(5, parts_.get(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(6, getMetadata()); + } + for (int i = 0; i < extensions_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 7, extensions_.getRaw(i)); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(artifactId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, artifactId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, name_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, description_); + } + for (int i = 0; i < parts_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, parts_.get(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, getMetadata()); + } + { + int dataSize = 0; + for (int i = 0; i < extensions_.size(); i++) { + dataSize += computeStringSizeNoTag(extensions_.getRaw(i)); + } + size += dataSize; + size += 1 * getExtensionsList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.Artifact)) { + return super.equals(obj); + } + io.a2a.grpc.Artifact other = (io.a2a.grpc.Artifact) obj; + + if (!getArtifactId() + .equals(other.getArtifactId())) return false; + if (!getName() + .equals(other.getName())) return false; + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getPartsList() + .equals(other.getPartsList())) return false; + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } + if (!getExtensionsList() + .equals(other.getExtensionsList())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ARTIFACT_ID_FIELD_NUMBER; + hash = (53 * hash) + getArtifactId().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + if (getPartsCount() > 0) { + hash = (37 * hash) + PARTS_FIELD_NUMBER; + hash = (53 * hash) + getPartsList().hashCode(); + } + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } + if (getExtensionsCount() > 0) { + hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER; + hash = (53 * hash) + getExtensionsList().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.Artifact parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Artifact parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Artifact parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Artifact parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Artifact parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Artifact parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Artifact parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Artifact parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.Artifact parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.Artifact parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.Artifact parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Artifact parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.Artifact prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Artifacts are the container for task completed results. These are similar
+   * to Messages but are intended to be the product of a task, as opposed to
+   * point-to-point communication.
+   * 
+ * + * Protobuf type {@code a2a.v1.Artifact} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.Artifact) + io.a2a.grpc.ArtifactOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Artifact_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Artifact_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Artifact.class, io.a2a.grpc.Artifact.Builder.class); + } + + // Construct using io.a2a.grpc.Artifact.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetPartsFieldBuilder(); + internalGetMetadataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + artifactId_ = ""; + name_ = ""; + description_ = ""; + if (partsBuilder_ == null) { + parts_ = java.util.Collections.emptyList(); + } else { + parts_ = null; + partsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Artifact_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.Artifact getDefaultInstanceForType() { + return io.a2a.grpc.Artifact.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.Artifact build() { + io.a2a.grpc.Artifact result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.Artifact buildPartial() { + io.a2a.grpc.Artifact result = new io.a2a.grpc.Artifact(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.Artifact result) { + if (partsBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0)) { + parts_ = java.util.Collections.unmodifiableList(parts_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.parts_ = parts_; + } else { + result.parts_ = partsBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.Artifact result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.artifactId_ = artifactId_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.name_ = name_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.description_ = description_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000010) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000020) != 0)) { + extensions_.makeImmutable(); + result.extensions_ = extensions_; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.Artifact) { + return mergeFrom((io.a2a.grpc.Artifact)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.Artifact other) { + if (other == io.a2a.grpc.Artifact.getDefaultInstance()) return this; + if (!other.getArtifactId().isEmpty()) { + artifactId_ = other.artifactId_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (partsBuilder_ == null) { + if (!other.parts_.isEmpty()) { + if (parts_.isEmpty()) { + parts_ = other.parts_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensurePartsIsMutable(); + parts_.addAll(other.parts_); + } + onChanged(); + } + } else { + if (!other.parts_.isEmpty()) { + if (partsBuilder_.isEmpty()) { + partsBuilder_.dispose(); + partsBuilder_ = null; + parts_ = other.parts_; + bitField0_ = (bitField0_ & ~0x00000008); + partsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetPartsFieldBuilder() : null; + } else { + partsBuilder_.addAllMessages(other.parts_); + } + } + } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } + if (!other.extensions_.isEmpty()) { + if (extensions_.isEmpty()) { + extensions_ = other.extensions_; + bitField0_ |= 0x00000020; + } else { + ensureExtensionsIsMutable(); + extensions_.addAll(other.extensions_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + artifactId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 26: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 26 + case 34: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 34 + case 42: { + io.a2a.grpc.Part m = + input.readMessage( + io.a2a.grpc.Part.parser(), + extensionRegistry); + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.add(m); + } else { + partsBuilder_.addMessage(m); + } + break; + } // case 42 + case 50: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000010; + break; + } // case 50 + case 58: { + java.lang.String s = input.readStringRequireUtf8(); + ensureExtensionsIsMutable(); + extensions_.add(s); + break; + } // case 58 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object artifactId_ = ""; + /** + *
+     * Unique id for the artifact. It must be at least unique within a task.
+     * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return The artifactId. + */ + public java.lang.String getArtifactId() { + java.lang.Object ref = artifactId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + artifactId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Unique id for the artifact. It must be at least unique within a task.
+     * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return The bytes for artifactId. + */ + public com.google.protobuf.ByteString + getArtifactIdBytes() { + java.lang.Object ref = artifactId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + artifactId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Unique id for the artifact. It must be at least unique within a task.
+     * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @param value The artifactId to set. + * @return This builder for chaining. + */ + public Builder setArtifactId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + artifactId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Unique id for the artifact. It must be at least unique within a task.
+     * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return This builder for chaining. + */ + public Builder clearArtifactId() { + artifactId_ = getDefaultInstance().getArtifactId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Unique id for the artifact. It must be at least unique within a task.
+     * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @param value The bytes for artifactId to set. + * @return This builder for chaining. + */ + public Builder setArtifactIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + artifactId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object name_ = ""; + /** + *
+     * A human readable name for the artifact.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A human readable name for the artifact.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A human readable name for the artifact.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A human readable name for the artifact.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * A human readable name for the artifact.
+     * 
+ * + * string name = 3 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object description_ = ""; + /** + *
+     * A human readable description of the artifact, optional.
+     * 
+ * + * string description = 4 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A human readable description of the artifact, optional.
+     * 
+ * + * string description = 4 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A human readable description of the artifact, optional.
+     * 
+ * + * string description = 4 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * A human readable description of the artifact, optional.
+     * 
+ * + * string description = 4 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * A human readable description of the artifact, optional.
+     * 
+ * + * string description = 4 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private java.util.List parts_ = + java.util.Collections.emptyList(); + private void ensurePartsIsMutable() { + if (!((bitField0_ & 0x00000008) != 0)) { + parts_ = new java.util.ArrayList(parts_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> partsBuilder_; + + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public java.util.List getPartsList() { + if (partsBuilder_ == null) { + return java.util.Collections.unmodifiableList(parts_); + } else { + return partsBuilder_.getMessageList(); + } + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public int getPartsCount() { + if (partsBuilder_ == null) { + return parts_.size(); + } else { + return partsBuilder_.getCount(); + } + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public io.a2a.grpc.Part getParts(int index) { + if (partsBuilder_ == null) { + return parts_.get(index); + } else { + return partsBuilder_.getMessage(index); + } + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder setParts( + int index, io.a2a.grpc.Part value) { + if (partsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePartsIsMutable(); + parts_.set(index, value); + onChanged(); + } else { + partsBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder setParts( + int index, io.a2a.grpc.Part.Builder builderForValue) { + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.set(index, builderForValue.build()); + onChanged(); + } else { + partsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder addParts(io.a2a.grpc.Part value) { + if (partsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePartsIsMutable(); + parts_.add(value); + onChanged(); + } else { + partsBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder addParts( + int index, io.a2a.grpc.Part value) { + if (partsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensurePartsIsMutable(); + parts_.add(index, value); + onChanged(); + } else { + partsBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder addParts( + io.a2a.grpc.Part.Builder builderForValue) { + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.add(builderForValue.build()); + onChanged(); + } else { + partsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder addParts( + int index, io.a2a.grpc.Part.Builder builderForValue) { + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.add(index, builderForValue.build()); + onChanged(); + } else { + partsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder addAllParts( + java.lang.Iterable values) { + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, parts_); + onChanged(); + } else { + partsBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder clearParts() { + if (partsBuilder_ == null) { + parts_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + partsBuilder_.clear(); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public Builder removeParts(int index) { + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.remove(index); + onChanged(); + } else { + partsBuilder_.remove(index); + } + return this; + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public io.a2a.grpc.Part.Builder getPartsBuilder( + int index) { + return internalGetPartsFieldBuilder().getBuilder(index); + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( + int index) { + if (partsBuilder_ == null) { + return parts_.get(index); } else { + return partsBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public java.util.List + getPartsOrBuilderList() { + if (partsBuilder_ != null) { + return partsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(parts_); + } + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public io.a2a.grpc.Part.Builder addPartsBuilder() { + return internalGetPartsFieldBuilder().addBuilder( + io.a2a.grpc.Part.getDefaultInstance()); + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public io.a2a.grpc.Part.Builder addPartsBuilder( + int index) { + return internalGetPartsFieldBuilder().addBuilder( + index, io.a2a.grpc.Part.getDefaultInstance()); + } + /** + *
+     * The content of the artifact.
+     * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + public java.util.List + getPartsBuilderList() { + return internalGetPartsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> + internalGetPartsFieldBuilder() { + if (partsBuilder_ == null) { + partsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder>( + parts_, + ((bitField0_ & 0x00000008) != 0), + getParentForChildren(), + isClean()); + parts_ = null; + } + return partsBuilder_; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000010) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000010; + onChanged(); + } + return this; + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000010); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + *
+     * Optional metadata included with the artifact.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + + private com.google.protobuf.LazyStringArrayList extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureExtensionsIsMutable() { + if (!extensions_.isModifiable()) { + extensions_ = new com.google.protobuf.LazyStringArrayList(extensions_); + } + bitField0_ |= 0x00000020; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return A list containing the extensions. + */ + public com.google.protobuf.ProtocolStringList + getExtensionsList() { + extensions_.makeImmutable(); + return extensions_; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return The count of extensions. + */ + public int getExtensionsCount() { + return extensions_.size(); + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the element to return. + * @return The extensions at the given index. + */ + public java.lang.String getExtensions(int index) { + return extensions_.get(index); + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the value to return. + * @return The bytes of the extensions at the given index. + */ + public com.google.protobuf.ByteString + getExtensionsBytes(int index) { + return extensions_.getByteString(index); + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index to set the value at. + * @param value The extensions to set. + * @return This builder for chaining. + */ + public Builder setExtensions( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureExtensionsIsMutable(); + extensions_.set(index, value); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param value The extensions to add. + * @return This builder for chaining. + */ + public Builder addExtensions( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureExtensionsIsMutable(); + extensions_.add(value); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param values The extensions to add. + * @return This builder for chaining. + */ + public Builder addAllExtensions( + java.lang.Iterable values) { + ensureExtensionsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, extensions_); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return This builder for chaining. + */ + public Builder clearExtensions() { + extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000020);; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Artifact.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param value The bytes of the extensions to add. + * @return This builder for chaining. + */ + public Builder addExtensionsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureExtensionsIsMutable(); + extensions_.add(value); + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.Artifact) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.Artifact) + private static final io.a2a.grpc.Artifact DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.Artifact(); + } + + public static io.a2a.grpc.Artifact getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Artifact parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.Artifact getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java new file mode 100644 index 000000000..54219afa3 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java @@ -0,0 +1,184 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ArtifactOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.Artifact) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Unique id for the artifact. It must be at least unique within a task.
+   * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return The artifactId. + */ + java.lang.String getArtifactId(); + /** + *
+   * Unique id for the artifact. It must be at least unique within a task.
+   * 
+ * + * string artifact_id = 1 [json_name = "artifactId"]; + * @return The bytes for artifactId. + */ + com.google.protobuf.ByteString + getArtifactIdBytes(); + + /** + *
+   * A human readable name for the artifact.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * A human readable name for the artifact.
+   * 
+ * + * string name = 3 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + *
+   * A human readable description of the artifact, optional.
+   * 
+ * + * string description = 4 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * A human readable description of the artifact, optional.
+   * 
+ * + * string description = 4 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + java.util.List + getPartsList(); + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + io.a2a.grpc.Part getParts(int index); + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + int getPartsCount(); + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + java.util.List + getPartsOrBuilderList(); + /** + *
+   * The content of the artifact.
+   * 
+ * + * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + */ + io.a2a.grpc.PartOrBuilder getPartsOrBuilder( + int index); + + /** + *
+   * Optional metadata included with the artifact.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * Optional metadata included with the artifact.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * Optional metadata included with the artifact.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); + + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return A list containing the extensions. + */ + java.util.List + getExtensionsList(); + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return The count of extensions. + */ + int getExtensionsCount(); + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the element to return. + * @return The extensions at the given index. + */ + java.lang.String getExtensions(int index); + /** + *
+   * The URIs of extensions that are present or contributed to this Artifact.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the value to return. + * @return The bytes of the extensions at the given index. + */ + com.google.protobuf.ByteString + getExtensionsBytes(int index); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java new file mode 100644 index 000000000..723f7164a --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java @@ -0,0 +1,779 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Defines authentication details, used for push notifications.
+ * 
+ * + * Protobuf type {@code a2a.v1.AuthenticationInfo} + */ +@com.google.protobuf.Generated +public final class AuthenticationInfo extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AuthenticationInfo) + AuthenticationInfoOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AuthenticationInfo.class.getName()); + } + // Use AuthenticationInfo.newBuilder() to construct. + private AuthenticationInfo(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AuthenticationInfo() { + schemes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + credentials_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthenticationInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthenticationInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AuthenticationInfo.class, io.a2a.grpc.AuthenticationInfo.Builder.class); + } + + public static final int SCHEMES_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList schemes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return A list containing the schemes. + */ + public com.google.protobuf.ProtocolStringList + getSchemesList() { + return schemes_; + } + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return The count of schemes. + */ + public int getSchemesCount() { + return schemes_.size(); + } + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index of the element to return. + * @return The schemes at the given index. + */ + public java.lang.String getSchemes(int index) { + return schemes_.get(index); + } + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index of the value to return. + * @return The bytes of the schemes at the given index. + */ + public com.google.protobuf.ByteString + getSchemesBytes(int index) { + return schemes_.getByteString(index); + } + + public static final int CREDENTIALS_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object credentials_ = ""; + /** + *
+   * Optional credentials
+   * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return The credentials. + */ + @java.lang.Override + public java.lang.String getCredentials() { + java.lang.Object ref = credentials_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + credentials_ = s; + return s; + } + } + /** + *
+   * Optional credentials
+   * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return The bytes for credentials. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getCredentialsBytes() { + java.lang.Object ref = credentials_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + credentials_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < schemes_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, schemes_.getRaw(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(credentials_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, credentials_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < schemes_.size(); i++) { + dataSize += computeStringSizeNoTag(schemes_.getRaw(i)); + } + size += dataSize; + size += 1 * getSchemesList().size(); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(credentials_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, credentials_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AuthenticationInfo)) { + return super.equals(obj); + } + io.a2a.grpc.AuthenticationInfo other = (io.a2a.grpc.AuthenticationInfo) obj; + + if (!getSchemesList() + .equals(other.getSchemesList())) return false; + if (!getCredentials() + .equals(other.getCredentials())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getSchemesCount() > 0) { + hash = (37 * hash) + SCHEMES_FIELD_NUMBER; + hash = (53 * hash) + getSchemesList().hashCode(); + } + hash = (37 * hash) + CREDENTIALS_FIELD_NUMBER; + hash = (53 * hash) + getCredentials().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AuthenticationInfo parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AuthenticationInfo parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AuthenticationInfo parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AuthenticationInfo parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AuthenticationInfo prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Defines authentication details, used for push notifications.
+   * 
+ * + * Protobuf type {@code a2a.v1.AuthenticationInfo} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AuthenticationInfo) + io.a2a.grpc.AuthenticationInfoOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthenticationInfo_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthenticationInfo_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AuthenticationInfo.class, io.a2a.grpc.AuthenticationInfo.Builder.class); + } + + // Construct using io.a2a.grpc.AuthenticationInfo.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + schemes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + credentials_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthenticationInfo_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AuthenticationInfo getDefaultInstanceForType() { + return io.a2a.grpc.AuthenticationInfo.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AuthenticationInfo build() { + io.a2a.grpc.AuthenticationInfo result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AuthenticationInfo buildPartial() { + io.a2a.grpc.AuthenticationInfo result = new io.a2a.grpc.AuthenticationInfo(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AuthenticationInfo result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + schemes_.makeImmutable(); + result.schemes_ = schemes_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.credentials_ = credentials_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AuthenticationInfo) { + return mergeFrom((io.a2a.grpc.AuthenticationInfo)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AuthenticationInfo other) { + if (other == io.a2a.grpc.AuthenticationInfo.getDefaultInstance()) return this; + if (!other.schemes_.isEmpty()) { + if (schemes_.isEmpty()) { + schemes_ = other.schemes_; + bitField0_ |= 0x00000001; + } else { + ensureSchemesIsMutable(); + schemes_.addAll(other.schemes_); + } + onChanged(); + } + if (!other.getCredentials().isEmpty()) { + credentials_ = other.credentials_; + bitField0_ |= 0x00000002; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + ensureSchemesIsMutable(); + schemes_.add(s); + break; + } // case 10 + case 18: { + credentials_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private com.google.protobuf.LazyStringArrayList schemes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureSchemesIsMutable() { + if (!schemes_.isModifiable()) { + schemes_ = new com.google.protobuf.LazyStringArrayList(schemes_); + } + bitField0_ |= 0x00000001; + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return A list containing the schemes. + */ + public com.google.protobuf.ProtocolStringList + getSchemesList() { + schemes_.makeImmutable(); + return schemes_; + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return The count of schemes. + */ + public int getSchemesCount() { + return schemes_.size(); + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index of the element to return. + * @return The schemes at the given index. + */ + public java.lang.String getSchemes(int index) { + return schemes_.get(index); + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index of the value to return. + * @return The bytes of the schemes at the given index. + */ + public com.google.protobuf.ByteString + getSchemesBytes(int index) { + return schemes_.getByteString(index); + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index to set the value at. + * @param value The schemes to set. + * @return This builder for chaining. + */ + public Builder setSchemes( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureSchemesIsMutable(); + schemes_.set(index, value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param value The schemes to add. + * @return This builder for chaining. + */ + public Builder addSchemes( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureSchemesIsMutable(); + schemes_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param values The schemes to add. + * @return This builder for chaining. + */ + public Builder addAllSchemes( + java.lang.Iterable values) { + ensureSchemesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, schemes_); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return This builder for chaining. + */ + public Builder clearSchemes() { + schemes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001);; + onChanged(); + return this; + } + /** + *
+     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param value The bytes of the schemes to add. + * @return This builder for chaining. + */ + public Builder addSchemesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureSchemesIsMutable(); + schemes_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object credentials_ = ""; + /** + *
+     * Optional credentials
+     * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return The credentials. + */ + public java.lang.String getCredentials() { + java.lang.Object ref = credentials_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + credentials_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Optional credentials
+     * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return The bytes for credentials. + */ + public com.google.protobuf.ByteString + getCredentialsBytes() { + java.lang.Object ref = credentials_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + credentials_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Optional credentials
+     * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @param value The credentials to set. + * @return This builder for chaining. + */ + public Builder setCredentials( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + credentials_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Optional credentials
+     * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return This builder for chaining. + */ + public Builder clearCredentials() { + credentials_ = getDefaultInstance().getCredentials(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * Optional credentials
+     * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @param value The bytes for credentials to set. + * @return This builder for chaining. + */ + public Builder setCredentialsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + credentials_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AuthenticationInfo) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AuthenticationInfo) + private static final io.a2a.grpc.AuthenticationInfo DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AuthenticationInfo(); + } + + public static io.a2a.grpc.AuthenticationInfo getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AuthenticationInfo parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AuthenticationInfo getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java new file mode 100644 index 000000000..097b910e5 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java @@ -0,0 +1,73 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AuthenticationInfoOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AuthenticationInfo) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return A list containing the schemes. + */ + java.util.List + getSchemesList(); + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @return The count of schemes. + */ + int getSchemesCount(); + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index of the element to return. + * @return The schemes at the given index. + */ + java.lang.String getSchemes(int index); + /** + *
+   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * 
+ * + * repeated string schemes = 1 [json_name = "schemes"]; + * @param index The index of the value to return. + * @return The bytes of the schemes at the given index. + */ + com.google.protobuf.ByteString + getSchemesBytes(int index); + + /** + *
+   * Optional credentials
+   * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return The credentials. + */ + java.lang.String getCredentials(); + /** + *
+   * Optional credentials
+   * 
+ * + * string credentials = 2 [json_name = "credentials"]; + * @return The bytes for credentials. + */ + com.google.protobuf.ByteString + getCredentialsBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java new file mode 100644 index 000000000..cbe26afb6 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java @@ -0,0 +1,1213 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.AuthorizationCodeOAuthFlow} + */ +@com.google.protobuf.Generated +public final class AuthorizationCodeOAuthFlow extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AuthorizationCodeOAuthFlow) + AuthorizationCodeOAuthFlowOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AuthorizationCodeOAuthFlow.class.getName()); + } + // Use AuthorizationCodeOAuthFlow.newBuilder() to construct. + private AuthorizationCodeOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AuthorizationCodeOAuthFlow() { + authorizationUrl_ = ""; + tokenUrl_ = ""; + refreshUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 4: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AuthorizationCodeOAuthFlow.class, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder.class); + } + + public static final int AUTHORIZATION_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object authorizationUrl_ = ""; + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The authorizationUrl. + */ + @java.lang.Override + public java.lang.String getAuthorizationUrl() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + authorizationUrl_ = s; + return s; + } + } + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The bytes for authorizationUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getAuthorizationUrlBytes() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + authorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TOKEN_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object tokenUrl_ = ""; + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + @java.lang.Override + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } + } + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REFRESH_URL_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object refreshUrl_ = ""; + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + @java.lang.Override + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } + } + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCOPES_FIELD_NUMBER = 4; + private static final class ScopesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, authorizationUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, refreshUrl_); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetScopes(), + ScopesDefaultEntryHolder.defaultEntry, + 4); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, authorizationUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, refreshUrl_); + } + for (java.util.Map.Entry entry + : internalGetScopes().getMap().entrySet()) { + com.google.protobuf.MapEntry + scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, scopes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AuthorizationCodeOAuthFlow)) { + return super.equals(obj); + } + io.a2a.grpc.AuthorizationCodeOAuthFlow other = (io.a2a.grpc.AuthorizationCodeOAuthFlow) obj; + + if (!getAuthorizationUrl() + .equals(other.getAuthorizationUrl())) return false; + if (!getTokenUrl() + .equals(other.getTokenUrl())) return false; + if (!getRefreshUrl() + .equals(other.getRefreshUrl())) return false; + if (!internalGetScopes().equals( + other.internalGetScopes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + AUTHORIZATION_URL_FIELD_NUMBER; + hash = (53 * hash) + getAuthorizationUrl().hashCode(); + hash = (37 * hash) + TOKEN_URL_FIELD_NUMBER; + hash = (53 * hash) + getTokenUrl().hashCode(); + hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; + hash = (53 * hash) + getRefreshUrl().hashCode(); + if (!internalGetScopes().getMap().isEmpty()) { + hash = (37 * hash) + SCOPES_FIELD_NUMBER; + hash = (53 * hash) + internalGetScopes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AuthorizationCodeOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AuthorizationCodeOAuthFlow prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.AuthorizationCodeOAuthFlow} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AuthorizationCodeOAuthFlow) + io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 4: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 4: + return internalGetMutableScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AuthorizationCodeOAuthFlow.class, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder.class); + } + + // Construct using io.a2a.grpc.AuthorizationCodeOAuthFlow.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + authorizationUrl_ = ""; + tokenUrl_ = ""; + refreshUrl_ = ""; + internalGetMutableScopes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlow build() { + io.a2a.grpc.AuthorizationCodeOAuthFlow result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlow buildPartial() { + io.a2a.grpc.AuthorizationCodeOAuthFlow result = new io.a2a.grpc.AuthorizationCodeOAuthFlow(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AuthorizationCodeOAuthFlow result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.authorizationUrl_ = authorizationUrl_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.tokenUrl_ = tokenUrl_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.scopes_ = internalGetScopes(); + result.scopes_.makeImmutable(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AuthorizationCodeOAuthFlow) { + return mergeFrom((io.a2a.grpc.AuthorizationCodeOAuthFlow)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AuthorizationCodeOAuthFlow other) { + if (other == io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance()) return this; + if (!other.getAuthorizationUrl().isEmpty()) { + authorizationUrl_ = other.authorizationUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getTokenUrl().isEmpty()) { + tokenUrl_ = other.tokenUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getRefreshUrl().isEmpty()) { + refreshUrl_ = other.refreshUrl_; + bitField0_ |= 0x00000004; + onChanged(); + } + internalGetMutableScopes().mergeFrom( + other.internalGetScopes()); + bitField0_ |= 0x00000008; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + authorizationUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + tokenUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 34: { + com.google.protobuf.MapEntry + scopes__ = input.readMessage( + ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableScopes().getMutableMap().put( + scopes__.getKey(), scopes__.getValue()); + bitField0_ |= 0x00000008; + break; + } // case 34 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object authorizationUrl_ = ""; + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The authorizationUrl. + */ + public java.lang.String getAuthorizationUrl() { + java.lang.Object ref = authorizationUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + authorizationUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The bytes for authorizationUrl. + */ + public com.google.protobuf.ByteString + getAuthorizationUrlBytes() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + authorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @param value The authorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setAuthorizationUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + authorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return This builder for chaining. + */ + public Builder clearAuthorizationUrl() { + authorizationUrl_ = getDefaultInstance().getAuthorizationUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @param value The bytes for authorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setAuthorizationUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + authorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object tokenUrl_ = ""; + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @param value The tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tokenUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return This builder for chaining. + */ + public Builder clearTokenUrl() { + tokenUrl_ = getDefaultInstance().getTokenUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @param value The bytes for tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tokenUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object refreshUrl_ = ""; + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @param value The refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + refreshUrl_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return This builder for chaining. + */ + public Builder clearRefreshUrl() { + refreshUrl_ = getDefaultInstance().getRefreshUrl(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @param value The bytes for refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + refreshUrl_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + private com.google.protobuf.MapField + internalGetMutableScopes() { + if (scopes_ == null) { + scopes_ = com.google.protobuf.MapField.newMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + if (!scopes_.isMutable()) { + scopes_ = scopes_.copy(); + } + bitField0_ |= 0x00000008; + onChanged(); + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + public Builder clearScopes() { + bitField0_ = (bitField0_ & ~0x00000008); + internalGetMutableScopes().getMutableMap() + .clear(); + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + public Builder removeScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableScopes().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableScopes() { + bitField0_ |= 0x00000008; + return internalGetMutableScopes().getMutableMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + public Builder putScopes( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableScopes().getMutableMap() + .put(key, value); + bitField0_ |= 0x00000008; + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + public Builder putAllScopes( + java.util.Map values) { + internalGetMutableScopes().getMutableMap() + .putAll(values); + bitField0_ |= 0x00000008; + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AuthorizationCodeOAuthFlow) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AuthorizationCodeOAuthFlow) + private static final io.a2a.grpc.AuthorizationCodeOAuthFlow DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AuthorizationCodeOAuthFlow(); + } + + public static io.a2a.grpc.AuthorizationCodeOAuthFlow getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AuthorizationCodeOAuthFlow parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlow getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java new file mode 100644 index 000000000..8e74a3d10 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java @@ -0,0 +1,137 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AuthorizationCodeOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AuthorizationCodeOAuthFlow) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The authorizationUrl. + */ + java.lang.String getAuthorizationUrl(); + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The bytes for authorizationUrl. + */ + com.google.protobuf.ByteString + getAuthorizationUrlBytes(); + + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + java.lang.String getTokenUrl(); + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 2 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + com.google.protobuf.ByteString + getTokenUrlBytes(); + + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + java.lang.String getRefreshUrl(); + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 3 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + com.google.protobuf.ByteString + getRefreshUrlBytes(); + + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + int getScopesCount(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + boolean containsScopes( + java.lang.String key); + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getScopes(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + java.util.Map + getScopesMap(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 4 [json_name = "scopes"]; + */ + java.lang.String getScopesOrThrow( + java.lang.String key); +} diff --git a/grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java new file mode 100644 index 000000000..a5120ff06 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java @@ -0,0 +1,530 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.CancelTaskRequest} + */ +@com.google.protobuf.Generated +public final class CancelTaskRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.CancelTaskRequest) + CancelTaskRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + CancelTaskRequest.class.getName()); + } + // Use CancelTaskRequest.newBuilder() to construct. + private CancelTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private CancelTaskRequest() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CancelTaskRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.CancelTaskRequest.class, io.a2a.grpc.CancelTaskRequest.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.CancelTaskRequest)) { + return super.equals(obj); + } + io.a2a.grpc.CancelTaskRequest other = (io.a2a.grpc.CancelTaskRequest) obj; + + if (!getName() + .equals(other.getName())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.CancelTaskRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.CancelTaskRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.CancelTaskRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.CancelTaskRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.CancelTaskRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.CancelTaskRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.CancelTaskRequest) + io.a2a.grpc.CancelTaskRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CancelTaskRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.CancelTaskRequest.class, io.a2a.grpc.CancelTaskRequest.Builder.class); + } + + // Construct using io.a2a.grpc.CancelTaskRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + name_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CancelTaskRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.CancelTaskRequest getDefaultInstanceForType() { + return io.a2a.grpc.CancelTaskRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.CancelTaskRequest build() { + io.a2a.grpc.CancelTaskRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.CancelTaskRequest buildPartial() { + io.a2a.grpc.CancelTaskRequest result = new io.a2a.grpc.CancelTaskRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.CancelTaskRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.name_ = name_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.CancelTaskRequest) { + return mergeFrom((io.a2a.grpc.CancelTaskRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.CancelTaskRequest other) { + if (other == io.a2a.grpc.CancelTaskRequest.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000001; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.CancelTaskRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.CancelTaskRequest) + private static final io.a2a.grpc.CancelTaskRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.CancelTaskRequest(); + } + + public static io.a2a.grpc.CancelTaskRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public CancelTaskRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.CancelTaskRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java new file mode 100644 index 000000000..4939f71e4 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java @@ -0,0 +1,32 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface CancelTaskRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.CancelTaskRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java b/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java new file mode 100644 index 000000000..3a5be9442 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java @@ -0,0 +1,1042 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.ClientCredentialsOAuthFlow} + */ +@com.google.protobuf.Generated +public final class ClientCredentialsOAuthFlow extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ClientCredentialsOAuthFlow) + ClientCredentialsOAuthFlowOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + ClientCredentialsOAuthFlow.class.getName()); + } + // Use ClientCredentialsOAuthFlow.newBuilder() to construct. + private ClientCredentialsOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ClientCredentialsOAuthFlow() { + tokenUrl_ = ""; + refreshUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ClientCredentialsOAuthFlow.class, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder.class); + } + + public static final int TOKEN_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object tokenUrl_ = ""; + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + @java.lang.Override + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } + } + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REFRESH_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object refreshUrl_ = ""; + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + @java.lang.Override + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } + } + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCOPES_FIELD_NUMBER = 3; + private static final class ScopesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetScopes(), + ScopesDefaultEntryHolder.defaultEntry, + 3); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); + } + for (java.util.Map.Entry entry + : internalGetScopes().getMap().entrySet()) { + com.google.protobuf.MapEntry + scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, scopes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ClientCredentialsOAuthFlow)) { + return super.equals(obj); + } + io.a2a.grpc.ClientCredentialsOAuthFlow other = (io.a2a.grpc.ClientCredentialsOAuthFlow) obj; + + if (!getTokenUrl() + .equals(other.getTokenUrl())) return false; + if (!getRefreshUrl() + .equals(other.getRefreshUrl())) return false; + if (!internalGetScopes().equals( + other.internalGetScopes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TOKEN_URL_FIELD_NUMBER; + hash = (53 * hash) + getTokenUrl().hashCode(); + hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; + hash = (53 * hash) + getRefreshUrl().hashCode(); + if (!internalGetScopes().getMap().isEmpty()) { + hash = (37 * hash) + SCOPES_FIELD_NUMBER; + hash = (53 * hash) + internalGetScopes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ClientCredentialsOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ClientCredentialsOAuthFlow prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.ClientCredentialsOAuthFlow} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ClientCredentialsOAuthFlow) + io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetMutableScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ClientCredentialsOAuthFlow.class, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder.class); + } + + // Construct using io.a2a.grpc.ClientCredentialsOAuthFlow.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + tokenUrl_ = ""; + refreshUrl_ = ""; + internalGetMutableScopes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlow build() { + io.a2a.grpc.ClientCredentialsOAuthFlow result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlow buildPartial() { + io.a2a.grpc.ClientCredentialsOAuthFlow result = new io.a2a.grpc.ClientCredentialsOAuthFlow(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.ClientCredentialsOAuthFlow result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.tokenUrl_ = tokenUrl_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.scopes_ = internalGetScopes(); + result.scopes_.makeImmutable(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ClientCredentialsOAuthFlow) { + return mergeFrom((io.a2a.grpc.ClientCredentialsOAuthFlow)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ClientCredentialsOAuthFlow other) { + if (other == io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance()) return this; + if (!other.getTokenUrl().isEmpty()) { + tokenUrl_ = other.tokenUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getRefreshUrl().isEmpty()) { + refreshUrl_ = other.refreshUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + internalGetMutableScopes().mergeFrom( + other.internalGetScopes()); + bitField0_ |= 0x00000004; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + tokenUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + com.google.protobuf.MapEntry + scopes__ = input.readMessage( + ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableScopes().getMutableMap().put( + scopes__.getKey(), scopes__.getValue()); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object tokenUrl_ = ""; + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @param value The tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tokenUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return This builder for chaining. + */ + public Builder clearTokenUrl() { + tokenUrl_ = getDefaultInstance().getTokenUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @param value The bytes for tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tokenUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object refreshUrl_ = ""; + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @param value The refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return This builder for chaining. + */ + public Builder clearRefreshUrl() { + refreshUrl_ = getDefaultInstance().getRefreshUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @param value The bytes for refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + private com.google.protobuf.MapField + internalGetMutableScopes() { + if (scopes_ == null) { + scopes_ = com.google.protobuf.MapField.newMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + if (!scopes_.isMutable()) { + scopes_ = scopes_.copy(); + } + bitField0_ |= 0x00000004; + onChanged(); + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + public Builder clearScopes() { + bitField0_ = (bitField0_ & ~0x00000004); + internalGetMutableScopes().getMutableMap() + .clear(); + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder removeScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableScopes().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableScopes() { + bitField0_ |= 0x00000004; + return internalGetMutableScopes().getMutableMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder putScopes( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableScopes().getMutableMap() + .put(key, value); + bitField0_ |= 0x00000004; + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder putAllScopes( + java.util.Map values) { + internalGetMutableScopes().getMutableMap() + .putAll(values); + bitField0_ |= 0x00000004; + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ClientCredentialsOAuthFlow) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ClientCredentialsOAuthFlow) + private static final io.a2a.grpc.ClientCredentialsOAuthFlow DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ClientCredentialsOAuthFlow(); + } + + public static io.a2a.grpc.ClientCredentialsOAuthFlow getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ClientCredentialsOAuthFlow parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlow getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java new file mode 100644 index 000000000..c1d3cd312 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java @@ -0,0 +1,115 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ClientCredentialsOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ClientCredentialsOAuthFlow) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + java.lang.String getTokenUrl(); + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + com.google.protobuf.ByteString + getTokenUrlBytes(); + + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + java.lang.String getRefreshUrl(); + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + com.google.protobuf.ByteString + getRefreshUrlBytes(); + + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + int getScopesCount(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + boolean containsScopes( + java.lang.String key); + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getScopes(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + java.util.Map + getScopesMap(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + java.lang.String getScopesOrThrow( + java.lang.String key); +} diff --git a/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java b/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java new file mode 100644 index 000000000..966b0432b --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java @@ -0,0 +1,866 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} + */ +@com.google.protobuf.Generated +public final class CreateTaskPushNotificationConfigRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.CreateTaskPushNotificationConfigRequest) + CreateTaskPushNotificationConfigRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + CreateTaskPushNotificationConfigRequest.class.getName()); + } + // Use CreateTaskPushNotificationConfigRequest.newBuilder() to construct. + private CreateTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private CreateTaskPushNotificationConfigRequest() { + parent_ = ""; + configId_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder.class); + } + + private int bitField0_; + public static final int PARENT_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object parent_ = ""; + /** + *
+   * The task resource for this config.
+   * Format: tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return The parent. + */ + @java.lang.Override + public java.lang.String getParent() { + java.lang.Object ref = parent_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + parent_ = s; + return s; + } + } + /** + *
+   * The task resource for this config.
+   * Format: tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for parent. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getParentBytes() { + java.lang.Object ref = parent_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + parent_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONFIG_ID_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object configId_ = ""; + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return The configId. + */ + @java.lang.Override + public java.lang.String getConfigId() { + java.lang.Object ref = configId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + configId_ = s; + return s; + } + } + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for configId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getConfigIdBytes() { + java.lang.Object ref = configId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + configId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONFIG_FIELD_NUMBER = 3; + private io.a2a.grpc.TaskPushNotificationConfig config_; + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * @return Whether the config field is set. + */ + @java.lang.Override + public boolean hasConfig() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * @return The config. + */ + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfig getConfig() { + return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { + return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, parent_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(configId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, configId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(3, getConfig()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, parent_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(configId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, configId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getConfig()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest)) { + return super.equals(obj); + } + io.a2a.grpc.CreateTaskPushNotificationConfigRequest other = (io.a2a.grpc.CreateTaskPushNotificationConfigRequest) obj; + + if (!getParent() + .equals(other.getParent())) return false; + if (!getConfigId() + .equals(other.getConfigId())) return false; + if (hasConfig() != other.hasConfig()) return false; + if (hasConfig()) { + if (!getConfig() + .equals(other.getConfig())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + PARENT_FIELD_NUMBER; + hash = (53 * hash) + getParent().hashCode(); + hash = (37 * hash) + CONFIG_ID_FIELD_NUMBER; + hash = (53 * hash) + getConfigId().hashCode(); + if (hasConfig()) { + hash = (37 * hash) + CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getConfig().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.CreateTaskPushNotificationConfigRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.CreateTaskPushNotificationConfigRequest) + io.a2a.grpc.CreateTaskPushNotificationConfigRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder.class); + } + + // Construct using io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetConfigFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + parent_ = ""; + configId_ = ""; + config_ = null; + if (configBuilder_ != null) { + configBuilder_.dispose(); + configBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest build() { + io.a2a.grpc.CreateTaskPushNotificationConfigRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest buildPartial() { + io.a2a.grpc.CreateTaskPushNotificationConfigRequest result = new io.a2a.grpc.CreateTaskPushNotificationConfigRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.CreateTaskPushNotificationConfigRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.parent_ = parent_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.configId_ = configId_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.config_ = configBuilder_ == null + ? config_ + : configBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest) { + return mergeFrom((io.a2a.grpc.CreateTaskPushNotificationConfigRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.CreateTaskPushNotificationConfigRequest other) { + if (other == io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getParent().isEmpty()) { + parent_ = other.parent_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getConfigId().isEmpty()) { + configId_ = other.configId_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasConfig()) { + mergeConfig(other.getConfig()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + parent_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + configId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetConfigFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object parent_ = ""; + /** + *
+     * The task resource for this config.
+     * Format: tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return The parent. + */ + public java.lang.String getParent() { + java.lang.Object ref = parent_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + parent_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The task resource for this config.
+     * Format: tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for parent. + */ + public com.google.protobuf.ByteString + getParentBytes() { + java.lang.Object ref = parent_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + parent_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The task resource for this config.
+     * Format: tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @param value The parent to set. + * @return This builder for chaining. + */ + public Builder setParent( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + parent_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The task resource for this config.
+     * Format: tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearParent() { + parent_ = getDefaultInstance().getParent(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The task resource for this config.
+     * Format: tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for parent to set. + * @return This builder for chaining. + */ + public Builder setParentBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + parent_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object configId_ = ""; + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return The configId. + */ + public java.lang.String getConfigId() { + java.lang.Object ref = configId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + configId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for configId. + */ + public com.google.protobuf.ByteString + getConfigIdBytes() { + java.lang.Object ref = configId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + configId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @param value The configId to set. + * @return This builder for chaining. + */ + public Builder setConfigId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + configId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearConfigId() { + configId_ = getDefaultInstance().getConfigId(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for configId to set. + * @return This builder for chaining. + */ + public Builder setConfigIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + configId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private io.a2a.grpc.TaskPushNotificationConfig config_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configBuilder_; + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * @return Whether the config field is set. + */ + public boolean hasConfig() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * @return The config. + */ + public io.a2a.grpc.TaskPushNotificationConfig getConfig() { + if (configBuilder_ == null) { + return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + } else { + return configBuilder_.getMessage(); + } + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { + if (configBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + config_ = value; + } else { + configBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder setConfig( + io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { + if (configBuilder_ == null) { + config_ = builderForValue.build(); + } else { + configBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { + if (configBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + config_ != null && + config_ != io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()) { + getConfigBuilder().mergeFrom(value); + } else { + config_ = value; + } + } else { + configBuilder_.mergeFrom(value); + } + if (config_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder clearConfig() { + bitField0_ = (bitField0_ & ~0x00000004); + config_ = null; + if (configBuilder_ != null) { + configBuilder_.dispose(); + configBuilder_ = null; + } + onChanged(); + return this; + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetConfigFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { + if (configBuilder_ != null) { + return configBuilder_.getMessageOrBuilder(); + } else { + return config_ == null ? + io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + } + } + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> + internalGetConfigFieldBuilder() { + if (configBuilder_ == null) { + configBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder>( + getConfig(), + getParentForChildren(), + isClean()); + config_ = null; + } + return configBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.CreateTaskPushNotificationConfigRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.CreateTaskPushNotificationConfigRequest) + private static final io.a2a.grpc.CreateTaskPushNotificationConfigRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.CreateTaskPushNotificationConfigRequest(); + } + + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public CreateTaskPushNotificationConfigRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java new file mode 100644 index 000000000..73fa1c0df --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java @@ -0,0 +1,61 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface CreateTaskPushNotificationConfigRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.CreateTaskPushNotificationConfigRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The task resource for this config.
+   * Format: tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return The parent. + */ + java.lang.String getParent(); + /** + *
+   * The task resource for this config.
+   * Format: tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for parent. + */ + com.google.protobuf.ByteString + getParentBytes(); + + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return The configId. + */ + java.lang.String getConfigId(); + /** + * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for configId. + */ + com.google.protobuf.ByteString + getConfigIdBytes(); + + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * @return Whether the config field is set. + */ + boolean hasConfig(); + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * @return The config. + */ + io.a2a.grpc.TaskPushNotificationConfig getConfig(); + /** + * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + */ + io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/DataPart.java b/grpc/src/main/java/io/a2a/grpc/DataPart.java new file mode 100644 index 000000000..9b793b4bf --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/DataPart.java @@ -0,0 +1,567 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * DataPart represents a structured blob. This is most commonly a JSON payload.
+ * 
+ * + * Protobuf type {@code a2a.v1.DataPart} + */ +@com.google.protobuf.Generated +public final class DataPart extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.DataPart) + DataPartOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + DataPart.class.getName()); + } + // Use DataPart.newBuilder() to construct. + private DataPart(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private DataPart() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.DataPart.class, io.a2a.grpc.DataPart.Builder.class); + } + + private int bitField0_; + public static final int DATA_FIELD_NUMBER = 1; + private com.google.protobuf.Struct data_; + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + * @return Whether the data field is set. + */ + @java.lang.Override + public boolean hasData() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + * @return The data. + */ + @java.lang.Override + public com.google.protobuf.Struct getData() { + return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getDataOrBuilder() { + return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(1, getData()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, getData()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.DataPart)) { + return super.equals(obj); + } + io.a2a.grpc.DataPart other = (io.a2a.grpc.DataPart) obj; + + if (hasData() != other.hasData()) return false; + if (hasData()) { + if (!getData() + .equals(other.getData())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasData()) { + hash = (37 * hash) + DATA_FIELD_NUMBER; + hash = (53 * hash) + getData().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.DataPart parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.DataPart parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.DataPart parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.DataPart parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.DataPart parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.DataPart parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.DataPart parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.DataPart parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.DataPart parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.DataPart parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.DataPart parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.DataPart parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.DataPart prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * DataPart represents a structured blob. This is most commonly a JSON payload.
+   * 
+ * + * Protobuf type {@code a2a.v1.DataPart} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.DataPart) + io.a2a.grpc.DataPartOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.DataPart.class, io.a2a.grpc.DataPart.Builder.class); + } + + // Construct using io.a2a.grpc.DataPart.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetDataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + data_ = null; + if (dataBuilder_ != null) { + dataBuilder_.dispose(); + dataBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.DataPart getDefaultInstanceForType() { + return io.a2a.grpc.DataPart.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.DataPart build() { + io.a2a.grpc.DataPart result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.DataPart buildPartial() { + io.a2a.grpc.DataPart result = new io.a2a.grpc.DataPart(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.DataPart result) { + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.data_ = dataBuilder_ == null + ? data_ + : dataBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.DataPart) { + return mergeFrom((io.a2a.grpc.DataPart)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.DataPart other) { + if (other == io.a2a.grpc.DataPart.getDefaultInstance()) return this; + if (other.hasData()) { + mergeData(other.getData()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + input.readMessage( + internalGetDataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private com.google.protobuf.Struct data_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> dataBuilder_; + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + * @return Whether the data field is set. + */ + public boolean hasData() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + * @return The data. + */ + public com.google.protobuf.Struct getData() { + if (dataBuilder_ == null) { + return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; + } else { + return dataBuilder_.getMessage(); + } + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + public Builder setData(com.google.protobuf.Struct value) { + if (dataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + data_ = value; + } else { + dataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + public Builder setData( + com.google.protobuf.Struct.Builder builderForValue) { + if (dataBuilder_ == null) { + data_ = builderForValue.build(); + } else { + dataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + public Builder mergeData(com.google.protobuf.Struct value) { + if (dataBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0) && + data_ != null && + data_ != com.google.protobuf.Struct.getDefaultInstance()) { + getDataBuilder().mergeFrom(value); + } else { + data_ = value; + } + } else { + dataBuilder_.mergeFrom(value); + } + if (data_ != null) { + bitField0_ |= 0x00000001; + onChanged(); + } + return this; + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + public Builder clearData() { + bitField0_ = (bitField0_ & ~0x00000001); + data_ = null; + if (dataBuilder_ != null) { + dataBuilder_.dispose(); + dataBuilder_ = null; + } + onChanged(); + return this; + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + public com.google.protobuf.Struct.Builder getDataBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return internalGetDataFieldBuilder().getBuilder(); + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + public com.google.protobuf.StructOrBuilder getDataOrBuilder() { + if (dataBuilder_ != null) { + return dataBuilder_.getMessageOrBuilder(); + } else { + return data_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : data_; + } + } + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetDataFieldBuilder() { + if (dataBuilder_ == null) { + dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getData(), + getParentForChildren(), + isClean()); + data_ = null; + } + return dataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.DataPart) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.DataPart) + private static final io.a2a.grpc.DataPart DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.DataPart(); + } + + public static io.a2a.grpc.DataPart getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public DataPart parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.DataPart getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java new file mode 100644 index 000000000..17f85b202 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java @@ -0,0 +1,27 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface DataPartOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.DataPart) + com.google.protobuf.MessageOrBuilder { + + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + * @return Whether the data field is set. + */ + boolean hasData(); + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + * @return The data. + */ + com.google.protobuf.Struct getData(); + /** + * .google.protobuf.Struct data = 1 [json_name = "data"]; + */ + com.google.protobuf.StructOrBuilder getDataOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java new file mode 100644 index 000000000..afc374be5 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -0,0 +1,530 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} + */ +@com.google.protobuf.Generated +public final class DeleteTaskPushNotificationConfigRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.DeleteTaskPushNotificationConfigRequest) + DeleteTaskPushNotificationConfigRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + DeleteTaskPushNotificationConfigRequest.class.getName()); + } + // Use DeleteTaskPushNotificationConfigRequest.newBuilder() to construct. + private DeleteTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private DeleteTaskPushNotificationConfigRequest() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.class, io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.DeleteTaskPushNotificationConfigRequest)) { + return super.equals(obj); + } + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest other = (io.a2a.grpc.DeleteTaskPushNotificationConfigRequest) obj; + + if (!getName() + .equals(other.getName())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.DeleteTaskPushNotificationConfigRequest) + io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.class, io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder.class); + } + + // Construct using io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + name_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.DeleteTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.DeleteTaskPushNotificationConfigRequest build() { + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.DeleteTaskPushNotificationConfigRequest buildPartial() { + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest result = new io.a2a.grpc.DeleteTaskPushNotificationConfigRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.name_ = name_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.DeleteTaskPushNotificationConfigRequest) { + return mergeFrom((io.a2a.grpc.DeleteTaskPushNotificationConfigRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest other) { + if (other == io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000001; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.DeleteTaskPushNotificationConfigRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.DeleteTaskPushNotificationConfigRequest) + private static final io.a2a.grpc.DeleteTaskPushNotificationConfigRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.DeleteTaskPushNotificationConfigRequest(); + } + + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public DeleteTaskPushNotificationConfigRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.DeleteTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java new file mode 100644 index 000000000..c3c743692 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java @@ -0,0 +1,32 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.DeleteTaskPushNotificationConfigRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/FilePart.java b/grpc/src/main/java/io/a2a/grpc/FilePart.java new file mode 100644 index 000000000..fa85fef4a --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/FilePart.java @@ -0,0 +1,855 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * FilePart represents the different ways files can be provided. If files are
+ * small, directly feeding the bytes is supported via file_with_bytes. If the
+ * file is large, the agent should read the content as appropriate directly
+ * from the file_with_uri source.
+ * 
+ * + * Protobuf type {@code a2a.v1.FilePart} + */ +@com.google.protobuf.Generated +public final class FilePart extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.FilePart) + FilePartOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + FilePart.class.getName()); + } + // Use FilePart.newBuilder() to construct. + private FilePart(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private FilePart() { + mimeType_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.FilePart.class, io.a2a.grpc.FilePart.Builder.class); + } + + private int fileCase_ = 0; + @SuppressWarnings("serial") + private java.lang.Object file_; + public enum FileCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + FILE_WITH_URI(1), + FILE_WITH_BYTES(2), + FILE_NOT_SET(0); + private final int value; + private FileCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static FileCase valueOf(int value) { + return forNumber(value); + } + + public static FileCase forNumber(int value) { + switch (value) { + case 1: return FILE_WITH_URI; + case 2: return FILE_WITH_BYTES; + case 0: return FILE_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public FileCase + getFileCase() { + return FileCase.forNumber( + fileCase_); + } + + public static final int FILE_WITH_URI_FIELD_NUMBER = 1; + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return Whether the fileWithUri field is set. + */ + public boolean hasFileWithUri() { + return fileCase_ == 1; + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return The fileWithUri. + */ + public java.lang.String getFileWithUri() { + java.lang.Object ref = ""; + if (fileCase_ == 1) { + ref = file_; + } + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (fileCase_ == 1) { + file_ = s; + } + return s; + } + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return The bytes for fileWithUri. + */ + public com.google.protobuf.ByteString + getFileWithUriBytes() { + java.lang.Object ref = ""; + if (fileCase_ == 1) { + ref = file_; + } + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (fileCase_ == 1) { + file_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FILE_WITH_BYTES_FIELD_NUMBER = 2; + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return Whether the fileWithBytes field is set. + */ + @java.lang.Override + public boolean hasFileWithBytes() { + return fileCase_ == 2; + } + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return The fileWithBytes. + */ + @java.lang.Override + public com.google.protobuf.ByteString getFileWithBytes() { + if (fileCase_ == 2) { + return (com.google.protobuf.ByteString) file_; + } + return com.google.protobuf.ByteString.EMPTY; + } + + public static final int MIME_TYPE_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object mimeType_ = ""; + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return The mimeType. + */ + @java.lang.Override + public java.lang.String getMimeType() { + java.lang.Object ref = mimeType_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + mimeType_ = s; + return s; + } + } + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return The bytes for mimeType. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getMimeTypeBytes() { + java.lang.Object ref = mimeType_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + mimeType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (fileCase_ == 1) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, file_); + } + if (fileCase_ == 2) { + output.writeBytes( + 2, (com.google.protobuf.ByteString) file_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mimeType_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, mimeType_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (fileCase_ == 1) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, file_); + } + if (fileCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize( + 2, (com.google.protobuf.ByteString) file_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mimeType_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, mimeType_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.FilePart)) { + return super.equals(obj); + } + io.a2a.grpc.FilePart other = (io.a2a.grpc.FilePart) obj; + + if (!getMimeType() + .equals(other.getMimeType())) return false; + if (!getFileCase().equals(other.getFileCase())) return false; + switch (fileCase_) { + case 1: + if (!getFileWithUri() + .equals(other.getFileWithUri())) return false; + break; + case 2: + if (!getFileWithBytes() + .equals(other.getFileWithBytes())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + MIME_TYPE_FIELD_NUMBER; + hash = (53 * hash) + getMimeType().hashCode(); + switch (fileCase_) { + case 1: + hash = (37 * hash) + FILE_WITH_URI_FIELD_NUMBER; + hash = (53 * hash) + getFileWithUri().hashCode(); + break; + case 2: + hash = (37 * hash) + FILE_WITH_BYTES_FIELD_NUMBER; + hash = (53 * hash) + getFileWithBytes().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.FilePart parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.FilePart parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.FilePart parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.FilePart parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.FilePart parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.FilePart parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.FilePart parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.FilePart parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.FilePart parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.FilePart parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.FilePart parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.FilePart parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.FilePart prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * FilePart represents the different ways files can be provided. If files are
+   * small, directly feeding the bytes is supported via file_with_bytes. If the
+   * file is large, the agent should read the content as appropriate directly
+   * from the file_with_uri source.
+   * 
+ * + * Protobuf type {@code a2a.v1.FilePart} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.FilePart) + io.a2a.grpc.FilePartOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.FilePart.class, io.a2a.grpc.FilePart.Builder.class); + } + + // Construct using io.a2a.grpc.FilePart.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + mimeType_ = ""; + fileCase_ = 0; + file_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.FilePart getDefaultInstanceForType() { + return io.a2a.grpc.FilePart.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.FilePart build() { + io.a2a.grpc.FilePart result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.FilePart buildPartial() { + io.a2a.grpc.FilePart result = new io.a2a.grpc.FilePart(this); + if (bitField0_ != 0) { buildPartial0(result); } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.FilePart result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.mimeType_ = mimeType_; + } + } + + private void buildPartialOneofs(io.a2a.grpc.FilePart result) { + result.fileCase_ = fileCase_; + result.file_ = this.file_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.FilePart) { + return mergeFrom((io.a2a.grpc.FilePart)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.FilePart other) { + if (other == io.a2a.grpc.FilePart.getDefaultInstance()) return this; + if (!other.getMimeType().isEmpty()) { + mimeType_ = other.mimeType_; + bitField0_ |= 0x00000004; + onChanged(); + } + switch (other.getFileCase()) { + case FILE_WITH_URI: { + fileCase_ = 1; + file_ = other.file_; + onChanged(); + break; + } + case FILE_WITH_BYTES: { + setFileWithBytes(other.getFileWithBytes()); + break; + } + case FILE_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + fileCase_ = 1; + file_ = s; + break; + } // case 10 + case 18: { + file_ = input.readBytes(); + fileCase_ = 2; + break; + } // case 18 + case 26: { + mimeType_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int fileCase_ = 0; + private java.lang.Object file_; + public FileCase + getFileCase() { + return FileCase.forNumber( + fileCase_); + } + + public Builder clearFile() { + fileCase_ = 0; + file_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return Whether the fileWithUri field is set. + */ + @java.lang.Override + public boolean hasFileWithUri() { + return fileCase_ == 1; + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return The fileWithUri. + */ + @java.lang.Override + public java.lang.String getFileWithUri() { + java.lang.Object ref = ""; + if (fileCase_ == 1) { + ref = file_; + } + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (fileCase_ == 1) { + file_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return The bytes for fileWithUri. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getFileWithUriBytes() { + java.lang.Object ref = ""; + if (fileCase_ == 1) { + ref = file_; + } + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (fileCase_ == 1) { + file_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @param value The fileWithUri to set. + * @return This builder for chaining. + */ + public Builder setFileWithUri( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + fileCase_ = 1; + file_ = value; + onChanged(); + return this; + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return This builder for chaining. + */ + public Builder clearFileWithUri() { + if (fileCase_ == 1) { + fileCase_ = 0; + file_ = null; + onChanged(); + } + return this; + } + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @param value The bytes for fileWithUri to set. + * @return This builder for chaining. + */ + public Builder setFileWithUriBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + fileCase_ = 1; + file_ = value; + onChanged(); + return this; + } + + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return Whether the fileWithBytes field is set. + */ + public boolean hasFileWithBytes() { + return fileCase_ == 2; + } + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return The fileWithBytes. + */ + public com.google.protobuf.ByteString getFileWithBytes() { + if (fileCase_ == 2) { + return (com.google.protobuf.ByteString) file_; + } + return com.google.protobuf.ByteString.EMPTY; + } + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @param value The fileWithBytes to set. + * @return This builder for chaining. + */ + public Builder setFileWithBytes(com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + fileCase_ = 2; + file_ = value; + onChanged(); + return this; + } + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return This builder for chaining. + */ + public Builder clearFileWithBytes() { + if (fileCase_ == 2) { + fileCase_ = 0; + file_ = null; + onChanged(); + } + return this; + } + + private java.lang.Object mimeType_ = ""; + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return The mimeType. + */ + public java.lang.String getMimeType() { + java.lang.Object ref = mimeType_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + mimeType_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return The bytes for mimeType. + */ + public com.google.protobuf.ByteString + getMimeTypeBytes() { + java.lang.Object ref = mimeType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + mimeType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @param value The mimeType to set. + * @return This builder for chaining. + */ + public Builder setMimeType( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + mimeType_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return This builder for chaining. + */ + public Builder clearMimeType() { + mimeType_ = getDefaultInstance().getMimeType(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @param value The bytes for mimeType to set. + * @return This builder for chaining. + */ + public Builder setMimeTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + mimeType_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.FilePart) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.FilePart) + private static final io.a2a.grpc.FilePart DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.FilePart(); + } + + public static io.a2a.grpc.FilePart getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public FilePart parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.FilePart getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java new file mode 100644 index 000000000..d902f7a78 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java @@ -0,0 +1,54 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface FilePartOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.FilePart) + com.google.protobuf.MessageOrBuilder { + + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return Whether the fileWithUri field is set. + */ + boolean hasFileWithUri(); + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return The fileWithUri. + */ + java.lang.String getFileWithUri(); + /** + * string file_with_uri = 1 [json_name = "fileWithUri"]; + * @return The bytes for fileWithUri. + */ + com.google.protobuf.ByteString + getFileWithUriBytes(); + + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return Whether the fileWithBytes field is set. + */ + boolean hasFileWithBytes(); + /** + * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * @return The fileWithBytes. + */ + com.google.protobuf.ByteString getFileWithBytes(); + + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return The mimeType. + */ + java.lang.String getMimeType(); + /** + * string mime_type = 3 [json_name = "mimeType"]; + * @return The bytes for mimeType. + */ + com.google.protobuf.ByteString + getMimeTypeBytes(); + + io.a2a.grpc.FilePart.FileCase getFileCase(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java b/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java new file mode 100644 index 000000000..22f7e9499 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java @@ -0,0 +1,367 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Empty. Added to fix linter violation.
+ * 
+ * + * Protobuf type {@code a2a.v1.GetAgentCardRequest} + */ +@com.google.protobuf.Generated +public final class GetAgentCardRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.GetAgentCardRequest) + GetAgentCardRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + GetAgentCardRequest.class.getName()); + } + // Use GetAgentCardRequest.newBuilder() to construct. + private GetAgentCardRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private GetAgentCardRequest() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.GetAgentCardRequest.class, io.a2a.grpc.GetAgentCardRequest.Builder.class); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.GetAgentCardRequest)) { + return super.equals(obj); + } + io.a2a.grpc.GetAgentCardRequest other = (io.a2a.grpc.GetAgentCardRequest) obj; + + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.GetAgentCardRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.GetAgentCardRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.GetAgentCardRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.GetAgentCardRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Empty. Added to fix linter violation.
+   * 
+ * + * Protobuf type {@code a2a.v1.GetAgentCardRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.GetAgentCardRequest) + io.a2a.grpc.GetAgentCardRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.GetAgentCardRequest.class, io.a2a.grpc.GetAgentCardRequest.Builder.class); + } + + // Construct using io.a2a.grpc.GetAgentCardRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.GetAgentCardRequest getDefaultInstanceForType() { + return io.a2a.grpc.GetAgentCardRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.GetAgentCardRequest build() { + io.a2a.grpc.GetAgentCardRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.GetAgentCardRequest buildPartial() { + io.a2a.grpc.GetAgentCardRequest result = new io.a2a.grpc.GetAgentCardRequest(this); + onBuilt(); + return result; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.GetAgentCardRequest) { + return mergeFrom((io.a2a.grpc.GetAgentCardRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.GetAgentCardRequest other) { + if (other == io.a2a.grpc.GetAgentCardRequest.getDefaultInstance()) return this; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.GetAgentCardRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.GetAgentCardRequest) + private static final io.a2a.grpc.GetAgentCardRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.GetAgentCardRequest(); + } + + public static io.a2a.grpc.GetAgentCardRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public GetAgentCardRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.GetAgentCardRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java new file mode 100644 index 000000000..71311b5e0 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java @@ -0,0 +1,12 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface GetAgentCardRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.GetAgentCardRequest) + com.google.protobuf.MessageOrBuilder { +} diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java b/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java new file mode 100644 index 000000000..f0720170c --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java @@ -0,0 +1,530 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.GetTaskPushNotificationConfigRequest} + */ +@com.google.protobuf.Generated +public final class GetTaskPushNotificationConfigRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.GetTaskPushNotificationConfigRequest) + GetTaskPushNotificationConfigRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + GetTaskPushNotificationConfigRequest.class.getName()); + } + // Use GetTaskPushNotificationConfigRequest.newBuilder() to construct. + private GetTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private GetTaskPushNotificationConfigRequest() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.GetTaskPushNotificationConfigRequest.class, io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.GetTaskPushNotificationConfigRequest)) { + return super.equals(obj); + } + io.a2a.grpc.GetTaskPushNotificationConfigRequest other = (io.a2a.grpc.GetTaskPushNotificationConfigRequest) obj; + + if (!getName() + .equals(other.getName())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.GetTaskPushNotificationConfigRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.GetTaskPushNotificationConfigRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.GetTaskPushNotificationConfigRequest) + io.a2a.grpc.GetTaskPushNotificationConfigRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.GetTaskPushNotificationConfigRequest.class, io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder.class); + } + + // Construct using io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + name_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.GetTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return io.a2a.grpc.GetTaskPushNotificationConfigRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.GetTaskPushNotificationConfigRequest build() { + io.a2a.grpc.GetTaskPushNotificationConfigRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.GetTaskPushNotificationConfigRequest buildPartial() { + io.a2a.grpc.GetTaskPushNotificationConfigRequest result = new io.a2a.grpc.GetTaskPushNotificationConfigRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.GetTaskPushNotificationConfigRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.name_ = name_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.GetTaskPushNotificationConfigRequest) { + return mergeFrom((io.a2a.grpc.GetTaskPushNotificationConfigRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.GetTaskPushNotificationConfigRequest other) { + if (other == io.a2a.grpc.GetTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000001; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.GetTaskPushNotificationConfigRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.GetTaskPushNotificationConfigRequest) + private static final io.a2a.grpc.GetTaskPushNotificationConfigRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.GetTaskPushNotificationConfigRequest(); + } + + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public GetTaskPushNotificationConfigRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.GetTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java new file mode 100644 index 000000000..7502f36a7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java @@ -0,0 +1,32 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface GetTaskPushNotificationConfigRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.GetTaskPushNotificationConfigRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java new file mode 100644 index 000000000..08c0515fa --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -0,0 +1,596 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.GetTaskRequest} + */ +@com.google.protobuf.Generated +public final class GetTaskRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.GetTaskRequest) + GetTaskRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + GetTaskRequest.class.getName()); + } + // Use GetTaskRequest.newBuilder() to construct. + private GetTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private GetTaskRequest() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.GetTaskRequest.class, io.a2a.grpc.GetTaskRequest.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int HISTORY_LENGTH_FIELD_NUMBER = 2; + private int historyLength_ = 0; + /** + * int32 history_length = 2 [json_name = "historyLength"]; + * @return The historyLength. + */ + @java.lang.Override + public int getHistoryLength() { + return historyLength_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + if (historyLength_ != 0) { + output.writeInt32(2, historyLength_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + if (historyLength_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, historyLength_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.GetTaskRequest)) { + return super.equals(obj); + } + io.a2a.grpc.GetTaskRequest other = (io.a2a.grpc.GetTaskRequest) obj; + + if (!getName() + .equals(other.getName())) return false; + if (getHistoryLength() + != other.getHistoryLength()) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getHistoryLength(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.GetTaskRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetTaskRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.GetTaskRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.GetTaskRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.GetTaskRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.GetTaskRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.GetTaskRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.GetTaskRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.GetTaskRequest) + io.a2a.grpc.GetTaskRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.GetTaskRequest.class, io.a2a.grpc.GetTaskRequest.Builder.class); + } + + // Construct using io.a2a.grpc.GetTaskRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + name_ = ""; + historyLength_ = 0; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetTaskRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.GetTaskRequest getDefaultInstanceForType() { + return io.a2a.grpc.GetTaskRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.GetTaskRequest build() { + io.a2a.grpc.GetTaskRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.GetTaskRequest buildPartial() { + io.a2a.grpc.GetTaskRequest result = new io.a2a.grpc.GetTaskRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.GetTaskRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.name_ = name_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.historyLength_ = historyLength_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.GetTaskRequest) { + return mergeFrom((io.a2a.grpc.GetTaskRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.GetTaskRequest other) { + if (other == io.a2a.grpc.GetTaskRequest.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.getHistoryLength() != 0) { + setHistoryLength(other.getHistoryLength()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 16: { + historyLength_ = input.readInt32(); + bitField0_ |= 0x00000002; + break; + } // case 16 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private int historyLength_ ; + /** + * int32 history_length = 2 [json_name = "historyLength"]; + * @return The historyLength. + */ + @java.lang.Override + public int getHistoryLength() { + return historyLength_; + } + /** + * int32 history_length = 2 [json_name = "historyLength"]; + * @param value The historyLength to set. + * @return This builder for chaining. + */ + public Builder setHistoryLength(int value) { + + historyLength_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * int32 history_length = 2 [json_name = "historyLength"]; + * @return This builder for chaining. + */ + public Builder clearHistoryLength() { + bitField0_ = (bitField0_ & ~0x00000002); + historyLength_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.GetTaskRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.GetTaskRequest) + private static final io.a2a.grpc.GetTaskRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.GetTaskRequest(); + } + + public static io.a2a.grpc.GetTaskRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public GetTaskRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.GetTaskRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java new file mode 100644 index 000000000..0f55b196b --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java @@ -0,0 +1,38 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface GetTaskRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.GetTaskRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * int32 history_length = 2 [json_name = "historyLength"]; + * @return The historyLength. + */ + int getHistoryLength(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java b/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java new file mode 100644 index 000000000..b6a093b85 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java @@ -0,0 +1,893 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.HTTPAuthSecurityScheme} + */ +@com.google.protobuf.Generated +public final class HTTPAuthSecurityScheme extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.HTTPAuthSecurityScheme) + HTTPAuthSecuritySchemeOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + HTTPAuthSecurityScheme.class.getName()); + } + // Use HTTPAuthSecurityScheme.newBuilder() to construct. + private HTTPAuthSecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private HTTPAuthSecurityScheme() { + description_ = ""; + scheme_ = ""; + bearerFormat_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.HTTPAuthSecurityScheme.class, io.a2a.grpc.HTTPAuthSecurityScheme.Builder.class); + } + + public static final int DESCRIPTION_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCHEME_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object scheme_ = ""; + /** + *
+   * The name of the HTTP Authentication scheme to be used in the
+   * Authorization header as defined in RFC7235. The values used SHOULD be
+   * registered in the IANA Authentication Scheme registry.
+   * The value is case-insensitive, as defined in RFC7235.
+   * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return The scheme. + */ + @java.lang.Override + public java.lang.String getScheme() { + java.lang.Object ref = scheme_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + scheme_ = s; + return s; + } + } + /** + *
+   * The name of the HTTP Authentication scheme to be used in the
+   * Authorization header as defined in RFC7235. The values used SHOULD be
+   * registered in the IANA Authentication Scheme registry.
+   * The value is case-insensitive, as defined in RFC7235.
+   * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return The bytes for scheme. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getSchemeBytes() { + java.lang.Object ref = scheme_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scheme_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int BEARER_FORMAT_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object bearerFormat_ = ""; + /** + *
+   * A hint to the client to identify how the bearer token is formatted.
+   * Bearer tokens are usually generated by an authorization server, so
+   * this information is primarily for documentation purposes.
+   * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return The bearerFormat. + */ + @java.lang.Override + public java.lang.String getBearerFormat() { + java.lang.Object ref = bearerFormat_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + bearerFormat_ = s; + return s; + } + } + /** + *
+   * A hint to the client to identify how the bearer token is formatted.
+   * Bearer tokens are usually generated by an authorization server, so
+   * this information is primarily for documentation purposes.
+   * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return The bytes for bearerFormat. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getBearerFormatBytes() { + java.lang.Object ref = bearerFormat_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + bearerFormat_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(scheme_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, scheme_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(bearerFormat_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, bearerFormat_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(scheme_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, scheme_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(bearerFormat_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, bearerFormat_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.HTTPAuthSecurityScheme)) { + return super.equals(obj); + } + io.a2a.grpc.HTTPAuthSecurityScheme other = (io.a2a.grpc.HTTPAuthSecurityScheme) obj; + + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getScheme() + .equals(other.getScheme())) return false; + if (!getBearerFormat() + .equals(other.getBearerFormat())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + hash = (37 * hash) + SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getScheme().hashCode(); + hash = (37 * hash) + BEARER_FORMAT_FIELD_NUMBER; + hash = (53 * hash) + getBearerFormat().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.HTTPAuthSecurityScheme parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.HTTPAuthSecurityScheme parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.HTTPAuthSecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.HTTPAuthSecurityScheme prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.HTTPAuthSecurityScheme} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.HTTPAuthSecurityScheme) + io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.HTTPAuthSecurityScheme.class, io.a2a.grpc.HTTPAuthSecurityScheme.Builder.class); + } + + // Construct using io.a2a.grpc.HTTPAuthSecurityScheme.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + description_ = ""; + scheme_ = ""; + bearerFormat_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecurityScheme getDefaultInstanceForType() { + return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecurityScheme build() { + io.a2a.grpc.HTTPAuthSecurityScheme result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecurityScheme buildPartial() { + io.a2a.grpc.HTTPAuthSecurityScheme result = new io.a2a.grpc.HTTPAuthSecurityScheme(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.HTTPAuthSecurityScheme result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.description_ = description_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.scheme_ = scheme_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.bearerFormat_ = bearerFormat_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.HTTPAuthSecurityScheme) { + return mergeFrom((io.a2a.grpc.HTTPAuthSecurityScheme)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.HTTPAuthSecurityScheme other) { + if (other == io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance()) return this; + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getScheme().isEmpty()) { + scheme_ = other.scheme_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getBearerFormat().isEmpty()) { + bearerFormat_ = other.bearerFormat_; + bitField0_ |= 0x00000004; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + scheme_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + bearerFormat_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object description_ = ""; + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object scheme_ = ""; + /** + *
+     * The name of the HTTP Authentication scheme to be used in the
+     * Authorization header as defined in RFC7235. The values used SHOULD be
+     * registered in the IANA Authentication Scheme registry.
+     * The value is case-insensitive, as defined in RFC7235.
+     * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return The scheme. + */ + public java.lang.String getScheme() { + java.lang.Object ref = scheme_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + scheme_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The name of the HTTP Authentication scheme to be used in the
+     * Authorization header as defined in RFC7235. The values used SHOULD be
+     * registered in the IANA Authentication Scheme registry.
+     * The value is case-insensitive, as defined in RFC7235.
+     * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return The bytes for scheme. + */ + public com.google.protobuf.ByteString + getSchemeBytes() { + java.lang.Object ref = scheme_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scheme_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The name of the HTTP Authentication scheme to be used in the
+     * Authorization header as defined in RFC7235. The values used SHOULD be
+     * registered in the IANA Authentication Scheme registry.
+     * The value is case-insensitive, as defined in RFC7235.
+     * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @param value The scheme to set. + * @return This builder for chaining. + */ + public Builder setScheme( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + scheme_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The name of the HTTP Authentication scheme to be used in the
+     * Authorization header as defined in RFC7235. The values used SHOULD be
+     * registered in the IANA Authentication Scheme registry.
+     * The value is case-insensitive, as defined in RFC7235.
+     * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return This builder for chaining. + */ + public Builder clearScheme() { + scheme_ = getDefaultInstance().getScheme(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The name of the HTTP Authentication scheme to be used in the
+     * Authorization header as defined in RFC7235. The values used SHOULD be
+     * registered in the IANA Authentication Scheme registry.
+     * The value is case-insensitive, as defined in RFC7235.
+     * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @param value The bytes for scheme to set. + * @return This builder for chaining. + */ + public Builder setSchemeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + scheme_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object bearerFormat_ = ""; + /** + *
+     * A hint to the client to identify how the bearer token is formatted.
+     * Bearer tokens are usually generated by an authorization server, so
+     * this information is primarily for documentation purposes.
+     * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return The bearerFormat. + */ + public java.lang.String getBearerFormat() { + java.lang.Object ref = bearerFormat_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + bearerFormat_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A hint to the client to identify how the bearer token is formatted.
+     * Bearer tokens are usually generated by an authorization server, so
+     * this information is primarily for documentation purposes.
+     * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return The bytes for bearerFormat. + */ + public com.google.protobuf.ByteString + getBearerFormatBytes() { + java.lang.Object ref = bearerFormat_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + bearerFormat_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A hint to the client to identify how the bearer token is formatted.
+     * Bearer tokens are usually generated by an authorization server, so
+     * this information is primarily for documentation purposes.
+     * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @param value The bearerFormat to set. + * @return This builder for chaining. + */ + public Builder setBearerFormat( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + bearerFormat_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * A hint to the client to identify how the bearer token is formatted.
+     * Bearer tokens are usually generated by an authorization server, so
+     * this information is primarily for documentation purposes.
+     * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return This builder for chaining. + */ + public Builder clearBearerFormat() { + bearerFormat_ = getDefaultInstance().getBearerFormat(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * A hint to the client to identify how the bearer token is formatted.
+     * Bearer tokens are usually generated by an authorization server, so
+     * this information is primarily for documentation purposes.
+     * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @param value The bytes for bearerFormat to set. + * @return This builder for chaining. + */ + public Builder setBearerFormatBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + bearerFormat_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.HTTPAuthSecurityScheme) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.HTTPAuthSecurityScheme) + private static final io.a2a.grpc.HTTPAuthSecurityScheme DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.HTTPAuthSecurityScheme(); + } + + public static io.a2a.grpc.HTTPAuthSecurityScheme getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public HTTPAuthSecurityScheme parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecurityScheme getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java new file mode 100644 index 000000000..90afd8f91 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java @@ -0,0 +1,82 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface HTTPAuthSecuritySchemeOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.HTTPAuthSecurityScheme) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * The name of the HTTP Authentication scheme to be used in the
+   * Authorization header as defined in RFC7235. The values used SHOULD be
+   * registered in the IANA Authentication Scheme registry.
+   * The value is case-insensitive, as defined in RFC7235.
+   * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return The scheme. + */ + java.lang.String getScheme(); + /** + *
+   * The name of the HTTP Authentication scheme to be used in the
+   * Authorization header as defined in RFC7235. The values used SHOULD be
+   * registered in the IANA Authentication Scheme registry.
+   * The value is case-insensitive, as defined in RFC7235.
+   * 
+ * + * string scheme = 2 [json_name = "scheme"]; + * @return The bytes for scheme. + */ + com.google.protobuf.ByteString + getSchemeBytes(); + + /** + *
+   * A hint to the client to identify how the bearer token is formatted.
+   * Bearer tokens are usually generated by an authorization server, so
+   * this information is primarily for documentation purposes.
+   * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return The bearerFormat. + */ + java.lang.String getBearerFormat(); + /** + *
+   * A hint to the client to identify how the bearer token is formatted.
+   * Bearer tokens are usually generated by an authorization server, so
+   * this information is primarily for documentation purposes.
+   * 
+ * + * string bearer_format = 3 [json_name = "bearerFormat"]; + * @return The bytes for bearerFormat. + */ + com.google.protobuf.ByteString + getBearerFormatBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java new file mode 100644 index 000000000..450e158a9 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java @@ -0,0 +1,1042 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} + */ +@com.google.protobuf.Generated +public final class ImplicitOAuthFlow extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ImplicitOAuthFlow) + ImplicitOAuthFlowOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + ImplicitOAuthFlow.class.getName()); + } + // Use ImplicitOAuthFlow.newBuilder() to construct. + private ImplicitOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ImplicitOAuthFlow() { + authorizationUrl_ = ""; + refreshUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ImplicitOAuthFlow.class, io.a2a.grpc.ImplicitOAuthFlow.Builder.class); + } + + public static final int AUTHORIZATION_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object authorizationUrl_ = ""; + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The authorizationUrl. + */ + @java.lang.Override + public java.lang.String getAuthorizationUrl() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + authorizationUrl_ = s; + return s; + } + } + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The bytes for authorizationUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getAuthorizationUrlBytes() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + authorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REFRESH_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object refreshUrl_ = ""; + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + @java.lang.Override + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } + } + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCOPES_FIELD_NUMBER = 3; + private static final class ScopesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, authorizationUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetScopes(), + ScopesDefaultEntryHolder.defaultEntry, + 3); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, authorizationUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); + } + for (java.util.Map.Entry entry + : internalGetScopes().getMap().entrySet()) { + com.google.protobuf.MapEntry + scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, scopes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ImplicitOAuthFlow)) { + return super.equals(obj); + } + io.a2a.grpc.ImplicitOAuthFlow other = (io.a2a.grpc.ImplicitOAuthFlow) obj; + + if (!getAuthorizationUrl() + .equals(other.getAuthorizationUrl())) return false; + if (!getRefreshUrl() + .equals(other.getRefreshUrl())) return false; + if (!internalGetScopes().equals( + other.internalGetScopes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + AUTHORIZATION_URL_FIELD_NUMBER; + hash = (53 * hash) + getAuthorizationUrl().hashCode(); + hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; + hash = (53 * hash) + getRefreshUrl().hashCode(); + if (!internalGetScopes().getMap().isEmpty()) { + hash = (37 * hash) + SCOPES_FIELD_NUMBER; + hash = (53 * hash) + internalGetScopes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ImplicitOAuthFlow parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ImplicitOAuthFlow parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ImplicitOAuthFlow prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ImplicitOAuthFlow) + io.a2a.grpc.ImplicitOAuthFlowOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetMutableScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ImplicitOAuthFlow.class, io.a2a.grpc.ImplicitOAuthFlow.Builder.class); + } + + // Construct using io.a2a.grpc.ImplicitOAuthFlow.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + authorizationUrl_ = ""; + refreshUrl_ = ""; + internalGetMutableScopes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow build() { + io.a2a.grpc.ImplicitOAuthFlow result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow buildPartial() { + io.a2a.grpc.ImplicitOAuthFlow result = new io.a2a.grpc.ImplicitOAuthFlow(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.ImplicitOAuthFlow result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.authorizationUrl_ = authorizationUrl_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.scopes_ = internalGetScopes(); + result.scopes_.makeImmutable(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ImplicitOAuthFlow) { + return mergeFrom((io.a2a.grpc.ImplicitOAuthFlow)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ImplicitOAuthFlow other) { + if (other == io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance()) return this; + if (!other.getAuthorizationUrl().isEmpty()) { + authorizationUrl_ = other.authorizationUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getRefreshUrl().isEmpty()) { + refreshUrl_ = other.refreshUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + internalGetMutableScopes().mergeFrom( + other.internalGetScopes()); + bitField0_ |= 0x00000004; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + authorizationUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + com.google.protobuf.MapEntry + scopes__ = input.readMessage( + ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableScopes().getMutableMap().put( + scopes__.getKey(), scopes__.getValue()); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object authorizationUrl_ = ""; + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The authorizationUrl. + */ + public java.lang.String getAuthorizationUrl() { + java.lang.Object ref = authorizationUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + authorizationUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The bytes for authorizationUrl. + */ + public com.google.protobuf.ByteString + getAuthorizationUrlBytes() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + authorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @param value The authorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setAuthorizationUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + authorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return This builder for chaining. + */ + public Builder clearAuthorizationUrl() { + authorizationUrl_ = getDefaultInstance().getAuthorizationUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The authorization URL to be used for this flow. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS
+     * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @param value The bytes for authorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setAuthorizationUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + authorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object refreshUrl_ = ""; + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @param value The refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return This builder for chaining. + */ + public Builder clearRefreshUrl() { + refreshUrl_ = getDefaultInstance().getRefreshUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @param value The bytes for refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + private com.google.protobuf.MapField + internalGetMutableScopes() { + if (scopes_ == null) { + scopes_ = com.google.protobuf.MapField.newMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + if (!scopes_.isMutable()) { + scopes_ = scopes_.copy(); + } + bitField0_ |= 0x00000004; + onChanged(); + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + public Builder clearScopes() { + bitField0_ = (bitField0_ & ~0x00000004); + internalGetMutableScopes().getMutableMap() + .clear(); + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder removeScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableScopes().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableScopes() { + bitField0_ |= 0x00000004; + return internalGetMutableScopes().getMutableMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder putScopes( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableScopes().getMutableMap() + .put(key, value); + bitField0_ |= 0x00000004; + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder putAllScopes( + java.util.Map values) { + internalGetMutableScopes().getMutableMap() + .putAll(values); + bitField0_ |= 0x00000004; + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ImplicitOAuthFlow) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ImplicitOAuthFlow) + private static final io.a2a.grpc.ImplicitOAuthFlow DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ImplicitOAuthFlow(); + } + + public static io.a2a.grpc.ImplicitOAuthFlow getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ImplicitOAuthFlow parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java new file mode 100644 index 000000000..78616d7aa --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java @@ -0,0 +1,115 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ImplicitOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ImplicitOAuthFlow) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The authorizationUrl. + */ + java.lang.String getAuthorizationUrl(); + /** + *
+   * The authorization URL to be used for this flow. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS
+   * 
+ * + * string authorization_url = 1 [json_name = "authorizationUrl"]; + * @return The bytes for authorizationUrl. + */ + com.google.protobuf.ByteString + getAuthorizationUrlBytes(); + + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + java.lang.String getRefreshUrl(); + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + com.google.protobuf.ByteString + getRefreshUrlBytes(); + + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + int getScopesCount(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + boolean containsScopes( + java.lang.String key); + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getScopes(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + java.util.Map + getScopesMap(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + java.lang.String getScopesOrThrow( + java.lang.String key); +} diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java new file mode 100644 index 000000000..8ed2fc1a7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java @@ -0,0 +1,819 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigRequest} + */ +@com.google.protobuf.Generated +public final class ListTaskPushNotificationConfigRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ListTaskPushNotificationConfigRequest) + ListTaskPushNotificationConfigRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + ListTaskPushNotificationConfigRequest.class.getName()); + } + // Use ListTaskPushNotificationConfigRequest.newBuilder() to construct. + private ListTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ListTaskPushNotificationConfigRequest() { + parent_ = ""; + pageToken_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTaskPushNotificationConfigRequest.class, io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder.class); + } + + public static final int PARENT_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object parent_ = ""; + /** + *
+   * parent=tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return The parent. + */ + @java.lang.Override + public java.lang.String getParent() { + java.lang.Object ref = parent_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + parent_ = s; + return s; + } + } + /** + *
+   * parent=tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return The bytes for parent. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getParentBytes() { + java.lang.Object ref = parent_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + parent_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PAGE_SIZE_FIELD_NUMBER = 2; + private int pageSize_ = 0; + /** + *
+   * For AIP-158 these fields are present. Usually not used/needed.
+   * The maximum number of configurations to return.
+   * If unspecified, all configs will be returned.
+   * 
+ * + * int32 page_size = 2 [json_name = "pageSize"]; + * @return The pageSize. + */ + @java.lang.Override + public int getPageSize() { + return pageSize_; + } + + public static final int PAGE_TOKEN_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object pageToken_ = ""; + /** + *
+   * A page token received from a previous
+   * ListTaskPushNotificationConfigRequest call.
+   * Provide this to retrieve the subsequent page.
+   * When paginating, all other parameters provided to
+   * `ListTaskPushNotificationConfigRequest` must match the call that provided
+   * the page token.
+   * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return The pageToken. + */ + @java.lang.Override + public java.lang.String getPageToken() { + java.lang.Object ref = pageToken_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + pageToken_ = s; + return s; + } + } + /** + *
+   * A page token received from a previous
+   * ListTaskPushNotificationConfigRequest call.
+   * Provide this to retrieve the subsequent page.
+   * When paginating, all other parameters provided to
+   * `ListTaskPushNotificationConfigRequest` must match the call that provided
+   * the page token.
+   * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return The bytes for pageToken. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getPageTokenBytes() { + java.lang.Object ref = pageToken_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + pageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, parent_); + } + if (pageSize_ != 0) { + output.writeInt32(2, pageSize_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, pageToken_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, parent_); + } + if (pageSize_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, pageSize_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, pageToken_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ListTaskPushNotificationConfigRequest)) { + return super.equals(obj); + } + io.a2a.grpc.ListTaskPushNotificationConfigRequest other = (io.a2a.grpc.ListTaskPushNotificationConfigRequest) obj; + + if (!getParent() + .equals(other.getParent())) return false; + if (getPageSize() + != other.getPageSize()) return false; + if (!getPageToken() + .equals(other.getPageToken())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + PARENT_FIELD_NUMBER; + hash = (53 * hash) + getParent().hashCode(); + hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; + hash = (53 * hash) + getPageSize(); + hash = (37 * hash) + PAGE_TOKEN_FIELD_NUMBER; + hash = (53 * hash) + getPageToken().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ListTaskPushNotificationConfigRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ListTaskPushNotificationConfigRequest) + io.a2a.grpc.ListTaskPushNotificationConfigRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTaskPushNotificationConfigRequest.class, io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder.class); + } + + // Construct using io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + parent_ = ""; + pageSize_ = 0; + pageToken_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return io.a2a.grpc.ListTaskPushNotificationConfigRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigRequest build() { + io.a2a.grpc.ListTaskPushNotificationConfigRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigRequest buildPartial() { + io.a2a.grpc.ListTaskPushNotificationConfigRequest result = new io.a2a.grpc.ListTaskPushNotificationConfigRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.ListTaskPushNotificationConfigRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.parent_ = parent_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.pageSize_ = pageSize_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.pageToken_ = pageToken_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ListTaskPushNotificationConfigRequest) { + return mergeFrom((io.a2a.grpc.ListTaskPushNotificationConfigRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ListTaskPushNotificationConfigRequest other) { + if (other == io.a2a.grpc.ListTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getParent().isEmpty()) { + parent_ = other.parent_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.getPageSize() != 0) { + setPageSize(other.getPageSize()); + } + if (!other.getPageToken().isEmpty()) { + pageToken_ = other.pageToken_; + bitField0_ |= 0x00000004; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + parent_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 16: { + pageSize_ = input.readInt32(); + bitField0_ |= 0x00000002; + break; + } // case 16 + case 26: { + pageToken_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object parent_ = ""; + /** + *
+     * parent=tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return The parent. + */ + public java.lang.String getParent() { + java.lang.Object ref = parent_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + parent_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * parent=tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return The bytes for parent. + */ + public com.google.protobuf.ByteString + getParentBytes() { + java.lang.Object ref = parent_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + parent_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * parent=tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent"]; + * @param value The parent to set. + * @return This builder for chaining. + */ + public Builder setParent( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + parent_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * parent=tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return This builder for chaining. + */ + public Builder clearParent() { + parent_ = getDefaultInstance().getParent(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * parent=tasks/{id}
+     * 
+ * + * string parent = 1 [json_name = "parent"]; + * @param value The bytes for parent to set. + * @return This builder for chaining. + */ + public Builder setParentBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + parent_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private int pageSize_ ; + /** + *
+     * For AIP-158 these fields are present. Usually not used/needed.
+     * The maximum number of configurations to return.
+     * If unspecified, all configs will be returned.
+     * 
+ * + * int32 page_size = 2 [json_name = "pageSize"]; + * @return The pageSize. + */ + @java.lang.Override + public int getPageSize() { + return pageSize_; + } + /** + *
+     * For AIP-158 these fields are present. Usually not used/needed.
+     * The maximum number of configurations to return.
+     * If unspecified, all configs will be returned.
+     * 
+ * + * int32 page_size = 2 [json_name = "pageSize"]; + * @param value The pageSize to set. + * @return This builder for chaining. + */ + public Builder setPageSize(int value) { + + pageSize_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * For AIP-158 these fields are present. Usually not used/needed.
+     * The maximum number of configurations to return.
+     * If unspecified, all configs will be returned.
+     * 
+ * + * int32 page_size = 2 [json_name = "pageSize"]; + * @return This builder for chaining. + */ + public Builder clearPageSize() { + bitField0_ = (bitField0_ & ~0x00000002); + pageSize_ = 0; + onChanged(); + return this; + } + + private java.lang.Object pageToken_ = ""; + /** + *
+     * A page token received from a previous
+     * ListTaskPushNotificationConfigRequest call.
+     * Provide this to retrieve the subsequent page.
+     * When paginating, all other parameters provided to
+     * `ListTaskPushNotificationConfigRequest` must match the call that provided
+     * the page token.
+     * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return The pageToken. + */ + public java.lang.String getPageToken() { + java.lang.Object ref = pageToken_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + pageToken_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A page token received from a previous
+     * ListTaskPushNotificationConfigRequest call.
+     * Provide this to retrieve the subsequent page.
+     * When paginating, all other parameters provided to
+     * `ListTaskPushNotificationConfigRequest` must match the call that provided
+     * the page token.
+     * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return The bytes for pageToken. + */ + public com.google.protobuf.ByteString + getPageTokenBytes() { + java.lang.Object ref = pageToken_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + pageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A page token received from a previous
+     * ListTaskPushNotificationConfigRequest call.
+     * Provide this to retrieve the subsequent page.
+     * When paginating, all other parameters provided to
+     * `ListTaskPushNotificationConfigRequest` must match the call that provided
+     * the page token.
+     * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @param value The pageToken to set. + * @return This builder for chaining. + */ + public Builder setPageToken( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + pageToken_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * A page token received from a previous
+     * ListTaskPushNotificationConfigRequest call.
+     * Provide this to retrieve the subsequent page.
+     * When paginating, all other parameters provided to
+     * `ListTaskPushNotificationConfigRequest` must match the call that provided
+     * the page token.
+     * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return This builder for chaining. + */ + public Builder clearPageToken() { + pageToken_ = getDefaultInstance().getPageToken(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * A page token received from a previous
+     * ListTaskPushNotificationConfigRequest call.
+     * Provide this to retrieve the subsequent page.
+     * When paginating, all other parameters provided to
+     * `ListTaskPushNotificationConfigRequest` must match the call that provided
+     * the page token.
+     * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @param value The bytes for pageToken to set. + * @return This builder for chaining. + */ + public Builder setPageTokenBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + pageToken_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ListTaskPushNotificationConfigRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ListTaskPushNotificationConfigRequest) + private static final io.a2a.grpc.ListTaskPushNotificationConfigRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ListTaskPushNotificationConfigRequest(); + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ListTaskPushNotificationConfigRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java new file mode 100644 index 000000000..1e6356045 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java @@ -0,0 +1,74 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ListTaskPushNotificationConfigRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ListTaskPushNotificationConfigRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * parent=tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return The parent. + */ + java.lang.String getParent(); + /** + *
+   * parent=tasks/{id}
+   * 
+ * + * string parent = 1 [json_name = "parent"]; + * @return The bytes for parent. + */ + com.google.protobuf.ByteString + getParentBytes(); + + /** + *
+   * For AIP-158 these fields are present. Usually not used/needed.
+   * The maximum number of configurations to return.
+   * If unspecified, all configs will be returned.
+   * 
+ * + * int32 page_size = 2 [json_name = "pageSize"]; + * @return The pageSize. + */ + int getPageSize(); + + /** + *
+   * A page token received from a previous
+   * ListTaskPushNotificationConfigRequest call.
+   * Provide this to retrieve the subsequent page.
+   * When paginating, all other parameters provided to
+   * `ListTaskPushNotificationConfigRequest` must match the call that provided
+   * the page token.
+   * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return The pageToken. + */ + java.lang.String getPageToken(); + /** + *
+   * A page token received from a previous
+   * ListTaskPushNotificationConfigRequest call.
+   * Provide this to retrieve the subsequent page.
+   * When paginating, all other parameters provided to
+   * `ListTaskPushNotificationConfigRequest` must match the call that provided
+   * the page token.
+   * 
+ * + * string page_token = 3 [json_name = "pageToken"]; + * @return The bytes for pageToken. + */ + com.google.protobuf.ByteString + getPageTokenBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java new file mode 100644 index 000000000..beb535430 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java @@ -0,0 +1,891 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigResponse} + */ +@com.google.protobuf.Generated +public final class ListTaskPushNotificationConfigResponse extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ListTaskPushNotificationConfigResponse) + ListTaskPushNotificationConfigResponseOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + ListTaskPushNotificationConfigResponse.class.getName()); + } + // Use ListTaskPushNotificationConfigResponse.newBuilder() to construct. + private ListTaskPushNotificationConfigResponse(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ListTaskPushNotificationConfigResponse() { + configs_ = java.util.Collections.emptyList(); + nextPageToken_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTaskPushNotificationConfigResponse.class, io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder.class); + } + + public static final int CONFIGS_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private java.util.List configs_; + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + @java.lang.Override + public java.util.List getConfigsList() { + return configs_; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + @java.lang.Override + public java.util.List + getConfigsOrBuilderList() { + return configs_; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + @java.lang.Override + public int getConfigsCount() { + return configs_.size(); + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { + return configs_.get(index); + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( + int index) { + return configs_.get(index); + } + + public static final int NEXT_PAGE_TOKEN_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object nextPageToken_ = ""; + /** + *
+   * A token, which can be sent as `page_token` to retrieve the next page.
+   * If this field is omitted, there are no subsequent pages.
+   * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return The nextPageToken. + */ + @java.lang.Override + public java.lang.String getNextPageToken() { + java.lang.Object ref = nextPageToken_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nextPageToken_ = s; + return s; + } + } + /** + *
+   * A token, which can be sent as `page_token` to retrieve the next page.
+   * If this field is omitted, there are no subsequent pages.
+   * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return The bytes for nextPageToken. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNextPageTokenBytes() { + java.lang.Object ref = nextPageToken_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nextPageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < configs_.size(); i++) { + output.writeMessage(1, configs_.get(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(nextPageToken_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, nextPageToken_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < configs_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, configs_.get(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(nextPageToken_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, nextPageToken_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ListTaskPushNotificationConfigResponse)) { + return super.equals(obj); + } + io.a2a.grpc.ListTaskPushNotificationConfigResponse other = (io.a2a.grpc.ListTaskPushNotificationConfigResponse) obj; + + if (!getConfigsList() + .equals(other.getConfigsList())) return false; + if (!getNextPageToken() + .equals(other.getNextPageToken())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getConfigsCount() > 0) { + hash = (37 * hash) + CONFIGS_FIELD_NUMBER; + hash = (53 * hash) + getConfigsList().hashCode(); + } + hash = (37 * hash) + NEXT_PAGE_TOKEN_FIELD_NUMBER; + hash = (53 * hash) + getNextPageToken().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ListTaskPushNotificationConfigResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigResponse} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ListTaskPushNotificationConfigResponse) + io.a2a.grpc.ListTaskPushNotificationConfigResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTaskPushNotificationConfigResponse.class, io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder.class); + } + + // Construct using io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (configsBuilder_ == null) { + configs_ = java.util.Collections.emptyList(); + } else { + configs_ = null; + configsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + nextPageToken_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigResponse getDefaultInstanceForType() { + return io.a2a.grpc.ListTaskPushNotificationConfigResponse.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigResponse build() { + io.a2a.grpc.ListTaskPushNotificationConfigResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigResponse buildPartial() { + io.a2a.grpc.ListTaskPushNotificationConfigResponse result = new io.a2a.grpc.ListTaskPushNotificationConfigResponse(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.ListTaskPushNotificationConfigResponse result) { + if (configsBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + configs_ = java.util.Collections.unmodifiableList(configs_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.configs_ = configs_; + } else { + result.configs_ = configsBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.ListTaskPushNotificationConfigResponse result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.nextPageToken_ = nextPageToken_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ListTaskPushNotificationConfigResponse) { + return mergeFrom((io.a2a.grpc.ListTaskPushNotificationConfigResponse)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ListTaskPushNotificationConfigResponse other) { + if (other == io.a2a.grpc.ListTaskPushNotificationConfigResponse.getDefaultInstance()) return this; + if (configsBuilder_ == null) { + if (!other.configs_.isEmpty()) { + if (configs_.isEmpty()) { + configs_ = other.configs_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureConfigsIsMutable(); + configs_.addAll(other.configs_); + } + onChanged(); + } + } else { + if (!other.configs_.isEmpty()) { + if (configsBuilder_.isEmpty()) { + configsBuilder_.dispose(); + configsBuilder_ = null; + configs_ = other.configs_; + bitField0_ = (bitField0_ & ~0x00000001); + configsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetConfigsFieldBuilder() : null; + } else { + configsBuilder_.addAllMessages(other.configs_); + } + } + } + if (!other.getNextPageToken().isEmpty()) { + nextPageToken_ = other.nextPageToken_; + bitField0_ |= 0x00000002; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + io.a2a.grpc.TaskPushNotificationConfig m = + input.readMessage( + io.a2a.grpc.TaskPushNotificationConfig.parser(), + extensionRegistry); + if (configsBuilder_ == null) { + ensureConfigsIsMutable(); + configs_.add(m); + } else { + configsBuilder_.addMessage(m); + } + break; + } // case 10 + case 18: { + nextPageToken_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.util.List configs_ = + java.util.Collections.emptyList(); + private void ensureConfigsIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + configs_ = new java.util.ArrayList(configs_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configsBuilder_; + + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public java.util.List getConfigsList() { + if (configsBuilder_ == null) { + return java.util.Collections.unmodifiableList(configs_); + } else { + return configsBuilder_.getMessageList(); + } + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public int getConfigsCount() { + if (configsBuilder_ == null) { + return configs_.size(); + } else { + return configsBuilder_.getCount(); + } + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { + if (configsBuilder_ == null) { + return configs_.get(index); + } else { + return configsBuilder_.getMessage(index); + } + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder setConfigs( + int index, io.a2a.grpc.TaskPushNotificationConfig value) { + if (configsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureConfigsIsMutable(); + configs_.set(index, value); + onChanged(); + } else { + configsBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder setConfigs( + int index, io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { + if (configsBuilder_ == null) { + ensureConfigsIsMutable(); + configs_.set(index, builderForValue.build()); + onChanged(); + } else { + configsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder addConfigs(io.a2a.grpc.TaskPushNotificationConfig value) { + if (configsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureConfigsIsMutable(); + configs_.add(value); + onChanged(); + } else { + configsBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder addConfigs( + int index, io.a2a.grpc.TaskPushNotificationConfig value) { + if (configsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureConfigsIsMutable(); + configs_.add(index, value); + onChanged(); + } else { + configsBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder addConfigs( + io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { + if (configsBuilder_ == null) { + ensureConfigsIsMutable(); + configs_.add(builderForValue.build()); + onChanged(); + } else { + configsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder addConfigs( + int index, io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { + if (configsBuilder_ == null) { + ensureConfigsIsMutable(); + configs_.add(index, builderForValue.build()); + onChanged(); + } else { + configsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder addAllConfigs( + java.lang.Iterable values) { + if (configsBuilder_ == null) { + ensureConfigsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, configs_); + onChanged(); + } else { + configsBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder clearConfigs() { + if (configsBuilder_ == null) { + configs_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + configsBuilder_.clear(); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public Builder removeConfigs(int index) { + if (configsBuilder_ == null) { + ensureConfigsIsMutable(); + configs_.remove(index); + onChanged(); + } else { + configsBuilder_.remove(index); + } + return this; + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigsBuilder( + int index) { + return internalGetConfigsFieldBuilder().getBuilder(index); + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( + int index) { + if (configsBuilder_ == null) { + return configs_.get(index); } else { + return configsBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public java.util.List + getConfigsOrBuilderList() { + if (configsBuilder_ != null) { + return configsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(configs_); + } + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder() { + return internalGetConfigsFieldBuilder().addBuilder( + io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()); + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder( + int index) { + return internalGetConfigsFieldBuilder().addBuilder( + index, io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()); + } + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + public java.util.List + getConfigsBuilderList() { + return internalGetConfigsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> + internalGetConfigsFieldBuilder() { + if (configsBuilder_ == null) { + configsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder>( + configs_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + configs_ = null; + } + return configsBuilder_; + } + + private java.lang.Object nextPageToken_ = ""; + /** + *
+     * A token, which can be sent as `page_token` to retrieve the next page.
+     * If this field is omitted, there are no subsequent pages.
+     * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return The nextPageToken. + */ + public java.lang.String getNextPageToken() { + java.lang.Object ref = nextPageToken_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nextPageToken_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A token, which can be sent as `page_token` to retrieve the next page.
+     * If this field is omitted, there are no subsequent pages.
+     * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return The bytes for nextPageToken. + */ + public com.google.protobuf.ByteString + getNextPageTokenBytes() { + java.lang.Object ref = nextPageToken_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nextPageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A token, which can be sent as `page_token` to retrieve the next page.
+     * If this field is omitted, there are no subsequent pages.
+     * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @param value The nextPageToken to set. + * @return This builder for chaining. + */ + public Builder setNextPageToken( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + nextPageToken_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A token, which can be sent as `page_token` to retrieve the next page.
+     * If this field is omitted, there are no subsequent pages.
+     * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return This builder for chaining. + */ + public Builder clearNextPageToken() { + nextPageToken_ = getDefaultInstance().getNextPageToken(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * A token, which can be sent as `page_token` to retrieve the next page.
+     * If this field is omitted, there are no subsequent pages.
+     * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @param value The bytes for nextPageToken to set. + * @return This builder for chaining. + */ + public Builder setNextPageTokenBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + nextPageToken_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ListTaskPushNotificationConfigResponse) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ListTaskPushNotificationConfigResponse) + private static final io.a2a.grpc.ListTaskPushNotificationConfigResponse DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ListTaskPushNotificationConfigResponse(); + } + + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ListTaskPushNotificationConfigResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ListTaskPushNotificationConfigResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java new file mode 100644 index 000000000..211aeee5a --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java @@ -0,0 +1,58 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ListTaskPushNotificationConfigResponseOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ListTaskPushNotificationConfigResponse) + com.google.protobuf.MessageOrBuilder { + + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + java.util.List + getConfigsList(); + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index); + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + int getConfigsCount(); + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + java.util.List + getConfigsOrBuilderList(); + /** + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + */ + io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( + int index); + + /** + *
+   * A token, which can be sent as `page_token` to retrieve the next page.
+   * If this field is omitted, there are no subsequent pages.
+   * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return The nextPageToken. + */ + java.lang.String getNextPageToken(); + /** + *
+   * A token, which can be sent as `page_token` to retrieve the next page.
+   * If this field is omitted, there are no subsequent pages.
+   * 
+ * + * string next_page_token = 2 [json_name = "nextPageToken"]; + * @return The bytes for nextPageToken. + */ + com.google.protobuf.ByteString + getNextPageTokenBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/Message.java b/grpc/src/main/java/io/a2a/grpc/Message.java new file mode 100644 index 000000000..cddcf43e5 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/Message.java @@ -0,0 +1,1983 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Message is one unit of communication between client and server. It is
+ * associated with a context and optionally a task. Since the server is
+ * responsible for the context definition, it must always provide a context_id
+ * in its messages. The client can optionally provide the context_id if it
+ * knows the context to associate the message to. Similarly for task_id,
+ * except the server decides if a task is created and whether to include the
+ * task_id.
+ * 
+ * + * Protobuf type {@code a2a.v1.Message} + */ +@com.google.protobuf.Generated +public final class Message extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.Message) + MessageOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + Message.class.getName()); + } + // Use Message.newBuilder() to construct. + private Message(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Message() { + messageId_ = ""; + contextId_ = ""; + taskId_ = ""; + role_ = 0; + content_ = java.util.Collections.emptyList(); + extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Message_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Message_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Message.class, io.a2a.grpc.Message.Builder.class); + } + + private int bitField0_; + public static final int MESSAGE_ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object messageId_ = ""; + /** + *
+   * The message id of the message. This is required and created by the
+   * message creator.
+   * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return The messageId. + */ + @java.lang.Override + public java.lang.String getMessageId() { + java.lang.Object ref = messageId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + messageId_ = s; + return s; + } + } + /** + *
+   * The message id of the message. This is required and created by the
+   * message creator.
+   * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return The bytes for messageId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getMessageIdBytes() { + java.lang.Object ref = messageId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + messageId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONTEXT_ID_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object contextId_ = ""; + /** + *
+   * The context id of the message. This is optional and if set, the message
+   * will be associated with the given context.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + @java.lang.Override + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } + } + /** + *
+   * The context id of the message. This is optional and if set, the message
+   * will be associated with the given context.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TASK_ID_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object taskId_ = ""; + /** + *
+   * The task id of the message. This is optional and if set, the message
+   * will be associated with the given task.
+   * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return The taskId. + */ + @java.lang.Override + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } + } + /** + *
+   * The task id of the message. This is optional and if set, the message
+   * will be associated with the given task.
+   * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ROLE_FIELD_NUMBER = 4; + private int role_ = 0; + /** + *
+   * A role for the message.
+   * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return The enum numeric value on the wire for role. + */ + @java.lang.Override public int getRoleValue() { + return role_; + } + /** + *
+   * A role for the message.
+   * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return The role. + */ + @java.lang.Override public io.a2a.grpc.Role getRole() { + io.a2a.grpc.Role result = io.a2a.grpc.Role.forNumber(role_); + return result == null ? io.a2a.grpc.Role.UNRECOGNIZED : result; + } + + public static final int CONTENT_FIELD_NUMBER = 5; + @SuppressWarnings("serial") + private java.util.List content_; + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + @java.lang.Override + public java.util.List getContentList() { + return content_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + @java.lang.Override + public java.util.List + getContentOrBuilderList() { + return content_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + @java.lang.Override + public int getContentCount() { + return content_.size(); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + @java.lang.Override + public io.a2a.grpc.Part getContent(int index) { + return content_.get(index); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + @java.lang.Override + public io.a2a.grpc.PartOrBuilder getContentOrBuilder( + int index) { + return content_.get(index); + } + + public static final int METADATA_FIELD_NUMBER = 6; + private com.google.protobuf.Struct metadata_; + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * Any optional metadata to provide along with the message.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * Any optional metadata to provide along with the message.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * Any optional metadata to provide along with the message.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + + public static final int EXTENSIONS_FIELD_NUMBER = 7; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return A list containing the extensions. + */ + public com.google.protobuf.ProtocolStringList + getExtensionsList() { + return extensions_; + } + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return The count of extensions. + */ + public int getExtensionsCount() { + return extensions_.size(); + } + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the element to return. + * @return The extensions at the given index. + */ + public java.lang.String getExtensions(int index) { + return extensions_.get(index); + } + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the value to return. + * @return The bytes of the extensions at the given index. + */ + public com.google.protobuf.ByteString + getExtensionsBytes(int index) { + return extensions_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(messageId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, messageId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, contextId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, taskId_); + } + if (role_ != io.a2a.grpc.Role.ROLE_UNSPECIFIED.getNumber()) { + output.writeEnum(4, role_); + } + for (int i = 0; i < content_.size(); i++) { + output.writeMessage(5, content_.get(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(6, getMetadata()); + } + for (int i = 0; i < extensions_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 7, extensions_.getRaw(i)); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(messageId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, messageId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, contextId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, taskId_); + } + if (role_ != io.a2a.grpc.Role.ROLE_UNSPECIFIED.getNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(4, role_); + } + for (int i = 0; i < content_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, content_.get(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, getMetadata()); + } + { + int dataSize = 0; + for (int i = 0; i < extensions_.size(); i++) { + dataSize += computeStringSizeNoTag(extensions_.getRaw(i)); + } + size += dataSize; + size += 1 * getExtensionsList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.Message)) { + return super.equals(obj); + } + io.a2a.grpc.Message other = (io.a2a.grpc.Message) obj; + + if (!getMessageId() + .equals(other.getMessageId())) return false; + if (!getContextId() + .equals(other.getContextId())) return false; + if (!getTaskId() + .equals(other.getTaskId())) return false; + if (role_ != other.role_) return false; + if (!getContentList() + .equals(other.getContentList())) return false; + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } + if (!getExtensionsList() + .equals(other.getExtensionsList())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + MESSAGE_ID_FIELD_NUMBER; + hash = (53 * hash) + getMessageId().hashCode(); + hash = (37 * hash) + CONTEXT_ID_FIELD_NUMBER; + hash = (53 * hash) + getContextId().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); + hash = (37 * hash) + ROLE_FIELD_NUMBER; + hash = (53 * hash) + role_; + if (getContentCount() > 0) { + hash = (37 * hash) + CONTENT_FIELD_NUMBER; + hash = (53 * hash) + getContentList().hashCode(); + } + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } + if (getExtensionsCount() > 0) { + hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER; + hash = (53 * hash) + getExtensionsList().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.Message parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Message parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Message parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Message parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Message parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Message parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Message parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Message parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.Message parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.Message parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.Message parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Message parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.Message prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Message is one unit of communication between client and server. It is
+   * associated with a context and optionally a task. Since the server is
+   * responsible for the context definition, it must always provide a context_id
+   * in its messages. The client can optionally provide the context_id if it
+   * knows the context to associate the message to. Similarly for task_id,
+   * except the server decides if a task is created and whether to include the
+   * task_id.
+   * 
+ * + * Protobuf type {@code a2a.v1.Message} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.Message) + io.a2a.grpc.MessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Message_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Message_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Message.class, io.a2a.grpc.Message.Builder.class); + } + + // Construct using io.a2a.grpc.Message.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetContentFieldBuilder(); + internalGetMetadataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + messageId_ = ""; + contextId_ = ""; + taskId_ = ""; + role_ = 0; + if (contentBuilder_ == null) { + content_ = java.util.Collections.emptyList(); + } else { + content_ = null; + contentBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Message_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.Message getDefaultInstanceForType() { + return io.a2a.grpc.Message.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.Message build() { + io.a2a.grpc.Message result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.Message buildPartial() { + io.a2a.grpc.Message result = new io.a2a.grpc.Message(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.Message result) { + if (contentBuilder_ == null) { + if (((bitField0_ & 0x00000010) != 0)) { + content_ = java.util.Collections.unmodifiableList(content_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.content_ = content_; + } else { + result.content_ = contentBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.Message result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.messageId_ = messageId_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.contextId_ = contextId_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.taskId_ = taskId_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.role_ = role_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000020) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000040) != 0)) { + extensions_.makeImmutable(); + result.extensions_ = extensions_; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.Message) { + return mergeFrom((io.a2a.grpc.Message)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.Message other) { + if (other == io.a2a.grpc.Message.getDefaultInstance()) return this; + if (!other.getMessageId().isEmpty()) { + messageId_ = other.messageId_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getContextId().isEmpty()) { + contextId_ = other.contextId_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (other.role_ != 0) { + setRoleValue(other.getRoleValue()); + } + if (contentBuilder_ == null) { + if (!other.content_.isEmpty()) { + if (content_.isEmpty()) { + content_ = other.content_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureContentIsMutable(); + content_.addAll(other.content_); + } + onChanged(); + } + } else { + if (!other.content_.isEmpty()) { + if (contentBuilder_.isEmpty()) { + contentBuilder_.dispose(); + contentBuilder_ = null; + content_ = other.content_; + bitField0_ = (bitField0_ & ~0x00000010); + contentBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetContentFieldBuilder() : null; + } else { + contentBuilder_.addAllMessages(other.content_); + } + } + } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } + if (!other.extensions_.isEmpty()) { + if (extensions_.isEmpty()) { + extensions_ = other.extensions_; + bitField0_ |= 0x00000040; + } else { + ensureExtensionsIsMutable(); + extensions_.addAll(other.extensions_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + messageId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + contextId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + taskId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 32: { + role_ = input.readEnum(); + bitField0_ |= 0x00000008; + break; + } // case 32 + case 42: { + io.a2a.grpc.Part m = + input.readMessage( + io.a2a.grpc.Part.parser(), + extensionRegistry); + if (contentBuilder_ == null) { + ensureContentIsMutable(); + content_.add(m); + } else { + contentBuilder_.addMessage(m); + } + break; + } // case 42 + case 50: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000020; + break; + } // case 50 + case 58: { + java.lang.String s = input.readStringRequireUtf8(); + ensureExtensionsIsMutable(); + extensions_.add(s); + break; + } // case 58 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object messageId_ = ""; + /** + *
+     * The message id of the message. This is required and created by the
+     * message creator.
+     * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return The messageId. + */ + public java.lang.String getMessageId() { + java.lang.Object ref = messageId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + messageId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The message id of the message. This is required and created by the
+     * message creator.
+     * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return The bytes for messageId. + */ + public com.google.protobuf.ByteString + getMessageIdBytes() { + java.lang.Object ref = messageId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + messageId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The message id of the message. This is required and created by the
+     * message creator.
+     * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @param value The messageId to set. + * @return This builder for chaining. + */ + public Builder setMessageId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + messageId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The message id of the message. This is required and created by the
+     * message creator.
+     * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return This builder for chaining. + */ + public Builder clearMessageId() { + messageId_ = getDefaultInstance().getMessageId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The message id of the message. This is required and created by the
+     * message creator.
+     * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @param value The bytes for messageId to set. + * @return This builder for chaining. + */ + public Builder setMessageIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + messageId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object contextId_ = ""; + /** + *
+     * The context id of the message. This is optional and if set, the message
+     * will be associated with the given context.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The context id of the message. This is optional and if set, the message
+     * will be associated with the given context.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The context id of the message. This is optional and if set, the message
+     * will be associated with the given context.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The contextId to set. + * @return This builder for chaining. + */ + public Builder setContextId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The context id of the message. This is optional and if set, the message
+     * will be associated with the given context.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return This builder for chaining. + */ + public Builder clearContextId() { + contextId_ = getDefaultInstance().getContextId(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The context id of the message. This is optional and if set, the message
+     * will be associated with the given context.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The bytes for contextId to set. + * @return This builder for chaining. + */ + public Builder setContextIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object taskId_ = ""; + /** + *
+     * The task id of the message. This is optional and if set, the message
+     * will be associated with the given task.
+     * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return The taskId. + */ + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The task id of the message. This is optional and if set, the message
+     * will be associated with the given task.
+     * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The task id of the message. This is optional and if set, the message
+     * will be associated with the given task.
+     * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @param value The taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + taskId_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The task id of the message. This is optional and if set, the message
+     * will be associated with the given task.
+     * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return This builder for chaining. + */ + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * The task id of the message. This is optional and if set, the message
+     * will be associated with the given task.
+     * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @param value The bytes for taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + taskId_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private int role_ = 0; + /** + *
+     * A role for the message.
+     * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return The enum numeric value on the wire for role. + */ + @java.lang.Override public int getRoleValue() { + return role_; + } + /** + *
+     * A role for the message.
+     * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @param value The enum numeric value on the wire for role to set. + * @return This builder for chaining. + */ + public Builder setRoleValue(int value) { + role_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * A role for the message.
+     * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return The role. + */ + @java.lang.Override + public io.a2a.grpc.Role getRole() { + io.a2a.grpc.Role result = io.a2a.grpc.Role.forNumber(role_); + return result == null ? io.a2a.grpc.Role.UNRECOGNIZED : result; + } + /** + *
+     * A role for the message.
+     * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @param value The role to set. + * @return This builder for chaining. + */ + public Builder setRole(io.a2a.grpc.Role value) { + if (value == null) { throw new NullPointerException(); } + bitField0_ |= 0x00000008; + role_ = value.getNumber(); + onChanged(); + return this; + } + /** + *
+     * A role for the message.
+     * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return This builder for chaining. + */ + public Builder clearRole() { + bitField0_ = (bitField0_ & ~0x00000008); + role_ = 0; + onChanged(); + return this; + } + + private java.util.List content_ = + java.util.Collections.emptyList(); + private void ensureContentIsMutable() { + if (!((bitField0_ & 0x00000010) != 0)) { + content_ = new java.util.ArrayList(content_); + bitField0_ |= 0x00000010; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> contentBuilder_; + + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public java.util.List getContentList() { + if (contentBuilder_ == null) { + return java.util.Collections.unmodifiableList(content_); + } else { + return contentBuilder_.getMessageList(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public int getContentCount() { + if (contentBuilder_ == null) { + return content_.size(); + } else { + return contentBuilder_.getCount(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public io.a2a.grpc.Part getContent(int index) { + if (contentBuilder_ == null) { + return content_.get(index); + } else { + return contentBuilder_.getMessage(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder setContent( + int index, io.a2a.grpc.Part value) { + if (contentBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContentIsMutable(); + content_.set(index, value); + onChanged(); + } else { + contentBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder setContent( + int index, io.a2a.grpc.Part.Builder builderForValue) { + if (contentBuilder_ == null) { + ensureContentIsMutable(); + content_.set(index, builderForValue.build()); + onChanged(); + } else { + contentBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder addContent(io.a2a.grpc.Part value) { + if (contentBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContentIsMutable(); + content_.add(value); + onChanged(); + } else { + contentBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder addContent( + int index, io.a2a.grpc.Part value) { + if (contentBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureContentIsMutable(); + content_.add(index, value); + onChanged(); + } else { + contentBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder addContent( + io.a2a.grpc.Part.Builder builderForValue) { + if (contentBuilder_ == null) { + ensureContentIsMutable(); + content_.add(builderForValue.build()); + onChanged(); + } else { + contentBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder addContent( + int index, io.a2a.grpc.Part.Builder builderForValue) { + if (contentBuilder_ == null) { + ensureContentIsMutable(); + content_.add(index, builderForValue.build()); + onChanged(); + } else { + contentBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder addAllContent( + java.lang.Iterable values) { + if (contentBuilder_ == null) { + ensureContentIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, content_); + onChanged(); + } else { + contentBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder clearContent() { + if (contentBuilder_ == null) { + content_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + } else { + contentBuilder_.clear(); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public Builder removeContent(int index) { + if (contentBuilder_ == null) { + ensureContentIsMutable(); + content_.remove(index); + onChanged(); + } else { + contentBuilder_.remove(index); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public io.a2a.grpc.Part.Builder getContentBuilder( + int index) { + return internalGetContentFieldBuilder().getBuilder(index); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public io.a2a.grpc.PartOrBuilder getContentOrBuilder( + int index) { + if (contentBuilder_ == null) { + return content_.get(index); } else { + return contentBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public java.util.List + getContentOrBuilderList() { + if (contentBuilder_ != null) { + return contentBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(content_); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public io.a2a.grpc.Part.Builder addContentBuilder() { + return internalGetContentFieldBuilder().addBuilder( + io.a2a.grpc.Part.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public io.a2a.grpc.Part.Builder addContentBuilder( + int index) { + return internalGetContentFieldBuilder().addBuilder( + index, io.a2a.grpc.Part.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Content is the container of the message content.
+     * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + public java.util.List + getContentBuilderList() { + return internalGetContentFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> + internalGetContentFieldBuilder() { + if (contentBuilder_ == null) { + contentBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder>( + content_, + ((bitField0_ & 0x00000010) != 0), + getParentForChildren(), + isClean()); + content_ = null; + } + return contentBuilder_; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000020) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000020; + onChanged(); + } + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000020); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * Any optional metadata to provide along with the message.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + + private com.google.protobuf.LazyStringArrayList extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureExtensionsIsMutable() { + if (!extensions_.isModifiable()) { + extensions_ = new com.google.protobuf.LazyStringArrayList(extensions_); + } + bitField0_ |= 0x00000040; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return A list containing the extensions. + */ + public com.google.protobuf.ProtocolStringList + getExtensionsList() { + extensions_.makeImmutable(); + return extensions_; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return The count of extensions. + */ + public int getExtensionsCount() { + return extensions_.size(); + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the element to return. + * @return The extensions at the given index. + */ + public java.lang.String getExtensions(int index) { + return extensions_.get(index); + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the value to return. + * @return The bytes of the extensions at the given index. + */ + public com.google.protobuf.ByteString + getExtensionsBytes(int index) { + return extensions_.getByteString(index); + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index to set the value at. + * @param value The extensions to set. + * @return This builder for chaining. + */ + public Builder setExtensions( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureExtensionsIsMutable(); + extensions_.set(index, value); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param value The extensions to add. + * @return This builder for chaining. + */ + public Builder addExtensions( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureExtensionsIsMutable(); + extensions_.add(value); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param values The extensions to add. + * @return This builder for chaining. + */ + public Builder addAllExtensions( + java.lang.Iterable values) { + ensureExtensionsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, extensions_); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return This builder for chaining. + */ + public Builder clearExtensions() { + extensions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000040);; + onChanged(); + return this; + } + /** + *
+     * The URIs of extensions that are present or contributed to this Message.
+     * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param value The bytes of the extensions to add. + * @return This builder for chaining. + */ + public Builder addExtensionsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureExtensionsIsMutable(); + extensions_.add(value); + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.Message) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.Message) + private static final io.a2a.grpc.Message DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.Message(); + } + + public static io.a2a.grpc.Message getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Message parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.Message getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java new file mode 100644 index 000000000..94c1947b2 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java @@ -0,0 +1,217 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface MessageOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.Message) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The message id of the message. This is required and created by the
+   * message creator.
+   * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return The messageId. + */ + java.lang.String getMessageId(); + /** + *
+   * The message id of the message. This is required and created by the
+   * message creator.
+   * 
+ * + * string message_id = 1 [json_name = "messageId"]; + * @return The bytes for messageId. + */ + com.google.protobuf.ByteString + getMessageIdBytes(); + + /** + *
+   * The context id of the message. This is optional and if set, the message
+   * will be associated with the given context.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + java.lang.String getContextId(); + /** + *
+   * The context id of the message. This is optional and if set, the message
+   * will be associated with the given context.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + com.google.protobuf.ByteString + getContextIdBytes(); + + /** + *
+   * The task id of the message. This is optional and if set, the message
+   * will be associated with the given task.
+   * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return The taskId. + */ + java.lang.String getTaskId(); + /** + *
+   * The task id of the message. This is optional and if set, the message
+   * will be associated with the given task.
+   * 
+ * + * string task_id = 3 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + com.google.protobuf.ByteString + getTaskIdBytes(); + + /** + *
+   * A role for the message.
+   * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return The enum numeric value on the wire for role. + */ + int getRoleValue(); + /** + *
+   * A role for the message.
+   * 
+ * + * .a2a.v1.Role role = 4 [json_name = "role"]; + * @return The role. + */ + io.a2a.grpc.Role getRole(); + + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + java.util.List + getContentList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + io.a2a.grpc.Part getContent(int index); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + int getContentCount(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + java.util.List + getContentOrBuilderList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Content is the container of the message content.
+   * 
+ * + * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + */ + io.a2a.grpc.PartOrBuilder getContentOrBuilder( + int index); + + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * Any optional metadata to provide along with the message.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * Any optional metadata to provide along with the message.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * Any optional metadata to provide along with the message.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); + + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return A list containing the extensions. + */ + java.util.List + getExtensionsList(); + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @return The count of extensions. + */ + int getExtensionsCount(); + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the element to return. + * @return The extensions at the given index. + */ + java.lang.String getExtensions(int index); + /** + *
+   * The URIs of extensions that are present or contributed to this Message.
+   * 
+ * + * repeated string extensions = 7 [json_name = "extensions"]; + * @param index The index of the value to return. + * @return The bytes of the extensions at the given index. + */ + com.google.protobuf.ByteString + getExtensionsBytes(int index); +} diff --git a/grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java new file mode 100644 index 000000000..22a91b84a --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java @@ -0,0 +1,771 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.OAuth2SecurityScheme} + */ +@com.google.protobuf.Generated +public final class OAuth2SecurityScheme extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.OAuth2SecurityScheme) + OAuth2SecuritySchemeOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + OAuth2SecurityScheme.class.getName()); + } + // Use OAuth2SecurityScheme.newBuilder() to construct. + private OAuth2SecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private OAuth2SecurityScheme() { + description_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuth2SecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.OAuth2SecurityScheme.class, io.a2a.grpc.OAuth2SecurityScheme.Builder.class); + } + + private int bitField0_; + public static final int DESCRIPTION_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FLOWS_FIELD_NUMBER = 2; + private io.a2a.grpc.OAuthFlows flows_; + /** + *
+   * An object containing configuration information for the flow types supported
+   * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * @return Whether the flows field is set. + */ + @java.lang.Override + public boolean hasFlows() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * An object containing configuration information for the flow types supported
+   * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * @return The flows. + */ + @java.lang.Override + public io.a2a.grpc.OAuthFlows getFlows() { + return flows_ == null ? io.a2a.grpc.OAuthFlows.getDefaultInstance() : flows_; + } + /** + *
+   * An object containing configuration information for the flow types supported
+   * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + @java.lang.Override + public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { + return flows_ == null ? io.a2a.grpc.OAuthFlows.getDefaultInstance() : flows_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, description_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(2, getFlows()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, description_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getFlows()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.OAuth2SecurityScheme)) { + return super.equals(obj); + } + io.a2a.grpc.OAuth2SecurityScheme other = (io.a2a.grpc.OAuth2SecurityScheme) obj; + + if (!getDescription() + .equals(other.getDescription())) return false; + if (hasFlows() != other.hasFlows()) return false; + if (hasFlows()) { + if (!getFlows() + .equals(other.getFlows())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + if (hasFlows()) { + hash = (37 * hash) + FLOWS_FIELD_NUMBER; + hash = (53 * hash) + getFlows().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.OAuth2SecurityScheme parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.OAuth2SecurityScheme parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.OAuth2SecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.OAuth2SecurityScheme prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.OAuth2SecurityScheme} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.OAuth2SecurityScheme) + io.a2a.grpc.OAuth2SecuritySchemeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuth2SecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.OAuth2SecurityScheme.class, io.a2a.grpc.OAuth2SecurityScheme.Builder.class); + } + + // Construct using io.a2a.grpc.OAuth2SecurityScheme.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetFlowsFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + description_ = ""; + flows_ = null; + if (flowsBuilder_ != null) { + flowsBuilder_.dispose(); + flowsBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuth2SecurityScheme_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.OAuth2SecurityScheme getDefaultInstanceForType() { + return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.OAuth2SecurityScheme build() { + io.a2a.grpc.OAuth2SecurityScheme result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.OAuth2SecurityScheme buildPartial() { + io.a2a.grpc.OAuth2SecurityScheme result = new io.a2a.grpc.OAuth2SecurityScheme(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.OAuth2SecurityScheme result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.description_ = description_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.flows_ = flowsBuilder_ == null + ? flows_ + : flowsBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.OAuth2SecurityScheme) { + return mergeFrom((io.a2a.grpc.OAuth2SecurityScheme)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.OAuth2SecurityScheme other) { + if (other == io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance()) return this; + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.hasFlows()) { + mergeFlows(other.getFlows()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetFlowsFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object description_ = ""; + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private io.a2a.grpc.OAuthFlows flows_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OAuthFlows, io.a2a.grpc.OAuthFlows.Builder, io.a2a.grpc.OAuthFlowsOrBuilder> flowsBuilder_; + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * @return Whether the flows field is set. + */ + public boolean hasFlows() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * @return The flows. + */ + public io.a2a.grpc.OAuthFlows getFlows() { + if (flowsBuilder_ == null) { + return flows_ == null ? io.a2a.grpc.OAuthFlows.getDefaultInstance() : flows_; + } else { + return flowsBuilder_.getMessage(); + } + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + public Builder setFlows(io.a2a.grpc.OAuthFlows value) { + if (flowsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + flows_ = value; + } else { + flowsBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + public Builder setFlows( + io.a2a.grpc.OAuthFlows.Builder builderForValue) { + if (flowsBuilder_ == null) { + flows_ = builderForValue.build(); + } else { + flowsBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + public Builder mergeFlows(io.a2a.grpc.OAuthFlows value) { + if (flowsBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0) && + flows_ != null && + flows_ != io.a2a.grpc.OAuthFlows.getDefaultInstance()) { + getFlowsBuilder().mergeFrom(value); + } else { + flows_ = value; + } + } else { + flowsBuilder_.mergeFrom(value); + } + if (flows_ != null) { + bitField0_ |= 0x00000002; + onChanged(); + } + return this; + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + public Builder clearFlows() { + bitField0_ = (bitField0_ & ~0x00000002); + flows_ = null; + if (flowsBuilder_ != null) { + flowsBuilder_.dispose(); + flowsBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + public io.a2a.grpc.OAuthFlows.Builder getFlowsBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return internalGetFlowsFieldBuilder().getBuilder(); + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { + if (flowsBuilder_ != null) { + return flowsBuilder_.getMessageOrBuilder(); + } else { + return flows_ == null ? + io.a2a.grpc.OAuthFlows.getDefaultInstance() : flows_; + } + } + /** + *
+     * An object containing configuration information for the flow types supported
+     * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OAuthFlows, io.a2a.grpc.OAuthFlows.Builder, io.a2a.grpc.OAuthFlowsOrBuilder> + internalGetFlowsFieldBuilder() { + if (flowsBuilder_ == null) { + flowsBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OAuthFlows, io.a2a.grpc.OAuthFlows.Builder, io.a2a.grpc.OAuthFlowsOrBuilder>( + getFlows(), + getParentForChildren(), + isClean()); + flows_ = null; + } + return flowsBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.OAuth2SecurityScheme) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.OAuth2SecurityScheme) + private static final io.a2a.grpc.OAuth2SecurityScheme DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.OAuth2SecurityScheme(); + } + + public static io.a2a.grpc.OAuth2SecurityScheme getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public OAuth2SecurityScheme parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.OAuth2SecurityScheme getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java new file mode 100644 index 000000000..40048a192 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java @@ -0,0 +1,59 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface OAuth2SecuritySchemeOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.OAuth2SecurityScheme) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * An object containing configuration information for the flow types supported
+   * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * @return Whether the flows field is set. + */ + boolean hasFlows(); + /** + *
+   * An object containing configuration information for the flow types supported
+   * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * @return The flows. + */ + io.a2a.grpc.OAuthFlows getFlows(); + /** + *
+   * An object containing configuration information for the flow types supported
+   * 
+ * + * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + */ + io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/grpc/src/main/java/io/a2a/grpc/OAuthFlows.java new file mode 100644 index 000000000..5d5927d17 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/OAuthFlows.java @@ -0,0 +1,1273 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.OAuthFlows} + */ +@com.google.protobuf.Generated +public final class OAuthFlows extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.OAuthFlows) + OAuthFlowsOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + OAuthFlows.class.getName()); + } + // Use OAuthFlows.newBuilder() to construct. + private OAuthFlows(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private OAuthFlows() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuthFlows_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuthFlows_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.OAuthFlows.class, io.a2a.grpc.OAuthFlows.Builder.class); + } + + private int flowCase_ = 0; + @SuppressWarnings("serial") + private java.lang.Object flow_; + public enum FlowCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + AUTHORIZATION_CODE(1), + CLIENT_CREDENTIALS(2), + IMPLICIT(3), + PASSWORD(4), + FLOW_NOT_SET(0); + private final int value; + private FlowCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static FlowCase valueOf(int value) { + return forNumber(value); + } + + public static FlowCase forNumber(int value) { + switch (value) { + case 1: return AUTHORIZATION_CODE; + case 2: return CLIENT_CREDENTIALS; + case 3: return IMPLICIT; + case 4: return PASSWORD; + case 0: return FLOW_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public FlowCase + getFlowCase() { + return FlowCase.forNumber( + flowCase_); + } + + public static final int AUTHORIZATION_CODE_FIELD_NUMBER = 1; + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * @return Whether the authorizationCode field is set. + */ + @java.lang.Override + public boolean hasAuthorizationCode() { + return flowCase_ == 1; + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * @return The authorizationCode. + */ + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode() { + if (flowCase_ == 1) { + return (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_; + } + return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder() { + if (flowCase_ == 1) { + return (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_; + } + return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } + + public static final int CLIENT_CREDENTIALS_FIELD_NUMBER = 2; + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * @return Whether the clientCredentials field is set. + */ + @java.lang.Override + public boolean hasClientCredentials() { + return flowCase_ == 2; + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * @return The clientCredentials. + */ + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials() { + if (flowCase_ == 2) { + return (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_; + } + return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder() { + if (flowCase_ == 2) { + return (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_; + } + return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } + + public static final int IMPLICIT_FIELD_NUMBER = 3; + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * @return Whether the implicit field is set. + */ + @java.lang.Override + public boolean hasImplicit() { + return flowCase_ == 3; + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * @return The implicit. + */ + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { + if (flowCase_ == 3) { + return (io.a2a.grpc.ImplicitOAuthFlow) flow_; + } + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { + if (flowCase_ == 3) { + return (io.a2a.grpc.ImplicitOAuthFlow) flow_; + } + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + + public static final int PASSWORD_FIELD_NUMBER = 4; + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * @return Whether the password field is set. + */ + @java.lang.Override + public boolean hasPassword() { + return flowCase_ == 4; + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * @return The password. + */ + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow getPassword() { + if (flowCase_ == 4) { + return (io.a2a.grpc.PasswordOAuthFlow) flow_; + } + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { + if (flowCase_ == 4) { + return (io.a2a.grpc.PasswordOAuthFlow) flow_; + } + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (flowCase_ == 1) { + output.writeMessage(1, (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_); + } + if (flowCase_ == 2) { + output.writeMessage(2, (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_); + } + if (flowCase_ == 3) { + output.writeMessage(3, (io.a2a.grpc.ImplicitOAuthFlow) flow_); + } + if (flowCase_ == 4) { + output.writeMessage(4, (io.a2a.grpc.PasswordOAuthFlow) flow_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (flowCase_ == 1) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_); + } + if (flowCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_); + } + if (flowCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, (io.a2a.grpc.ImplicitOAuthFlow) flow_); + } + if (flowCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, (io.a2a.grpc.PasswordOAuthFlow) flow_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.OAuthFlows)) { + return super.equals(obj); + } + io.a2a.grpc.OAuthFlows other = (io.a2a.grpc.OAuthFlows) obj; + + if (!getFlowCase().equals(other.getFlowCase())) return false; + switch (flowCase_) { + case 1: + if (!getAuthorizationCode() + .equals(other.getAuthorizationCode())) return false; + break; + case 2: + if (!getClientCredentials() + .equals(other.getClientCredentials())) return false; + break; + case 3: + if (!getImplicit() + .equals(other.getImplicit())) return false; + break; + case 4: + if (!getPassword() + .equals(other.getPassword())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (flowCase_) { + case 1: + hash = (37 * hash) + AUTHORIZATION_CODE_FIELD_NUMBER; + hash = (53 * hash) + getAuthorizationCode().hashCode(); + break; + case 2: + hash = (37 * hash) + CLIENT_CREDENTIALS_FIELD_NUMBER; + hash = (53 * hash) + getClientCredentials().hashCode(); + break; + case 3: + hash = (37 * hash) + IMPLICIT_FIELD_NUMBER; + hash = (53 * hash) + getImplicit().hashCode(); + break; + case 4: + hash = (37 * hash) + PASSWORD_FIELD_NUMBER; + hash = (53 * hash) + getPassword().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.OAuthFlows parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OAuthFlows parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OAuthFlows parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.OAuthFlows parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.OAuthFlows parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.OAuthFlows parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.OAuthFlows prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.OAuthFlows} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.OAuthFlows) + io.a2a.grpc.OAuthFlowsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuthFlows_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuthFlows_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.OAuthFlows.class, io.a2a.grpc.OAuthFlows.Builder.class); + } + + // Construct using io.a2a.grpc.OAuthFlows.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (authorizationCodeBuilder_ != null) { + authorizationCodeBuilder_.clear(); + } + if (clientCredentialsBuilder_ != null) { + clientCredentialsBuilder_.clear(); + } + if (implicitBuilder_ != null) { + implicitBuilder_.clear(); + } + if (passwordBuilder_ != null) { + passwordBuilder_.clear(); + } + flowCase_ = 0; + flow_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OAuthFlows_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.OAuthFlows getDefaultInstanceForType() { + return io.a2a.grpc.OAuthFlows.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.OAuthFlows build() { + io.a2a.grpc.OAuthFlows result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.OAuthFlows buildPartial() { + io.a2a.grpc.OAuthFlows result = new io.a2a.grpc.OAuthFlows(this); + if (bitField0_ != 0) { buildPartial0(result); } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.OAuthFlows result) { + int from_bitField0_ = bitField0_; + } + + private void buildPartialOneofs(io.a2a.grpc.OAuthFlows result) { + result.flowCase_ = flowCase_; + result.flow_ = this.flow_; + if (flowCase_ == 1 && + authorizationCodeBuilder_ != null) { + result.flow_ = authorizationCodeBuilder_.build(); + } + if (flowCase_ == 2 && + clientCredentialsBuilder_ != null) { + result.flow_ = clientCredentialsBuilder_.build(); + } + if (flowCase_ == 3 && + implicitBuilder_ != null) { + result.flow_ = implicitBuilder_.build(); + } + if (flowCase_ == 4 && + passwordBuilder_ != null) { + result.flow_ = passwordBuilder_.build(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.OAuthFlows) { + return mergeFrom((io.a2a.grpc.OAuthFlows)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.OAuthFlows other) { + if (other == io.a2a.grpc.OAuthFlows.getDefaultInstance()) return this; + switch (other.getFlowCase()) { + case AUTHORIZATION_CODE: { + mergeAuthorizationCode(other.getAuthorizationCode()); + break; + } + case CLIENT_CREDENTIALS: { + mergeClientCredentials(other.getClientCredentials()); + break; + } + case IMPLICIT: { + mergeImplicit(other.getImplicit()); + break; + } + case PASSWORD: { + mergePassword(other.getPassword()); + break; + } + case FLOW_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + input.readMessage( + internalGetAuthorizationCodeFieldBuilder().getBuilder(), + extensionRegistry); + flowCase_ = 1; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetClientCredentialsFieldBuilder().getBuilder(), + extensionRegistry); + flowCase_ = 2; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetImplicitFieldBuilder().getBuilder(), + extensionRegistry); + flowCase_ = 3; + break; + } // case 26 + case 34: { + input.readMessage( + internalGetPasswordFieldBuilder().getBuilder(), + extensionRegistry); + flowCase_ = 4; + break; + } // case 34 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int flowCase_ = 0; + private java.lang.Object flow_; + public FlowCase + getFlowCase() { + return FlowCase.forNumber( + flowCase_); + } + + public Builder clearFlow() { + flowCase_ = 0; + flow_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AuthorizationCodeOAuthFlow, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder, io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder> authorizationCodeBuilder_; + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * @return Whether the authorizationCode field is set. + */ + @java.lang.Override + public boolean hasAuthorizationCode() { + return flowCase_ == 1; + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * @return The authorizationCode. + */ + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode() { + if (authorizationCodeBuilder_ == null) { + if (flowCase_ == 1) { + return (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_; + } + return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } else { + if (flowCase_ == 1) { + return authorizationCodeBuilder_.getMessage(); + } + return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + public Builder setAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value) { + if (authorizationCodeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + flow_ = value; + onChanged(); + } else { + authorizationCodeBuilder_.setMessage(value); + } + flowCase_ = 1; + return this; + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + public Builder setAuthorizationCode( + io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder builderForValue) { + if (authorizationCodeBuilder_ == null) { + flow_ = builderForValue.build(); + onChanged(); + } else { + authorizationCodeBuilder_.setMessage(builderForValue.build()); + } + flowCase_ = 1; + return this; + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + public Builder mergeAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value) { + if (authorizationCodeBuilder_ == null) { + if (flowCase_ == 1 && + flow_ != io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance()) { + flow_ = io.a2a.grpc.AuthorizationCodeOAuthFlow.newBuilder((io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_) + .mergeFrom(value).buildPartial(); + } else { + flow_ = value; + } + onChanged(); + } else { + if (flowCase_ == 1) { + authorizationCodeBuilder_.mergeFrom(value); + } else { + authorizationCodeBuilder_.setMessage(value); + } + } + flowCase_ = 1; + return this; + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + public Builder clearAuthorizationCode() { + if (authorizationCodeBuilder_ == null) { + if (flowCase_ == 1) { + flowCase_ = 0; + flow_ = null; + onChanged(); + } + } else { + if (flowCase_ == 1) { + flowCase_ = 0; + flow_ = null; + } + authorizationCodeBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + public io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder getAuthorizationCodeBuilder() { + return internalGetAuthorizationCodeFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + @java.lang.Override + public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder() { + if ((flowCase_ == 1) && (authorizationCodeBuilder_ != null)) { + return authorizationCodeBuilder_.getMessageOrBuilder(); + } else { + if (flowCase_ == 1) { + return (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_; + } + return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AuthorizationCodeOAuthFlow, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder, io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder> + internalGetAuthorizationCodeFieldBuilder() { + if (authorizationCodeBuilder_ == null) { + if (!(flowCase_ == 1)) { + flow_ = io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); + } + authorizationCodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AuthorizationCodeOAuthFlow, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder, io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder>( + (io.a2a.grpc.AuthorizationCodeOAuthFlow) flow_, + getParentForChildren(), + isClean()); + flow_ = null; + } + flowCase_ = 1; + onChanged(); + return authorizationCodeBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.ClientCredentialsOAuthFlow, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder, io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder> clientCredentialsBuilder_; + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * @return Whether the clientCredentials field is set. + */ + @java.lang.Override + public boolean hasClientCredentials() { + return flowCase_ == 2; + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * @return The clientCredentials. + */ + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials() { + if (clientCredentialsBuilder_ == null) { + if (flowCase_ == 2) { + return (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_; + } + return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } else { + if (flowCase_ == 2) { + return clientCredentialsBuilder_.getMessage(); + } + return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + public Builder setClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value) { + if (clientCredentialsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + flow_ = value; + onChanged(); + } else { + clientCredentialsBuilder_.setMessage(value); + } + flowCase_ = 2; + return this; + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + public Builder setClientCredentials( + io.a2a.grpc.ClientCredentialsOAuthFlow.Builder builderForValue) { + if (clientCredentialsBuilder_ == null) { + flow_ = builderForValue.build(); + onChanged(); + } else { + clientCredentialsBuilder_.setMessage(builderForValue.build()); + } + flowCase_ = 2; + return this; + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + public Builder mergeClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value) { + if (clientCredentialsBuilder_ == null) { + if (flowCase_ == 2 && + flow_ != io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance()) { + flow_ = io.a2a.grpc.ClientCredentialsOAuthFlow.newBuilder((io.a2a.grpc.ClientCredentialsOAuthFlow) flow_) + .mergeFrom(value).buildPartial(); + } else { + flow_ = value; + } + onChanged(); + } else { + if (flowCase_ == 2) { + clientCredentialsBuilder_.mergeFrom(value); + } else { + clientCredentialsBuilder_.setMessage(value); + } + } + flowCase_ = 2; + return this; + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + public Builder clearClientCredentials() { + if (clientCredentialsBuilder_ == null) { + if (flowCase_ == 2) { + flowCase_ = 0; + flow_ = null; + onChanged(); + } + } else { + if (flowCase_ == 2) { + flowCase_ = 0; + flow_ = null; + } + clientCredentialsBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + public io.a2a.grpc.ClientCredentialsOAuthFlow.Builder getClientCredentialsBuilder() { + return internalGetClientCredentialsFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + @java.lang.Override + public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder() { + if ((flowCase_ == 2) && (clientCredentialsBuilder_ != null)) { + return clientCredentialsBuilder_.getMessageOrBuilder(); + } else { + if (flowCase_ == 2) { + return (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_; + } + return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.ClientCredentialsOAuthFlow, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder, io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder> + internalGetClientCredentialsFieldBuilder() { + if (clientCredentialsBuilder_ == null) { + if (!(flowCase_ == 2)) { + flow_ = io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); + } + clientCredentialsBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.ClientCredentialsOAuthFlow, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder, io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder>( + (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_, + getParentForChildren(), + isClean()); + flow_ = null; + } + flowCase_ = 2; + onChanged(); + return clientCredentialsBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> implicitBuilder_; + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * @return Whether the implicit field is set. + */ + @java.lang.Override + public boolean hasImplicit() { + return flowCase_ == 3; + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * @return The implicit. + */ + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { + if (implicitBuilder_ == null) { + if (flowCase_ == 3) { + return (io.a2a.grpc.ImplicitOAuthFlow) flow_; + } + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } else { + if (flowCase_ == 3) { + return implicitBuilder_.getMessage(); + } + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { + if (implicitBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + flow_ = value; + onChanged(); + } else { + implicitBuilder_.setMessage(value); + } + flowCase_ = 3; + return this; + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + public Builder setImplicit( + io.a2a.grpc.ImplicitOAuthFlow.Builder builderForValue) { + if (implicitBuilder_ == null) { + flow_ = builderForValue.build(); + onChanged(); + } else { + implicitBuilder_.setMessage(builderForValue.build()); + } + flowCase_ = 3; + return this; + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { + if (implicitBuilder_ == null) { + if (flowCase_ == 3 && + flow_ != io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance()) { + flow_ = io.a2a.grpc.ImplicitOAuthFlow.newBuilder((io.a2a.grpc.ImplicitOAuthFlow) flow_) + .mergeFrom(value).buildPartial(); + } else { + flow_ = value; + } + onChanged(); + } else { + if (flowCase_ == 3) { + implicitBuilder_.mergeFrom(value); + } else { + implicitBuilder_.setMessage(value); + } + } + flowCase_ = 3; + return this; + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + public Builder clearImplicit() { + if (implicitBuilder_ == null) { + if (flowCase_ == 3) { + flowCase_ = 0; + flow_ = null; + onChanged(); + } + } else { + if (flowCase_ == 3) { + flowCase_ = 0; + flow_ = null; + } + implicitBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + public io.a2a.grpc.ImplicitOAuthFlow.Builder getImplicitBuilder() { + return internalGetImplicitFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { + if ((flowCase_ == 3) && (implicitBuilder_ != null)) { + return implicitBuilder_.getMessageOrBuilder(); + } else { + if (flowCase_ == 3) { + return (io.a2a.grpc.ImplicitOAuthFlow) flow_; + } + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> + internalGetImplicitFieldBuilder() { + if (implicitBuilder_ == null) { + if (!(flowCase_ == 3)) { + flow_ = io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + implicitBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder>( + (io.a2a.grpc.ImplicitOAuthFlow) flow_, + getParentForChildren(), + isClean()); + flow_ = null; + } + flowCase_ = 3; + onChanged(); + return implicitBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> passwordBuilder_; + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * @return Whether the password field is set. + */ + @java.lang.Override + public boolean hasPassword() { + return flowCase_ == 4; + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * @return The password. + */ + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow getPassword() { + if (passwordBuilder_ == null) { + if (flowCase_ == 4) { + return (io.a2a.grpc.PasswordOAuthFlow) flow_; + } + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } else { + if (flowCase_ == 4) { + return passwordBuilder_.getMessage(); + } + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) { + if (passwordBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + flow_ = value; + onChanged(); + } else { + passwordBuilder_.setMessage(value); + } + flowCase_ = 4; + return this; + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + public Builder setPassword( + io.a2a.grpc.PasswordOAuthFlow.Builder builderForValue) { + if (passwordBuilder_ == null) { + flow_ = builderForValue.build(); + onChanged(); + } else { + passwordBuilder_.setMessage(builderForValue.build()); + } + flowCase_ = 4; + return this; + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) { + if (passwordBuilder_ == null) { + if (flowCase_ == 4 && + flow_ != io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance()) { + flow_ = io.a2a.grpc.PasswordOAuthFlow.newBuilder((io.a2a.grpc.PasswordOAuthFlow) flow_) + .mergeFrom(value).buildPartial(); + } else { + flow_ = value; + } + onChanged(); + } else { + if (flowCase_ == 4) { + passwordBuilder_.mergeFrom(value); + } else { + passwordBuilder_.setMessage(value); + } + } + flowCase_ = 4; + return this; + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + public Builder clearPassword() { + if (passwordBuilder_ == null) { + if (flowCase_ == 4) { + flowCase_ = 0; + flow_ = null; + onChanged(); + } + } else { + if (flowCase_ == 4) { + flowCase_ = 0; + flow_ = null; + } + passwordBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + public io.a2a.grpc.PasswordOAuthFlow.Builder getPasswordBuilder() { + return internalGetPasswordFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { + if ((flowCase_ == 4) && (passwordBuilder_ != null)) { + return passwordBuilder_.getMessageOrBuilder(); + } else { + if (flowCase_ == 4) { + return (io.a2a.grpc.PasswordOAuthFlow) flow_; + } + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + } + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> + internalGetPasswordFieldBuilder() { + if (passwordBuilder_ == null) { + if (!(flowCase_ == 4)) { + flow_ = io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + passwordBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder>( + (io.a2a.grpc.PasswordOAuthFlow) flow_, + getParentForChildren(), + isClean()); + flow_ = null; + } + flowCase_ = 4; + onChanged(); + return passwordBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.OAuthFlows) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.OAuthFlows) + private static final io.a2a.grpc.OAuthFlows DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.OAuthFlows(); + } + + public static io.a2a.grpc.OAuthFlows getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public OAuthFlows parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.OAuthFlows getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java new file mode 100644 index 000000000..a8ecd460b --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java @@ -0,0 +1,74 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface OAuthFlowsOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.OAuthFlows) + com.google.protobuf.MessageOrBuilder { + + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * @return Whether the authorizationCode field is set. + */ + boolean hasAuthorizationCode(); + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * @return The authorizationCode. + */ + io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode(); + /** + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + */ + io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder(); + + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * @return Whether the clientCredentials field is set. + */ + boolean hasClientCredentials(); + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * @return The clientCredentials. + */ + io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials(); + /** + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + */ + io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder(); + + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * @return Whether the implicit field is set. + */ + boolean hasImplicit(); + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * @return The implicit. + */ + io.a2a.grpc.ImplicitOAuthFlow getImplicit(); + /** + * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + */ + io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder(); + + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * @return Whether the password field is set. + */ + boolean hasPassword(); + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * @return The password. + */ + io.a2a.grpc.PasswordOAuthFlow getPassword(); + /** + * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + */ + io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder(); + + io.a2a.grpc.OAuthFlows.FlowCase getFlowCase(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java b/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java new file mode 100644 index 000000000..f06f91dd9 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java @@ -0,0 +1,701 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.OpenIdConnectSecurityScheme} + */ +@com.google.protobuf.Generated +public final class OpenIdConnectSecurityScheme extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.OpenIdConnectSecurityScheme) + OpenIdConnectSecuritySchemeOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + OpenIdConnectSecurityScheme.class.getName()); + } + // Use OpenIdConnectSecurityScheme.newBuilder() to construct. + private OpenIdConnectSecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private OpenIdConnectSecurityScheme() { + description_ = ""; + openIdConnectUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.OpenIdConnectSecurityScheme.class, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder.class); + } + + public static final int DESCRIPTION_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int OPEN_ID_CONNECT_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object openIdConnectUrl_ = ""; + /** + *
+   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+   * metadata.
+   * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return The openIdConnectUrl. + */ + @java.lang.Override + public java.lang.String getOpenIdConnectUrl() { + java.lang.Object ref = openIdConnectUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + openIdConnectUrl_ = s; + return s; + } + } + /** + *
+   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+   * metadata.
+   * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return The bytes for openIdConnectUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getOpenIdConnectUrlBytes() { + java.lang.Object ref = openIdConnectUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + openIdConnectUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(openIdConnectUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, openIdConnectUrl_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, description_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(openIdConnectUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, openIdConnectUrl_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.OpenIdConnectSecurityScheme)) { + return super.equals(obj); + } + io.a2a.grpc.OpenIdConnectSecurityScheme other = (io.a2a.grpc.OpenIdConnectSecurityScheme) obj; + + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getOpenIdConnectUrl() + .equals(other.getOpenIdConnectUrl())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + hash = (37 * hash) + OPEN_ID_CONNECT_URL_FIELD_NUMBER; + hash = (53 * hash) + getOpenIdConnectUrl().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.OpenIdConnectSecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.OpenIdConnectSecurityScheme prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.OpenIdConnectSecurityScheme} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.OpenIdConnectSecurityScheme) + io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.OpenIdConnectSecurityScheme.class, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder.class); + } + + // Construct using io.a2a.grpc.OpenIdConnectSecurityScheme.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + description_ = ""; + openIdConnectUrl_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecurityScheme getDefaultInstanceForType() { + return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecurityScheme build() { + io.a2a.grpc.OpenIdConnectSecurityScheme result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecurityScheme buildPartial() { + io.a2a.grpc.OpenIdConnectSecurityScheme result = new io.a2a.grpc.OpenIdConnectSecurityScheme(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.OpenIdConnectSecurityScheme result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.description_ = description_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.openIdConnectUrl_ = openIdConnectUrl_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.OpenIdConnectSecurityScheme) { + return mergeFrom((io.a2a.grpc.OpenIdConnectSecurityScheme)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.OpenIdConnectSecurityScheme other) { + if (other == io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance()) return this; + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getOpenIdConnectUrl().isEmpty()) { + openIdConnectUrl_ = other.openIdConnectUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + openIdConnectUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object description_ = ""; + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1 [json_name = "description"]; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object openIdConnectUrl_ = ""; + /** + *
+     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+     * metadata.
+     * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return The openIdConnectUrl. + */ + public java.lang.String getOpenIdConnectUrl() { + java.lang.Object ref = openIdConnectUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + openIdConnectUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+     * metadata.
+     * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return The bytes for openIdConnectUrl. + */ + public com.google.protobuf.ByteString + getOpenIdConnectUrlBytes() { + java.lang.Object ref = openIdConnectUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + openIdConnectUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+     * metadata.
+     * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @param value The openIdConnectUrl to set. + * @return This builder for chaining. + */ + public Builder setOpenIdConnectUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + openIdConnectUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+     * metadata.
+     * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return This builder for chaining. + */ + public Builder clearOpenIdConnectUrl() { + openIdConnectUrl_ = getDefaultInstance().getOpenIdConnectUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+     * metadata.
+     * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @param value The bytes for openIdConnectUrl to set. + * @return This builder for chaining. + */ + public Builder setOpenIdConnectUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + openIdConnectUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.OpenIdConnectSecurityScheme) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.OpenIdConnectSecurityScheme) + private static final io.a2a.grpc.OpenIdConnectSecurityScheme DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.OpenIdConnectSecurityScheme(); + } + + public static io.a2a.grpc.OpenIdConnectSecurityScheme getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public OpenIdConnectSecurityScheme parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecurityScheme getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java new file mode 100644 index 000000000..0682b5f93 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java @@ -0,0 +1,54 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface OpenIdConnectSecuritySchemeOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.OpenIdConnectSecurityScheme) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1 [json_name = "description"]; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); + + /** + *
+   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+   * metadata.
+   * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return The openIdConnectUrl. + */ + java.lang.String getOpenIdConnectUrl(); + /** + *
+   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
+   * metadata.
+   * 
+ * + * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * @return The bytes for openIdConnectUrl. + */ + com.google.protobuf.ByteString + getOpenIdConnectUrlBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/Part.java b/grpc/src/main/java/io/a2a/grpc/Part.java new file mode 100644 index 000000000..5582d8d5e --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/Part.java @@ -0,0 +1,1042 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Part represents a container for a section of communication content.
+ * Parts can be purely textual, some sort of file (image, video, etc) or
+ * a structured data blob (i.e. JSON).
+ * 
+ * + * Protobuf type {@code a2a.v1.Part} + */ +@com.google.protobuf.Generated +public final class Part extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.Part) + PartOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + Part.class.getName()); + } + // Use Part.newBuilder() to construct. + private Part(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Part() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Part_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Part_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Part.class, io.a2a.grpc.Part.Builder.class); + } + + private int partCase_ = 0; + @SuppressWarnings("serial") + private java.lang.Object part_; + public enum PartCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + TEXT(1), + FILE(2), + DATA(3), + PART_NOT_SET(0); + private final int value; + private PartCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static PartCase valueOf(int value) { + return forNumber(value); + } + + public static PartCase forNumber(int value) { + switch (value) { + case 1: return TEXT; + case 2: return FILE; + case 3: return DATA; + case 0: return PART_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public PartCase + getPartCase() { + return PartCase.forNumber( + partCase_); + } + + public static final int TEXT_FIELD_NUMBER = 1; + /** + * string text = 1 [json_name = "text"]; + * @return Whether the text field is set. + */ + public boolean hasText() { + return partCase_ == 1; + } + /** + * string text = 1 [json_name = "text"]; + * @return The text. + */ + public java.lang.String getText() { + java.lang.Object ref = ""; + if (partCase_ == 1) { + ref = part_; + } + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (partCase_ == 1) { + part_ = s; + } + return s; + } + } + /** + * string text = 1 [json_name = "text"]; + * @return The bytes for text. + */ + public com.google.protobuf.ByteString + getTextBytes() { + java.lang.Object ref = ""; + if (partCase_ == 1) { + ref = part_; + } + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (partCase_ == 1) { + part_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int FILE_FIELD_NUMBER = 2; + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * @return Whether the file field is set. + */ + @java.lang.Override + public boolean hasFile() { + return partCase_ == 2; + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * @return The file. + */ + @java.lang.Override + public io.a2a.grpc.FilePart getFile() { + if (partCase_ == 2) { + return (io.a2a.grpc.FilePart) part_; + } + return io.a2a.grpc.FilePart.getDefaultInstance(); + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + @java.lang.Override + public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { + if (partCase_ == 2) { + return (io.a2a.grpc.FilePart) part_; + } + return io.a2a.grpc.FilePart.getDefaultInstance(); + } + + public static final int DATA_FIELD_NUMBER = 3; + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * @return Whether the data field is set. + */ + @java.lang.Override + public boolean hasData() { + return partCase_ == 3; + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * @return The data. + */ + @java.lang.Override + public io.a2a.grpc.DataPart getData() { + if (partCase_ == 3) { + return (io.a2a.grpc.DataPart) part_; + } + return io.a2a.grpc.DataPart.getDefaultInstance(); + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + @java.lang.Override + public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { + if (partCase_ == 3) { + return (io.a2a.grpc.DataPart) part_; + } + return io.a2a.grpc.DataPart.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (partCase_ == 1) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, part_); + } + if (partCase_ == 2) { + output.writeMessage(2, (io.a2a.grpc.FilePart) part_); + } + if (partCase_ == 3) { + output.writeMessage(3, (io.a2a.grpc.DataPart) part_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (partCase_ == 1) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, part_); + } + if (partCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (io.a2a.grpc.FilePart) part_); + } + if (partCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, (io.a2a.grpc.DataPart) part_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.Part)) { + return super.equals(obj); + } + io.a2a.grpc.Part other = (io.a2a.grpc.Part) obj; + + if (!getPartCase().equals(other.getPartCase())) return false; + switch (partCase_) { + case 1: + if (!getText() + .equals(other.getText())) return false; + break; + case 2: + if (!getFile() + .equals(other.getFile())) return false; + break; + case 3: + if (!getData() + .equals(other.getData())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (partCase_) { + case 1: + hash = (37 * hash) + TEXT_FIELD_NUMBER; + hash = (53 * hash) + getText().hashCode(); + break; + case 2: + hash = (37 * hash) + FILE_FIELD_NUMBER; + hash = (53 * hash) + getFile().hashCode(); + break; + case 3: + hash = (37 * hash) + DATA_FIELD_NUMBER; + hash = (53 * hash) + getData().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.Part parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Part parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Part parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Part parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Part parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Part parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Part parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Part parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.Part parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.Part parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.Part parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Part parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.Part prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Part represents a container for a section of communication content.
+   * Parts can be purely textual, some sort of file (image, video, etc) or
+   * a structured data blob (i.e. JSON).
+   * 
+ * + * Protobuf type {@code a2a.v1.Part} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.Part) + io.a2a.grpc.PartOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Part_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Part_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Part.class, io.a2a.grpc.Part.Builder.class); + } + + // Construct using io.a2a.grpc.Part.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (fileBuilder_ != null) { + fileBuilder_.clear(); + } + if (dataBuilder_ != null) { + dataBuilder_.clear(); + } + partCase_ = 0; + part_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Part_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.Part getDefaultInstanceForType() { + return io.a2a.grpc.Part.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.Part build() { + io.a2a.grpc.Part result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.Part buildPartial() { + io.a2a.grpc.Part result = new io.a2a.grpc.Part(this); + if (bitField0_ != 0) { buildPartial0(result); } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.Part result) { + int from_bitField0_ = bitField0_; + } + + private void buildPartialOneofs(io.a2a.grpc.Part result) { + result.partCase_ = partCase_; + result.part_ = this.part_; + if (partCase_ == 2 && + fileBuilder_ != null) { + result.part_ = fileBuilder_.build(); + } + if (partCase_ == 3 && + dataBuilder_ != null) { + result.part_ = dataBuilder_.build(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.Part) { + return mergeFrom((io.a2a.grpc.Part)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.Part other) { + if (other == io.a2a.grpc.Part.getDefaultInstance()) return this; + switch (other.getPartCase()) { + case TEXT: { + partCase_ = 1; + part_ = other.part_; + onChanged(); + break; + } + case FILE: { + mergeFile(other.getFile()); + break; + } + case DATA: { + mergeData(other.getData()); + break; + } + case PART_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + partCase_ = 1; + part_ = s; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetFileFieldBuilder().getBuilder(), + extensionRegistry); + partCase_ = 2; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetDataFieldBuilder().getBuilder(), + extensionRegistry); + partCase_ = 3; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int partCase_ = 0; + private java.lang.Object part_; + public PartCase + getPartCase() { + return PartCase.forNumber( + partCase_); + } + + public Builder clearPart() { + partCase_ = 0; + part_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + /** + * string text = 1 [json_name = "text"]; + * @return Whether the text field is set. + */ + @java.lang.Override + public boolean hasText() { + return partCase_ == 1; + } + /** + * string text = 1 [json_name = "text"]; + * @return The text. + */ + @java.lang.Override + public java.lang.String getText() { + java.lang.Object ref = ""; + if (partCase_ == 1) { + ref = part_; + } + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (partCase_ == 1) { + part_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string text = 1 [json_name = "text"]; + * @return The bytes for text. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTextBytes() { + java.lang.Object ref = ""; + if (partCase_ == 1) { + ref = part_; + } + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (partCase_ == 1) { + part_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string text = 1 [json_name = "text"]; + * @param value The text to set. + * @return This builder for chaining. + */ + public Builder setText( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + partCase_ = 1; + part_ = value; + onChanged(); + return this; + } + /** + * string text = 1 [json_name = "text"]; + * @return This builder for chaining. + */ + public Builder clearText() { + if (partCase_ == 1) { + partCase_ = 0; + part_ = null; + onChanged(); + } + return this; + } + /** + * string text = 1 [json_name = "text"]; + * @param value The bytes for text to set. + * @return This builder for chaining. + */ + public Builder setTextBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + partCase_ = 1; + part_ = value; + onChanged(); + return this; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> fileBuilder_; + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * @return Whether the file field is set. + */ + @java.lang.Override + public boolean hasFile() { + return partCase_ == 2; + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * @return The file. + */ + @java.lang.Override + public io.a2a.grpc.FilePart getFile() { + if (fileBuilder_ == null) { + if (partCase_ == 2) { + return (io.a2a.grpc.FilePart) part_; + } + return io.a2a.grpc.FilePart.getDefaultInstance(); + } else { + if (partCase_ == 2) { + return fileBuilder_.getMessage(); + } + return io.a2a.grpc.FilePart.getDefaultInstance(); + } + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + public Builder setFile(io.a2a.grpc.FilePart value) { + if (fileBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + part_ = value; + onChanged(); + } else { + fileBuilder_.setMessage(value); + } + partCase_ = 2; + return this; + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + public Builder setFile( + io.a2a.grpc.FilePart.Builder builderForValue) { + if (fileBuilder_ == null) { + part_ = builderForValue.build(); + onChanged(); + } else { + fileBuilder_.setMessage(builderForValue.build()); + } + partCase_ = 2; + return this; + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + public Builder mergeFile(io.a2a.grpc.FilePart value) { + if (fileBuilder_ == null) { + if (partCase_ == 2 && + part_ != io.a2a.grpc.FilePart.getDefaultInstance()) { + part_ = io.a2a.grpc.FilePart.newBuilder((io.a2a.grpc.FilePart) part_) + .mergeFrom(value).buildPartial(); + } else { + part_ = value; + } + onChanged(); + } else { + if (partCase_ == 2) { + fileBuilder_.mergeFrom(value); + } else { + fileBuilder_.setMessage(value); + } + } + partCase_ = 2; + return this; + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + public Builder clearFile() { + if (fileBuilder_ == null) { + if (partCase_ == 2) { + partCase_ = 0; + part_ = null; + onChanged(); + } + } else { + if (partCase_ == 2) { + partCase_ = 0; + part_ = null; + } + fileBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + public io.a2a.grpc.FilePart.Builder getFileBuilder() { + return internalGetFileFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + @java.lang.Override + public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { + if ((partCase_ == 2) && (fileBuilder_ != null)) { + return fileBuilder_.getMessageOrBuilder(); + } else { + if (partCase_ == 2) { + return (io.a2a.grpc.FilePart) part_; + } + return io.a2a.grpc.FilePart.getDefaultInstance(); + } + } + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> + internalGetFileFieldBuilder() { + if (fileBuilder_ == null) { + if (!(partCase_ == 2)) { + part_ = io.a2a.grpc.FilePart.getDefaultInstance(); + } + fileBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder>( + (io.a2a.grpc.FilePart) part_, + getParentForChildren(), + isClean()); + part_ = null; + } + partCase_ = 2; + onChanged(); + return fileBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> dataBuilder_; + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * @return Whether the data field is set. + */ + @java.lang.Override + public boolean hasData() { + return partCase_ == 3; + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * @return The data. + */ + @java.lang.Override + public io.a2a.grpc.DataPart getData() { + if (dataBuilder_ == null) { + if (partCase_ == 3) { + return (io.a2a.grpc.DataPart) part_; + } + return io.a2a.grpc.DataPart.getDefaultInstance(); + } else { + if (partCase_ == 3) { + return dataBuilder_.getMessage(); + } + return io.a2a.grpc.DataPart.getDefaultInstance(); + } + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + public Builder setData(io.a2a.grpc.DataPart value) { + if (dataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + part_ = value; + onChanged(); + } else { + dataBuilder_.setMessage(value); + } + partCase_ = 3; + return this; + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + public Builder setData( + io.a2a.grpc.DataPart.Builder builderForValue) { + if (dataBuilder_ == null) { + part_ = builderForValue.build(); + onChanged(); + } else { + dataBuilder_.setMessage(builderForValue.build()); + } + partCase_ = 3; + return this; + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + public Builder mergeData(io.a2a.grpc.DataPart value) { + if (dataBuilder_ == null) { + if (partCase_ == 3 && + part_ != io.a2a.grpc.DataPart.getDefaultInstance()) { + part_ = io.a2a.grpc.DataPart.newBuilder((io.a2a.grpc.DataPart) part_) + .mergeFrom(value).buildPartial(); + } else { + part_ = value; + } + onChanged(); + } else { + if (partCase_ == 3) { + dataBuilder_.mergeFrom(value); + } else { + dataBuilder_.setMessage(value); + } + } + partCase_ = 3; + return this; + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + public Builder clearData() { + if (dataBuilder_ == null) { + if (partCase_ == 3) { + partCase_ = 0; + part_ = null; + onChanged(); + } + } else { + if (partCase_ == 3) { + partCase_ = 0; + part_ = null; + } + dataBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + public io.a2a.grpc.DataPart.Builder getDataBuilder() { + return internalGetDataFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + @java.lang.Override + public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { + if ((partCase_ == 3) && (dataBuilder_ != null)) { + return dataBuilder_.getMessageOrBuilder(); + } else { + if (partCase_ == 3) { + return (io.a2a.grpc.DataPart) part_; + } + return io.a2a.grpc.DataPart.getDefaultInstance(); + } + } + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> + internalGetDataFieldBuilder() { + if (dataBuilder_ == null) { + if (!(partCase_ == 3)) { + part_ = io.a2a.grpc.DataPart.getDefaultInstance(); + } + dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder>( + (io.a2a.grpc.DataPart) part_, + getParentForChildren(), + isClean()); + part_ = null; + } + partCase_ = 3; + onChanged(); + return dataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.Part) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.Part) + private static final io.a2a.grpc.Part DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.Part(); + } + + public static io.a2a.grpc.Part getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Part parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.Part getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java new file mode 100644 index 000000000..5840438a3 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java @@ -0,0 +1,61 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface PartOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.Part) + com.google.protobuf.MessageOrBuilder { + + /** + * string text = 1 [json_name = "text"]; + * @return Whether the text field is set. + */ + boolean hasText(); + /** + * string text = 1 [json_name = "text"]; + * @return The text. + */ + java.lang.String getText(); + /** + * string text = 1 [json_name = "text"]; + * @return The bytes for text. + */ + com.google.protobuf.ByteString + getTextBytes(); + + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * @return Whether the file field is set. + */ + boolean hasFile(); + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * @return The file. + */ + io.a2a.grpc.FilePart getFile(); + /** + * .a2a.v1.FilePart file = 2 [json_name = "file"]; + */ + io.a2a.grpc.FilePartOrBuilder getFileOrBuilder(); + + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * @return Whether the data field is set. + */ + boolean hasData(); + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * @return The data. + */ + io.a2a.grpc.DataPart getData(); + /** + * .a2a.v1.DataPart data = 3 [json_name = "data"]; + */ + io.a2a.grpc.DataPartOrBuilder getDataOrBuilder(); + + io.a2a.grpc.Part.PartCase getPartCase(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java new file mode 100644 index 000000000..3801ef006 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java @@ -0,0 +1,1042 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.PasswordOAuthFlow} + */ +@com.google.protobuf.Generated +public final class PasswordOAuthFlow extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.PasswordOAuthFlow) + PasswordOAuthFlowOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + PasswordOAuthFlow.class.getName()); + } + // Use PasswordOAuthFlow.newBuilder() to construct. + private PasswordOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private PasswordOAuthFlow() { + tokenUrl_ = ""; + refreshUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.PasswordOAuthFlow.class, io.a2a.grpc.PasswordOAuthFlow.Builder.class); + } + + public static final int TOKEN_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object tokenUrl_ = ""; + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + @java.lang.Override + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } + } + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REFRESH_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object refreshUrl_ = ""; + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + @java.lang.Override + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } + } + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCOPES_FIELD_NUMBER = 3; + private static final class ScopesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetScopes(), + ScopesDefaultEntryHolder.defaultEntry, + 3); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); + } + for (java.util.Map.Entry entry + : internalGetScopes().getMap().entrySet()) { + com.google.protobuf.MapEntry + scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, scopes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.PasswordOAuthFlow)) { + return super.equals(obj); + } + io.a2a.grpc.PasswordOAuthFlow other = (io.a2a.grpc.PasswordOAuthFlow) obj; + + if (!getTokenUrl() + .equals(other.getTokenUrl())) return false; + if (!getRefreshUrl() + .equals(other.getRefreshUrl())) return false; + if (!internalGetScopes().equals( + other.internalGetScopes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TOKEN_URL_FIELD_NUMBER; + hash = (53 * hash) + getTokenUrl().hashCode(); + hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; + hash = (53 * hash) + getRefreshUrl().hashCode(); + if (!internalGetScopes().getMap().isEmpty()) { + hash = (37 * hash) + SCOPES_FIELD_NUMBER; + hash = (53 * hash) + internalGetScopes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.PasswordOAuthFlow parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.PasswordOAuthFlow parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.PasswordOAuthFlow prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.PasswordOAuthFlow} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.PasswordOAuthFlow) + io.a2a.grpc.PasswordOAuthFlowOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetMutableScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.PasswordOAuthFlow.class, io.a2a.grpc.PasswordOAuthFlow.Builder.class); + } + + // Construct using io.a2a.grpc.PasswordOAuthFlow.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + tokenUrl_ = ""; + refreshUrl_ = ""; + internalGetMutableScopes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow build() { + io.a2a.grpc.PasswordOAuthFlow result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow buildPartial() { + io.a2a.grpc.PasswordOAuthFlow result = new io.a2a.grpc.PasswordOAuthFlow(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.PasswordOAuthFlow result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.tokenUrl_ = tokenUrl_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.scopes_ = internalGetScopes(); + result.scopes_.makeImmutable(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.PasswordOAuthFlow) { + return mergeFrom((io.a2a.grpc.PasswordOAuthFlow)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.PasswordOAuthFlow other) { + if (other == io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance()) return this; + if (!other.getTokenUrl().isEmpty()) { + tokenUrl_ = other.tokenUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getRefreshUrl().isEmpty()) { + refreshUrl_ = other.refreshUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + internalGetMutableScopes().mergeFrom( + other.internalGetScopes()); + bitField0_ |= 0x00000004; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + tokenUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + com.google.protobuf.MapEntry + scopes__ = input.readMessage( + ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableScopes().getMutableMap().put( + scopes__.getKey(), scopes__.getValue()); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object tokenUrl_ = ""; + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @param value The tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tokenUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return This builder for chaining. + */ + public Builder clearTokenUrl() { + tokenUrl_ = getDefaultInstance().getTokenUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The token URL to be used for this flow. This MUST be in the form of a URL.
+     * The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @param value The bytes for tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tokenUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object refreshUrl_ = ""; + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @param value The refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return This builder for chaining. + */ + public Builder clearRefreshUrl() { + refreshUrl_ = getDefaultInstance().getRefreshUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The URL to be used for obtaining refresh tokens. This MUST be in the
+     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @param value The bytes for refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + private com.google.protobuf.MapField + internalGetMutableScopes() { + if (scopes_ == null) { + scopes_ = com.google.protobuf.MapField.newMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + if (!scopes_.isMutable()) { + scopes_ = scopes_.copy(); + } + bitField0_ |= 0x00000004; + onChanged(); + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + public Builder clearScopes() { + bitField0_ = (bitField0_ & ~0x00000004); + internalGetMutableScopes().getMutableMap() + .clear(); + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder removeScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableScopes().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableScopes() { + bitField0_ |= 0x00000004; + return internalGetMutableScopes().getMutableMap(); + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder putScopes( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableScopes().getMutableMap() + .put(key, value); + bitField0_ |= 0x00000004; + return this; + } + /** + *
+     * The available scopes for the OAuth2 security scheme. A map between the
+     * scope name and a short description for it. The map MAY be empty.
+     * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + public Builder putAllScopes( + java.util.Map values) { + internalGetMutableScopes().getMutableMap() + .putAll(values); + bitField0_ |= 0x00000004; + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.PasswordOAuthFlow) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.PasswordOAuthFlow) + private static final io.a2a.grpc.PasswordOAuthFlow DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.PasswordOAuthFlow(); + } + + public static io.a2a.grpc.PasswordOAuthFlow getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public PasswordOAuthFlow parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java new file mode 100644 index 000000000..73c88685f --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java @@ -0,0 +1,115 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface PasswordOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.PasswordOAuthFlow) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The tokenUrl. + */ + java.lang.String getTokenUrl(); + /** + *
+   * The token URL to be used for this flow. This MUST be in the form of a URL.
+   * The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string token_url = 1 [json_name = "tokenUrl"]; + * @return The bytes for tokenUrl. + */ + com.google.protobuf.ByteString + getTokenUrlBytes(); + + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The refreshUrl. + */ + java.lang.String getRefreshUrl(); + /** + *
+   * The URL to be used for obtaining refresh tokens. This MUST be in the
+   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * 
+ * + * string refresh_url = 2 [json_name = "refreshUrl"]; + * @return The bytes for refreshUrl. + */ + com.google.protobuf.ByteString + getRefreshUrlBytes(); + + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + int getScopesCount(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + boolean containsScopes( + java.lang.String key); + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getScopes(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + java.util.Map + getScopesMap(); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue); + /** + *
+   * The available scopes for the OAuth2 security scheme. A map between the
+   * scope name and a short description for it. The map MAY be empty.
+   * 
+ * + * map<string, string> scopes = 3 [json_name = "scopes"]; + */ + java.lang.String getScopesOrThrow( + java.lang.String key); +} diff --git a/grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java new file mode 100644 index 000000000..b9b1909f5 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java @@ -0,0 +1,1107 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Configuration for setting up push notifications for task updates.
+ * 
+ * + * Protobuf type {@code a2a.v1.PushNotificationConfig} + */ +@com.google.protobuf.Generated +public final class PushNotificationConfig extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.PushNotificationConfig) + PushNotificationConfigOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + PushNotificationConfig.class.getName()); + } + // Use PushNotificationConfig.newBuilder() to construct. + private PushNotificationConfig(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private PushNotificationConfig() { + id_ = ""; + url_ = ""; + token_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PushNotificationConfig_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PushNotificationConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.PushNotificationConfig.class, io.a2a.grpc.PushNotificationConfig.Builder.class); + } + + private int bitField0_; + public static final int ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + *
+   * A unique id for this push notification.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + *
+   * A unique id for this push notification.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object url_ = ""; + /** + *
+   * Url to send the notification too
+   * 
+ * + * string url = 2 [json_name = "url"]; + * @return The url. + */ + @java.lang.Override + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } + } + /** + *
+   * Url to send the notification too
+   * 
+ * + * string url = 2 [json_name = "url"]; + * @return The bytes for url. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TOKEN_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object token_ = ""; + /** + *
+   * Token unique for this task/session
+   * 
+ * + * string token = 3 [json_name = "token"]; + * @return The token. + */ + @java.lang.Override + public java.lang.String getToken() { + java.lang.Object ref = token_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + token_ = s; + return s; + } + } + /** + *
+   * Token unique for this task/session
+   * 
+ * + * string token = 3 [json_name = "token"]; + * @return The bytes for token. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTokenBytes() { + java.lang.Object ref = token_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + token_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int AUTHENTICATION_FIELD_NUMBER = 4; + private io.a2a.grpc.AuthenticationInfo authentication_; + /** + *
+   * Information about the authentication to sent with the notification
+   * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * @return Whether the authentication field is set. + */ + @java.lang.Override + public boolean hasAuthentication() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Information about the authentication to sent with the notification
+   * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * @return The authentication. + */ + @java.lang.Override + public io.a2a.grpc.AuthenticationInfo getAuthentication() { + return authentication_ == null ? io.a2a.grpc.AuthenticationInfo.getDefaultInstance() : authentication_; + } + /** + *
+   * Information about the authentication to sent with the notification
+   * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + @java.lang.Override + public io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder() { + return authentication_ == null ? io.a2a.grpc.AuthenticationInfo.getDefaultInstance() : authentication_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, url_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(token_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, token_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(4, getAuthentication()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, url_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(token_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, token_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, getAuthentication()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.PushNotificationConfig)) { + return super.equals(obj); + } + io.a2a.grpc.PushNotificationConfig other = (io.a2a.grpc.PushNotificationConfig) obj; + + if (!getId() + .equals(other.getId())) return false; + if (!getUrl() + .equals(other.getUrl())) return false; + if (!getToken() + .equals(other.getToken())) return false; + if (hasAuthentication() != other.hasAuthentication()) return false; + if (hasAuthentication()) { + if (!getAuthentication() + .equals(other.getAuthentication())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); + hash = (37 * hash) + URL_FIELD_NUMBER; + hash = (53 * hash) + getUrl().hashCode(); + hash = (37 * hash) + TOKEN_FIELD_NUMBER; + hash = (53 * hash) + getToken().hashCode(); + if (hasAuthentication()) { + hash = (37 * hash) + AUTHENTICATION_FIELD_NUMBER; + hash = (53 * hash) + getAuthentication().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.PushNotificationConfig parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.PushNotificationConfig parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.PushNotificationConfig parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.PushNotificationConfig parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.PushNotificationConfig prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Configuration for setting up push notifications for task updates.
+   * 
+ * + * Protobuf type {@code a2a.v1.PushNotificationConfig} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.PushNotificationConfig) + io.a2a.grpc.PushNotificationConfigOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PushNotificationConfig_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PushNotificationConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.PushNotificationConfig.class, io.a2a.grpc.PushNotificationConfig.Builder.class); + } + + // Construct using io.a2a.grpc.PushNotificationConfig.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetAuthenticationFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = ""; + url_ = ""; + token_ = ""; + authentication_ = null; + if (authenticationBuilder_ != null) { + authenticationBuilder_.dispose(); + authenticationBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PushNotificationConfig_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.PushNotificationConfig getDefaultInstanceForType() { + return io.a2a.grpc.PushNotificationConfig.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.PushNotificationConfig build() { + io.a2a.grpc.PushNotificationConfig result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.PushNotificationConfig buildPartial() { + io.a2a.grpc.PushNotificationConfig result = new io.a2a.grpc.PushNotificationConfig(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.PushNotificationConfig result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.url_ = url_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.token_ = token_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000008) != 0)) { + result.authentication_ = authenticationBuilder_ == null + ? authentication_ + : authenticationBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.PushNotificationConfig) { + return mergeFrom((io.a2a.grpc.PushNotificationConfig)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.PushNotificationConfig other) { + if (other == io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) return this; + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getUrl().isEmpty()) { + url_ = other.url_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getToken().isEmpty()) { + token_ = other.token_; + bitField0_ |= 0x00000004; + onChanged(); + } + if (other.hasAuthentication()) { + mergeAuthentication(other.getAuthentication()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + url_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + token_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 34: { + input.readMessage( + internalGetAuthenticationFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000008; + break; + } // case 34 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object id_ = ""; + /** + *
+     * A unique id for this push notification.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * A unique id for this push notification.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * A unique id for this push notification.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * A unique id for this push notification.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * A unique id for this push notification.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object url_ = ""; + /** + *
+     * Url to send the notification too
+     * 
+ * + * string url = 2 [json_name = "url"]; + * @return The url. + */ + public java.lang.String getUrl() { + java.lang.Object ref = url_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + url_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Url to send the notification too
+     * 
+ * + * string url = 2 [json_name = "url"]; + * @return The bytes for url. + */ + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = url_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + url_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Url to send the notification too
+     * 
+ * + * string url = 2 [json_name = "url"]; + * @param value The url to set. + * @return This builder for chaining. + */ + public Builder setUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + url_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Url to send the notification too
+     * 
+ * + * string url = 2 [json_name = "url"]; + * @return This builder for chaining. + */ + public Builder clearUrl() { + url_ = getDefaultInstance().getUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * Url to send the notification too
+     * 
+ * + * string url = 2 [json_name = "url"]; + * @param value The bytes for url to set. + * @return This builder for chaining. + */ + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + url_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object token_ = ""; + /** + *
+     * Token unique for this task/session
+     * 
+ * + * string token = 3 [json_name = "token"]; + * @return The token. + */ + public java.lang.String getToken() { + java.lang.Object ref = token_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + token_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Token unique for this task/session
+     * 
+ * + * string token = 3 [json_name = "token"]; + * @return The bytes for token. + */ + public com.google.protobuf.ByteString + getTokenBytes() { + java.lang.Object ref = token_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + token_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Token unique for this task/session
+     * 
+ * + * string token = 3 [json_name = "token"]; + * @param value The token to set. + * @return This builder for chaining. + */ + public Builder setToken( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + token_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Token unique for this task/session
+     * 
+ * + * string token = 3 [json_name = "token"]; + * @return This builder for chaining. + */ + public Builder clearToken() { + token_ = getDefaultInstance().getToken(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * Token unique for this task/session
+     * 
+ * + * string token = 3 [json_name = "token"]; + * @param value The bytes for token to set. + * @return This builder for chaining. + */ + public Builder setTokenBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + token_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + + private io.a2a.grpc.AuthenticationInfo authentication_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AuthenticationInfo, io.a2a.grpc.AuthenticationInfo.Builder, io.a2a.grpc.AuthenticationInfoOrBuilder> authenticationBuilder_; + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * @return Whether the authentication field is set. + */ + public boolean hasAuthentication() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * @return The authentication. + */ + public io.a2a.grpc.AuthenticationInfo getAuthentication() { + if (authenticationBuilder_ == null) { + return authentication_ == null ? io.a2a.grpc.AuthenticationInfo.getDefaultInstance() : authentication_; + } else { + return authenticationBuilder_.getMessage(); + } + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + public Builder setAuthentication(io.a2a.grpc.AuthenticationInfo value) { + if (authenticationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + authentication_ = value; + } else { + authenticationBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + public Builder setAuthentication( + io.a2a.grpc.AuthenticationInfo.Builder builderForValue) { + if (authenticationBuilder_ == null) { + authentication_ = builderForValue.build(); + } else { + authenticationBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + public Builder mergeAuthentication(io.a2a.grpc.AuthenticationInfo value) { + if (authenticationBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0) && + authentication_ != null && + authentication_ != io.a2a.grpc.AuthenticationInfo.getDefaultInstance()) { + getAuthenticationBuilder().mergeFrom(value); + } else { + authentication_ = value; + } + } else { + authenticationBuilder_.mergeFrom(value); + } + if (authentication_ != null) { + bitField0_ |= 0x00000008; + onChanged(); + } + return this; + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + public Builder clearAuthentication() { + bitField0_ = (bitField0_ & ~0x00000008); + authentication_ = null; + if (authenticationBuilder_ != null) { + authenticationBuilder_.dispose(); + authenticationBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + public io.a2a.grpc.AuthenticationInfo.Builder getAuthenticationBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return internalGetAuthenticationFieldBuilder().getBuilder(); + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + public io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder() { + if (authenticationBuilder_ != null) { + return authenticationBuilder_.getMessageOrBuilder(); + } else { + return authentication_ == null ? + io.a2a.grpc.AuthenticationInfo.getDefaultInstance() : authentication_; + } + } + /** + *
+     * Information about the authentication to sent with the notification
+     * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AuthenticationInfo, io.a2a.grpc.AuthenticationInfo.Builder, io.a2a.grpc.AuthenticationInfoOrBuilder> + internalGetAuthenticationFieldBuilder() { + if (authenticationBuilder_ == null) { + authenticationBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.AuthenticationInfo, io.a2a.grpc.AuthenticationInfo.Builder, io.a2a.grpc.AuthenticationInfoOrBuilder>( + getAuthentication(), + getParentForChildren(), + isClean()); + authentication_ = null; + } + return authenticationBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.PushNotificationConfig) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.PushNotificationConfig) + private static final io.a2a.grpc.PushNotificationConfig DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.PushNotificationConfig(); + } + + public static io.a2a.grpc.PushNotificationConfig getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public PushNotificationConfig parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.PushNotificationConfig getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java new file mode 100644 index 000000000..9aa257747 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java @@ -0,0 +1,99 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface PushNotificationConfigOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.PushNotificationConfig) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * A unique id for this push notification.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + java.lang.String getId(); + /** + *
+   * A unique id for this push notification.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + com.google.protobuf.ByteString + getIdBytes(); + + /** + *
+   * Url to send the notification too
+   * 
+ * + * string url = 2 [json_name = "url"]; + * @return The url. + */ + java.lang.String getUrl(); + /** + *
+   * Url to send the notification too
+   * 
+ * + * string url = 2 [json_name = "url"]; + * @return The bytes for url. + */ + com.google.protobuf.ByteString + getUrlBytes(); + + /** + *
+   * Token unique for this task/session
+   * 
+ * + * string token = 3 [json_name = "token"]; + * @return The token. + */ + java.lang.String getToken(); + /** + *
+   * Token unique for this task/session
+   * 
+ * + * string token = 3 [json_name = "token"]; + * @return The bytes for token. + */ + com.google.protobuf.ByteString + getTokenBytes(); + + /** + *
+   * Information about the authentication to sent with the notification
+   * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * @return Whether the authentication field is set. + */ + boolean hasAuthentication(); + /** + *
+   * Information about the authentication to sent with the notification
+   * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * @return The authentication. + */ + io.a2a.grpc.AuthenticationInfo getAuthentication(); + /** + *
+   * Information about the authentication to sent with the notification
+   * 
+ * + * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + */ + io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/Role.java b/grpc/src/main/java/io/a2a/grpc/Role.java new file mode 100644 index 000000000..25f4d57ad --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/Role.java @@ -0,0 +1,150 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf enum {@code a2a.v1.Role} + */ +@com.google.protobuf.Generated +public enum Role + implements com.google.protobuf.ProtocolMessageEnum { + /** + * ROLE_UNSPECIFIED = 0; + */ + ROLE_UNSPECIFIED(0), + /** + *
+   * USER role refers to communication from the client to the server.
+   * 
+ * + * ROLE_USER = 1; + */ + ROLE_USER(1), + /** + *
+   * AGENT role refers to communication from the server to the client.
+   * 
+ * + * ROLE_AGENT = 2; + */ + ROLE_AGENT(2), + UNRECOGNIZED(-1), + ; + + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + Role.class.getName()); + } + /** + * ROLE_UNSPECIFIED = 0; + */ + public static final int ROLE_UNSPECIFIED_VALUE = 0; + /** + *
+   * USER role refers to communication from the client to the server.
+   * 
+ * + * ROLE_USER = 1; + */ + public static final int ROLE_USER_VALUE = 1; + /** + *
+   * AGENT role refers to communication from the server to the client.
+   * 
+ * + * ROLE_AGENT = 2; + */ + public static final int ROLE_AGENT_VALUE = 2; + + + public final int getNumber() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static Role valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static Role forNumber(int value) { + switch (value) { + case 0: return ROLE_UNSPECIFIED; + case 1: return ROLE_USER; + case 2: return ROLE_AGENT; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + Role> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Role findValueByNumber(int number) { + return Role.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return io.a2a.grpc.A2A.getDescriptor().getEnumTypes().get(1); + } + + private static final Role[] VALUES = values(); + + public static Role valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private Role(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:a2a.v1.Role) +} + diff --git a/grpc/src/main/java/io/a2a/grpc/Security.java b/grpc/src/main/java/io/a2a/grpc/Security.java new file mode 100644 index 000000000..f19e94a20 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/Security.java @@ -0,0 +1,672 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.Security} + */ +@com.google.protobuf.Generated +public final class Security extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.Security) + SecurityOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + Security.class.getName()); + } + // Use Security.newBuilder() to construct. + private Security(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Security() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 1: + return internalGetSchemes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Security.class, io.a2a.grpc.Security.Builder.class); + } + + public static final int SCHEMES_FIELD_NUMBER = 1; + private static final class SchemesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, io.a2a.grpc.StringList> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_Security_SchemesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.MESSAGE, + io.a2a.grpc.StringList.getDefaultInstance()); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, io.a2a.grpc.StringList> schemes_; + private com.google.protobuf.MapField + internalGetSchemes() { + if (schemes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + SchemesDefaultEntryHolder.defaultEntry); + } + return schemes_; + } + public int getSchemesCount() { + return internalGetSchemes().getMap().size(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public boolean containsSchemes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetSchemes().getMap().containsKey(key); + } + /** + * Use {@link #getSchemesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getSchemes() { + return getSchemesMap(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public java.util.Map getSchemesMap() { + return internalGetSchemes().getMap(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public /* nullable */ +io.a2a.grpc.StringList getSchemesOrDefault( + java.lang.String key, + /* nullable */ +io.a2a.grpc.StringList defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetSchemes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public io.a2a.grpc.StringList getSchemesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetSchemes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetSchemes(), + SchemesDefaultEntryHolder.defaultEntry, + 1); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (java.util.Map.Entry entry + : internalGetSchemes().getMap().entrySet()) { + com.google.protobuf.MapEntry + schemes__ = SchemesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, schemes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.Security)) { + return super.equals(obj); + } + io.a2a.grpc.Security other = (io.a2a.grpc.Security) obj; + + if (!internalGetSchemes().equals( + other.internalGetSchemes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (!internalGetSchemes().getMap().isEmpty()) { + hash = (37 * hash) + SCHEMES_FIELD_NUMBER; + hash = (53 * hash) + internalGetSchemes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.Security parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Security parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Security parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Security parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Security parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Security parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Security parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Security parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.Security parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.Security parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.Security parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Security parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.Security prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.Security} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.Security) + io.a2a.grpc.SecurityOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 1: + return internalGetSchemes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 1: + return internalGetMutableSchemes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Security.class, io.a2a.grpc.Security.Builder.class); + } + + // Construct using io.a2a.grpc.Security.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + internalGetMutableSchemes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.Security getDefaultInstanceForType() { + return io.a2a.grpc.Security.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.Security build() { + io.a2a.grpc.Security result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.Security buildPartial() { + io.a2a.grpc.Security result = new io.a2a.grpc.Security(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.Security result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.schemes_ = internalGetSchemes().build(SchemesDefaultEntryHolder.defaultEntry); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.Security) { + return mergeFrom((io.a2a.grpc.Security)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.Security other) { + if (other == io.a2a.grpc.Security.getDefaultInstance()) return this; + internalGetMutableSchemes().mergeFrom( + other.internalGetSchemes()); + bitField0_ |= 0x00000001; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + com.google.protobuf.MapEntry + schemes__ = input.readMessage( + SchemesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableSchemes().ensureBuilderMap().put( + schemes__.getKey(), schemes__.getValue()); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private static final class SchemesConverter implements com.google.protobuf.MapFieldBuilder.Converter { + @java.lang.Override + public io.a2a.grpc.StringList build(io.a2a.grpc.StringListOrBuilder val) { + if (val instanceof io.a2a.grpc.StringList) { return (io.a2a.grpc.StringList) val; } + return ((io.a2a.grpc.StringList.Builder) val).build(); + } + + @java.lang.Override + public com.google.protobuf.MapEntry defaultEntry() { + return SchemesDefaultEntryHolder.defaultEntry; + } + }; + private static final SchemesConverter schemesConverter = new SchemesConverter(); + + private com.google.protobuf.MapFieldBuilder< + java.lang.String, io.a2a.grpc.StringListOrBuilder, io.a2a.grpc.StringList, io.a2a.grpc.StringList.Builder> schemes_; + private com.google.protobuf.MapFieldBuilder + internalGetSchemes() { + if (schemes_ == null) { + return new com.google.protobuf.MapFieldBuilder<>(schemesConverter); + } + return schemes_; + } + private com.google.protobuf.MapFieldBuilder + internalGetMutableSchemes() { + if (schemes_ == null) { + schemes_ = new com.google.protobuf.MapFieldBuilder<>(schemesConverter); + } + bitField0_ |= 0x00000001; + onChanged(); + return schemes_; + } + public int getSchemesCount() { + return internalGetSchemes().ensureBuilderMap().size(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public boolean containsSchemes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetSchemes().ensureBuilderMap().containsKey(key); + } + /** + * Use {@link #getSchemesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getSchemes() { + return getSchemesMap(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public java.util.Map getSchemesMap() { + return internalGetSchemes().getImmutableMap(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public /* nullable */ +io.a2a.grpc.StringList getSchemesOrDefault( + java.lang.String key, + /* nullable */ +io.a2a.grpc.StringList defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = internalGetMutableSchemes().ensureBuilderMap(); + return map.containsKey(key) ? schemesConverter.build(map.get(key)) : defaultValue; + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + @java.lang.Override + public io.a2a.grpc.StringList getSchemesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = internalGetMutableSchemes().ensureBuilderMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return schemesConverter.build(map.get(key)); + } + public Builder clearSchemes() { + bitField0_ = (bitField0_ & ~0x00000001); + internalGetMutableSchemes().clear(); + return this; + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + public Builder removeSchemes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableSchemes().ensureBuilderMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableSchemes() { + bitField0_ |= 0x00000001; + return internalGetMutableSchemes().ensureMessageMap(); + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + public Builder putSchemes( + java.lang.String key, + io.a2a.grpc.StringList value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableSchemes().ensureBuilderMap() + .put(key, value); + bitField0_ |= 0x00000001; + return this; + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + public Builder putAllSchemes( + java.util.Map values) { + for (java.util.Map.Entry e : values.entrySet()) { + if (e.getKey() == null || e.getValue() == null) { + throw new NullPointerException(); + } + } + internalGetMutableSchemes().ensureBuilderMap() + .putAll(values); + bitField0_ |= 0x00000001; + return this; + } + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + public io.a2a.grpc.StringList.Builder putSchemesBuilderIfAbsent( + java.lang.String key) { + java.util.Map builderMap = internalGetMutableSchemes().ensureBuilderMap(); + io.a2a.grpc.StringListOrBuilder entry = builderMap.get(key); + if (entry == null) { + entry = io.a2a.grpc.StringList.newBuilder(); + builderMap.put(key, entry); + } + if (entry instanceof io.a2a.grpc.StringList) { + entry = ((io.a2a.grpc.StringList) entry).toBuilder(); + builderMap.put(key, entry); + } + return (io.a2a.grpc.StringList.Builder) entry; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.Security) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.Security) + private static final io.a2a.grpc.Security DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.Security(); + } + + public static io.a2a.grpc.Security getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Security parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.Security getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java new file mode 100644 index 000000000..c6cad1a13 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java @@ -0,0 +1,46 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface SecurityOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.Security) + com.google.protobuf.MessageOrBuilder { + + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + int getSchemesCount(); + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + boolean containsSchemes( + java.lang.String key); + /** + * Use {@link #getSchemesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getSchemes(); + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + java.util.Map + getSchemesMap(); + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + /* nullable */ +io.a2a.grpc.StringList getSchemesOrDefault( + java.lang.String key, + /* nullable */ +io.a2a.grpc.StringList defaultValue); + /** + * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + */ + io.a2a.grpc.StringList getSchemesOrThrow( + java.lang.String key); +} diff --git a/grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/grpc/src/main/java/io/a2a/grpc/SecurityScheme.java new file mode 100644 index 000000000..a0de79755 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SecurityScheme.java @@ -0,0 +1,1273 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.SecurityScheme} + */ +@com.google.protobuf.Generated +public final class SecurityScheme extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.SecurityScheme) + SecuritySchemeOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + SecurityScheme.class.getName()); + } + // Use SecurityScheme.newBuilder() to construct. + private SecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private SecurityScheme() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SecurityScheme.class, io.a2a.grpc.SecurityScheme.Builder.class); + } + + private int schemeCase_ = 0; + @SuppressWarnings("serial") + private java.lang.Object scheme_; + public enum SchemeCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + API_KEY_SECURITY_SCHEME(1), + HTTP_AUTH_SECURITY_SCHEME(2), + OAUTH2_SECURITY_SCHEME(3), + OPEN_ID_CONNECT_SECURITY_SCHEME(4), + SCHEME_NOT_SET(0); + private final int value; + private SchemeCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static SchemeCase valueOf(int value) { + return forNumber(value); + } + + public static SchemeCase forNumber(int value) { + switch (value) { + case 1: return API_KEY_SECURITY_SCHEME; + case 2: return HTTP_AUTH_SECURITY_SCHEME; + case 3: return OAUTH2_SECURITY_SCHEME; + case 4: return OPEN_ID_CONNECT_SECURITY_SCHEME; + case 0: return SCHEME_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public SchemeCase + getSchemeCase() { + return SchemeCase.forNumber( + schemeCase_); + } + + public static final int API_KEY_SECURITY_SCHEME_FIELD_NUMBER = 1; + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * @return Whether the apiKeySecurityScheme field is set. + */ + @java.lang.Override + public boolean hasApiKeySecurityScheme() { + return schemeCase_ == 1; + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * @return The apiKeySecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme() { + if (schemeCase_ == 1) { + return (io.a2a.grpc.APIKeySecurityScheme) scheme_; + } + return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder() { + if (schemeCase_ == 1) { + return (io.a2a.grpc.APIKeySecurityScheme) scheme_; + } + return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } + + public static final int HTTP_AUTH_SECURITY_SCHEME_FIELD_NUMBER = 2; + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * @return Whether the httpAuthSecurityScheme field is set. + */ + @java.lang.Override + public boolean hasHttpAuthSecurityScheme() { + return schemeCase_ == 2; + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * @return The httpAuthSecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme() { + if (schemeCase_ == 2) { + return (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_; + } + return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder() { + if (schemeCase_ == 2) { + return (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_; + } + return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } + + public static final int OAUTH2_SECURITY_SCHEME_FIELD_NUMBER = 3; + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * @return Whether the oauth2SecurityScheme field is set. + */ + @java.lang.Override + public boolean hasOauth2SecurityScheme() { + return schemeCase_ == 3; + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * @return The oauth2SecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme() { + if (schemeCase_ == 3) { + return (io.a2a.grpc.OAuth2SecurityScheme) scheme_; + } + return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder() { + if (schemeCase_ == 3) { + return (io.a2a.grpc.OAuth2SecurityScheme) scheme_; + } + return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } + + public static final int OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_NUMBER = 4; + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * @return Whether the openIdConnectSecurityScheme field is set. + */ + @java.lang.Override + public boolean hasOpenIdConnectSecurityScheme() { + return schemeCase_ == 4; + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * @return The openIdConnectSecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme() { + if (schemeCase_ == 4) { + return (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_; + } + return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder() { + if (schemeCase_ == 4) { + return (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_; + } + return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (schemeCase_ == 1) { + output.writeMessage(1, (io.a2a.grpc.APIKeySecurityScheme) scheme_); + } + if (schemeCase_ == 2) { + output.writeMessage(2, (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_); + } + if (schemeCase_ == 3) { + output.writeMessage(3, (io.a2a.grpc.OAuth2SecurityScheme) scheme_); + } + if (schemeCase_ == 4) { + output.writeMessage(4, (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (schemeCase_ == 1) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, (io.a2a.grpc.APIKeySecurityScheme) scheme_); + } + if (schemeCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_); + } + if (schemeCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, (io.a2a.grpc.OAuth2SecurityScheme) scheme_); + } + if (schemeCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.SecurityScheme)) { + return super.equals(obj); + } + io.a2a.grpc.SecurityScheme other = (io.a2a.grpc.SecurityScheme) obj; + + if (!getSchemeCase().equals(other.getSchemeCase())) return false; + switch (schemeCase_) { + case 1: + if (!getApiKeySecurityScheme() + .equals(other.getApiKeySecurityScheme())) return false; + break; + case 2: + if (!getHttpAuthSecurityScheme() + .equals(other.getHttpAuthSecurityScheme())) return false; + break; + case 3: + if (!getOauth2SecurityScheme() + .equals(other.getOauth2SecurityScheme())) return false; + break; + case 4: + if (!getOpenIdConnectSecurityScheme() + .equals(other.getOpenIdConnectSecurityScheme())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (schemeCase_) { + case 1: + hash = (37 * hash) + API_KEY_SECURITY_SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getApiKeySecurityScheme().hashCode(); + break; + case 2: + hash = (37 * hash) + HTTP_AUTH_SECURITY_SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getHttpAuthSecurityScheme().hashCode(); + break; + case 3: + hash = (37 * hash) + OAUTH2_SECURITY_SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getOauth2SecurityScheme().hashCode(); + break; + case 4: + hash = (37 * hash) + OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getOpenIdConnectSecurityScheme().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.SecurityScheme parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SecurityScheme parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SecurityScheme parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.SecurityScheme parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.SecurityScheme parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.SecurityScheme prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.SecurityScheme} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.SecurityScheme) + io.a2a.grpc.SecuritySchemeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SecurityScheme.class, io.a2a.grpc.SecurityScheme.Builder.class); + } + + // Construct using io.a2a.grpc.SecurityScheme.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (apiKeySecuritySchemeBuilder_ != null) { + apiKeySecuritySchemeBuilder_.clear(); + } + if (httpAuthSecuritySchemeBuilder_ != null) { + httpAuthSecuritySchemeBuilder_.clear(); + } + if (oauth2SecuritySchemeBuilder_ != null) { + oauth2SecuritySchemeBuilder_.clear(); + } + if (openIdConnectSecuritySchemeBuilder_ != null) { + openIdConnectSecuritySchemeBuilder_.clear(); + } + schemeCase_ = 0; + scheme_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityScheme_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.SecurityScheme getDefaultInstanceForType() { + return io.a2a.grpc.SecurityScheme.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.SecurityScheme build() { + io.a2a.grpc.SecurityScheme result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.SecurityScheme buildPartial() { + io.a2a.grpc.SecurityScheme result = new io.a2a.grpc.SecurityScheme(this); + if (bitField0_ != 0) { buildPartial0(result); } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.SecurityScheme result) { + int from_bitField0_ = bitField0_; + } + + private void buildPartialOneofs(io.a2a.grpc.SecurityScheme result) { + result.schemeCase_ = schemeCase_; + result.scheme_ = this.scheme_; + if (schemeCase_ == 1 && + apiKeySecuritySchemeBuilder_ != null) { + result.scheme_ = apiKeySecuritySchemeBuilder_.build(); + } + if (schemeCase_ == 2 && + httpAuthSecuritySchemeBuilder_ != null) { + result.scheme_ = httpAuthSecuritySchemeBuilder_.build(); + } + if (schemeCase_ == 3 && + oauth2SecuritySchemeBuilder_ != null) { + result.scheme_ = oauth2SecuritySchemeBuilder_.build(); + } + if (schemeCase_ == 4 && + openIdConnectSecuritySchemeBuilder_ != null) { + result.scheme_ = openIdConnectSecuritySchemeBuilder_.build(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.SecurityScheme) { + return mergeFrom((io.a2a.grpc.SecurityScheme)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.SecurityScheme other) { + if (other == io.a2a.grpc.SecurityScheme.getDefaultInstance()) return this; + switch (other.getSchemeCase()) { + case API_KEY_SECURITY_SCHEME: { + mergeApiKeySecurityScheme(other.getApiKeySecurityScheme()); + break; + } + case HTTP_AUTH_SECURITY_SCHEME: { + mergeHttpAuthSecurityScheme(other.getHttpAuthSecurityScheme()); + break; + } + case OAUTH2_SECURITY_SCHEME: { + mergeOauth2SecurityScheme(other.getOauth2SecurityScheme()); + break; + } + case OPEN_ID_CONNECT_SECURITY_SCHEME: { + mergeOpenIdConnectSecurityScheme(other.getOpenIdConnectSecurityScheme()); + break; + } + case SCHEME_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + input.readMessage( + internalGetApiKeySecuritySchemeFieldBuilder().getBuilder(), + extensionRegistry); + schemeCase_ = 1; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetHttpAuthSecuritySchemeFieldBuilder().getBuilder(), + extensionRegistry); + schemeCase_ = 2; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetOauth2SecuritySchemeFieldBuilder().getBuilder(), + extensionRegistry); + schemeCase_ = 3; + break; + } // case 26 + case 34: { + input.readMessage( + internalGetOpenIdConnectSecuritySchemeFieldBuilder().getBuilder(), + extensionRegistry); + schemeCase_ = 4; + break; + } // case 34 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int schemeCase_ = 0; + private java.lang.Object scheme_; + public SchemeCase + getSchemeCase() { + return SchemeCase.forNumber( + schemeCase_); + } + + public Builder clearScheme() { + schemeCase_ = 0; + scheme_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.APIKeySecurityScheme, io.a2a.grpc.APIKeySecurityScheme.Builder, io.a2a.grpc.APIKeySecuritySchemeOrBuilder> apiKeySecuritySchemeBuilder_; + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * @return Whether the apiKeySecurityScheme field is set. + */ + @java.lang.Override + public boolean hasApiKeySecurityScheme() { + return schemeCase_ == 1; + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * @return The apiKeySecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme() { + if (apiKeySecuritySchemeBuilder_ == null) { + if (schemeCase_ == 1) { + return (io.a2a.grpc.APIKeySecurityScheme) scheme_; + } + return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } else { + if (schemeCase_ == 1) { + return apiKeySecuritySchemeBuilder_.getMessage(); + } + return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + public Builder setApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { + if (apiKeySecuritySchemeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + scheme_ = value; + onChanged(); + } else { + apiKeySecuritySchemeBuilder_.setMessage(value); + } + schemeCase_ = 1; + return this; + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + public Builder setApiKeySecurityScheme( + io.a2a.grpc.APIKeySecurityScheme.Builder builderForValue) { + if (apiKeySecuritySchemeBuilder_ == null) { + scheme_ = builderForValue.build(); + onChanged(); + } else { + apiKeySecuritySchemeBuilder_.setMessage(builderForValue.build()); + } + schemeCase_ = 1; + return this; + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + public Builder mergeApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { + if (apiKeySecuritySchemeBuilder_ == null) { + if (schemeCase_ == 1 && + scheme_ != io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance()) { + scheme_ = io.a2a.grpc.APIKeySecurityScheme.newBuilder((io.a2a.grpc.APIKeySecurityScheme) scheme_) + .mergeFrom(value).buildPartial(); + } else { + scheme_ = value; + } + onChanged(); + } else { + if (schemeCase_ == 1) { + apiKeySecuritySchemeBuilder_.mergeFrom(value); + } else { + apiKeySecuritySchemeBuilder_.setMessage(value); + } + } + schemeCase_ = 1; + return this; + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + public Builder clearApiKeySecurityScheme() { + if (apiKeySecuritySchemeBuilder_ == null) { + if (schemeCase_ == 1) { + schemeCase_ = 0; + scheme_ = null; + onChanged(); + } + } else { + if (schemeCase_ == 1) { + schemeCase_ = 0; + scheme_ = null; + } + apiKeySecuritySchemeBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + public io.a2a.grpc.APIKeySecurityScheme.Builder getApiKeySecuritySchemeBuilder() { + return internalGetApiKeySecuritySchemeFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder() { + if ((schemeCase_ == 1) && (apiKeySecuritySchemeBuilder_ != null)) { + return apiKeySecuritySchemeBuilder_.getMessageOrBuilder(); + } else { + if (schemeCase_ == 1) { + return (io.a2a.grpc.APIKeySecurityScheme) scheme_; + } + return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.APIKeySecurityScheme, io.a2a.grpc.APIKeySecurityScheme.Builder, io.a2a.grpc.APIKeySecuritySchemeOrBuilder> + internalGetApiKeySecuritySchemeFieldBuilder() { + if (apiKeySecuritySchemeBuilder_ == null) { + if (!(schemeCase_ == 1)) { + scheme_ = io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); + } + apiKeySecuritySchemeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.APIKeySecurityScheme, io.a2a.grpc.APIKeySecurityScheme.Builder, io.a2a.grpc.APIKeySecuritySchemeOrBuilder>( + (io.a2a.grpc.APIKeySecurityScheme) scheme_, + getParentForChildren(), + isClean()); + scheme_ = null; + } + schemeCase_ = 1; + onChanged(); + return apiKeySecuritySchemeBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.HTTPAuthSecurityScheme, io.a2a.grpc.HTTPAuthSecurityScheme.Builder, io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder> httpAuthSecuritySchemeBuilder_; + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * @return Whether the httpAuthSecurityScheme field is set. + */ + @java.lang.Override + public boolean hasHttpAuthSecurityScheme() { + return schemeCase_ == 2; + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * @return The httpAuthSecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme() { + if (httpAuthSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 2) { + return (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_; + } + return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } else { + if (schemeCase_ == 2) { + return httpAuthSecuritySchemeBuilder_.getMessage(); + } + return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + public Builder setHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme value) { + if (httpAuthSecuritySchemeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + scheme_ = value; + onChanged(); + } else { + httpAuthSecuritySchemeBuilder_.setMessage(value); + } + schemeCase_ = 2; + return this; + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + public Builder setHttpAuthSecurityScheme( + io.a2a.grpc.HTTPAuthSecurityScheme.Builder builderForValue) { + if (httpAuthSecuritySchemeBuilder_ == null) { + scheme_ = builderForValue.build(); + onChanged(); + } else { + httpAuthSecuritySchemeBuilder_.setMessage(builderForValue.build()); + } + schemeCase_ = 2; + return this; + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + public Builder mergeHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme value) { + if (httpAuthSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 2 && + scheme_ != io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance()) { + scheme_ = io.a2a.grpc.HTTPAuthSecurityScheme.newBuilder((io.a2a.grpc.HTTPAuthSecurityScheme) scheme_) + .mergeFrom(value).buildPartial(); + } else { + scheme_ = value; + } + onChanged(); + } else { + if (schemeCase_ == 2) { + httpAuthSecuritySchemeBuilder_.mergeFrom(value); + } else { + httpAuthSecuritySchemeBuilder_.setMessage(value); + } + } + schemeCase_ = 2; + return this; + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + public Builder clearHttpAuthSecurityScheme() { + if (httpAuthSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 2) { + schemeCase_ = 0; + scheme_ = null; + onChanged(); + } + } else { + if (schemeCase_ == 2) { + schemeCase_ = 0; + scheme_ = null; + } + httpAuthSecuritySchemeBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + public io.a2a.grpc.HTTPAuthSecurityScheme.Builder getHttpAuthSecuritySchemeBuilder() { + return internalGetHttpAuthSecuritySchemeFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder() { + if ((schemeCase_ == 2) && (httpAuthSecuritySchemeBuilder_ != null)) { + return httpAuthSecuritySchemeBuilder_.getMessageOrBuilder(); + } else { + if (schemeCase_ == 2) { + return (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_; + } + return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.HTTPAuthSecurityScheme, io.a2a.grpc.HTTPAuthSecurityScheme.Builder, io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder> + internalGetHttpAuthSecuritySchemeFieldBuilder() { + if (httpAuthSecuritySchemeBuilder_ == null) { + if (!(schemeCase_ == 2)) { + scheme_ = io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); + } + httpAuthSecuritySchemeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.HTTPAuthSecurityScheme, io.a2a.grpc.HTTPAuthSecurityScheme.Builder, io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder>( + (io.a2a.grpc.HTTPAuthSecurityScheme) scheme_, + getParentForChildren(), + isClean()); + scheme_ = null; + } + schemeCase_ = 2; + onChanged(); + return httpAuthSecuritySchemeBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OAuth2SecurityScheme, io.a2a.grpc.OAuth2SecurityScheme.Builder, io.a2a.grpc.OAuth2SecuritySchemeOrBuilder> oauth2SecuritySchemeBuilder_; + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * @return Whether the oauth2SecurityScheme field is set. + */ + @java.lang.Override + public boolean hasOauth2SecurityScheme() { + return schemeCase_ == 3; + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * @return The oauth2SecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme() { + if (oauth2SecuritySchemeBuilder_ == null) { + if (schemeCase_ == 3) { + return (io.a2a.grpc.OAuth2SecurityScheme) scheme_; + } + return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } else { + if (schemeCase_ == 3) { + return oauth2SecuritySchemeBuilder_.getMessage(); + } + return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + public Builder setOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { + if (oauth2SecuritySchemeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + scheme_ = value; + onChanged(); + } else { + oauth2SecuritySchemeBuilder_.setMessage(value); + } + schemeCase_ = 3; + return this; + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + public Builder setOauth2SecurityScheme( + io.a2a.grpc.OAuth2SecurityScheme.Builder builderForValue) { + if (oauth2SecuritySchemeBuilder_ == null) { + scheme_ = builderForValue.build(); + onChanged(); + } else { + oauth2SecuritySchemeBuilder_.setMessage(builderForValue.build()); + } + schemeCase_ = 3; + return this; + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + public Builder mergeOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { + if (oauth2SecuritySchemeBuilder_ == null) { + if (schemeCase_ == 3 && + scheme_ != io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance()) { + scheme_ = io.a2a.grpc.OAuth2SecurityScheme.newBuilder((io.a2a.grpc.OAuth2SecurityScheme) scheme_) + .mergeFrom(value).buildPartial(); + } else { + scheme_ = value; + } + onChanged(); + } else { + if (schemeCase_ == 3) { + oauth2SecuritySchemeBuilder_.mergeFrom(value); + } else { + oauth2SecuritySchemeBuilder_.setMessage(value); + } + } + schemeCase_ = 3; + return this; + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + public Builder clearOauth2SecurityScheme() { + if (oauth2SecuritySchemeBuilder_ == null) { + if (schemeCase_ == 3) { + schemeCase_ = 0; + scheme_ = null; + onChanged(); + } + } else { + if (schemeCase_ == 3) { + schemeCase_ = 0; + scheme_ = null; + } + oauth2SecuritySchemeBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + public io.a2a.grpc.OAuth2SecurityScheme.Builder getOauth2SecuritySchemeBuilder() { + return internalGetOauth2SecuritySchemeFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder() { + if ((schemeCase_ == 3) && (oauth2SecuritySchemeBuilder_ != null)) { + return oauth2SecuritySchemeBuilder_.getMessageOrBuilder(); + } else { + if (schemeCase_ == 3) { + return (io.a2a.grpc.OAuth2SecurityScheme) scheme_; + } + return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OAuth2SecurityScheme, io.a2a.grpc.OAuth2SecurityScheme.Builder, io.a2a.grpc.OAuth2SecuritySchemeOrBuilder> + internalGetOauth2SecuritySchemeFieldBuilder() { + if (oauth2SecuritySchemeBuilder_ == null) { + if (!(schemeCase_ == 3)) { + scheme_ = io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); + } + oauth2SecuritySchemeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OAuth2SecurityScheme, io.a2a.grpc.OAuth2SecurityScheme.Builder, io.a2a.grpc.OAuth2SecuritySchemeOrBuilder>( + (io.a2a.grpc.OAuth2SecurityScheme) scheme_, + getParentForChildren(), + isClean()); + scheme_ = null; + } + schemeCase_ = 3; + onChanged(); + return oauth2SecuritySchemeBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OpenIdConnectSecurityScheme, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder, io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder> openIdConnectSecuritySchemeBuilder_; + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * @return Whether the openIdConnectSecurityScheme field is set. + */ + @java.lang.Override + public boolean hasOpenIdConnectSecurityScheme() { + return schemeCase_ == 4; + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * @return The openIdConnectSecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme() { + if (openIdConnectSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 4) { + return (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_; + } + return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } else { + if (schemeCase_ == 4) { + return openIdConnectSecuritySchemeBuilder_.getMessage(); + } + return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + public Builder setOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityScheme value) { + if (openIdConnectSecuritySchemeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + scheme_ = value; + onChanged(); + } else { + openIdConnectSecuritySchemeBuilder_.setMessage(value); + } + schemeCase_ = 4; + return this; + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + public Builder setOpenIdConnectSecurityScheme( + io.a2a.grpc.OpenIdConnectSecurityScheme.Builder builderForValue) { + if (openIdConnectSecuritySchemeBuilder_ == null) { + scheme_ = builderForValue.build(); + onChanged(); + } else { + openIdConnectSecuritySchemeBuilder_.setMessage(builderForValue.build()); + } + schemeCase_ = 4; + return this; + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + public Builder mergeOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityScheme value) { + if (openIdConnectSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 4 && + scheme_ != io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance()) { + scheme_ = io.a2a.grpc.OpenIdConnectSecurityScheme.newBuilder((io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_) + .mergeFrom(value).buildPartial(); + } else { + scheme_ = value; + } + onChanged(); + } else { + if (schemeCase_ == 4) { + openIdConnectSecuritySchemeBuilder_.mergeFrom(value); + } else { + openIdConnectSecuritySchemeBuilder_.setMessage(value); + } + } + schemeCase_ = 4; + return this; + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + public Builder clearOpenIdConnectSecurityScheme() { + if (openIdConnectSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 4) { + schemeCase_ = 0; + scheme_ = null; + onChanged(); + } + } else { + if (schemeCase_ == 4) { + schemeCase_ = 0; + scheme_ = null; + } + openIdConnectSecuritySchemeBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + public io.a2a.grpc.OpenIdConnectSecurityScheme.Builder getOpenIdConnectSecuritySchemeBuilder() { + return internalGetOpenIdConnectSecuritySchemeFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + @java.lang.Override + public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder() { + if ((schemeCase_ == 4) && (openIdConnectSecuritySchemeBuilder_ != null)) { + return openIdConnectSecuritySchemeBuilder_.getMessageOrBuilder(); + } else { + if (schemeCase_ == 4) { + return (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_; + } + return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OpenIdConnectSecurityScheme, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder, io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder> + internalGetOpenIdConnectSecuritySchemeFieldBuilder() { + if (openIdConnectSecuritySchemeBuilder_ == null) { + if (!(schemeCase_ == 4)) { + scheme_ = io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); + } + openIdConnectSecuritySchemeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.OpenIdConnectSecurityScheme, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder, io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder>( + (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_, + getParentForChildren(), + isClean()); + scheme_ = null; + } + schemeCase_ = 4; + onChanged(); + return openIdConnectSecuritySchemeBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.SecurityScheme) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.SecurityScheme) + private static final io.a2a.grpc.SecurityScheme DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.SecurityScheme(); + } + + public static io.a2a.grpc.SecurityScheme getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SecurityScheme parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.SecurityScheme getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java new file mode 100644 index 000000000..ed4aa69c0 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java @@ -0,0 +1,74 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface SecuritySchemeOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SecurityScheme) + com.google.protobuf.MessageOrBuilder { + + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * @return Whether the apiKeySecurityScheme field is set. + */ + boolean hasApiKeySecurityScheme(); + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * @return The apiKeySecurityScheme. + */ + io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme(); + /** + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + */ + io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder(); + + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * @return Whether the httpAuthSecurityScheme field is set. + */ + boolean hasHttpAuthSecurityScheme(); + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * @return The httpAuthSecurityScheme. + */ + io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme(); + /** + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + */ + io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder(); + + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * @return Whether the oauth2SecurityScheme field is set. + */ + boolean hasOauth2SecurityScheme(); + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * @return The oauth2SecurityScheme. + */ + io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme(); + /** + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + */ + io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder(); + + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * @return Whether the openIdConnectSecurityScheme field is set. + */ + boolean hasOpenIdConnectSecurityScheme(); + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * @return The openIdConnectSecurityScheme. + */ + io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme(); + /** + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + */ + io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder(); + + io.a2a.grpc.SecurityScheme.SchemeCase getSchemeCase(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java new file mode 100644 index 000000000..42bd62bcc --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -0,0 +1,1037 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Configuration of a send message request.
+ * 
+ * + * Protobuf type {@code a2a.v1.SendMessageConfiguration} + */ +@com.google.protobuf.Generated +public final class SendMessageConfiguration extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.SendMessageConfiguration) + SendMessageConfigurationOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + SendMessageConfiguration.class.getName()); + } + // Use SendMessageConfiguration.newBuilder() to construct. + private SendMessageConfiguration(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private SendMessageConfiguration() { + acceptedOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageConfiguration_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageConfiguration_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SendMessageConfiguration.class, io.a2a.grpc.SendMessageConfiguration.Builder.class); + } + + private int bitField0_; + public static final int ACCEPTED_OUTPUT_MODES_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList acceptedOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return A list containing the acceptedOutputModes. + */ + public com.google.protobuf.ProtocolStringList + getAcceptedOutputModesList() { + return acceptedOutputModes_; + } + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return The count of acceptedOutputModes. + */ + public int getAcceptedOutputModesCount() { + return acceptedOutputModes_.size(); + } + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index of the element to return. + * @return The acceptedOutputModes at the given index. + */ + public java.lang.String getAcceptedOutputModes(int index) { + return acceptedOutputModes_.get(index); + } + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index of the value to return. + * @return The bytes of the acceptedOutputModes at the given index. + */ + public com.google.protobuf.ByteString + getAcceptedOutputModesBytes(int index) { + return acceptedOutputModes_.getByteString(index); + } + + public static final int PUSH_NOTIFICATION_FIELD_NUMBER = 2; + private io.a2a.grpc.PushNotificationConfig pushNotification_; + /** + *
+   * A configuration of a webhook that can be used to receive updates
+   * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * @return Whether the pushNotification field is set. + */ + @java.lang.Override + public boolean hasPushNotification() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * A configuration of a webhook that can be used to receive updates
+   * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * @return The pushNotification. + */ + @java.lang.Override + public io.a2a.grpc.PushNotificationConfig getPushNotification() { + return pushNotification_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + } + /** + *
+   * A configuration of a webhook that can be used to receive updates
+   * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + @java.lang.Override + public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder() { + return pushNotification_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + } + + public static final int HISTORY_LENGTH_FIELD_NUMBER = 3; + private int historyLength_ = 0; + /** + *
+   * The maximum number of messages to include in the history. if 0, the
+   * history will be unlimited.
+   * 
+ * + * int32 history_length = 3 [json_name = "historyLength"]; + * @return The historyLength. + */ + @java.lang.Override + public int getHistoryLength() { + return historyLength_; + } + + public static final int BLOCKING_FIELD_NUMBER = 4; + private boolean blocking_ = false; + /** + *
+   * If true, the message will be blocking until the task is completed. If
+   * false, the message will be non-blocking and the task will be returned
+   * immediately. It is the caller's responsibility to check for any task
+   * updates.
+   * 
+ * + * bool blocking = 4 [json_name = "blocking"]; + * @return The blocking. + */ + @java.lang.Override + public boolean getBlocking() { + return blocking_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < acceptedOutputModes_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, acceptedOutputModes_.getRaw(i)); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(2, getPushNotification()); + } + if (historyLength_ != 0) { + output.writeInt32(3, historyLength_); + } + if (blocking_ != false) { + output.writeBool(4, blocking_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < acceptedOutputModes_.size(); i++) { + dataSize += computeStringSizeNoTag(acceptedOutputModes_.getRaw(i)); + } + size += dataSize; + size += 1 * getAcceptedOutputModesList().size(); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getPushNotification()); + } + if (historyLength_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, historyLength_); + } + if (blocking_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(4, blocking_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.SendMessageConfiguration)) { + return super.equals(obj); + } + io.a2a.grpc.SendMessageConfiguration other = (io.a2a.grpc.SendMessageConfiguration) obj; + + if (!getAcceptedOutputModesList() + .equals(other.getAcceptedOutputModesList())) return false; + if (hasPushNotification() != other.hasPushNotification()) return false; + if (hasPushNotification()) { + if (!getPushNotification() + .equals(other.getPushNotification())) return false; + } + if (getHistoryLength() + != other.getHistoryLength()) return false; + if (getBlocking() + != other.getBlocking()) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getAcceptedOutputModesCount() > 0) { + hash = (37 * hash) + ACCEPTED_OUTPUT_MODES_FIELD_NUMBER; + hash = (53 * hash) + getAcceptedOutputModesList().hashCode(); + } + if (hasPushNotification()) { + hash = (37 * hash) + PUSH_NOTIFICATION_FIELD_NUMBER; + hash = (53 * hash) + getPushNotification().hashCode(); + } + hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getHistoryLength(); + hash = (37 * hash) + BLOCKING_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getBlocking()); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.SendMessageConfiguration parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.SendMessageConfiguration parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SendMessageConfiguration parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.SendMessageConfiguration prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Configuration of a send message request.
+   * 
+ * + * Protobuf type {@code a2a.v1.SendMessageConfiguration} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.SendMessageConfiguration) + io.a2a.grpc.SendMessageConfigurationOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageConfiguration_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageConfiguration_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SendMessageConfiguration.class, io.a2a.grpc.SendMessageConfiguration.Builder.class); + } + + // Construct using io.a2a.grpc.SendMessageConfiguration.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetPushNotificationFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + acceptedOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + pushNotification_ = null; + if (pushNotificationBuilder_ != null) { + pushNotificationBuilder_.dispose(); + pushNotificationBuilder_ = null; + } + historyLength_ = 0; + blocking_ = false; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageConfiguration_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageConfiguration getDefaultInstanceForType() { + return io.a2a.grpc.SendMessageConfiguration.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.SendMessageConfiguration build() { + io.a2a.grpc.SendMessageConfiguration result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageConfiguration buildPartial() { + io.a2a.grpc.SendMessageConfiguration result = new io.a2a.grpc.SendMessageConfiguration(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.SendMessageConfiguration result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + acceptedOutputModes_.makeImmutable(); + result.acceptedOutputModes_ = acceptedOutputModes_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.pushNotification_ = pushNotificationBuilder_ == null + ? pushNotification_ + : pushNotificationBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.historyLength_ = historyLength_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.blocking_ = blocking_; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.SendMessageConfiguration) { + return mergeFrom((io.a2a.grpc.SendMessageConfiguration)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.SendMessageConfiguration other) { + if (other == io.a2a.grpc.SendMessageConfiguration.getDefaultInstance()) return this; + if (!other.acceptedOutputModes_.isEmpty()) { + if (acceptedOutputModes_.isEmpty()) { + acceptedOutputModes_ = other.acceptedOutputModes_; + bitField0_ |= 0x00000001; + } else { + ensureAcceptedOutputModesIsMutable(); + acceptedOutputModes_.addAll(other.acceptedOutputModes_); + } + onChanged(); + } + if (other.hasPushNotification()) { + mergePushNotification(other.getPushNotification()); + } + if (other.getHistoryLength() != 0) { + setHistoryLength(other.getHistoryLength()); + } + if (other.getBlocking() != false) { + setBlocking(other.getBlocking()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + ensureAcceptedOutputModesIsMutable(); + acceptedOutputModes_.add(s); + break; + } // case 10 + case 18: { + input.readMessage( + internalGetPushNotificationFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 24: { + historyLength_ = input.readInt32(); + bitField0_ |= 0x00000004; + break; + } // case 24 + case 32: { + blocking_ = input.readBool(); + bitField0_ |= 0x00000008; + break; + } // case 32 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private com.google.protobuf.LazyStringArrayList acceptedOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureAcceptedOutputModesIsMutable() { + if (!acceptedOutputModes_.isModifiable()) { + acceptedOutputModes_ = new com.google.protobuf.LazyStringArrayList(acceptedOutputModes_); + } + bitField0_ |= 0x00000001; + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return A list containing the acceptedOutputModes. + */ + public com.google.protobuf.ProtocolStringList + getAcceptedOutputModesList() { + acceptedOutputModes_.makeImmutable(); + return acceptedOutputModes_; + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return The count of acceptedOutputModes. + */ + public int getAcceptedOutputModesCount() { + return acceptedOutputModes_.size(); + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index of the element to return. + * @return The acceptedOutputModes at the given index. + */ + public java.lang.String getAcceptedOutputModes(int index) { + return acceptedOutputModes_.get(index); + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index of the value to return. + * @return The bytes of the acceptedOutputModes at the given index. + */ + public com.google.protobuf.ByteString + getAcceptedOutputModesBytes(int index) { + return acceptedOutputModes_.getByteString(index); + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index to set the value at. + * @param value The acceptedOutputModes to set. + * @return This builder for chaining. + */ + public Builder setAcceptedOutputModes( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureAcceptedOutputModesIsMutable(); + acceptedOutputModes_.set(index, value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param value The acceptedOutputModes to add. + * @return This builder for chaining. + */ + public Builder addAcceptedOutputModes( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureAcceptedOutputModesIsMutable(); + acceptedOutputModes_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param values The acceptedOutputModes to add. + * @return This builder for chaining. + */ + public Builder addAllAcceptedOutputModes( + java.lang.Iterable values) { + ensureAcceptedOutputModesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, acceptedOutputModes_); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return This builder for chaining. + */ + public Builder clearAcceptedOutputModes() { + acceptedOutputModes_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001);; + onChanged(); + return this; + } + /** + *
+     * The output modes that the agent is expected to respond with.
+     * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param value The bytes of the acceptedOutputModes to add. + * @return This builder for chaining. + */ + public Builder addAcceptedOutputModesBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureAcceptedOutputModesIsMutable(); + acceptedOutputModes_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private io.a2a.grpc.PushNotificationConfig pushNotification_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationBuilder_; + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * @return Whether the pushNotification field is set. + */ + public boolean hasPushNotification() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * @return The pushNotification. + */ + public io.a2a.grpc.PushNotificationConfig getPushNotification() { + if (pushNotificationBuilder_ == null) { + return pushNotification_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + } else { + return pushNotificationBuilder_.getMessage(); + } + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + public Builder setPushNotification(io.a2a.grpc.PushNotificationConfig value) { + if (pushNotificationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + pushNotification_ = value; + } else { + pushNotificationBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + public Builder setPushNotification( + io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { + if (pushNotificationBuilder_ == null) { + pushNotification_ = builderForValue.build(); + } else { + pushNotificationBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + public Builder mergePushNotification(io.a2a.grpc.PushNotificationConfig value) { + if (pushNotificationBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0) && + pushNotification_ != null && + pushNotification_ != io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) { + getPushNotificationBuilder().mergeFrom(value); + } else { + pushNotification_ = value; + } + } else { + pushNotificationBuilder_.mergeFrom(value); + } + if (pushNotification_ != null) { + bitField0_ |= 0x00000002; + onChanged(); + } + return this; + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + public Builder clearPushNotification() { + bitField0_ = (bitField0_ & ~0x00000002); + pushNotification_ = null; + if (pushNotificationBuilder_ != null) { + pushNotificationBuilder_.dispose(); + pushNotificationBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return internalGetPushNotificationFieldBuilder().getBuilder(); + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder() { + if (pushNotificationBuilder_ != null) { + return pushNotificationBuilder_.getMessageOrBuilder(); + } else { + return pushNotification_ == null ? + io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + } + } + /** + *
+     * A configuration of a webhook that can be used to receive updates
+     * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> + internalGetPushNotificationFieldBuilder() { + if (pushNotificationBuilder_ == null) { + pushNotificationBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder>( + getPushNotification(), + getParentForChildren(), + isClean()); + pushNotification_ = null; + } + return pushNotificationBuilder_; + } + + private int historyLength_ ; + /** + *
+     * The maximum number of messages to include in the history. if 0, the
+     * history will be unlimited.
+     * 
+ * + * int32 history_length = 3 [json_name = "historyLength"]; + * @return The historyLength. + */ + @java.lang.Override + public int getHistoryLength() { + return historyLength_; + } + /** + *
+     * The maximum number of messages to include in the history. if 0, the
+     * history will be unlimited.
+     * 
+ * + * int32 history_length = 3 [json_name = "historyLength"]; + * @param value The historyLength to set. + * @return This builder for chaining. + */ + public Builder setHistoryLength(int value) { + + historyLength_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The maximum number of messages to include in the history. if 0, the
+     * history will be unlimited.
+     * 
+ * + * int32 history_length = 3 [json_name = "historyLength"]; + * @return This builder for chaining. + */ + public Builder clearHistoryLength() { + bitField0_ = (bitField0_ & ~0x00000004); + historyLength_ = 0; + onChanged(); + return this; + } + + private boolean blocking_ ; + /** + *
+     * If true, the message will be blocking until the task is completed. If
+     * false, the message will be non-blocking and the task will be returned
+     * immediately. It is the caller's responsibility to check for any task
+     * updates.
+     * 
+ * + * bool blocking = 4 [json_name = "blocking"]; + * @return The blocking. + */ + @java.lang.Override + public boolean getBlocking() { + return blocking_; + } + /** + *
+     * If true, the message will be blocking until the task is completed. If
+     * false, the message will be non-blocking and the task will be returned
+     * immediately. It is the caller's responsibility to check for any task
+     * updates.
+     * 
+ * + * bool blocking = 4 [json_name = "blocking"]; + * @param value The blocking to set. + * @return This builder for chaining. + */ + public Builder setBlocking(boolean value) { + + blocking_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * If true, the message will be blocking until the task is completed. If
+     * false, the message will be non-blocking and the task will be returned
+     * immediately. It is the caller's responsibility to check for any task
+     * updates.
+     * 
+ * + * bool blocking = 4 [json_name = "blocking"]; + * @return This builder for chaining. + */ + public Builder clearBlocking() { + bitField0_ = (bitField0_ & ~0x00000008); + blocking_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.SendMessageConfiguration) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.SendMessageConfiguration) + private static final io.a2a.grpc.SendMessageConfiguration DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.SendMessageConfiguration(); + } + + public static io.a2a.grpc.SendMessageConfiguration getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SendMessageConfiguration parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageConfiguration getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java new file mode 100644 index 000000000..7df0eb45a --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -0,0 +1,104 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface SendMessageConfigurationOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SendMessageConfiguration) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return A list containing the acceptedOutputModes. + */ + java.util.List + getAcceptedOutputModesList(); + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @return The count of acceptedOutputModes. + */ + int getAcceptedOutputModesCount(); + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index of the element to return. + * @return The acceptedOutputModes at the given index. + */ + java.lang.String getAcceptedOutputModes(int index); + /** + *
+   * The output modes that the agent is expected to respond with.
+   * 
+ * + * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * @param index The index of the value to return. + * @return The bytes of the acceptedOutputModes at the given index. + */ + com.google.protobuf.ByteString + getAcceptedOutputModesBytes(int index); + + /** + *
+   * A configuration of a webhook that can be used to receive updates
+   * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * @return Whether the pushNotification field is set. + */ + boolean hasPushNotification(); + /** + *
+   * A configuration of a webhook that can be used to receive updates
+   * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * @return The pushNotification. + */ + io.a2a.grpc.PushNotificationConfig getPushNotification(); + /** + *
+   * A configuration of a webhook that can be used to receive updates
+   * 
+ * + * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + */ + io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder(); + + /** + *
+   * The maximum number of messages to include in the history. if 0, the
+   * history will be unlimited.
+   * 
+ * + * int32 history_length = 3 [json_name = "historyLength"]; + * @return The historyLength. + */ + int getHistoryLength(); + + /** + *
+   * If true, the message will be blocking until the task is completed. If
+   * false, the message will be non-blocking and the task will be returned
+   * immediately. It is the caller's responsibility to check for any task
+   * updates.
+   * 
+ * + * bool blocking = 4 [json_name = "blocking"]; + * @return The blocking. + */ + boolean getBlocking(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java new file mode 100644 index 000000000..7b3c9cb87 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -0,0 +1,937 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * /////////// Request Messages ///////////
+ * 
+ * + * Protobuf type {@code a2a.v1.SendMessageRequest} + */ +@com.google.protobuf.Generated +public final class SendMessageRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.SendMessageRequest) + SendMessageRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + SendMessageRequest.class.getName()); + } + // Use SendMessageRequest.newBuilder() to construct. + private SendMessageRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private SendMessageRequest() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SendMessageRequest.class, io.a2a.grpc.SendMessageRequest.Builder.class); + } + + private int bitField0_; + public static final int REQUEST_FIELD_NUMBER = 1; + private io.a2a.grpc.Message request_; + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * @return Whether the request field is set. + */ + @java.lang.Override + public boolean hasRequest() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * @return The request. + */ + @java.lang.Override + public io.a2a.grpc.Message getRequest() { + return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { + return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; + } + + public static final int CONFIGURATION_FIELD_NUMBER = 2; + private io.a2a.grpc.SendMessageConfiguration configuration_; + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * @return Whether the configuration field is set. + */ + @java.lang.Override + public boolean hasConfiguration() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * @return The configuration. + */ + @java.lang.Override + public io.a2a.grpc.SendMessageConfiguration getConfiguration() { + return configuration_ == null ? io.a2a.grpc.SendMessageConfiguration.getDefaultInstance() : configuration_; + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + @java.lang.Override + public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() { + return configuration_ == null ? io.a2a.grpc.SendMessageConfiguration.getDefaultInstance() : configuration_; + } + + public static final int METADATA_FIELD_NUMBER = 3; + private com.google.protobuf.Struct metadata_; + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(1, getRequest()); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeMessage(2, getConfiguration()); + } + if (((bitField0_ & 0x00000004) != 0)) { + output.writeMessage(3, getMetadata()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, getRequest()); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getConfiguration()); + } + if (((bitField0_ & 0x00000004) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getMetadata()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.SendMessageRequest)) { + return super.equals(obj); + } + io.a2a.grpc.SendMessageRequest other = (io.a2a.grpc.SendMessageRequest) obj; + + if (hasRequest() != other.hasRequest()) return false; + if (hasRequest()) { + if (!getRequest() + .equals(other.getRequest())) return false; + } + if (hasConfiguration() != other.hasConfiguration()) return false; + if (hasConfiguration()) { + if (!getConfiguration() + .equals(other.getConfiguration())) return false; + } + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (hasRequest()) { + hash = (37 * hash) + REQUEST_FIELD_NUMBER; + hash = (53 * hash) + getRequest().hashCode(); + } + if (hasConfiguration()) { + hash = (37 * hash) + CONFIGURATION_FIELD_NUMBER; + hash = (53 * hash) + getConfiguration().hashCode(); + } + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.SendMessageRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.SendMessageRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.SendMessageRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SendMessageRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.SendMessageRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * /////////// Request Messages ///////////
+   * 
+ * + * Protobuf type {@code a2a.v1.SendMessageRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.SendMessageRequest) + io.a2a.grpc.SendMessageRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SendMessageRequest.class, io.a2a.grpc.SendMessageRequest.Builder.class); + } + + // Construct using io.a2a.grpc.SendMessageRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetRequestFieldBuilder(); + internalGetConfigurationFieldBuilder(); + internalGetMetadataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + request_ = null; + if (requestBuilder_ != null) { + requestBuilder_.dispose(); + requestBuilder_ = null; + } + configuration_ = null; + if (configurationBuilder_ != null) { + configurationBuilder_.dispose(); + configurationBuilder_ = null; + } + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageRequest getDefaultInstanceForType() { + return io.a2a.grpc.SendMessageRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.SendMessageRequest build() { + io.a2a.grpc.SendMessageRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageRequest buildPartial() { + io.a2a.grpc.SendMessageRequest result = new io.a2a.grpc.SendMessageRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.SendMessageRequest result) { + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.request_ = requestBuilder_ == null + ? request_ + : requestBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.configuration_ = configurationBuilder_ == null + ? configuration_ + : configurationBuilder_.build(); + to_bitField0_ |= 0x00000002; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000004; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.SendMessageRequest) { + return mergeFrom((io.a2a.grpc.SendMessageRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.SendMessageRequest other) { + if (other == io.a2a.grpc.SendMessageRequest.getDefaultInstance()) return this; + if (other.hasRequest()) { + mergeRequest(other.getRequest()); + } + if (other.hasConfiguration()) { + mergeConfiguration(other.getConfiguration()); + } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + input.readMessage( + internalGetRequestFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetConfigurationFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private io.a2a.grpc.Message request_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> requestBuilder_; + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * @return Whether the request field is set. + */ + public boolean hasRequest() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * @return The request. + */ + public io.a2a.grpc.Message getRequest() { + if (requestBuilder_ == null) { + return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; + } else { + return requestBuilder_.getMessage(); + } + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder setRequest(io.a2a.grpc.Message value) { + if (requestBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + request_ = value; + } else { + requestBuilder_.setMessage(value); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder setRequest( + io.a2a.grpc.Message.Builder builderForValue) { + if (requestBuilder_ == null) { + request_ = builderForValue.build(); + } else { + requestBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder mergeRequest(io.a2a.grpc.Message value) { + if (requestBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0) && + request_ != null && + request_ != io.a2a.grpc.Message.getDefaultInstance()) { + getRequestBuilder().mergeFrom(value); + } else { + request_ = value; + } + } else { + requestBuilder_.mergeFrom(value); + } + if (request_ != null) { + bitField0_ |= 0x00000001; + onChanged(); + } + return this; + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + public Builder clearRequest() { + bitField0_ = (bitField0_ & ~0x00000001); + request_ = null; + if (requestBuilder_ != null) { + requestBuilder_.dispose(); + requestBuilder_ = null; + } + onChanged(); + return this; + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + public io.a2a.grpc.Message.Builder getRequestBuilder() { + bitField0_ |= 0x00000001; + onChanged(); + return internalGetRequestFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { + if (requestBuilder_ != null) { + return requestBuilder_.getMessageOrBuilder(); + } else { + return request_ == null ? + io.a2a.grpc.Message.getDefaultInstance() : request_; + } + } + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> + internalGetRequestFieldBuilder() { + if (requestBuilder_ == null) { + requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( + getRequest(), + getParentForChildren(), + isClean()); + request_ = null; + } + return requestBuilder_; + } + + private io.a2a.grpc.SendMessageConfiguration configuration_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.SendMessageConfiguration, io.a2a.grpc.SendMessageConfiguration.Builder, io.a2a.grpc.SendMessageConfigurationOrBuilder> configurationBuilder_; + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * @return Whether the configuration field is set. + */ + public boolean hasConfiguration() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * @return The configuration. + */ + public io.a2a.grpc.SendMessageConfiguration getConfiguration() { + if (configurationBuilder_ == null) { + return configuration_ == null ? io.a2a.grpc.SendMessageConfiguration.getDefaultInstance() : configuration_; + } else { + return configurationBuilder_.getMessage(); + } + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + public Builder setConfiguration(io.a2a.grpc.SendMessageConfiguration value) { + if (configurationBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + configuration_ = value; + } else { + configurationBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + public Builder setConfiguration( + io.a2a.grpc.SendMessageConfiguration.Builder builderForValue) { + if (configurationBuilder_ == null) { + configuration_ = builderForValue.build(); + } else { + configurationBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) { + if (configurationBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0) && + configuration_ != null && + configuration_ != io.a2a.grpc.SendMessageConfiguration.getDefaultInstance()) { + getConfigurationBuilder().mergeFrom(value); + } else { + configuration_ = value; + } + } else { + configurationBuilder_.mergeFrom(value); + } + if (configuration_ != null) { + bitField0_ |= 0x00000002; + onChanged(); + } + return this; + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + public Builder clearConfiguration() { + bitField0_ = (bitField0_ & ~0x00000002); + configuration_ = null; + if (configurationBuilder_ != null) { + configurationBuilder_.dispose(); + configurationBuilder_ = null; + } + onChanged(); + return this; + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + public io.a2a.grpc.SendMessageConfiguration.Builder getConfigurationBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return internalGetConfigurationFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() { + if (configurationBuilder_ != null) { + return configurationBuilder_.getMessageOrBuilder(); + } else { + return configuration_ == null ? + io.a2a.grpc.SendMessageConfiguration.getDefaultInstance() : configuration_; + } + } + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.SendMessageConfiguration, io.a2a.grpc.SendMessageConfiguration.Builder, io.a2a.grpc.SendMessageConfigurationOrBuilder> + internalGetConfigurationFieldBuilder() { + if (configurationBuilder_ == null) { + configurationBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.SendMessageConfiguration, io.a2a.grpc.SendMessageConfiguration.Builder, io.a2a.grpc.SendMessageConfigurationOrBuilder>( + getConfiguration(), + getParentForChildren(), + isClean()); + configuration_ = null; + } + return configurationBuilder_; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000004); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.SendMessageRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.SendMessageRequest) + private static final io.a2a.grpc.SendMessageRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.SendMessageRequest(); + } + + public static io.a2a.grpc.SendMessageRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SendMessageRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java new file mode 100644 index 000000000..824622db1 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java @@ -0,0 +1,57 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface SendMessageRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SendMessageRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * @return Whether the request field is set. + */ + boolean hasRequest(); + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * @return The request. + */ + io.a2a.grpc.Message getRequest(); + /** + * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + */ + io.a2a.grpc.MessageOrBuilder getRequestOrBuilder(); + + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * @return Whether the configuration field is set. + */ + boolean hasConfiguration(); + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * @return The configuration. + */ + io.a2a.grpc.SendMessageConfiguration getConfiguration(); + /** + * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + */ + io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder(); + + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java new file mode 100644 index 000000000..fbad3f50e --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java @@ -0,0 +1,865 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * ////// Response Messages ///////////
+ * 
+ * + * Protobuf type {@code a2a.v1.SendMessageResponse} + */ +@com.google.protobuf.Generated +public final class SendMessageResponse extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.SendMessageResponse) + SendMessageResponseOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + SendMessageResponse.class.getName()); + } + // Use SendMessageResponse.newBuilder() to construct. + private SendMessageResponse(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private SendMessageResponse() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SendMessageResponse.class, io.a2a.grpc.SendMessageResponse.Builder.class); + } + + private int payloadCase_ = 0; + @SuppressWarnings("serial") + private java.lang.Object payload_; + public enum PayloadCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + TASK(1), + MSG(2), + PAYLOAD_NOT_SET(0); + private final int value; + private PayloadCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static PayloadCase valueOf(int value) { + return forNumber(value); + } + + public static PayloadCase forNumber(int value) { + switch (value) { + case 1: return TASK; + case 2: return MSG; + case 0: return PAYLOAD_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public PayloadCase + getPayloadCase() { + return PayloadCase.forNumber( + payloadCase_); + } + + public static final int TASK_FIELD_NUMBER = 1; + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return Whether the task field is set. + */ + @java.lang.Override + public boolean hasTask() { + return payloadCase_ == 1; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return The task. + */ + @java.lang.Override + public io.a2a.grpc.Task getTask() { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + + public static final int MSG_FIELD_NUMBER = 2; + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return Whether the msg field is set. + */ + @java.lang.Override + public boolean hasMsg() { + return payloadCase_ == 2; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return The msg. + */ + @java.lang.Override + public io.a2a.grpc.Message getMsg() { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (payloadCase_ == 1) { + output.writeMessage(1, (io.a2a.grpc.Task) payload_); + } + if (payloadCase_ == 2) { + output.writeMessage(2, (io.a2a.grpc.Message) payload_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (payloadCase_ == 1) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, (io.a2a.grpc.Task) payload_); + } + if (payloadCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (io.a2a.grpc.Message) payload_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.SendMessageResponse)) { + return super.equals(obj); + } + io.a2a.grpc.SendMessageResponse other = (io.a2a.grpc.SendMessageResponse) obj; + + if (!getPayloadCase().equals(other.getPayloadCase())) return false; + switch (payloadCase_) { + case 1: + if (!getTask() + .equals(other.getTask())) return false; + break; + case 2: + if (!getMsg() + .equals(other.getMsg())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (payloadCase_) { + case 1: + hash = (37 * hash) + TASK_FIELD_NUMBER; + hash = (53 * hash) + getTask().hashCode(); + break; + case 2: + hash = (37 * hash) + MSG_FIELD_NUMBER; + hash = (53 * hash) + getMsg().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.SendMessageResponse parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.SendMessageResponse parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.SendMessageResponse parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.SendMessageResponse parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.SendMessageResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.SendMessageResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * ////// Response Messages ///////////
+   * 
+ * + * Protobuf type {@code a2a.v1.SendMessageResponse} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.SendMessageResponse) + io.a2a.grpc.SendMessageResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.SendMessageResponse.class, io.a2a.grpc.SendMessageResponse.Builder.class); + } + + // Construct using io.a2a.grpc.SendMessageResponse.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (taskBuilder_ != null) { + taskBuilder_.clear(); + } + if (msgBuilder_ != null) { + msgBuilder_.clear(); + } + payloadCase_ = 0; + payload_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_SendMessageResponse_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageResponse getDefaultInstanceForType() { + return io.a2a.grpc.SendMessageResponse.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.SendMessageResponse build() { + io.a2a.grpc.SendMessageResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageResponse buildPartial() { + io.a2a.grpc.SendMessageResponse result = new io.a2a.grpc.SendMessageResponse(this); + if (bitField0_ != 0) { buildPartial0(result); } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.SendMessageResponse result) { + int from_bitField0_ = bitField0_; + } + + private void buildPartialOneofs(io.a2a.grpc.SendMessageResponse result) { + result.payloadCase_ = payloadCase_; + result.payload_ = this.payload_; + if (payloadCase_ == 1 && + taskBuilder_ != null) { + result.payload_ = taskBuilder_.build(); + } + if (payloadCase_ == 2 && + msgBuilder_ != null) { + result.payload_ = msgBuilder_.build(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.SendMessageResponse) { + return mergeFrom((io.a2a.grpc.SendMessageResponse)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.SendMessageResponse other) { + if (other == io.a2a.grpc.SendMessageResponse.getDefaultInstance()) return this; + switch (other.getPayloadCase()) { + case TASK: { + mergeTask(other.getTask()); + break; + } + case MSG: { + mergeMsg(other.getMsg()); + break; + } + case PAYLOAD_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + input.readMessage( + internalGetTaskFieldBuilder().getBuilder(), + extensionRegistry); + payloadCase_ = 1; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetMsgFieldBuilder().getBuilder(), + extensionRegistry); + payloadCase_ = 2; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int payloadCase_ = 0; + private java.lang.Object payload_; + public PayloadCase + getPayloadCase() { + return PayloadCase.forNumber( + payloadCase_); + } + + public Builder clearPayload() { + payloadCase_ = 0; + payload_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> taskBuilder_; + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return Whether the task field is set. + */ + @java.lang.Override + public boolean hasTask() { + return payloadCase_ == 1; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return The task. + */ + @java.lang.Override + public io.a2a.grpc.Task getTask() { + if (taskBuilder_ == null) { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } else { + if (payloadCase_ == 1) { + return taskBuilder_.getMessage(); + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder setTask(io.a2a.grpc.Task value) { + if (taskBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + onChanged(); + } else { + taskBuilder_.setMessage(value); + } + payloadCase_ = 1; + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder setTask( + io.a2a.grpc.Task.Builder builderForValue) { + if (taskBuilder_ == null) { + payload_ = builderForValue.build(); + onChanged(); + } else { + taskBuilder_.setMessage(builderForValue.build()); + } + payloadCase_ = 1; + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder mergeTask(io.a2a.grpc.Task value) { + if (taskBuilder_ == null) { + if (payloadCase_ == 1 && + payload_ != io.a2a.grpc.Task.getDefaultInstance()) { + payload_ = io.a2a.grpc.Task.newBuilder((io.a2a.grpc.Task) payload_) + .mergeFrom(value).buildPartial(); + } else { + payload_ = value; + } + onChanged(); + } else { + if (payloadCase_ == 1) { + taskBuilder_.mergeFrom(value); + } else { + taskBuilder_.setMessage(value); + } + } + payloadCase_ = 1; + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder clearTask() { + if (taskBuilder_ == null) { + if (payloadCase_ == 1) { + payloadCase_ = 0; + payload_ = null; + onChanged(); + } + } else { + if (payloadCase_ == 1) { + payloadCase_ = 0; + payload_ = null; + } + taskBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public io.a2a.grpc.Task.Builder getTaskBuilder() { + return internalGetTaskFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { + if ((payloadCase_ == 1) && (taskBuilder_ != null)) { + return taskBuilder_.getMessageOrBuilder(); + } else { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> + internalGetTaskFieldBuilder() { + if (taskBuilder_ == null) { + if (!(payloadCase_ == 1)) { + payload_ = io.a2a.grpc.Task.getDefaultInstance(); + } + taskBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder>( + (io.a2a.grpc.Task) payload_, + getParentForChildren(), + isClean()); + payload_ = null; + } + payloadCase_ = 1; + onChanged(); + return taskBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> msgBuilder_; + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return Whether the msg field is set. + */ + @java.lang.Override + public boolean hasMsg() { + return payloadCase_ == 2; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return The msg. + */ + @java.lang.Override + public io.a2a.grpc.Message getMsg() { + if (msgBuilder_ == null) { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } else { + if (payloadCase_ == 2) { + return msgBuilder_.getMessage(); + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder setMsg(io.a2a.grpc.Message value) { + if (msgBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + onChanged(); + } else { + msgBuilder_.setMessage(value); + } + payloadCase_ = 2; + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder setMsg( + io.a2a.grpc.Message.Builder builderForValue) { + if (msgBuilder_ == null) { + payload_ = builderForValue.build(); + onChanged(); + } else { + msgBuilder_.setMessage(builderForValue.build()); + } + payloadCase_ = 2; + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder mergeMsg(io.a2a.grpc.Message value) { + if (msgBuilder_ == null) { + if (payloadCase_ == 2 && + payload_ != io.a2a.grpc.Message.getDefaultInstance()) { + payload_ = io.a2a.grpc.Message.newBuilder((io.a2a.grpc.Message) payload_) + .mergeFrom(value).buildPartial(); + } else { + payload_ = value; + } + onChanged(); + } else { + if (payloadCase_ == 2) { + msgBuilder_.mergeFrom(value); + } else { + msgBuilder_.setMessage(value); + } + } + payloadCase_ = 2; + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder clearMsg() { + if (msgBuilder_ == null) { + if (payloadCase_ == 2) { + payloadCase_ = 0; + payload_ = null; + onChanged(); + } + } else { + if (payloadCase_ == 2) { + payloadCase_ = 0; + payload_ = null; + } + msgBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public io.a2a.grpc.Message.Builder getMsgBuilder() { + return internalGetMsgFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { + if ((payloadCase_ == 2) && (msgBuilder_ != null)) { + return msgBuilder_.getMessageOrBuilder(); + } else { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> + internalGetMsgFieldBuilder() { + if (msgBuilder_ == null) { + if (!(payloadCase_ == 2)) { + payload_ = io.a2a.grpc.Message.getDefaultInstance(); + } + msgBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( + (io.a2a.grpc.Message) payload_, + getParentForChildren(), + isClean()); + payload_ = null; + } + payloadCase_ = 2; + onChanged(); + return msgBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.SendMessageResponse) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.SendMessageResponse) + private static final io.a2a.grpc.SendMessageResponse DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.SendMessageResponse(); + } + + public static io.a2a.grpc.SendMessageResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public SendMessageResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.SendMessageResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java new file mode 100644 index 000000000..b0032bac7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java @@ -0,0 +1,44 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface SendMessageResponseOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SendMessageResponse) + com.google.protobuf.MessageOrBuilder { + + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return Whether the task field is set. + */ + boolean hasTask(); + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return The task. + */ + io.a2a.grpc.Task getTask(); + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + io.a2a.grpc.TaskOrBuilder getTaskOrBuilder(); + + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return Whether the msg field is set. + */ + boolean hasMsg(); + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return The msg. + */ + io.a2a.grpc.Message getMsg(); + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + io.a2a.grpc.MessageOrBuilder getMsgOrBuilder(); + + io.a2a.grpc.SendMessageResponse.PayloadCase getPayloadCase(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/grpc/src/main/java/io/a2a/grpc/StreamResponse.java new file mode 100644 index 000000000..f9a76e2a7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/StreamResponse.java @@ -0,0 +1,1297 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * The stream response for a message. The stream should be one of the following
+ * sequences:
+ * If the response is a message, the stream should contain one, and only one,
+ * message and then close
+ * If the response is a task lifecycle, the first response should be a Task
+ * object followed by zero or more TaskStatusUpdateEvents and
+ * TaskArtifactUpdateEvents. The stream should complete when the Task
+ * if in an interrupted or terminal state. A stream that ends before these
+ * conditions are met are
+ * 
+ * + * Protobuf type {@code a2a.v1.StreamResponse} + */ +@com.google.protobuf.Generated +public final class StreamResponse extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.StreamResponse) + StreamResponseOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + StreamResponse.class.getName()); + } + // Use StreamResponse.newBuilder() to construct. + private StreamResponse(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private StreamResponse() { + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StreamResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StreamResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.StreamResponse.class, io.a2a.grpc.StreamResponse.Builder.class); + } + + private int payloadCase_ = 0; + @SuppressWarnings("serial") + private java.lang.Object payload_; + public enum PayloadCase + implements com.google.protobuf.Internal.EnumLite, + com.google.protobuf.AbstractMessage.InternalOneOfEnum { + TASK(1), + MSG(2), + STATUS_UPDATE(3), + ARTIFACT_UPDATE(4), + PAYLOAD_NOT_SET(0); + private final int value; + private PayloadCase(int value) { + this.value = value; + } + /** + * @param value The number of the enum to look for. + * @return The enum associated with the given number. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static PayloadCase valueOf(int value) { + return forNumber(value); + } + + public static PayloadCase forNumber(int value) { + switch (value) { + case 1: return TASK; + case 2: return MSG; + case 3: return STATUS_UPDATE; + case 4: return ARTIFACT_UPDATE; + case 0: return PAYLOAD_NOT_SET; + default: return null; + } + } + public int getNumber() { + return this.value; + } + }; + + public PayloadCase + getPayloadCase() { + return PayloadCase.forNumber( + payloadCase_); + } + + public static final int TASK_FIELD_NUMBER = 1; + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return Whether the task field is set. + */ + @java.lang.Override + public boolean hasTask() { + return payloadCase_ == 1; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return The task. + */ + @java.lang.Override + public io.a2a.grpc.Task getTask() { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + + public static final int MSG_FIELD_NUMBER = 2; + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return Whether the msg field is set. + */ + @java.lang.Override + public boolean hasMsg() { + return payloadCase_ == 2; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return The msg. + */ + @java.lang.Override + public io.a2a.grpc.Message getMsg() { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + + public static final int STATUS_UPDATE_FIELD_NUMBER = 3; + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * @return Whether the statusUpdate field is set. + */ + @java.lang.Override + public boolean hasStatusUpdate() { + return payloadCase_ == 3; + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * @return The statusUpdate. + */ + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate() { + if (payloadCase_ == 3) { + return (io.a2a.grpc.TaskStatusUpdateEvent) payload_; + } + return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { + if (payloadCase_ == 3) { + return (io.a2a.grpc.TaskStatusUpdateEvent) payload_; + } + return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } + + public static final int ARTIFACT_UPDATE_FIELD_NUMBER = 4; + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * @return Whether the artifactUpdate field is set. + */ + @java.lang.Override + public boolean hasArtifactUpdate() { + return payloadCase_ == 4; + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * @return The artifactUpdate. + */ + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate() { + if (payloadCase_ == 4) { + return (io.a2a.grpc.TaskArtifactUpdateEvent) payload_; + } + return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder() { + if (payloadCase_ == 4) { + return (io.a2a.grpc.TaskArtifactUpdateEvent) payload_; + } + return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (payloadCase_ == 1) { + output.writeMessage(1, (io.a2a.grpc.Task) payload_); + } + if (payloadCase_ == 2) { + output.writeMessage(2, (io.a2a.grpc.Message) payload_); + } + if (payloadCase_ == 3) { + output.writeMessage(3, (io.a2a.grpc.TaskStatusUpdateEvent) payload_); + } + if (payloadCase_ == 4) { + output.writeMessage(4, (io.a2a.grpc.TaskArtifactUpdateEvent) payload_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (payloadCase_ == 1) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, (io.a2a.grpc.Task) payload_); + } + if (payloadCase_ == 2) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, (io.a2a.grpc.Message) payload_); + } + if (payloadCase_ == 3) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, (io.a2a.grpc.TaskStatusUpdateEvent) payload_); + } + if (payloadCase_ == 4) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, (io.a2a.grpc.TaskArtifactUpdateEvent) payload_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.StreamResponse)) { + return super.equals(obj); + } + io.a2a.grpc.StreamResponse other = (io.a2a.grpc.StreamResponse) obj; + + if (!getPayloadCase().equals(other.getPayloadCase())) return false; + switch (payloadCase_) { + case 1: + if (!getTask() + .equals(other.getTask())) return false; + break; + case 2: + if (!getMsg() + .equals(other.getMsg())) return false; + break; + case 3: + if (!getStatusUpdate() + .equals(other.getStatusUpdate())) return false; + break; + case 4: + if (!getArtifactUpdate() + .equals(other.getArtifactUpdate())) return false; + break; + case 0: + default: + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + switch (payloadCase_) { + case 1: + hash = (37 * hash) + TASK_FIELD_NUMBER; + hash = (53 * hash) + getTask().hashCode(); + break; + case 2: + hash = (37 * hash) + MSG_FIELD_NUMBER; + hash = (53 * hash) + getMsg().hashCode(); + break; + case 3: + hash = (37 * hash) + STATUS_UPDATE_FIELD_NUMBER; + hash = (53 * hash) + getStatusUpdate().hashCode(); + break; + case 4: + hash = (37 * hash) + ARTIFACT_UPDATE_FIELD_NUMBER; + hash = (53 * hash) + getArtifactUpdate().hashCode(); + break; + case 0: + default: + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.StreamResponse parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.StreamResponse parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.StreamResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.StreamResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.StreamResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.StreamResponse parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.StreamResponse parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.StreamResponse parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.StreamResponse parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.StreamResponse parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.StreamResponse parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.StreamResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.StreamResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * The stream response for a message. The stream should be one of the following
+   * sequences:
+   * If the response is a message, the stream should contain one, and only one,
+   * message and then close
+   * If the response is a task lifecycle, the first response should be a Task
+   * object followed by zero or more TaskStatusUpdateEvents and
+   * TaskArtifactUpdateEvents. The stream should complete when the Task
+   * if in an interrupted or terminal state. A stream that ends before these
+   * conditions are met are
+   * 
+ * + * Protobuf type {@code a2a.v1.StreamResponse} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.StreamResponse) + io.a2a.grpc.StreamResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StreamResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StreamResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.StreamResponse.class, io.a2a.grpc.StreamResponse.Builder.class); + } + + // Construct using io.a2a.grpc.StreamResponse.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (taskBuilder_ != null) { + taskBuilder_.clear(); + } + if (msgBuilder_ != null) { + msgBuilder_.clear(); + } + if (statusUpdateBuilder_ != null) { + statusUpdateBuilder_.clear(); + } + if (artifactUpdateBuilder_ != null) { + artifactUpdateBuilder_.clear(); + } + payloadCase_ = 0; + payload_ = null; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StreamResponse_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.StreamResponse getDefaultInstanceForType() { + return io.a2a.grpc.StreamResponse.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.StreamResponse build() { + io.a2a.grpc.StreamResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.StreamResponse buildPartial() { + io.a2a.grpc.StreamResponse result = new io.a2a.grpc.StreamResponse(this); + if (bitField0_ != 0) { buildPartial0(result); } + buildPartialOneofs(result); + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.StreamResponse result) { + int from_bitField0_ = bitField0_; + } + + private void buildPartialOneofs(io.a2a.grpc.StreamResponse result) { + result.payloadCase_ = payloadCase_; + result.payload_ = this.payload_; + if (payloadCase_ == 1 && + taskBuilder_ != null) { + result.payload_ = taskBuilder_.build(); + } + if (payloadCase_ == 2 && + msgBuilder_ != null) { + result.payload_ = msgBuilder_.build(); + } + if (payloadCase_ == 3 && + statusUpdateBuilder_ != null) { + result.payload_ = statusUpdateBuilder_.build(); + } + if (payloadCase_ == 4 && + artifactUpdateBuilder_ != null) { + result.payload_ = artifactUpdateBuilder_.build(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.StreamResponse) { + return mergeFrom((io.a2a.grpc.StreamResponse)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.StreamResponse other) { + if (other == io.a2a.grpc.StreamResponse.getDefaultInstance()) return this; + switch (other.getPayloadCase()) { + case TASK: { + mergeTask(other.getTask()); + break; + } + case MSG: { + mergeMsg(other.getMsg()); + break; + } + case STATUS_UPDATE: { + mergeStatusUpdate(other.getStatusUpdate()); + break; + } + case ARTIFACT_UPDATE: { + mergeArtifactUpdate(other.getArtifactUpdate()); + break; + } + case PAYLOAD_NOT_SET: { + break; + } + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + input.readMessage( + internalGetTaskFieldBuilder().getBuilder(), + extensionRegistry); + payloadCase_ = 1; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetMsgFieldBuilder().getBuilder(), + extensionRegistry); + payloadCase_ = 2; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetStatusUpdateFieldBuilder().getBuilder(), + extensionRegistry); + payloadCase_ = 3; + break; + } // case 26 + case 34: { + input.readMessage( + internalGetArtifactUpdateFieldBuilder().getBuilder(), + extensionRegistry); + payloadCase_ = 4; + break; + } // case 34 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int payloadCase_ = 0; + private java.lang.Object payload_; + public PayloadCase + getPayloadCase() { + return PayloadCase.forNumber( + payloadCase_); + } + + public Builder clearPayload() { + payloadCase_ = 0; + payload_ = null; + onChanged(); + return this; + } + + private int bitField0_; + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> taskBuilder_; + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return Whether the task field is set. + */ + @java.lang.Override + public boolean hasTask() { + return payloadCase_ == 1; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return The task. + */ + @java.lang.Override + public io.a2a.grpc.Task getTask() { + if (taskBuilder_ == null) { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } else { + if (payloadCase_ == 1) { + return taskBuilder_.getMessage(); + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder setTask(io.a2a.grpc.Task value) { + if (taskBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + onChanged(); + } else { + taskBuilder_.setMessage(value); + } + payloadCase_ = 1; + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder setTask( + io.a2a.grpc.Task.Builder builderForValue) { + if (taskBuilder_ == null) { + payload_ = builderForValue.build(); + onChanged(); + } else { + taskBuilder_.setMessage(builderForValue.build()); + } + payloadCase_ = 1; + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder mergeTask(io.a2a.grpc.Task value) { + if (taskBuilder_ == null) { + if (payloadCase_ == 1 && + payload_ != io.a2a.grpc.Task.getDefaultInstance()) { + payload_ = io.a2a.grpc.Task.newBuilder((io.a2a.grpc.Task) payload_) + .mergeFrom(value).buildPartial(); + } else { + payload_ = value; + } + onChanged(); + } else { + if (payloadCase_ == 1) { + taskBuilder_.mergeFrom(value); + } else { + taskBuilder_.setMessage(value); + } + } + payloadCase_ = 1; + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public Builder clearTask() { + if (taskBuilder_ == null) { + if (payloadCase_ == 1) { + payloadCase_ = 0; + payload_ = null; + onChanged(); + } + } else { + if (payloadCase_ == 1) { + payloadCase_ = 0; + payload_ = null; + } + taskBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + public io.a2a.grpc.Task.Builder getTaskBuilder() { + return internalGetTaskFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { + if ((payloadCase_ == 1) && (taskBuilder_ != null)) { + return taskBuilder_.getMessageOrBuilder(); + } else { + if (payloadCase_ == 1) { + return (io.a2a.grpc.Task) payload_; + } + return io.a2a.grpc.Task.getDefaultInstance(); + } + } + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> + internalGetTaskFieldBuilder() { + if (taskBuilder_ == null) { + if (!(payloadCase_ == 1)) { + payload_ = io.a2a.grpc.Task.getDefaultInstance(); + } + taskBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder>( + (io.a2a.grpc.Task) payload_, + getParentForChildren(), + isClean()); + payload_ = null; + } + payloadCase_ = 1; + onChanged(); + return taskBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> msgBuilder_; + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return Whether the msg field is set. + */ + @java.lang.Override + public boolean hasMsg() { + return payloadCase_ == 2; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return The msg. + */ + @java.lang.Override + public io.a2a.grpc.Message getMsg() { + if (msgBuilder_ == null) { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } else { + if (payloadCase_ == 2) { + return msgBuilder_.getMessage(); + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder setMsg(io.a2a.grpc.Message value) { + if (msgBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + onChanged(); + } else { + msgBuilder_.setMessage(value); + } + payloadCase_ = 2; + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder setMsg( + io.a2a.grpc.Message.Builder builderForValue) { + if (msgBuilder_ == null) { + payload_ = builderForValue.build(); + onChanged(); + } else { + msgBuilder_.setMessage(builderForValue.build()); + } + payloadCase_ = 2; + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder mergeMsg(io.a2a.grpc.Message value) { + if (msgBuilder_ == null) { + if (payloadCase_ == 2 && + payload_ != io.a2a.grpc.Message.getDefaultInstance()) { + payload_ = io.a2a.grpc.Message.newBuilder((io.a2a.grpc.Message) payload_) + .mergeFrom(value).buildPartial(); + } else { + payload_ = value; + } + onChanged(); + } else { + if (payloadCase_ == 2) { + msgBuilder_.mergeFrom(value); + } else { + msgBuilder_.setMessage(value); + } + } + payloadCase_ = 2; + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public Builder clearMsg() { + if (msgBuilder_ == null) { + if (payloadCase_ == 2) { + payloadCase_ = 0; + payload_ = null; + onChanged(); + } + } else { + if (payloadCase_ == 2) { + payloadCase_ = 0; + payload_ = null; + } + msgBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + public io.a2a.grpc.Message.Builder getMsgBuilder() { + return internalGetMsgFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { + if ((payloadCase_ == 2) && (msgBuilder_ != null)) { + return msgBuilder_.getMessageOrBuilder(); + } else { + if (payloadCase_ == 2) { + return (io.a2a.grpc.Message) payload_; + } + return io.a2a.grpc.Message.getDefaultInstance(); + } + } + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> + internalGetMsgFieldBuilder() { + if (msgBuilder_ == null) { + if (!(payloadCase_ == 2)) { + payload_ = io.a2a.grpc.Message.getDefaultInstance(); + } + msgBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( + (io.a2a.grpc.Message) payload_, + getParentForChildren(), + isClean()); + payload_ = null; + } + payloadCase_ = 2; + onChanged(); + return msgBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatusUpdateEvent, io.a2a.grpc.TaskStatusUpdateEvent.Builder, io.a2a.grpc.TaskStatusUpdateEventOrBuilder> statusUpdateBuilder_; + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * @return Whether the statusUpdate field is set. + */ + @java.lang.Override + public boolean hasStatusUpdate() { + return payloadCase_ == 3; + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * @return The statusUpdate. + */ + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate() { + if (statusUpdateBuilder_ == null) { + if (payloadCase_ == 3) { + return (io.a2a.grpc.TaskStatusUpdateEvent) payload_; + } + return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } else { + if (payloadCase_ == 3) { + return statusUpdateBuilder_.getMessage(); + } + return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + public Builder setStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { + if (statusUpdateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + onChanged(); + } else { + statusUpdateBuilder_.setMessage(value); + } + payloadCase_ = 3; + return this; + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + public Builder setStatusUpdate( + io.a2a.grpc.TaskStatusUpdateEvent.Builder builderForValue) { + if (statusUpdateBuilder_ == null) { + payload_ = builderForValue.build(); + onChanged(); + } else { + statusUpdateBuilder_.setMessage(builderForValue.build()); + } + payloadCase_ = 3; + return this; + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + public Builder mergeStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { + if (statusUpdateBuilder_ == null) { + if (payloadCase_ == 3 && + payload_ != io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance()) { + payload_ = io.a2a.grpc.TaskStatusUpdateEvent.newBuilder((io.a2a.grpc.TaskStatusUpdateEvent) payload_) + .mergeFrom(value).buildPartial(); + } else { + payload_ = value; + } + onChanged(); + } else { + if (payloadCase_ == 3) { + statusUpdateBuilder_.mergeFrom(value); + } else { + statusUpdateBuilder_.setMessage(value); + } + } + payloadCase_ = 3; + return this; + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + public Builder clearStatusUpdate() { + if (statusUpdateBuilder_ == null) { + if (payloadCase_ == 3) { + payloadCase_ = 0; + payload_ = null; + onChanged(); + } + } else { + if (payloadCase_ == 3) { + payloadCase_ = 0; + payload_ = null; + } + statusUpdateBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + public io.a2a.grpc.TaskStatusUpdateEvent.Builder getStatusUpdateBuilder() { + return internalGetStatusUpdateFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { + if ((payloadCase_ == 3) && (statusUpdateBuilder_ != null)) { + return statusUpdateBuilder_.getMessageOrBuilder(); + } else { + if (payloadCase_ == 3) { + return (io.a2a.grpc.TaskStatusUpdateEvent) payload_; + } + return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } + } + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatusUpdateEvent, io.a2a.grpc.TaskStatusUpdateEvent.Builder, io.a2a.grpc.TaskStatusUpdateEventOrBuilder> + internalGetStatusUpdateFieldBuilder() { + if (statusUpdateBuilder_ == null) { + if (!(payloadCase_ == 3)) { + payload_ = io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } + statusUpdateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatusUpdateEvent, io.a2a.grpc.TaskStatusUpdateEvent.Builder, io.a2a.grpc.TaskStatusUpdateEventOrBuilder>( + (io.a2a.grpc.TaskStatusUpdateEvent) payload_, + getParentForChildren(), + isClean()); + payload_ = null; + } + payloadCase_ = 3; + onChanged(); + return statusUpdateBuilder_; + } + + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskArtifactUpdateEvent, io.a2a.grpc.TaskArtifactUpdateEvent.Builder, io.a2a.grpc.TaskArtifactUpdateEventOrBuilder> artifactUpdateBuilder_; + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * @return Whether the artifactUpdate field is set. + */ + @java.lang.Override + public boolean hasArtifactUpdate() { + return payloadCase_ == 4; + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * @return The artifactUpdate. + */ + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate() { + if (artifactUpdateBuilder_ == null) { + if (payloadCase_ == 4) { + return (io.a2a.grpc.TaskArtifactUpdateEvent) payload_; + } + return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } else { + if (payloadCase_ == 4) { + return artifactUpdateBuilder_.getMessage(); + } + return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + public Builder setArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { + if (artifactUpdateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + onChanged(); + } else { + artifactUpdateBuilder_.setMessage(value); + } + payloadCase_ = 4; + return this; + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + public Builder setArtifactUpdate( + io.a2a.grpc.TaskArtifactUpdateEvent.Builder builderForValue) { + if (artifactUpdateBuilder_ == null) { + payload_ = builderForValue.build(); + onChanged(); + } else { + artifactUpdateBuilder_.setMessage(builderForValue.build()); + } + payloadCase_ = 4; + return this; + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + public Builder mergeArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { + if (artifactUpdateBuilder_ == null) { + if (payloadCase_ == 4 && + payload_ != io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance()) { + payload_ = io.a2a.grpc.TaskArtifactUpdateEvent.newBuilder((io.a2a.grpc.TaskArtifactUpdateEvent) payload_) + .mergeFrom(value).buildPartial(); + } else { + payload_ = value; + } + onChanged(); + } else { + if (payloadCase_ == 4) { + artifactUpdateBuilder_.mergeFrom(value); + } else { + artifactUpdateBuilder_.setMessage(value); + } + } + payloadCase_ = 4; + return this; + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + public Builder clearArtifactUpdate() { + if (artifactUpdateBuilder_ == null) { + if (payloadCase_ == 4) { + payloadCase_ = 0; + payload_ = null; + onChanged(); + } + } else { + if (payloadCase_ == 4) { + payloadCase_ = 0; + payload_ = null; + } + artifactUpdateBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + public io.a2a.grpc.TaskArtifactUpdateEvent.Builder getArtifactUpdateBuilder() { + return internalGetArtifactUpdateFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder() { + if ((payloadCase_ == 4) && (artifactUpdateBuilder_ != null)) { + return artifactUpdateBuilder_.getMessageOrBuilder(); + } else { + if (payloadCase_ == 4) { + return (io.a2a.grpc.TaskArtifactUpdateEvent) payload_; + } + return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } + } + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskArtifactUpdateEvent, io.a2a.grpc.TaskArtifactUpdateEvent.Builder, io.a2a.grpc.TaskArtifactUpdateEventOrBuilder> + internalGetArtifactUpdateFieldBuilder() { + if (artifactUpdateBuilder_ == null) { + if (!(payloadCase_ == 4)) { + payload_ = io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } + artifactUpdateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskArtifactUpdateEvent, io.a2a.grpc.TaskArtifactUpdateEvent.Builder, io.a2a.grpc.TaskArtifactUpdateEventOrBuilder>( + (io.a2a.grpc.TaskArtifactUpdateEvent) payload_, + getParentForChildren(), + isClean()); + payload_ = null; + } + payloadCase_ = 4; + onChanged(); + return artifactUpdateBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.StreamResponse) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.StreamResponse) + private static final io.a2a.grpc.StreamResponse DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.StreamResponse(); + } + + public static io.a2a.grpc.StreamResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public StreamResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.StreamResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java new file mode 100644 index 000000000..3a17929cd --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java @@ -0,0 +1,74 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface StreamResponseOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.StreamResponse) + com.google.protobuf.MessageOrBuilder { + + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return Whether the task field is set. + */ + boolean hasTask(); + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + * @return The task. + */ + io.a2a.grpc.Task getTask(); + /** + * .a2a.v1.Task task = 1 [json_name = "task"]; + */ + io.a2a.grpc.TaskOrBuilder getTaskOrBuilder(); + + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return Whether the msg field is set. + */ + boolean hasMsg(); + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + * @return The msg. + */ + io.a2a.grpc.Message getMsg(); + /** + * .a2a.v1.Message msg = 2 [json_name = "message"]; + */ + io.a2a.grpc.MessageOrBuilder getMsgOrBuilder(); + + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * @return Whether the statusUpdate field is set. + */ + boolean hasStatusUpdate(); + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * @return The statusUpdate. + */ + io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate(); + /** + * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + */ + io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder(); + + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * @return Whether the artifactUpdate field is set. + */ + boolean hasArtifactUpdate(); + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * @return The artifactUpdate. + */ + io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate(); + /** + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + */ + io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder(); + + io.a2a.grpc.StreamResponse.PayloadCase getPayloadCase(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/StringList.java b/grpc/src/main/java/io/a2a/grpc/StringList.java new file mode 100644 index 000000000..4ee06422d --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/StringList.java @@ -0,0 +1,563 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+ * 
+ * + * Protobuf type {@code a2a.v1.StringList} + */ +@com.google.protobuf.Generated +public final class StringList extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.StringList) + StringListOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + StringList.class.getName()); + } + // Use StringList.newBuilder() to construct. + private StringList(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private StringList() { + list_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StringList_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StringList_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.StringList.class, io.a2a.grpc.StringList.Builder.class); + } + + public static final int LIST_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList list_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + * repeated string list = 1 [json_name = "list"]; + * @return A list containing the list. + */ + public com.google.protobuf.ProtocolStringList + getListList() { + return list_; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @return The count of list. + */ + public int getListCount() { + return list_.size(); + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index of the element to return. + * @return The list at the given index. + */ + public java.lang.String getList(int index) { + return list_.get(index); + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index of the value to return. + * @return The bytes of the list at the given index. + */ + public com.google.protobuf.ByteString + getListBytes(int index) { + return list_.getByteString(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < list_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, list_.getRaw(i)); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < list_.size(); i++) { + dataSize += computeStringSizeNoTag(list_.getRaw(i)); + } + size += dataSize; + size += 1 * getListList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.StringList)) { + return super.equals(obj); + } + io.a2a.grpc.StringList other = (io.a2a.grpc.StringList) obj; + + if (!getListList() + .equals(other.getListList())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getListCount() > 0) { + hash = (37 * hash) + LIST_FIELD_NUMBER; + hash = (53 * hash) + getListList().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.StringList parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.StringList parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.StringList parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.StringList parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.StringList parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.StringList parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.StringList parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.StringList parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.StringList parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.StringList parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.StringList parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.StringList parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.StringList prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * 
+ * + * Protobuf type {@code a2a.v1.StringList} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.StringList) + io.a2a.grpc.StringListOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StringList_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StringList_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.StringList.class, io.a2a.grpc.StringList.Builder.class); + } + + // Construct using io.a2a.grpc.StringList.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + list_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_StringList_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.StringList getDefaultInstanceForType() { + return io.a2a.grpc.StringList.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.StringList build() { + io.a2a.grpc.StringList result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.StringList buildPartial() { + io.a2a.grpc.StringList result = new io.a2a.grpc.StringList(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.StringList result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + list_.makeImmutable(); + result.list_ = list_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.StringList) { + return mergeFrom((io.a2a.grpc.StringList)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.StringList other) { + if (other == io.a2a.grpc.StringList.getDefaultInstance()) return this; + if (!other.list_.isEmpty()) { + if (list_.isEmpty()) { + list_ = other.list_; + bitField0_ |= 0x00000001; + } else { + ensureListIsMutable(); + list_.addAll(other.list_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + ensureListIsMutable(); + list_.add(s); + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private com.google.protobuf.LazyStringArrayList list_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureListIsMutable() { + if (!list_.isModifiable()) { + list_ = new com.google.protobuf.LazyStringArrayList(list_); + } + bitField0_ |= 0x00000001; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @return A list containing the list. + */ + public com.google.protobuf.ProtocolStringList + getListList() { + list_.makeImmutable(); + return list_; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @return The count of list. + */ + public int getListCount() { + return list_.size(); + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index of the element to return. + * @return The list at the given index. + */ + public java.lang.String getList(int index) { + return list_.get(index); + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index of the value to return. + * @return The bytes of the list at the given index. + */ + public com.google.protobuf.ByteString + getListBytes(int index) { + return list_.getByteString(index); + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index to set the value at. + * @param value The list to set. + * @return This builder for chaining. + */ + public Builder setList( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureListIsMutable(); + list_.set(index, value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param value The list to add. + * @return This builder for chaining. + */ + public Builder addList( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureListIsMutable(); + list_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param values The list to add. + * @return This builder for chaining. + */ + public Builder addAllList( + java.lang.Iterable values) { + ensureListIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, list_); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @return This builder for chaining. + */ + public Builder clearList() { + list_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001);; + onChanged(); + return this; + } + /** + * repeated string list = 1 [json_name = "list"]; + * @param value The bytes of the list to add. + * @return This builder for chaining. + */ + public Builder addListBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureListIsMutable(); + list_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.StringList) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.StringList) + private static final io.a2a.grpc.StringList DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.StringList(); + } + + public static io.a2a.grpc.StringList getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public StringList parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.StringList getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java new file mode 100644 index 000000000..fbc8bf9db --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java @@ -0,0 +1,37 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface StringListOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.StringList) + com.google.protobuf.MessageOrBuilder { + + /** + * repeated string list = 1 [json_name = "list"]; + * @return A list containing the list. + */ + java.util.List + getListList(); + /** + * repeated string list = 1 [json_name = "list"]; + * @return The count of list. + */ + int getListCount(); + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index of the element to return. + * @return The list at the given index. + */ + java.lang.String getList(int index); + /** + * repeated string list = 1 [json_name = "list"]; + * @param index The index of the value to return. + * @return The bytes of the list at the given index. + */ + com.google.protobuf.ByteString + getListBytes(int index); +} diff --git a/grpc/src/main/java/io/a2a/grpc/Task.java b/grpc/src/main/java/io/a2a/grpc/Task.java new file mode 100644 index 000000000..74605278b --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/Task.java @@ -0,0 +1,2114 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * Task is the core unit of action for A2A. It has a current status
+ * and when results are created for the task they are stored in the
+ * artifact. If there are multiple turns for a task, these are stored in
+ * history.
+ * 
+ * + * Protobuf type {@code a2a.v1.Task} + */ +@com.google.protobuf.Generated +public final class Task extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.Task) + TaskOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + Task.class.getName()); + } + // Use Task.newBuilder() to construct. + private Task(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Task() { + id_ = ""; + contextId_ = ""; + artifacts_ = java.util.Collections.emptyList(); + history_ = java.util.Collections.emptyList(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Task_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Task_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Task.class, io.a2a.grpc.Task.Builder.class); + } + + private int bitField0_; + public static final int ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + *
+   * Unique identifier for a task, created by the A2A server.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + *
+   * Unique identifier for a task, created by the A2A server.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONTEXT_ID_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object contextId_ = ""; + /** + *
+   * Unique identifier for the contextual collection of interactions (tasks
+   * and messages). Created by the A2A server.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + @java.lang.Override + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } + } + /** + *
+   * Unique identifier for the contextual collection of interactions (tasks
+   * and messages). Created by the A2A server.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int STATUS_FIELD_NUMBER = 3; + private io.a2a.grpc.TaskStatus status_; + /** + *
+   * The current status of a Task, including state and a message.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return Whether the status field is set. + */ + @java.lang.Override + public boolean hasStatus() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The current status of a Task, including state and a message.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return The status. + */ + @java.lang.Override + public io.a2a.grpc.TaskStatus getStatus() { + return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } + /** + *
+   * The current status of a Task, including state and a message.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { + return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } + + public static final int ARTIFACTS_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private java.util.List artifacts_; + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + @java.lang.Override + public java.util.List getArtifactsList() { + return artifacts_; + } + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + @java.lang.Override + public java.util.List + getArtifactsOrBuilderList() { + return artifacts_; + } + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + @java.lang.Override + public int getArtifactsCount() { + return artifacts_.size(); + } + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + @java.lang.Override + public io.a2a.grpc.Artifact getArtifacts(int index) { + return artifacts_.get(index); + } + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + @java.lang.Override + public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( + int index) { + return artifacts_.get(index); + } + + public static final int HISTORY_FIELD_NUMBER = 5; + @SuppressWarnings("serial") + private java.util.List history_; + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + @java.lang.Override + public java.util.List getHistoryList() { + return history_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + @java.lang.Override + public java.util.List + getHistoryOrBuilderList() { + return history_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + @java.lang.Override + public int getHistoryCount() { + return history_.size(); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + @java.lang.Override + public io.a2a.grpc.Message getHistory(int index) { + return history_.get(index); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( + int index) { + return history_.get(index); + } + + public static final int METADATA_FIELD_NUMBER = 6; + private com.google.protobuf.Struct metadata_; + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * A key/value object to store custom metadata about a task.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * A key/value object to store custom metadata about a task.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * A key/value object to store custom metadata about a task.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, contextId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(3, getStatus()); + } + for (int i = 0; i < artifacts_.size(); i++) { + output.writeMessage(4, artifacts_.get(i)); + } + for (int i = 0; i < history_.size(); i++) { + output.writeMessage(5, history_.get(i)); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeMessage(6, getMetadata()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, contextId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getStatus()); + } + for (int i = 0; i < artifacts_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, artifacts_.get(i)); + } + for (int i = 0; i < history_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, history_.get(i)); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, getMetadata()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.Task)) { + return super.equals(obj); + } + io.a2a.grpc.Task other = (io.a2a.grpc.Task) obj; + + if (!getId() + .equals(other.getId())) return false; + if (!getContextId() + .equals(other.getContextId())) return false; + if (hasStatus() != other.hasStatus()) return false; + if (hasStatus()) { + if (!getStatus() + .equals(other.getStatus())) return false; + } + if (!getArtifactsList() + .equals(other.getArtifactsList())) return false; + if (!getHistoryList() + .equals(other.getHistoryList())) return false; + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); + hash = (37 * hash) + CONTEXT_ID_FIELD_NUMBER; + hash = (53 * hash) + getContextId().hashCode(); + if (hasStatus()) { + hash = (37 * hash) + STATUS_FIELD_NUMBER; + hash = (53 * hash) + getStatus().hashCode(); + } + if (getArtifactsCount() > 0) { + hash = (37 * hash) + ARTIFACTS_FIELD_NUMBER; + hash = (53 * hash) + getArtifactsList().hashCode(); + } + if (getHistoryCount() > 0) { + hash = (37 * hash) + HISTORY_FIELD_NUMBER; + hash = (53 * hash) + getHistoryList().hashCode(); + } + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.Task parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Task parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Task parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Task parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Task parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.Task parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.Task parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Task parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.Task parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.Task parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.Task parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.Task parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.Task prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Task is the core unit of action for A2A. It has a current status
+   * and when results are created for the task they are stored in the
+   * artifact. If there are multiple turns for a task, these are stored in
+   * history.
+   * 
+ * + * Protobuf type {@code a2a.v1.Task} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.Task) + io.a2a.grpc.TaskOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Task_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Task_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.Task.class, io.a2a.grpc.Task.Builder.class); + } + + // Construct using io.a2a.grpc.Task.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetStatusFieldBuilder(); + internalGetArtifactsFieldBuilder(); + internalGetHistoryFieldBuilder(); + internalGetMetadataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = ""; + contextId_ = ""; + status_ = null; + if (statusBuilder_ != null) { + statusBuilder_.dispose(); + statusBuilder_ = null; + } + if (artifactsBuilder_ == null) { + artifacts_ = java.util.Collections.emptyList(); + } else { + artifacts_ = null; + artifactsBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); + if (historyBuilder_ == null) { + history_ = java.util.Collections.emptyList(); + } else { + history_ = null; + historyBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_Task_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.Task getDefaultInstanceForType() { + return io.a2a.grpc.Task.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.Task build() { + io.a2a.grpc.Task result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.Task buildPartial() { + io.a2a.grpc.Task result = new io.a2a.grpc.Task(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.Task result) { + if (artifactsBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0)) { + artifacts_ = java.util.Collections.unmodifiableList(artifacts_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.artifacts_ = artifacts_; + } else { + result.artifacts_ = artifactsBuilder_.build(); + } + if (historyBuilder_ == null) { + if (((bitField0_ & 0x00000010) != 0)) { + history_ = java.util.Collections.unmodifiableList(history_); + bitField0_ = (bitField0_ & ~0x00000010); + } + result.history_ = history_; + } else { + result.history_ = historyBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.Task result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.contextId_ = contextId_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.status_ = statusBuilder_ == null + ? status_ + : statusBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000002; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.Task) { + return mergeFrom((io.a2a.grpc.Task)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.Task other) { + if (other == io.a2a.grpc.Task.getDefaultInstance()) return this; + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getContextId().isEmpty()) { + contextId_ = other.contextId_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasStatus()) { + mergeStatus(other.getStatus()); + } + if (artifactsBuilder_ == null) { + if (!other.artifacts_.isEmpty()) { + if (artifacts_.isEmpty()) { + artifacts_ = other.artifacts_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureArtifactsIsMutable(); + artifacts_.addAll(other.artifacts_); + } + onChanged(); + } + } else { + if (!other.artifacts_.isEmpty()) { + if (artifactsBuilder_.isEmpty()) { + artifactsBuilder_.dispose(); + artifactsBuilder_ = null; + artifacts_ = other.artifacts_; + bitField0_ = (bitField0_ & ~0x00000008); + artifactsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetArtifactsFieldBuilder() : null; + } else { + artifactsBuilder_.addAllMessages(other.artifacts_); + } + } + } + if (historyBuilder_ == null) { + if (!other.history_.isEmpty()) { + if (history_.isEmpty()) { + history_ = other.history_; + bitField0_ = (bitField0_ & ~0x00000010); + } else { + ensureHistoryIsMutable(); + history_.addAll(other.history_); + } + onChanged(); + } + } else { + if (!other.history_.isEmpty()) { + if (historyBuilder_.isEmpty()) { + historyBuilder_.dispose(); + historyBuilder_ = null; + history_ = other.history_; + bitField0_ = (bitField0_ & ~0x00000010); + historyBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetHistoryFieldBuilder() : null; + } else { + historyBuilder_.addAllMessages(other.history_); + } + } + } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + contextId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetStatusFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 34: { + io.a2a.grpc.Artifact m = + input.readMessage( + io.a2a.grpc.Artifact.parser(), + extensionRegistry); + if (artifactsBuilder_ == null) { + ensureArtifactsIsMutable(); + artifacts_.add(m); + } else { + artifactsBuilder_.addMessage(m); + } + break; + } // case 34 + case 42: { + io.a2a.grpc.Message m = + input.readMessage( + io.a2a.grpc.Message.parser(), + extensionRegistry); + if (historyBuilder_ == null) { + ensureHistoryIsMutable(); + history_.add(m); + } else { + historyBuilder_.addMessage(m); + } + break; + } // case 42 + case 50: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000020; + break; + } // case 50 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object id_ = ""; + /** + *
+     * Unique identifier for a task, created by the A2A server.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Unique identifier for a task, created by the A2A server.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Unique identifier for a task, created by the A2A server.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Unique identifier for a task, created by the A2A server.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Unique identifier for a task, created by the A2A server.
+     * 
+ * + * string id = 1 [json_name = "id"]; + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object contextId_ = ""; + /** + *
+     * Unique identifier for the contextual collection of interactions (tasks
+     * and messages). Created by the A2A server.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Unique identifier for the contextual collection of interactions (tasks
+     * and messages). Created by the A2A server.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Unique identifier for the contextual collection of interactions (tasks
+     * and messages). Created by the A2A server.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The contextId to set. + * @return This builder for chaining. + */ + public Builder setContextId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Unique identifier for the contextual collection of interactions (tasks
+     * and messages). Created by the A2A server.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return This builder for chaining. + */ + public Builder clearContextId() { + contextId_ = getDefaultInstance().getContextId(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * Unique identifier for the contextual collection of interactions (tasks
+     * and messages). Created by the A2A server.
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The bytes for contextId to set. + * @return This builder for chaining. + */ + public Builder setContextIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private io.a2a.grpc.TaskStatus status_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> statusBuilder_; + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return Whether the status field is set. + */ + public boolean hasStatus() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return The status. + */ + public io.a2a.grpc.TaskStatus getStatus() { + if (statusBuilder_ == null) { + return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } else { + return statusBuilder_.getMessage(); + } + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder setStatus(io.a2a.grpc.TaskStatus value) { + if (statusBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + status_ = value; + } else { + statusBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder setStatus( + io.a2a.grpc.TaskStatus.Builder builderForValue) { + if (statusBuilder_ == null) { + status_ = builderForValue.build(); + } else { + statusBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { + if (statusBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + status_ != null && + status_ != io.a2a.grpc.TaskStatus.getDefaultInstance()) { + getStatusBuilder().mergeFrom(value); + } else { + status_ = value; + } + } else { + statusBuilder_.mergeFrom(value); + } + if (status_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder clearStatus() { + bitField0_ = (bitField0_ & ~0x00000004); + status_ = null; + if (statusBuilder_ != null) { + statusBuilder_.dispose(); + statusBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetStatusFieldBuilder().getBuilder(); + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { + if (statusBuilder_ != null) { + return statusBuilder_.getMessageOrBuilder(); + } else { + return status_ == null ? + io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } + } + /** + *
+     * The current status of a Task, including state and a message.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> + internalGetStatusFieldBuilder() { + if (statusBuilder_ == null) { + statusBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder>( + getStatus(), + getParentForChildren(), + isClean()); + status_ = null; + } + return statusBuilder_; + } + + private java.util.List artifacts_ = + java.util.Collections.emptyList(); + private void ensureArtifactsIsMutable() { + if (!((bitField0_ & 0x00000008) != 0)) { + artifacts_ = new java.util.ArrayList(artifacts_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> artifactsBuilder_; + + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public java.util.List getArtifactsList() { + if (artifactsBuilder_ == null) { + return java.util.Collections.unmodifiableList(artifacts_); + } else { + return artifactsBuilder_.getMessageList(); + } + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public int getArtifactsCount() { + if (artifactsBuilder_ == null) { + return artifacts_.size(); + } else { + return artifactsBuilder_.getCount(); + } + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public io.a2a.grpc.Artifact getArtifacts(int index) { + if (artifactsBuilder_ == null) { + return artifacts_.get(index); + } else { + return artifactsBuilder_.getMessage(index); + } + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder setArtifacts( + int index, io.a2a.grpc.Artifact value) { + if (artifactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureArtifactsIsMutable(); + artifacts_.set(index, value); + onChanged(); + } else { + artifactsBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder setArtifacts( + int index, io.a2a.grpc.Artifact.Builder builderForValue) { + if (artifactsBuilder_ == null) { + ensureArtifactsIsMutable(); + artifacts_.set(index, builderForValue.build()); + onChanged(); + } else { + artifactsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder addArtifacts(io.a2a.grpc.Artifact value) { + if (artifactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureArtifactsIsMutable(); + artifacts_.add(value); + onChanged(); + } else { + artifactsBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder addArtifacts( + int index, io.a2a.grpc.Artifact value) { + if (artifactsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureArtifactsIsMutable(); + artifacts_.add(index, value); + onChanged(); + } else { + artifactsBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder addArtifacts( + io.a2a.grpc.Artifact.Builder builderForValue) { + if (artifactsBuilder_ == null) { + ensureArtifactsIsMutable(); + artifacts_.add(builderForValue.build()); + onChanged(); + } else { + artifactsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder addArtifacts( + int index, io.a2a.grpc.Artifact.Builder builderForValue) { + if (artifactsBuilder_ == null) { + ensureArtifactsIsMutable(); + artifacts_.add(index, builderForValue.build()); + onChanged(); + } else { + artifactsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder addAllArtifacts( + java.lang.Iterable values) { + if (artifactsBuilder_ == null) { + ensureArtifactsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, artifacts_); + onChanged(); + } else { + artifactsBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder clearArtifacts() { + if (artifactsBuilder_ == null) { + artifacts_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + artifactsBuilder_.clear(); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public Builder removeArtifacts(int index) { + if (artifactsBuilder_ == null) { + ensureArtifactsIsMutable(); + artifacts_.remove(index); + onChanged(); + } else { + artifactsBuilder_.remove(index); + } + return this; + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public io.a2a.grpc.Artifact.Builder getArtifactsBuilder( + int index) { + return internalGetArtifactsFieldBuilder().getBuilder(index); + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( + int index) { + if (artifactsBuilder_ == null) { + return artifacts_.get(index); } else { + return artifactsBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public java.util.List + getArtifactsOrBuilderList() { + if (artifactsBuilder_ != null) { + return artifactsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(artifacts_); + } + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public io.a2a.grpc.Artifact.Builder addArtifactsBuilder() { + return internalGetArtifactsFieldBuilder().addBuilder( + io.a2a.grpc.Artifact.getDefaultInstance()); + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public io.a2a.grpc.Artifact.Builder addArtifactsBuilder( + int index) { + return internalGetArtifactsFieldBuilder().addBuilder( + index, io.a2a.grpc.Artifact.getDefaultInstance()); + } + /** + *
+     * A set of output artifacts for a Task.
+     * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + public java.util.List + getArtifactsBuilderList() { + return internalGetArtifactsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> + internalGetArtifactsFieldBuilder() { + if (artifactsBuilder_ == null) { + artifactsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder>( + artifacts_, + ((bitField0_ & 0x00000008) != 0), + getParentForChildren(), + isClean()); + artifacts_ = null; + } + return artifactsBuilder_; + } + + private java.util.List history_ = + java.util.Collections.emptyList(); + private void ensureHistoryIsMutable() { + if (!((bitField0_ & 0x00000010) != 0)) { + history_ = new java.util.ArrayList(history_); + bitField0_ |= 0x00000010; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> historyBuilder_; + + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public java.util.List getHistoryList() { + if (historyBuilder_ == null) { + return java.util.Collections.unmodifiableList(history_); + } else { + return historyBuilder_.getMessageList(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public int getHistoryCount() { + if (historyBuilder_ == null) { + return history_.size(); + } else { + return historyBuilder_.getCount(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public io.a2a.grpc.Message getHistory(int index) { + if (historyBuilder_ == null) { + return history_.get(index); + } else { + return historyBuilder_.getMessage(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder setHistory( + int index, io.a2a.grpc.Message value) { + if (historyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureHistoryIsMutable(); + history_.set(index, value); + onChanged(); + } else { + historyBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder setHistory( + int index, io.a2a.grpc.Message.Builder builderForValue) { + if (historyBuilder_ == null) { + ensureHistoryIsMutable(); + history_.set(index, builderForValue.build()); + onChanged(); + } else { + historyBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder addHistory(io.a2a.grpc.Message value) { + if (historyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureHistoryIsMutable(); + history_.add(value); + onChanged(); + } else { + historyBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder addHistory( + int index, io.a2a.grpc.Message value) { + if (historyBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureHistoryIsMutable(); + history_.add(index, value); + onChanged(); + } else { + historyBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder addHistory( + io.a2a.grpc.Message.Builder builderForValue) { + if (historyBuilder_ == null) { + ensureHistoryIsMutable(); + history_.add(builderForValue.build()); + onChanged(); + } else { + historyBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder addHistory( + int index, io.a2a.grpc.Message.Builder builderForValue) { + if (historyBuilder_ == null) { + ensureHistoryIsMutable(); + history_.add(index, builderForValue.build()); + onChanged(); + } else { + historyBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder addAllHistory( + java.lang.Iterable values) { + if (historyBuilder_ == null) { + ensureHistoryIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, history_); + onChanged(); + } else { + historyBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder clearHistory() { + if (historyBuilder_ == null) { + history_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000010); + onChanged(); + } else { + historyBuilder_.clear(); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public Builder removeHistory(int index) { + if (historyBuilder_ == null) { + ensureHistoryIsMutable(); + history_.remove(index); + onChanged(); + } else { + historyBuilder_.remove(index); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public io.a2a.grpc.Message.Builder getHistoryBuilder( + int index) { + return internalGetHistoryFieldBuilder().getBuilder(index); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( + int index) { + if (historyBuilder_ == null) { + return history_.get(index); } else { + return historyBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public java.util.List + getHistoryOrBuilderList() { + if (historyBuilder_ != null) { + return historyBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(history_); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public io.a2a.grpc.Message.Builder addHistoryBuilder() { + return internalGetHistoryFieldBuilder().addBuilder( + io.a2a.grpc.Message.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public io.a2a.grpc.Message.Builder addHistoryBuilder( + int index) { + return internalGetHistoryFieldBuilder().addBuilder( + index, io.a2a.grpc.Message.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * The history of interactions from a task.
+     * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + public java.util.List + getHistoryBuilderList() { + return internalGetHistoryFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> + internalGetHistoryFieldBuilder() { + if (historyBuilder_ == null) { + historyBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( + history_, + ((bitField0_ & 0x00000010) != 0), + getParentForChildren(), + isClean()); + history_ = null; + } + return historyBuilder_; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000020) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000020; + onChanged(); + } + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000020); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + *
+     * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+     * A key/value object to store custom metadata about a task.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.Task) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.Task) + private static final io.a2a.grpc.Task DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.Task(); + } + + public static io.a2a.grpc.Task getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public Task parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.Task getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java b/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java new file mode 100644 index 000000000..2019adc0c --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java @@ -0,0 +1,1344 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * TaskArtifactUpdateEvent represents a task delta where an artifact has
+ * been generated.
+ * 
+ * + * Protobuf type {@code a2a.v1.TaskArtifactUpdateEvent} + */ +@com.google.protobuf.Generated +public final class TaskArtifactUpdateEvent extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.TaskArtifactUpdateEvent) + TaskArtifactUpdateEventOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + TaskArtifactUpdateEvent.class.getName()); + } + // Use TaskArtifactUpdateEvent.newBuilder() to construct. + private TaskArtifactUpdateEvent(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private TaskArtifactUpdateEvent() { + taskId_ = ""; + contextId_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskArtifactUpdateEvent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskArtifactUpdateEvent.class, io.a2a.grpc.TaskArtifactUpdateEvent.Builder.class); + } + + private int bitField0_; + public static final int TASK_ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object taskId_ = ""; + /** + *
+   * The id of the task for this artifact
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The taskId. + */ + @java.lang.Override + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } + } + /** + *
+   * The id of the task for this artifact
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONTEXT_ID_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object contextId_ = ""; + /** + *
+   * The id of the context that this task belongs too
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + @java.lang.Override + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } + } + /** + *
+   * The id of the context that this task belongs too
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ARTIFACT_FIELD_NUMBER = 3; + private io.a2a.grpc.Artifact artifact_; + /** + *
+   * The artifact itself
+   * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * @return Whether the artifact field is set. + */ + @java.lang.Override + public boolean hasArtifact() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The artifact itself
+   * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * @return The artifact. + */ + @java.lang.Override + public io.a2a.grpc.Artifact getArtifact() { + return artifact_ == null ? io.a2a.grpc.Artifact.getDefaultInstance() : artifact_; + } + /** + *
+   * The artifact itself
+   * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + @java.lang.Override + public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { + return artifact_ == null ? io.a2a.grpc.Artifact.getDefaultInstance() : artifact_; + } + + public static final int APPEND_FIELD_NUMBER = 4; + private boolean append_ = false; + /** + *
+   * Whether this should be appended to a prior one produced
+   * 
+ * + * bool append = 4 [json_name = "append"]; + * @return The append. + */ + @java.lang.Override + public boolean getAppend() { + return append_; + } + + public static final int LAST_CHUNK_FIELD_NUMBER = 5; + private boolean lastChunk_ = false; + /** + *
+   * Whether this represents the last part of an artifact
+   * 
+ * + * bool last_chunk = 5 [json_name = "lastChunk"]; + * @return The lastChunk. + */ + @java.lang.Override + public boolean getLastChunk() { + return lastChunk_; + } + + public static final int METADATA_FIELD_NUMBER = 6; + private com.google.protobuf.Struct metadata_; + /** + *
+   * Optional metadata associated with the artifact update.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * Optional metadata associated with the artifact update.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * Optional metadata associated with the artifact update.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, taskId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, contextId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(3, getArtifact()); + } + if (append_ != false) { + output.writeBool(4, append_); + } + if (lastChunk_ != false) { + output.writeBool(5, lastChunk_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeMessage(6, getMetadata()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, taskId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, contextId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getArtifact()); + } + if (append_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(4, append_); + } + if (lastChunk_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, lastChunk_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, getMetadata()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.TaskArtifactUpdateEvent)) { + return super.equals(obj); + } + io.a2a.grpc.TaskArtifactUpdateEvent other = (io.a2a.grpc.TaskArtifactUpdateEvent) obj; + + if (!getTaskId() + .equals(other.getTaskId())) return false; + if (!getContextId() + .equals(other.getContextId())) return false; + if (hasArtifact() != other.hasArtifact()) return false; + if (hasArtifact()) { + if (!getArtifact() + .equals(other.getArtifact())) return false; + } + if (getAppend() + != other.getAppend()) return false; + if (getLastChunk() + != other.getLastChunk()) return false; + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); + hash = (37 * hash) + CONTEXT_ID_FIELD_NUMBER; + hash = (53 * hash) + getContextId().hashCode(); + if (hasArtifact()) { + hash = (37 * hash) + ARTIFACT_FIELD_NUMBER; + hash = (53 * hash) + getArtifact().hashCode(); + } + hash = (37 * hash) + APPEND_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getAppend()); + hash = (37 * hash) + LAST_CHUNK_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getLastChunk()); + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.TaskArtifactUpdateEvent parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.TaskArtifactUpdateEvent parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskArtifactUpdateEvent parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.TaskArtifactUpdateEvent prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * TaskArtifactUpdateEvent represents a task delta where an artifact has
+   * been generated.
+   * 
+ * + * Protobuf type {@code a2a.v1.TaskArtifactUpdateEvent} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.TaskArtifactUpdateEvent) + io.a2a.grpc.TaskArtifactUpdateEventOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskArtifactUpdateEvent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskArtifactUpdateEvent.class, io.a2a.grpc.TaskArtifactUpdateEvent.Builder.class); + } + + // Construct using io.a2a.grpc.TaskArtifactUpdateEvent.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetArtifactFieldBuilder(); + internalGetMetadataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + taskId_ = ""; + contextId_ = ""; + artifact_ = null; + if (artifactBuilder_ != null) { + artifactBuilder_.dispose(); + artifactBuilder_ = null; + } + append_ = false; + lastChunk_ = false; + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEvent getDefaultInstanceForType() { + return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEvent build() { + io.a2a.grpc.TaskArtifactUpdateEvent result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEvent buildPartial() { + io.a2a.grpc.TaskArtifactUpdateEvent result = new io.a2a.grpc.TaskArtifactUpdateEvent(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.TaskArtifactUpdateEvent result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.taskId_ = taskId_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.contextId_ = contextId_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.artifact_ = artifactBuilder_ == null + ? artifact_ + : artifactBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.append_ = append_; + } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.lastChunk_ = lastChunk_; + } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000002; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.TaskArtifactUpdateEvent) { + return mergeFrom((io.a2a.grpc.TaskArtifactUpdateEvent)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.TaskArtifactUpdateEvent other) { + if (other == io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance()) return this; + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getContextId().isEmpty()) { + contextId_ = other.contextId_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasArtifact()) { + mergeArtifact(other.getArtifact()); + } + if (other.getAppend() != false) { + setAppend(other.getAppend()); + } + if (other.getLastChunk() != false) { + setLastChunk(other.getLastChunk()); + } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + taskId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + contextId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetArtifactFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 32: { + append_ = input.readBool(); + bitField0_ |= 0x00000008; + break; + } // case 32 + case 40: { + lastChunk_ = input.readBool(); + bitField0_ |= 0x00000010; + break; + } // case 40 + case 50: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000020; + break; + } // case 50 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object taskId_ = ""; + /** + *
+     * The id of the task for this artifact
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The taskId. + */ + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The id of the task for this artifact
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The id of the task for this artifact
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @param value The taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + taskId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The id of the task for this artifact
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return This builder for chaining. + */ + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The id of the task for this artifact
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @param value The bytes for taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + taskId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object contextId_ = ""; + /** + *
+     * The id of the context that this task belongs too
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The id of the context that this task belongs too
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The id of the context that this task belongs too
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The contextId to set. + * @return This builder for chaining. + */ + public Builder setContextId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The id of the context that this task belongs too
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return This builder for chaining. + */ + public Builder clearContextId() { + contextId_ = getDefaultInstance().getContextId(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The id of the context that this task belongs too
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The bytes for contextId to set. + * @return This builder for chaining. + */ + public Builder setContextIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private io.a2a.grpc.Artifact artifact_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> artifactBuilder_; + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * @return Whether the artifact field is set. + */ + public boolean hasArtifact() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * @return The artifact. + */ + public io.a2a.grpc.Artifact getArtifact() { + if (artifactBuilder_ == null) { + return artifact_ == null ? io.a2a.grpc.Artifact.getDefaultInstance() : artifact_; + } else { + return artifactBuilder_.getMessage(); + } + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + public Builder setArtifact(io.a2a.grpc.Artifact value) { + if (artifactBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + artifact_ = value; + } else { + artifactBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + public Builder setArtifact( + io.a2a.grpc.Artifact.Builder builderForValue) { + if (artifactBuilder_ == null) { + artifact_ = builderForValue.build(); + } else { + artifactBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + public Builder mergeArtifact(io.a2a.grpc.Artifact value) { + if (artifactBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + artifact_ != null && + artifact_ != io.a2a.grpc.Artifact.getDefaultInstance()) { + getArtifactBuilder().mergeFrom(value); + } else { + artifact_ = value; + } + } else { + artifactBuilder_.mergeFrom(value); + } + if (artifact_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + public Builder clearArtifact() { + bitField0_ = (bitField0_ & ~0x00000004); + artifact_ = null; + if (artifactBuilder_ != null) { + artifactBuilder_.dispose(); + artifactBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + public io.a2a.grpc.Artifact.Builder getArtifactBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetArtifactFieldBuilder().getBuilder(); + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { + if (artifactBuilder_ != null) { + return artifactBuilder_.getMessageOrBuilder(); + } else { + return artifact_ == null ? + io.a2a.grpc.Artifact.getDefaultInstance() : artifact_; + } + } + /** + *
+     * The artifact itself
+     * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> + internalGetArtifactFieldBuilder() { + if (artifactBuilder_ == null) { + artifactBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder>( + getArtifact(), + getParentForChildren(), + isClean()); + artifact_ = null; + } + return artifactBuilder_; + } + + private boolean append_ ; + /** + *
+     * Whether this should be appended to a prior one produced
+     * 
+ * + * bool append = 4 [json_name = "append"]; + * @return The append. + */ + @java.lang.Override + public boolean getAppend() { + return append_; + } + /** + *
+     * Whether this should be appended to a prior one produced
+     * 
+ * + * bool append = 4 [json_name = "append"]; + * @param value The append to set. + * @return This builder for chaining. + */ + public Builder setAppend(boolean value) { + + append_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Whether this should be appended to a prior one produced
+     * 
+ * + * bool append = 4 [json_name = "append"]; + * @return This builder for chaining. + */ + public Builder clearAppend() { + bitField0_ = (bitField0_ & ~0x00000008); + append_ = false; + onChanged(); + return this; + } + + private boolean lastChunk_ ; + /** + *
+     * Whether this represents the last part of an artifact
+     * 
+ * + * bool last_chunk = 5 [json_name = "lastChunk"]; + * @return The lastChunk. + */ + @java.lang.Override + public boolean getLastChunk() { + return lastChunk_; + } + /** + *
+     * Whether this represents the last part of an artifact
+     * 
+ * + * bool last_chunk = 5 [json_name = "lastChunk"]; + * @param value The lastChunk to set. + * @return This builder for chaining. + */ + public Builder setLastChunk(boolean value) { + + lastChunk_ = value; + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * Whether this represents the last part of an artifact
+     * 
+ * + * bool last_chunk = 5 [json_name = "lastChunk"]; + * @return This builder for chaining. + */ + public Builder clearLastChunk() { + bitField0_ = (bitField0_ & ~0x00000010); + lastChunk_ = false; + onChanged(); + return this; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000020) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000020; + onChanged(); + } + return this; + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000020); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + *
+     * Optional metadata associated with the artifact update.
+     * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.TaskArtifactUpdateEvent) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.TaskArtifactUpdateEvent) + private static final io.a2a.grpc.TaskArtifactUpdateEvent DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.TaskArtifactUpdateEvent(); + } + + public static io.a2a.grpc.TaskArtifactUpdateEvent getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TaskArtifactUpdateEvent parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.TaskArtifactUpdateEvent getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java new file mode 100644 index 000000000..5e0c4c98f --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java @@ -0,0 +1,126 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface TaskArtifactUpdateEventOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.TaskArtifactUpdateEvent) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The id of the task for this artifact
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The taskId. + */ + java.lang.String getTaskId(); + /** + *
+   * The id of the task for this artifact
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + com.google.protobuf.ByteString + getTaskIdBytes(); + + /** + *
+   * The id of the context that this task belongs too
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + java.lang.String getContextId(); + /** + *
+   * The id of the context that this task belongs too
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + com.google.protobuf.ByteString + getContextIdBytes(); + + /** + *
+   * The artifact itself
+   * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * @return Whether the artifact field is set. + */ + boolean hasArtifact(); + /** + *
+   * The artifact itself
+   * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * @return The artifact. + */ + io.a2a.grpc.Artifact getArtifact(); + /** + *
+   * The artifact itself
+   * 
+ * + * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + */ + io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder(); + + /** + *
+   * Whether this should be appended to a prior one produced
+   * 
+ * + * bool append = 4 [json_name = "append"]; + * @return The append. + */ + boolean getAppend(); + + /** + *
+   * Whether this represents the last part of an artifact
+   * 
+ * + * bool last_chunk = 5 [json_name = "lastChunk"]; + * @return The lastChunk. + */ + boolean getLastChunk(); + + /** + *
+   * Optional metadata associated with the artifact update.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * Optional metadata associated with the artifact update.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * Optional metadata associated with the artifact update.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java new file mode 100644 index 000000000..d4bcf9727 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java @@ -0,0 +1,204 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface TaskOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.Task) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Unique identifier for a task, created by the A2A server.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The id. + */ + java.lang.String getId(); + /** + *
+   * Unique identifier for a task, created by the A2A server.
+   * 
+ * + * string id = 1 [json_name = "id"]; + * @return The bytes for id. + */ + com.google.protobuf.ByteString + getIdBytes(); + + /** + *
+   * Unique identifier for the contextual collection of interactions (tasks
+   * and messages). Created by the A2A server.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + java.lang.String getContextId(); + /** + *
+   * Unique identifier for the contextual collection of interactions (tasks
+   * and messages). Created by the A2A server.
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + com.google.protobuf.ByteString + getContextIdBytes(); + + /** + *
+   * The current status of a Task, including state and a message.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return Whether the status field is set. + */ + boolean hasStatus(); + /** + *
+   * The current status of a Task, including state and a message.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return The status. + */ + io.a2a.grpc.TaskStatus getStatus(); + /** + *
+   * The current status of a Task, including state and a message.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder(); + + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + java.util.List + getArtifactsList(); + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + io.a2a.grpc.Artifact getArtifacts(int index); + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + int getArtifactsCount(); + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + java.util.List + getArtifactsOrBuilderList(); + /** + *
+   * A set of output artifacts for a Task.
+   * 
+ * + * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + */ + io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( + int index); + + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + java.util.List + getHistoryList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + io.a2a.grpc.Message getHistory(int index); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + int getHistoryCount(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + java.util.List + getHistoryOrBuilderList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * The history of interactions from a task.
+   * 
+ * + * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + */ + io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( + int index); + + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * A key/value object to store custom metadata about a task.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * A key/value object to store custom metadata about a task.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
+   * A key/value object to store custom metadata about a task.
+   * 
+ * + * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java new file mode 100644 index 000000000..89d2fdd85 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java @@ -0,0 +1,723 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.TaskPushNotificationConfig} + */ +@com.google.protobuf.Generated +public final class TaskPushNotificationConfig extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.TaskPushNotificationConfig) + TaskPushNotificationConfigOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + TaskPushNotificationConfig.class.getName()); + } + // Use TaskPushNotificationConfig.newBuilder() to construct. + private TaskPushNotificationConfig(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private TaskPushNotificationConfig() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskPushNotificationConfig_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskPushNotificationConfig.class, io.a2a.grpc.TaskPushNotificationConfig.Builder.class); + } + + private int bitField0_; + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER = 2; + private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * @return Whether the pushNotificationConfig field is set. + */ + @java.lang.Override + public boolean hasPushNotificationConfig() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * @return The pushNotificationConfig. + */ + @java.lang.Override + public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { + return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + @java.lang.Override + public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { + return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(2, getPushNotificationConfig()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getPushNotificationConfig()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.TaskPushNotificationConfig)) { + return super.equals(obj); + } + io.a2a.grpc.TaskPushNotificationConfig other = (io.a2a.grpc.TaskPushNotificationConfig) obj; + + if (!getName() + .equals(other.getName())) return false; + if (hasPushNotificationConfig() != other.hasPushNotificationConfig()) return false; + if (hasPushNotificationConfig()) { + if (!getPushNotificationConfig() + .equals(other.getPushNotificationConfig())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + if (hasPushNotificationConfig()) { + hash = (37 * hash) + PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getPushNotificationConfig().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.TaskPushNotificationConfig parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.TaskPushNotificationConfig parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskPushNotificationConfig parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.TaskPushNotificationConfig prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.TaskPushNotificationConfig} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.TaskPushNotificationConfig) + io.a2a.grpc.TaskPushNotificationConfigOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskPushNotificationConfig_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskPushNotificationConfig.class, io.a2a.grpc.TaskPushNotificationConfig.Builder.class); + } + + // Construct using io.a2a.grpc.TaskPushNotificationConfig.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetPushNotificationConfigFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + name_ = ""; + pushNotificationConfig_ = null; + if (pushNotificationConfigBuilder_ != null) { + pushNotificationConfigBuilder_.dispose(); + pushNotificationConfigBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskPushNotificationConfig_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfig getDefaultInstanceForType() { + return io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfig build() { + io.a2a.grpc.TaskPushNotificationConfig result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfig buildPartial() { + io.a2a.grpc.TaskPushNotificationConfig result = new io.a2a.grpc.TaskPushNotificationConfig(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.TaskPushNotificationConfig result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.name_ = name_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.pushNotificationConfig_ = pushNotificationConfigBuilder_ == null + ? pushNotificationConfig_ + : pushNotificationConfigBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.TaskPushNotificationConfig) { + return mergeFrom((io.a2a.grpc.TaskPushNotificationConfig)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.TaskPushNotificationConfig other) { + if (other == io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.hasPushNotificationConfig()) { + mergePushNotificationConfig(other.getPushNotificationConfig()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + input.readMessage( + internalGetPushNotificationConfigFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationConfigBuilder_; + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * @return Whether the pushNotificationConfig field is set. + */ + public boolean hasPushNotificationConfig() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * @return The pushNotificationConfig. + */ + public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { + if (pushNotificationConfigBuilder_ == null) { + return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; + } else { + return pushNotificationConfigBuilder_.getMessage(); + } + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { + if (pushNotificationConfigBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + pushNotificationConfig_ = value; + } else { + pushNotificationConfigBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + public Builder setPushNotificationConfig( + io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { + if (pushNotificationConfigBuilder_ == null) { + pushNotificationConfig_ = builderForValue.build(); + } else { + pushNotificationConfigBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { + if (pushNotificationConfigBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0) && + pushNotificationConfig_ != null && + pushNotificationConfig_ != io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) { + getPushNotificationConfigBuilder().mergeFrom(value); + } else { + pushNotificationConfig_ = value; + } + } else { + pushNotificationConfigBuilder_.mergeFrom(value); + } + if (pushNotificationConfig_ != null) { + bitField0_ |= 0x00000002; + onChanged(); + } + return this; + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + public Builder clearPushNotificationConfig() { + bitField0_ = (bitField0_ & ~0x00000002); + pushNotificationConfig_ = null; + if (pushNotificationConfigBuilder_ != null) { + pushNotificationConfigBuilder_.dispose(); + pushNotificationConfigBuilder_ = null; + } + onChanged(); + return this; + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return internalGetPushNotificationConfigFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { + if (pushNotificationConfigBuilder_ != null) { + return pushNotificationConfigBuilder_.getMessageOrBuilder(); + } else { + return pushNotificationConfig_ == null ? + io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; + } + } + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> + internalGetPushNotificationConfigFieldBuilder() { + if (pushNotificationConfigBuilder_ == null) { + pushNotificationConfigBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder>( + getPushNotificationConfig(), + getParentForChildren(), + isClean()); + pushNotificationConfig_ = null; + } + return pushNotificationConfigBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.TaskPushNotificationConfig) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.TaskPushNotificationConfig) + private static final io.a2a.grpc.TaskPushNotificationConfig DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.TaskPushNotificationConfig(); + } + + public static io.a2a.grpc.TaskPushNotificationConfig getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TaskPushNotificationConfig parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.TaskPushNotificationConfig getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java new file mode 100644 index 000000000..923cdb062 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java @@ -0,0 +1,47 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface TaskPushNotificationConfigOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.TaskPushNotificationConfig) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * @return Whether the pushNotificationConfig field is set. + */ + boolean hasPushNotificationConfig(); + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * @return The pushNotificationConfig. + */ + io.a2a.grpc.PushNotificationConfig getPushNotificationConfig(); + /** + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + */ + io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/TaskState.java b/grpc/src/main/java/io/a2a/grpc/TaskState.java new file mode 100644 index 000000000..e6c361966 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskState.java @@ -0,0 +1,268 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * The set of states a Task can be in.
+ * 
+ * + * Protobuf enum {@code a2a.v1.TaskState} + */ +@com.google.protobuf.Generated +public enum TaskState + implements com.google.protobuf.ProtocolMessageEnum { + /** + * TASK_STATE_UNSPECIFIED = 0; + */ + TASK_STATE_UNSPECIFIED(0), + /** + *
+   * Represents the status that acknowledges a task is created
+   * 
+ * + * TASK_STATE_SUBMITTED = 1; + */ + TASK_STATE_SUBMITTED(1), + /** + *
+   * Represents the status that a task is actively being processed
+   * 
+ * + * TASK_STATE_WORKING = 2; + */ + TASK_STATE_WORKING(2), + /** + *
+   * Represents the status a task is finished. This is a terminal state
+   * 
+ * + * TASK_STATE_COMPLETED = 3; + */ + TASK_STATE_COMPLETED(3), + /** + *
+   * Represents the status a task is done but failed. This is a terminal state
+   * 
+ * + * TASK_STATE_FAILED = 4; + */ + TASK_STATE_FAILED(4), + /** + *
+   * Represents the status a task was cancelled before it finished.
+   * This is a terminal state.
+   * 
+ * + * TASK_STATE_CANCELLED = 5; + */ + TASK_STATE_CANCELLED(5), + /** + *
+   * Represents the status that the task requires information to complete.
+   * This is an interrupted state.
+   * 
+ * + * TASK_STATE_INPUT_REQUIRED = 6; + */ + TASK_STATE_INPUT_REQUIRED(6), + /** + *
+   * Represents the status that the agent has decided to not perform the task.
+   * This may be done during initial task creation or later once an agent
+   * has determined it can't or won't proceed. This is a terminal state.
+   * 
+ * + * TASK_STATE_REJECTED = 7; + */ + TASK_STATE_REJECTED(7), + /** + *
+   * Represents the state that some authentication is needed from the upstream
+   * client. Authentication is expected to come out-of-band thus this is not
+   * an interrupted or terminal state.
+   * 
+ * + * TASK_STATE_AUTH_REQUIRED = 8; + */ + TASK_STATE_AUTH_REQUIRED(8), + UNRECOGNIZED(-1), + ; + + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + TaskState.class.getName()); + } + /** + * TASK_STATE_UNSPECIFIED = 0; + */ + public static final int TASK_STATE_UNSPECIFIED_VALUE = 0; + /** + *
+   * Represents the status that acknowledges a task is created
+   * 
+ * + * TASK_STATE_SUBMITTED = 1; + */ + public static final int TASK_STATE_SUBMITTED_VALUE = 1; + /** + *
+   * Represents the status that a task is actively being processed
+   * 
+ * + * TASK_STATE_WORKING = 2; + */ + public static final int TASK_STATE_WORKING_VALUE = 2; + /** + *
+   * Represents the status a task is finished. This is a terminal state
+   * 
+ * + * TASK_STATE_COMPLETED = 3; + */ + public static final int TASK_STATE_COMPLETED_VALUE = 3; + /** + *
+   * Represents the status a task is done but failed. This is a terminal state
+   * 
+ * + * TASK_STATE_FAILED = 4; + */ + public static final int TASK_STATE_FAILED_VALUE = 4; + /** + *
+   * Represents the status a task was cancelled before it finished.
+   * This is a terminal state.
+   * 
+ * + * TASK_STATE_CANCELLED = 5; + */ + public static final int TASK_STATE_CANCELLED_VALUE = 5; + /** + *
+   * Represents the status that the task requires information to complete.
+   * This is an interrupted state.
+   * 
+ * + * TASK_STATE_INPUT_REQUIRED = 6; + */ + public static final int TASK_STATE_INPUT_REQUIRED_VALUE = 6; + /** + *
+   * Represents the status that the agent has decided to not perform the task.
+   * This may be done during initial task creation or later once an agent
+   * has determined it can't or won't proceed. This is a terminal state.
+   * 
+ * + * TASK_STATE_REJECTED = 7; + */ + public static final int TASK_STATE_REJECTED_VALUE = 7; + /** + *
+   * Represents the state that some authentication is needed from the upstream
+   * client. Authentication is expected to come out-of-band thus this is not
+   * an interrupted or terminal state.
+   * 
+ * + * TASK_STATE_AUTH_REQUIRED = 8; + */ + public static final int TASK_STATE_AUTH_REQUIRED_VALUE = 8; + + + public final int getNumber() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalArgumentException( + "Can't get the number of an unknown enum value."); + } + return value; + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + * @deprecated Use {@link #forNumber(int)} instead. + */ + @java.lang.Deprecated + public static TaskState valueOf(int value) { + return forNumber(value); + } + + /** + * @param value The numeric wire value of the corresponding enum entry. + * @return The enum associated with the given numeric wire value. + */ + public static TaskState forNumber(int value) { + switch (value) { + case 0: return TASK_STATE_UNSPECIFIED; + case 1: return TASK_STATE_SUBMITTED; + case 2: return TASK_STATE_WORKING; + case 3: return TASK_STATE_COMPLETED; + case 4: return TASK_STATE_FAILED; + case 5: return TASK_STATE_CANCELLED; + case 6: return TASK_STATE_INPUT_REQUIRED; + case 7: return TASK_STATE_REJECTED; + case 8: return TASK_STATE_AUTH_REQUIRED; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static final com.google.protobuf.Internal.EnumLiteMap< + TaskState> internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public TaskState findValueByNumber(int number) { + return TaskState.forNumber(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + if (this == UNRECOGNIZED) { + throw new java.lang.IllegalStateException( + "Can't get the descriptor of an unrecognized enum value."); + } + return getDescriptor().getValues().get(ordinal()); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return io.a2a.grpc.A2A.getDescriptor().getEnumTypes().get(0); + } + + private static final TaskState[] VALUES = values(); + + public static TaskState valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + if (desc.getIndex() == -1) { + return UNRECOGNIZED; + } + return VALUES[desc.getIndex()]; + } + + private final int value; + + private TaskState(int value) { + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:a2a.v1.TaskState) +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatus.java b/grpc/src/main/java/io/a2a/grpc/TaskStatus.java new file mode 100644 index 000000000..5ff2e72a7 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskStatus.java @@ -0,0 +1,980 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * A container for the status of a task
+ * 
+ * + * Protobuf type {@code a2a.v1.TaskStatus} + */ +@com.google.protobuf.Generated +public final class TaskStatus extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.TaskStatus) + TaskStatusOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + TaskStatus.class.getName()); + } + // Use TaskStatus.newBuilder() to construct. + private TaskStatus(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private TaskStatus() { + state_ = 0; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatus_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatus_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskStatus.class, io.a2a.grpc.TaskStatus.Builder.class); + } + + private int bitField0_; + public static final int STATE_FIELD_NUMBER = 1; + private int state_ = 0; + /** + *
+   * The current state of this task
+   * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return The enum numeric value on the wire for state. + */ + @java.lang.Override public int getStateValue() { + return state_; + } + /** + *
+   * The current state of this task
+   * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return The state. + */ + @java.lang.Override public io.a2a.grpc.TaskState getState() { + io.a2a.grpc.TaskState result = io.a2a.grpc.TaskState.forNumber(state_); + return result == null ? io.a2a.grpc.TaskState.UNRECOGNIZED : result; + } + + public static final int UPDATE_FIELD_NUMBER = 2; + private io.a2a.grpc.Message update_; + /** + *
+   * A message associated with the status.
+   * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + * @return Whether the update field is set. + */ + @java.lang.Override + public boolean hasUpdate() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * A message associated with the status.
+   * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + * @return The update. + */ + @java.lang.Override + public io.a2a.grpc.Message getUpdate() { + return update_ == null ? io.a2a.grpc.Message.getDefaultInstance() : update_; + } + /** + *
+   * A message associated with the status.
+   * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + @java.lang.Override + public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { + return update_ == null ? io.a2a.grpc.Message.getDefaultInstance() : update_; + } + + public static final int TIMESTAMP_FIELD_NUMBER = 3; + private com.google.protobuf.Timestamp timestamp_; + /** + *
+   * Timestamp when the status was recorded.
+   * Example: "2023-10-27T10:00:00Z"
+   * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * @return Whether the timestamp field is set. + */ + @java.lang.Override + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * Timestamp when the status was recorded.
+   * Example: "2023-10-27T10:00:00Z"
+   * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * @return The timestamp. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getTimestamp() { + return timestamp_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : timestamp_; + } + /** + *
+   * Timestamp when the status was recorded.
+   * Example: "2023-10-27T10:00:00Z"
+   * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder() { + return timestamp_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : timestamp_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (state_ != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED.getNumber()) { + output.writeEnum(1, state_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(2, getUpdate()); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeMessage(3, getTimestamp()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (state_ != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED.getNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, state_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, getUpdate()); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getTimestamp()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.TaskStatus)) { + return super.equals(obj); + } + io.a2a.grpc.TaskStatus other = (io.a2a.grpc.TaskStatus) obj; + + if (state_ != other.state_) return false; + if (hasUpdate() != other.hasUpdate()) return false; + if (hasUpdate()) { + if (!getUpdate() + .equals(other.getUpdate())) return false; + } + if (hasTimestamp() != other.hasTimestamp()) return false; + if (hasTimestamp()) { + if (!getTimestamp() + .equals(other.getTimestamp())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + STATE_FIELD_NUMBER; + hash = (53 * hash) + state_; + if (hasUpdate()) { + hash = (37 * hash) + UPDATE_FIELD_NUMBER; + hash = (53 * hash) + getUpdate().hashCode(); + } + if (hasTimestamp()) { + hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER; + hash = (53 * hash) + getTimestamp().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.TaskStatus parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskStatus parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskStatus parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskStatus parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskStatus parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskStatus parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskStatus parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskStatus parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.TaskStatus parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.TaskStatus parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.TaskStatus parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskStatus parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.TaskStatus prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * A container for the status of a task
+   * 
+ * + * Protobuf type {@code a2a.v1.TaskStatus} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.TaskStatus) + io.a2a.grpc.TaskStatusOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatus_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatus_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskStatus.class, io.a2a.grpc.TaskStatus.Builder.class); + } + + // Construct using io.a2a.grpc.TaskStatus.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetUpdateFieldBuilder(); + internalGetTimestampFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + state_ = 0; + update_ = null; + if (updateBuilder_ != null) { + updateBuilder_.dispose(); + updateBuilder_ = null; + } + timestamp_ = null; + if (timestampBuilder_ != null) { + timestampBuilder_.dispose(); + timestampBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatus_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.TaskStatus getDefaultInstanceForType() { + return io.a2a.grpc.TaskStatus.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.TaskStatus build() { + io.a2a.grpc.TaskStatus result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.TaskStatus buildPartial() { + io.a2a.grpc.TaskStatus result = new io.a2a.grpc.TaskStatus(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.TaskStatus result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.state_ = state_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.update_ = updateBuilder_ == null + ? update_ + : updateBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.timestamp_ = timestampBuilder_ == null + ? timestamp_ + : timestampBuilder_.build(); + to_bitField0_ |= 0x00000002; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.TaskStatus) { + return mergeFrom((io.a2a.grpc.TaskStatus)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.TaskStatus other) { + if (other == io.a2a.grpc.TaskStatus.getDefaultInstance()) return this; + if (other.state_ != 0) { + setStateValue(other.getStateValue()); + } + if (other.hasUpdate()) { + mergeUpdate(other.getUpdate()); + } + if (other.hasTimestamp()) { + mergeTimestamp(other.getTimestamp()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: { + state_ = input.readEnum(); + bitField0_ |= 0x00000001; + break; + } // case 8 + case 18: { + input.readMessage( + internalGetUpdateFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetTimestampFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private int state_ = 0; + /** + *
+     * The current state of this task
+     * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return The enum numeric value on the wire for state. + */ + @java.lang.Override public int getStateValue() { + return state_; + } + /** + *
+     * The current state of this task
+     * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @param value The enum numeric value on the wire for state to set. + * @return This builder for chaining. + */ + public Builder setStateValue(int value) { + state_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The current state of this task
+     * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return The state. + */ + @java.lang.Override + public io.a2a.grpc.TaskState getState() { + io.a2a.grpc.TaskState result = io.a2a.grpc.TaskState.forNumber(state_); + return result == null ? io.a2a.grpc.TaskState.UNRECOGNIZED : result; + } + /** + *
+     * The current state of this task
+     * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @param value The state to set. + * @return This builder for chaining. + */ + public Builder setState(io.a2a.grpc.TaskState value) { + if (value == null) { throw new NullPointerException(); } + bitField0_ |= 0x00000001; + state_ = value.getNumber(); + onChanged(); + return this; + } + /** + *
+     * The current state of this task
+     * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return This builder for chaining. + */ + public Builder clearState() { + bitField0_ = (bitField0_ & ~0x00000001); + state_ = 0; + onChanged(); + return this; + } + + private io.a2a.grpc.Message update_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> updateBuilder_; + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + * @return Whether the update field is set. + */ + public boolean hasUpdate() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + * @return The update. + */ + public io.a2a.grpc.Message getUpdate() { + if (updateBuilder_ == null) { + return update_ == null ? io.a2a.grpc.Message.getDefaultInstance() : update_; + } else { + return updateBuilder_.getMessage(); + } + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + public Builder setUpdate(io.a2a.grpc.Message value) { + if (updateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + update_ = value; + } else { + updateBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + public Builder setUpdate( + io.a2a.grpc.Message.Builder builderForValue) { + if (updateBuilder_ == null) { + update_ = builderForValue.build(); + } else { + updateBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + public Builder mergeUpdate(io.a2a.grpc.Message value) { + if (updateBuilder_ == null) { + if (((bitField0_ & 0x00000002) != 0) && + update_ != null && + update_ != io.a2a.grpc.Message.getDefaultInstance()) { + getUpdateBuilder().mergeFrom(value); + } else { + update_ = value; + } + } else { + updateBuilder_.mergeFrom(value); + } + if (update_ != null) { + bitField0_ |= 0x00000002; + onChanged(); + } + return this; + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + public Builder clearUpdate() { + bitField0_ = (bitField0_ & ~0x00000002); + update_ = null; + if (updateBuilder_ != null) { + updateBuilder_.dispose(); + updateBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + public io.a2a.grpc.Message.Builder getUpdateBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return internalGetUpdateFieldBuilder().getBuilder(); + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { + if (updateBuilder_ != null) { + return updateBuilder_.getMessageOrBuilder(); + } else { + return update_ == null ? + io.a2a.grpc.Message.getDefaultInstance() : update_; + } + } + /** + *
+     * A message associated with the status.
+     * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> + internalGetUpdateFieldBuilder() { + if (updateBuilder_ == null) { + updateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( + getUpdate(), + getParentForChildren(), + isClean()); + update_ = null; + } + return updateBuilder_; + } + + private com.google.protobuf.Timestamp timestamp_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> timestampBuilder_; + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * @return Whether the timestamp field is set. + */ + public boolean hasTimestamp() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * @return The timestamp. + */ + public com.google.protobuf.Timestamp getTimestamp() { + if (timestampBuilder_ == null) { + return timestamp_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : timestamp_; + } else { + return timestampBuilder_.getMessage(); + } + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + public Builder setTimestamp(com.google.protobuf.Timestamp value) { + if (timestampBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + timestamp_ = value; + } else { + timestampBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + public Builder setTimestamp( + com.google.protobuf.Timestamp.Builder builderForValue) { + if (timestampBuilder_ == null) { + timestamp_ = builderForValue.build(); + } else { + timestampBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + public Builder mergeTimestamp(com.google.protobuf.Timestamp value) { + if (timestampBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + timestamp_ != null && + timestamp_ != com.google.protobuf.Timestamp.getDefaultInstance()) { + getTimestampBuilder().mergeFrom(value); + } else { + timestamp_ = value; + } + } else { + timestampBuilder_.mergeFrom(value); + } + if (timestamp_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + public Builder clearTimestamp() { + bitField0_ = (bitField0_ & ~0x00000004); + timestamp_ = null; + if (timestampBuilder_ != null) { + timestampBuilder_.dispose(); + timestampBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + public com.google.protobuf.Timestamp.Builder getTimestampBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetTimestampFieldBuilder().getBuilder(); + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + public com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder() { + if (timestampBuilder_ != null) { + return timestampBuilder_.getMessageOrBuilder(); + } else { + return timestamp_ == null ? + com.google.protobuf.Timestamp.getDefaultInstance() : timestamp_; + } + } + /** + *
+     * Timestamp when the status was recorded.
+     * Example: "2023-10-27T10:00:00Z"
+     * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> + internalGetTimestampFieldBuilder() { + if (timestampBuilder_ == null) { + timestampBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>( + getTimestamp(), + getParentForChildren(), + isClean()); + timestamp_ = null; + } + return timestampBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.TaskStatus) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.TaskStatus) + private static final io.a2a.grpc.TaskStatus DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.TaskStatus(); + } + + public static io.a2a.grpc.TaskStatus getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TaskStatus parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.TaskStatus getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java new file mode 100644 index 000000000..2fc574154 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java @@ -0,0 +1,88 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface TaskStatusOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.TaskStatus) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The current state of this task
+   * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return The enum numeric value on the wire for state. + */ + int getStateValue(); + /** + *
+   * The current state of this task
+   * 
+ * + * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * @return The state. + */ + io.a2a.grpc.TaskState getState(); + + /** + *
+   * A message associated with the status.
+   * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + * @return Whether the update field is set. + */ + boolean hasUpdate(); + /** + *
+   * A message associated with the status.
+   * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + * @return The update. + */ + io.a2a.grpc.Message getUpdate(); + /** + *
+   * A message associated with the status.
+   * 
+ * + * .a2a.v1.Message update = 2 [json_name = "message"]; + */ + io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder(); + + /** + *
+   * Timestamp when the status was recorded.
+   * Example: "2023-10-27T10:00:00Z"
+   * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * @return Whether the timestamp field is set. + */ + boolean hasTimestamp(); + /** + *
+   * Timestamp when the status was recorded.
+   * Example: "2023-10-27T10:00:00Z"
+   * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * @return The timestamp. + */ + com.google.protobuf.Timestamp getTimestamp(); + /** + *
+   * Timestamp when the status was recorded.
+   * Example: "2023-10-27T10:00:00Z"
+   * 
+ * + * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + */ + com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java new file mode 100644 index 000000000..61cf2a157 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java @@ -0,0 +1,1261 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * TaskStatusUpdateEvent is a delta even on a task indicating that a task
+ * has changed.
+ * 
+ * + * Protobuf type {@code a2a.v1.TaskStatusUpdateEvent} + */ +@com.google.protobuf.Generated +public final class TaskStatusUpdateEvent extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.TaskStatusUpdateEvent) + TaskStatusUpdateEventOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + TaskStatusUpdateEvent.class.getName()); + } + // Use TaskStatusUpdateEvent.newBuilder() to construct. + private TaskStatusUpdateEvent(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private TaskStatusUpdateEvent() { + taskId_ = ""; + contextId_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatusUpdateEvent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskStatusUpdateEvent.class, io.a2a.grpc.TaskStatusUpdateEvent.Builder.class); + } + + private int bitField0_; + public static final int TASK_ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object taskId_ = ""; + /** + *
+   * The id of the task that is changed
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The taskId. + */ + @java.lang.Override + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } + } + /** + *
+   * The id of the task that is changed
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int CONTEXT_ID_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object contextId_ = ""; + /** + *
+   * The id of the context that the task belongs to
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + @java.lang.Override + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } + } + /** + *
+   * The id of the context that the task belongs to
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int STATUS_FIELD_NUMBER = 3; + private io.a2a.grpc.TaskStatus status_; + /** + *
+   * The new status of the task.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return Whether the status field is set. + */ + @java.lang.Override + public boolean hasStatus() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The new status of the task.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return The status. + */ + @java.lang.Override + public io.a2a.grpc.TaskStatus getStatus() { + return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } + /** + *
+   * The new status of the task.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + @java.lang.Override + public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { + return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } + + public static final int FINAL_FIELD_NUMBER = 4; + private boolean final_ = false; + /** + *
+   * Whether this is the last status update expected for this task.
+   * 
+ * + * bool final = 4 [json_name = "final"]; + * @return The final. + */ + @java.lang.Override + public boolean getFinal() { + return final_; + } + + public static final int METADATA_FIELD_NUMBER = 5; + private com.google.protobuf.Struct metadata_; + /** + *
+   * Optional metadata to associate with the task update.
+   * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * Optional metadata to associate with the task update.
+   * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * Optional metadata to associate with the task update.
+   * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, taskId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, contextId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(3, getStatus()); + } + if (final_ != false) { + output.writeBool(4, final_); + } + if (((bitField0_ & 0x00000002) != 0)) { + output.writeMessage(5, getMetadata()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, taskId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, contextId_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getStatus()); + } + if (final_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(4, final_); + } + if (((bitField0_ & 0x00000002) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, getMetadata()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.TaskStatusUpdateEvent)) { + return super.equals(obj); + } + io.a2a.grpc.TaskStatusUpdateEvent other = (io.a2a.grpc.TaskStatusUpdateEvent) obj; + + if (!getTaskId() + .equals(other.getTaskId())) return false; + if (!getContextId() + .equals(other.getContextId())) return false; + if (hasStatus() != other.hasStatus()) return false; + if (hasStatus()) { + if (!getStatus() + .equals(other.getStatus())) return false; + } + if (getFinal() + != other.getFinal()) return false; + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); + hash = (37 * hash) + CONTEXT_ID_FIELD_NUMBER; + hash = (53 * hash) + getContextId().hashCode(); + if (hasStatus()) { + hash = (37 * hash) + STATUS_FIELD_NUMBER; + hash = (53 * hash) + getStatus().hashCode(); + } + hash = (37 * hash) + FINAL_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getFinal()); + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.TaskStatusUpdateEvent parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.TaskStatusUpdateEvent parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskStatusUpdateEvent parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.TaskStatusUpdateEvent prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * TaskStatusUpdateEvent is a delta even on a task indicating that a task
+   * has changed.
+   * 
+ * + * Protobuf type {@code a2a.v1.TaskStatusUpdateEvent} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.TaskStatusUpdateEvent) + io.a2a.grpc.TaskStatusUpdateEventOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatusUpdateEvent_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskStatusUpdateEvent.class, io.a2a.grpc.TaskStatusUpdateEvent.Builder.class); + } + + // Construct using io.a2a.grpc.TaskStatusUpdateEvent.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetStatusFieldBuilder(); + internalGetMetadataFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + taskId_ = ""; + contextId_ = ""; + status_ = null; + if (statusBuilder_ != null) { + statusBuilder_.dispose(); + statusBuilder_ = null; + } + final_ = false; + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEvent getDefaultInstanceForType() { + return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEvent build() { + io.a2a.grpc.TaskStatusUpdateEvent result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEvent buildPartial() { + io.a2a.grpc.TaskStatusUpdateEvent result = new io.a2a.grpc.TaskStatusUpdateEvent(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.TaskStatusUpdateEvent result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.taskId_ = taskId_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.contextId_ = contextId_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.status_ = statusBuilder_ == null + ? status_ + : statusBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.final_ = final_; + } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000002; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.TaskStatusUpdateEvent) { + return mergeFrom((io.a2a.grpc.TaskStatusUpdateEvent)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.TaskStatusUpdateEvent other) { + if (other == io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance()) return this; + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getContextId().isEmpty()) { + contextId_ = other.contextId_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasStatus()) { + mergeStatus(other.getStatus()); + } + if (other.getFinal() != false) { + setFinal(other.getFinal()); + } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + taskId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + contextId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetStatusFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 32: { + final_ = input.readBool(); + bitField0_ |= 0x00000008; + break; + } // case 32 + case 42: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000010; + break; + } // case 42 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object taskId_ = ""; + /** + *
+     * The id of the task that is changed
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The taskId. + */ + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The id of the task that is changed
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The id of the task that is changed
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @param value The taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + taskId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The id of the task that is changed
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return This builder for chaining. + */ + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The id of the task that is changed
+     * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @param value The bytes for taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + taskId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object contextId_ = ""; + /** + *
+     * The id of the context that the task belongs to
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The id of the context that the task belongs to
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The id of the context that the task belongs to
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The contextId to set. + * @return This builder for chaining. + */ + public Builder setContextId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The id of the context that the task belongs to
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return This builder for chaining. + */ + public Builder clearContextId() { + contextId_ = getDefaultInstance().getContextId(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The id of the context that the task belongs to
+     * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @param value The bytes for contextId to set. + * @return This builder for chaining. + */ + public Builder setContextIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + contextId_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private io.a2a.grpc.TaskStatus status_; + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> statusBuilder_; + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return Whether the status field is set. + */ + public boolean hasStatus() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return The status. + */ + public io.a2a.grpc.TaskStatus getStatus() { + if (statusBuilder_ == null) { + return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } else { + return statusBuilder_.getMessage(); + } + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder setStatus(io.a2a.grpc.TaskStatus value) { + if (statusBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + status_ = value; + } else { + statusBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder setStatus( + io.a2a.grpc.TaskStatus.Builder builderForValue) { + if (statusBuilder_ == null) { + status_ = builderForValue.build(); + } else { + statusBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { + if (statusBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + status_ != null && + status_ != io.a2a.grpc.TaskStatus.getDefaultInstance()) { + getStatusBuilder().mergeFrom(value); + } else { + status_ = value; + } + } else { + statusBuilder_.mergeFrom(value); + } + if (status_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public Builder clearStatus() { + bitField0_ = (bitField0_ & ~0x00000004); + status_ = null; + if (statusBuilder_ != null) { + statusBuilder_.dispose(); + statusBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetStatusFieldBuilder().getBuilder(); + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { + if (statusBuilder_ != null) { + return statusBuilder_.getMessageOrBuilder(); + } else { + return status_ == null ? + io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; + } + } + /** + *
+     * The new status of the task.
+     * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> + internalGetStatusFieldBuilder() { + if (statusBuilder_ == null) { + statusBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder>( + getStatus(), + getParentForChildren(), + isClean()); + status_ = null; + } + return statusBuilder_; + } + + private boolean final_ ; + /** + *
+     * Whether this is the last status update expected for this task.
+     * 
+ * + * bool final = 4 [json_name = "final"]; + * @return The final. + */ + @java.lang.Override + public boolean getFinal() { + return final_; + } + /** + *
+     * Whether this is the last status update expected for this task.
+     * 
+ * + * bool final = 4 [json_name = "final"]; + * @param value The final to set. + * @return This builder for chaining. + */ + public Builder setFinal(boolean value) { + + final_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Whether this is the last status update expected for this task.
+     * 
+ * + * bool final = 4 [json_name = "final"]; + * @return This builder for chaining. + */ + public Builder clearFinal() { + bitField0_ = (bitField0_ & ~0x00000008); + final_ = false; + onChanged(); + return this; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000010) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000010; + onChanged(); + } + return this; + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000010); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + *
+     * Optional metadata to associate with the task update.
+     * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.TaskStatusUpdateEvent) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.TaskStatusUpdateEvent) + private static final io.a2a.grpc.TaskStatusUpdateEvent DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.TaskStatusUpdateEvent(); + } + + public static io.a2a.grpc.TaskStatusUpdateEvent getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TaskStatusUpdateEvent parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.TaskStatusUpdateEvent getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java new file mode 100644 index 000000000..42fd0d67f --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java @@ -0,0 +1,116 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface TaskStatusUpdateEventOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.TaskStatusUpdateEvent) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The id of the task that is changed
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The taskId. + */ + java.lang.String getTaskId(); + /** + *
+   * The id of the task that is changed
+   * 
+ * + * string task_id = 1 [json_name = "taskId"]; + * @return The bytes for taskId. + */ + com.google.protobuf.ByteString + getTaskIdBytes(); + + /** + *
+   * The id of the context that the task belongs to
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The contextId. + */ + java.lang.String getContextId(); + /** + *
+   * The id of the context that the task belongs to
+   * 
+ * + * string context_id = 2 [json_name = "contextId"]; + * @return The bytes for contextId. + */ + com.google.protobuf.ByteString + getContextIdBytes(); + + /** + *
+   * The new status of the task.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return Whether the status field is set. + */ + boolean hasStatus(); + /** + *
+   * The new status of the task.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * @return The status. + */ + io.a2a.grpc.TaskStatus getStatus(); + /** + *
+   * The new status of the task.
+   * 
+ * + * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + */ + io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder(); + + /** + *
+   * Whether this is the last status update expected for this task.
+   * 
+ * + * bool final = 4 [json_name = "final"]; + * @return The final. + */ + boolean getFinal(); + + /** + *
+   * Optional metadata to associate with the task update.
+   * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * Optional metadata to associate with the task update.
+   * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * Optional metadata to associate with the task update.
+   * 
+ * + * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java b/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java new file mode 100644 index 000000000..5f4020608 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java @@ -0,0 +1,530 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.TaskSubscriptionRequest} + */ +@com.google.protobuf.Generated +public final class TaskSubscriptionRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.TaskSubscriptionRequest) + TaskSubscriptionRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + TaskSubscriptionRequest.class.getName()); + } + // Use TaskSubscriptionRequest.newBuilder() to construct. + private TaskSubscriptionRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private TaskSubscriptionRequest() { + name_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskSubscriptionRequest.class, io.a2a.grpc.TaskSubscriptionRequest.Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object name_ = ""; + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + @java.lang.Override + public java.lang.String getName() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.TaskSubscriptionRequest)) { + return super.equals(obj); + } + io.a2a.grpc.TaskSubscriptionRequest other = (io.a2a.grpc.TaskSubscriptionRequest) obj; + + if (!getName() + .equals(other.getName())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.TaskSubscriptionRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.TaskSubscriptionRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.TaskSubscriptionRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.TaskSubscriptionRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.TaskSubscriptionRequest) + io.a2a.grpc.TaskSubscriptionRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.TaskSubscriptionRequest.class, io.a2a.grpc.TaskSubscriptionRequest.Builder.class); + } + + // Construct using io.a2a.grpc.TaskSubscriptionRequest.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + name_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.TaskSubscriptionRequest getDefaultInstanceForType() { + return io.a2a.grpc.TaskSubscriptionRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.TaskSubscriptionRequest build() { + io.a2a.grpc.TaskSubscriptionRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.TaskSubscriptionRequest buildPartial() { + io.a2a.grpc.TaskSubscriptionRequest result = new io.a2a.grpc.TaskSubscriptionRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.TaskSubscriptionRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.name_ = name_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.TaskSubscriptionRequest) { + return mergeFrom((io.a2a.grpc.TaskSubscriptionRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.TaskSubscriptionRequest other) { + if (other == io.a2a.grpc.TaskSubscriptionRequest.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + bitField0_ |= 0x00000001; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + name_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object name_ = ""; + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + public java.lang.String getName() { + java.lang.Object ref = name_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + java.lang.Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @return This builder for chaining. + */ + public Builder clearName() { + name_ = getDefaultInstance().getName(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * name=tasks/{id}
+     * 
+ * + * string name = 1 [json_name = "name"]; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + name_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.TaskSubscriptionRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.TaskSubscriptionRequest) + private static final io.a2a.grpc.TaskSubscriptionRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.TaskSubscriptionRequest(); + } + + public static io.a2a.grpc.TaskSubscriptionRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public TaskSubscriptionRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.TaskSubscriptionRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java b/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java new file mode 100644 index 000000000..da8c0e621 --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java @@ -0,0 +1,32 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface TaskSubscriptionRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.TaskSubscriptionRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The name. + */ + java.lang.String getName(); + /** + *
+   * name=tasks/{id}
+   * 
+ * + * string name = 1 [json_name = "name"]; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); +} diff --git a/grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java new file mode 100644 index 000000000..a3e36ce3f --- /dev/null +++ b/grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -0,0 +1,871 @@ +package io.a2a.grpc.utils; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.protobuf.ByteString; +import com.google.protobuf.Struct; +import com.google.protobuf.Value; + +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.APIKeySecurityScheme; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentExtension; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.AgentProvider; +import io.a2a.spec.AgentSkill; +import io.a2a.spec.Artifact; +import io.a2a.spec.AuthorizationCodeOAuthFlow; +import io.a2a.spec.ClientCredentialsOAuthFlow; +import io.a2a.spec.DataPart; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.FileContent; +import io.a2a.spec.FilePart; +import io.a2a.spec.FileWithBytes; +import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.ImplicitOAuthFlow; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendConfiguration; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.OAuth2SecurityScheme; +import io.a2a.spec.OAuthFlows; +import io.a2a.spec.OpenIdConnectSecurityScheme; +import io.a2a.spec.Part; +import io.a2a.spec.PasswordOAuthFlow; +import io.a2a.spec.PushNotificationAuthenticationInfo; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.SecurityScheme; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; + +/** + * Utility class to convert between GRPC and Spec objects. + */ +public class ProtoUtils { + public static class ToProto { + + public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { + io.a2a.grpc.AgentCard.Builder builder = io.a2a.grpc.AgentCard.newBuilder(); + if (agentCard.protocolVersion() != null) { + builder.setProtocolVersion(agentCard.protocolVersion()); + } + if (agentCard.name() != null) { + builder.setName(agentCard.name()); + } + if (agentCard.description() != null) { + builder.setDescription(agentCard.description()); + } + if (agentCard.url() != null) { + builder.setUrl(agentCard.url()); + } + if (agentCard.preferredTransport() != null) { + builder.setPreferredTransport(agentCard.preferredTransport()); + } + if (agentCard.additionalInterfaces() != null) { + builder.addAllAdditionalInterfaces(agentCard.additionalInterfaces().stream().map(item -> agentInterface(item)).collect(Collectors.toList())); + } + if (agentCard.provider() != null) { + builder.setProvider(agentProvider(agentCard.provider())); + } + if (agentCard.version() != null) { + builder.setVersion(agentCard.version()); + } + if (agentCard.documentationUrl() != null) { + builder.setDocumentationUrl(agentCard.documentationUrl()); + } + if (agentCard.capabilities() != null) { + builder.setCapabilities(agentCapabilities(agentCard.capabilities())); + } + if (agentCard.securitySchemes() != null) { + builder.putAllSecuritySchemes( + agentCard.securitySchemes().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> securityScheme(e.getValue()))) + ); + } + if (agentCard.security() != null) { + builder.addAllSecurity(agentCard.security().stream().map(s -> { + io.a2a.grpc.Security.Builder securityBuilder = io.a2a.grpc.Security.newBuilder(); + s.forEach((key, value) -> { + io.a2a.grpc.StringList.Builder stringListBuilder = io.a2a.grpc.StringList.newBuilder(); + stringListBuilder.addAllList(value); + securityBuilder.putSchemes(key, stringListBuilder.build()); + }); + return securityBuilder.build(); + }).collect(Collectors.toList())); + } + if (agentCard.defaultInputModes() != null) { + builder.addAllDefaultInputModes(agentCard.defaultInputModes()); + } + if (agentCard.defaultOutputModes() != null) { + builder.addAllDefaultOutputModes(agentCard.defaultOutputModes()); + } + if (agentCard.skills() != null) { + builder.addAllSkills(agentCard.skills().stream().map(ToProto::agentSkill).collect(Collectors.toList())); + } + builder.setSupportsAuthenticatedExtendedCard(agentCard.supportsAuthenticatedExtendedCard()); + return builder.build(); + } + + public static io.a2a.grpc.Task task(Task task) { + io.a2a.grpc.Task.Builder builder = io.a2a.grpc.Task.newBuilder(); + builder.setId(task.getId()); + builder.setContextId(task.getContextId()); + builder.setStatus(taskStatus(task.getStatus())); + if (task.getArtifacts() != null) { + builder.addAllArtifacts(task.getArtifacts().stream().map(ToProto::artifact).collect(Collectors.toList())); + } + if (task.getHistory() != null) { + builder.addAllHistory(task.getHistory().stream().map(ToProto::message).collect(Collectors.toList())); + } + builder.setMetadata(struct(task.getMetadata())); + return builder.build(); + } + + public static io.a2a.grpc.Message message(Message message) { + io.a2a.grpc.Message.Builder builder = io.a2a.grpc.Message.newBuilder(); + builder.setMessageId(message.getMessageId()); + builder.setContextId(message.getContextId()); + builder.setTaskId(message.getTaskId()); + builder.setRole(role(message.getRole())); + if (message.getParts() != null) { + builder.addAllContent(message.getParts().stream().map(ToProto::part).collect(Collectors.toList())); + } + builder.setMetadata(struct(message.getMetadata())); + return builder.build(); + } + + public static io.a2a.grpc.TaskPushNotificationConfig taskPushNotificationConfig(TaskPushNotificationConfig config) { + io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); + builder.setName("tasks/" + config.taskId() + "/pushNotificationConfigs/" + config.pushNotificationConfig().id()); + builder.setPushNotificationConfig(pushNotificationConfig(config.pushNotificationConfig())); + return builder.build(); + } + + private static io.a2a.grpc.PushNotificationConfig pushNotificationConfig(PushNotificationConfig config) { + io.a2a.grpc.PushNotificationConfig.Builder builder = io.a2a.grpc.PushNotificationConfig.newBuilder(); + if (config.url() != null) { + builder.setUrl(config.url()); + } + if (config.token() != null) { + builder.setToken(config.token()); + } + if (config.authentication() != null) { + builder.setAuthentication(authenticationInfo(config.authentication())); + } + if (config.id() != null) { + builder.setId(config.id()); + } + return builder.build(); + } + + public static io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent(TaskArtifactUpdateEvent event) { + io.a2a.grpc.TaskArtifactUpdateEvent.Builder builder = io.a2a.grpc.TaskArtifactUpdateEvent.newBuilder(); + builder.setTaskId(event.getTaskId()); + builder.setContextId(event.getContextId()); + builder.setArtifact(artifact(event.getArtifact())); + builder.setAppend(event.isAppend() == null ? false : event.isAppend()); + builder.setLastChunk(event.isLastChunk() == null ? false : event.isLastChunk()); + if (event.getMetadata() != null) { + builder.setMetadata(struct(event.getMetadata())); + } + return builder.build(); + } + + public static io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent(TaskStatusUpdateEvent event) { + io.a2a.grpc.TaskStatusUpdateEvent.Builder builder = io.a2a.grpc.TaskStatusUpdateEvent.newBuilder(); + builder.setTaskId(event.getTaskId()); + builder.setContextId(event.getContextId()); + builder.setStatus(taskStatus(event.getStatus())); + builder.setFinal(event.isFinal()); + if (event.getMetadata() != null) { + builder.setMetadata(struct(event.getMetadata())); + } + return builder.build(); + } + + private static io.a2a.grpc.Artifact artifact(Artifact artifact) { + io.a2a.grpc.Artifact.Builder builder = io.a2a.grpc.Artifact.newBuilder(); + if (artifact.artifactId() != null) { + builder.setArtifactId(artifact.artifactId()); + } + if (artifact.name() != null) { + builder.setName(artifact.name()); + } + if (artifact.description() != null) { + builder.setDescription(artifact.description()); + } + if (artifact.parts() != null) { + builder.addAllParts(artifact.parts().stream().map(ToProto::part).collect(Collectors.toList())); + } + if (artifact.metadata() != null) { + builder.setMetadata(struct(artifact.metadata())); + } + return builder.build(); + } + + private static io.a2a.grpc.Part part(Part part) { + io.a2a.grpc.Part.Builder builder = io.a2a.grpc.Part.newBuilder(); + if (part instanceof TextPart) { + builder.setText(((TextPart) part).getText()); + } else if (part instanceof FilePart) { + builder.setFile(filePart((FilePart) part)); + } else if (part instanceof DataPart) { + builder.setData(dataPart((DataPart) part)); + } + return builder.build(); + } + + private static io.a2a.grpc.FilePart filePart(FilePart filePart) { + io.a2a.grpc.FilePart.Builder builder = io.a2a.grpc.FilePart.newBuilder(); + FileContent fileContent = filePart.getFile(); + if (fileContent instanceof FileWithBytes) { + builder.setFileWithBytes(ByteString.copyFrom(((FileWithBytes) fileContent).bytes(), StandardCharsets.UTF_8)); + } else if (fileContent instanceof FileWithUri) { + builder.setFileWithUri(((FileWithUri) fileContent).uri()); + } + return builder.build(); + } + + private static io.a2a.grpc.DataPart dataPart(DataPart dataPart) { + io.a2a.grpc.DataPart.Builder builder = io.a2a.grpc.DataPart.newBuilder(); + if (dataPart.getData() != null) { + builder.setData(struct(dataPart.getData())); + } + return builder.build(); + } + + private static io.a2a.grpc.Role role(Message.Role role) { + if (role == null) { + return io.a2a.grpc.Role.ROLE_UNSPECIFIED; + } + return switch (role) { + case USER -> io.a2a.grpc.Role.ROLE_USER; + case AGENT -> io.a2a.grpc.Role.ROLE_AGENT; + }; + } + + private static io.a2a.grpc.TaskStatus taskStatus(TaskStatus taskStatus) { + io.a2a.grpc.TaskStatus.Builder builder = io.a2a.grpc.TaskStatus.newBuilder(); + if (taskStatus.state() != null) { + builder.setState(taskState(taskStatus.state())); + } + if (taskStatus.message() != null) { + builder.setUpdate(message(taskStatus.message())); + } + if (taskStatus.timestamp() != null) { + Instant instant = taskStatus.timestamp().toInstant(ZoneOffset.UTC); + builder.setTimestamp(com.google.protobuf.Timestamp.newBuilder().setSeconds(instant.getEpochSecond()).setNanos(instant.getNano()).build()); + } + return builder.build(); + } + + private static io.a2a.grpc.TaskState taskState(TaskState taskState) { + if (taskState == null) { + return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; + } + return switch (taskState) { + case SUBMITTED -> io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED; + case WORKING -> io.a2a.grpc.TaskState.TASK_STATE_WORKING; + case INPUT_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED; + case AUTH_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED; + case COMPLETED -> io.a2a.grpc.TaskState.TASK_STATE_COMPLETED; + case CANCELED -> io.a2a.grpc.TaskState.TASK_STATE_CANCELLED; + case FAILED -> io.a2a.grpc.TaskState.TASK_STATE_FAILED; + case REJECTED -> io.a2a.grpc.TaskState.TASK_STATE_REJECTED; + default -> io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; + }; + } + + private static io.a2a.grpc.AuthenticationInfo authenticationInfo(PushNotificationAuthenticationInfo pushNotificationAuthenticationInfo) { + io.a2a.grpc.AuthenticationInfo.Builder builder = io.a2a.grpc.AuthenticationInfo.newBuilder(); + if (pushNotificationAuthenticationInfo.schemes() != null) { + builder.addAllSchemes(pushNotificationAuthenticationInfo.schemes()); + } + if (pushNotificationAuthenticationInfo.credentials() != null) { + builder.setCredentials(pushNotificationAuthenticationInfo.credentials()); + } + return builder.build(); + } + + public static io.a2a.grpc.SendMessageConfiguration messageSendConfiguration(MessageSendConfiguration messageSendConfiguration) { + io.a2a.grpc.SendMessageConfiguration.Builder builder = io.a2a.grpc.SendMessageConfiguration.newBuilder(); + builder.addAllAcceptedOutputModes(messageSendConfiguration.acceptedOutputModes()); + if (messageSendConfiguration.historyLength() != null) { + builder.setHistoryLength(messageSendConfiguration.historyLength()); + } + if (messageSendConfiguration.pushNotification() != null) { + builder.setPushNotification(pushNotificationConfig(messageSendConfiguration.pushNotification())); + } + builder.setBlocking(messageSendConfiguration.blocking()); + return builder.build(); + } + + private static io.a2a.grpc.AgentProvider agentProvider(AgentProvider agentProvider) { + io.a2a.grpc.AgentProvider.Builder builder = io.a2a.grpc.AgentProvider.newBuilder(); + builder.setOrganization(agentProvider.organization()); + builder.setUrl(agentProvider.url()); + return builder.build(); + } + + private static io.a2a.grpc.AgentCapabilities agentCapabilities(AgentCapabilities agentCapabilities) { + io.a2a.grpc.AgentCapabilities.Builder builder = io.a2a.grpc.AgentCapabilities.newBuilder(); + builder.setStreaming(agentCapabilities.streaming()); + builder.setPushNotifications(agentCapabilities.pushNotifications()); + if (agentCapabilities.extensions() != null) { + builder.addAllExtensions(agentCapabilities.extensions().stream().map(ToProto::agentExtension).collect(Collectors.toList())); + } + return builder.build(); + } + + private static io.a2a.grpc.AgentExtension agentExtension(AgentExtension agentExtension) { + io.a2a.grpc.AgentExtension.Builder builder = io.a2a.grpc.AgentExtension.newBuilder(); + if (agentExtension.description() != null) { + builder.setDescription(agentExtension.description()); + } + if (agentExtension.params() != null) { + builder.setParams(struct(agentExtension.params())); + } + builder.setRequired(agentExtension.required()); + if (agentExtension.uri() != null) { + builder.setUri(agentExtension.uri()); + } + return builder.build(); + } + + private static io.a2a.grpc.AgentSkill agentSkill(AgentSkill agentSkill) { + io.a2a.grpc.AgentSkill.Builder builder = io.a2a.grpc.AgentSkill.newBuilder(); + if (agentSkill.id() != null) { + builder.setId(agentSkill.id()); + } + if (agentSkill.name() != null) { + builder.setName(agentSkill.name()); + } + if (agentSkill.description() != null) { + builder.setDescription(agentSkill.description()); + } + if (agentSkill.tags() != null) { + builder.addAllTags(agentSkill.tags()); + } + if (agentSkill.examples() != null) { + builder.addAllExamples(agentSkill.examples()); + } + if (agentSkill.inputModes() != null) { + builder.addAllInputModes(agentSkill.inputModes()); + } + if (agentSkill.outputModes() != null) { + builder.addAllOutputModes(agentSkill.outputModes()); + } + return builder.build(); + } + + private static io.a2a.grpc.SecurityScheme securityScheme(SecurityScheme securityScheme) { + io.a2a.grpc.SecurityScheme.Builder builder = io.a2a.grpc.SecurityScheme.newBuilder(); + if (securityScheme instanceof APIKeySecurityScheme) { + builder.setApiKeySecurityScheme(apiKeySecurityScheme((APIKeySecurityScheme) securityScheme)); + } else if (securityScheme instanceof HTTPAuthSecurityScheme) { + builder.setHttpAuthSecurityScheme(httpAuthSecurityScheme((HTTPAuthSecurityScheme) securityScheme)); + } else if (securityScheme instanceof OAuth2SecurityScheme) { + builder.setOauth2SecurityScheme(oauthSecurityScheme((OAuth2SecurityScheme) securityScheme)); + } else if (securityScheme instanceof OpenIdConnectSecurityScheme) { + builder.setOpenIdConnectSecurityScheme(openIdConnectSecurityScheme((OpenIdConnectSecurityScheme) securityScheme)); + } + return builder.build(); + } + + private static io.a2a.grpc.APIKeySecurityScheme apiKeySecurityScheme(APIKeySecurityScheme apiKeySecurityScheme) { + io.a2a.grpc.APIKeySecurityScheme.Builder builder = io.a2a.grpc.APIKeySecurityScheme.newBuilder(); + if (apiKeySecurityScheme.getDescription() != null) { + builder.setDescription(apiKeySecurityScheme.getDescription()); + } + if (apiKeySecurityScheme.getIn() != null) { + builder.setLocation(apiKeySecurityScheme.getIn()); + } + if (apiKeySecurityScheme.getName() != null) { + builder.setName(apiKeySecurityScheme.getName()); + } + return builder.build(); + } + + private static io.a2a.grpc.HTTPAuthSecurityScheme httpAuthSecurityScheme(HTTPAuthSecurityScheme httpAuthSecurityScheme) { + io.a2a.grpc.HTTPAuthSecurityScheme.Builder builder = io.a2a.grpc.HTTPAuthSecurityScheme.newBuilder(); + if (httpAuthSecurityScheme.getBearerFormat() != null) { + builder.setBearerFormat(httpAuthSecurityScheme.getBearerFormat()); + } + if (httpAuthSecurityScheme.getDescription() != null) { + builder.setDescription(httpAuthSecurityScheme.getDescription()); + } + if (httpAuthSecurityScheme.getScheme() != null) { + builder.setScheme(httpAuthSecurityScheme.getScheme()); + } + return builder.build(); + } + + private static io.a2a.grpc.OAuth2SecurityScheme oauthSecurityScheme(OAuth2SecurityScheme oauth2SecurityScheme) { + io.a2a.grpc.OAuth2SecurityScheme.Builder builder = io.a2a.grpc.OAuth2SecurityScheme.newBuilder(); + if (oauth2SecurityScheme.getDescription() != null) { + builder.setDescription(oauth2SecurityScheme.getDescription()); + } + if (oauth2SecurityScheme.getFlows() != null) { + builder.setFlows(oauthFlows(oauth2SecurityScheme.getFlows())); + } + return builder.build(); + } + + private static io.a2a.grpc.OAuthFlows oauthFlows(OAuthFlows oAuthFlows) { + io.a2a.grpc.OAuthFlows.Builder builder = io.a2a.grpc.OAuthFlows.newBuilder(); + if (oAuthFlows.authorizationCode() != null) { + builder.setAuthorizationCode(authorizationCodeOAuthFlow(oAuthFlows.authorizationCode())); + } + if (oAuthFlows.clientCredentials() != null) { + builder.setClientCredentials(clientCredentialsOAuthFlow(oAuthFlows.clientCredentials())); + } + if (oAuthFlows.implicit() != null) { + builder.setImplicit(implicitOAuthFlow(oAuthFlows.implicit())); + } + if (oAuthFlows.password() != null) { + builder.setPassword(passwordOAuthFlow(oAuthFlows.password())); + } + return builder.build(); + } + + private static io.a2a.grpc.AuthorizationCodeOAuthFlow authorizationCodeOAuthFlow(AuthorizationCodeOAuthFlow authorizationCodeOAuthFlow) { + io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder builder = io.a2a.grpc.AuthorizationCodeOAuthFlow.newBuilder(); + if (authorizationCodeOAuthFlow.authorizationUrl() != null) { + builder.setAuthorizationUrl(authorizationCodeOAuthFlow.authorizationUrl()); + } + if (authorizationCodeOAuthFlow.refreshUrl() != null) { + builder.setRefreshUrl(authorizationCodeOAuthFlow.refreshUrl()); + } + if (authorizationCodeOAuthFlow.scopes() != null) { + builder.putAllScopes(authorizationCodeOAuthFlow.scopes()); + } + if (authorizationCodeOAuthFlow.tokenUrl() != null) { + builder.setTokenUrl(authorizationCodeOAuthFlow.tokenUrl()); + } + return builder.build(); + } + + private static io.a2a.grpc.ClientCredentialsOAuthFlow clientCredentialsOAuthFlow(ClientCredentialsOAuthFlow clientCredentialsOAuthFlow) { + io.a2a.grpc.ClientCredentialsOAuthFlow.Builder builder = io.a2a.grpc.ClientCredentialsOAuthFlow.newBuilder(); + if (clientCredentialsOAuthFlow.refreshUrl() != null) { + builder.setRefreshUrl(clientCredentialsOAuthFlow.refreshUrl()); + } + if (clientCredentialsOAuthFlow.scopes() != null) { + builder.putAllScopes(clientCredentialsOAuthFlow.scopes()); + } + if (clientCredentialsOAuthFlow.tokenUrl() != null) { + builder.setTokenUrl(clientCredentialsOAuthFlow.tokenUrl()); + } + return builder.build(); + } + + private static io.a2a.grpc.ImplicitOAuthFlow implicitOAuthFlow(ImplicitOAuthFlow implicitOAuthFlow) { + io.a2a.grpc.ImplicitOAuthFlow.Builder builder = io.a2a.grpc.ImplicitOAuthFlow.newBuilder(); + if (implicitOAuthFlow.authorizationUrl() != null) { + builder.setAuthorizationUrl(implicitOAuthFlow.authorizationUrl()); + } + if (implicitOAuthFlow.refreshUrl() != null) { + builder.setRefreshUrl(implicitOAuthFlow.refreshUrl()); + } + if (implicitOAuthFlow.scopes() != null) { + builder.putAllScopes(implicitOAuthFlow.scopes()); + } + return builder.build(); + } + + private static io.a2a.grpc.PasswordOAuthFlow passwordOAuthFlow(PasswordOAuthFlow passwordOAuthFlow) { + io.a2a.grpc.PasswordOAuthFlow.Builder builder = io.a2a.grpc.PasswordOAuthFlow.newBuilder(); + if (passwordOAuthFlow.refreshUrl() != null) { + builder.setRefreshUrl(passwordOAuthFlow.refreshUrl()); + } + if (passwordOAuthFlow.scopes() != null) { + builder.putAllScopes(passwordOAuthFlow.scopes()); + } + if (passwordOAuthFlow.tokenUrl() != null) { + builder.setTokenUrl(passwordOAuthFlow.tokenUrl()); + } + return builder.build(); + } + + private static io.a2a.grpc.OpenIdConnectSecurityScheme openIdConnectSecurityScheme(OpenIdConnectSecurityScheme openIdConnectSecurityScheme) { + io.a2a.grpc.OpenIdConnectSecurityScheme.Builder builder = io.a2a.grpc.OpenIdConnectSecurityScheme.newBuilder(); + if (openIdConnectSecurityScheme.getDescription() != null) { + builder.setDescription(openIdConnectSecurityScheme.getDescription()); + } + if (openIdConnectSecurityScheme.getOpenIdConnectUrl() != null) { + builder.setOpenIdConnectUrl(openIdConnectSecurityScheme.getOpenIdConnectUrl()); + } + return builder.build(); + } + + private static io.a2a.grpc.AgentInterface agentInterface(AgentInterface agentInterface) { + io.a2a.grpc.AgentInterface.Builder builder = io.a2a.grpc.AgentInterface.newBuilder(); + if (agentInterface.transport() != null) { + builder.setTransport(agentInterface.transport()); + } + if (agentInterface.url() != null) { + builder.setUrl(agentInterface.url()); + } + return builder.build(); + } + + public static Struct struct(Map map) { + Struct.Builder structBuilder = Struct.newBuilder(); + if (map != null) { + map.forEach((k, v) -> structBuilder.putFields(k, value(v))); + } + return structBuilder.build(); + } + + private static Value value(Object value) { + Value.Builder valueBuilder = Value.newBuilder(); + if (value instanceof String) { + valueBuilder.setStringValue((String) value); + } else if (value instanceof Number) { + valueBuilder.setNumberValue(((Number) value).doubleValue()); + } else if (value instanceof Boolean) { + valueBuilder.setBoolValue((Boolean) value); + } else if (value instanceof Map) { + valueBuilder.setStructValue(struct((Map) value)); + } else if (value instanceof List) { + valueBuilder.setListValue(listValue((List) value)); + } + return valueBuilder.build(); + } + + private static com.google.protobuf.ListValue listValue(List list) { + com.google.protobuf.ListValue.Builder listValueBuilder = com.google.protobuf.ListValue.newBuilder(); + if (list != null) { + list.forEach(o -> listValueBuilder.addValues(value(o))); + } + return listValueBuilder.build(); + } + + public static StreamResponse streamResponse(StreamingEventKind streamingEventKind) { + if (streamingEventKind instanceof TaskStatusUpdateEvent) { + return StreamResponse.newBuilder() + .setStatusUpdate(taskStatusUpdateEvent((TaskStatusUpdateEvent) streamingEventKind)) + .build(); + } else if (streamingEventKind instanceof TaskArtifactUpdateEvent) { + return StreamResponse.newBuilder() + .setArtifactUpdate(taskArtifactUpdateEvent((TaskArtifactUpdateEvent) streamingEventKind)) + .build(); + } else if (streamingEventKind instanceof Message) { + return StreamResponse.newBuilder() + .setMsg(message((Message) streamingEventKind)) + .build(); + } else if (streamingEventKind instanceof Task) { + return StreamResponse.newBuilder() + .setTask(task((Task) streamingEventKind)) + .build(); + } else { + throw new IllegalArgumentException("Unsupported event type: " + streamingEventKind); + } + } + + public static io.a2a.grpc.SendMessageResponse taskOrMessage(EventKind eventKind) { + if (eventKind instanceof Task) { + return io.a2a.grpc.SendMessageResponse.newBuilder() + .setTask(task((Task) eventKind)) + .build(); + } else if (eventKind instanceof Message) { + return io.a2a.grpc.SendMessageResponse.newBuilder() + .setMsg(message((Message) eventKind)) + .build(); + } else { + throw new IllegalArgumentException("Unsupported event type: " + eventKind); + } + } + + + } + + public static class FromProto { + + public static TaskQueryParams taskQueryParams(io.a2a.grpc.GetTaskRequest request) { + String name = request.getName(); + String id = name.substring(name.lastIndexOf('/') + 1); + return new TaskQueryParams(id, request.getHistoryLength()); + } + + public static TaskIdParams taskIdParams(io.a2a.grpc.CancelTaskRequest request) { + String name = request.getName(); + String id = name.substring(name.lastIndexOf('/') + 1); + return new TaskIdParams(id); + } + + public static MessageSendParams messageSendParams(io.a2a.grpc.SendMessageRequest request) { + MessageSendParams.Builder builder = new MessageSendParams.Builder(); + builder.message(message(request.getRequest())); + if (request.hasConfiguration()) { + builder.configuration(messageSendConfiguration(request.getConfiguration())); + } + if (request.hasMetadata()) { + builder.metadata(struct(request.getMetadata())); + } + return builder.build(); + } + + public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + return taskPushNotificationConfig(request.getConfig()); + } + + public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfig config) { + String name = config.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" + String[] parts = name.split("/"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + name); + } + String taskId = parts[1]; + String configId = parts[3]; + PushNotificationConfig pnc = pushNotification(config.getPushNotificationConfig(), configId); + return new TaskPushNotificationConfig(taskId, pnc); + } + + public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { + String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" + String[] parts = name.split("/"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid name format for GetTaskPushNotificationConfigRequest: " + name); + } + String taskId = parts[1]; + String configId = parts[3]; + return new GetTaskPushNotificationConfigParams(taskId, configId); + } + + public static TaskIdParams taskIdParams(io.a2a.grpc.TaskSubscriptionRequest request) { + String name = request.getName(); + String id = name.substring(name.lastIndexOf('/') + 1); + return new TaskIdParams(id); + } + + public static ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigRequest request) { + String parent = request.getParent(); + String id = parent.substring(parent.lastIndexOf('/') + 1); + return new ListTaskPushNotificationConfigParams(id); + } + + public static DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) { + String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" + String[] parts = name.split("/"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid name format for DeleteTaskPushNotificationConfigRequest: " + name); + } + String taskId = parts[1]; + String configId = parts[3]; + return new DeleteTaskPushNotificationConfigParams(taskId, configId); + } + + private static AgentCapabilities agentCapabilities(io.a2a.grpc.AgentCapabilities agentCapabilities) { + return new AgentCapabilities(agentCapabilities.getStreaming(), agentCapabilities.getPushNotifications(), false, + agentCapabilities.getExtensionsList().stream().map(item -> agentExtension(item)).collect(Collectors.toList()) + ); + } + + private static AgentExtension agentExtension(io.a2a.grpc.AgentExtension agentExtension) { + return new AgentExtension( + agentExtension.getDescription(), + struct(agentExtension.getParams()), + agentExtension.getRequired(), + agentExtension.getUri() + ); + } + + private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.SendMessageConfiguration sendMessageConfiguration) { + return new MessageSendConfiguration( + new ArrayList<>(sendMessageConfiguration.getAcceptedOutputModesList()), + sendMessageConfiguration.getHistoryLength(), + pushNotification(sendMessageConfiguration.getPushNotification()), + sendMessageConfiguration.getBlocking() + ); + } + + private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification, String configId) { + return new PushNotificationConfig( + pushNotification.getUrl(), + pushNotification.getToken(), + authenticationInfo(pushNotification.getAuthentication()), + pushNotification.getId().isEmpty() ? configId : pushNotification.getId() + ); + } + + private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification) { + return pushNotification(pushNotification, pushNotification.getId()); + } + + private static PushNotificationAuthenticationInfo authenticationInfo(io.a2a.grpc.AuthenticationInfo authenticationInfo) { + return new PushNotificationAuthenticationInfo( + new ArrayList<>(authenticationInfo.getSchemesList()), + authenticationInfo.getCredentials() + ); + } + + public static Task task(io.a2a.grpc.Task task) { + return new Task( + task.getId(), + task.getContextId(), + taskStatus(task.getStatus()), + task.getArtifactsList().stream().map(item -> artifact(item)).collect(Collectors.toList()), + task.getHistoryList().stream().map(item -> message(item)).collect(Collectors.toList()), + struct(task.getMetadata()) + ); + } + + public static Message message(io.a2a.grpc.Message message) { + return new Message( + role(message.getRole()), + message.getContentList().stream().map(item -> part(item)).collect(Collectors.toList()), + message.getMessageId(), + message.getContextId(), + message.getTaskId(), + null, // referenceTaskIds is not in grpc message + struct(message.getMetadata()) + ); + } + + public static TaskStatusUpdateEvent taskStatusUpdateEvent(io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent) { + return new TaskStatusUpdateEvent.Builder() + .taskId(taskStatusUpdateEvent.getTaskId()) + .status(taskStatus(taskStatusUpdateEvent.getStatus())) + .contextId(taskStatusUpdateEvent.getContextId()) + .isFinal(taskStatusUpdateEvent.getFinal()) + .metadata(struct(taskStatusUpdateEvent.getMetadata())) + .build(); + } + + public static TaskArtifactUpdateEvent taskArtifactUpdateEvent(io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent) { + return new TaskArtifactUpdateEvent.Builder() + .taskId(taskArtifactUpdateEvent.getTaskId()) + .append(taskArtifactUpdateEvent.getAppend()) + .lastChunk(taskArtifactUpdateEvent.getLastChunk()) + .artifact(artifact(taskArtifactUpdateEvent.getArtifact())) + .contextId(taskArtifactUpdateEvent.getContextId()) + .metadata(struct(taskArtifactUpdateEvent.getMetadata())) + .build(); + } + + private static Artifact artifact(io.a2a.grpc.Artifact artifact) { + return new Artifact( + artifact.getArtifactId(), + artifact.getName(), + artifact.getDescription(), + artifact.getPartsList().stream().map(item -> part(item)).collect(Collectors.toList()), + struct(artifact.getMetadata()) + ); + } + + private static Part part(io.a2a.grpc.Part part) { + if (part.hasText()) { + return textPart(part.getText()); + } else if (part.hasFile()) { + return filePart(part.getFile()); + } else if (part.hasData()) { + return dataPart(part.getData()); + } + return null; + } + + private static TextPart textPart(String text) { + return new TextPart(text); + } + + private static FilePart filePart(io.a2a.grpc.FilePart filePart) { + if (filePart.hasFileWithBytes()) { + return new FilePart(new FileWithBytes(filePart.getMimeType(), null, filePart.getFileWithBytes().toStringUtf8())); + } else if (filePart.hasFileWithUri()) { + return new FilePart(new FileWithUri(filePart.getMimeType(), null, filePart.getFileWithUri())); + } + return null; + } + + private static DataPart dataPart(io.a2a.grpc.DataPart dataPart) { + return new DataPart(struct(dataPart.getData())); + } + + private static TaskStatus taskStatus(io.a2a.grpc.TaskStatus taskStatus) { + return new TaskStatus( + taskState(taskStatus.getState()), + message(taskStatus.getUpdate()), + LocalDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) + ); + } + + private static Message.Role role(io.a2a.grpc.Role role) { + if (role == null) { + return null; + } + return switch (role) { + case ROLE_USER -> Message.Role.USER; + case ROLE_AGENT -> Message.Role.AGENT; + default -> null; + }; + } + + private static TaskState taskState(io.a2a.grpc.TaskState taskState) { + if (taskState == null) { + return null; + } + return switch (taskState) { + case TASK_STATE_SUBMITTED -> TaskState.SUBMITTED; + case TASK_STATE_WORKING -> TaskState.WORKING; + case TASK_STATE_INPUT_REQUIRED -> TaskState.INPUT_REQUIRED; + case TASK_STATE_AUTH_REQUIRED -> TaskState.AUTH_REQUIRED; + case TASK_STATE_COMPLETED -> TaskState.COMPLETED; + case TASK_STATE_CANCELLED -> TaskState.CANCELED; + case TASK_STATE_FAILED -> TaskState.FAILED; + case TASK_STATE_REJECTED -> TaskState.REJECTED; + case TASK_STATE_UNSPECIFIED -> null; + case UNRECOGNIZED -> null; + }; + } + + private static Map struct(Struct struct) { + if (struct == null || struct.getFieldsCount() == 0) { + return null; + } + return struct.getFieldsMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> value(e.getValue()))); + } + + private static Object value(Value value) { + switch (value.getKindCase()) { + case STRUCT_VALUE: + return struct(value.getStructValue()); + case LIST_VALUE: + return value.getListValue().getValuesList().stream() + .map(FromProto::value) + .collect(Collectors.toList()); + case BOOL_VALUE: + return value.getBoolValue(); + case NUMBER_VALUE: + return value.getNumberValue(); + case STRING_VALUE: + return value.getStringValue(); + case NULL_VALUE: + default: + return null; + } + } + } + + +} \ No newline at end of file diff --git a/grpc/src/main/resources/META-INF/beans.xml b/grpc/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..be7990041 --- /dev/null +++ b/grpc/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index e34d09477..95898110e 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ + 1.73.0 UTF-8 3.11.0 3.4.2 @@ -51,11 +52,12 @@ 2.0.1 2.1.3 3.1.0 - 5.12.2 + 5.13.4 5.17.0 5.15.0 1.1.1 - 3.22.3 + 4.31.1 + 3.24.5 5.5.1 2.0.17 1.5.18 @@ -69,6 +71,13 @@ + + io.grpc + grpc-bom + ${grpc.version} + pom + import + io.quarkus quarkus-bom @@ -93,6 +102,11 @@ jackson-datatype-jsr310 ${jackson.version} + + com.google.protobuf + protobuf-java + ${protobuf.version} + io.smallrye.reactive mutiny-zero @@ -191,15 +205,6 @@ quarkus-maven-plugin true ${quarkus.platform.version} - - - - build - generate-code - generate-code-tests - - - org.sonatype.central @@ -273,8 +278,10 @@ sdk-server-common common + grpc spec client + reference-grpc reference-impl tck examples/helloworld diff --git a/reference-grpc/pom.xml b/reference-grpc/pom.xml new file mode 100644 index 000000000..d80dae49f --- /dev/null +++ b/reference-grpc/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../pom.xml + + + reference-grpc + Java A2A gRPC Reference Server + Java SDK for the Agent2Agent Protocol (A2A) - A2A gRPC Reference Server (based on Quarkus) + + + + ${project.groupId} + a2a-java-sdk-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-tests-server-common + ${project.version} + provided + + + ${project.groupId} + a2a-java-sdk-tests-server-common + test-jar + test + ${project.version} + + + + io.quarkus + quarkus-grpc + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-junit5 + test + + + org.assertj + assertj-core + 3.25.3 + test + + + com.google.api.grpc + proto-google-common-protos + + + com.google.protobuf + protobuf-java + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + + \ No newline at end of file diff --git a/reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java new file mode 100644 index 000000000..05207321b --- /dev/null +++ b/reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -0,0 +1,27 @@ +package io.a2a.server.grpc.quarkus; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.Task; +import io.a2a.grpc.TaskState; +import io.a2a.grpc.TaskStatus; +import io.a2a.grpc.TaskStatusUpdateEvent; +import io.a2a.server.PublicAgentCard; +import io.a2a.server.requesthandlers.GrpcHandler; +import io.a2a.server.requesthandlers.RequestHandler; +import io.a2a.spec.AgentCard; +import io.grpc.stub.StreamObserver; +import io.quarkus.grpc.GrpcService; + +@GrpcService +public class QuarkusGrpcHandler extends GrpcHandler { + + @Inject + public QuarkusGrpcHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { + super(agentCard, requestHandler); + } +} \ No newline at end of file diff --git a/reference-grpc/src/main/resources/application.properties b/reference-grpc/src/main/resources/application.properties new file mode 100644 index 000000000..f8286bd6f --- /dev/null +++ b/reference-grpc/src/main/resources/application.properties @@ -0,0 +1,5 @@ +quarkus.grpc.clients.a2-a-service.host=localhost +quarkus.grpc.clients.a2-a-service.port=9001 + +# The GrpcHandler @ApplicationScoped annotation is not compatible with Quarkus +quarkus.arc.exclude-types=io.a2a.server.requesthandlers.GrpcHandler \ No newline at end of file diff --git a/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java new file mode 100644 index 000000000..917606e42 --- /dev/null +++ b/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -0,0 +1,132 @@ +package io.a2a.server.grpc.quarkus; + +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; + +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import io.a2a.server.apps.common.TestUtilsBean; +import io.a2a.server.requesthandlers.GrpcHandler; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.util.Utils; + +@Path("/test") +@ApplicationScoped +public class A2ATestResource { + @Inject + TestUtilsBean testUtilsBean; + + private final AtomicInteger streamingSubscribedCount = new AtomicInteger(0); + + @PostConstruct + public void init() { + GrpcHandler.setStreamingSubscribedRunnable(streamingSubscribedCount::incrementAndGet); + } + + + @POST + @Path("/task") + @Consumes(MediaType.APPLICATION_JSON) + public Response saveTask(String body) throws Exception { + Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class); + testUtilsBean.saveTask(task); + return Response.ok().build(); + } + + @GET + @Path("/task/{taskId}") + public Response getTask(@PathParam("taskId") String taskId) throws Exception { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + return Response.status(404).build(); + } + return Response.ok() + .entity(Utils.OBJECT_MAPPER.writeValueAsString(task)) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .build(); + } + + @DELETE + @Path("/task/{taskId}") + public Response deleteTask(@PathParam("taskId") String taskId) { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + return Response.status(404).build(); + } + testUtilsBean.deleteTask(taskId); + return Response.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .build(); + } + + @POST + @Path("/queue/ensure/{taskId}") + public Response ensureQueue(@PathParam("taskId") String taskId) { + testUtilsBean.ensureQueue(taskId); + return Response.ok().build(); + } + + @POST + @Path("/queue/enqueueTaskStatusUpdateEvent/{taskId}") + public Response enqueueTaskStatusUpdateEvent(@PathParam("taskId") String taskId, String body) throws Exception { + TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class); + testUtilsBean.enqueueEvent(taskId, event); + return Response.ok().build(); + } + + @POST + @Path("/queue/enqueueTaskArtifactUpdateEvent/{taskId}") + public Response enqueueTaskArtifactUpdateEvent(@PathParam("taskId") String taskId, String body) throws Exception { + TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class); + testUtilsBean.enqueueEvent(taskId, event); + return Response.ok().build(); + } + + @GET + @Path("/streamingSubscribedCount") + @Produces(TEXT_PLAIN) + public Response getStreamingSubscribedCount() { + return Response.ok(String.valueOf(streamingSubscribedCount.get()), TEXT_PLAIN).build(); + } + + @DELETE + @Path("/task/{taskId}/config/{configId}") + public Response deleteTaskPushNotificationConfig(@PathParam("taskId") String taskId, @PathParam("configId") String configId) { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + return Response.status(404).build(); + } + testUtilsBean.deleteTaskPushNotificationConfig(taskId, configId); + return Response.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .build(); + } + + @POST + @Path("/task/{taskId}") + @Consumes(MediaType.APPLICATION_JSON) + public Response savePushNotificationConfigInStore(@PathParam("taskId") String taskId, String body) throws Exception { + PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class); + if (notificationConfig == null) { + return Response.status(404).build(); + } + testUtilsBean.saveTaskPushNotificationConfig(taskId, notificationConfig); + return Response.ok().build(); + } +} diff --git a/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java new file mode 100644 index 000000000..076d9a29b --- /dev/null +++ b/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -0,0 +1,1245 @@ +package io.a2a.server.grpc.quarkus; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.CancelTaskRequest; +import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; +import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskRequest; +import io.a2a.grpc.ListTaskPushNotificationConfigRequest; +import io.a2a.grpc.ListTaskPushNotificationConfigResponse; +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.TaskSubscriptionRequest; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.grpc.GetAgentCardRequest; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Artifact; +import io.a2a.spec.Event; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import io.a2a.util.Utils; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.quarkus.grpc.GrpcClient; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class QuarkusA2AGrpcTest { + + + private static final Task MINIMAL_TASK = new Task.Builder() + .id("task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + private static final Task CANCEL_TASK = new Task.Builder() + .id("cancel-task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + private static final Task CANCEL_TASK_NOT_SUPPORTED = new Task.Builder() + .id("cancel-task-not-supported-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + private static final Task SEND_MESSAGE_NOT_SUPPORTED = new Task.Builder() + .id("task-not-supported-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + private static final Message MESSAGE = new Message.Builder() + .messageId("111") + .role(Message.Role.AGENT) + .parts(new TextPart("test message")) + .build(); + public static final String APPLICATION_JSON = "application/json"; + + @GrpcClient("a2-a-service") + A2AServiceGrpc.A2AServiceBlockingStub client; + + private final int serverPort = 8081; + @Test + public void testTaskStoreMethodsSanityTest() throws Exception { + Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); + saveTaskInTaskStore(task); + Task saved = getTaskFromTaskStore(task.getId()); + assertEquals(task.getId(), saved.getId()); + assertEquals(task.getContextId(), saved.getContextId()); + assertEquals(task.getStatus().state(), saved.getStatus().state()); + + deleteTaskInTaskStore(task.getId()); + Task saved2 = getTaskFromTaskStore(task.getId()); + assertNull(saved2); + } + + @Test + public void testGetTaskSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + GetTaskRequest request = GetTaskRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + io.a2a.grpc.Task response = client.getTask(request); + assertEquals("task-123", response.getId()); + assertEquals("session-xyz", response.getContextId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED, response.getStatus().getState()); + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testGetTaskNotFound() throws Exception { + assertTrue(getTaskFromTaskStore("non-existent-task") == null); + GetTaskRequest request = GetTaskRequest.newBuilder() + .setName("tasks/non-existent-task") + .build(); + try { + client.getTask(request); + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("TaskNotFoundError")); + } + } + + @Test + public void testCancelTaskSuccess() throws Exception { + saveTaskInTaskStore(CANCEL_TASK); + try { + CancelTaskRequest request = CancelTaskRequest.newBuilder() + .setName("tasks/" + CANCEL_TASK.getId()) + .build(); + io.a2a.grpc.Task response = client.cancelTask(request); + assertEquals(CANCEL_TASK.getId(), response.getId()); + assertEquals(CANCEL_TASK.getContextId(), response.getContextId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_CANCELLED, response.getStatus().getState()); + } finally { + deleteTaskInTaskStore(CANCEL_TASK.getId()); + } + } + + @Test + public void testCancelTaskNotFound() throws Exception { + CancelTaskRequest request = CancelTaskRequest.newBuilder() + .setName("tasks/non-existent-task") + .build(); + try { + client.cancelTask(request); + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("TaskNotFoundError")); + } + } + + @Test + public void testCancelTaskNotSupported() throws Exception { + saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); + try { + CancelTaskRequest request = CancelTaskRequest.newBuilder() + .setName("tasks/" + CANCEL_TASK_NOT_SUPPORTED.getId()) + .build(); + try { + client.cancelTask(request); + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + assertEquals(Status.UNIMPLEMENTED.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("UnsupportedOperationError")); + } + } finally { + deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); + } + } + + @Test + public void testSendMessageNewMessageSuccess() throws Exception { + assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(ProtoUtils.ToProto.message(message)) + .build(); + SendMessageResponse response = client.sendMessage(request); + assertTrue(response.hasMsg()); + io.a2a.grpc.Message grpcMessage = response.getMsg(); + // Convert back to spec Message for easier assertions + Message messageResponse = ProtoUtils.FromProto.message(grpcMessage); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + } + + @Test + public void testSendMessageExistingTaskSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(ProtoUtils.ToProto.message(message)) + .build(); + SendMessageResponse response = client.sendMessage(request); + + assertTrue(response.hasMsg()); + io.a2a.grpc.Message grpcMessage = response.getMsg(); + // Convert back to spec Message for easier assertions + Message messageResponse = ProtoUtils.FromProto.message(grpcMessage); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testError() throws Exception { + Message message = new Message.Builder(MESSAGE) + .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId()) + .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId()) + .build(); + + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(ProtoUtils.ToProto.message(message)) + .build(); + + try { + client.sendMessage(request); + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + assertEquals(Status.UNIMPLEMENTED.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("UnsupportedOperationError")); + } + } + + @Test + public void testGetAgentCard() throws Exception { + // Test gRPC getAgentCard method + GetAgentCardRequest request = GetAgentCardRequest.newBuilder().build(); + + io.a2a.grpc.AgentCard grpcAgentCard = client.getAgentCard(request); + + // Verify the expected agent card fields directly on the gRPC response + assertNotNull(grpcAgentCard); + assertEquals("test-card", grpcAgentCard.getName()); + assertEquals("A test agent card", grpcAgentCard.getDescription()); + assertEquals("http://localhost:" + serverPort, grpcAgentCard.getUrl()); // Use dynamic port + assertEquals("1.0", grpcAgentCard.getVersion()); + assertEquals("http://example.com/docs", grpcAgentCard.getDocumentationUrl()); + assertTrue(grpcAgentCard.getCapabilities().getPushNotifications()); + assertTrue(grpcAgentCard.getCapabilities().getStreaming()); + // Note: stateTransitionHistory is not present in gRPC AgentCapabilities + assertTrue(grpcAgentCard.getSkillsList().isEmpty()); + } + + @Test + public void testGetExtendAgentCardNotSupported() { + // NOTE: This test is not applicable to gRPC since extended agent card retrieval + // is an HTTP/REST-specific feature that tests the /agent/authenticatedExtendedCard endpoint. + // gRPC handles agent capabilities differently through service definitions. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testMalformedJSONRPCRequest() { + // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific + // JSON parsing errors. gRPC uses Protocol Buffers for serialization and has its own + // parsing and validation mechanisms. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testInvalidParamsJSONRPCRequest() { + // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific + // parameter validation errors. gRPC uses strongly-typed Protocol Buffer messages + // which provide built-in type safety and validation. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testInvalidJSONRPCRequestMissingJsonrpc() { + // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific + // validation of the "jsonrpc" field. gRPC does not use JSON-RPC protocol elements. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testInvalidJSONRPCRequestMissingMethod() { + // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific + // validation of the "method" field. gRPC methods are defined in the service definition + // and invoked directly, not through JSON-RPC method names. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testInvalidJSONRPCRequestInvalidId() { + // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific + // validation of the "id" field. gRPC handles request/response correlation differently + // through its streaming mechanisms. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testInvalidJSONRPCRequestNonExistentMethod() { + // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific + // method not found errors. gRPC method resolution is handled at the service definition + // level and unknown methods result in different error types. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testNonStreamingMethodWithAcceptHeader() throws Exception { + // NOTE: This test is not applicable to gRPC since HTTP Accept headers + // are an HTTP/REST-specific concept and do not apply to gRPC protocol. + // gRPC uses Protocol Buffers for message encoding and doesn't use HTTP content negotiation. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testSetPushNotificationSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // Create a PushNotificationConfig with an ID (needed for gRPC conversion) + PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) // Using task ID as config ID for simplicity + .build(); + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig); + + CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId(MINIMAL_TASK.getId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig)) + .build(); + + io.a2a.grpc.TaskPushNotificationConfig response = client.createTaskPushNotificationConfig(request); + + // Convert back to spec for easier assertions + TaskPushNotificationConfig responseConfig = ProtoUtils.FromProto.taskPushNotificationConfig(response); + assertEquals(MINIMAL_TASK.getId(), responseConfig.taskId()); + assertEquals("http://example.com", responseConfig.pushNotificationConfig().url()); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testGetPushNotificationSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // First, create a push notification config (same as previous test) + PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build(); + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig); + + CreateTaskPushNotificationConfigRequest createRequest = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId(MINIMAL_TASK.getId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig)) + .build(); + + io.a2a.grpc.TaskPushNotificationConfig createResponse = client.createTaskPushNotificationConfig(createRequest); + assertNotNull(createResponse); + + // Now, get the push notification config + GetTaskPushNotificationConfigRequest getRequest = GetTaskPushNotificationConfigRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId()) + .build(); + + io.a2a.grpc.TaskPushNotificationConfig getResponse = client.getTaskPushNotificationConfig(getRequest); + + // Convert back to spec for easier assertions + TaskPushNotificationConfig responseConfig = ProtoUtils.FromProto.taskPushNotificationConfig(getResponse); + assertEquals(MINIMAL_TASK.getId(), responseConfig.taskId()); + assertEquals("http://example.com", responseConfig.pushNotificationConfig().url()); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testListPushNotificationConfigWithConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // Create first push notification config + PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config1") + .build(); + TaskPushNotificationConfig taskPushConfig1 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); + + CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId("config1") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) + .build(); + client.createTaskPushNotificationConfig(createRequest1); + + // Create second push notification config + PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config2") + .build(); + TaskPushNotificationConfig taskPushConfig2 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); + + CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId("config2") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) + .build(); + client.createTaskPushNotificationConfig(createRequest2); + + // Now, list all push notification configs for the task + ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); + + // Verify the response + assertEquals(2, listResponse.getConfigsCount()); + + // Convert back to spec for easier assertions + TaskPushNotificationConfig config1 = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse.getConfigs(0)); + TaskPushNotificationConfig config2 = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse.getConfigs(1)); + + assertEquals(MINIMAL_TASK.getId(), config1.taskId()); + assertEquals("http://example.com", config1.pushNotificationConfig().url()); + assertEquals("config1", config1.pushNotificationConfig().id()); + + assertEquals(MINIMAL_TASK.getId(), config2.taskId()); + assertEquals("http://example.com", config2.pushNotificationConfig().url()); + assertEquals("config2", config2.pushNotificationConfig().id()); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testListPushNotificationConfigWithoutConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // Create first push notification config without explicit ID (will use task ID as default) + PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() + .url("http://1.example.com") + .id(MINIMAL_TASK.getId()) // Use task ID as config ID + .build(); + TaskPushNotificationConfig taskPushConfig1 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); + + CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId(MINIMAL_TASK.getId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) + .build(); + client.createTaskPushNotificationConfig(createRequest1); + + // Create second push notification config with same ID (will overwrite the previous one) + PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() + .url("http://2.example.com") + .id(MINIMAL_TASK.getId()) // Same ID, will overwrite + .build(); + TaskPushNotificationConfig taskPushConfig2 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); + + CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId(MINIMAL_TASK.getId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) + .build(); + client.createTaskPushNotificationConfig(createRequest2); + + // Now, list all push notification configs for the task + ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); + + // Verify only 1 config exists (second one overwrote the first) + assertEquals(1, listResponse.getConfigsCount()); + + // Convert back to spec for easier assertions + TaskPushNotificationConfig config = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse.getConfigs(0)); + + assertEquals(MINIMAL_TASK.getId(), config.taskId()); + assertEquals("http://2.example.com", config.pushNotificationConfig().url()); + assertEquals(MINIMAL_TASK.getId(), config.pushNotificationConfig().id()); + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testListPushNotificationConfigTaskNotFound() throws Exception { + ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/non-existent-task") + .build(); + + try { + client.listTaskPushNotificationConfig(listRequest); + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("TaskNotFoundError")); + } + } + + @Test + public void testListPushNotificationConfigEmptyList() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // List configs for a task that has no configs + ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); + + // Verify empty list + assertEquals(0, listResponse.getConfigsCount()); + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testDeletePushNotificationConfigWithValidConfigId() throws Exception { + // Create a second task for testing cross-task isolation + Task secondTask = new Task.Builder() + .id("task-456") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + saveTaskInTaskStore(MINIMAL_TASK); + saveTaskInTaskStore(secondTask); + try { + // Create config1 and config2 for MINIMAL_TASK + PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config1") + .build(); + TaskPushNotificationConfig taskPushConfig1 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); + + CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId("config1") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) + .build(); + client.createTaskPushNotificationConfig(createRequest1); + + PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config2") + .build(); + TaskPushNotificationConfig taskPushConfig2 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); + + CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId("config2") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) + .build(); + client.createTaskPushNotificationConfig(createRequest2); + + // Create config1 for secondTask + TaskPushNotificationConfig taskPushConfig3 = + new TaskPushNotificationConfig(secondTask.getId(), pushConfig1); + + CreateTaskPushNotificationConfigRequest createRequest3 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + secondTask.getId()) + .setConfigId("config1") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig3)) + .build(); + client.createTaskPushNotificationConfig(createRequest3); + + // Delete config1 from MINIMAL_TASK + DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/config1") + .build(); + + com.google.protobuf.Empty deleteResponse = client.deleteTaskPushNotificationConfig(deleteRequest); + assertNotNull(deleteResponse); // Should return Empty, not null + + // Verify MINIMAL_TASK now has only 1 config (config2) + ListTaskPushNotificationConfigRequest listRequest1 = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + ListTaskPushNotificationConfigResponse listResponse1 = client.listTaskPushNotificationConfig(listRequest1); + assertEquals(1, listResponse1.getConfigsCount()); + + TaskPushNotificationConfig remainingConfig = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse1.getConfigs(0)); + assertEquals("config2", remainingConfig.pushNotificationConfig().id()); + + // Verify secondTask remains unchanged (still has config1) + ListTaskPushNotificationConfigRequest listRequest2 = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + secondTask.getId()) + .build(); + ListTaskPushNotificationConfigResponse listResponse2 = client.listTaskPushNotificationConfig(listRequest2); + assertEquals(1, listResponse2.getConfigsCount()); + + TaskPushNotificationConfig secondTaskConfig = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse2.getConfigs(0)); + assertEquals("config1", secondTaskConfig.pushNotificationConfig().id()); + + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deletePushNotificationConfigInStore(secondTask.getId(), "config1"); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(secondTask.getId()); + } + } + + @Test + public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // Create config1 and config2 + PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config1") + .build(); + TaskPushNotificationConfig taskPushConfig1 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); + + CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId("config1") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) + .build(); + client.createTaskPushNotificationConfig(createRequest1); + + PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() + .url("http://example.com") + .id("config2") + .build(); + TaskPushNotificationConfig taskPushConfig2 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); + + CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId("config2") + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) + .build(); + client.createTaskPushNotificationConfig(createRequest2); + + // Try to delete non-existent config (should succeed silently) + DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/non-existent-config-id") + .build(); + + com.google.protobuf.Empty deleteResponse = client.deleteTaskPushNotificationConfig(deleteRequest); + assertNotNull(deleteResponse); // Should return Empty, not throw error + + // Verify both configs remain unchanged + ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); + assertEquals(2, listResponse.getConfigsCount()); + + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testDeletePushNotificationConfigTaskNotFound() throws Exception { + DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName("tasks/non-existent-task/pushNotificationConfigs/non-existent-config-id") + .build(); + + try { + client.deleteTaskPushNotificationConfig(deleteRequest); + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("TaskNotFoundError")); + } + } + + @Test + public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // Create first config without explicit ID (will use task ID as default) + PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() + .url("http://1.example.com") + .id(MINIMAL_TASK.getId()) + .build(); + TaskPushNotificationConfig taskPushConfig1 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); + + CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId(MINIMAL_TASK.getId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) + .build(); + client.createTaskPushNotificationConfig(createRequest1); + + // Create second config with same ID (will overwrite the previous one) + PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() + .url("http://2.example.com") + .id(MINIMAL_TASK.getId()) + .build(); + TaskPushNotificationConfig taskPushConfig2 = + new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); + + CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .setConfigId(MINIMAL_TASK.getId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) + .build(); + client.createTaskPushNotificationConfig(createRequest2); + + // Delete the config using task ID + DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId()) + .build(); + + com.google.protobuf.Empty deleteResponse = client.deleteTaskPushNotificationConfig(deleteRequest); + assertNotNull(deleteResponse); // Should return Empty + + // Verify no configs remain + ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); + assertEquals(0, listResponse.getConfigsCount()); + + } finally { + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testSendMessageStreamExistingTaskSuccess() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // Build message for existing task + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + + // Create gRPC streaming request + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(ProtoUtils.ToProto.message(message)) + .build(); + + // Use blocking iterator to consume stream responses + java.util.Iterator responseIterator = client.sendStreamingMessage(request); + + // Collect responses - expect at least one + java.util.List responses = new java.util.ArrayList<>(); + while (responseIterator.hasNext()) { + StreamResponse response = responseIterator.next(); + responses.add(response); + + // For this test, we expect to get the message back - stop after first response + if (response.hasMsg()) { + break; + } + } + + // Verify we got at least one response + assertTrue(responses.size() >= 1, "Expected at least one response from streaming call"); + + // Find the message response + StreamResponse messageResponse = null; + for (StreamResponse response : responses) { + if (response.hasMsg()) { + messageResponse = response; + break; + } + } + + assertNotNull(messageResponse, "Expected to receive a message response"); + + // Verify the message content + io.a2a.grpc.Message grpcMessage = messageResponse.getMsg(); + Message responseMessage = ProtoUtils.FromProto.message(grpcMessage); + assertEquals(MESSAGE.getMessageId(), responseMessage.getMessageId()); + assertEquals(MESSAGE.getRole(), responseMessage.getRole()); + Part part = responseMessage.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + @Test + public void testStreamingMethodWithAcceptHeader() throws Exception { + // NOTE: This test is not applicable to gRPC since HTTP Accept headers + // are an HTTP/REST-specific concept and do not apply to gRPC protocol. + // gRPC uses Protocol Buffers for message encoding and doesn't use HTTP content negotiation. + + // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest + // for future migration when extending that base class. + } + + @Test + public void testSendMessageStreamNewMessageSuccess() throws Exception { + // Ensure no task exists initially (test creates new task via streaming) + assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null, "Task should not exist initially"); + + try { + // Build message for new task (no pre-existing task) + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + + // Create gRPC streaming request + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(ProtoUtils.ToProto.message(message)) + .build(); + + // Use blocking iterator to consume stream responses + java.util.Iterator responseIterator = client.sendStreamingMessage(request); + + // Collect responses - expect at least one + java.util.List responses = new java.util.ArrayList<>(); + while (responseIterator.hasNext()) { + StreamResponse response = responseIterator.next(); + responses.add(response); + + // For this test, we expect to get the message back - stop after first response + if (response.hasMsg()) { + break; + } + } + + // Verify we got at least one response + assertTrue(responses.size() >= 1, "Expected at least one response from streaming call"); + + // Find the message response + StreamResponse messageResponse = null; + for (StreamResponse response : responses) { + if (response.hasMsg()) { + messageResponse = response; + break; + } + } + + assertNotNull(messageResponse, "Expected to receive a message response"); + + // Verify the message content + io.a2a.grpc.Message grpcMessage = messageResponse.getMsg(); + Message responseMessage = ProtoUtils.FromProto.message(grpcMessage); + assertEquals(MESSAGE.getMessageId(), responseMessage.getMessageId()); + assertEquals(MESSAGE.getRole(), responseMessage.getRole()); + Part part = responseMessage.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + + } finally { + // Clean up any task that may have been created (ignore if task doesn't exist) + try { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } catch (RuntimeException e) { + // Ignore if task doesn't exist (404 error) + if (!e.getMessage().contains("404")) { + throw e; + } + } + } + } + + @Test + public void testResubscribeExistingTaskSuccess() throws Exception { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + saveTaskInTaskStore(MINIMAL_TASK); + + try { + // Ensure queue for task exists (required for resubscription) + ensureQueueForTask(MINIMAL_TASK.getId()); + + CountDownLatch taskResubscriptionRequestSent = new CountDownLatch(1); + CountDownLatch taskResubscriptionResponseReceived = new CountDownLatch(2); + AtomicReference firstResponse = new AtomicReference<>(); + AtomicReference secondResponse = new AtomicReference<>(); + + // Create gRPC task subscription request + TaskSubscriptionRequest subscriptionRequest = TaskSubscriptionRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + + // Count down the latch when the gRPC streaming subscription is established + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> taskResubscriptionRequestSent.countDown()); + + AtomicReference errorRef = new AtomicReference<>(); + + // Start the subscription in a separate thread + executorService.submit(() -> { + try { + java.util.Iterator responseIterator = client.taskSubscription(subscriptionRequest); + + while (responseIterator.hasNext()) { + StreamResponse response = responseIterator.next(); + + if (taskResubscriptionResponseReceived.getCount() == 2) { + firstResponse.set(response); + } else { + secondResponse.set(response); + } + taskResubscriptionResponseReceived.countDown(); + + if (taskResubscriptionResponseReceived.getCount() == 0) { + break; + } + } + } catch (Exception e) { + errorRef.set(e); + // Count down both latches to unblock the test + taskResubscriptionRequestSent.countDown(); + while (taskResubscriptionResponseReceived.getCount() > 0) { + taskResubscriptionResponseReceived.countDown(); + } + } + }); + + // Wait for subscription to be established + assertTrue(taskResubscriptionRequestSent.await(10, TimeUnit.SECONDS), "Subscription should be established"); + + // Inject events into the server's event queue + java.util.List events = java.util.List.of( + new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + new TaskStatusUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build()); + + for (Event event : events) { + enqueueEventOnServer(event); + } + + // Wait for the client to receive the responses + assertTrue(taskResubscriptionResponseReceived.await(20, TimeUnit.SECONDS), "Should receive both responses"); + + // Check for errors + if (errorRef.get() != null) { + throw new RuntimeException("Error in subscription thread", errorRef.get()); + } + + // Verify first response (TaskArtifactUpdateEvent) + assertNotNull(firstResponse.get(), "Should receive first response"); + StreamResponse firstStreamResponse = firstResponse.get(); + assertTrue(firstStreamResponse.hasArtifactUpdate(), "First response should be artifact update"); + + io.a2a.grpc.TaskArtifactUpdateEvent artifactUpdate = firstStreamResponse.getArtifactUpdate(); + assertEquals(MINIMAL_TASK.getId(), artifactUpdate.getTaskId()); + assertEquals(MINIMAL_TASK.getContextId(), artifactUpdate.getContextId()); + assertEquals("11", artifactUpdate.getArtifact().getArtifactId()); + assertEquals("text", artifactUpdate.getArtifact().getParts(0).getText()); + + // Verify second response (TaskStatusUpdateEvent) + assertNotNull(secondResponse.get(), "Should receive second response"); + StreamResponse secondStreamResponse = secondResponse.get(); + assertTrue(secondStreamResponse.hasStatusUpdate(), "Second response should be status update"); + + io.a2a.grpc.TaskStatusUpdateEvent statusUpdate = secondStreamResponse.getStatusUpdate(); + assertEquals(MINIMAL_TASK.getId(), statusUpdate.getTaskId()); + assertEquals(MINIMAL_TASK.getContextId(), statusUpdate.getContextId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_COMPLETED, statusUpdate.getStatus().getState()); + assertTrue(statusUpdate.getFinal(), "Final status update should be marked as final"); + + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + executorService.shutdown(); + if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } + } + + @Test + public void testResubscribeNoExistingTaskError() throws Exception { + // Try to resubscribe to a non-existent task - should get TaskNotFoundError + TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + .setName("tasks/non-existent-task") + .build(); + + try { + // Use blocking iterator to consume stream responses + java.util.Iterator responseIterator = client.taskSubscription(request); + + // Try to get first response - should throw StatusRuntimeException + if (responseIterator.hasNext()) { + responseIterator.next(); + } + + // Should not reach here + assertTrue(false, "Expected StatusRuntimeException but method returned normally"); + } catch (StatusRuntimeException e) { + // Verify this is a TaskNotFoundError mapped to NOT_FOUND status + assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); + String description = e.getStatus().getDescription(); + assertTrue(description != null && description.contains("TaskNotFoundError")); + } + } + + + protected void saveTaskInTaskStore(Task task) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/task")) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(task))) + .header("Content-Type", APPLICATION_JSON) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException( + String.format("Saving task failed! Status: %d, Body: %s", response.statusCode(), response.body())); + } + } + + protected Task getTaskFromTaskStore(String taskId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() == 404) { + return null; + } + if (response.statusCode() != 200) { + throw new RuntimeException(String.format("Getting task failed! Status: %d, Body: %s", response.statusCode(), response.body())); + } + return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); + } + + protected void deleteTaskInTaskStore(String taskId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(("http://localhost:" + serverPort + "/test/task/" + taskId))) + .DELETE() + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); + } + } + + protected void ensureQueueForTask(String taskId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/queue/ensure/" + taskId)) + .POST(HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(String.format("Ensuring queue failed! Status: %d, Body: %s", response.statusCode(), response.body())); + } + } + + protected void enqueueEventOnServer(Event event) throws Exception { + String path; + if (event instanceof TaskArtifactUpdateEvent e) { + path = "test/queue/enqueueTaskArtifactUpdateEvent/" + e.getTaskId(); + } else if (event instanceof TaskStatusUpdateEvent e) { + path = "test/queue/enqueueTaskStatusUpdateEvent/" + e.getTaskId(); + } else { + throw new RuntimeException("Unknown event type " + event.getClass() + ". If you need the ability to" + + " handle more types, please add the REST endpoints."); + } + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/" + path)) + .header("Content-Type", APPLICATION_JSON) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(event))) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Queueing event failed!" + response.body()); + } + } + + private CompletableFuture awaitStreamingSubscription() { + int cnt = getStreamingSubscribedCount(); + AtomicInteger initialCount = new AtomicInteger(cnt); + + return CompletableFuture.runAsync(() -> { + try { + boolean done = false; + long end = System.currentTimeMillis() + 15000; + while (System.currentTimeMillis() < end) { + int count = getStreamingSubscribedCount(); + if (count > initialCount.get()) { + done = true; + break; + } + Thread.sleep(500); + } + if (!done) { + throw new RuntimeException("Timed out waiting for subscription"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted"); + } + }); + } + + private int getStreamingSubscribedCount() { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/streamingSubscribedCount")) + .GET() + .build(); + try { + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + String body = response.body().trim(); + return Integer.parseInt(body); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + protected void deletePushNotificationConfigInStore(String taskId, String configId) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(("http://localhost:" + serverPort + "/test/task/" + taskId + "/config/" + configId))) + .DELETE() + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); + } + } + + protected void savePushNotificationConfigInStore(String taskId, PushNotificationConfig notificationConfig) throws Exception { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(notificationConfig))) + .header("Content-Type", APPLICATION_JSON) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + if (response.statusCode() != 200) { + throw new RuntimeException(response.statusCode() + ": Creating task push notification config failed! " + response.body()); + } + } + +} diff --git a/sdk-server-common/pom.xml b/sdk-server-common/pom.xml index 8fc372323..a0ed10cca 100644 --- a/sdk-server-common/pom.xml +++ b/sdk-server-common/pom.xml @@ -27,6 +27,11 @@ a2a-java-sdk-client ${project.version} + + ${project.groupId} + a2a-java-sdk-grpc + ${project.version} + com.fasterxml.jackson.core jackson-databind @@ -94,6 +99,11 @@ logback-classic test + + io.grpc + grpc-testing + test + \ No newline at end of file diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java new file mode 100644 index 000000000..ef173ba0d --- /dev/null +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java @@ -0,0 +1,8 @@ +package io.a2a.server.requesthandlers; + +import io.a2a.server.ServerCallContext; +import io.grpc.stub.StreamObserver; + +public interface CallContextFactory { + ServerCallContext create(StreamObserver responseObserver); +} diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java new file mode 100644 index 000000000..e514feae7 --- /dev/null +++ b/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java @@ -0,0 +1,394 @@ +package io.a2a.server.requesthandlers; + +import static io.a2a.grpc.utils.ProtoUtils.FromProto; +import static io.a2a.grpc.utils.ProtoUtils.ToProto; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Flow; + +import com.google.protobuf.Empty; +import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.StreamResponse; +import io.a2a.server.PublicAgentCard; +import io.a2a.server.ServerCallContext; +import io.a2a.server.auth.UnauthenticatedUser; +import io.a2a.server.auth.User; +import io.a2a.spec.AgentCard; +import io.a2a.spec.ContentTypeNotSupportedError; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.InternalError; +import io.a2a.spec.InvalidAgentResponseError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskNotFoundError; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.UnsupportedOperationError; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; + +import java.util.HashMap; +import java.util.Map; + +@ApplicationScoped +public class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { + + private AgentCard agentCard; + private RequestHandler requestHandler; + + // Hook so testing can wait until streaming subscriptions are established. + // Without this we get intermittent failures + private static volatile Runnable streamingSubscribedRunnable; + + @Inject + Instance callContextFactory; + + protected GrpcHandler() { + } + + @Inject + public GrpcHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { + this.agentCard = agentCard; + this.requestHandler = requestHandler; + } + + @Override + public void sendMessage(io.a2a.grpc.SendMessageRequest request, + StreamObserver responseObserver) { + try { + ServerCallContext context = createCallContext(responseObserver); + MessageSendParams params = FromProto.messageSendParams(request); + EventKind taskOrMessage = requestHandler.onMessageSend(params, context); + io.a2a.grpc.SendMessageResponse response = ToProto.taskOrMessage(taskOrMessage); + responseObserver.onNext(response); + responseObserver.onCompleted(); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void getTask(io.a2a.grpc.GetTaskRequest request, + StreamObserver responseObserver) { + try { + ServerCallContext context = createCallContext(responseObserver); + TaskQueryParams params = FromProto.taskQueryParams(request); + Task task = requestHandler.onGetTask(params, context); + if (task != null) { + responseObserver.onNext(ToProto.task(task)); + responseObserver.onCompleted(); + } else { + handleError(responseObserver, new TaskNotFoundError()); + } + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void cancelTask(io.a2a.grpc.CancelTaskRequest request, + StreamObserver responseObserver) { + try { + ServerCallContext context = createCallContext(responseObserver); + TaskIdParams params = FromProto.taskIdParams(request); + Task task = requestHandler.onCancelTask(params, context); + if (task != null) { + responseObserver.onNext(ToProto.task(task)); + responseObserver.onCompleted(); + } else { + handleError(responseObserver, new TaskNotFoundError()); + } + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, + StreamObserver responseObserver) { + if (! agentCard.capabilities().pushNotifications()) { + handleError(responseObserver, new PushNotificationNotSupportedError()); + return; + } + + try { + ServerCallContext context = createCallContext(responseObserver); + TaskPushNotificationConfig config = FromProto.taskPushNotificationConfig(request); + TaskPushNotificationConfig responseConfig = requestHandler.onSetTaskPushNotificationConfig(config, context); + responseObserver.onNext(ToProto.taskPushNotificationConfig(responseConfig)); + responseObserver.onCompleted(); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request, + StreamObserver responseObserver) { + if (! agentCard.capabilities().pushNotifications()) { + handleError(responseObserver, new PushNotificationNotSupportedError()); + return; + } + + try { + ServerCallContext context = createCallContext(responseObserver); + GetTaskPushNotificationConfigParams params = FromProto.getTaskPushNotificationConfigParams(request); + TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(params, context); + responseObserver.onNext(ToProto.taskPushNotificationConfig(config)); + responseObserver.onCompleted(); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request, + StreamObserver responseObserver) { + if (! agentCard.capabilities().pushNotifications()) { + handleError(responseObserver, new PushNotificationNotSupportedError()); + return; + } + + try { + ServerCallContext context = createCallContext(responseObserver); + ListTaskPushNotificationConfigParams params = FromProto.listTaskPushNotificationConfigParams(request); + List configList = requestHandler.onListTaskPushNotificationConfig(params, context); + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = + io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); + for (TaskPushNotificationConfig config : configList) { + responseBuilder.addConfigs(ToProto.taskPushNotificationConfig(config)); + } + responseObserver.onNext(responseBuilder.build()); + responseObserver.onCompleted(); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, + StreamObserver responseObserver) { + if (! agentCard.capabilities().streaming()) { + handleError(responseObserver, new InvalidRequestError()); + return; + } + + try { + ServerCallContext context = createCallContext(responseObserver); + MessageSendParams params = FromProto.messageSendParams(request); + Flow.Publisher publisher = requestHandler.onMessageSendStream(params, context); + convertToStreamResponse(publisher, responseObserver); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, + StreamObserver responseObserver) { + if (! agentCard.capabilities().streaming()) { + handleError(responseObserver, new InvalidRequestError()); + return; + } + + try { + ServerCallContext context = createCallContext(responseObserver); + TaskIdParams params = FromProto.taskIdParams(request); + Flow.Publisher publisher = requestHandler.onResubscribeToTask(params, context); + convertToStreamResponse(publisher, responseObserver); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + private void convertToStreamResponse(Flow.Publisher publisher, + StreamObserver responseObserver) { + CompletableFuture.runAsync(() -> { + publisher.subscribe(new Flow.Subscriber() { + private Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + + // Notify tests that we are subscribed + Runnable runnable = streamingSubscribedRunnable; + if (runnable != null) { + runnable.run(); + } + } + + @Override + public void onNext(StreamingEventKind event) { + StreamResponse response = ToProto.streamResponse(event); + responseObserver.onNext(response); + if (response.hasStatusUpdate() && response.getStatusUpdate().getFinal()) { + responseObserver.onCompleted(); + } else { + subscription.request(1); + } + } + + @Override + public void onError(Throwable throwable) { + if (throwable instanceof JSONRPCError jsonrpcError) { + handleError(responseObserver, jsonrpcError); + } else { + handleInternalError(responseObserver, throwable); + } + responseObserver.onCompleted(); + } + + @Override + public void onComplete() { + responseObserver.onCompleted(); + } + }); + }); + } + + @Override + public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, + StreamObserver responseObserver) { + try { + responseObserver.onNext(ToProto.agentCard(agentCard)); + responseObserver.onCompleted(); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + @Override + public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request, + StreamObserver responseObserver) { + if (! agentCard.capabilities().pushNotifications()) { + handleError(responseObserver, new PushNotificationNotSupportedError()); + return; + } + + try { + ServerCallContext context = createCallContext(responseObserver); + DeleteTaskPushNotificationConfigParams params = FromProto.deleteTaskPushNotificationConfigParams(request); + requestHandler.onDeleteTaskPushNotificationConfig(params, context); + // void response + responseObserver.onNext(Empty.getDefaultInstance()); + responseObserver.onCompleted(); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + + private ServerCallContext createCallContext(StreamObserver responseObserver) { + if (callContextFactory == null || callContextFactory.isUnsatisfied()) { + // Default implementation when no custom CallContextFactory is provided + // This handles both CDI injection scenarios and test scenarios where callContextFactory is null + User user = UnauthenticatedUser.INSTANCE; + Map state = new HashMap<>(); + + // TODO: ARCHITECTURAL LIMITATION - StreamObserver is NOT equivalent to Python's grpc.aio.ServicerContext + // In Python: context parameter provides metadata, peer info, cancellation, etc. + // In Java: the proper equivalent would be ServerCall + Metadata from ServerInterceptor + // Current compromise: Store StreamObserver for basic functionality, but it lacks rich context + // + // FUTURE ENHANCEMENT: Implement ServerInterceptor to capture ServerCall + Metadata, + // store in gRPC Context using Context.Key, then access via Context.current().get(key) + // This would provide proper equivalence to Python's ServicerContext + state.put("grpc_response_observer", responseObserver); + + return new ServerCallContext(user, state); + } else { + CallContextFactory factory = callContextFactory.get(); + // TODO: CallContextFactory interface expects ServerCall + Metadata, but we only have StreamObserver + // This is another manifestation of the architectural limitation mentioned above + return factory.create(responseObserver); // Fall back to basic create() method for now + } + } + + private void handleError(StreamObserver responseObserver, JSONRPCError error) { + Status status; + String description; + if (error instanceof InvalidRequestError) { + status = Status.INVALID_ARGUMENT; + description = "InvalidRequestError: " + error.getMessage(); + } else if (error instanceof MethodNotFoundError) { + status = Status.NOT_FOUND; + description = "MethodNotFoundError: " + error.getMessage(); + } else if (error instanceof InvalidParamsError) { + status = Status.INVALID_ARGUMENT; + description = "InvalidParamsError: " + error.getMessage(); + } else if (error instanceof InternalError) { + status = Status.INTERNAL; + description = "InternalError: " + error.getMessage(); + } else if (error instanceof TaskNotFoundError) { + status = Status.NOT_FOUND; + description = "TaskNotFoundError: " + error.getMessage(); + } else if (error instanceof TaskNotCancelableError) { + status = Status.UNIMPLEMENTED; + description = "TaskNotCancelableError: " + error.getMessage(); + } else if (error instanceof PushNotificationNotSupportedError) { + status = Status.UNIMPLEMENTED; + description = "PushNotificationNotSupportedError: " + error.getMessage(); + } else if (error instanceof UnsupportedOperationError) { + status = Status.UNIMPLEMENTED; + description = "UnsupportedOperationError: " + error.getMessage(); + } else if (error instanceof JSONParseError) { + status = Status.INTERNAL; + description = "JSONParseError: " + error.getMessage(); + } else if (error instanceof ContentTypeNotSupportedError) { + status = Status.UNIMPLEMENTED; + description = "ContentTypeNotSupportedError: " + error.getMessage(); + } else if (error instanceof InvalidAgentResponseError) { + status = Status.INTERNAL; + description = "InvalidAgentResponseError: " + error.getMessage(); + } else { + status = Status.UNKNOWN; + description = "Unknown error type: " + error.getMessage(); + } + responseObserver.onError(status.withDescription(description).asRuntimeException()); + } + + private void handleInternalError(StreamObserver responseObserver, Throwable t) { + handleError(responseObserver, new InternalError(t.getMessage())); + } + + public static void setStreamingSubscribedRunnable(Runnable runnable) { + streamingSubscribedRunnable = runnable; + } + +} diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java new file mode 100644 index 000000000..a8cf3fcf0 --- /dev/null +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -0,0 +1,188 @@ +package io.a2a.server.requesthandlers; + +import jakarta.enterprise.context.Dependent; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +import io.a2a.http.A2AHttpClient; +import io.a2a.http.A2AHttpResponse; +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.tasks.BasePushNotificationSender; +import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; +import io.a2a.server.tasks.InMemoryTaskStore; +import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.server.tasks.PushNotificationSender; +import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TextPart; +import io.a2a.util.Utils; +import io.quarkus.arc.profile.IfBuildProfile; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public class AbstractA2ARequestHandlerTest { + + protected static final AgentCard CARD = createAgentCard(true, true, true); + + protected static final Task MINIMAL_TASK = new Task.Builder() + .id("task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + protected static final Message MESSAGE = new Message.Builder() + .messageId("111") + .role(Message.Role.AGENT) + .parts(new TextPart("test message")) + .build(); + + AgentExecutor executor; + TaskStore taskStore; + RequestHandler requestHandler; + AgentExecutorMethod agentExecutorExecute; + AgentExecutorMethod agentExecutorCancel; + InMemoryQueueManager queueManager; + TestHttpClient httpClient; + + final Executor internalExecutor = Executors.newCachedThreadPool(); + + @BeforeEach + public void init() { + executor = new AgentExecutor() { + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + if (agentExecutorExecute != null) { + agentExecutorExecute.invoke(context, eventQueue); + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + if (agentExecutorCancel != null) { + agentExecutorCancel.invoke(context, eventQueue); + } + } + }; + + taskStore = new InMemoryTaskStore(); + queueManager = new InMemoryQueueManager(); + httpClient = new TestHttpClient(); + PushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); + PushNotificationSender pushSender = new BasePushNotificationSender(pushConfigStore, httpClient); + + requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, pushConfigStore, pushSender, internalExecutor); + } + + @AfterEach + public void cleanup() { + agentExecutorExecute = null; + agentExecutorCancel = null; + } + + protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory) { + return new AgentCard.Builder() + .name("test-card") + .description("A test agent card") + .url("http://example.com") + .version("1.0") + .documentationUrl("http://example.com/docs") + .capabilities(new AgentCapabilities.Builder() + .streaming(streaming) + .pushNotifications(pushNotifications) + .stateTransitionHistory(stateTransitionHistory) + .build()) + .defaultInputModes(new ArrayList<>()) + .defaultOutputModes(new ArrayList<>()) + .skills(new ArrayList<>()) + .protocolVersion("0.2.5") + .build(); + } + + protected interface AgentExecutorMethod { + void invoke(RequestContext context, EventQueue eventQueue) throws JSONRPCError; + } + + @Dependent + @IfBuildProfile("test") + protected static class TestHttpClient implements A2AHttpClient { + final List tasks = Collections.synchronizedList(new ArrayList<>()); + volatile CountDownLatch latch; + + @Override + public GetBuilder createGet() { + return null; + } + + @Override + public PostBuilder createPost() { + return new TestHttpClient.TestPostBuilder(); + } + + class TestPostBuilder implements A2AHttpClient.PostBuilder { + private volatile String body; + @Override + public PostBuilder body(String body) { + this.body = body; + return this; + } + + @Override + public A2AHttpResponse post() throws IOException, InterruptedException { + tasks.add(Utils.OBJECT_MAPPER.readValue(body, Task.TYPE_REFERENCE)); + try { + return new A2AHttpResponse() { + @Override + public int status() { + return 200; + } + + @Override + public boolean success() { + return true; + } + + @Override + public String body() { + return ""; + } + }; + } finally { + latch.countDown(); + } + } + + @Override + public CompletableFuture postAsyncSSE(Consumer messageConsumer, Consumer errorConsumer, Runnable completeRunnable) throws IOException, InterruptedException { + return null; + } + + @Override + public PostBuilder url(String s) { + return this; + } + + @Override + public PostBuilder addHeader(String name, String value) { + return this; + } + } + } +} diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java new file mode 100644 index 000000000..87a1a9bc8 --- /dev/null +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java @@ -0,0 +1,806 @@ +package io.a2a.server.requesthandlers; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.google.protobuf.Empty; +import com.google.protobuf.Struct; + +import io.a2a.grpc.AuthenticationInfo; +import io.a2a.grpc.CancelTaskRequest; +import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; +import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskRequest; +import io.a2a.grpc.ListTaskPushNotificationConfigRequest; +import io.a2a.grpc.ListTaskPushNotificationConfigResponse; +import io.a2a.grpc.Message; +import io.a2a.grpc.Part; +import io.a2a.grpc.PushNotificationConfig; +import io.a2a.grpc.Role; +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.Task; +import io.a2a.grpc.TaskPushNotificationConfig; +import io.a2a.grpc.TaskState; +import io.a2a.grpc.TaskStatus; +import io.a2a.grpc.TaskSubscriptionRequest; +import io.a2a.server.ServerCallContext; +import io.a2a.server.events.EventConsumer; +import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Artifact; +import io.a2a.spec.Event; +import io.a2a.spec.InternalError; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import io.a2a.spec.UnsupportedOperationError; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.internal.testing.StreamRecorder; +import io.grpc.stub.StreamObserver; +import mutiny.zero.ZeroPublisher; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; + +public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { + + private static final Message GRPC_MESSAGE = Message.newBuilder() + .setTaskId(MINIMAL_TASK.getId()) + .setContextId(MINIMAL_TASK.getContextId()) + .setMessageId(MESSAGE.getMessageId()) + .setRole(Role.ROLE_AGENT) + .addContent(Part.newBuilder().setText(((TextPart)MESSAGE.getParts().get(0)).getText()).build()) + .setMetadata(Struct.newBuilder().build()) + .build(); + + + @Test + public void testOnGetTaskSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + GetTaskRequest request = GetTaskRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.getTask(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + Task task = result.get(0); + assertEquals(MINIMAL_TASK.getId(), task.getId()); + assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(TaskState.TASK_STATE_SUBMITTED, task.getStatus().getState()); + } + + @Test + public void testOnGetTaskNotFound() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GetTaskRequest request = GetTaskRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.getTask(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + + assertGrpcError(streamRecorder, Status.Code.NOT_FOUND); + } + + @Test + public void testOnCancelTaskSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + agentExecutorCancel = (context, eventQueue) -> { + // We need to cancel the task or the EventConsumer never finds a 'final' event. + // Looking at the Python implementation, they typically use AgentExecutors that + // don't support cancellation. So my theory is the Agent updates the task to the CANCEL status + io.a2a.spec.Task task = context.getTask(); + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + taskUpdater.cancel(); + }; + + CancelTaskRequest request = CancelTaskRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.cancelTask(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + Task task = result.get(0); + assertEquals(MINIMAL_TASK.getId(), task.getId()); + assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(TaskState.TASK_STATE_CANCELLED, task.getStatus().getState()); + } + + @Test + public void testOnCancelTaskNotSupported() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + agentExecutorCancel = (context, eventQueue) -> { + throw new UnsupportedOperationError(); + }; + + CancelTaskRequest request = CancelTaskRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.cancelTask(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testOnCancelTaskNotFound() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + CancelTaskRequest request = CancelTaskRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.cancelTask(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + + assertGrpcError(streamRecorder, Status.Code.NOT_FOUND); + } + + @Test + public void testOnMessageNewMessageSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getMessage()); + }; + + StreamRecorder streamRecorder = sendMessageRequest(handler); + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + SendMessageResponse response = result.get(0); + assertEquals(GRPC_MESSAGE, response.getMsg()); + } + + @Test + public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getMessage()); + }; + StreamRecorder streamRecorder = sendMessageRequest(handler); + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + SendMessageResponse response = result.get(0); + assertEquals(GRPC_MESSAGE, response.getMsg()); + } + + @Test + public void testOnMessageError() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(new UnsupportedOperationError()); + }; + StreamRecorder streamRecorder = sendMessageRequest(handler); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testSetPushNotificationConfigSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; + StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + TaskPushNotificationConfig response = result.get(0); + assertEquals(NAME, response.getName()); + PushNotificationConfig responseConfig = response.getPushNotificationConfig(); + assertEquals("config456", responseConfig.getId()); + assertEquals("http://example.com", responseConfig.getUrl()); + assertEquals(AuthenticationInfo.getDefaultInstance(), responseConfig.getAuthentication()); + assertTrue(responseConfig.getToken().isEmpty()); + } + + @Test + public void testGetPushNotificationConfigSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; + + // first set the task push notification config + StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + assertNull(streamRecorder.getError()); + + // then get the task push notification config + streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + TaskPushNotificationConfig response = result.get(0); + assertEquals(NAME, response.getName()); + PushNotificationConfig responseConfig = response.getPushNotificationConfig(); + assertEquals("config456", responseConfig.getId()); + assertEquals("http://example.com", responseConfig.getUrl()); + assertEquals(AuthenticationInfo.getDefaultInstance(), responseConfig.getAuthentication()); + assertTrue(responseConfig.getToken().isEmpty()); + } + + @Test + public void testPushNotificationsNotSupportedError() throws Exception { + AgentCard card = createAgentCard(true, false, true); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { + // Create request handler without a push notifier + DefaultRequestHandler requestHandler = + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + AgentCard card = createAgentCard(false, true, false); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + StreamRecorder streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { + // Create request handler without a push notifier + DefaultRequestHandler requestHandler = + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + AgentCard card = createAgentCard(false, true, false); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testOnMessageStreamNewMessageSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + StreamResponse response = result.get(0); + assertTrue(response.hasMsg()); + Message message = response.getMsg(); + assertEquals(GRPC_MESSAGE, message); + } + + @Test + public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(MINIMAL_TASK) + .history(new ArrayList<>()) + .build(); + taskStore.save(task); + + List results = new ArrayList<>(); + List errors = new ArrayList<>(); + final CountDownLatch latch = new CountDownLatch(1); + httpClient.latch = latch; + StreamObserver streamObserver = new StreamObserver<>() { + @Override + public void onNext(StreamResponse streamResponse) { + results.add(streamResponse); + latch.countDown(); + } + + @Override + public void onError(Throwable throwable) { + errors.add(throwable); + } + + @Override + public void onCompleted() { + } + }; + sendStreamingMessageRequest(handler, streamObserver); + Assertions.assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(errors.isEmpty()); + assertEquals(1, results.size()); + StreamResponse response = results.get(0); + assertTrue(response.hasTask()); + Task taskResponse = response.getTask(); + + Task expected = Task.newBuilder() + .setId(MINIMAL_TASK.getId()) + .setContextId(MINIMAL_TASK.getContextId()) + .addAllHistory(List.of(GRPC_MESSAGE)) + .setStatus(TaskStatus.newBuilder().setStateValue(TaskState.TASK_STATE_SUBMITTED_VALUE)) + .build(); + assertEquals(expected.getId(), taskResponse.getId()); + assertEquals(expected.getContextId(), taskResponse.getContextId()); + assertEquals(expected.getStatus().getState(), taskResponse.getStatus().getState()); + assertEquals(expected.getHistoryList(), taskResponse.getHistoryList()); + } + + @Test + public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + + io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(MINIMAL_TASK) + .history(new ArrayList<>()) + .build(); + taskStore.save(task); + + // This is used to send events from a mock + List events = List.of( + new TaskArtifactUpdateEvent.Builder() + .taskId(task.getId()) + .contextId(task.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + new TaskStatusUpdateEvent.Builder() + .taskId(task.getId()) + .contextId(task.getContextId()) + .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING)) + .build()); + + StreamRecorder streamRecorder; + try (MockedConstruction mocked = Mockito.mockConstruction( + EventConsumer.class, + (mock, context) -> { + Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + streamRecorder = sendStreamingMessageRequest(handler); + } + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertEquals(2, result.size()); + StreamResponse first = result.get(0); + assertTrue(first.hasArtifactUpdate()); + io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent = first.getArtifactUpdate(); + assertEquals(task.getId(), taskArtifactUpdateEvent.getTaskId()); + assertEquals(task.getContextId(), taskArtifactUpdateEvent.getContextId()); + assertEquals("11", taskArtifactUpdateEvent.getArtifact().getArtifactId()); + assertEquals("text", taskArtifactUpdateEvent.getArtifact().getParts(0).getText()); + StreamResponse second = result.get(1); + assertTrue(second.hasStatusUpdate()); + io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent = second.getStatusUpdate(); + assertEquals(task.getId(), taskStatusUpdateEvent.getTaskId()); + assertEquals(task.getContextId(), taskStatusUpdateEvent.getContextId()); + assertEquals(TaskState.TASK_STATE_WORKING, taskStatusUpdateEvent.getStatus().getState()); + } + + @Test + public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + List events = List.of( + MINIMAL_TASK, + new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + new TaskStatusUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.COMPLETED)) + .build()); + + + agentExecutorExecute = (context, eventQueue) -> { + // Hardcode the events to send here + for (Event event : events) { + eventQueue.enqueueEvent(event); + } + }; + + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + StreamRecorder pushStreamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + assertNull(pushStreamRecorder.getError()); + + List results = new ArrayList<>(); + List errors = new ArrayList<>(); + final CountDownLatch latch = new CountDownLatch(6); + httpClient.latch = latch; + StreamObserver streamObserver = new StreamObserver<>() { + @Override + public void onNext(StreamResponse streamResponse) { + results.add(streamResponse); + latch.countDown(); + } + + @Override + public void onError(Throwable throwable) { + errors.add(throwable); + } + + @Override + public void onCompleted() { + } + }; + sendStreamingMessageRequest(handler, streamObserver); + assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue(errors.isEmpty()); + assertEquals(3, results.size()); + assertEquals(3, httpClient.tasks.size()); + + io.a2a.spec.Task curr = httpClient.tasks.get(0); + assertEquals(MINIMAL_TASK.getId(), curr.getId()); + assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); + + curr = httpClient.tasks.get(1); + assertEquals(MINIMAL_TASK.getId(), curr.getId()); + assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + assertEquals(1, curr.getArtifacts().size()); + assertEquals(1, curr.getArtifacts().get(0).parts().size()); + assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + + curr = httpClient.tasks.get(2); + assertEquals(MINIMAL_TASK.getId(), curr.getId()); + assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.getStatus().state()); + assertEquals(1, curr.getArtifacts().size()); + assertEquals(1, curr.getArtifacts().get(0).parts().size()); + assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + } + + @Test + public void testOnResubscribeNoExistingTaskError() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.taskSubscription(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + assertGrpcError(streamRecorder, Status.Code.NOT_FOUND); + } + + @Test + public void testOnResubscribeExistingTaskSuccess() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + queueManager.createOrTap(MINIMAL_TASK.getId()); + + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getMessage()); + }; + + StreamRecorder streamRecorder = StreamRecorder.create(); + TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + handler.taskSubscription(request, streamRecorder); + + // We need to send some events in order for those to end up in the queue + SendMessageRequest sendMessageRequest = SendMessageRequest.newBuilder() + .setRequest(GRPC_MESSAGE) + .build(); + StreamRecorder messageRecorder = StreamRecorder.create(); + handler.sendStreamingMessage(sendMessageRequest, messageRecorder); + messageRecorder.awaitCompletion(5, TimeUnit.SECONDS); + assertNull(messageRecorder.getError()); + + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + List result = streamRecorder.getValues(); + assertNotNull(result); + assertEquals(1, result.size()); + StreamResponse response = result.get(0); + assertTrue(response.hasMsg()); + assertEquals(GRPC_MESSAGE, response.getMsg()); + assertNull(streamRecorder.getError()); + } + + @Test + public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + queueManager.createOrTap(MINIMAL_TASK.getId()); + + List events = List.of( + new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + new TaskStatusUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING)) + .build()); + + StreamRecorder streamRecorder = StreamRecorder.create(); + TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + try (MockedConstruction mocked = Mockito.mockConstruction( + EventConsumer.class, + (mock, context) -> { + Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + handler.taskSubscription(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + } + List result = streamRecorder.getValues(); + assertEquals(events.size(), result.size()); + StreamResponse first = result.get(0); + assertTrue(first.hasArtifactUpdate()); + io.a2a.grpc.TaskArtifactUpdateEvent event = first.getArtifactUpdate(); + assertEquals("11", event.getArtifact().getArtifactId()); + assertEquals("text", (event.getArtifact().getParts(0)).getText()); + StreamResponse second = result.get(1); + assertTrue(second.hasStatusUpdate()); + assertEquals(TaskState.TASK_STATE_WORKING, second.getStatusUpdate().getStatus().getState()); + } + + @Test + public void testStreamingNotSupportedError() throws Exception { + AgentCard card = createAgentCard(false, true, true); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); + assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); + } + + @Test + public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception { + // This test does not exist in the Python implementation + AgentCard card = createAgentCard(false, true, true); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + .setName("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.taskSubscription(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); + } + + @Test + public void testOnMessageStreamInternalError() throws Exception { + DefaultRequestHandler mocked = Mockito.mock(DefaultRequestHandler.class); + Mockito.doThrow(new InternalError("Internal Error")).when(mocked).onMessageSendStream(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); + GrpcHandler handler = new GrpcHandler(CARD, mocked); + StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); + assertGrpcError(streamRecorder, Status.Code.INTERNAL); + } + + @Test + public void testListPushNotificationConfig() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + StreamRecorder pushRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + assertNull(pushRecorder.getError()); + + ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.listTaskPushNotificationConfig(request, streamRecorder); + assertNull(streamRecorder.getError()); + List result = streamRecorder.getValues(); + assertEquals(1, result.size()); + List configList = result.get(0).getConfigsList(); + assertEquals(1, configList.size()); + assertEquals(pushRecorder.getValues().get(0), configList.get(0)); + } + + @Test + public void testListPushNotificationConfigNotSupported() throws Exception { + AgentCard card = createAgentCard(true, false, true); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.listTaskPushNotificationConfig(request, streamRecorder); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testListPushNotificationConfigNoPushConfigStore() { + DefaultRequestHandler requestHandler = + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.listTaskPushNotificationConfig(request, streamRecorder); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testListPushNotificationConfigTaskNotFound() { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + MINIMAL_TASK.getId()) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.listTaskPushNotificationConfig(request, streamRecorder); + assertGrpcError(streamRecorder, Status.Code.NOT_FOUND); + } + + @Test + public void testDeletePushNotificationConfig() throws Exception { + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + StreamRecorder pushRecorder = createTaskPushNotificationConfigRequest(handler, NAME); + assertNull(pushRecorder.getError()); + + DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName(NAME) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.deleteTaskPushNotificationConfig(request, streamRecorder); + assertNull(streamRecorder.getError()); + assertEquals(1, streamRecorder.getValues().size()); + assertEquals(Empty.getDefaultInstance(), streamRecorder.getValues().get(0)); + } + + @Test + public void testDeletePushNotificationConfigNotSupported() throws Exception { + AgentCard card = createAgentCard(true, false, true); + GrpcHandler handler = new GrpcHandler(card, requestHandler); + taskStore.save(MINIMAL_TASK); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); + }; + + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName(NAME) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.deleteTaskPushNotificationConfig(request, streamRecorder); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + @Test + public void testDeletePushNotificationConfigNoPushConfigStore() { + DefaultRequestHandler requestHandler = + new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName(NAME) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.deleteTaskPushNotificationConfig(request, streamRecorder); + assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); + } + + private StreamRecorder sendMessageRequest(GrpcHandler handler) throws Exception { + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(GRPC_MESSAGE) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.sendMessage(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + return streamRecorder; + } + + private StreamRecorder createTaskPushNotificationConfigRequest(GrpcHandler handler, String name) throws Exception { + taskStore.save(MINIMAL_TASK); + PushNotificationConfig config = PushNotificationConfig.newBuilder() + .setUrl("http://example.com") + .build(); + TaskPushNotificationConfig taskPushNotificationConfig = TaskPushNotificationConfig.newBuilder() + .setName(name) + .setPushNotificationConfig(config) + .build(); + CreateTaskPushNotificationConfigRequest setRequest = CreateTaskPushNotificationConfigRequest.newBuilder() + .setConfig(taskPushNotificationConfig) + .build(); + + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.createTaskPushNotificationConfig(setRequest, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + return streamRecorder; + } + + private StreamRecorder getTaskPushNotificationConfigRequest(GrpcHandler handler, String name) throws Exception { + GetTaskPushNotificationConfigRequest request = GetTaskPushNotificationConfigRequest.newBuilder() + .setName(name) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.getTaskPushNotificationConfig(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + return streamRecorder; + } + + private StreamRecorder sendStreamingMessageRequest(GrpcHandler handler) throws Exception { + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(GRPC_MESSAGE) + .build(); + StreamRecorder streamRecorder = StreamRecorder.create(); + handler.sendStreamingMessage(request, streamRecorder); + streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); + return streamRecorder; + } + + private void sendStreamingMessageRequest(GrpcHandler handler, StreamObserver streamObserver) throws Exception { + SendMessageRequest request = SendMessageRequest.newBuilder() + .setRequest(GRPC_MESSAGE) + .build(); + handler.sendStreamingMessage(request, streamObserver); + } + + private void assertGrpcError(StreamRecorder streamRecorder, Status.Code expectedStatusCode) { + assertNotNull(streamRecorder.getError()); + assertInstanceOf(StatusRuntimeException.class, streamRecorder.getError()); + assertEquals(expectedStatusCode, ((StatusRuntimeException) streamRecorder.getError()).getStatus().getCode()); + assertTrue(streamRecorder.getValues().isEmpty()); + } +} diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java index f67fa87cf..1851a5a90 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java @@ -8,40 +8,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Flow; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import jakarta.enterprise.context.Dependent; - -import io.a2a.http.A2AHttpClient; -import io.a2a.http.A2AHttpResponse; import io.a2a.server.ServerCallContext; -import io.a2a.server.agentexecution.AgentExecutor; -import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; -import io.a2a.server.events.EventQueue; -import io.a2a.server.events.InMemoryQueueManager; -import io.a2a.server.tasks.BasePushNotificationSender; -import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; -import io.a2a.server.tasks.InMemoryTaskStore; -import io.a2a.server.tasks.PushNotificationConfigStore; -import io.a2a.server.tasks.PushNotificationSender; import io.a2a.server.tasks.ResultAggregator; -import io.a2a.server.tasks.TaskStore; import io.a2a.server.tasks.TaskUpdater; -import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskRequest; @@ -57,7 +38,6 @@ import io.a2a.spec.GetTaskResponse; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; @@ -84,78 +64,16 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.util.Utils; -import io.quarkus.arc.profile.IfBuildProfile; import mutiny.zero.ZeroPublisher; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; -public class JSONRPCHandlerTest { - - private static final AgentCard CARD = createAgentCard(true, true, true); - - private static final Task MINIMAL_TASK = new Task.Builder() - .id("task-123") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - private static final Message MESSAGE = new Message.Builder() - .messageId("111") - .role(Message.Role.AGENT) - .parts(new TextPart("test message")) - .build(); - - AgentExecutor executor; - TaskStore taskStore; - RequestHandler requestHandler; - AgentExecutorMethod agentExecutorExecute; - AgentExecutorMethod agentExecutorCancel; - private InMemoryQueueManager queueManager; - private TestHttpClient httpClient; - - private final Executor internalExecutor = Executors.newCachedThreadPool(); +public class JSONRPCHandlerTest extends AbstractA2ARequestHandlerTest { private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar")); - - @BeforeEach - public void init() { - executor = new AgentExecutor() { - @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { - if (agentExecutorExecute != null) { - agentExecutorExecute.invoke(context, eventQueue); - } - } - - @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { - if (agentExecutorCancel != null) { - agentExecutorCancel.invoke(context, eventQueue); - } - } - }; - - taskStore = new InMemoryTaskStore(); - queueManager = new InMemoryQueueManager(); - httpClient = new TestHttpClient(); - PushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); - PushNotificationSender pushSender = new BasePushNotificationSender(pushConfigStore, httpClient); - - requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, pushConfigStore, pushSender, internalExecutor); - } - - @AfterEach - public void cleanup() { - agentExecutorExecute = null; - agentExecutorCancel = null; - } - @Test public void testOnGetTaskSuccess() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); @@ -1436,92 +1354,4 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { assertInstanceOf(UnsupportedOperationError.class, deleteResponse.getError()); } - private static AgentCard createAgentCard(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory) { - return new AgentCard.Builder() - .name("test-card") - .description("A test agent card") - .url("http://example.com") - .version("1.0") - .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() - .streaming(streaming) - .pushNotifications(pushNotifications) - .stateTransitionHistory(stateTransitionHistory) - .build()) - .defaultInputModes(new ArrayList<>()) - .defaultOutputModes(new ArrayList<>()) - .skills(new ArrayList<>()) - .protocolVersion("0.2.5") - .build(); - } - - private interface AgentExecutorMethod { - void invoke(RequestContext context, EventQueue eventQueue) throws JSONRPCError; - } - - @Dependent - @IfBuildProfile("test") - private static class TestHttpClient implements A2AHttpClient { - final List tasks = Collections.synchronizedList(new ArrayList<>()); - volatile CountDownLatch latch; - - @Override - public GetBuilder createGet() { - return null; - } - - @Override - public PostBuilder createPost() { - return new TestPostBuilder(); - } - - class TestPostBuilder implements A2AHttpClient.PostBuilder { - private volatile String body; - @Override - public PostBuilder body(String body) { - this.body = body; - return this; - } - - @Override - public A2AHttpResponse post() throws IOException, InterruptedException { - tasks.add(Utils.OBJECT_MAPPER.readValue(body, Task.TYPE_REFERENCE)); - try { - return new A2AHttpResponse() { - @Override - public int status() { - return 200; - } - - @Override - public boolean success() { - return true; - } - - @Override - public String body() { - return ""; - } - }; - } finally { - latch.countDown(); - } - } - - @Override - public CompletableFuture postAsyncSSE(Consumer messageConsumer, Consumer errorConsumer, Runnable completeRunnable) throws IOException, InterruptedException { - return null; - } - - @Override - public PostBuilder url(String s) { - return this; - } - - @Override - public PostBuilder addHeader(String name, String value) { - return this; - } - } - } } diff --git a/tests/server-common/src/test/resources/META-INF/beans.xml b/tests/server-common/src/test/resources/META-INF/beans.xml index 9dfae34df..b0e6ff3e2 100644 --- a/tests/server-common/src/test/resources/META-INF/beans.xml +++ b/tests/server-common/src/test/resources/META-INF/beans.xml @@ -1,6 +1,7 @@ + - \ No newline at end of file + From af34181d5b2f1bf74038169284834da2957cbc82 Mon Sep 17 00:00:00 2001 From: Michal Broz Date: Wed, 6 Aug 2025 05:32:02 -0500 Subject: [PATCH 056/493] chore: Update Example to print the actual response returned from Agent (#203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description The current helloworld example only ends up printing `Response: io.a2a.spec.SendMessageResponse@63252cf7` when it does `System.out.println("Response: " + response.toString());`. I expect the actual intent to be to print the response message sent by the Agent which is what this update does. Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) - The current helloworld example READMEs (client and server) seem to need work beyond this minor update. Happy to handle that under another Issue/PR if strategic and helpful. Fixes # 🦕 --- .../examples/helloworld/HelloWorldClient.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index 45f202b96..94e595228 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -7,9 +7,12 @@ import io.a2a.client.A2AClient; import io.a2a.A2A; import io.a2a.spec.AgentCard; +import io.a2a.spec.EventKind; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.SendMessageResponse; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; /** * A simple example of using the A2A Java SDK to communicate with an A2A server. @@ -50,7 +53,19 @@ public static void main(String[] args) { .build(); SendMessageResponse response = client.sendMessage(params); System.out.println("Message sent with ID: " + response.getId()); - System.out.println("Response: " + response.toString()); + + EventKind result = response.getResult(); + if (result instanceof Message responseMessage) { + StringBuilder textBuilder = new StringBuilder(); + if (responseMessage.getParts() != null) { + for (Part part : responseMessage.getParts()) { + if (part instanceof TextPart textPart) { + textBuilder.append(textPart.getText()); + } + } + } + System.out.println("Response: " + textBuilder.toString()); + } } catch (Exception e) { System.err.println("An error occurred: " + e.getMessage()); e.printStackTrace(); From 8900cbd47889c40a7b3ee053b80e2d5acac4056a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 6 Aug 2025 14:21:39 +0100 Subject: [PATCH 057/493] feat: add default noop providers for AgentCard and -Executor (#215) This avoids CDI injection problems when building an application with the Quarkus plugin, and still makes people have to implement them to run their application # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #213 --- .../server/apps/quarkus/DefaultProducers.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java new file mode 100644 index 000000000..2264355ac --- /dev/null +++ b/reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java @@ -0,0 +1,48 @@ +package io.a2a.server.apps.quarkus; + +import java.util.ArrayList; +import java.util.Collections; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import jakarta.ws.rs.Produces; + +import io.a2a.server.PublicAgentCard; +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.JSONRPCError; +import io.quarkus.arc.DefaultBean; + +/** + * Contains beans annotated with the Quarkus @DefaultBean annotation, in order to avoid + * injection failures when building the Quarkus application as discussed in + * Issue 213. + * + * If an application provides actual implementations of these beans, + * those will be used instead. + */ +@ApplicationScoped +public class DefaultProducers { + @Produces + @PublicAgentCard + @DefaultBean + public AgentCard createDefaultAgentCard() { + throw new IllegalStateException(wrap("Please provide your own AgentCard implementation")); + } + + @Produces + @Default + @DefaultBean + public AgentExecutor createDefaultAgentExecutor() { + throw new IllegalStateException(wrap("Please provide your own AgentExecutor implementation")); + } + + private String wrap(String s) { + return s + + " as a CDI bean. Your bean will automatically take precedence over this @DefaultBean " + + "annotated implementation."; + } +} From a788f033b96edd1674f7f14dddb375c312607c17 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 7 Aug 2025 16:34:42 +0200 Subject: [PATCH 058/493] chore!: refactor split the transports into different packages (#214) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- README.md | 17 +- client/pom.xml | 4 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 15 +- .../src/main/resources/application.properties | 5 - {reference-impl => reference/common}/pom.xml | 12 +- .../common}/quarkus/DefaultProducers.java | 9 +- .../src/main/resources/META-INF/beans.xml | 0 {reference-grpc => reference/grpc}/pom.xml | 13 +- .../grpc/quarkus/QuarkusGrpcHandler.java | 11 +- .../server/grpc/quarkus/A2ATestResource.java | 2 +- .../grpc/quarkus/QuarkusA2AGrpcTest.java | 2 +- .../src/test/resources/application.properties | 5 + .../jsonrpc}/README.md | 0 reference/jsonrpc/pom.xml | 86 +++++ .../server/apps/quarkus/A2AServerRoutes.java | 2 +- .../apps/quarkus/CallContextFactory.java | 0 .../src/main/resources/META-INF/beans.xml | 0 .../server/apps/quarkus/A2ATestRoutes.java | 0 .../apps/quarkus/QuarkusA2AJSONRPCTest.java | 4 +- .../src/test/resources/application.properties | 0 {sdk-server-common => server-common}/pom.xml | 32 +- .../java/io/a2a/server/ExtendedAgentCard.java | 0 .../java/io/a2a/server/JSONRPCException.java | 0 .../java/io/a2a/server/PublicAgentCard.java | 0 .../java/io/a2a/server/ServerCallContext.java | 0 .../server/agentexecution/AgentExecutor.java | 0 .../server/agentexecution/RequestContext.java | 0 .../SimpleRequestContextBuilder.java | 0 .../a2a/server/auth/UnauthenticatedUser.java | 0 .../main/java/io/a2a/server/auth/User.java | 0 .../a2a/server/events/EnhancedRunnable.java | 0 .../io/a2a/server/events/EventConsumer.java | 0 .../java/io/a2a/server/events/EventQueue.java | 0 .../events/EventQueueClosedException.java | 0 .../server/events/InMemoryQueueManager.java | 0 .../server/events/NoTaskQueueException.java | 0 .../io/a2a/server/events/QueueManager.java | 0 .../events/TaskQueueExistsException.java | 0 .../requesthandlers/CallContextFactory.java | 0 .../DefaultRequestHandler.java | 0 .../requesthandlers/RequestHandler.java | 0 .../tasks/BasePushNotificationSender.java | 0 .../InMemoryPushNotificationConfigStore.java | 0 .../a2a/server/tasks/InMemoryTaskStore.java | 0 .../tasks/PushNotificationConfigStore.java | 0 .../server/tasks/PushNotificationSender.java | 0 .../io/a2a/server/tasks/ResultAggregator.java | 0 .../java/io/a2a/server/tasks/TaskManager.java | 0 .../java/io/a2a/server/tasks/TaskStore.java | 0 .../java/io/a2a/server/tasks/TaskUpdater.java | 0 .../util/async/AsyncExecutorProducer.java | 0 .../io/a2a/server/util/async/AsyncUtils.java | 0 .../io/a2a/server/util/async/Internal.java | 0 .../src/main/resources/META-INF/beans.xml | 0 .../agentexecution/RequestContextTest.java | 0 .../a2a/server/events/EventConsumerTest.java | 0 .../io/a2a/server/events/EventQueueTest.java | 0 .../AbstractA2ARequestHandlerTest.java | 21 +- .../server/tasks/InMemoryTaskStoreTest.java | 0 .../io/a2a/server/tasks/TaskManagerTest.java | 0 .../io/a2a/server/tasks/TaskUpdaterTest.java | 0 .../a2a/server/util/async/AsyncUtilsTest.java | 0 .../src/test/resources/logback.xml | 0 {grpc => spec-grpc}/pom.xml | 16 +- .../src/main/java/io/a2a/grpc/A2A.java | 0 .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 0 .../io/a2a/grpc/APIKeySecurityScheme.java | 0 .../grpc/APIKeySecuritySchemeOrBuilder.java | 0 .../java/io/a2a/grpc/AgentCapabilities.java | 0 .../a2a/grpc/AgentCapabilitiesOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/AgentCard.java | 0 .../java/io/a2a/grpc/AgentCardOrBuilder.java | 0 .../main/java/io/a2a/grpc/AgentExtension.java | 0 .../io/a2a/grpc/AgentExtensionOrBuilder.java | 0 .../main/java/io/a2a/grpc/AgentInterface.java | 0 .../io/a2a/grpc/AgentInterfaceOrBuilder.java | 0 .../main/java/io/a2a/grpc/AgentProvider.java | 0 .../io/a2a/grpc/AgentProviderOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/AgentSkill.java | 0 .../java/io/a2a/grpc/AgentSkillOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/Artifact.java | 0 .../java/io/a2a/grpc/ArtifactOrBuilder.java | 0 .../java/io/a2a/grpc/AuthenticationInfo.java | 0 .../a2a/grpc/AuthenticationInfoOrBuilder.java | 0 .../a2a/grpc/AuthorizationCodeOAuthFlow.java | 0 .../AuthorizationCodeOAuthFlowOrBuilder.java | 0 .../java/io/a2a/grpc/CancelTaskRequest.java | 0 .../a2a/grpc/CancelTaskRequestOrBuilder.java | 0 .../a2a/grpc/ClientCredentialsOAuthFlow.java | 0 .../ClientCredentialsOAuthFlowOrBuilder.java | 0 ...eateTaskPushNotificationConfigRequest.java | 0 ...ushNotificationConfigRequestOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/DataPart.java | 0 .../java/io/a2a/grpc/DataPartOrBuilder.java | 0 ...leteTaskPushNotificationConfigRequest.java | 0 ...ushNotificationConfigRequestOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/FilePart.java | 0 .../java/io/a2a/grpc/FilePartOrBuilder.java | 0 .../java/io/a2a/grpc/GetAgentCardRequest.java | 0 .../grpc/GetAgentCardRequestOrBuilder.java | 0 .../GetTaskPushNotificationConfigRequest.java | 0 ...ushNotificationConfigRequestOrBuilder.java | 0 .../main/java/io/a2a/grpc/GetTaskRequest.java | 0 .../io/a2a/grpc/GetTaskRequestOrBuilder.java | 0 .../io/a2a/grpc/HTTPAuthSecurityScheme.java | 0 .../grpc/HTTPAuthSecuritySchemeOrBuilder.java | 0 .../java/io/a2a/grpc/ImplicitOAuthFlow.java | 0 .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java | 0 ...ListTaskPushNotificationConfigRequest.java | 0 ...ushNotificationConfigRequestOrBuilder.java | 0 ...istTaskPushNotificationConfigResponse.java | 0 ...shNotificationConfigResponseOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/Message.java | 0 .../java/io/a2a/grpc/MessageOrBuilder.java | 0 .../io/a2a/grpc/OAuth2SecurityScheme.java | 0 .../grpc/OAuth2SecuritySchemeOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/OAuthFlows.java | 0 .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java | 0 .../a2a/grpc/OpenIdConnectSecurityScheme.java | 0 .../OpenIdConnectSecuritySchemeOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/Part.java | 0 .../main/java/io/a2a/grpc/PartOrBuilder.java | 0 .../java/io/a2a/grpc/PasswordOAuthFlow.java | 0 .../a2a/grpc/PasswordOAuthFlowOrBuilder.java | 0 .../io/a2a/grpc/PushNotificationConfig.java | 0 .../grpc/PushNotificationConfigOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/Role.java | 0 .../src/main/java/io/a2a/grpc/Security.java | 0 .../java/io/a2a/grpc/SecurityOrBuilder.java | 0 .../main/java/io/a2a/grpc/SecurityScheme.java | 0 .../io/a2a/grpc/SecuritySchemeOrBuilder.java | 0 .../io/a2a/grpc/SendMessageConfiguration.java | 0 .../SendMessageConfigurationOrBuilder.java | 0 .../java/io/a2a/grpc/SendMessageRequest.java | 0 .../a2a/grpc/SendMessageRequestOrBuilder.java | 0 .../java/io/a2a/grpc/SendMessageResponse.java | 0 .../grpc/SendMessageResponseOrBuilder.java | 0 .../main/java/io/a2a/grpc/StreamResponse.java | 0 .../io/a2a/grpc/StreamResponseOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/StringList.java | 0 .../java/io/a2a/grpc/StringListOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/Task.java | 0 .../io/a2a/grpc/TaskArtifactUpdateEvent.java | 0 .../TaskArtifactUpdateEventOrBuilder.java | 0 .../main/java/io/a2a/grpc/TaskOrBuilder.java | 0 .../a2a/grpc/TaskPushNotificationConfig.java | 0 .../TaskPushNotificationConfigOrBuilder.java | 0 .../src/main/java/io/a2a/grpc/TaskState.java | 0 .../src/main/java/io/a2a/grpc/TaskStatus.java | 0 .../java/io/a2a/grpc/TaskStatusOrBuilder.java | 0 .../io/a2a/grpc/TaskStatusUpdateEvent.java | 0 .../grpc/TaskStatusUpdateEventOrBuilder.java | 0 .../io/a2a/grpc/TaskSubscriptionRequest.java | 0 .../TaskSubscriptionRequestOrBuilder.java | 0 .../java/io/a2a/grpc/utils/ProtoUtils.java | 0 .../src/main/resources/META-INF/beans.xml | 0 tck/pom.xml | 2 +- transport/grpc/pom.xml | 81 +++++ .../io/a2a/grpc/handler}/GrpcHandler.java | 4 +- .../src/main/resources/META-INF/beans.xml | 6 + .../io/a2a/grpc/handler}/GrpcHandlerTest.java | 328 +++++++++--------- transport/jsonrpc/pom.xml | 51 +++ .../a2a/jsonrpc/handler}/JSONRPCHandler.java | 4 +- .../src/main/resources/META-INF/beans.xml | 6 + .../jsonrpc/handler}/JSONRPCHandlerTest.java | 231 ++++++------ 167 files changed, 606 insertions(+), 367 deletions(-) delete mode 100644 reference-grpc/src/main/resources/application.properties rename {reference-impl => reference/common}/pom.xml (86%) rename {reference-impl/src/main/java/io/a2a/server/apps => reference/common/src/main/java/io/a2a/server/common}/quarkus/DefaultProducers.java (83%) rename {reference-impl => reference/common}/src/main/resources/META-INF/beans.xml (100%) rename {reference-grpc => reference/grpc}/pom.xml (86%) rename {reference-grpc => reference/grpc}/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java (55%) rename {reference-grpc => reference/grpc}/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java (98%) rename {reference-grpc => reference/grpc}/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java (99%) create mode 100644 reference/grpc/src/test/resources/application.properties rename {reference-impl => reference/jsonrpc}/README.md (100%) create mode 100644 reference/jsonrpc/pom.xml rename {reference-impl => reference/jsonrpc}/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java (99%) rename {reference-impl => reference/jsonrpc}/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java (100%) create mode 100644 reference/jsonrpc/src/main/resources/META-INF/beans.xml rename {reference-impl => reference/jsonrpc}/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java (100%) rename reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java => reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java (63%) rename {reference-impl => reference/jsonrpc}/src/test/resources/application.properties (100%) rename {sdk-server-common => server-common}/pom.xml (86%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/ExtendedAgentCard.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/JSONRPCException.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/PublicAgentCard.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/ServerCallContext.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/agentexecution/RequestContext.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/auth/User.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/EnhancedRunnable.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/EventConsumer.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/EventQueue.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/EventQueueClosedException.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/InMemoryQueueManager.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/NoTaskQueueException.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/QueueManager.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/events/TaskQueueExistsException.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/PushNotificationSender.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/ResultAggregator.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/TaskManager.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/TaskStore.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/tasks/TaskUpdater.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/util/async/AsyncUtils.java (100%) rename {sdk-server-common => server-common}/src/main/java/io/a2a/server/util/async/Internal.java (100%) rename {sdk-server-common => server-common}/src/main/resources/META-INF/beans.xml (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/events/EventConsumerTest.java (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/events/EventQueueTest.java (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java (91%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/tasks/TaskManagerTest.java (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java (100%) rename {sdk-server-common => server-common}/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java (100%) rename {sdk-server-common => server-common}/src/test/resources/logback.xml (100%) rename {grpc => spec-grpc}/pom.xml (67%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/A2A.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/A2AServiceGrpc.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentCapabilities.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentCard.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentExtension.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentInterface.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentProvider.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentSkill.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/Artifact.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AuthenticationInfo.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/CancelTaskRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/DataPart.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/DataPartOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/FilePart.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/FilePartOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/GetAgentCardRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/GetTaskRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/Message.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/MessageOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/OAuthFlows.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/Part.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/PartOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/PushNotificationConfig.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/Role.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/Security.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SecurityOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SecurityScheme.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SendMessageConfiguration.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SendMessageRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SendMessageResponse.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/StreamResponse.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/StringList.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/StringListOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/Task.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskState.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskStatus.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java (100%) rename {grpc => spec-grpc}/src/main/java/io/a2a/grpc/utils/ProtoUtils.java (100%) rename {grpc => spec-grpc}/src/main/resources/META-INF/beans.xml (100%) create mode 100644 transport/grpc/pom.xml rename {sdk-server-common/src/main/java/io/a2a/server/requesthandlers => transport/grpc/src/main/java/io/a2a/grpc/handler}/GrpcHandler.java (99%) create mode 100644 transport/grpc/src/main/resources/META-INF/beans.xml rename {sdk-server-common/src/test/java/io/a2a/server/requesthandlers => transport/grpc/src/test/java/io/a2a/grpc/handler}/GrpcHandlerTest.java (68%) create mode 100644 transport/jsonrpc/pom.xml rename {sdk-server-common/src/main/java/io/a2a/server/requesthandlers => transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler}/JSONRPCHandler.java (99%) create mode 100644 transport/jsonrpc/src/main/resources/META-INF/beans.xml rename {sdk-server-common/src/test/java/io/a2a/server/requesthandlers => transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler}/JSONRPCHandlerTest.java (86%) diff --git a/README.md b/README.md index 2186dde3b..d3a3db4a3 100644 --- a/README.md +++ b/README.md @@ -42,14 +42,27 @@ The A2A Java SDK provides a [reference A2A server implementation](reference-impl [Server Integrations](#server-integrations) contains a list of community contributed integrations of the server with various runtimes. You might be able to use one of these for your target runtime, or you can use them as inspiration to create your own. -To use the reference implementation add the following dependency to your project: +To use the reference implementation with the JSONRPC protocol add the following dependency to your project: > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* ```xml io.github.a2asdk - a2a-java-reference-server + a2a-java-sdk-reference-jsonrpc + + ${io.a2a.sdk.version} + +``` + +To use the reference implementation with the gRPC protocol add the following dependency to your project: + +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* + +```xml + + io.github.a2asdk + a2a-java-sdk-reference-grpc ${io.a2a.sdk.version} diff --git a/client/pom.xml b/client/pom.xml index d593b54ef..0a71440b4 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -24,12 +24,12 @@ ${project.groupId} - a2a-java-sdk-grpc + a2a-java-sdk-spec ${project.version} ${project.groupId} - a2a-java-sdk-spec + a2a-java-sdk-spec-grpc ${project.version} diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 7900d3cef..d026edb21 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -33,7 +33,7 @@ io.github.a2asdk - a2a-java-reference-server + a2a-java-sdk-reference-jsonrpc ${project.version} diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index ee7c5324b..3a051dbab 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -18,7 +18,7 @@ io.github.a2asdk - a2a-java-reference-server + a2a-java-sdk-reference-jsonrpc io.quarkus diff --git a/pom.xml b/pom.xml index 95898110e..f95d6c094 100644 --- a/pom.xml +++ b/pom.xml @@ -276,16 +276,19 @@ - sdk-server-common + client common - grpc + examples/helloworld + reference/common + reference/grpc + reference/jsonrpc + server-common spec - client - reference-grpc - reference-impl + spec-grpc tck - examples/helloworld tests/server-common + transport/jsonrpc + transport/grpc diff --git a/reference-grpc/src/main/resources/application.properties b/reference-grpc/src/main/resources/application.properties deleted file mode 100644 index f8286bd6f..000000000 --- a/reference-grpc/src/main/resources/application.properties +++ /dev/null @@ -1,5 +0,0 @@ -quarkus.grpc.clients.a2-a-service.host=localhost -quarkus.grpc.clients.a2-a-service.port=9001 - -# The GrpcHandler @ApplicationScoped annotation is not compatible with Quarkus -quarkus.arc.exclude-types=io.a2a.server.requesthandlers.GrpcHandler \ No newline at end of file diff --git a/reference-impl/pom.xml b/reference/common/pom.xml similarity index 86% rename from reference-impl/pom.xml rename to reference/common/pom.xml index b128aa3f0..84f4ff74c 100644 --- a/reference-impl/pom.xml +++ b/reference/common/pom.xml @@ -8,20 +8,16 @@ io.github.a2asdk a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT + ../../pom.xml - a2a-java-reference-server + a2a-java-sdk-reference-common jar - Java A2A Reference Server - Java SDK for the Agent2Agent Protocol (A2A) - A2A Reference Server (based on Quarkus) + Java A2A Reference Server: Common + Java SDK for the Agent2Agent Protocol (A2A) - Common classes for A2A Reference Servers (based on Quarkus) - - ${project.groupId} - a2a-java-sdk-spec - ${project.version} - ${project.groupId} a2a-java-sdk-server-common diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java b/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java similarity index 83% rename from reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java rename to reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java index 2264355ac..95227c008 100644 --- a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/DefaultProducers.java +++ b/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java @@ -1,7 +1,4 @@ -package io.a2a.server.apps.quarkus; - -import java.util.ArrayList; -import java.util.Collections; +package io.a2a.server.common.quarkus; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Default; @@ -9,11 +6,7 @@ import io.a2a.server.PublicAgentCard; import io.a2a.server.agentexecution.AgentExecutor; -import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.events.EventQueue; -import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; -import io.a2a.spec.JSONRPCError; import io.quarkus.arc.DefaultBean; /** diff --git a/reference-impl/src/main/resources/META-INF/beans.xml b/reference/common/src/main/resources/META-INF/beans.xml similarity index 100% rename from reference-impl/src/main/resources/META-INF/beans.xml rename to reference/common/src/main/resources/META-INF/beans.xml diff --git a/reference-grpc/pom.xml b/reference/grpc/pom.xml similarity index 86% rename from reference-grpc/pom.xml rename to reference/grpc/pom.xml index d80dae49f..2f2d7edd6 100644 --- a/reference-grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -7,17 +7,22 @@ io.github.a2asdk a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT - ../pom.xml + ../../pom.xml - reference-grpc - Java A2A gRPC Reference Server + a2a-java-sdk-reference-grpc + Java A2A Reference Server: gRPC Java SDK for the Agent2Agent Protocol (A2A) - A2A gRPC Reference Server (based on Quarkus) ${project.groupId} - a2a-java-sdk-grpc + a2a-java-sdk-reference-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-grpc ${project.version} diff --git a/reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java similarity index 55% rename from reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java rename to reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 05207321b..40abbc95c 100644 --- a/reference-grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -1,20 +1,11 @@ package io.a2a.server.grpc.quarkus; -import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import io.a2a.grpc.SendMessageRequest; -import io.a2a.grpc.SendMessageResponse; -import io.a2a.grpc.StreamResponse; -import io.a2a.grpc.Task; -import io.a2a.grpc.TaskState; -import io.a2a.grpc.TaskStatus; -import io.a2a.grpc.TaskStatusUpdateEvent; import io.a2a.server.PublicAgentCard; -import io.a2a.server.requesthandlers.GrpcHandler; +import io.a2a.grpc.handler.GrpcHandler; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; -import io.grpc.stub.StreamObserver; import io.quarkus.grpc.GrpcService; @GrpcService diff --git a/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java similarity index 98% rename from reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java rename to reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java index 917606e42..25758da9c 100644 --- a/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -19,7 +19,7 @@ import jakarta.ws.rs.core.Response; import io.a2a.server.apps.common.TestUtilsBean; -import io.a2a.server.requesthandlers.GrpcHandler; +import io.a2a.grpc.handler.GrpcHandler; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; diff --git a/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java similarity index 99% rename from reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java rename to reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java index 076d9a29b..30fb9c71c 100644 --- a/reference-grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -88,7 +88,7 @@ public class QuarkusA2AGrpcTest { .build(); public static final String APPLICATION_JSON = "application/json"; - @GrpcClient("a2-a-service") + @GrpcClient("a2a-service") A2AServiceGrpc.A2AServiceBlockingStub client; private final int serverPort = 8081; diff --git a/reference/grpc/src/test/resources/application.properties b/reference/grpc/src/test/resources/application.properties new file mode 100644 index 000000000..840386426 --- /dev/null +++ b/reference/grpc/src/test/resources/application.properties @@ -0,0 +1,5 @@ +quarkus.grpc.clients.a2a-service.host=localhost +quarkus.grpc.clients.a2a-service.port=9001 + +# The GrpcHandler @ApplicationScoped annotation is not compatible with Quarkus +quarkus.arc.exclude-types=io.a2a.grpc.handler.GrpcHandler \ No newline at end of file diff --git a/reference-impl/README.md b/reference/jsonrpc/README.md similarity index 100% rename from reference-impl/README.md rename to reference/jsonrpc/README.md diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml new file mode 100644 index 000000000..3f0116ce2 --- /dev/null +++ b/reference/jsonrpc/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-reference-jsonrpc + + jar + + Java A2A Reference Server: JSONRPC + Java SDK for the Agent2Agent Protocol (A2A) - A2A JSONRPC Reference Server (based on Quarkus) + + + + ${project.groupId} + a2a-java-sdk-reference-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-tests-server-common + ${project.version} + provided + + + ${project.groupId} + a2a-java-sdk-tests-server-common + test-jar + test + ${project.version} + + + io.quarkus + quarkus-reactive-routes + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + org.slf4j + slf4j-api + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-rest-client-jackson + test + + + org.junit.jupiter + junit-jupiter-api + test + + + io.rest-assured + rest-assured + test + + + \ No newline at end of file diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java similarity index 99% rename from reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java rename to reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 988736c91..3ebf22ccd 100644 --- a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -20,11 +20,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; +import io.a2a.jsonrpc.handler.JSONRPCHandler; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; -import io.a2a.server.requesthandlers.JSONRPCHandler; import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; diff --git a/reference-impl/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java similarity index 100% rename from reference-impl/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java rename to reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/CallContextFactory.java diff --git a/reference/jsonrpc/src/main/resources/META-INF/beans.xml b/reference/jsonrpc/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java similarity index 100% rename from reference-impl/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java rename to reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java diff --git a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java similarity index 63% rename from reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java rename to reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java index f9ed48643..f1442da33 100644 --- a/reference-impl/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AServerTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java @@ -4,9 +4,9 @@ import io.quarkus.test.junit.QuarkusTest; @QuarkusTest -public class QuarkusA2AServerTest extends AbstractA2AServerTest { +public class QuarkusA2AJSONRPCTest extends AbstractA2AServerTest { - public QuarkusA2AServerTest() { + public QuarkusA2AJSONRPCTest() { super(8081); } } diff --git a/reference-impl/src/test/resources/application.properties b/reference/jsonrpc/src/test/resources/application.properties similarity index 100% rename from reference-impl/src/test/resources/application.properties rename to reference/jsonrpc/src/test/resources/application.properties diff --git a/sdk-server-common/pom.xml b/server-common/pom.xml similarity index 86% rename from sdk-server-common/pom.xml rename to server-common/pom.xml index a0ed10cca..caf58b2d1 100644 --- a/sdk-server-common/pom.xml +++ b/server-common/pom.xml @@ -27,11 +27,6 @@ a2a-java-sdk-client ${project.version} - - ${project.groupId} - a2a-java-sdk-grpc - ${project.version} - com.fasterxml.jackson.core jackson-databind @@ -89,21 +84,26 @@ mockito-core test - - org.mock-server - mockserver-netty - test - ch.qos.logback logback-classic test - - io.grpc - grpc-testing - test - - \ No newline at end of file + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + diff --git a/sdk-server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java b/server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java rename to server-common/src/main/java/io/a2a/server/ExtendedAgentCard.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/JSONRPCException.java b/server-common/src/main/java/io/a2a/server/JSONRPCException.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/JSONRPCException.java rename to server-common/src/main/java/io/a2a/server/JSONRPCException.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/PublicAgentCard.java b/server-common/src/main/java/io/a2a/server/PublicAgentCard.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/PublicAgentCard.java rename to server-common/src/main/java/io/a2a/server/PublicAgentCard.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java b/server-common/src/main/java/io/a2a/server/ServerCallContext.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/ServerCallContext.java rename to server-common/src/main/java/io/a2a/server/ServerCallContext.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java b/server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java rename to server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java rename to server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java b/server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java rename to server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java b/server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java rename to server-common/src/main/java/io/a2a/server/auth/UnauthenticatedUser.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/auth/User.java b/server-common/src/main/java/io/a2a/server/auth/User.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/auth/User.java rename to server-common/src/main/java/io/a2a/server/auth/User.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java b/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java rename to server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/EventConsumer.java rename to server-common/src/main/java/io/a2a/server/events/EventConsumer.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/EventQueue.java rename to server-common/src/main/java/io/a2a/server/events/EventQueue.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java b/server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java rename to server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java rename to server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java b/server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java rename to server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/QueueManager.java b/server-common/src/main/java/io/a2a/server/events/QueueManager.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/QueueManager.java rename to server-common/src/main/java/io/a2a/server/events/QueueManager.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/events/TaskQueueExistsException.java b/server-common/src/main/java/io/a2a/server/events/TaskQueueExistsException.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/events/TaskQueueExistsException.java rename to server-common/src/main/java/io/a2a/server/events/TaskQueueExistsException.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java b/server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java rename to server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java rename to server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java rename to server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java rename to server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java rename to server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java rename to server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java rename to server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java rename to server-common/src/main/java/io/a2a/server/tasks/PushNotificationSender.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java rename to server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/TaskManager.java rename to server-common/src/main/java/io/a2a/server/tasks/TaskManager.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/TaskStore.java rename to server-common/src/main/java/io/a2a/server/tasks/TaskStore.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java rename to server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java rename to server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java rename to server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java diff --git a/sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java b/server-common/src/main/java/io/a2a/server/util/async/Internal.java similarity index 100% rename from sdk-server-common/src/main/java/io/a2a/server/util/async/Internal.java rename to server-common/src/main/java/io/a2a/server/util/async/Internal.java diff --git a/sdk-server-common/src/main/resources/META-INF/beans.xml b/server-common/src/main/resources/META-INF/beans.xml similarity index 100% rename from sdk-server-common/src/main/resources/META-INF/beans.xml rename to server-common/src/main/resources/META-INF/beans.xml diff --git a/sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java rename to server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java diff --git a/sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java rename to server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java diff --git a/sdk-server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/events/EventQueueTest.java rename to server-common/src/test/java/io/a2a/server/events/EventQueueTest.java diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java similarity index 91% rename from sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java rename to server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index a8cf3fcf0..7232d1a0c 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -54,15 +54,15 @@ public class AbstractA2ARequestHandlerTest { .parts(new TextPart("test message")) .build(); - AgentExecutor executor; - TaskStore taskStore; - RequestHandler requestHandler; - AgentExecutorMethod agentExecutorExecute; - AgentExecutorMethod agentExecutorCancel; - InMemoryQueueManager queueManager; - TestHttpClient httpClient; + protected AgentExecutor executor; + protected TaskStore taskStore; + protected RequestHandler requestHandler; + protected AgentExecutorMethod agentExecutorExecute; + protected AgentExecutorMethod agentExecutorCancel; + protected InMemoryQueueManager queueManager; + protected TestHttpClient httpClient; - final Executor internalExecutor = Executors.newCachedThreadPool(); + protected final Executor internalExecutor = Executors.newCachedThreadPool(); @BeforeEach public void init() { @@ -123,8 +123,8 @@ protected interface AgentExecutorMethod { @Dependent @IfBuildProfile("test") protected static class TestHttpClient implements A2AHttpClient { - final List tasks = Collections.synchronizedList(new ArrayList<>()); - volatile CountDownLatch latch; + public final List tasks = Collections.synchronizedList(new ArrayList<>()); + public volatile CountDownLatch latch; @Override public GetBuilder createGet() { @@ -183,6 +183,7 @@ public PostBuilder url(String s) { public PostBuilder addHeader(String name, String value) { return this; } + } } } diff --git a/sdk-server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java rename to server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java diff --git a/sdk-server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java rename to server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java diff --git a/sdk-server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java rename to server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java diff --git a/sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java b/server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java similarity index 100% rename from sdk-server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java rename to server-common/src/test/java/io/a2a/server/util/async/AsyncUtilsTest.java diff --git a/sdk-server-common/src/test/resources/logback.xml b/server-common/src/test/resources/logback.xml similarity index 100% rename from sdk-server-common/src/test/resources/logback.xml rename to server-common/src/test/resources/logback.xml diff --git a/grpc/pom.xml b/spec-grpc/pom.xml similarity index 67% rename from grpc/pom.xml rename to spec-grpc/pom.xml index a8e7d634e..129be2d7c 100644 --- a/grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -9,16 +9,16 @@ a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT - a2a-java-sdk-grpc + a2a-java-sdk-spec-grpc jar - Java SDK A2A gRPC - Java SDK for the Agent2Agent Protocol (A2A) - gRPC + Java SDK A2A Spec: gRPC + Java SDK for the Agent2Agent Protocol (A2A) - Spec: gRPC - io.github.a2asdk + ${project.groupId} a2a-java-sdk-spec ${project.version} @@ -34,6 +34,14 @@ io.grpc grpc-stub + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + diff --git a/grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/A2A.java rename to spec-grpc/src/main/java/io/a2a/grpc/A2A.java diff --git a/grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java rename to spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java diff --git a/grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java rename to spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java diff --git a/grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentCard.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentExtension.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentExtension.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentInterface.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentProvider.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentProvider.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentSkill.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java diff --git a/grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/Artifact.java b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/Artifact.java rename to spec-grpc/src/main/java/io/a2a/grpc/Artifact.java diff --git a/grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java rename to spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java diff --git a/grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java rename to spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java diff --git a/grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java rename to spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java diff --git a/grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/DataPart.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/DataPart.java rename to spec-grpc/src/main/java/io/a2a/grpc/DataPart.java diff --git a/grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/FilePart.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/FilePart.java rename to spec-grpc/src/main/java/io/a2a/grpc/FilePart.java diff --git a/grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java rename to spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java diff --git a/grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java rename to spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java diff --git a/grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java rename to spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java diff --git a/grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/Message.java b/spec-grpc/src/main/java/io/a2a/grpc/Message.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/Message.java rename to spec-grpc/src/main/java/io/a2a/grpc/Message.java diff --git a/grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java rename to spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java diff --git a/grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/OAuthFlows.java rename to spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java diff --git a/grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java rename to spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java diff --git a/grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/Part.java b/spec-grpc/src/main/java/io/a2a/grpc/Part.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/Part.java rename to spec-grpc/src/main/java/io/a2a/grpc/Part.java diff --git a/grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java rename to spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java diff --git a/grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java rename to spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java diff --git a/grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/Role.java b/spec-grpc/src/main/java/io/a2a/grpc/Role.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/Role.java rename to spec-grpc/src/main/java/io/a2a/grpc/Role.java diff --git a/grpc/src/main/java/io/a2a/grpc/Security.java b/spec-grpc/src/main/java/io/a2a/grpc/Security.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/Security.java rename to spec-grpc/src/main/java/io/a2a/grpc/Security.java diff --git a/grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SecurityScheme.java rename to spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java diff --git a/grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java rename to spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java rename to spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java diff --git a/grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/StreamResponse.java rename to spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java diff --git a/grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/StringList.java b/spec-grpc/src/main/java/io/a2a/grpc/StringList.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/StringList.java rename to spec-grpc/src/main/java/io/a2a/grpc/StringList.java diff --git a/grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/Task.java b/spec-grpc/src/main/java/io/a2a/grpc/Task.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/Task.java rename to spec-grpc/src/main/java/io/a2a/grpc/Task.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskState.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskState.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskState.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatus.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskStatus.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java diff --git a/grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java diff --git a/grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java similarity index 100% rename from grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java rename to spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java diff --git a/grpc/src/main/resources/META-INF/beans.xml b/spec-grpc/src/main/resources/META-INF/beans.xml similarity index 100% rename from grpc/src/main/resources/META-INF/beans.xml rename to spec-grpc/src/main/resources/META-INF/beans.xml diff --git a/tck/pom.xml b/tck/pom.xml index 42c25a6db..5272c1a1c 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -18,7 +18,7 @@ io.github.a2asdk - a2a-java-reference-server + a2a-java-sdk-reference-jsonrpc ${project.version} diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml new file mode 100644 index 000000000..982ae3b8e --- /dev/null +++ b/transport/grpc/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-transport-grpc + + jar + + Java SDK A2A Transport: gRPC + Java SDK for the Agent2Agent Protocol (A2A) - gRPC + + + + io.github.a2asdk + a2a-java-sdk-server-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + test-jar + test + + + ${project.groupId} + a2a-java-sdk-spec-grpc + ${project.version} + + + com.google.protobuf + protobuf-java + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + ch.qos.logback + logback-classic + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + io.grpc + grpc-testing + test + + + + + diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java similarity index 99% rename from sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java rename to transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java index e514feae7..68311009f 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java @@ -1,4 +1,4 @@ -package io.a2a.server.requesthandlers; +package io.a2a.grpc.handler; import static io.a2a.grpc.utils.ProtoUtils.FromProto; import static io.a2a.grpc.utils.ProtoUtils.ToProto; @@ -18,6 +18,8 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; +import io.a2a.server.requesthandlers.CallContextFactory; +import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; diff --git a/transport/grpc/src/main/resources/META-INF/beans.xml b/transport/grpc/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..be7990041 --- /dev/null +++ b/transport/grpc/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java similarity index 68% rename from sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java rename to transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java index 87a1a9bc8..75956f8b8 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java @@ -1,4 +1,4 @@ -package io.a2a.server.requesthandlers; +package io.a2a.grpc.handler; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -36,6 +36,8 @@ import io.a2a.grpc.TaskSubscriptionRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.events.EventConsumer; +import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; +import io.a2a.server.requesthandlers.DefaultRequestHandler; import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; @@ -60,42 +62,42 @@ public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { private static final Message GRPC_MESSAGE = Message.newBuilder() - .setTaskId(MINIMAL_TASK.getId()) - .setContextId(MINIMAL_TASK.getContextId()) - .setMessageId(MESSAGE.getMessageId()) + .setTaskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) + .setMessageId(AbstractA2ARequestHandlerTest.MESSAGE.getMessageId()) .setRole(Role.ROLE_AGENT) - .addContent(Part.newBuilder().setText(((TextPart)MESSAGE.getParts().get(0)).getText()).build()) + .addContent(Part.newBuilder().setText(((TextPart) AbstractA2ARequestHandlerTest.MESSAGE.getParts().get(0)).getText()).build()) .setMetadata(Struct.newBuilder().build()) .build(); @Test public void testOnGetTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); GetTaskRequest request = GetTaskRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.getTask(request, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); Task task = result.get(0); - assertEquals(MINIMAL_TASK.getId(), task.getId()); - assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), task.getId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), task.getContextId()); assertEquals(TaskState.TASK_STATE_SUBMITTED, task.getStatus().getState()); } @Test public void testOnGetTaskNotFound() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); GetTaskRequest request = GetTaskRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); @@ -107,8 +109,8 @@ public void testOnGetTaskNotFound() throws Exception { @Test public void testOnCancelTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { // We need to cancel the task or the EventConsumer never finds a 'final' event. @@ -120,33 +122,33 @@ public void testOnCancelTaskSuccess() throws Exception { }; CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.cancelTask(request, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); Task task = result.get(0); - assertEquals(MINIMAL_TASK.getId(), task.getId()); - assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), task.getId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), task.getContextId()); assertEquals(TaskState.TASK_STATE_CANCELLED, task.getStatus().getState()); } @Test public void testOnCancelTaskNotSupported() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { throw new UnsupportedOperationError(); }; CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.cancelTask(request, streamRecorder); @@ -157,9 +159,9 @@ public void testOnCancelTaskNotSupported() throws Exception { @Test public void testOnCancelTaskNotFound() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.cancelTask(request, streamRecorder); @@ -170,39 +172,39 @@ public void testOnCancelTaskNotFound() throws Exception { @Test public void testOnMessageNewMessageSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; StreamRecorder streamRecorder = sendMessageRequest(handler); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); SendMessageResponse response = result.get(0); assertEquals(GRPC_MESSAGE, response.getMsg()); } @Test public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; StreamRecorder streamRecorder = sendMessageRequest(handler); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); SendMessageResponse response = result.get(0); assertEquals(GRPC_MESSAGE, response.getMsg()); } @Test public void testOnMessageError() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(new UnsupportedOperationError()); }; @@ -212,56 +214,56 @@ public void testOnMessageError() throws Exception { @Test public void testSetPushNotificationConfigSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); TaskPushNotificationConfig response = result.get(0); assertEquals(NAME, response.getName()); PushNotificationConfig responseConfig = response.getPushNotificationConfig(); assertEquals("config456", responseConfig.getId()); assertEquals("http://example.com", responseConfig.getUrl()); assertEquals(AuthenticationInfo.getDefaultInstance(), responseConfig.getAuthentication()); - assertTrue(responseConfig.getToken().isEmpty()); + Assertions.assertTrue(responseConfig.getToken().isEmpty()); } @Test public void testGetPushNotificationConfigSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; // first set the task push notification config StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); // then get the task push notification config streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); TaskPushNotificationConfig response = result.get(0); assertEquals(NAME, response.getName()); PushNotificationConfig responseConfig = response.getPushNotificationConfig(); assertEquals("config456", responseConfig.getId()); assertEquals("http://example.com", responseConfig.getUrl()); assertEquals(AuthenticationInfo.getDefaultInstance(), responseConfig.getAuthentication()); - assertTrue(responseConfig.getToken().isEmpty()); + Assertions.assertTrue(responseConfig.getToken().isEmpty()); } @Test public void testPushNotificationsNotSupportedError() throws Exception { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); GrpcHandler handler = new GrpcHandler(card, requestHandler); - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } @@ -271,9 +273,9 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - AgentCard card = createAgentCard(false, true, false); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new GrpcHandler(card, requestHandler); - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } @@ -283,39 +285,39 @@ public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - AgentCard card = createAgentCard(false, true, false); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new GrpcHandler(card, requestHandler); - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } @Test public void testOnMessageStreamNewMessageSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); StreamResponse response = result.get(0); - assertTrue(response.hasMsg()); + Assertions.assertTrue(response.hasMsg()); Message message = response.getMsg(); - assertEquals(GRPC_MESSAGE, message); + Assertions.assertEquals(GRPC_MESSAGE, message); } @Test public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(MINIMAL_TASK) + io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK) .history(new ArrayList<>()) .build(); taskStore.save(task); @@ -342,15 +344,15 @@ public void onCompleted() { }; sendStreamingMessageRequest(handler, streamObserver); Assertions.assertTrue(latch.await(1, TimeUnit.SECONDS)); - assertTrue(errors.isEmpty()); - assertEquals(1, results.size()); + Assertions.assertTrue(errors.isEmpty()); + Assertions.assertEquals(1, results.size()); StreamResponse response = results.get(0); - assertTrue(response.hasTask()); + Assertions.assertTrue(response.hasTask()); Task taskResponse = response.getTask(); Task expected = Task.newBuilder() - .setId(MINIMAL_TASK.getId()) - .setContextId(MINIMAL_TASK.getContextId()) + .setId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) .addAllHistory(List.of(GRPC_MESSAGE)) .setStatus(TaskStatus.newBuilder().setStateValue(TaskState.TASK_STATE_SUBMITTED_VALUE)) .build(); @@ -362,9 +364,9 @@ public void onCompleted() { @Test public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); - io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(MINIMAL_TASK) + io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK) .history(new ArrayList<>()) .build(); taskStore.save(task); @@ -392,18 +394,18 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ streamRecorder = sendStreamingMessageRequest(handler); } - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertEquals(2, result.size()); + Assertions.assertEquals(2, result.size()); StreamResponse first = result.get(0); - assertTrue(first.hasArtifactUpdate()); + Assertions.assertTrue(first.hasArtifactUpdate()); io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent = first.getArtifactUpdate(); assertEquals(task.getId(), taskArtifactUpdateEvent.getTaskId()); assertEquals(task.getContextId(), taskArtifactUpdateEvent.getContextId()); assertEquals("11", taskArtifactUpdateEvent.getArtifact().getArtifactId()); assertEquals("text", taskArtifactUpdateEvent.getArtifact().getParts(0).getText()); StreamResponse second = result.get(1); - assertTrue(second.hasStatusUpdate()); + Assertions.assertTrue(second.hasStatusUpdate()); io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent = second.getStatusUpdate(); assertEquals(task.getId(), taskStatusUpdateEvent.getTaskId()); assertEquals(task.getContextId(), taskStatusUpdateEvent.getContextId()); @@ -412,20 +414,20 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep @Test public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); List events = List.of( - MINIMAL_TASK, + AbstractA2ARequestHandlerTest.MINIMAL_TASK, new TaskArtifactUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) .artifact(new Artifact.Builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), new TaskStatusUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.COMPLETED)) .build()); @@ -437,9 +439,9 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex } }; - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder pushStreamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); - assertNull(pushStreamRecorder.getError()); + Assertions.assertNull(pushStreamRecorder.getError()); List results = new ArrayList<>(); List errors = new ArrayList<>(); @@ -462,39 +464,39 @@ public void onCompleted() { } }; sendStreamingMessageRequest(handler, streamObserver); - assertTrue(latch.await(5, TimeUnit.SECONDS)); - assertTrue(errors.isEmpty()); - assertEquals(3, results.size()); - assertEquals(3, httpClient.tasks.size()); + Assertions.assertTrue(latch.await(5, TimeUnit.SECONDS)); + Assertions.assertTrue(errors.isEmpty()); + Assertions.assertEquals(3, results.size()); + Assertions.assertEquals(3, httpClient.tasks.size()); io.a2a.spec.Task curr = httpClient.tasks.get(0); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), curr.getId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), curr.getContextId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + Assertions.assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); curr = httpClient.tasks.get(1); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - assertEquals(1, curr.getArtifacts().size()); - assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), curr.getId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), curr.getContextId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + Assertions.assertEquals(1, curr.getArtifacts().size()); + Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); curr = httpClient.tasks.get(2); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.getStatus().state()); - assertEquals(1, curr.getArtifacts().size()); - assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), curr.getId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), curr.getContextId()); + Assertions.assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.getStatus().state()); + Assertions.assertEquals(1, curr.getArtifacts().size()); + Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); } @Test public void testOnResubscribeNoExistingTaskError() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.taskSubscription(request, streamRecorder); @@ -504,9 +506,9 @@ public void testOnResubscribeNoExistingTaskError() throws Exception { @Test public void testOnResubscribeExistingTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); - queueManager.createOrTap(MINIMAL_TASK.getId()); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); @@ -514,7 +516,7 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { StreamRecorder streamRecorder = StreamRecorder.create(); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); handler.taskSubscription(request, streamRecorder); @@ -525,42 +527,42 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { StreamRecorder messageRecorder = StreamRecorder.create(); handler.sendStreamingMessage(sendMessageRequest, messageRecorder); messageRecorder.awaitCompletion(5, TimeUnit.SECONDS); - assertNull(messageRecorder.getError()); + Assertions.assertNull(messageRecorder.getError()); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); List result = streamRecorder.getValues(); - assertNotNull(result); - assertEquals(1, result.size()); + Assertions.assertNotNull(result); + Assertions.assertEquals(1, result.size()); StreamResponse response = result.get(0); - assertTrue(response.hasMsg()); + Assertions.assertTrue(response.hasMsg()); assertEquals(GRPC_MESSAGE, response.getMsg()); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); } @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); - queueManager.createOrTap(MINIMAL_TASK.getId()); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); List events = List.of( new TaskArtifactUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) .artifact(new Artifact.Builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), new TaskStatusUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING)) .build()); StreamRecorder streamRecorder = StreamRecorder.create(); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, @@ -570,20 +572,20 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); } List result = streamRecorder.getValues(); - assertEquals(events.size(), result.size()); + Assertions.assertEquals(events.size(), result.size()); StreamResponse first = result.get(0); - assertTrue(first.hasArtifactUpdate()); + Assertions.assertTrue(first.hasArtifactUpdate()); io.a2a.grpc.TaskArtifactUpdateEvent event = first.getArtifactUpdate(); assertEquals("11", event.getArtifact().getArtifactId()); assertEquals("text", (event.getArtifact().getParts(0)).getText()); StreamResponse second = result.get(1); - assertTrue(second.hasStatusUpdate()); + Assertions.assertTrue(second.hasStatusUpdate()); assertEquals(TaskState.TASK_STATE_WORKING, second.getStatusUpdate().getStatus().getState()); } @Test public void testStreamingNotSupportedError() throws Exception { - AgentCard card = createAgentCard(false, true, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); GrpcHandler handler = new GrpcHandler(card, requestHandler); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); @@ -592,10 +594,10 @@ public void testStreamingNotSupportedError() throws Exception { @Test public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception { // This test does not exist in the Python implementation - AgentCard card = createAgentCard(false, true, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); GrpcHandler handler = new GrpcHandler(card, requestHandler); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.taskSubscription(request, streamRecorder); @@ -607,47 +609,47 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception public void testOnMessageStreamInternalError() throws Exception { DefaultRequestHandler mocked = Mockito.mock(DefaultRequestHandler.class); Mockito.doThrow(new InternalError("Internal Error")).when(mocked).onMessageSendStream(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); - GrpcHandler handler = new GrpcHandler(CARD, mocked); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, mocked); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INTERNAL); } @Test public void testListPushNotificationConfig() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder pushRecorder = createTaskPushNotificationConfigRequest(handler, NAME); - assertNull(pushRecorder.getError()); + Assertions.assertNull(pushRecorder.getError()); ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); - assertNull(streamRecorder.getError()); + Assertions.assertNull(streamRecorder.getError()); List result = streamRecorder.getValues(); - assertEquals(1, result.size()); + Assertions.assertEquals(1, result.size()); List configList = result.get(0).getConfigsList(); - assertEquals(1, configList.size()); - assertEquals(pushRecorder.getValues().get(0), configList.get(0)); + Assertions.assertEquals(1, configList.size()); + Assertions.assertEquals(pushRecorder.getValues().get(0), configList.get(0)); } @Test public void testListPushNotificationConfigNotSupported() throws Exception { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); GrpcHandler handler = new GrpcHandler(card, requestHandler); - taskStore.save(MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -658,14 +660,14 @@ public void testListPushNotificationConfigNotSupported() throws Exception { public void testListPushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -674,13 +676,13 @@ public void testListPushNotificationConfigNoPushConfigStore() { @Test public void testListPushNotificationConfigTaskNotFound() { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -689,36 +691,36 @@ public void testListPushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfig() throws Exception { - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - taskStore.save(MINIMAL_TASK); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder pushRecorder = createTaskPushNotificationConfigRequest(handler, NAME); - assertNull(pushRecorder.getError()); + Assertions.assertNull(pushRecorder.getError()); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.deleteTaskPushNotificationConfig(request, streamRecorder); - assertNull(streamRecorder.getError()); - assertEquals(1, streamRecorder.getValues().size()); + Assertions.assertNull(streamRecorder.getError()); + Assertions.assertEquals(1, streamRecorder.getValues().size()); assertEquals(Empty.getDefaultInstance(), streamRecorder.getValues().get(0)); } @Test public void testDeletePushNotificationConfigNotSupported() throws Exception { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); GrpcHandler handler = new GrpcHandler(card, requestHandler); - taskStore.save(MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) .build(); @@ -731,8 +733,8 @@ public void testDeletePushNotificationConfigNotSupported() throws Exception { public void testDeletePushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - GrpcHandler handler = new GrpcHandler(CARD, requestHandler); - String NAME = "tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId(); + GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) .build(); @@ -752,7 +754,7 @@ private StreamRecorder sendMessageRequest(GrpcHandler handl } private StreamRecorder createTaskPushNotificationConfigRequest(GrpcHandler handler, String name) throws Exception { - taskStore.save(MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); PushNotificationConfig config = PushNotificationConfig.newBuilder() .setUrl("http://example.com") .build(); @@ -798,9 +800,9 @@ private void sendStreamingMessageRequest(GrpcHandler handler, StreamObserver void assertGrpcError(StreamRecorder streamRecorder, Status.Code expectedStatusCode) { - assertNotNull(streamRecorder.getError()); - assertInstanceOf(StatusRuntimeException.class, streamRecorder.getError()); - assertEquals(expectedStatusCode, ((StatusRuntimeException) streamRecorder.getError()).getStatus().getCode()); - assertTrue(streamRecorder.getValues().isEmpty()); + Assertions.assertNotNull(streamRecorder.getError()); + Assertions.assertInstanceOf(StatusRuntimeException.class, streamRecorder.getError()); + Assertions.assertEquals(expectedStatusCode, ((StatusRuntimeException) streamRecorder.getError()).getStatus().getCode()); + Assertions.assertTrue(streamRecorder.getValues().isEmpty()); } } diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml new file mode 100644 index 000000000..b66e3b1f5 --- /dev/null +++ b/transport/jsonrpc/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-transport-jsonrpc + + jar + + Java SDK A2A Transport: JSONRPC + Java SDK for the Agent2Agent Protocol (A2A) - JSONRPC + + + + io.github.a2asdk + a2a-java-sdk-server-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + test-jar + test + + + ch.qos.logback + logback-classic + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + + + diff --git a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java similarity index 99% rename from sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java rename to transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java index fb120f981..b2928dfd5 100644 --- a/sdk-server-common/src/main/java/io/a2a/server/requesthandlers/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java @@ -1,8 +1,7 @@ -package io.a2a.server.requesthandlers; +package io.a2a.jsonrpc.handler; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import java.util.List; @@ -10,6 +9,7 @@ import io.a2a.server.PublicAgentCard; import io.a2a.server.ServerCallContext; +import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; diff --git a/transport/jsonrpc/src/main/resources/META-INF/beans.xml b/transport/jsonrpc/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..9b2940fc2 --- /dev/null +++ b/transport/jsonrpc/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java similarity index 86% rename from sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java rename to transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java index 1851a5a90..070df7aa1 100644 --- a/sdk-server-common/src/test/java/io/a2a/server/requesthandlers/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1,12 +1,4 @@ -package io.a2a.server.requesthandlers; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +package io.a2a.jsonrpc.handler; import java.util.ArrayList; import java.util.Collections; @@ -21,6 +13,8 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; +import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; +import io.a2a.server.requesthandlers.DefaultRequestHandler; import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.AgentCard; @@ -65,6 +59,7 @@ import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import mutiny.zero.ZeroPublisher; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; @@ -80,9 +75,9 @@ public void testOnGetTaskSuccess() throws Exception { taskStore.save(MINIMAL_TASK); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); GetTaskResponse response = handler.onGetTask(request, callContext); - assertEquals(request.getId(), response.getId()); - assertSame(MINIMAL_TASK, response.getResult()); - assertNull(response.getError()); + Assertions.assertEquals(request.getId(), response.getId()); + Assertions.assertSame(MINIMAL_TASK, response.getResult()); + Assertions.assertNull(response.getError()); } @Test @@ -90,9 +85,9 @@ public void testOnGetTaskNotFound() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); GetTaskResponse response = handler.onGetTask(request, callContext); - assertEquals(request.getId(), response.getId()); - assertInstanceOf(TaskNotFoundError.class, response.getError()); - assertNull(response.getResult()); + Assertions.assertEquals(request.getId(), response.getId()); + Assertions.assertInstanceOf(TaskNotFoundError.class, response.getError()); + Assertions.assertNull(response.getResult()); } @Test @@ -112,12 +107,12 @@ public void testOnCancelTaskSuccess() throws Exception { CancelTaskRequest request = new CancelTaskRequest("111", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); - assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); + Assertions.assertNull(response.getError()); + Assertions.assertEquals(request.getId(), response.getId()); Task task = response.getResult(); - assertEquals(MINIMAL_TASK.getId(), task.getId()); - assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); - assertEquals(TaskState.CANCELED, task.getStatus().state()); + Assertions.assertEquals(MINIMAL_TASK.getId(), task.getId()); + Assertions.assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); + Assertions.assertEquals(TaskState.CANCELED, task.getStatus().state()); } @Test @@ -131,9 +126,9 @@ public void testOnCancelTaskNotSupported() { CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - assertInstanceOf(UnsupportedOperationError.class, response.getError()); + Assertions.assertEquals(request.getId(), response.getId()); + Assertions.assertNull(response.getResult()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); } @Test @@ -141,9 +136,9 @@ public void testOnCancelTaskNotFound() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - assertInstanceOf(TaskNotFoundError.class, response.getError()); + Assertions.assertEquals(request.getId(), response.getId()); + Assertions.assertNull(response.getResult()); + Assertions.assertInstanceOf(TaskNotFoundError.class, response.getError()); } @Test @@ -158,14 +153,14 @@ public void testOnMessageNewMessageSuccess() { .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - assertNull(response.getError()); + Assertions.assertNull(response.getError()); // The Python implementation returns a Task here, but then again they are using hardcoded mocks and // bypassing the whole EventQueue. // If we were to send a Task in agentExecutorExecute EventConsumer.consumeAll() would not exit due to // the Task not having a 'final' state // // See testOnMessageNewMessageSuccessMocks() for a test more similar to the Python implementation - assertSame(message, response.getResult()); + Assertions.assertSame(message, response.getResult()); } @Test @@ -184,8 +179,8 @@ public void testOnMessageNewMessageSuccessMocks() { (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } - assertNull(response.getError()); - assertSame(MINIMAL_TASK, response.getResult()); + Assertions.assertNull(response.getError()); + Assertions.assertSame(MINIMAL_TASK, response.getResult()); } @Test @@ -201,14 +196,14 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() { .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - assertNull(response.getError()); + Assertions.assertNull(response.getError()); // The Python implementation returns a Task here, but then again they are using hardcoded mocks and // bypassing the whole EventQueue. // If we were to send a Task in agentExecutorExecute EventConsumer.consumeAll() would not exit due to // the Task not having a 'final' state // // See testOnMessageNewMessageWithExistingTaskSuccessMocks() for a test more similar to the Python implementation - assertSame(message, response.getResult()); + Assertions.assertSame(message, response.getResult()); } @Test @@ -228,8 +223,8 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } - assertNull(response.getError()); - assertSame(MINIMAL_TASK, response.getResult()); + Assertions.assertNull(response.getError()); + Assertions.assertSame(MINIMAL_TASK, response.getResult()); } @@ -248,8 +243,8 @@ public void testOnMessageError() { SendMessageRequest request = new SendMessageRequest( "1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - assertInstanceOf(UnsupportedOperationError.class, response.getError()); - assertNull(response.getResult()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); + Assertions.assertNull(response.getResult()); } @Test @@ -269,8 +264,8 @@ public void testOnMessageErrorMocks() { response = handler.onMessageSend(request, callContext); } - assertInstanceOf(UnsupportedOperationError.class, response.getError()); - assertNull(response.getResult()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); + Assertions.assertNull(response.getResult()); } @Test @@ -324,8 +319,8 @@ public void onComplete() { // so there would be no more Events. // // See testOnMessageStreamNewMessageSuccessMocks() for a test more similar to the Python implementation - assertEquals(1, results.size()); - assertSame(message, results.get(0)); + Assertions.assertEquals(1, results.size()); + Assertions.assertSame(message, results.get(0)); } @Test @@ -392,7 +387,7 @@ public void onComplete() { } }); - assertEquals(events, results); + Assertions.assertEquals(events, results); } @Test @@ -451,7 +446,7 @@ public void onComplete() { }); }); - assertTrue(latch.await(1, TimeUnit.SECONDS)); + Assertions.assertTrue(latch.await(1, TimeUnit.SECONDS)); subscriptionRef.get().cancel(); // The Python implementation has several events emitted since it uses mocks. // @@ -459,14 +454,14 @@ public void onComplete() { Task expected = new Task.Builder(task) .history(message) .build(); - assertEquals(1, results.size()); + Assertions.assertEquals(1, results.size()); StreamingEventKind receivedType = results.get(0); - assertInstanceOf(Task.class, receivedType); + Assertions.assertInstanceOf(Task.class, receivedType); Task received = (Task) receivedType; - assertEquals(expected.getId(), received.getId()); - assertEquals(expected.getContextId(), received.getContextId()); - assertEquals(expected.getStatus(), received.getStatus()); - assertEquals(expected.getHistory(), received.getHistory()); + Assertions.assertEquals(expected.getId(), received.getId()); + Assertions.assertEquals(expected.getContextId(), received.getContextId()); + Assertions.assertEquals(expected.getStatus(), received.getStatus()); + Assertions.assertEquals(expected.getHistory(), received.getHistory()); } @Test @@ -540,7 +535,7 @@ public void onComplete() { } }); - assertEquals(events, results); + Assertions.assertEquals(events, results); } @@ -554,7 +549,7 @@ public void testSetPushNotificationConfigSuccess() { MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - assertSame(taskPushConfig, response.getResult()); + Assertions.assertSame(taskPushConfig, response.getResult()); } @Test @@ -579,7 +574,7 @@ public void testGetPushNotificationConfigSuccess() { TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id(MINIMAL_TASK.getId()).url("http://example.com").build()); - assertEquals(expectedConfig, getResponse.getResult()); + Assertions.assertEquals(expectedConfig, getResponse.getResult()); } @Test @@ -617,7 +612,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); - assertNull(stpnResponse.getError()); + Assertions.assertNull(stpnResponse.getError()); Message msg = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) @@ -659,32 +654,32 @@ public void onComplete() { }); }); - assertTrue(latch.await(5, TimeUnit.SECONDS)); + Assertions.assertTrue(latch.await(5, TimeUnit.SECONDS)); subscriptionRef.get().cancel(); - assertEquals(3, results.size()); - assertEquals(3, httpClient.tasks.size()); + Assertions.assertEquals(3, results.size()); + Assertions.assertEquals(3, httpClient.tasks.size()); Task curr = httpClient.tasks.get(0); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); + Assertions.assertEquals(MINIMAL_TASK.getId(), curr.getId()); + Assertions.assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + Assertions.assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + Assertions.assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); curr = httpClient.tasks.get(1); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - assertEquals(1, curr.getArtifacts().size()); - assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + Assertions.assertEquals(MINIMAL_TASK.getId(), curr.getId()); + Assertions.assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + Assertions.assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + Assertions.assertEquals(1, curr.getArtifacts().size()); + Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); curr = httpClient.tasks.get(2); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(TaskState.COMPLETED, curr.getStatus().state()); - assertEquals(1, curr.getArtifacts().size()); - assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + Assertions.assertEquals(MINIMAL_TASK.getId(), curr.getId()); + Assertions.assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + Assertions.assertEquals(TaskState.COMPLETED, curr.getStatus().state()); + Assertions.assertEquals(1, curr.getArtifacts().size()); + Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); } @Test @@ -713,7 +708,7 @@ public void testOnResubscribeExistingTaskSuccess() { handler.onMessageSend( new SendMessageRequest("1", new MessageSendParams(message, null, null)), callContext); - assertNull(smr.getError()); + Assertions.assertNull(smr.getError()); List results = new ArrayList<>(); @@ -747,7 +742,7 @@ public void onComplete() { // The Python implementation has several events emitted since it uses mocks. // // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation - assertEquals(1, results.size()); + Assertions.assertEquals(1, results.size()); } @@ -815,7 +810,7 @@ public void onComplete() { // The Python implementation has several events emitted since it uses mocks. // // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation - assertEquals(events, results); + Assertions.assertEquals(events, results); } @Test @@ -855,9 +850,9 @@ public void onComplete() { } }); - assertEquals(1, results.size()); - assertNull(results.get(0).getResult()); - assertInstanceOf(TaskNotFoundError.class, results.get(0).getError()); + Assertions.assertEquals(1, results.size()); + Assertions.assertNull(results.get(0).getResult()); + Assertions.assertInstanceOf(TaskNotFoundError.class, results.get(0).getError()); } @Test @@ -902,11 +897,11 @@ public void onComplete() { } }); - assertEquals(1, results.size()); + Assertions.assertEquals(1, results.size()); if (results.get(0).getError() != null && results.get(0).getError() instanceof InvalidRequestError ire) { - assertEquals("Streaming is not supported by the agent", ire.getMessage()); + Assertions.assertEquals("Streaming is not supported by the agent", ire.getMessage()); } else { - fail("Expected a response containing an error"); + Assertions.fail("Expected a response containing an error"); } } @@ -948,11 +943,11 @@ public void onComplete() { } }); - assertEquals(1, results.size()); + Assertions.assertEquals(1, results.size()); if (results.get(0).getError() != null && results.get(0).getError() instanceof InvalidRequestError ire) { - assertEquals("Streaming is not supported by the agent", ire.getMessage()); + Assertions.assertEquals("Streaming is not supported by the agent", ire.getMessage()); } else { - fail("Expected a response containing an error"); + Assertions.fail("Expected a response containing an error"); } } @@ -974,7 +969,7 @@ public void testPushNotificationsNotSupportedError() { .params(config) .build(); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); + Assertions.assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); } @Test @@ -991,9 +986,9 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request, callContext); - assertNotNull(response.getError()); - assertInstanceOf(UnsupportedOperationError.class, response.getError()); - assertEquals("This operation is not supported", response.getError().getMessage()); + Assertions.assertNotNull(response.getError()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); + Assertions.assertEquals("This operation is not supported", response.getError().getMessage()); } @Test @@ -1018,8 +1013,8 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { .build(); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - assertInstanceOf(UnsupportedOperationError.class, response.getError()); - assertEquals("This operation is not supported", response.getError().getMessage()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); + Assertions.assertEquals("This operation is not supported", response.getError().getMessage()); } @Test @@ -1033,7 +1028,7 @@ public void testOnMessageSendInternalError() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - assertInstanceOf(InternalError.class, response.getError()); + Assertions.assertInstanceOf(InternalError.class, response.getError()); } @Test @@ -1077,8 +1072,8 @@ public void onComplete() { } }); - assertEquals(1, results.size()); - assertInstanceOf(InternalError.class, results.get(0).getError()); + Assertions.assertEquals(1, results.size()); + Assertions.assertInstanceOf(InternalError.class, results.get(0).getError()); } @Test @@ -1114,7 +1109,7 @@ public void testOnMessageSendErrorHandling() { response = handler.onMessageSend(request, callContext); } - assertInstanceOf(UnsupportedOperationError.class, response.getError()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); } @@ -1129,7 +1124,7 @@ public void testOnMessageSendTaskIdMismatch() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - assertInstanceOf(InternalError.class, response.getError()); + Assertions.assertInstanceOf(InternalError.class, response.getError()); } @@ -1174,9 +1169,9 @@ public void onComplete() { } }); - assertNull(error.get()); - assertEquals(1, results.size()); - assertInstanceOf(InternalError.class, results.get(0).getError()); + Assertions.assertNull(error.get()); + Assertions.assertEquals(1, results.size()); + Assertions.assertInstanceOf(InternalError.class, results.get(0).getError()); } @Test @@ -1200,9 +1195,9 @@ public void testListPushNotificationConfig() { new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - assertEquals("111", listResponse.getId()); - assertEquals(1, listResponse.getResult().size()); - assertEquals(taskPushConfig, listResponse.getResult().get(0)); + Assertions.assertEquals("111", listResponse.getId()); + Assertions.assertEquals(1, listResponse.getResult().size()); + Assertions.assertEquals(taskPushConfig, listResponse.getResult().get(0)); } @Test @@ -1228,9 +1223,9 @@ public void testListPushNotificationConfigNotSupported() { ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - assertEquals("111", listResponse.getId()); - assertNull(listResponse.getResult()); - assertInstanceOf(PushNotificationNotSupportedError.class, listResponse.getError()); + Assertions.assertEquals("111", listResponse.getId()); + Assertions.assertNull(listResponse.getResult()); + Assertions.assertInstanceOf(PushNotificationNotSupportedError.class, listResponse.getError()); } @Test @@ -1248,9 +1243,9 @@ public void testListPushNotificationConfigNoPushConfigStore() { ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - assertEquals("111", listResponse.getId()); - assertNull(listResponse.getResult()); - assertInstanceOf(UnsupportedOperationError.class, listResponse.getError()); + Assertions.assertEquals("111", listResponse.getId()); + Assertions.assertNull(listResponse.getResult()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, listResponse.getError()); } @Test @@ -1265,9 +1260,9 @@ public void testListPushNotificationConfigTaskNotFound() { ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - assertEquals("111", listResponse.getId()); - assertNull(listResponse.getResult()); - assertInstanceOf(TaskNotFoundError.class, listResponse.getError()); + Assertions.assertEquals("111", listResponse.getId()); + Assertions.assertNull(listResponse.getResult()); + Assertions.assertInstanceOf(TaskNotFoundError.class, listResponse.getError()); } @Test @@ -1292,9 +1287,9 @@ public void testDeletePushNotificationConfig() { DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); - assertEquals("111", deleteResponse.getId()); - assertNull(deleteResponse.getError()); - assertNull(deleteResponse.getResult()); + Assertions.assertEquals("111", deleteResponse.getId()); + Assertions.assertNull(deleteResponse.getError()); + Assertions.assertNull(deleteResponse.getResult()); } @Test @@ -1320,9 +1315,9 @@ public void testDeletePushNotificationConfigNotSupported() { DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); - assertEquals("111", deleteResponse.getId()); - assertNull(deleteResponse.getResult()); - assertInstanceOf(PushNotificationNotSupportedError.class, deleteResponse.getError()); + Assertions.assertEquals("111", deleteResponse.getId()); + Assertions.assertNull(deleteResponse.getResult()); + Assertions.assertInstanceOf(PushNotificationNotSupportedError.class, deleteResponse.getError()); } @Test @@ -1349,9 +1344,9 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); - assertEquals("111", deleteResponse.getId()); - assertNull(deleteResponse.getResult()); - assertInstanceOf(UnsupportedOperationError.class, deleteResponse.getError()); + Assertions.assertEquals("111", deleteResponse.getId()); + Assertions.assertNull(deleteResponse.getResult()); + Assertions.assertInstanceOf(UnsupportedOperationError.class, deleteResponse.getError()); } } From 9a5cce49cb7520cde86c489feedf392acdb8e930 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 7 Aug 2025 18:01:14 +0200 Subject: [PATCH 059/493] chore: Generate grpc classes from protobuf in build (#217) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- README.md | 7 + pom.xml | 3 + spec-grpc/pom.xml | 85 ++- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 411 +++++------ .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 7 +- .../io/a2a/grpc/APIKeySecurityScheme.java | 42 +- .../grpc/APIKeySecuritySchemeOrBuilder.java | 12 +- .../java/io/a2a/grpc/AgentCapabilities.java | 62 +- .../a2a/grpc/AgentCapabilitiesOrBuilder.java | 14 +- .../src/main/java/io/a2a/grpc/AgentCard.java | 368 +++++----- .../java/io/a2a/grpc/AgentCardOrBuilder.java | 98 +-- .../main/java/io/a2a/grpc/AgentExtension.java | 60 +- .../io/a2a/grpc/AgentExtensionOrBuilder.java | 16 +- .../main/java/io/a2a/grpc/AgentInterface.java | 28 +- .../io/a2a/grpc/AgentInterfaceOrBuilder.java | 8 +- .../main/java/io/a2a/grpc/AgentProvider.java | 28 +- .../io/a2a/grpc/AgentProviderOrBuilder.java | 8 +- .../src/main/java/io/a2a/grpc/AgentSkill.java | 146 ++-- .../java/io/a2a/grpc/AgentSkillOrBuilder.java | 44 +- .../src/main/java/io/a2a/grpc/Artifact.java | 138 ++-- .../java/io/a2a/grpc/ArtifactOrBuilder.java | 36 +- .../java/io/a2a/grpc/AuthenticationInfo.java | 40 +- .../a2a/grpc/AuthenticationInfoOrBuilder.java | 12 +- .../a2a/grpc/AuthorizationCodeOAuthFlow.java | 64 +- .../AuthorizationCodeOAuthFlowOrBuilder.java | 22 +- .../java/io/a2a/grpc/CancelTaskRequest.java | 14 +- .../a2a/grpc/CancelTaskRequestOrBuilder.java | 4 +- .../a2a/grpc/ClientCredentialsOAuthFlow.java | 50 +- .../ClientCredentialsOAuthFlowOrBuilder.java | 18 +- ...eateTaskPushNotificationConfigRequest.java | 52 +- ...ushNotificationConfigRequestOrBuilder.java | 14 +- .../src/main/java/io/a2a/grpc/DataPart.java | 24 +- .../java/io/a2a/grpc/DataPartOrBuilder.java | 6 +- ...leteTaskPushNotificationConfigRequest.java | 14 +- ...ushNotificationConfigRequestOrBuilder.java | 4 +- .../src/main/java/io/a2a/grpc/FilePart.java | 44 +- .../java/io/a2a/grpc/FilePartOrBuilder.java | 14 +- .../GetTaskPushNotificationConfigRequest.java | 14 +- ...ushNotificationConfigRequestOrBuilder.java | 4 +- .../main/java/io/a2a/grpc/GetTaskRequest.java | 22 +- .../io/a2a/grpc/GetTaskRequestOrBuilder.java | 6 +- .../io/a2a/grpc/HTTPAuthSecurityScheme.java | 42 +- .../grpc/HTTPAuthSecuritySchemeOrBuilder.java | 12 +- .../java/io/a2a/grpc/ImplicitOAuthFlow.java | 50 +- .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java | 18 +- ...ListTaskPushNotificationConfigRequest.java | 36 +- ...ushNotificationConfigRequestOrBuilder.java | 10 +- ...istTaskPushNotificationConfigResponse.java | 60 +- ...shNotificationConfigResponseOrBuilder.java | 14 +- .../src/main/java/io/a2a/grpc/Message.java | 152 ++-- .../java/io/a2a/grpc/MessageOrBuilder.java | 40 +- .../io/a2a/grpc/OAuth2SecurityScheme.java | 38 +- .../grpc/OAuth2SecuritySchemeOrBuilder.java | 10 +- .../src/main/java/io/a2a/grpc/OAuthFlows.java | 96 +-- .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java | 24 +- .../a2a/grpc/OpenIdConnectSecurityScheme.java | 28 +- .../OpenIdConnectSecuritySchemeOrBuilder.java | 8 +- spec-grpc/src/main/java/io/a2a/grpc/Part.java | 66 +- .../main/java/io/a2a/grpc/PartOrBuilder.java | 18 +- .../java/io/a2a/grpc/PasswordOAuthFlow.java | 50 +- .../a2a/grpc/PasswordOAuthFlowOrBuilder.java | 18 +- .../io/a2a/grpc/PushNotificationConfig.java | 66 +- .../grpc/PushNotificationConfigOrBuilder.java | 18 +- .../src/main/java/io/a2a/grpc/Security.java | 24 +- .../java/io/a2a/grpc/SecurityOrBuilder.java | 10 +- .../main/java/io/a2a/grpc/SecurityScheme.java | 96 +-- .../io/a2a/grpc/SecuritySchemeOrBuilder.java | 24 +- .../io/a2a/grpc/SendMessageConfiguration.java | 66 +- .../SendMessageConfigurationOrBuilder.java | 18 +- .../java/io/a2a/grpc/SendMessageRequest.java | 72 +- .../a2a/grpc/SendMessageRequestOrBuilder.java | 18 +- .../java/io/a2a/grpc/SendMessageResponse.java | 24 +- .../grpc/SendMessageResponseOrBuilder.java | 6 +- .../main/java/io/a2a/grpc/StreamResponse.java | 72 +- .../io/a2a/grpc/StreamResponseOrBuilder.java | 18 +- .../src/main/java/io/a2a/grpc/StringList.java | 26 +- .../java/io/a2a/grpc/StringListOrBuilder.java | 8 +- spec-grpc/src/main/java/io/a2a/grpc/Task.java | 168 ++--- .../io/a2a/grpc/TaskArtifactUpdateEvent.java | 92 +-- .../TaskArtifactUpdateEventOrBuilder.java | 24 +- .../main/java/io/a2a/grpc/TaskOrBuilder.java | 40 +- .../a2a/grpc/TaskPushNotificationConfig.java | 38 +- .../TaskPushNotificationConfigOrBuilder.java | 10 +- .../src/main/java/io/a2a/grpc/TaskStatus.java | 38 +- .../java/io/a2a/grpc/TaskStatusOrBuilder.java | 10 +- .../io/a2a/grpc/TaskStatusUpdateEvent.java | 84 +-- .../grpc/TaskStatusUpdateEventOrBuilder.java | 22 +- .../io/a2a/grpc/TaskSubscriptionRequest.java | 14 +- .../TaskSubscriptionRequestOrBuilder.java | 4 +- spec-grpc/src/main/proto/a2a.proto | 674 ++++++++++++++++++ .../io/a2a/grpc/handler/GrpcHandlerTest.java | 6 - 91 files changed, 2681 insertions(+), 1962 deletions(-) create mode 100644 spec-grpc/src/main/proto/a2a.proto diff --git a/README.md b/README.md index d3a3db4a3..0b528adf0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,13 @@ You can build the A2A Java SDK using `mvn`: mvn clean install ``` +### Regeneration of gRPC files +We copy https://github.com/a2aproject/A2A/blob/main/specification/grpc/a2a.proto to the [`spec-grpc/`](./spec-grpc) project, and adjust the `java_package` option to be as follows: +``` +option java_package = "io.a2a.grpc"; +``` +Then build the `spec-grpc` module with `mvn clean install -Pproto-compile` to regenerate the gRPC classes in the `io.a2a.grpc` package. + ## Examples You can find an example of how to use the A2A Java SDK in the [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent/weather_agent). diff --git a/pom.xml b/pom.xml index f95d6c094..0a3bfba88 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ 1.73.0 UTF-8 + 3.5.0 3.11.0 3.4.2 3.3.1 @@ -56,7 +57,9 @@ 5.17.0 5.15.0 1.1.1 + 1.7.1 4.31.1 + 0.6.1 3.24.5 5.5.1 2.0.17 diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 129be2d7c..cb97c6ca2 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -42,6 +42,89 @@ jakarta.inject jakarta.inject-api + + com.google.api.grpc + proto-google-common-protos + + + + + javax.annotation + javax.annotation-api + provided + - + + + + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + + + + + + proto-compile + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + remove-generated-files + initialize + + clean + + + true + + + ${project.basedir}/src/main/java/io/a2a/grpc + false + + *.java + + + + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${protobuf-maven-plugin.version} + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + src/main/java + false + + + + + compile + compile-custom + + + + + + + + + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index 1b38002d7..ae331178e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -285,232 +285,191 @@ public static void registerAllExtensions( "le/api/field_behavior.proto\032\033google/prot" + "obuf/empty.proto\032\034google/protobuf/struct" + ".proto\032\037google/protobuf/timestamp.proto\"" + - "\336\001\n\030SendMessageConfiguration\0222\n\025accepted" + - "_output_modes\030\001 \003(\tR\023acceptedOutputModes" + - "\022K\n\021push_notification\030\002 \001(\0132\036.a2a.v1.Pus" + - "hNotificationConfigR\020pushNotification\022%\n" + - "\016history_length\030\003 \001(\005R\rhistoryLength\022\032\n\010" + - "blocking\030\004 \001(\010R\010blocking\"\361\001\n\004Task\022\016\n\002id\030" + - "\001 \001(\tR\002id\022\035\n\ncontext_id\030\002 \001(\tR\tcontextId" + - "\022*\n\006status\030\003 \001(\0132\022.a2a.v1.TaskStatusR\006st" + - "atus\022.\n\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifac" + - "tR\tartifacts\022)\n\007history\030\005 \003(\0132\017.a2a.v1.M" + - "essageR\007history\0223\n\010metadata\030\006 \001(\0132\027.goog" + - "le.protobuf.StructR\010metadata\"\231\001\n\nTaskSta" + - "tus\022\'\n\005state\030\001 \001(\0162\021.a2a.v1.TaskStateR\005s" + - "tate\022(\n\006update\030\002 \001(\0132\017.a2a.v1.MessageR\007m" + - "essage\0228\n\ttimestamp\030\003 \001(\0132\032.google.proto" + - "buf.TimestampR\ttimestamp\"t\n\004Part\022\024\n\004text" + - "\030\001 \001(\tH\000R\004text\022&\n\004file\030\002 \001(\0132\020.a2a.v1.Fi" + - "lePartH\000R\004file\022&\n\004data\030\003 \001(\0132\020.a2a.v1.Da" + - "taPartH\000R\004dataB\006\n\004part\"\177\n\010FilePart\022$\n\rfi" + - "le_with_uri\030\001 \001(\tH\000R\013fileWithUri\022(\n\017file" + - "_with_bytes\030\002 \001(\014H\000R\rfileWithBytes\022\033\n\tmi" + - "me_type\030\003 \001(\tR\010mimeTypeB\006\n\004file\"7\n\010DataP" + - "art\022+\n\004data\030\001 \001(\0132\027.google.protobuf.Stru" + - "ctR\004data\"\377\001\n\007Message\022\035\n\nmessage_id\030\001 \001(\t" + - "R\tmessageId\022\035\n\ncontext_id\030\002 \001(\tR\tcontext" + - "Id\022\027\n\007task_id\030\003 \001(\tR\006taskId\022 \n\004role\030\004 \001(" + - "\0162\014.a2a.v1.RoleR\004role\022&\n\007content\030\005 \003(\0132\014" + - ".a2a.v1.PartR\007content\0223\n\010metadata\030\006 \001(\0132" + - "\027.google.protobuf.StructR\010metadata\022\036\n\nex" + - "tensions\030\007 \003(\tR\nextensions\"\332\001\n\010Artifact\022" + - "\037\n\013artifact_id\030\001 \001(\tR\nartifactId\022\022\n\004name" + - "\030\003 \001(\tR\004name\022 \n\013description\030\004 \001(\tR\013descr" + - "iption\022\"\n\005parts\030\005 \003(\0132\014.a2a.v1.PartR\005par" + - "ts\0223\n\010metadata\030\006 \001(\0132\027.google.protobuf.S" + - "tructR\010metadata\022\036\n\nextensions\030\007 \003(\tR\next" + - "ensions\"\306\001\n\025TaskStatusUpdateEvent\022\027\n\007tas" + - "k_id\030\001 \001(\tR\006taskId\022\035\n\ncontext_id\030\002 \001(\tR\t" + - "contextId\022*\n\006status\030\003 \001(\0132\022.a2a.v1.TaskS" + - "tatusR\006status\022\024\n\005final\030\004 \001(\010R\005final\0223\n\010m" + - "etadata\030\005 \001(\0132\027.google.protobuf.StructR\010" + - "metadata\"\353\001\n\027TaskArtifactUpdateEvent\022\027\n\007" + - "task_id\030\001 \001(\tR\006taskId\022\035\n\ncontext_id\030\002 \001(" + - "\tR\tcontextId\022,\n\010artifact\030\003 \001(\0132\020.a2a.v1." + - "ArtifactR\010artifact\022\026\n\006append\030\004 \001(\010R\006appe" + - "nd\022\035\n\nlast_chunk\030\005 \001(\010R\tlastChunk\0223\n\010met" + - "adata\030\006 \001(\0132\027.google.protobuf.StructR\010me" + - "tadata\"\224\001\n\026PushNotificationConfig\022\016\n\002id\030" + - "\001 \001(\tR\002id\022\020\n\003url\030\002 \001(\tR\003url\022\024\n\005token\030\003 \001" + - "(\tR\005token\022B\n\016authentication\030\004 \001(\0132\032.a2a." + - "v1.AuthenticationInfoR\016authentication\"P\n" + - "\022AuthenticationInfo\022\030\n\007schemes\030\001 \003(\tR\007sc" + - "hemes\022 \n\013credentials\030\002 \001(\tR\013credentials\"" + - "@\n\016AgentInterface\022\020\n\003url\030\001 \001(\tR\003url\022\034\n\tt" + - "ransport\030\002 \001(\tR\ttransport\"\361\006\n\tAgentCard\022" + - ")\n\020protocol_version\030\020 \001(\tR\017protocolVersi" + - "on\022\022\n\004name\030\001 \001(\tR\004name\022 \n\013description\030\002 " + - "\001(\tR\013description\022\020\n\003url\030\003 \001(\tR\003url\022/\n\023pr" + - "eferred_transport\030\016 \001(\tR\022preferredTransp" + - "ort\022K\n\025additional_interfaces\030\017 \003(\0132\026.a2a" + - ".v1.AgentInterfaceR\024additionalInterfaces" + - "\0221\n\010provider\030\004 \001(\0132\025.a2a.v1.AgentProvide" + - "rR\010provider\022\030\n\007version\030\005 \001(\tR\007version\022+\n" + - "\021documentation_url\030\006 \001(\tR\020documentationU" + - "rl\022=\n\014capabilities\030\007 \001(\0132\031.a2a.v1.AgentC" + - "apabilitiesR\014capabilities\022Q\n\020security_sc" + - "hemes\030\010 \003(\0132&.a2a.v1.AgentCard.SecurityS" + - "chemesEntryR\017securitySchemes\022,\n\010security" + - "\030\t \003(\0132\020.a2a.v1.SecurityR\010security\022.\n\023de" + - "fault_input_modes\030\n \003(\tR\021defaultInputMod" + - "es\0220\n\024default_output_modes\030\013 \003(\tR\022defaul" + - "tOutputModes\022*\n\006skills\030\014 \003(\0132\022.a2a.v1.Ag" + - "entSkillR\006skills\022O\n$supports_authenticat" + - "ed_extended_card\030\r \001(\010R!supportsAuthenti" + - "catedExtendedCard\032Z\n\024SecuritySchemesEntr" + - "y\022\020\n\003key\030\001 \001(\tR\003key\022,\n\005value\030\002 \001(\0132\026.a2a" + - ".v1.SecuritySchemeR\005value:\0028\001\"E\n\rAgentPr" + - "ovider\022\020\n\003url\030\001 \001(\tR\003url\022\"\n\014organization" + - "\030\002 \001(\tR\014organization\"\230\001\n\021AgentCapabiliti" + - "es\022\034\n\tstreaming\030\001 \001(\010R\tstreaming\022-\n\022push" + - "_notifications\030\002 \001(\010R\021pushNotifications\022" + - "6\n\nextensions\030\003 \003(\0132\026.a2a.v1.AgentExtens" + - "ionR\nextensions\"\221\001\n\016AgentExtension\022\020\n\003ur" + - "i\030\001 \001(\tR\003uri\022 \n\013description\030\002 \001(\tR\013descr" + - "iption\022\032\n\010required\030\003 \001(\010R\010required\022/\n\006pa" + - "rams\030\004 \001(\0132\027.google.protobuf.StructR\006par" + - "ams\"\306\001\n\nAgentSkill\022\016\n\002id\030\001 \001(\tR\002id\022\022\n\004na" + - "me\030\002 \001(\tR\004name\022 \n\013description\030\003 \001(\tR\013des" + - "cription\022\022\n\004tags\030\004 \003(\tR\004tags\022\032\n\010examples" + - "\030\005 \003(\tR\010examples\022\037\n\013input_modes\030\006 \003(\tR\ni" + - "nputModes\022!\n\014output_modes\030\007 \003(\tR\013outputM" + - "odes\"\212\001\n\032TaskPushNotificationConfig\022\022\n\004n" + - "ame\030\001 \001(\tR\004name\022X\n\030push_notification_con" + - "fig\030\002 \001(\0132\036.a2a.v1.PushNotificationConfi" + - "gR\026pushNotificationConfig\" \n\nStringList\022" + - "\022\n\004list\030\001 \003(\tR\004list\"\223\001\n\010Security\0227\n\007sche" + - "mes\030\001 \003(\0132\035.a2a.v1.Security.SchemesEntry" + - "R\007schemes\032N\n\014SchemesEntry\022\020\n\003key\030\001 \001(\tR\003" + - "key\022(\n\005value\030\002 \001(\0132\022.a2a.v1.StringListR\005" + - "value:\0028\001\"\221\003\n\016SecurityScheme\022U\n\027api_key_" + - "security_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySec" + - "uritySchemeH\000R\024apiKeySecurityScheme\022[\n\031h" + - "ttp_auth_security_scheme\030\002 \001(\0132\036.a2a.v1." + - "HTTPAuthSecuritySchemeH\000R\026httpAuthSecuri" + - "tyScheme\022T\n\026oauth2_security_scheme\030\003 \001(\013" + - "2\034.a2a.v1.OAuth2SecuritySchemeH\000R\024oauth2" + - "SecurityScheme\022k\n\037open_id_connect_securi" + - "ty_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConnectSe" + - "curitySchemeH\000R\033openIdConnectSecuritySch" + - "emeB\010\n\006scheme\"h\n\024APIKeySecurityScheme\022 \n" + - "\013description\030\001 \001(\tR\013description\022\032\n\010locat" + - "ion\030\002 \001(\tR\010location\022\022\n\004name\030\003 \001(\tR\004name\"" + - "w\n\026HTTPAuthSecurityScheme\022 \n\013description" + - "\030\001 \001(\tR\013description\022\026\n\006scheme\030\002 \001(\tR\006sch" + - "eme\022#\n\rbearer_format\030\003 \001(\tR\014bearerFormat" + - "\"b\n\024OAuth2SecurityScheme\022 \n\013description\030" + - "\001 \001(\tR\013description\022(\n\005flows\030\002 \001(\0132\022.a2a." + - "v1.OAuthFlowsR\005flows\"n\n\033OpenIdConnectSec" + - "urityScheme\022 \n\013description\030\001 \001(\tR\013descri" + - "ption\022-\n\023open_id_connect_url\030\002 \001(\tR\020open" + - "IdConnectUrl\"\260\002\n\nOAuthFlows\022S\n\022authoriza" + - "tion_code\030\001 \001(\0132\".a2a.v1.AuthorizationCo" + - "deOAuthFlowH\000R\021authorizationCode\022S\n\022clie" + - "nt_credentials\030\002 \001(\0132\".a2a.v1.ClientCred" + - "entialsOAuthFlowH\000R\021clientCredentials\0227\n" + - "\010implicit\030\003 \001(\0132\031.a2a.v1.ImplicitOAuthFl" + - "owH\000R\010implicit\0227\n\010password\030\004 \001(\0132\031.a2a.v" + - "1.PasswordOAuthFlowH\000R\010passwordB\006\n\004flow\"" + - "\212\002\n\032AuthorizationCodeOAuthFlow\022+\n\021author" + - "ization_url\030\001 \001(\tR\020authorizationUrl\022\033\n\tt" + - "oken_url\030\002 \001(\tR\010tokenUrl\022\037\n\013refresh_url\030" + - "\003 \001(\tR\nrefreshUrl\022F\n\006scopes\030\004 \003(\0132..a2a." + - "v1.AuthorizationCodeOAuthFlow.ScopesEntr" + - "yR\006scopes\0329\n\013ScopesEntry\022\020\n\003key\030\001 \001(\tR\003k" + - "ey\022\024\n\005value\030\002 \001(\tR\005value:\0028\001\"\335\001\n\032ClientC" + - "redentialsOAuthFlow\022\033\n\ttoken_url\030\001 \001(\tR\010" + - "tokenUrl\022\037\n\013refresh_url\030\002 \001(\tR\nrefreshUr" + - "l\022F\n\006scopes\030\003 \003(\0132..a2a.v1.ClientCredent" + - "ialsOAuthFlow.ScopesEntryR\006scopes\0329\n\013Sco" + - "pesEntry\022\020\n\003key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001(" + - "\tR\005value:\0028\001\"\333\001\n\021ImplicitOAuthFlow\022+\n\021au" + - "thorization_url\030\001 \001(\tR\020authorizationUrl\022" + - "\037\n\013refresh_url\030\002 \001(\tR\nrefreshUrl\022=\n\006scop" + - "es\030\003 \003(\0132%.a2a.v1.ImplicitOAuthFlow.Scop" + - "esEntryR\006scopes\0329\n\013ScopesEntry\022\020\n\003key\030\001 " + - "\001(\tR\003key\022\024\n\005value\030\002 \001(\tR\005value:\0028\001\"\313\001\n\021P" + - "asswordOAuthFlow\022\033\n\ttoken_url\030\001 \001(\tR\010tok" + - "enUrl\022\037\n\013refresh_url\030\002 \001(\tR\nrefreshUrl\022=" + - "\n\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuthFlo" + - "w.ScopesEntryR\006scopes\0329\n\013ScopesEntry\022\020\n\003" + - "key\030\001 \001(\tR\003key\022\024\n\005value\030\002 \001(\tR\005value:\0028\001" + - "\"\301\001\n\022SendMessageRequest\022.\n\007request\030\001 \001(\013" + - "2\017.a2a.v1.MessageB\003\340A\002R\007request\022F\n\rconfi" + - "guration\030\002 \001(\0132 .a2a.v1.SendMessageConfi" + - "gurationR\rconfiguration\0223\n\010metadata\030\003 \001(" + - "\0132\027.google.protobuf.StructR\010metadata\"P\n\016" + - "GetTaskRequest\022\027\n\004name\030\001 \001(\tB\003\340A\002R\004name\022" + - "%\n\016history_length\030\002 \001(\005R\rhistoryLength\"\'" + - "\n\021CancelTaskRequest\022\022\n\004name\030\001 \001(\tR\004name\"" + - ":\n$GetTaskPushNotificationConfigRequest\022" + - "\022\n\004name\030\001 \001(\tR\004name\"=\n\'DeleteTaskPushNot" + - "ificationConfigRequest\022\022\n\004name\030\001 \001(\tR\004na" + - "me\"\251\001\n\'CreateTaskPushNotificationConfigR" + - "equest\022\033\n\006parent\030\001 \001(\tB\003\340A\002R\006parent\022 \n\tc" + - "onfig_id\030\002 \001(\tB\003\340A\002R\010configId\022?\n\006config\030" + - "\003 \001(\0132\".a2a.v1.TaskPushNotificationConfi" + - "gB\003\340A\002R\006config\"-\n\027TaskSubscriptionReques" + - "t\022\022\n\004name\030\001 \001(\tR\004name\"{\n%ListTaskPushNot" + - "ificationConfigRequest\022\026\n\006parent\030\001 \001(\tR\006" + - "parent\022\033\n\tpage_size\030\002 \001(\005R\010pageSize\022\035\n\np" + - "age_token\030\003 \001(\tR\tpageToken\"\025\n\023GetAgentCa" + - "rdRequest\"m\n\023SendMessageResponse\022\"\n\004task" + - "\030\001 \001(\0132\014.a2a.v1.TaskH\000R\004task\022\'\n\003msg\030\002 \001(" + - "\0132\017.a2a.v1.MessageH\000R\007messageB\t\n\007payload" + - "\"\372\001\n\016StreamResponse\022\"\n\004task\030\001 \001(\0132\014.a2a." + - "v1.TaskH\000R\004task\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.Me" + - "ssageH\000R\007message\022D\n\rstatus_update\030\003 \001(\0132" + - "\035.a2a.v1.TaskStatusUpdateEventH\000R\014status" + - "Update\022J\n\017artifact_update\030\004 \001(\0132\037.a2a.v1" + - ".TaskArtifactUpdateEventH\000R\016artifactUpda" + - "teB\t\n\007payload\"\216\001\n&ListTaskPushNotificati" + - "onConfigResponse\022<\n\007configs\030\001 \003(\0132\".a2a." + - "v1.TaskPushNotificationConfigR\007configs\022&" + - "\n\017next_page_token\030\002 \001(\tR\rnextPageToken*\372" + - "\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020\000" + - "\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STATE" + - "_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n\021" + - "TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCEL" + - "LED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n\023" + - "TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUTH" + - "_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020" + - "\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\272\n\n\nA2A" + - "Service\022c\n\013SendMessage\022\032.a2a.v1.SendMess" + - "ageRequest\032\033.a2a.v1.SendMessageResponse\"" + - "\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024SendStre" + - "amingMessage\022\032.a2a.v1.SendMessageRequest" + - "\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"\022/v1/me" + - "ssage:stream:\001*0\001\022R\n\007GetTask\022\026.a2a.v1.Ge" + - "tTaskRequest\032\014.a2a.v1.Task\"!\332A\004name\202\323\344\223\002" + - "\024\022\022/v1/{name=tasks/*}\022[\n\nCancelTask\022\031.a2" + - "a.v1.CancelTaskRequest\032\014.a2a.v1.Task\"$\202\323" + - "\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*\022s\n\020Ta" + - "skSubscription\022\037.a2a.v1.TaskSubscription" + - "Request\032\026.a2a.v1.StreamResponse\"$\202\323\344\223\002\036\022" + - "\034/v1/{name=tasks/*}:subscribe0\001\022\304\001\n Crea" + - "teTaskPushNotificationConfig\022/.a2a.v1.Cr" + - "eateTaskPushNotificationConfigRequest\032\"." + - "a2a.v1.TaskPushNotificationConfig\"K\332A\rpa" + - "rent,config\202\323\344\223\0025\"+/v1/{parent=task/*/pu" + - "shNotificationConfigs}:\006config\022\256\001\n\035GetTa" + - "skPushNotificationConfig\022,.a2a.v1.GetTas" + - "kPushNotificationConfigRequest\032\".a2a.v1." + - "TaskPushNotificationConfig\";\332A\004name\202\323\344\223\002" + - ".\022,/v1/{name=tasks/*/pushNotificationCon" + - "figs/*}\022\276\001\n\036ListTaskPushNotificationConf" + - "ig\022-.a2a.v1.ListTaskPushNotificationConf" + - "igRequest\032..a2a.v1.ListTaskPushNotificat" + - "ionConfigResponse\"=\332A\006parent\202\323\344\223\002.\022,/v1/" + - "{parent=tasks/*}/pushNotificationConfigs" + - "\022P\n\014GetAgentCard\022\033.a2a.v1.GetAgentCardRe" + - "quest\032\021.a2a.v1.AgentCard\"\020\202\323\344\223\002\n\022\010/v1/ca" + - "rd\022\250\001\n DeleteTaskPushNotificationConfig\022" + - "/.a2a.v1.DeleteTaskPushNotificationConfi" + - "gRequest\032\026.google.protobuf.Empty\";\332A\004nam" + - "e\202\323\344\223\002.*,/v1/{name=tasks/*/pushNotificat" + - "ionConfigs/*}B7\n\013io.a2a.grpcB\003A2AP\001Z\030goo" + - "gle.golang.org/a2a/v1\252\002\006A2a.V1b\006proto3" + "\236\001\n\030SendMessageConfiguration\022\035\n\025accepted" + + "_output_modes\030\001 \003(\t\0229\n\021push_notification" + + "\030\002 \001(\0132\036.a2a.v1.PushNotificationConfig\022\026" + + "\n\016history_length\030\003 \001(\005\022\020\n\010blocking\030\004 \001(\010" + + "\"\274\001\n\004Task\022\n\n\002id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(" + + "\t\022\"\n\006status\030\003 \001(\0132\022.a2a.v1.TaskStatus\022#\n" + + "\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifact\022 \n\007hi" + + "story\030\005 \003(\0132\017.a2a.v1.Message\022)\n\010metadata" + + "\030\006 \001(\0132\027.google.protobuf.Struct\"\207\001\n\nTask" + + "Status\022 \n\005state\030\001 \001(\0162\021.a2a.v1.TaskState" + + "\022(\n\006update\030\002 \001(\0132\017.a2a.v1.MessageR\007messa" + + "ge\022-\n\ttimestamp\030\003 \001(\0132\032.google.protobuf." + + "Timestamp\"b\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022 \n\004fil" + + "e\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004data\030\003 \001(" + + "\0132\020.a2a.v1.DataPartH\000B\006\n\004part\"Y\n\010FilePar" + + "t\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n\017file_with_" + + "bytes\030\002 \001(\014H\000\022\021\n\tmime_type\030\003 \001(\tB\006\n\004file" + + "\"1\n\010DataPart\022%\n\004data\030\001 \001(\0132\027.google.prot" + + "obuf.Struct\"\274\001\n\007Message\022\022\n\nmessage_id\030\001 " + + "\001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t" + + "\022\032\n\004role\030\004 \001(\0162\014.a2a.v1.Role\022\035\n\007content\030" + + "\005 \003(\0132\014.a2a.v1.Part\022)\n\010metadata\030\006 \001(\0132\027." + + "google.protobuf.Struct\022\022\n\nextensions\030\007 \003" + + "(\t\"\236\001\n\010Artifact\022\023\n\013artifact_id\030\001 \001(\t\022\014\n\004" + + "name\030\003 \001(\t\022\023\n\013description\030\004 \001(\t\022\033\n\005parts" + + "\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010metadata\030\006 \001(\0132\027" + + ".google.protobuf.Struct\022\022\n\nextensions\030\007 " + + "\003(\t\"\232\001\n\025TaskStatusUpdateEvent\022\017\n\007task_id" + + "\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\006status\030\003 \001" + + "(\0132\022.a2a.v1.TaskStatus\022\r\n\005final\030\004 \001(\010\022)\n" + + "\010metadata\030\005 \001(\0132\027.google.protobuf.Struct" + + "\"\261\001\n\027TaskArtifactUpdateEvent\022\017\n\007task_id\030" + + "\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\010artifact\030\003 " + + "\001(\0132\020.a2a.v1.Artifact\022\016\n\006append\030\004 \001(\010\022\022\n" + + "\nlast_chunk\030\005 \001(\010\022)\n\010metadata\030\006 \001(\0132\027.go" + + "ogle.protobuf.Struct\"t\n\026PushNotification" + + "Config\022\n\n\002id\030\001 \001(\t\022\013\n\003url\030\002 \001(\t\022\r\n\005token" + + "\030\003 \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a.v1." + + "AuthenticationInfo\":\n\022AuthenticationInfo" + + "\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credentials\030\002 \001(\t\"0" + + "\n\016AgentInterface\022\013\n\003url\030\001 \001(\t\022\021\n\ttranspo" + + "rt\030\002 \001(\t\"\362\004\n\tAgentCard\022\030\n\020protocol_versi" + + "on\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013description\030\002 " + + "\001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred_transport\030" + + "\016 \001(\t\0225\n\025additional_interfaces\030\017 \003(\0132\026.a" + + "2a.v1.AgentInterface\022\'\n\010provider\030\004 \001(\0132\025" + + ".a2a.v1.AgentProvider\022\017\n\007version\030\005 \001(\t\022\031" + + "\n\021documentation_url\030\006 \001(\t\022/\n\014capabilitie" + + "s\030\007 \001(\0132\031.a2a.v1.AgentCapabilities\022@\n\020se" + + "curity_schemes\030\010 \003(\0132&.a2a.v1.AgentCard." + + "SecuritySchemesEntry\022\"\n\010security\030\t \003(\0132\020" + + ".a2a.v1.Security\022\033\n\023default_input_modes\030" + + "\n \003(\t\022\034\n\024default_output_modes\030\013 \003(\t\022\"\n\006s" + + "kills\030\014 \003(\0132\022.a2a.v1.AgentSkill\022,\n$suppo" + + "rts_authenticated_extended_card\030\r \001(\010\032N\n" + + "\024SecuritySchemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005va" + + "lue\030\002 \001(\0132\026.a2a.v1.SecurityScheme:\0028\001\"2\n" + + "\rAgentProvider\022\013\n\003url\030\001 \001(\t\022\024\n\014organizat" + + "ion\030\002 \001(\t\"n\n\021AgentCapabilities\022\021\n\tstream" + + "ing\030\001 \001(\010\022\032\n\022push_notifications\030\002 \001(\010\022*\n" + + "\nextensions\030\003 \003(\0132\026.a2a.v1.AgentExtensio" + + "n\"m\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013desc" + + "ription\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006param" + + "s\030\004 \001(\0132\027.google.protobuf.Struct\"\206\001\n\nAge" + + "ntSkill\022\n\n\002id\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\022\023\n\013des" + + "cription\030\003 \001(\t\022\014\n\004tags\030\004 \003(\t\022\020\n\010examples" + + "\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014output_mo" + + "des\030\007 \003(\t\"l\n\032TaskPushNotificationConfig\022" + + "\014\n\004name\030\001 \001(\t\022@\n\030push_notification_confi" + + "g\030\002 \001(\0132\036.a2a.v1.PushNotificationConfig\"" + + "\032\n\nStringList\022\014\n\004list\030\001 \003(\t\"~\n\010Security\022" + + ".\n\007schemes\030\001 \003(\0132\035.a2a.v1.Security.Schem" + + "esEntry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001(\t\022!\n\005" + + "value\030\002 \001(\0132\022.a2a.v1.StringList:\0028\001\"\260\002\n\016" + + "SecurityScheme\022?\n\027api_key_security_schem" + + "e\030\001 \001(\0132\034.a2a.v1.APIKeySecuritySchemeH\000\022" + + "C\n\031http_auth_security_scheme\030\002 \001(\0132\036.a2a" + + ".v1.HTTPAuthSecuritySchemeH\000\022>\n\026oauth2_s" + + "ecurity_scheme\030\003 \001(\0132\034.a2a.v1.OAuth2Secu" + + "ritySchemeH\000\022N\n\037open_id_connect_security" + + "_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConnectSecu" + + "ritySchemeH\000B\010\n\006scheme\"K\n\024APIKeySecurity" + + "Scheme\022\023\n\013description\030\001 \001(\t\022\020\n\010location\030" + + "\002 \001(\t\022\014\n\004name\030\003 \001(\t\"T\n\026HTTPAuthSecurityS" + + "cheme\022\023\n\013description\030\001 \001(\t\022\016\n\006scheme\030\002 \001" + + "(\t\022\025\n\rbearer_format\030\003 \001(\t\"N\n\024OAuth2Secur" + + "ityScheme\022\023\n\013description\030\001 \001(\t\022!\n\005flows\030" + + "\002 \001(\0132\022.a2a.v1.OAuthFlows\"O\n\033OpenIdConne" + + "ctSecurityScheme\022\023\n\013description\030\001 \001(\t\022\033\n" + + "\023open_id_connect_url\030\002 \001(\t\"\366\001\n\nOAuthFlow" + + "s\022@\n\022authorization_code\030\001 \001(\0132\".a2a.v1.A" + + "uthorizationCodeOAuthFlowH\000\022@\n\022client_cr" + + "edentials\030\002 \001(\0132\".a2a.v1.ClientCredentia" + + "lsOAuthFlowH\000\022-\n\010implicit\030\003 \001(\0132\031.a2a.v1" + + ".ImplicitOAuthFlowH\000\022-\n\010password\030\004 \001(\0132\031" + + ".a2a.v1.PasswordOAuthFlowH\000B\006\n\004flow\"\316\001\n\032" + + "AuthorizationCodeOAuthFlow\022\031\n\021authorizat" + + "ion_url\030\001 \001(\t\022\021\n\ttoken_url\030\002 \001(\t\022\023\n\013refr" + + "esh_url\030\003 \001(\t\022>\n\006scopes\030\004 \003(\0132..a2a.v1.A" + + "uthorizationCodeOAuthFlow.ScopesEntry\032-\n" + + "\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t" + + ":\0028\001\"\263\001\n\032ClientCredentialsOAuthFlow\022\021\n\tt" + + "oken_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\022>\n\006s" + + "copes\030\003 \003(\0132..a2a.v1.ClientCredentialsOA" + + "uthFlow.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003ke" + + "y\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021ImplicitO" + + "AuthFlow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013r" + + "efresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a.v" + + "1.ImplicitOAuthFlow.ScopesEntry\032-\n\013Scope" + + "sEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\241" + + "\001\n\021PasswordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t\022" + + "\023\n\013refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a" + + "2a.v1.PasswordOAuthFlow.ScopesEntry\032-\n\013S" + + "copesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\002" + + "8\001\"\237\001\n\022SendMessageRequest\022%\n\007request\030\001 \001" + + "(\0132\017.a2a.v1.MessageB\003\340A\002\0227\n\rconfiguratio" + + "n\030\002 \001(\0132 .a2a.v1.SendMessageConfiguratio" + + "n\022)\n\010metadata\030\003 \001(\0132\027.google.protobuf.St" + + "ruct\";\n\016GetTaskRequest\022\021\n\004name\030\001 \001(\tB\003\340A" + + "\002\022\026\n\016history_length\030\002 \001(\005\"!\n\021CancelTaskR" + + "equest\022\014\n\004name\030\001 \001(\t\"4\n$GetTaskPushNotif" + + "icationConfigRequest\022\014\n\004name\030\001 \001(\t\"7\n\'De" + + "leteTaskPushNotificationConfigRequest\022\014\n" + + "\004name\030\001 \001(\t\"\217\001\n\'CreateTaskPushNotificati" + + "onConfigRequest\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\t" + + "config_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a" + + "2a.v1.TaskPushNotificationConfigB\003\340A\002\"\'\n" + + "\027TaskSubscriptionRequest\022\014\n\004name\030\001 \001(\t\"^" + + "\n%ListTaskPushNotificationConfigRequest\022" + + "\016\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npa" + + "ge_token\030\003 \001(\t\"\025\n\023GetAgentCardRequest\"g\n" + + "\023SendMessageResponse\022\034\n\004task\030\001 \001(\0132\014.a2a" + + ".v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.Message" + + "H\000R\007messageB\t\n\007payload\"\326\001\n\016StreamRespons" + + "e\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002" + + " \001(\0132\017.a2a.v1.MessageH\000R\007message\0226\n\rstat" + + "us_update\030\003 \001(\0132\035.a2a.v1.TaskStatusUpdat" + + "eEventH\000\022:\n\017artifact_update\030\004 \001(\0132\037.a2a." + + "v1.TaskArtifactUpdateEventH\000B\t\n\007payload\"" + + "v\n&ListTaskPushNotificationConfigRespons" + + "e\0223\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPushNoti" + + "ficationConfig\022\027\n\017next_page_token\030\002 \001(\t*" + + "\372\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020" + + "\000\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STAT" + + "E_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n" + + "\021TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCE" + + "LLED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n" + + "\023TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUT" + + "H_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED" + + "\020\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\272\n\n\nA2" + + "AService\022c\n\013SendMessage\022\032.a2a.v1.SendMes" + + "sageRequest\032\033.a2a.v1.SendMessageResponse" + + "\"\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024SendStr" + + "eamingMessage\022\032.a2a.v1.SendMessageReques" + + "t\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"\022/v1/m" + + "essage:stream:\001*0\001\022R\n\007GetTask\022\026.a2a.v1.G" + + "etTaskRequest\032\014.a2a.v1.Task\"!\332A\004name\202\323\344\223" + + "\002\024\022\022/v1/{name=tasks/*}\022[\n\nCancelTask\022\031.a" + + "2a.v1.CancelTaskRequest\032\014.a2a.v1.Task\"$\202" + + "\323\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*\022s\n\020T" + + "askSubscription\022\037.a2a.v1.TaskSubscriptio" + + "nRequest\032\026.a2a.v1.StreamResponse\"$\202\323\344\223\002\036" + + "\022\034/v1/{name=tasks/*}:subscribe0\001\022\304\001\n Cre" + + "ateTaskPushNotificationConfig\022/.a2a.v1.C" + + "reateTaskPushNotificationConfigRequest\032\"" + + ".a2a.v1.TaskPushNotificationConfig\"K\332A\rp" + + "arent,config\202\323\344\223\0025\"+/v1/{parent=task/*/p" + + "ushNotificationConfigs}:\006config\022\256\001\n\035GetT" + + "askPushNotificationConfig\022,.a2a.v1.GetTa" + + "skPushNotificationConfigRequest\032\".a2a.v1" + + ".TaskPushNotificationConfig\";\332A\004name\202\323\344\223" + + "\002.\022,/v1/{name=tasks/*/pushNotificationCo" + + "nfigs/*}\022\276\001\n\036ListTaskPushNotificationCon" + + "fig\022-.a2a.v1.ListTaskPushNotificationCon" + + "figRequest\032..a2a.v1.ListTaskPushNotifica" + + "tionConfigResponse\"=\332A\006parent\202\323\344\223\002.\022,/v1" + + "/{parent=tasks/*}/pushNotificationConfig" + + "s\022P\n\014GetAgentCard\022\033.a2a.v1.GetAgentCardR" + + "equest\032\021.a2a.v1.AgentCard\"\020\202\323\344\223\002\n\022\010/v1/c" + + "ard\022\250\001\n DeleteTaskPushNotificationConfig" + + "\022/.a2a.v1.DeleteTaskPushNotificationConf" + + "igRequest\032\026.google.protobuf.Empty\";\332A\004na" + + "me\202\323\344\223\002.*,/v1/{name=tasks/*/pushNotifica" + + "tionConfigs/*}B7\n\013io.a2a.grpcB\003A2AP\001Z\030go" + + "ogle.golang.org/a2a/v1\252\002\006A2a.V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java index 4ec3a9ac0..6f83ead1d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -19,10 +19,9 @@ * violates AIP-127 and AIP-131. * */ -// Temporarily comment this out until https://github.com/grpc/grpc-java/pull/12080 is included in a release -//@javax.annotation.Generated( -// value = "by gRPC proto compiler (version 1.73.0)", -// comments = "Source: a2a.proto") +@javax.annotation.Generated( + value = "by gRPC proto compiler (version 1.73.0)", + comments = "Source: a2a.proto") @io.grpc.stub.annotations.GrpcGenerated public final class A2AServiceGrpc { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java index f2a9778d6..2f749cdb9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java @@ -54,7 +54,7 @@ private APIKeySecurityScheme() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ @java.lang.Override @@ -75,7 +75,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ @java.lang.Override @@ -101,7 +101,7 @@ public java.lang.String getDescription() { * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return The location. */ @java.lang.Override @@ -122,7 +122,7 @@ public java.lang.String getLocation() { * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return The bytes for location. */ @java.lang.Override @@ -148,7 +148,7 @@ public java.lang.String getLocation() { * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The name. */ @java.lang.Override @@ -169,7 +169,7 @@ public java.lang.String getName() { * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The bytes for name. */ @java.lang.Override @@ -535,7 +535,7 @@ public Builder mergeFrom( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ public java.lang.String getDescription() { @@ -555,7 +555,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -576,7 +576,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The description to set. * @return This builder for chaining. */ @@ -593,7 +593,7 @@ public Builder setDescription( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return This builder for chaining. */ public Builder clearDescription() { @@ -607,7 +607,7 @@ public Builder clearDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -627,7 +627,7 @@ public Builder setDescriptionBytes( * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return The location. */ public java.lang.String getLocation() { @@ -647,7 +647,7 @@ public java.lang.String getLocation() { * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return The bytes for location. */ public com.google.protobuf.ByteString @@ -668,7 +668,7 @@ public java.lang.String getLocation() { * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @param value The location to set. * @return This builder for chaining. */ @@ -685,7 +685,7 @@ public Builder setLocation( * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return This builder for chaining. */ public Builder clearLocation() { @@ -699,7 +699,7 @@ public Builder clearLocation() { * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @param value The bytes for location to set. * @return This builder for chaining. */ @@ -719,7 +719,7 @@ public Builder setLocationBytes( * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The name. */ public java.lang.String getName() { @@ -739,7 +739,7 @@ public java.lang.String getName() { * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -760,7 +760,7 @@ public java.lang.String getName() { * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @param value The name to set. * @return This builder for chaining. */ @@ -777,7 +777,7 @@ public Builder setName( * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return This builder for chaining. */ public Builder clearName() { @@ -791,7 +791,7 @@ public Builder clearName() { * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @param value The bytes for name to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java index ec893f4f8..f3e505c6a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java @@ -15,7 +15,7 @@ public interface APIKeySecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ java.lang.String getDescription(); @@ -24,7 +24,7 @@ public interface APIKeySecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface APIKeySecuritySchemeOrBuilder extends * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return The location. */ java.lang.String getLocation(); @@ -44,7 +44,7 @@ public interface APIKeySecuritySchemeOrBuilder extends * Location of the API key, valid values are "query", "header", or "cookie" * * - * string location = 2 [json_name = "location"]; + * string location = 2; * @return The bytes for location. */ com.google.protobuf.ByteString @@ -55,7 +55,7 @@ public interface APIKeySecuritySchemeOrBuilder extends * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The name. */ java.lang.String getName(); @@ -64,7 +64,7 @@ public interface APIKeySecuritySchemeOrBuilder extends * Name of the header, query or cookie parameter to be used. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The bytes for name. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java index ca67660d7..bc9202234 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -55,7 +55,7 @@ private AgentCapabilities() { * If the agent will support streaming responses * * - * bool streaming = 1 [json_name = "streaming"]; + * bool streaming = 1; * @return The streaming. */ @java.lang.Override @@ -70,7 +70,7 @@ public boolean getStreaming() { * If the agent can send push notifications to the clients webhook * * - * bool push_notifications = 2 [json_name = "pushNotifications"]; + * bool push_notifications = 2; * @return The pushNotifications. */ @java.lang.Override @@ -86,7 +86,7 @@ public boolean getPushNotifications() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ @java.lang.Override public java.util.List getExtensionsList() { @@ -97,7 +97,7 @@ public java.util.List getExtensionsList() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ @java.lang.Override public java.util.List @@ -109,7 +109,7 @@ public java.util.List getExtensionsList() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ @java.lang.Override public int getExtensionsCount() { @@ -120,7 +120,7 @@ public int getExtensionsCount() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ @java.lang.Override public io.a2a.grpc.AgentExtension getExtensions(int index) { @@ -131,7 +131,7 @@ public io.a2a.grpc.AgentExtension getExtensions(int index) { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ @java.lang.Override public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( @@ -539,7 +539,7 @@ public Builder mergeFrom( * If the agent will support streaming responses * * - * bool streaming = 1 [json_name = "streaming"]; + * bool streaming = 1; * @return The streaming. */ @java.lang.Override @@ -551,7 +551,7 @@ public boolean getStreaming() { * If the agent will support streaming responses * * - * bool streaming = 1 [json_name = "streaming"]; + * bool streaming = 1; * @param value The streaming to set. * @return This builder for chaining. */ @@ -567,7 +567,7 @@ public Builder setStreaming(boolean value) { * If the agent will support streaming responses * * - * bool streaming = 1 [json_name = "streaming"]; + * bool streaming = 1; * @return This builder for chaining. */ public Builder clearStreaming() { @@ -583,7 +583,7 @@ public Builder clearStreaming() { * If the agent can send push notifications to the clients webhook * * - * bool push_notifications = 2 [json_name = "pushNotifications"]; + * bool push_notifications = 2; * @return The pushNotifications. */ @java.lang.Override @@ -595,7 +595,7 @@ public boolean getPushNotifications() { * If the agent can send push notifications to the clients webhook * * - * bool push_notifications = 2 [json_name = "pushNotifications"]; + * bool push_notifications = 2; * @param value The pushNotifications to set. * @return This builder for chaining. */ @@ -611,7 +611,7 @@ public Builder setPushNotifications(boolean value) { * If the agent can send push notifications to the clients webhook * * - * bool push_notifications = 2 [json_name = "pushNotifications"]; + * bool push_notifications = 2; * @return This builder for chaining. */ public Builder clearPushNotifications() { @@ -638,7 +638,7 @@ private void ensureExtensionsIsMutable() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public java.util.List getExtensionsList() { if (extensionsBuilder_ == null) { @@ -652,7 +652,7 @@ public java.util.List getExtensionsList() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public int getExtensionsCount() { if (extensionsBuilder_ == null) { @@ -666,7 +666,7 @@ public int getExtensionsCount() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public io.a2a.grpc.AgentExtension getExtensions(int index) { if (extensionsBuilder_ == null) { @@ -680,7 +680,7 @@ public io.a2a.grpc.AgentExtension getExtensions(int index) { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder setExtensions( int index, io.a2a.grpc.AgentExtension value) { @@ -701,7 +701,7 @@ public Builder setExtensions( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder setExtensions( int index, io.a2a.grpc.AgentExtension.Builder builderForValue) { @@ -719,7 +719,7 @@ public Builder setExtensions( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder addExtensions(io.a2a.grpc.AgentExtension value) { if (extensionsBuilder_ == null) { @@ -739,7 +739,7 @@ public Builder addExtensions(io.a2a.grpc.AgentExtension value) { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder addExtensions( int index, io.a2a.grpc.AgentExtension value) { @@ -760,7 +760,7 @@ public Builder addExtensions( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder addExtensions( io.a2a.grpc.AgentExtension.Builder builderForValue) { @@ -778,7 +778,7 @@ public Builder addExtensions( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder addExtensions( int index, io.a2a.grpc.AgentExtension.Builder builderForValue) { @@ -796,7 +796,7 @@ public Builder addExtensions( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder addAllExtensions( java.lang.Iterable values) { @@ -815,7 +815,7 @@ public Builder addAllExtensions( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder clearExtensions() { if (extensionsBuilder_ == null) { @@ -832,7 +832,7 @@ public Builder clearExtensions() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public Builder removeExtensions(int index) { if (extensionsBuilder_ == null) { @@ -849,7 +849,7 @@ public Builder removeExtensions(int index) { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public io.a2a.grpc.AgentExtension.Builder getExtensionsBuilder( int index) { @@ -860,7 +860,7 @@ public io.a2a.grpc.AgentExtension.Builder getExtensionsBuilder( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( int index) { @@ -874,7 +874,7 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public java.util.List getExtensionsOrBuilderList() { @@ -889,7 +889,7 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder() { return internalGetExtensionsFieldBuilder().addBuilder( @@ -900,7 +900,7 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder() { * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder( int index) { @@ -912,7 +912,7 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder( * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ public java.util.List getExtensionsBuilderList() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java index 311841ae9..d009adff5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java @@ -15,7 +15,7 @@ public interface AgentCapabilitiesOrBuilder extends * If the agent will support streaming responses * * - * bool streaming = 1 [json_name = "streaming"]; + * bool streaming = 1; * @return The streaming. */ boolean getStreaming(); @@ -25,7 +25,7 @@ public interface AgentCapabilitiesOrBuilder extends * If the agent can send push notifications to the clients webhook * * - * bool push_notifications = 2 [json_name = "pushNotifications"]; + * bool push_notifications = 2; * @return The pushNotifications. */ boolean getPushNotifications(); @@ -35,7 +35,7 @@ public interface AgentCapabilitiesOrBuilder extends * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ java.util.List getExtensionsList(); @@ -44,7 +44,7 @@ public interface AgentCapabilitiesOrBuilder extends * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ io.a2a.grpc.AgentExtension getExtensions(int index); /** @@ -52,7 +52,7 @@ public interface AgentCapabilitiesOrBuilder extends * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ int getExtensionsCount(); /** @@ -60,7 +60,7 @@ public interface AgentCapabilitiesOrBuilder extends * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ java.util.List getExtensionsOrBuilderList(); @@ -69,7 +69,7 @@ public interface AgentCapabilitiesOrBuilder extends * Extensions supported by this agent. * * - * repeated .a2a.v1.AgentExtension extensions = 3 [json_name = "extensions"]; + * repeated .a2a.v1.AgentExtension extensions = 3; */ io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( int index); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index 8327b6376..5862d4ef9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -86,7 +86,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return The protocolVersion. */ @java.lang.Override @@ -107,7 +107,7 @@ public java.lang.String getProtocolVersion() { * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return The bytes for protocolVersion. */ @java.lang.Override @@ -134,7 +134,7 @@ public java.lang.String getProtocolVersion() { * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ @java.lang.Override @@ -156,7 +156,7 @@ public java.lang.String getName() { * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ @java.lang.Override @@ -183,7 +183,7 @@ public java.lang.String getName() { * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The description. */ @java.lang.Override @@ -205,7 +205,7 @@ public java.lang.String getDescription() { * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The bytes for description. */ @java.lang.Override @@ -232,7 +232,7 @@ public java.lang.String getDescription() { * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return The url. */ @java.lang.Override @@ -254,7 +254,7 @@ public java.lang.String getUrl() { * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return The bytes for url. */ @java.lang.Override @@ -280,7 +280,7 @@ public java.lang.String getUrl() { * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return The preferredTransport. */ @java.lang.Override @@ -301,7 +301,7 @@ public java.lang.String getPreferredTransport() { * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return The bytes for preferredTransport. */ @java.lang.Override @@ -328,7 +328,7 @@ public java.lang.String getPreferredTransport() { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ @java.lang.Override public java.util.List getAdditionalInterfacesList() { @@ -340,7 +340,7 @@ public java.util.List getAdditionalInterfacesList() * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ @java.lang.Override public java.util.List @@ -353,7 +353,7 @@ public java.util.List getAdditionalInterfacesList() * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ @java.lang.Override public int getAdditionalInterfacesCount() { @@ -365,7 +365,7 @@ public int getAdditionalInterfacesCount() { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ @java.lang.Override public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { @@ -377,7 +377,7 @@ public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ @java.lang.Override public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( @@ -392,7 +392,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; * @return Whether the provider field is set. */ @java.lang.Override @@ -404,7 +404,7 @@ public boolean hasProvider() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; * @return The provider. */ @java.lang.Override @@ -416,7 +416,7 @@ public io.a2a.grpc.AgentProvider getProvider() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ @java.lang.Override public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { @@ -432,7 +432,7 @@ public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return The version. */ @java.lang.Override @@ -454,7 +454,7 @@ public java.lang.String getVersion() { * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return The bytes for version. */ @java.lang.Override @@ -480,7 +480,7 @@ public java.lang.String getVersion() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return The documentationUrl. */ @java.lang.Override @@ -501,7 +501,7 @@ public java.lang.String getDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return The bytes for documentationUrl. */ @java.lang.Override @@ -526,7 +526,7 @@ public java.lang.String getDocumentationUrl() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; * @return Whether the capabilities field is set. */ @java.lang.Override @@ -538,7 +538,7 @@ public boolean hasCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; * @return The capabilities. */ @java.lang.Override @@ -550,7 +550,7 @@ public io.a2a.grpc.AgentCapabilities getCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ @java.lang.Override public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { @@ -588,7 +588,7 @@ public int getSecuritySchemesCount() { * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public boolean containsSecuritySchemes( @@ -609,7 +609,7 @@ public java.util.Map getSecuritySc * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public java.util.Map getSecuritySchemesMap() { @@ -620,7 +620,7 @@ public java.util.Map getSecuritySc * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public /* nullable */ @@ -638,7 +638,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( @@ -661,7 +661,7 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ @java.lang.Override public java.util.List getSecurityList() { @@ -673,7 +673,7 @@ public java.util.List getSecurityList() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ @java.lang.Override public java.util.List @@ -686,7 +686,7 @@ public java.util.List getSecurityList() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ @java.lang.Override public int getSecurityCount() { @@ -698,7 +698,7 @@ public int getSecurityCount() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ @java.lang.Override public io.a2a.grpc.Security getSecurity(int index) { @@ -710,7 +710,7 @@ public io.a2a.grpc.Security getSecurity(int index) { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ @java.lang.Override public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( @@ -729,7 +729,7 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return A list containing the defaultInputModes. */ public com.google.protobuf.ProtocolStringList @@ -743,7 +743,7 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return The count of defaultInputModes. */ public int getDefaultInputModesCount() { @@ -756,7 +756,7 @@ public int getDefaultInputModesCount() { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index of the element to return. * @return The defaultInputModes at the given index. */ @@ -770,7 +770,7 @@ public java.lang.String getDefaultInputModes(int index) { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index of the value to return. * @return The bytes of the defaultInputModes at the given index. */ @@ -788,7 +788,7 @@ public java.lang.String getDefaultInputModes(int index) { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return A list containing the defaultOutputModes. */ public com.google.protobuf.ProtocolStringList @@ -800,7 +800,7 @@ public java.lang.String getDefaultInputModes(int index) { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return The count of defaultOutputModes. */ public int getDefaultOutputModesCount() { @@ -811,7 +811,7 @@ public int getDefaultOutputModesCount() { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index of the element to return. * @return The defaultOutputModes at the given index. */ @@ -823,7 +823,7 @@ public java.lang.String getDefaultOutputModes(int index) { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index of the value to return. * @return The bytes of the defaultOutputModes at the given index. */ @@ -842,7 +842,7 @@ public java.lang.String getDefaultOutputModes(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ @java.lang.Override public java.util.List getSkillsList() { @@ -855,7 +855,7 @@ public java.util.List getSkillsList() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ @java.lang.Override public java.util.List @@ -869,7 +869,7 @@ public java.util.List getSkillsList() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ @java.lang.Override public int getSkillsCount() { @@ -882,7 +882,7 @@ public int getSkillsCount() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ @java.lang.Override public io.a2a.grpc.AgentSkill getSkills(int index) { @@ -895,7 +895,7 @@ public io.a2a.grpc.AgentSkill getSkills(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ @java.lang.Override public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( @@ -912,7 +912,7 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * different than the card from GetAgentCard. * * - * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * bool supports_authenticated_extended_card = 13; * @return The supportsAuthenticatedExtendedCard. */ @java.lang.Override @@ -1828,7 +1828,7 @@ public Builder mergeFrom( * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return The protocolVersion. */ public java.lang.String getProtocolVersion() { @@ -1848,7 +1848,7 @@ public java.lang.String getProtocolVersion() { * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return The bytes for protocolVersion. */ public com.google.protobuf.ByteString @@ -1869,7 +1869,7 @@ public java.lang.String getProtocolVersion() { * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @param value The protocolVersion to set. * @return This builder for chaining. */ @@ -1886,7 +1886,7 @@ public Builder setProtocolVersion( * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return This builder for chaining. */ public Builder clearProtocolVersion() { @@ -1900,7 +1900,7 @@ public Builder clearProtocolVersion() { * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @param value The bytes for protocolVersion to set. * @return This builder for chaining. */ @@ -1921,7 +1921,7 @@ public Builder setProtocolVersionBytes( * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ public java.lang.String getName() { @@ -1942,7 +1942,7 @@ public java.lang.String getName() { * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -1964,7 +1964,7 @@ public java.lang.String getName() { * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The name to set. * @return This builder for chaining. */ @@ -1982,7 +1982,7 @@ public Builder setName( * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return This builder for chaining. */ public Builder clearName() { @@ -1997,7 +1997,7 @@ public Builder clearName() { * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -2018,7 +2018,7 @@ public Builder setNameBytes( * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The description. */ public java.lang.String getDescription() { @@ -2039,7 +2039,7 @@ public java.lang.String getDescription() { * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -2061,7 +2061,7 @@ public java.lang.String getDescription() { * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @param value The description to set. * @return This builder for chaining. */ @@ -2079,7 +2079,7 @@ public Builder setDescription( * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return This builder for chaining. */ public Builder clearDescription() { @@ -2094,7 +2094,7 @@ public Builder clearDescription() { * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -2115,7 +2115,7 @@ public Builder setDescriptionBytes( * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return The url. */ public java.lang.String getUrl() { @@ -2136,7 +2136,7 @@ public java.lang.String getUrl() { * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -2158,7 +2158,7 @@ public java.lang.String getUrl() { * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @param value The url to set. * @return This builder for chaining. */ @@ -2176,7 +2176,7 @@ public Builder setUrl( * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return This builder for chaining. */ public Builder clearUrl() { @@ -2191,7 +2191,7 @@ public Builder clearUrl() { * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -2211,7 +2211,7 @@ public Builder setUrlBytes( * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return The preferredTransport. */ public java.lang.String getPreferredTransport() { @@ -2231,7 +2231,7 @@ public java.lang.String getPreferredTransport() { * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return The bytes for preferredTransport. */ public com.google.protobuf.ByteString @@ -2252,7 +2252,7 @@ public java.lang.String getPreferredTransport() { * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @param value The preferredTransport to set. * @return This builder for chaining. */ @@ -2269,7 +2269,7 @@ public Builder setPreferredTransport( * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return This builder for chaining. */ public Builder clearPreferredTransport() { @@ -2283,7 +2283,7 @@ public Builder clearPreferredTransport() { * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @param value The bytes for preferredTransport to set. * @return This builder for chaining. */ @@ -2315,7 +2315,7 @@ private void ensureAdditionalInterfacesIsMutable() { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public java.util.List getAdditionalInterfacesList() { if (additionalInterfacesBuilder_ == null) { @@ -2330,7 +2330,7 @@ public java.util.List getAdditionalInterfacesList() * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public int getAdditionalInterfacesCount() { if (additionalInterfacesBuilder_ == null) { @@ -2345,7 +2345,7 @@ public int getAdditionalInterfacesCount() { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { if (additionalInterfacesBuilder_ == null) { @@ -2360,7 +2360,7 @@ public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder setAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface value) { @@ -2382,7 +2382,7 @@ public Builder setAdditionalInterfaces( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder setAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { @@ -2401,7 +2401,7 @@ public Builder setAdditionalInterfaces( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { if (additionalInterfacesBuilder_ == null) { @@ -2422,7 +2422,7 @@ public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder addAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface value) { @@ -2444,7 +2444,7 @@ public Builder addAdditionalInterfaces( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder addAdditionalInterfaces( io.a2a.grpc.AgentInterface.Builder builderForValue) { @@ -2463,7 +2463,7 @@ public Builder addAdditionalInterfaces( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder addAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { @@ -2482,7 +2482,7 @@ public Builder addAdditionalInterfaces( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder addAllAdditionalInterfaces( java.lang.Iterable values) { @@ -2502,7 +2502,7 @@ public Builder addAllAdditionalInterfaces( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder clearAdditionalInterfaces() { if (additionalInterfacesBuilder_ == null) { @@ -2520,7 +2520,7 @@ public Builder clearAdditionalInterfaces() { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public Builder removeAdditionalInterfaces(int index) { if (additionalInterfacesBuilder_ == null) { @@ -2538,7 +2538,7 @@ public Builder removeAdditionalInterfaces(int index) { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public io.a2a.grpc.AgentInterface.Builder getAdditionalInterfacesBuilder( int index) { @@ -2550,7 +2550,7 @@ public io.a2a.grpc.AgentInterface.Builder getAdditionalInterfacesBuilder( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( int index) { @@ -2565,7 +2565,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public java.util.List getAdditionalInterfacesOrBuilderList() { @@ -2581,7 +2581,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder() { return internalGetAdditionalInterfacesFieldBuilder().addBuilder( @@ -2593,7 +2593,7 @@ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder() { * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( int index) { @@ -2606,7 +2606,7 @@ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ public java.util.List getAdditionalInterfacesBuilderList() { @@ -2635,7 +2635,7 @@ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; * @return Whether the provider field is set. */ public boolean hasProvider() { @@ -2646,7 +2646,7 @@ public boolean hasProvider() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; * @return The provider. */ public io.a2a.grpc.AgentProvider getProvider() { @@ -2661,7 +2661,7 @@ public io.a2a.grpc.AgentProvider getProvider() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ public Builder setProvider(io.a2a.grpc.AgentProvider value) { if (providerBuilder_ == null) { @@ -2681,7 +2681,7 @@ public Builder setProvider(io.a2a.grpc.AgentProvider value) { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ public Builder setProvider( io.a2a.grpc.AgentProvider.Builder builderForValue) { @@ -2699,7 +2699,7 @@ public Builder setProvider( * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) { if (providerBuilder_ == null) { @@ -2724,7 +2724,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ public Builder clearProvider() { bitField0_ = (bitField0_ & ~0x00000040); @@ -2741,7 +2741,7 @@ public Builder clearProvider() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ public io.a2a.grpc.AgentProvider.Builder getProviderBuilder() { bitField0_ |= 0x00000040; @@ -2753,7 +2753,7 @@ public io.a2a.grpc.AgentProvider.Builder getProviderBuilder() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { if (providerBuilder_ != null) { @@ -2768,7 +2768,7 @@ public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AgentProvider, io.a2a.grpc.AgentProvider.Builder, io.a2a.grpc.AgentProviderOrBuilder> @@ -2791,7 +2791,7 @@ public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() { * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return The version. */ public java.lang.String getVersion() { @@ -2812,7 +2812,7 @@ public java.lang.String getVersion() { * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return The bytes for version. */ public com.google.protobuf.ByteString @@ -2834,7 +2834,7 @@ public java.lang.String getVersion() { * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @param value The version to set. * @return This builder for chaining. */ @@ -2852,7 +2852,7 @@ public Builder setVersion( * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return This builder for chaining. */ public Builder clearVersion() { @@ -2867,7 +2867,7 @@ public Builder clearVersion() { * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @param value The bytes for version to set. * @return This builder for chaining. */ @@ -2887,7 +2887,7 @@ public Builder setVersionBytes( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return The documentationUrl. */ public java.lang.String getDocumentationUrl() { @@ -2907,7 +2907,7 @@ public java.lang.String getDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return The bytes for documentationUrl. */ public com.google.protobuf.ByteString @@ -2928,7 +2928,7 @@ public java.lang.String getDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @param value The documentationUrl to set. * @return This builder for chaining. */ @@ -2945,7 +2945,7 @@ public Builder setDocumentationUrl( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return This builder for chaining. */ public Builder clearDocumentationUrl() { @@ -2959,7 +2959,7 @@ public Builder clearDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @param value The bytes for documentationUrl to set. * @return This builder for chaining. */ @@ -2981,7 +2981,7 @@ public Builder setDocumentationUrlBytes( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; * @return Whether the capabilities field is set. */ public boolean hasCapabilities() { @@ -2992,7 +2992,7 @@ public boolean hasCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; * @return The capabilities. */ public io.a2a.grpc.AgentCapabilities getCapabilities() { @@ -3007,7 +3007,7 @@ public io.a2a.grpc.AgentCapabilities getCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) { if (capabilitiesBuilder_ == null) { @@ -3027,7 +3027,7 @@ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ public Builder setCapabilities( io.a2a.grpc.AgentCapabilities.Builder builderForValue) { @@ -3045,7 +3045,7 @@ public Builder setCapabilities( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) { if (capabilitiesBuilder_ == null) { @@ -3070,7 +3070,7 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ public Builder clearCapabilities() { bitField0_ = (bitField0_ & ~0x00000200); @@ -3087,7 +3087,7 @@ public Builder clearCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() { bitField0_ |= 0x00000200; @@ -3099,7 +3099,7 @@ public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { if (capabilitiesBuilder_ != null) { @@ -3114,7 +3114,7 @@ public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AgentCapabilities, io.a2a.grpc.AgentCapabilities.Builder, io.a2a.grpc.AgentCapabilitiesOrBuilder> @@ -3170,7 +3170,7 @@ public int getSecuritySchemesCount() { * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public boolean containsSecuritySchemes( @@ -3191,7 +3191,7 @@ public java.util.Map getSecuritySc * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public java.util.Map getSecuritySchemesMap() { @@ -3202,7 +3202,7 @@ public java.util.Map getSecuritySc * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public /* nullable */ @@ -3219,7 +3219,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ @java.lang.Override public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( @@ -3241,7 +3241,7 @@ public Builder clearSecuritySchemes() { * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ public Builder removeSecuritySchemes( java.lang.String key) { @@ -3264,7 +3264,7 @@ public Builder removeSecuritySchemes( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ public Builder putSecuritySchemes( java.lang.String key, @@ -3281,7 +3281,7 @@ public Builder putSecuritySchemes( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ public Builder putAllSecuritySchemes( java.util.Map values) { @@ -3300,7 +3300,7 @@ public Builder putAllSecuritySchemes( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ public io.a2a.grpc.SecurityScheme.Builder putSecuritySchemesBuilderIfAbsent( java.lang.String key) { @@ -3335,7 +3335,7 @@ private void ensureSecurityIsMutable() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public java.util.List getSecurityList() { if (securityBuilder_ == null) { @@ -3350,7 +3350,7 @@ public java.util.List getSecurityList() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public int getSecurityCount() { if (securityBuilder_ == null) { @@ -3365,7 +3365,7 @@ public int getSecurityCount() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public io.a2a.grpc.Security getSecurity(int index) { if (securityBuilder_ == null) { @@ -3380,7 +3380,7 @@ public io.a2a.grpc.Security getSecurity(int index) { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder setSecurity( int index, io.a2a.grpc.Security value) { @@ -3402,7 +3402,7 @@ public Builder setSecurity( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder setSecurity( int index, io.a2a.grpc.Security.Builder builderForValue) { @@ -3421,7 +3421,7 @@ public Builder setSecurity( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder addSecurity(io.a2a.grpc.Security value) { if (securityBuilder_ == null) { @@ -3442,7 +3442,7 @@ public Builder addSecurity(io.a2a.grpc.Security value) { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder addSecurity( int index, io.a2a.grpc.Security value) { @@ -3464,7 +3464,7 @@ public Builder addSecurity( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder addSecurity( io.a2a.grpc.Security.Builder builderForValue) { @@ -3483,7 +3483,7 @@ public Builder addSecurity( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder addSecurity( int index, io.a2a.grpc.Security.Builder builderForValue) { @@ -3502,7 +3502,7 @@ public Builder addSecurity( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder addAllSecurity( java.lang.Iterable values) { @@ -3522,7 +3522,7 @@ public Builder addAllSecurity( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder clearSecurity() { if (securityBuilder_ == null) { @@ -3540,7 +3540,7 @@ public Builder clearSecurity() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public Builder removeSecurity(int index) { if (securityBuilder_ == null) { @@ -3558,7 +3558,7 @@ public Builder removeSecurity(int index) { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public io.a2a.grpc.Security.Builder getSecurityBuilder( int index) { @@ -3570,7 +3570,7 @@ public io.a2a.grpc.Security.Builder getSecurityBuilder( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( int index) { @@ -3585,7 +3585,7 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public java.util.List getSecurityOrBuilderList() { @@ -3601,7 +3601,7 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public io.a2a.grpc.Security.Builder addSecurityBuilder() { return internalGetSecurityFieldBuilder().addBuilder( @@ -3613,7 +3613,7 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder() { * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public io.a2a.grpc.Security.Builder addSecurityBuilder( int index) { @@ -3626,7 +3626,7 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ public java.util.List getSecurityBuilderList() { @@ -3662,7 +3662,7 @@ private void ensureDefaultInputModesIsMutable() { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return A list containing the defaultInputModes. */ public com.google.protobuf.ProtocolStringList @@ -3677,7 +3677,7 @@ private void ensureDefaultInputModesIsMutable() { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return The count of defaultInputModes. */ public int getDefaultInputModesCount() { @@ -3690,7 +3690,7 @@ public int getDefaultInputModesCount() { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index of the element to return. * @return The defaultInputModes at the given index. */ @@ -3704,7 +3704,7 @@ public java.lang.String getDefaultInputModes(int index) { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index of the value to return. * @return The bytes of the defaultInputModes at the given index. */ @@ -3719,7 +3719,7 @@ public java.lang.String getDefaultInputModes(int index) { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index to set the value at. * @param value The defaultInputModes to set. * @return This builder for chaining. @@ -3740,7 +3740,7 @@ public Builder setDefaultInputModes( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param value The defaultInputModes to add. * @return This builder for chaining. */ @@ -3760,7 +3760,7 @@ public Builder addDefaultInputModes( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param values The defaultInputModes to add. * @return This builder for chaining. */ @@ -3780,7 +3780,7 @@ public Builder addAllDefaultInputModes( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return This builder for chaining. */ public Builder clearDefaultInputModes() { @@ -3797,7 +3797,7 @@ public Builder clearDefaultInputModes() { * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param value The bytes of the defaultInputModes to add. * @return This builder for chaining. */ @@ -3825,7 +3825,7 @@ private void ensureDefaultOutputModesIsMutable() { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return A list containing the defaultOutputModes. */ public com.google.protobuf.ProtocolStringList @@ -3838,7 +3838,7 @@ private void ensureDefaultOutputModesIsMutable() { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return The count of defaultOutputModes. */ public int getDefaultOutputModesCount() { @@ -3849,7 +3849,7 @@ public int getDefaultOutputModesCount() { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index of the element to return. * @return The defaultOutputModes at the given index. */ @@ -3861,7 +3861,7 @@ public java.lang.String getDefaultOutputModes(int index) { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index of the value to return. * @return The bytes of the defaultOutputModes at the given index. */ @@ -3874,7 +3874,7 @@ public java.lang.String getDefaultOutputModes(int index) { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index to set the value at. * @param value The defaultOutputModes to set. * @return This builder for chaining. @@ -3893,7 +3893,7 @@ public Builder setDefaultOutputModes( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param value The defaultOutputModes to add. * @return This builder for chaining. */ @@ -3911,7 +3911,7 @@ public Builder addDefaultOutputModes( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param values The defaultOutputModes to add. * @return This builder for chaining. */ @@ -3929,7 +3929,7 @@ public Builder addAllDefaultOutputModes( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return This builder for chaining. */ public Builder clearDefaultOutputModes() { @@ -3944,7 +3944,7 @@ public Builder clearDefaultOutputModes() { * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param value The bytes of the defaultOutputModes to add. * @return This builder for chaining. */ @@ -3978,7 +3978,7 @@ private void ensureSkillsIsMutable() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public java.util.List getSkillsList() { if (skillsBuilder_ == null) { @@ -3994,7 +3994,7 @@ public java.util.List getSkillsList() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public int getSkillsCount() { if (skillsBuilder_ == null) { @@ -4010,7 +4010,7 @@ public int getSkillsCount() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public io.a2a.grpc.AgentSkill getSkills(int index) { if (skillsBuilder_ == null) { @@ -4026,7 +4026,7 @@ public io.a2a.grpc.AgentSkill getSkills(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder setSkills( int index, io.a2a.grpc.AgentSkill value) { @@ -4049,7 +4049,7 @@ public Builder setSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder setSkills( int index, io.a2a.grpc.AgentSkill.Builder builderForValue) { @@ -4069,7 +4069,7 @@ public Builder setSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder addSkills(io.a2a.grpc.AgentSkill value) { if (skillsBuilder_ == null) { @@ -4091,7 +4091,7 @@ public Builder addSkills(io.a2a.grpc.AgentSkill value) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder addSkills( int index, io.a2a.grpc.AgentSkill value) { @@ -4114,7 +4114,7 @@ public Builder addSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder addSkills( io.a2a.grpc.AgentSkill.Builder builderForValue) { @@ -4134,7 +4134,7 @@ public Builder addSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder addSkills( int index, io.a2a.grpc.AgentSkill.Builder builderForValue) { @@ -4154,7 +4154,7 @@ public Builder addSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder addAllSkills( java.lang.Iterable values) { @@ -4175,7 +4175,7 @@ public Builder addAllSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder clearSkills() { if (skillsBuilder_ == null) { @@ -4194,7 +4194,7 @@ public Builder clearSkills() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public Builder removeSkills(int index) { if (skillsBuilder_ == null) { @@ -4213,7 +4213,7 @@ public Builder removeSkills(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public io.a2a.grpc.AgentSkill.Builder getSkillsBuilder( int index) { @@ -4226,7 +4226,7 @@ public io.a2a.grpc.AgentSkill.Builder getSkillsBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( int index) { @@ -4242,7 +4242,7 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public java.util.List getSkillsOrBuilderList() { @@ -4259,7 +4259,7 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder() { return internalGetSkillsFieldBuilder().addBuilder( @@ -4272,7 +4272,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( int index) { @@ -4286,7 +4286,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ public java.util.List getSkillsBuilderList() { @@ -4315,7 +4315,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( * different than the card from GetAgentCard. * * - * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * bool supports_authenticated_extended_card = 13; * @return The supportsAuthenticatedExtendedCard. */ @java.lang.Override @@ -4329,7 +4329,7 @@ public boolean getSupportsAuthenticatedExtendedCard() { * different than the card from GetAgentCard. * * - * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * bool supports_authenticated_extended_card = 13; * @param value The supportsAuthenticatedExtendedCard to set. * @return This builder for chaining. */ @@ -4347,7 +4347,7 @@ public Builder setSupportsAuthenticatedExtendedCard(boolean value) { * different than the card from GetAgentCard. * * - * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * bool supports_authenticated_extended_card = 13; * @return This builder for chaining. */ public Builder clearSupportsAuthenticatedExtendedCard() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java index 4de7e064d..6a992f83e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java @@ -15,7 +15,7 @@ public interface AgentCardOrBuilder extends * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return The protocolVersion. */ java.lang.String getProtocolVersion(); @@ -24,7 +24,7 @@ public interface AgentCardOrBuilder extends * The version of the A2A protocol this agent supports. * * - * string protocol_version = 16 [json_name = "protocolVersion"]; + * string protocol_version = 16; * @return The bytes for protocolVersion. */ com.google.protobuf.ByteString @@ -36,7 +36,7 @@ public interface AgentCardOrBuilder extends * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ java.lang.String getName(); @@ -46,7 +46,7 @@ public interface AgentCardOrBuilder extends * Example: "Recipe Agent" * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString @@ -58,7 +58,7 @@ public interface AgentCardOrBuilder extends * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The description. */ java.lang.String getDescription(); @@ -68,7 +68,7 @@ public interface AgentCardOrBuilder extends * Example: "Agent that helps users with recipes and cooking." * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -80,7 +80,7 @@ public interface AgentCardOrBuilder extends * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return The url. */ java.lang.String getUrl(); @@ -90,7 +90,7 @@ public interface AgentCardOrBuilder extends * preferred endpoint as declared by the agent. * * - * string url = 3 [json_name = "url"]; + * string url = 3; * @return The bytes for url. */ com.google.protobuf.ByteString @@ -101,7 +101,7 @@ public interface AgentCardOrBuilder extends * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return The preferredTransport. */ java.lang.String getPreferredTransport(); @@ -110,7 +110,7 @@ public interface AgentCardOrBuilder extends * The transport of the preferred endpoint. If empty, defaults to JSONRPC. * * - * string preferred_transport = 14 [json_name = "preferredTransport"]; + * string preferred_transport = 14; * @return The bytes for preferredTransport. */ com.google.protobuf.ByteString @@ -122,7 +122,7 @@ public interface AgentCardOrBuilder extends * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ java.util.List getAdditionalInterfacesList(); @@ -132,7 +132,7 @@ public interface AgentCardOrBuilder extends * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index); /** @@ -141,7 +141,7 @@ public interface AgentCardOrBuilder extends * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ int getAdditionalInterfacesCount(); /** @@ -150,7 +150,7 @@ public interface AgentCardOrBuilder extends * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ java.util.List getAdditionalInterfacesOrBuilderList(); @@ -160,7 +160,7 @@ public interface AgentCardOrBuilder extends * the supported transports. * * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [json_name = "additionalInterfaces"]; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15; */ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( int index); @@ -170,7 +170,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; * @return Whether the provider field is set. */ boolean hasProvider(); @@ -179,7 +179,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; * @return The provider. */ io.a2a.grpc.AgentProvider getProvider(); @@ -188,7 +188,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * The service provider of the agent. * * - * .a2a.v1.AgentProvider provider = 4 [json_name = "provider"]; + * .a2a.v1.AgentProvider provider = 4; */ io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder(); @@ -198,7 +198,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return The version. */ java.lang.String getVersion(); @@ -208,7 +208,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * Example: "1.0.0" * * - * string version = 5 [json_name = "version"]; + * string version = 5; * @return The bytes for version. */ com.google.protobuf.ByteString @@ -219,7 +219,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return The documentationUrl. */ java.lang.String getDocumentationUrl(); @@ -228,7 +228,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6 [json_name = "documentationUrl"]; + * string documentation_url = 6; * @return The bytes for documentationUrl. */ com.google.protobuf.ByteString @@ -239,7 +239,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; * @return Whether the capabilities field is set. */ boolean hasCapabilities(); @@ -248,7 +248,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; * @return The capabilities. */ io.a2a.grpc.AgentCapabilities getCapabilities(); @@ -257,7 +257,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7 [json_name = "capabilities"]; + * .a2a.v1.AgentCapabilities capabilities = 7; */ io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder(); @@ -266,7 +266,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ int getSecuritySchemesCount(); /** @@ -274,7 +274,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ boolean containsSecuritySchemes( java.lang.String key); @@ -289,7 +289,7 @@ boolean containsSecuritySchemes( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ java.util.Map getSecuritySchemesMap(); @@ -298,7 +298,7 @@ boolean containsSecuritySchemes( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ /* nullable */ io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( @@ -310,7 +310,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrDefault( * The security scheme details used for authenticating with this agent. * * - * map<string, .a2a.v1.SecurityScheme> security_schemes = 8 [json_name = "securitySchemes"]; + * map<string, .a2a.v1.SecurityScheme> security_schemes = 8; */ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( java.lang.String key); @@ -321,7 +321,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ java.util.List getSecurityList(); @@ -331,7 +331,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ io.a2a.grpc.Security getSecurity(int index); /** @@ -340,7 +340,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ int getSecurityCount(); /** @@ -349,7 +349,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ java.util.List getSecurityOrBuilderList(); @@ -359,7 +359,7 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( * Security requirements for contacting the agent. * * - * repeated .a2a.v1.Security security = 9 [json_name = "security"]; + * repeated .a2a.v1.Security security = 9; */ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( int index); @@ -371,7 +371,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return A list containing the defaultInputModes. */ java.util.List @@ -383,7 +383,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @return The count of defaultInputModes. */ int getDefaultInputModesCount(); @@ -394,7 +394,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index of the element to return. * @return The defaultInputModes at the given index. */ @@ -406,7 +406,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * This can be overridden per skill. Defined as mime types. * * - * repeated string default_input_modes = 10 [json_name = "defaultInputModes"]; + * repeated string default_input_modes = 10; * @param index The index of the value to return. * @return The bytes of the defaultInputModes at the given index. */ @@ -418,7 +418,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return A list containing the defaultOutputModes. */ java.util.List @@ -428,7 +428,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @return The count of defaultOutputModes. */ int getDefaultOutputModesCount(); @@ -437,7 +437,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index of the element to return. * @return The defaultOutputModes at the given index. */ @@ -447,7 +447,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * The mime types supported as outputs from this agent. * * - * repeated string default_output_modes = 11 [json_name = "defaultOutputModes"]; + * repeated string default_output_modes = 11; * @param index The index of the value to return. * @return The bytes of the defaultOutputModes at the given index. */ @@ -461,7 +461,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ java.util.List getSkillsList(); @@ -472,7 +472,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ io.a2a.grpc.AgentSkill getSkills(int index); /** @@ -482,7 +482,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ int getSkillsCount(); /** @@ -492,7 +492,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ java.util.List getSkillsOrBuilderList(); @@ -503,7 +503,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12 [json_name = "skills"]; + * repeated .a2a.v1.AgentSkill skills = 12; */ io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( int index); @@ -515,7 +515,7 @@ io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * different than the card from GetAgentCard. * * - * bool supports_authenticated_extended_card = 13 [json_name = "supportsAuthenticatedExtendedCard"]; + * bool supports_authenticated_extended_card = 13; * @return The supportsAuthenticatedExtendedCard. */ boolean getSupportsAuthenticatedExtendedCard(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java index 04e298d8d..dd3906cb5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java @@ -59,7 +59,7 @@ private AgentExtension() { * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return The uri. */ @java.lang.Override @@ -81,7 +81,7 @@ public java.lang.String getUri() { * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return The bytes for uri. */ @java.lang.Override @@ -108,7 +108,7 @@ public java.lang.String getUri() { * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The description. */ @java.lang.Override @@ -130,7 +130,7 @@ public java.lang.String getDescription() { * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The bytes for description. */ @java.lang.Override @@ -156,7 +156,7 @@ public java.lang.String getDescription() { * Example: false * * - * bool required = 3 [json_name = "required"]; + * bool required = 3; * @return The required. */ @java.lang.Override @@ -171,7 +171,7 @@ public boolean getRequired() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; * @return Whether the params field is set. */ @java.lang.Override @@ -183,7 +183,7 @@ public boolean hasParams() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; * @return The params. */ @java.lang.Override @@ -195,7 +195,7 @@ public com.google.protobuf.Struct getParams() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getParamsOrBuilder() { @@ -600,7 +600,7 @@ public Builder mergeFrom( * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return The uri. */ public java.lang.String getUri() { @@ -621,7 +621,7 @@ public java.lang.String getUri() { * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return The bytes for uri. */ public com.google.protobuf.ByteString @@ -643,7 +643,7 @@ public java.lang.String getUri() { * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @param value The uri to set. * @return This builder for chaining. */ @@ -661,7 +661,7 @@ public Builder setUri( * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return This builder for chaining. */ public Builder clearUri() { @@ -676,7 +676,7 @@ public Builder clearUri() { * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @param value The bytes for uri to set. * @return This builder for chaining. */ @@ -697,7 +697,7 @@ public Builder setUriBytes( * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The description. */ public java.lang.String getDescription() { @@ -718,7 +718,7 @@ public java.lang.String getDescription() { * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -740,7 +740,7 @@ public java.lang.String getDescription() { * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @param value The description to set. * @return This builder for chaining. */ @@ -758,7 +758,7 @@ public Builder setDescription( * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return This builder for chaining. */ public Builder clearDescription() { @@ -773,7 +773,7 @@ public Builder clearDescription() { * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -794,7 +794,7 @@ public Builder setDescriptionBytes( * Example: false * * - * bool required = 3 [json_name = "required"]; + * bool required = 3; * @return The required. */ @java.lang.Override @@ -807,7 +807,7 @@ public boolean getRequired() { * Example: false * * - * bool required = 3 [json_name = "required"]; + * bool required = 3; * @param value The required to set. * @return This builder for chaining. */ @@ -824,7 +824,7 @@ public Builder setRequired(boolean value) { * Example: false * * - * bool required = 3 [json_name = "required"]; + * bool required = 3; * @return This builder for chaining. */ public Builder clearRequired() { @@ -842,7 +842,7 @@ public Builder clearRequired() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; * @return Whether the params field is set. */ public boolean hasParams() { @@ -853,7 +853,7 @@ public boolean hasParams() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; * @return The params. */ public com.google.protobuf.Struct getParams() { @@ -868,7 +868,7 @@ public com.google.protobuf.Struct getParams() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ public Builder setParams(com.google.protobuf.Struct value) { if (paramsBuilder_ == null) { @@ -888,7 +888,7 @@ public Builder setParams(com.google.protobuf.Struct value) { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ public Builder setParams( com.google.protobuf.Struct.Builder builderForValue) { @@ -906,7 +906,7 @@ public Builder setParams( * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ public Builder mergeParams(com.google.protobuf.Struct value) { if (paramsBuilder_ == null) { @@ -931,7 +931,7 @@ public Builder mergeParams(com.google.protobuf.Struct value) { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ public Builder clearParams() { bitField0_ = (bitField0_ & ~0x00000008); @@ -948,7 +948,7 @@ public Builder clearParams() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ public com.google.protobuf.Struct.Builder getParamsBuilder() { bitField0_ |= 0x00000008; @@ -960,7 +960,7 @@ public com.google.protobuf.Struct.Builder getParamsBuilder() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ public com.google.protobuf.StructOrBuilder getParamsOrBuilder() { if (paramsBuilder_ != null) { @@ -975,7 +975,7 @@ public com.google.protobuf.StructOrBuilder getParamsOrBuilder() { * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java index 580b65020..789b314f5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java @@ -16,7 +16,7 @@ public interface AgentExtensionOrBuilder extends * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return The uri. */ java.lang.String getUri(); @@ -26,7 +26,7 @@ public interface AgentExtensionOrBuilder extends * Example: "https://developers.google.com/identity/protocols/oauth2" * * - * string uri = 1 [json_name = "uri"]; + * string uri = 1; * @return The bytes for uri. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface AgentExtensionOrBuilder extends * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The description. */ java.lang.String getDescription(); @@ -48,7 +48,7 @@ public interface AgentExtensionOrBuilder extends * Example: "Google OAuth 2.0 authentication" * * - * string description = 2 [json_name = "description"]; + * string description = 2; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -60,7 +60,7 @@ public interface AgentExtensionOrBuilder extends * Example: false * * - * bool required = 3 [json_name = "required"]; + * bool required = 3; * @return The required. */ boolean getRequired(); @@ -70,7 +70,7 @@ public interface AgentExtensionOrBuilder extends * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; * @return Whether the params field is set. */ boolean hasParams(); @@ -79,7 +79,7 @@ public interface AgentExtensionOrBuilder extends * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; * @return The params. */ com.google.protobuf.Struct getParams(); @@ -88,7 +88,7 @@ public interface AgentExtensionOrBuilder extends * Optional configuration for the extension. * * - * .google.protobuf.Struct params = 4 [json_name = "params"]; + * .google.protobuf.Struct params = 4; */ com.google.protobuf.StructOrBuilder getParamsOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java index 78ad52d8e..120acd347 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java @@ -57,7 +57,7 @@ private AgentInterface() { * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The url. */ @java.lang.Override @@ -78,7 +78,7 @@ public java.lang.String getUrl() { * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The bytes for url. */ @java.lang.Override @@ -106,7 +106,7 @@ public java.lang.String getUrl() { * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return The transport. */ @java.lang.Override @@ -129,7 +129,7 @@ public java.lang.String getTransport() { * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return The bytes for transport. */ @java.lang.Override @@ -475,7 +475,7 @@ public Builder mergeFrom( * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The url. */ public java.lang.String getUrl() { @@ -495,7 +495,7 @@ public java.lang.String getUrl() { * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -516,7 +516,7 @@ public java.lang.String getUrl() { * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @param value The url to set. * @return This builder for chaining. */ @@ -533,7 +533,7 @@ public Builder setUrl( * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return This builder for chaining. */ public Builder clearUrl() { @@ -547,7 +547,7 @@ public Builder clearUrl() { * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -569,7 +569,7 @@ public Builder setUrlBytes( * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return The transport. */ public java.lang.String getTransport() { @@ -591,7 +591,7 @@ public java.lang.String getTransport() { * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return The bytes for transport. */ public com.google.protobuf.ByteString @@ -614,7 +614,7 @@ public java.lang.String getTransport() { * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @param value The transport to set. * @return This builder for chaining. */ @@ -633,7 +633,7 @@ public Builder setTransport( * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return This builder for chaining. */ public Builder clearTransport() { @@ -649,7 +649,7 @@ public Builder clearTransport() { * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @param value The bytes for transport to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java index 22d5faf01..c0263e901 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java @@ -15,7 +15,7 @@ public interface AgentInterfaceOrBuilder extends * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The url. */ java.lang.String getUrl(); @@ -24,7 +24,7 @@ public interface AgentInterfaceOrBuilder extends * The url this interface is found at. * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The bytes for url. */ com.google.protobuf.ByteString @@ -37,7 +37,7 @@ public interface AgentInterfaceOrBuilder extends * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return The transport. */ java.lang.String getTransport(); @@ -48,7 +48,7 @@ public interface AgentInterfaceOrBuilder extends * supported are JSONRPC, GRPC and HTTP+JSON. * * - * string transport = 2 [json_name = "transport"]; + * string transport = 2; * @return The bytes for transport. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java index a166ddaa4..57226e43c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java @@ -58,7 +58,7 @@ private AgentProvider() { * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The url. */ @java.lang.Override @@ -80,7 +80,7 @@ public java.lang.String getUrl() { * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The bytes for url. */ @java.lang.Override @@ -107,7 +107,7 @@ public java.lang.String getUrl() { * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return The organization. */ @java.lang.Override @@ -129,7 +129,7 @@ public java.lang.String getOrganization() { * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return The bytes for organization. */ @java.lang.Override @@ -476,7 +476,7 @@ public Builder mergeFrom( * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The url. */ public java.lang.String getUrl() { @@ -497,7 +497,7 @@ public java.lang.String getUrl() { * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -519,7 +519,7 @@ public java.lang.String getUrl() { * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @param value The url to set. * @return This builder for chaining. */ @@ -537,7 +537,7 @@ public Builder setUrl( * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return This builder for chaining. */ public Builder clearUrl() { @@ -552,7 +552,7 @@ public Builder clearUrl() { * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -573,7 +573,7 @@ public Builder setUrlBytes( * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return The organization. */ public java.lang.String getOrganization() { @@ -594,7 +594,7 @@ public java.lang.String getOrganization() { * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return The bytes for organization. */ public com.google.protobuf.ByteString @@ -616,7 +616,7 @@ public java.lang.String getOrganization() { * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @param value The organization to set. * @return This builder for chaining. */ @@ -634,7 +634,7 @@ public Builder setOrganization( * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return This builder for chaining. */ public Builder clearOrganization() { @@ -649,7 +649,7 @@ public Builder clearOrganization() { * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @param value The bytes for organization to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java index 0a54dc18f..f5e70cc40 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java @@ -16,7 +16,7 @@ public interface AgentProviderOrBuilder extends * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The url. */ java.lang.String getUrl(); @@ -26,7 +26,7 @@ public interface AgentProviderOrBuilder extends * Example: "https://ai.google.dev" * * - * string url = 1 [json_name = "url"]; + * string url = 1; * @return The bytes for url. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface AgentProviderOrBuilder extends * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return The organization. */ java.lang.String getOrganization(); @@ -48,7 +48,7 @@ public interface AgentProviderOrBuilder extends * Example: "Google" * * - * string organization = 2 [json_name = "organization"]; + * string organization = 2; * @return The bytes for organization. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java index c8d4dbf0b..1d6fed46c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -70,7 +70,7 @@ private AgentSkill() { * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ @java.lang.Override @@ -91,7 +91,7 @@ public java.lang.String getId() { * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ @java.lang.Override @@ -117,7 +117,7 @@ public java.lang.String getId() { * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return The name. */ @java.lang.Override @@ -138,7 +138,7 @@ public java.lang.String getName() { * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return The bytes for name. */ @java.lang.Override @@ -165,7 +165,7 @@ public java.lang.String getName() { * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return The description. */ @java.lang.Override @@ -187,7 +187,7 @@ public java.lang.String getDescription() { * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return The bytes for description. */ @java.lang.Override @@ -215,7 +215,7 @@ public java.lang.String getDescription() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return A list containing the tags. */ public com.google.protobuf.ProtocolStringList @@ -228,7 +228,7 @@ public java.lang.String getDescription() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return The count of tags. */ public int getTagsCount() { @@ -240,7 +240,7 @@ public int getTagsCount() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index of the element to return. * @return The tags at the given index. */ @@ -253,7 +253,7 @@ public java.lang.String getTags(int index) { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index of the value to return. * @return The bytes of the tags at the given index. */ @@ -274,7 +274,7 @@ public java.lang.String getTags(int index) { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return A list containing the examples. */ public com.google.protobuf.ProtocolStringList @@ -289,7 +289,7 @@ public java.lang.String getTags(int index) { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return The count of examples. */ public int getExamplesCount() { @@ -303,7 +303,7 @@ public int getExamplesCount() { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index of the element to return. * @return The examples at the given index. */ @@ -318,7 +318,7 @@ public java.lang.String getExamples(int index) { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index of the value to return. * @return The bytes of the examples at the given index. */ @@ -336,7 +336,7 @@ public java.lang.String getExamples(int index) { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return A list containing the inputModes. */ public com.google.protobuf.ProtocolStringList @@ -348,7 +348,7 @@ public java.lang.String getExamples(int index) { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return The count of inputModes. */ public int getInputModesCount() { @@ -359,7 +359,7 @@ public int getInputModesCount() { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index of the element to return. * @return The inputModes at the given index. */ @@ -371,7 +371,7 @@ public java.lang.String getInputModes(int index) { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index of the value to return. * @return The bytes of the inputModes at the given index. */ @@ -389,7 +389,7 @@ public java.lang.String getInputModes(int index) { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return A list containing the outputModes. */ public com.google.protobuf.ProtocolStringList @@ -401,7 +401,7 @@ public java.lang.String getInputModes(int index) { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return The count of outputModes. */ public int getOutputModesCount() { @@ -412,7 +412,7 @@ public int getOutputModesCount() { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index of the element to return. * @return The outputModes at the given index. */ @@ -424,7 +424,7 @@ public java.lang.String getOutputModes(int index) { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index of the value to return. * @return The bytes of the outputModes at the given index. */ @@ -945,7 +945,7 @@ public Builder mergeFrom( * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ public java.lang.String getId() { @@ -965,7 +965,7 @@ public java.lang.String getId() { * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ public com.google.protobuf.ByteString @@ -986,7 +986,7 @@ public java.lang.String getId() { * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @param value The id to set. * @return This builder for chaining. */ @@ -1003,7 +1003,7 @@ public Builder setId( * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return This builder for chaining. */ public Builder clearId() { @@ -1017,7 +1017,7 @@ public Builder clearId() { * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @param value The bytes for id to set. * @return This builder for chaining. */ @@ -1037,7 +1037,7 @@ public Builder setIdBytes( * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return The name. */ public java.lang.String getName() { @@ -1057,7 +1057,7 @@ public java.lang.String getName() { * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -1078,7 +1078,7 @@ public java.lang.String getName() { * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @param value The name to set. * @return This builder for chaining. */ @@ -1095,7 +1095,7 @@ public Builder setName( * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return This builder for chaining. */ public Builder clearName() { @@ -1109,7 +1109,7 @@ public Builder clearName() { * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -1130,7 +1130,7 @@ public Builder setNameBytes( * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return The description. */ public java.lang.String getDescription() { @@ -1151,7 +1151,7 @@ public java.lang.String getDescription() { * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -1173,7 +1173,7 @@ public java.lang.String getDescription() { * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @param value The description to set. * @return This builder for chaining. */ @@ -1191,7 +1191,7 @@ public Builder setDescription( * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return This builder for chaining. */ public Builder clearDescription() { @@ -1206,7 +1206,7 @@ public Builder clearDescription() { * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -1234,7 +1234,7 @@ private void ensureTagsIsMutable() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return A list containing the tags. */ public com.google.protobuf.ProtocolStringList @@ -1248,7 +1248,7 @@ private void ensureTagsIsMutable() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return The count of tags. */ public int getTagsCount() { @@ -1260,7 +1260,7 @@ public int getTagsCount() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index of the element to return. * @return The tags at the given index. */ @@ -1273,7 +1273,7 @@ public java.lang.String getTags(int index) { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index of the value to return. * @return The bytes of the tags at the given index. */ @@ -1287,7 +1287,7 @@ public java.lang.String getTags(int index) { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index to set the value at. * @param value The tags to set. * @return This builder for chaining. @@ -1307,7 +1307,7 @@ public Builder setTags( * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param value The tags to add. * @return This builder for chaining. */ @@ -1326,7 +1326,7 @@ public Builder addTags( * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param values The tags to add. * @return This builder for chaining. */ @@ -1345,7 +1345,7 @@ public Builder addAllTags( * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return This builder for chaining. */ public Builder clearTags() { @@ -1361,7 +1361,7 @@ public Builder clearTags() { * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param value The bytes of the tags to add. * @return This builder for chaining. */ @@ -1392,7 +1392,7 @@ private void ensureExamplesIsMutable() { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return A list containing the examples. */ public com.google.protobuf.ProtocolStringList @@ -1408,7 +1408,7 @@ private void ensureExamplesIsMutable() { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return The count of examples. */ public int getExamplesCount() { @@ -1422,7 +1422,7 @@ public int getExamplesCount() { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index of the element to return. * @return The examples at the given index. */ @@ -1437,7 +1437,7 @@ public java.lang.String getExamples(int index) { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index of the value to return. * @return The bytes of the examples at the given index. */ @@ -1453,7 +1453,7 @@ public java.lang.String getExamples(int index) { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index to set the value at. * @param value The examples to set. * @return This builder for chaining. @@ -1475,7 +1475,7 @@ public Builder setExamples( * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param value The examples to add. * @return This builder for chaining. */ @@ -1496,7 +1496,7 @@ public Builder addExamples( * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param values The examples to add. * @return This builder for chaining. */ @@ -1517,7 +1517,7 @@ public Builder addAllExamples( * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return This builder for chaining. */ public Builder clearExamples() { @@ -1535,7 +1535,7 @@ public Builder clearExamples() { * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param value The bytes of the examples to add. * @return This builder for chaining. */ @@ -1563,7 +1563,7 @@ private void ensureInputModesIsMutable() { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return A list containing the inputModes. */ public com.google.protobuf.ProtocolStringList @@ -1576,7 +1576,7 @@ private void ensureInputModesIsMutable() { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return The count of inputModes. */ public int getInputModesCount() { @@ -1587,7 +1587,7 @@ public int getInputModesCount() { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index of the element to return. * @return The inputModes at the given index. */ @@ -1599,7 +1599,7 @@ public java.lang.String getInputModes(int index) { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index of the value to return. * @return The bytes of the inputModes at the given index. */ @@ -1612,7 +1612,7 @@ public java.lang.String getInputModes(int index) { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index to set the value at. * @param value The inputModes to set. * @return This builder for chaining. @@ -1631,7 +1631,7 @@ public Builder setInputModes( * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param value The inputModes to add. * @return This builder for chaining. */ @@ -1649,7 +1649,7 @@ public Builder addInputModes( * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param values The inputModes to add. * @return This builder for chaining. */ @@ -1667,7 +1667,7 @@ public Builder addAllInputModes( * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return This builder for chaining. */ public Builder clearInputModes() { @@ -1682,7 +1682,7 @@ public Builder clearInputModes() { * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param value The bytes of the inputModes to add. * @return This builder for chaining. */ @@ -1710,7 +1710,7 @@ private void ensureOutputModesIsMutable() { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return A list containing the outputModes. */ public com.google.protobuf.ProtocolStringList @@ -1723,7 +1723,7 @@ private void ensureOutputModesIsMutable() { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return The count of outputModes. */ public int getOutputModesCount() { @@ -1734,7 +1734,7 @@ public int getOutputModesCount() { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index of the element to return. * @return The outputModes at the given index. */ @@ -1746,7 +1746,7 @@ public java.lang.String getOutputModes(int index) { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index of the value to return. * @return The bytes of the outputModes at the given index. */ @@ -1759,7 +1759,7 @@ public java.lang.String getOutputModes(int index) { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index to set the value at. * @param value The outputModes to set. * @return This builder for chaining. @@ -1778,7 +1778,7 @@ public Builder setOutputModes( * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param value The outputModes to add. * @return This builder for chaining. */ @@ -1796,7 +1796,7 @@ public Builder addOutputModes( * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param values The outputModes to add. * @return This builder for chaining. */ @@ -1814,7 +1814,7 @@ public Builder addAllOutputModes( * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return This builder for chaining. */ public Builder clearOutputModes() { @@ -1829,7 +1829,7 @@ public Builder clearOutputModes() { * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param value The bytes of the outputModes to add. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java index bfcb9d956..d776b3112 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -15,7 +15,7 @@ public interface AgentSkillOrBuilder extends * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ java.lang.String getId(); @@ -24,7 +24,7 @@ public interface AgentSkillOrBuilder extends * Unique id of the skill within this agent. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface AgentSkillOrBuilder extends * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return The name. */ java.lang.String getName(); @@ -44,7 +44,7 @@ public interface AgentSkillOrBuilder extends * A human readable name for the skill. * * - * string name = 2 [json_name = "name"]; + * string name = 2; * @return The bytes for name. */ com.google.protobuf.ByteString @@ -56,7 +56,7 @@ public interface AgentSkillOrBuilder extends * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return The description. */ java.lang.String getDescription(); @@ -66,7 +66,7 @@ public interface AgentSkillOrBuilder extends * details and behaviors. * * - * string description = 3 [json_name = "description"]; + * string description = 3; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -78,7 +78,7 @@ public interface AgentSkillOrBuilder extends * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return A list containing the tags. */ java.util.List @@ -89,7 +89,7 @@ public interface AgentSkillOrBuilder extends * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @return The count of tags. */ int getTagsCount(); @@ -99,7 +99,7 @@ public interface AgentSkillOrBuilder extends * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index of the element to return. * @return The tags at the given index. */ @@ -110,7 +110,7 @@ public interface AgentSkillOrBuilder extends * Example: ["cooking", "customer support", "billing"] * * - * repeated string tags = 4 [json_name = "tags"]; + * repeated string tags = 4; * @param index The index of the value to return. * @return The bytes of the tags at the given index. */ @@ -125,7 +125,7 @@ public interface AgentSkillOrBuilder extends * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return A list containing the examples. */ java.util.List @@ -138,7 +138,7 @@ public interface AgentSkillOrBuilder extends * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @return The count of examples. */ int getExamplesCount(); @@ -150,7 +150,7 @@ public interface AgentSkillOrBuilder extends * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index of the element to return. * @return The examples at the given index. */ @@ -163,7 +163,7 @@ public interface AgentSkillOrBuilder extends * Example: ["I need a recipe for bread"] * * - * repeated string examples = 5 [json_name = "examples"]; + * repeated string examples = 5; * @param index The index of the value to return. * @return The bytes of the examples at the given index. */ @@ -175,7 +175,7 @@ public interface AgentSkillOrBuilder extends * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return A list containing the inputModes. */ java.util.List @@ -185,7 +185,7 @@ public interface AgentSkillOrBuilder extends * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @return The count of inputModes. */ int getInputModesCount(); @@ -194,7 +194,7 @@ public interface AgentSkillOrBuilder extends * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index of the element to return. * @return The inputModes at the given index. */ @@ -204,7 +204,7 @@ public interface AgentSkillOrBuilder extends * Possible input modalities supported. * * - * repeated string input_modes = 6 [json_name = "inputModes"]; + * repeated string input_modes = 6; * @param index The index of the value to return. * @return The bytes of the inputModes at the given index. */ @@ -216,7 +216,7 @@ public interface AgentSkillOrBuilder extends * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return A list containing the outputModes. */ java.util.List @@ -226,7 +226,7 @@ public interface AgentSkillOrBuilder extends * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @return The count of outputModes. */ int getOutputModesCount(); @@ -235,7 +235,7 @@ public interface AgentSkillOrBuilder extends * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index of the element to return. * @return The outputModes at the given index. */ @@ -245,7 +245,7 @@ public interface AgentSkillOrBuilder extends * Possible output modalities produced * * - * repeated string output_modes = 7 [json_name = "outputModes"]; + * repeated string output_modes = 7; * @param index The index of the value to return. * @return The bytes of the outputModes at the given index. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java index 3e50bbcf8..e9be6369a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java @@ -64,7 +64,7 @@ private Artifact() { * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return The artifactId. */ @java.lang.Override @@ -85,7 +85,7 @@ public java.lang.String getArtifactId() { * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return The bytes for artifactId. */ @java.lang.Override @@ -111,7 +111,7 @@ public java.lang.String getArtifactId() { * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The name. */ @java.lang.Override @@ -132,7 +132,7 @@ public java.lang.String getName() { * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The bytes for name. */ @java.lang.Override @@ -158,7 +158,7 @@ public java.lang.String getName() { * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return The description. */ @java.lang.Override @@ -179,7 +179,7 @@ public java.lang.String getDescription() { * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return The bytes for description. */ @java.lang.Override @@ -205,7 +205,7 @@ public java.lang.String getDescription() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override public java.util.List getPartsList() { @@ -216,7 +216,7 @@ public java.util.List getPartsList() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override public java.util.List @@ -228,7 +228,7 @@ public java.util.List getPartsList() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override public int getPartsCount() { @@ -239,7 +239,7 @@ public int getPartsCount() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override public io.a2a.grpc.Part getParts(int index) { @@ -250,7 +250,7 @@ public io.a2a.grpc.Part getParts(int index) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( @@ -265,7 +265,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ @java.lang.Override @@ -277,7 +277,7 @@ public boolean hasMetadata() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ @java.lang.Override @@ -289,7 +289,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -305,7 +305,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return A list containing the extensions. */ public com.google.protobuf.ProtocolStringList @@ -317,7 +317,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return The count of extensions. */ public int getExtensionsCount() { @@ -328,7 +328,7 @@ public int getExtensionsCount() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the element to return. * @return The extensions at the given index. */ @@ -340,7 +340,7 @@ public java.lang.String getExtensions(int index) { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the value to return. * @return The bytes of the extensions at the given index. */ @@ -860,7 +860,7 @@ public Builder mergeFrom( * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return The artifactId. */ public java.lang.String getArtifactId() { @@ -880,7 +880,7 @@ public java.lang.String getArtifactId() { * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return The bytes for artifactId. */ public com.google.protobuf.ByteString @@ -901,7 +901,7 @@ public java.lang.String getArtifactId() { * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @param value The artifactId to set. * @return This builder for chaining. */ @@ -918,7 +918,7 @@ public Builder setArtifactId( * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return This builder for chaining. */ public Builder clearArtifactId() { @@ -932,7 +932,7 @@ public Builder clearArtifactId() { * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @param value The bytes for artifactId to set. * @return This builder for chaining. */ @@ -952,7 +952,7 @@ public Builder setArtifactIdBytes( * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The name. */ public java.lang.String getName() { @@ -972,7 +972,7 @@ public java.lang.String getName() { * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -993,7 +993,7 @@ public java.lang.String getName() { * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @param value The name to set. * @return This builder for chaining. */ @@ -1010,7 +1010,7 @@ public Builder setName( * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return This builder for chaining. */ public Builder clearName() { @@ -1024,7 +1024,7 @@ public Builder clearName() { * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -1044,7 +1044,7 @@ public Builder setNameBytes( * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return The description. */ public java.lang.String getDescription() { @@ -1064,7 +1064,7 @@ public java.lang.String getDescription() { * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -1085,7 +1085,7 @@ public java.lang.String getDescription() { * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @param value The description to set. * @return This builder for chaining. */ @@ -1102,7 +1102,7 @@ public Builder setDescription( * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return This builder for chaining. */ public Builder clearDescription() { @@ -1116,7 +1116,7 @@ public Builder clearDescription() { * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -1147,7 +1147,7 @@ private void ensurePartsIsMutable() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public java.util.List getPartsList() { if (partsBuilder_ == null) { @@ -1161,7 +1161,7 @@ public java.util.List getPartsList() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public int getPartsCount() { if (partsBuilder_ == null) { @@ -1175,7 +1175,7 @@ public int getPartsCount() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public io.a2a.grpc.Part getParts(int index) { if (partsBuilder_ == null) { @@ -1189,7 +1189,7 @@ public io.a2a.grpc.Part getParts(int index) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder setParts( int index, io.a2a.grpc.Part value) { @@ -1210,7 +1210,7 @@ public Builder setParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder setParts( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1228,7 +1228,7 @@ public Builder setParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder addParts(io.a2a.grpc.Part value) { if (partsBuilder_ == null) { @@ -1248,7 +1248,7 @@ public Builder addParts(io.a2a.grpc.Part value) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder addParts( int index, io.a2a.grpc.Part value) { @@ -1269,7 +1269,7 @@ public Builder addParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder addParts( io.a2a.grpc.Part.Builder builderForValue) { @@ -1287,7 +1287,7 @@ public Builder addParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder addParts( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1305,7 +1305,7 @@ public Builder addParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder addAllParts( java.lang.Iterable values) { @@ -1324,7 +1324,7 @@ public Builder addAllParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder clearParts() { if (partsBuilder_ == null) { @@ -1341,7 +1341,7 @@ public Builder clearParts() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public Builder removeParts(int index) { if (partsBuilder_ == null) { @@ -1358,7 +1358,7 @@ public Builder removeParts(int index) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public io.a2a.grpc.Part.Builder getPartsBuilder( int index) { @@ -1369,7 +1369,7 @@ public io.a2a.grpc.Part.Builder getPartsBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index) { @@ -1383,7 +1383,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public java.util.List getPartsOrBuilderList() { @@ -1398,7 +1398,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public io.a2a.grpc.Part.Builder addPartsBuilder() { return internalGetPartsFieldBuilder().addBuilder( @@ -1409,7 +1409,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public io.a2a.grpc.Part.Builder addPartsBuilder( int index) { @@ -1421,7 +1421,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ public java.util.List getPartsBuilderList() { @@ -1450,7 +1450,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder( * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ public boolean hasMetadata() { @@ -1461,7 +1461,7 @@ public boolean hasMetadata() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -1476,7 +1476,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1496,7 +1496,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -1514,7 +1514,7 @@ public Builder setMetadata( * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1539,7 +1539,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000010); @@ -1556,7 +1556,7 @@ public Builder clearMetadata() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { bitField0_ |= 0x00000010; @@ -1568,7 +1568,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -1583,7 +1583,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> @@ -1612,7 +1612,7 @@ private void ensureExtensionsIsMutable() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return A list containing the extensions. */ public com.google.protobuf.ProtocolStringList @@ -1625,7 +1625,7 @@ private void ensureExtensionsIsMutable() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return The count of extensions. */ public int getExtensionsCount() { @@ -1636,7 +1636,7 @@ public int getExtensionsCount() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the element to return. * @return The extensions at the given index. */ @@ -1648,7 +1648,7 @@ public java.lang.String getExtensions(int index) { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the value to return. * @return The bytes of the extensions at the given index. */ @@ -1661,7 +1661,7 @@ public java.lang.String getExtensions(int index) { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index to set the value at. * @param value The extensions to set. * @return This builder for chaining. @@ -1680,7 +1680,7 @@ public Builder setExtensions( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param value The extensions to add. * @return This builder for chaining. */ @@ -1698,7 +1698,7 @@ public Builder addExtensions( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param values The extensions to add. * @return This builder for chaining. */ @@ -1716,7 +1716,7 @@ public Builder addAllExtensions( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return This builder for chaining. */ public Builder clearExtensions() { @@ -1731,7 +1731,7 @@ public Builder clearExtensions() { * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param value The bytes of the extensions to add. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java index 54219afa3..05c14a8eb 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java @@ -15,7 +15,7 @@ public interface ArtifactOrBuilder extends * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return The artifactId. */ java.lang.String getArtifactId(); @@ -24,7 +24,7 @@ public interface ArtifactOrBuilder extends * Unique id for the artifact. It must be at least unique within a task. * * - * string artifact_id = 1 [json_name = "artifactId"]; + * string artifact_id = 1; * @return The bytes for artifactId. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface ArtifactOrBuilder extends * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The name. */ java.lang.String getName(); @@ -44,7 +44,7 @@ public interface ArtifactOrBuilder extends * A human readable name for the artifact. * * - * string name = 3 [json_name = "name"]; + * string name = 3; * @return The bytes for name. */ com.google.protobuf.ByteString @@ -55,7 +55,7 @@ public interface ArtifactOrBuilder extends * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return The description. */ java.lang.String getDescription(); @@ -64,7 +64,7 @@ public interface ArtifactOrBuilder extends * A human readable description of the artifact, optional. * * - * string description = 4 [json_name = "description"]; + * string description = 4; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -75,7 +75,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ java.util.List getPartsList(); @@ -84,7 +84,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ io.a2a.grpc.Part getParts(int index); /** @@ -92,7 +92,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ int getPartsCount(); /** @@ -100,7 +100,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ java.util.List getPartsOrBuilderList(); @@ -109,7 +109,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5 [json_name = "parts"]; + * repeated .a2a.v1.Part parts = 5; */ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index); @@ -119,7 +119,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ boolean hasMetadata(); @@ -128,7 +128,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); @@ -137,7 +137,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * Optional metadata included with the artifact. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); @@ -146,7 +146,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return A list containing the extensions. */ java.util.List @@ -156,7 +156,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return The count of extensions. */ int getExtensionsCount(); @@ -165,7 +165,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the element to return. * @return The extensions at the given index. */ @@ -175,7 +175,7 @@ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The URIs of extensions that are present or contributed to this Artifact. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the value to return. * @return The bytes of the extensions at the given index. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java index 723f7164a..c7a21b14d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java @@ -59,7 +59,7 @@ private AuthenticationInfo() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return A list containing the schemes. */ public com.google.protobuf.ProtocolStringList @@ -71,7 +71,7 @@ private AuthenticationInfo() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return The count of schemes. */ public int getSchemesCount() { @@ -82,7 +82,7 @@ public int getSchemesCount() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index of the element to return. * @return The schemes at the given index. */ @@ -94,7 +94,7 @@ public java.lang.String getSchemes(int index) { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index of the value to return. * @return The bytes of the schemes at the given index. */ @@ -111,7 +111,7 @@ public java.lang.String getSchemes(int index) { * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return The credentials. */ @java.lang.Override @@ -132,7 +132,7 @@ public java.lang.String getCredentials() { * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return The bytes for credentials. */ @java.lang.Override @@ -500,7 +500,7 @@ private void ensureSchemesIsMutable() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return A list containing the schemes. */ public com.google.protobuf.ProtocolStringList @@ -513,7 +513,7 @@ private void ensureSchemesIsMutable() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return The count of schemes. */ public int getSchemesCount() { @@ -524,7 +524,7 @@ public int getSchemesCount() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index of the element to return. * @return The schemes at the given index. */ @@ -536,7 +536,7 @@ public java.lang.String getSchemes(int index) { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index of the value to return. * @return The bytes of the schemes at the given index. */ @@ -549,7 +549,7 @@ public java.lang.String getSchemes(int index) { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index to set the value at. * @param value The schemes to set. * @return This builder for chaining. @@ -568,7 +568,7 @@ public Builder setSchemes( * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param value The schemes to add. * @return This builder for chaining. */ @@ -586,7 +586,7 @@ public Builder addSchemes( * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param values The schemes to add. * @return This builder for chaining. */ @@ -604,7 +604,7 @@ public Builder addAllSchemes( * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return This builder for chaining. */ public Builder clearSchemes() { @@ -619,7 +619,7 @@ public Builder clearSchemes() { * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param value The bytes of the schemes to add. * @return This builder for chaining. */ @@ -640,7 +640,7 @@ public Builder addSchemesBytes( * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return The credentials. */ public java.lang.String getCredentials() { @@ -660,7 +660,7 @@ public java.lang.String getCredentials() { * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return The bytes for credentials. */ public com.google.protobuf.ByteString @@ -681,7 +681,7 @@ public java.lang.String getCredentials() { * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @param value The credentials to set. * @return This builder for chaining. */ @@ -698,7 +698,7 @@ public Builder setCredentials( * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return This builder for chaining. */ public Builder clearCredentials() { @@ -712,7 +712,7 @@ public Builder clearCredentials() { * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @param value The bytes for credentials to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java index 097b910e5..b18ba8b63 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java @@ -15,7 +15,7 @@ public interface AuthenticationInfoOrBuilder extends * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return A list containing the schemes. */ java.util.List @@ -25,7 +25,7 @@ public interface AuthenticationInfoOrBuilder extends * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @return The count of schemes. */ int getSchemesCount(); @@ -34,7 +34,7 @@ public interface AuthenticationInfoOrBuilder extends * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index of the element to return. * @return The schemes at the given index. */ @@ -44,7 +44,7 @@ public interface AuthenticationInfoOrBuilder extends * Supported authentication schemes - e.g. Basic, Bearer, etc * * - * repeated string schemes = 1 [json_name = "schemes"]; + * repeated string schemes = 1; * @param index The index of the value to return. * @return The bytes of the schemes at the given index. */ @@ -56,7 +56,7 @@ public interface AuthenticationInfoOrBuilder extends * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return The credentials. */ java.lang.String getCredentials(); @@ -65,7 +65,7 @@ public interface AuthenticationInfoOrBuilder extends * Optional credentials * * - * string credentials = 2 [json_name = "credentials"]; + * string credentials = 2; * @return The bytes for credentials. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java index cbe26afb6..25d4dfd17 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java @@ -67,7 +67,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The authorizationUrl. */ @java.lang.Override @@ -89,7 +89,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The bytes for authorizationUrl. */ @java.lang.Override @@ -116,7 +116,7 @@ public java.lang.String getAuthorizationUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return The tokenUrl. */ @java.lang.Override @@ -138,7 +138,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -165,7 +165,7 @@ public java.lang.String getTokenUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return The refreshUrl. */ @java.lang.Override @@ -187,7 +187,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return The bytes for refreshUrl. */ @java.lang.Override @@ -237,7 +237,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public boolean containsScopes( @@ -259,7 +259,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -271,7 +271,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public /* nullable */ @@ -290,7 +290,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -714,7 +714,7 @@ public Builder mergeFrom( * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The authorizationUrl. */ public java.lang.String getAuthorizationUrl() { @@ -735,7 +735,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The bytes for authorizationUrl. */ public com.google.protobuf.ByteString @@ -757,7 +757,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @param value The authorizationUrl to set. * @return This builder for chaining. */ @@ -775,7 +775,7 @@ public Builder setAuthorizationUrl( * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return This builder for chaining. */ public Builder clearAuthorizationUrl() { @@ -790,7 +790,7 @@ public Builder clearAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @param value The bytes for authorizationUrl to set. * @return This builder for chaining. */ @@ -811,7 +811,7 @@ public Builder setAuthorizationUrlBytes( * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -832,7 +832,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -854,7 +854,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -872,7 +872,7 @@ public Builder setTokenUrl( * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return This builder for chaining. */ public Builder clearTokenUrl() { @@ -887,7 +887,7 @@ public Builder clearTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -908,7 +908,7 @@ public Builder setTokenUrlBytes( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return The refreshUrl. */ public java.lang.String getRefreshUrl() { @@ -929,7 +929,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return The bytes for refreshUrl. */ public com.google.protobuf.ByteString @@ -951,7 +951,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @param value The refreshUrl to set. * @return This builder for chaining. */ @@ -969,7 +969,7 @@ public Builder setRefreshUrl( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return This builder for chaining. */ public Builder clearRefreshUrl() { @@ -984,7 +984,7 @@ public Builder clearRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @param value The bytes for refreshUrl to set. * @return This builder for chaining. */ @@ -1030,7 +1030,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public boolean containsScopes( @@ -1052,7 +1052,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -1064,7 +1064,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public /* nullable */ @@ -1083,7 +1083,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -1108,7 +1108,7 @@ public Builder clearScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ public Builder removeScopes( java.lang.String key) { @@ -1132,7 +1132,7 @@ public Builder removeScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ public Builder putScopes( java.lang.String key, @@ -1150,7 +1150,7 @@ public Builder putScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java index 8e74a3d10..fd6247a7f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java @@ -16,7 +16,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The authorizationUrl. */ java.lang.String getAuthorizationUrl(); @@ -26,7 +26,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The bytes for authorizationUrl. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return The tokenUrl. */ java.lang.String getTokenUrl(); @@ -48,7 +48,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * The OAuth2 standard requires the use of TLS. * * - * string token_url = 2 [json_name = "tokenUrl"]; + * string token_url = 2; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -60,7 +60,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return The refreshUrl. */ java.lang.String getRefreshUrl(); @@ -70,7 +70,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 3 [json_name = "refreshUrl"]; + * string refresh_url = 3; * @return The bytes for refreshUrl. */ com.google.protobuf.ByteString @@ -82,7 +82,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ int getScopesCount(); /** @@ -91,7 +91,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ boolean containsScopes( java.lang.String key); @@ -107,7 +107,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ java.util.Map getScopesMap(); @@ -117,7 +117,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -130,7 +130,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 4 [json_name = "scopes"]; + * map<string, string> scopes = 4; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java index a5120ff06..8674c35c3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java @@ -52,7 +52,7 @@ private CancelTaskRequest() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ @java.lang.Override @@ -73,7 +73,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ @java.lang.Override @@ -391,7 +391,7 @@ public Builder mergeFrom( * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ public java.lang.String getName() { @@ -411,7 +411,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -432,7 +432,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The name to set. * @return This builder for chaining. */ @@ -449,7 +449,7 @@ public Builder setName( * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return This builder for chaining. */ public Builder clearName() { @@ -463,7 +463,7 @@ public Builder clearName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java index 4939f71e4..95431bfc4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java @@ -15,7 +15,7 @@ public interface CancelTaskRequestOrBuilder extends * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ java.lang.String getName(); @@ -24,7 +24,7 @@ public interface CancelTaskRequestOrBuilder extends * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java index 3a5be9442..184e94596 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java @@ -66,7 +66,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The tokenUrl. */ @java.lang.Override @@ -88,7 +88,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -115,7 +115,7 @@ public java.lang.String getTokenUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ @java.lang.Override @@ -137,7 +137,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ @java.lang.Override @@ -187,7 +187,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public boolean containsScopes( @@ -209,7 +209,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -221,7 +221,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public /* nullable */ @@ -240,7 +240,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -640,7 +640,7 @@ public Builder mergeFrom( * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -661,7 +661,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -683,7 +683,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -701,7 +701,7 @@ public Builder setTokenUrl( * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return This builder for chaining. */ public Builder clearTokenUrl() { @@ -716,7 +716,7 @@ public Builder clearTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -737,7 +737,7 @@ public Builder setTokenUrlBytes( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ public java.lang.String getRefreshUrl() { @@ -758,7 +758,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ public com.google.protobuf.ByteString @@ -780,7 +780,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @param value The refreshUrl to set. * @return This builder for chaining. */ @@ -798,7 +798,7 @@ public Builder setRefreshUrl( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return This builder for chaining. */ public Builder clearRefreshUrl() { @@ -813,7 +813,7 @@ public Builder clearRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @param value The bytes for refreshUrl to set. * @return This builder for chaining. */ @@ -859,7 +859,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public boolean containsScopes( @@ -881,7 +881,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -893,7 +893,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public /* nullable */ @@ -912,7 +912,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -937,7 +937,7 @@ public Builder clearScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder removeScopes( java.lang.String key) { @@ -961,7 +961,7 @@ public Builder removeScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder putScopes( java.lang.String key, @@ -979,7 +979,7 @@ public Builder putScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java index c1d3cd312..81ab91e99 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java @@ -16,7 +16,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The tokenUrl. */ java.lang.String getTokenUrl(); @@ -26,7 +26,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ java.lang.String getRefreshUrl(); @@ -48,7 +48,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ com.google.protobuf.ByteString @@ -60,7 +60,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ int getScopesCount(); /** @@ -69,7 +69,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ boolean containsScopes( java.lang.String key); @@ -85,7 +85,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ java.util.Map getScopesMap(); @@ -95,7 +95,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -108,7 +108,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java index 966b0432b..1bd069a94 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java @@ -55,7 +55,7 @@ private CreateTaskPushNotificationConfigRequest() { * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The parent. */ @java.lang.Override @@ -77,7 +77,7 @@ public java.lang.String getParent() { * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for parent. */ @java.lang.Override @@ -99,7 +99,7 @@ public java.lang.String getParent() { @SuppressWarnings("serial") private volatile java.lang.Object configId_ = ""; /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The configId. */ @java.lang.Override @@ -116,7 +116,7 @@ public java.lang.String getConfigId() { } } /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for configId. */ @java.lang.Override @@ -137,7 +137,7 @@ public java.lang.String getConfigId() { public static final int CONFIG_FIELD_NUMBER = 3; private io.a2a.grpc.TaskPushNotificationConfig config_; /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ @java.lang.Override @@ -145,7 +145,7 @@ public boolean hasConfig() { return ((bitField0_ & 0x00000001) != 0); } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ @java.lang.Override @@ -153,7 +153,7 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfig() { return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { @@ -530,7 +530,7 @@ public Builder mergeFrom( * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The parent. */ public java.lang.String getParent() { @@ -551,7 +551,7 @@ public java.lang.String getParent() { * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for parent. */ public com.google.protobuf.ByteString @@ -573,7 +573,7 @@ public java.lang.String getParent() { * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The parent to set. * @return This builder for chaining. */ @@ -591,7 +591,7 @@ public Builder setParent( * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearParent() { @@ -606,7 +606,7 @@ public Builder clearParent() { * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for parent to set. * @return This builder for chaining. */ @@ -622,7 +622,7 @@ public Builder setParentBytes( private java.lang.Object configId_ = ""; /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The configId. */ public java.lang.String getConfigId() { @@ -638,7 +638,7 @@ public java.lang.String getConfigId() { } } /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for configId. */ public com.google.protobuf.ByteString @@ -655,7 +655,7 @@ public java.lang.String getConfigId() { } } /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The configId to set. * @return This builder for chaining. */ @@ -668,7 +668,7 @@ public Builder setConfigId( return this; } /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearConfigId() { @@ -678,7 +678,7 @@ public Builder clearConfigId() { return this; } /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for configId to set. * @return This builder for chaining. */ @@ -696,14 +696,14 @@ public Builder setConfigIdBytes( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configBuilder_; /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ public boolean hasConfig() { return ((bitField0_ & 0x00000004) != 0); } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ public io.a2a.grpc.TaskPushNotificationConfig getConfig() { @@ -714,7 +714,7 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfig() { } } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { if (configBuilder_ == null) { @@ -730,7 +730,7 @@ public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { return this; } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setConfig( io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { @@ -744,7 +744,7 @@ public Builder setConfig( return this; } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { if (configBuilder_ == null) { @@ -765,7 +765,7 @@ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { return this; } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearConfig() { bitField0_ = (bitField0_ & ~0x00000004); @@ -778,7 +778,7 @@ public Builder clearConfig() { return this; } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { bitField0_ |= 0x00000004; @@ -786,7 +786,7 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { return internalGetConfigFieldBuilder().getBuilder(); } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { if (configBuilder_ != null) { @@ -797,7 +797,7 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { } } /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java index 73fa1c0df..003750d66 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java @@ -16,7 +16,7 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The parent. */ java.lang.String getParent(); @@ -26,36 +26,36 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends * Format: tasks/{id} * * - * string parent = 1 [json_name = "parent", (.google.api.field_behavior) = REQUIRED]; + * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for parent. */ com.google.protobuf.ByteString getParentBytes(); /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The configId. */ java.lang.String getConfigId(); /** - * string config_id = 2 [json_name = "configId", (.google.api.field_behavior) = REQUIRED]; + * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for configId. */ com.google.protobuf.ByteString getConfigIdBytes(); /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ boolean hasConfig(); /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ io.a2a.grpc.TaskPushNotificationConfig getConfig(); /** - * .a2a.v1.TaskPushNotificationConfig config = 3 [json_name = "config", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java index 9b793b4bf..de2f2edae 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java @@ -51,7 +51,7 @@ private DataPart() { public static final int DATA_FIELD_NUMBER = 1; private com.google.protobuf.Struct data_; /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; * @return Whether the data field is set. */ @java.lang.Override @@ -59,7 +59,7 @@ public boolean hasData() { return ((bitField0_ & 0x00000001) != 0); } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; * @return The data. */ @java.lang.Override @@ -67,7 +67,7 @@ public com.google.protobuf.Struct getData() { return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getDataOrBuilder() { @@ -397,14 +397,14 @@ public Builder mergeFrom( private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> dataBuilder_; /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; * @return Whether the data field is set. */ public boolean hasData() { return ((bitField0_ & 0x00000001) != 0); } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; * @return The data. */ public com.google.protobuf.Struct getData() { @@ -415,7 +415,7 @@ public com.google.protobuf.Struct getData() { } } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ public Builder setData(com.google.protobuf.Struct value) { if (dataBuilder_ == null) { @@ -431,7 +431,7 @@ public Builder setData(com.google.protobuf.Struct value) { return this; } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ public Builder setData( com.google.protobuf.Struct.Builder builderForValue) { @@ -445,7 +445,7 @@ public Builder setData( return this; } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ public Builder mergeData(com.google.protobuf.Struct value) { if (dataBuilder_ == null) { @@ -466,7 +466,7 @@ public Builder mergeData(com.google.protobuf.Struct value) { return this; } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ public Builder clearData() { bitField0_ = (bitField0_ & ~0x00000001); @@ -479,7 +479,7 @@ public Builder clearData() { return this; } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ public com.google.protobuf.Struct.Builder getDataBuilder() { bitField0_ |= 0x00000001; @@ -487,7 +487,7 @@ public com.google.protobuf.Struct.Builder getDataBuilder() { return internalGetDataFieldBuilder().getBuilder(); } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ public com.google.protobuf.StructOrBuilder getDataOrBuilder() { if (dataBuilder_ != null) { @@ -498,7 +498,7 @@ public com.google.protobuf.StructOrBuilder getDataOrBuilder() { } } /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java index 17f85b202..9877e9f4d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java @@ -11,17 +11,17 @@ public interface DataPartOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; * @return Whether the data field is set. */ boolean hasData(); /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; * @return The data. */ com.google.protobuf.Struct getData(); /** - * .google.protobuf.Struct data = 1 [json_name = "data"]; + * .google.protobuf.Struct data = 1; */ com.google.protobuf.StructOrBuilder getDataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java index afc374be5..3f3fcc441 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -52,7 +52,7 @@ private DeleteTaskPushNotificationConfigRequest() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ @java.lang.Override @@ -73,7 +73,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ @java.lang.Override @@ -391,7 +391,7 @@ public Builder mergeFrom( * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ public java.lang.String getName() { @@ -411,7 +411,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -432,7 +432,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The name to set. * @return This builder for chaining. */ @@ -449,7 +449,7 @@ public Builder setName( * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return This builder for chaining. */ public Builder clearName() { @@ -463,7 +463,7 @@ public Builder clearName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java index c3c743692..a1ad16ac0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java @@ -15,7 +15,7 @@ public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ java.lang.String getName(); @@ -24,7 +24,7 @@ public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java index fa85fef4a..ab117dd38 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java @@ -95,14 +95,14 @@ public int getNumber() { public static final int FILE_WITH_URI_FIELD_NUMBER = 1; /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return Whether the fileWithUri field is set. */ public boolean hasFileWithUri() { return fileCase_ == 1; } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return The fileWithUri. */ public java.lang.String getFileWithUri() { @@ -123,7 +123,7 @@ public java.lang.String getFileWithUri() { } } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return The bytes for fileWithUri. */ public com.google.protobuf.ByteString @@ -147,7 +147,7 @@ public java.lang.String getFileWithUri() { public static final int FILE_WITH_BYTES_FIELD_NUMBER = 2; /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return Whether the fileWithBytes field is set. */ @java.lang.Override @@ -155,7 +155,7 @@ public boolean hasFileWithBytes() { return fileCase_ == 2; } /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return The fileWithBytes. */ @java.lang.Override @@ -170,7 +170,7 @@ public com.google.protobuf.ByteString getFileWithBytes() { @SuppressWarnings("serial") private volatile java.lang.Object mimeType_ = ""; /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return The mimeType. */ @java.lang.Override @@ -187,7 +187,7 @@ public java.lang.String getMimeType() { } } /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return The bytes for mimeType. */ @java.lang.Override @@ -596,7 +596,7 @@ public Builder clearFile() { private int bitField0_; /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return Whether the fileWithUri field is set. */ @java.lang.Override @@ -604,7 +604,7 @@ public boolean hasFileWithUri() { return fileCase_ == 1; } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return The fileWithUri. */ @java.lang.Override @@ -626,7 +626,7 @@ public java.lang.String getFileWithUri() { } } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return The bytes for fileWithUri. */ @java.lang.Override @@ -649,7 +649,7 @@ public java.lang.String getFileWithUri() { } } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @param value The fileWithUri to set. * @return This builder for chaining. */ @@ -662,7 +662,7 @@ public Builder setFileWithUri( return this; } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return This builder for chaining. */ public Builder clearFileWithUri() { @@ -674,7 +674,7 @@ public Builder clearFileWithUri() { return this; } /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @param value The bytes for fileWithUri to set. * @return This builder for chaining. */ @@ -689,14 +689,14 @@ public Builder setFileWithUriBytes( } /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return Whether the fileWithBytes field is set. */ public boolean hasFileWithBytes() { return fileCase_ == 2; } /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return The fileWithBytes. */ public com.google.protobuf.ByteString getFileWithBytes() { @@ -706,7 +706,7 @@ public com.google.protobuf.ByteString getFileWithBytes() { return com.google.protobuf.ByteString.EMPTY; } /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @param value The fileWithBytes to set. * @return This builder for chaining. */ @@ -718,7 +718,7 @@ public Builder setFileWithBytes(com.google.protobuf.ByteString value) { return this; } /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return This builder for chaining. */ public Builder clearFileWithBytes() { @@ -732,7 +732,7 @@ public Builder clearFileWithBytes() { private java.lang.Object mimeType_ = ""; /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return The mimeType. */ public java.lang.String getMimeType() { @@ -748,7 +748,7 @@ public java.lang.String getMimeType() { } } /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return The bytes for mimeType. */ public com.google.protobuf.ByteString @@ -765,7 +765,7 @@ public java.lang.String getMimeType() { } } /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @param value The mimeType to set. * @return This builder for chaining. */ @@ -778,7 +778,7 @@ public Builder setMimeType( return this; } /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return This builder for chaining. */ public Builder clearMimeType() { @@ -788,7 +788,7 @@ public Builder clearMimeType() { return this; } /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @param value The bytes for mimeType to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java index d902f7a78..3f53232b3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java @@ -11,40 +11,40 @@ public interface FilePartOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return Whether the fileWithUri field is set. */ boolean hasFileWithUri(); /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return The fileWithUri. */ java.lang.String getFileWithUri(); /** - * string file_with_uri = 1 [json_name = "fileWithUri"]; + * string file_with_uri = 1; * @return The bytes for fileWithUri. */ com.google.protobuf.ByteString getFileWithUriBytes(); /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return Whether the fileWithBytes field is set. */ boolean hasFileWithBytes(); /** - * bytes file_with_bytes = 2 [json_name = "fileWithBytes"]; + * bytes file_with_bytes = 2; * @return The fileWithBytes. */ com.google.protobuf.ByteString getFileWithBytes(); /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return The mimeType. */ java.lang.String getMimeType(); /** - * string mime_type = 3 [json_name = "mimeType"]; + * string mime_type = 3; * @return The bytes for mimeType. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java index f0720170c..f828d56cd 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java @@ -52,7 +52,7 @@ private GetTaskPushNotificationConfigRequest() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ @java.lang.Override @@ -73,7 +73,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ @java.lang.Override @@ -391,7 +391,7 @@ public Builder mergeFrom( * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ public java.lang.String getName() { @@ -411,7 +411,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -432,7 +432,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The name to set. * @return This builder for chaining. */ @@ -449,7 +449,7 @@ public Builder setName( * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return This builder for chaining. */ public Builder clearName() { @@ -463,7 +463,7 @@ public Builder clearName() { * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java index 7502f36a7..420bdfdb9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java @@ -15,7 +15,7 @@ public interface GetTaskPushNotificationConfigRequestOrBuilder extends * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ java.lang.String getName(); @@ -24,7 +24,7 @@ public interface GetTaskPushNotificationConfigRequestOrBuilder extends * name=tasks/{id}/pushNotificationConfigs/{push_id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java index 08c0515fa..717b8afc6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -52,7 +52,7 @@ private GetTaskRequest() { * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ @java.lang.Override @@ -73,7 +73,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ @java.lang.Override @@ -94,7 +94,7 @@ public java.lang.String getName() { public static final int HISTORY_LENGTH_FIELD_NUMBER = 2; private int historyLength_ = 0; /** - * int32 history_length = 2 [json_name = "historyLength"]; + * int32 history_length = 2; * @return The historyLength. */ @java.lang.Override @@ -425,7 +425,7 @@ public Builder mergeFrom( * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ public java.lang.String getName() { @@ -445,7 +445,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -466,7 +466,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The name to set. * @return This builder for chaining. */ @@ -483,7 +483,7 @@ public Builder setName( * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearName() { @@ -497,7 +497,7 @@ public Builder clearName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -513,7 +513,7 @@ public Builder setNameBytes( private int historyLength_ ; /** - * int32 history_length = 2 [json_name = "historyLength"]; + * int32 history_length = 2; * @return The historyLength. */ @java.lang.Override @@ -521,7 +521,7 @@ public int getHistoryLength() { return historyLength_; } /** - * int32 history_length = 2 [json_name = "historyLength"]; + * int32 history_length = 2; * @param value The historyLength to set. * @return This builder for chaining. */ @@ -533,7 +533,7 @@ public Builder setHistoryLength(int value) { return this; } /** - * int32 history_length = 2 [json_name = "historyLength"]; + * int32 history_length = 2; * @return This builder for chaining. */ public Builder clearHistoryLength() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java index 0f55b196b..4e686dc47 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java @@ -15,7 +15,7 @@ public interface GetTaskRequestOrBuilder extends * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ java.lang.String getName(); @@ -24,14 +24,14 @@ public interface GetTaskRequestOrBuilder extends * name=tasks/{id} * * - * string name = 1 [json_name = "name", (.google.api.field_behavior) = REQUIRED]; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ com.google.protobuf.ByteString getNameBytes(); /** - * int32 history_length = 2 [json_name = "historyLength"]; + * int32 history_length = 2; * @return The historyLength. */ int getHistoryLength(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java index b6a093b85..2c612d7f9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java @@ -54,7 +54,7 @@ private HTTPAuthSecurityScheme() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ @java.lang.Override @@ -75,7 +75,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ @java.lang.Override @@ -104,7 +104,7 @@ public java.lang.String getDescription() { * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return The scheme. */ @java.lang.Override @@ -128,7 +128,7 @@ public java.lang.String getScheme() { * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return The bytes for scheme. */ @java.lang.Override @@ -156,7 +156,7 @@ public java.lang.String getScheme() { * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return The bearerFormat. */ @java.lang.Override @@ -179,7 +179,7 @@ public java.lang.String getBearerFormat() { * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return The bytes for bearerFormat. */ @java.lang.Override @@ -545,7 +545,7 @@ public Builder mergeFrom( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ public java.lang.String getDescription() { @@ -565,7 +565,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -586,7 +586,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The description to set. * @return This builder for chaining. */ @@ -603,7 +603,7 @@ public Builder setDescription( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return This builder for chaining. */ public Builder clearDescription() { @@ -617,7 +617,7 @@ public Builder clearDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -640,7 +640,7 @@ public Builder setDescriptionBytes( * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return The scheme. */ public java.lang.String getScheme() { @@ -663,7 +663,7 @@ public java.lang.String getScheme() { * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return The bytes for scheme. */ public com.google.protobuf.ByteString @@ -687,7 +687,7 @@ public java.lang.String getScheme() { * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @param value The scheme to set. * @return This builder for chaining. */ @@ -707,7 +707,7 @@ public Builder setScheme( * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return This builder for chaining. */ public Builder clearScheme() { @@ -724,7 +724,7 @@ public Builder clearScheme() { * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @param value The bytes for scheme to set. * @return This builder for chaining. */ @@ -746,7 +746,7 @@ public Builder setSchemeBytes( * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return The bearerFormat. */ public java.lang.String getBearerFormat() { @@ -768,7 +768,7 @@ public java.lang.String getBearerFormat() { * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return The bytes for bearerFormat. */ public com.google.protobuf.ByteString @@ -791,7 +791,7 @@ public java.lang.String getBearerFormat() { * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @param value The bearerFormat to set. * @return This builder for chaining. */ @@ -810,7 +810,7 @@ public Builder setBearerFormat( * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return This builder for chaining. */ public Builder clearBearerFormat() { @@ -826,7 +826,7 @@ public Builder clearBearerFormat() { * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @param value The bytes for bearerFormat to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java index 90afd8f91..b4e9b4628 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java @@ -15,7 +15,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ java.lang.String getDescription(); @@ -24,7 +24,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return The scheme. */ java.lang.String getScheme(); @@ -50,7 +50,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends * The value is case-insensitive, as defined in RFC7235. * * - * string scheme = 2 [json_name = "scheme"]; + * string scheme = 2; * @return The bytes for scheme. */ com.google.protobuf.ByteString @@ -63,7 +63,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return The bearerFormat. */ java.lang.String getBearerFormat(); @@ -74,7 +74,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends * this information is primarily for documentation purposes. * * - * string bearer_format = 3 [json_name = "bearerFormat"]; + * string bearer_format = 3; * @return The bytes for bearerFormat. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java index 450e158a9..7593dde41 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java @@ -66,7 +66,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The authorizationUrl. */ @java.lang.Override @@ -88,7 +88,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The bytes for authorizationUrl. */ @java.lang.Override @@ -115,7 +115,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ @java.lang.Override @@ -137,7 +137,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ @java.lang.Override @@ -187,7 +187,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public boolean containsScopes( @@ -209,7 +209,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -221,7 +221,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public /* nullable */ @@ -240,7 +240,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -640,7 +640,7 @@ public Builder mergeFrom( * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The authorizationUrl. */ public java.lang.String getAuthorizationUrl() { @@ -661,7 +661,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The bytes for authorizationUrl. */ public com.google.protobuf.ByteString @@ -683,7 +683,7 @@ public java.lang.String getAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @param value The authorizationUrl to set. * @return This builder for chaining. */ @@ -701,7 +701,7 @@ public Builder setAuthorizationUrl( * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return This builder for chaining. */ public Builder clearAuthorizationUrl() { @@ -716,7 +716,7 @@ public Builder clearAuthorizationUrl() { * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @param value The bytes for authorizationUrl to set. * @return This builder for chaining. */ @@ -737,7 +737,7 @@ public Builder setAuthorizationUrlBytes( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ public java.lang.String getRefreshUrl() { @@ -758,7 +758,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ public com.google.protobuf.ByteString @@ -780,7 +780,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @param value The refreshUrl to set. * @return This builder for chaining. */ @@ -798,7 +798,7 @@ public Builder setRefreshUrl( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return This builder for chaining. */ public Builder clearRefreshUrl() { @@ -813,7 +813,7 @@ public Builder clearRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @param value The bytes for refreshUrl to set. * @return This builder for chaining. */ @@ -859,7 +859,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public boolean containsScopes( @@ -881,7 +881,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -893,7 +893,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public /* nullable */ @@ -912,7 +912,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -937,7 +937,7 @@ public Builder clearScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder removeScopes( java.lang.String key) { @@ -961,7 +961,7 @@ public Builder removeScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder putScopes( java.lang.String key, @@ -979,7 +979,7 @@ public Builder putScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java index 78616d7aa..9ff012f6e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java @@ -16,7 +16,7 @@ public interface ImplicitOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The authorizationUrl. */ java.lang.String getAuthorizationUrl(); @@ -26,7 +26,7 @@ public interface ImplicitOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS * * - * string authorization_url = 1 [json_name = "authorizationUrl"]; + * string authorization_url = 1; * @return The bytes for authorizationUrl. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface ImplicitOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ java.lang.String getRefreshUrl(); @@ -48,7 +48,7 @@ public interface ImplicitOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ com.google.protobuf.ByteString @@ -60,7 +60,7 @@ public interface ImplicitOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ int getScopesCount(); /** @@ -69,7 +69,7 @@ public interface ImplicitOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ boolean containsScopes( java.lang.String key); @@ -85,7 +85,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ java.util.Map getScopesMap(); @@ -95,7 +95,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -108,7 +108,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java index 8ed2fc1a7..6563baeb8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java @@ -53,7 +53,7 @@ private ListTaskPushNotificationConfigRequest() { * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return The parent. */ @java.lang.Override @@ -74,7 +74,7 @@ public java.lang.String getParent() { * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return The bytes for parent. */ @java.lang.Override @@ -101,7 +101,7 @@ public java.lang.String getParent() { * If unspecified, all configs will be returned. * * - * int32 page_size = 2 [json_name = "pageSize"]; + * int32 page_size = 2; * @return The pageSize. */ @java.lang.Override @@ -122,7 +122,7 @@ public int getPageSize() { * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return The pageToken. */ @java.lang.Override @@ -148,7 +148,7 @@ public java.lang.String getPageToken() { * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return The bytes for pageToken. */ @java.lang.Override @@ -513,7 +513,7 @@ public Builder mergeFrom( * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return The parent. */ public java.lang.String getParent() { @@ -533,7 +533,7 @@ public java.lang.String getParent() { * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return The bytes for parent. */ public com.google.protobuf.ByteString @@ -554,7 +554,7 @@ public java.lang.String getParent() { * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @param value The parent to set. * @return This builder for chaining. */ @@ -571,7 +571,7 @@ public Builder setParent( * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return This builder for chaining. */ public Builder clearParent() { @@ -585,7 +585,7 @@ public Builder clearParent() { * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @param value The bytes for parent to set. * @return This builder for chaining. */ @@ -607,7 +607,7 @@ public Builder setParentBytes( * If unspecified, all configs will be returned. * * - * int32 page_size = 2 [json_name = "pageSize"]; + * int32 page_size = 2; * @return The pageSize. */ @java.lang.Override @@ -621,7 +621,7 @@ public int getPageSize() { * If unspecified, all configs will be returned. * * - * int32 page_size = 2 [json_name = "pageSize"]; + * int32 page_size = 2; * @param value The pageSize to set. * @return This builder for chaining. */ @@ -639,7 +639,7 @@ public Builder setPageSize(int value) { * If unspecified, all configs will be returned. * * - * int32 page_size = 2 [json_name = "pageSize"]; + * int32 page_size = 2; * @return This builder for chaining. */ public Builder clearPageSize() { @@ -660,7 +660,7 @@ public Builder clearPageSize() { * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return The pageToken. */ public java.lang.String getPageToken() { @@ -685,7 +685,7 @@ public java.lang.String getPageToken() { * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return The bytes for pageToken. */ public com.google.protobuf.ByteString @@ -711,7 +711,7 @@ public java.lang.String getPageToken() { * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @param value The pageToken to set. * @return This builder for chaining. */ @@ -733,7 +733,7 @@ public Builder setPageToken( * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return This builder for chaining. */ public Builder clearPageToken() { @@ -752,7 +752,7 @@ public Builder clearPageToken() { * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @param value The bytes for pageToken to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java index 1e6356045..77f60b630 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java @@ -15,7 +15,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return The parent. */ java.lang.String getParent(); @@ -24,7 +24,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends * parent=tasks/{id} * * - * string parent = 1 [json_name = "parent"]; + * string parent = 1; * @return The bytes for parent. */ com.google.protobuf.ByteString @@ -37,7 +37,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends * If unspecified, all configs will be returned. * * - * int32 page_size = 2 [json_name = "pageSize"]; + * int32 page_size = 2; * @return The pageSize. */ int getPageSize(); @@ -52,7 +52,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return The pageToken. */ java.lang.String getPageToken(); @@ -66,7 +66,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends * the page token. * * - * string page_token = 3 [json_name = "pageToken"]; + * string page_token = 3; * @return The bytes for pageToken. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java index beb535430..8c22e9f5e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java @@ -49,14 +49,14 @@ private ListTaskPushNotificationConfigResponse() { @SuppressWarnings("serial") private java.util.List configs_; /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override public java.util.List getConfigsList() { return configs_; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override public java.util.List @@ -64,21 +64,21 @@ public java.util.List getConfigsList() { return configs_; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override public int getConfigsCount() { return configs_.size(); } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { return configs_.get(index); } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( @@ -95,7 +95,7 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return The nextPageToken. */ @java.lang.Override @@ -117,7 +117,7 @@ public java.lang.String getNextPageToken() { * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return The bytes for nextPageToken. */ @java.lang.Override @@ -514,7 +514,7 @@ private void ensureConfigsIsMutable() { io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configsBuilder_; /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public java.util.List getConfigsList() { if (configsBuilder_ == null) { @@ -524,7 +524,7 @@ public java.util.List getConfigsList() { } } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public int getConfigsCount() { if (configsBuilder_ == null) { @@ -534,7 +534,7 @@ public int getConfigsCount() { } } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { if (configsBuilder_ == null) { @@ -544,7 +544,7 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { } } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder setConfigs( int index, io.a2a.grpc.TaskPushNotificationConfig value) { @@ -561,7 +561,7 @@ public Builder setConfigs( return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder setConfigs( int index, io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { @@ -575,7 +575,7 @@ public Builder setConfigs( return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs(io.a2a.grpc.TaskPushNotificationConfig value) { if (configsBuilder_ == null) { @@ -591,7 +591,7 @@ public Builder addConfigs(io.a2a.grpc.TaskPushNotificationConfig value) { return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs( int index, io.a2a.grpc.TaskPushNotificationConfig value) { @@ -608,7 +608,7 @@ public Builder addConfigs( return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs( io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { @@ -622,7 +622,7 @@ public Builder addConfigs( return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs( int index, io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { @@ -636,7 +636,7 @@ public Builder addConfigs( return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addAllConfigs( java.lang.Iterable values) { @@ -651,7 +651,7 @@ public Builder addAllConfigs( return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder clearConfigs() { if (configsBuilder_ == null) { @@ -664,7 +664,7 @@ public Builder clearConfigs() { return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder removeConfigs(int index) { if (configsBuilder_ == null) { @@ -677,14 +677,14 @@ public Builder removeConfigs(int index) { return this; } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigsBuilder( int index) { return internalGetConfigsFieldBuilder().getBuilder(index); } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( int index) { @@ -694,7 +694,7 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( } } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public java.util.List getConfigsOrBuilderList() { @@ -705,14 +705,14 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( } } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder() { return internalGetConfigsFieldBuilder().addBuilder( io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()); } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder( int index) { @@ -720,7 +720,7 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder( index, io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()); } /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public java.util.List getConfigsBuilderList() { @@ -748,7 +748,7 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder( * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return The nextPageToken. */ public java.lang.String getNextPageToken() { @@ -769,7 +769,7 @@ public java.lang.String getNextPageToken() { * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return The bytes for nextPageToken. */ public com.google.protobuf.ByteString @@ -791,7 +791,7 @@ public java.lang.String getNextPageToken() { * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @param value The nextPageToken to set. * @return This builder for chaining. */ @@ -809,7 +809,7 @@ public Builder setNextPageToken( * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return This builder for chaining. */ public Builder clearNextPageToken() { @@ -824,7 +824,7 @@ public Builder clearNextPageToken() { * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @param value The bytes for nextPageToken to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java index 211aeee5a..e23fa8182 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java @@ -11,25 +11,25 @@ public interface ListTaskPushNotificationConfigResponseOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ java.util.List getConfigsList(); /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index); /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ int getConfigsCount(); /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ java.util.List getConfigsOrBuilderList(); /** - * repeated .a2a.v1.TaskPushNotificationConfig configs = 1 [json_name = "configs"]; + * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( int index); @@ -40,7 +40,7 @@ io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return The nextPageToken. */ java.lang.String getNextPageToken(); @@ -50,7 +50,7 @@ io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( * If this field is omitted, there are no subsequent pages. * * - * string next_page_token = 2 [json_name = "nextPageToken"]; + * string next_page_token = 2; * @return The bytes for nextPageToken. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Message.java b/spec-grpc/src/main/java/io/a2a/grpc/Message.java index cddcf43e5..b4b63ecb1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Message.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Message.java @@ -70,7 +70,7 @@ private Message() { * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return The messageId. */ @java.lang.Override @@ -92,7 +92,7 @@ public java.lang.String getMessageId() { * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return The bytes for messageId. */ @java.lang.Override @@ -119,7 +119,7 @@ public java.lang.String getMessageId() { * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ @java.lang.Override @@ -141,7 +141,7 @@ public java.lang.String getContextId() { * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ @java.lang.Override @@ -168,7 +168,7 @@ public java.lang.String getContextId() { * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return The taskId. */ @java.lang.Override @@ -190,7 +190,7 @@ public java.lang.String getTaskId() { * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return The bytes for taskId. */ @java.lang.Override @@ -215,7 +215,7 @@ public java.lang.String getTaskId() { * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return The enum numeric value on the wire for role. */ @java.lang.Override public int getRoleValue() { @@ -226,7 +226,7 @@ public java.lang.String getTaskId() { * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return The role. */ @java.lang.Override public io.a2a.grpc.Role getRole() { @@ -243,7 +243,7 @@ public java.lang.String getTaskId() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ @java.lang.Override public java.util.List getContentList() { @@ -255,7 +255,7 @@ public java.util.List getContentList() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ @java.lang.Override public java.util.List @@ -268,7 +268,7 @@ public java.util.List getContentList() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ @java.lang.Override public int getContentCount() { @@ -280,7 +280,7 @@ public int getContentCount() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ @java.lang.Override public io.a2a.grpc.Part getContent(int index) { @@ -292,7 +292,7 @@ public io.a2a.grpc.Part getContent(int index) { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ @java.lang.Override public io.a2a.grpc.PartOrBuilder getContentOrBuilder( @@ -308,7 +308,7 @@ public io.a2a.grpc.PartOrBuilder getContentOrBuilder( * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ @java.lang.Override @@ -321,7 +321,7 @@ public boolean hasMetadata() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ @java.lang.Override @@ -334,7 +334,7 @@ public com.google.protobuf.Struct getMetadata() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -350,7 +350,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return A list containing the extensions. */ public com.google.protobuf.ProtocolStringList @@ -362,7 +362,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return The count of extensions. */ public int getExtensionsCount() { @@ -373,7 +373,7 @@ public int getExtensionsCount() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the element to return. * @return The extensions at the given index. */ @@ -385,7 +385,7 @@ public java.lang.String getExtensions(int index) { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the value to return. * @return The bytes of the extensions at the given index. */ @@ -932,7 +932,7 @@ public Builder mergeFrom( * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return The messageId. */ public java.lang.String getMessageId() { @@ -953,7 +953,7 @@ public java.lang.String getMessageId() { * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return The bytes for messageId. */ public com.google.protobuf.ByteString @@ -975,7 +975,7 @@ public java.lang.String getMessageId() { * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @param value The messageId to set. * @return This builder for chaining. */ @@ -993,7 +993,7 @@ public Builder setMessageId( * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return This builder for chaining. */ public Builder clearMessageId() { @@ -1008,7 +1008,7 @@ public Builder clearMessageId() { * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @param value The bytes for messageId to set. * @return This builder for chaining. */ @@ -1029,7 +1029,7 @@ public Builder setMessageIdBytes( * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ public java.lang.String getContextId() { @@ -1050,7 +1050,7 @@ public java.lang.String getContextId() { * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -1072,7 +1072,7 @@ public java.lang.String getContextId() { * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The contextId to set. * @return This builder for chaining. */ @@ -1090,7 +1090,7 @@ public Builder setContextId( * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return This builder for chaining. */ public Builder clearContextId() { @@ -1105,7 +1105,7 @@ public Builder clearContextId() { * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -1126,7 +1126,7 @@ public Builder setContextIdBytes( * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return The taskId. */ public java.lang.String getTaskId() { @@ -1147,7 +1147,7 @@ public java.lang.String getTaskId() { * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return The bytes for taskId. */ public com.google.protobuf.ByteString @@ -1169,7 +1169,7 @@ public java.lang.String getTaskId() { * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @param value The taskId to set. * @return This builder for chaining. */ @@ -1187,7 +1187,7 @@ public Builder setTaskId( * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return This builder for chaining. */ public Builder clearTaskId() { @@ -1202,7 +1202,7 @@ public Builder clearTaskId() { * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @param value The bytes for taskId to set. * @return This builder for chaining. */ @@ -1222,7 +1222,7 @@ public Builder setTaskIdBytes( * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return The enum numeric value on the wire for role. */ @java.lang.Override public int getRoleValue() { @@ -1233,7 +1233,7 @@ public Builder setTaskIdBytes( * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @param value The enum numeric value on the wire for role to set. * @return This builder for chaining. */ @@ -1248,7 +1248,7 @@ public Builder setRoleValue(int value) { * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return The role. */ @java.lang.Override @@ -1261,7 +1261,7 @@ public io.a2a.grpc.Role getRole() { * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @param value The role to set. * @return This builder for chaining. */ @@ -1277,7 +1277,7 @@ public Builder setRole(io.a2a.grpc.Role value) { * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return This builder for chaining. */ public Builder clearRole() { @@ -1305,7 +1305,7 @@ private void ensureContentIsMutable() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public java.util.List getContentList() { if (contentBuilder_ == null) { @@ -1320,7 +1320,7 @@ public java.util.List getContentList() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public int getContentCount() { if (contentBuilder_ == null) { @@ -1335,7 +1335,7 @@ public int getContentCount() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public io.a2a.grpc.Part getContent(int index) { if (contentBuilder_ == null) { @@ -1350,7 +1350,7 @@ public io.a2a.grpc.Part getContent(int index) { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder setContent( int index, io.a2a.grpc.Part value) { @@ -1372,7 +1372,7 @@ public Builder setContent( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder setContent( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1391,7 +1391,7 @@ public Builder setContent( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder addContent(io.a2a.grpc.Part value) { if (contentBuilder_ == null) { @@ -1412,7 +1412,7 @@ public Builder addContent(io.a2a.grpc.Part value) { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder addContent( int index, io.a2a.grpc.Part value) { @@ -1434,7 +1434,7 @@ public Builder addContent( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder addContent( io.a2a.grpc.Part.Builder builderForValue) { @@ -1453,7 +1453,7 @@ public Builder addContent( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder addContent( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1472,7 +1472,7 @@ public Builder addContent( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder addAllContent( java.lang.Iterable values) { @@ -1492,7 +1492,7 @@ public Builder addAllContent( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder clearContent() { if (contentBuilder_ == null) { @@ -1510,7 +1510,7 @@ public Builder clearContent() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public Builder removeContent(int index) { if (contentBuilder_ == null) { @@ -1528,7 +1528,7 @@ public Builder removeContent(int index) { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public io.a2a.grpc.Part.Builder getContentBuilder( int index) { @@ -1540,7 +1540,7 @@ public io.a2a.grpc.Part.Builder getContentBuilder( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public io.a2a.grpc.PartOrBuilder getContentOrBuilder( int index) { @@ -1555,7 +1555,7 @@ public io.a2a.grpc.PartOrBuilder getContentOrBuilder( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public java.util.List getContentOrBuilderList() { @@ -1571,7 +1571,7 @@ public io.a2a.grpc.PartOrBuilder getContentOrBuilder( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public io.a2a.grpc.Part.Builder addContentBuilder() { return internalGetContentFieldBuilder().addBuilder( @@ -1583,7 +1583,7 @@ public io.a2a.grpc.Part.Builder addContentBuilder() { * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public io.a2a.grpc.Part.Builder addContentBuilder( int index) { @@ -1596,7 +1596,7 @@ public io.a2a.grpc.Part.Builder addContentBuilder( * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ public java.util.List getContentBuilderList() { @@ -1626,7 +1626,7 @@ public io.a2a.grpc.Part.Builder addContentBuilder( * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ public boolean hasMetadata() { @@ -1638,7 +1638,7 @@ public boolean hasMetadata() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -1654,7 +1654,7 @@ public com.google.protobuf.Struct getMetadata() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1675,7 +1675,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -1694,7 +1694,7 @@ public Builder setMetadata( * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1720,7 +1720,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000020); @@ -1738,7 +1738,7 @@ public Builder clearMetadata() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { bitField0_ |= 0x00000020; @@ -1751,7 +1751,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -1767,7 +1767,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> @@ -1796,7 +1796,7 @@ private void ensureExtensionsIsMutable() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return A list containing the extensions. */ public com.google.protobuf.ProtocolStringList @@ -1809,7 +1809,7 @@ private void ensureExtensionsIsMutable() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return The count of extensions. */ public int getExtensionsCount() { @@ -1820,7 +1820,7 @@ public int getExtensionsCount() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the element to return. * @return The extensions at the given index. */ @@ -1832,7 +1832,7 @@ public java.lang.String getExtensions(int index) { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the value to return. * @return The bytes of the extensions at the given index. */ @@ -1845,7 +1845,7 @@ public java.lang.String getExtensions(int index) { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index to set the value at. * @param value The extensions to set. * @return This builder for chaining. @@ -1864,7 +1864,7 @@ public Builder setExtensions( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param value The extensions to add. * @return This builder for chaining. */ @@ -1882,7 +1882,7 @@ public Builder addExtensions( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param values The extensions to add. * @return This builder for chaining. */ @@ -1900,7 +1900,7 @@ public Builder addAllExtensions( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return This builder for chaining. */ public Builder clearExtensions() { @@ -1915,7 +1915,7 @@ public Builder clearExtensions() { * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param value The bytes of the extensions to add. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java index 94c1947b2..81727886b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java @@ -16,7 +16,7 @@ public interface MessageOrBuilder extends * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return The messageId. */ java.lang.String getMessageId(); @@ -26,7 +26,7 @@ public interface MessageOrBuilder extends * message creator. * * - * string message_id = 1 [json_name = "messageId"]; + * string message_id = 1; * @return The bytes for messageId. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface MessageOrBuilder extends * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ java.lang.String getContextId(); @@ -48,7 +48,7 @@ public interface MessageOrBuilder extends * will be associated with the given context. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -60,7 +60,7 @@ public interface MessageOrBuilder extends * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return The taskId. */ java.lang.String getTaskId(); @@ -70,7 +70,7 @@ public interface MessageOrBuilder extends * will be associated with the given task. * * - * string task_id = 3 [json_name = "taskId"]; + * string task_id = 3; * @return The bytes for taskId. */ com.google.protobuf.ByteString @@ -81,7 +81,7 @@ public interface MessageOrBuilder extends * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return The enum numeric value on the wire for role. */ int getRoleValue(); @@ -90,7 +90,7 @@ public interface MessageOrBuilder extends * A role for the message. * * - * .a2a.v1.Role role = 4 [json_name = "role"]; + * .a2a.v1.Role role = 4; * @return The role. */ io.a2a.grpc.Role getRole(); @@ -101,7 +101,7 @@ public interface MessageOrBuilder extends * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ java.util.List getContentList(); @@ -111,7 +111,7 @@ public interface MessageOrBuilder extends * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ io.a2a.grpc.Part getContent(int index); /** @@ -120,7 +120,7 @@ public interface MessageOrBuilder extends * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ int getContentCount(); /** @@ -129,7 +129,7 @@ public interface MessageOrBuilder extends * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ java.util.List getContentOrBuilderList(); @@ -139,7 +139,7 @@ public interface MessageOrBuilder extends * Content is the container of the message content. * * - * repeated .a2a.v1.Part content = 5 [json_name = "content"]; + * repeated .a2a.v1.Part content = 5; */ io.a2a.grpc.PartOrBuilder getContentOrBuilder( int index); @@ -150,7 +150,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ boolean hasMetadata(); @@ -160,7 +160,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); @@ -170,7 +170,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * Any optional metadata to provide along with the message. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); @@ -179,7 +179,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return A list containing the extensions. */ java.util.List @@ -189,7 +189,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @return The count of extensions. */ int getExtensionsCount(); @@ -198,7 +198,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the element to return. * @return The extensions at the given index. */ @@ -208,7 +208,7 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( * The URIs of extensions that are present or contributed to this Message. * * - * repeated string extensions = 7 [json_name = "extensions"]; + * repeated string extensions = 7; * @param index The index of the value to return. * @return The bytes of the extensions at the given index. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java index 22a91b84a..11065d682 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java @@ -53,7 +53,7 @@ private OAuth2SecurityScheme() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ @java.lang.Override @@ -74,7 +74,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ @java.lang.Override @@ -99,7 +99,7 @@ public java.lang.String getDescription() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; * @return Whether the flows field is set. */ @java.lang.Override @@ -111,7 +111,7 @@ public boolean hasFlows() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; * @return The flows. */ @java.lang.Override @@ -123,7 +123,7 @@ public io.a2a.grpc.OAuthFlows getFlows() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ @java.lang.Override public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { @@ -475,7 +475,7 @@ public Builder mergeFrom( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ public java.lang.String getDescription() { @@ -495,7 +495,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -516,7 +516,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The description to set. * @return This builder for chaining. */ @@ -533,7 +533,7 @@ public Builder setDescription( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return This builder for chaining. */ public Builder clearDescription() { @@ -547,7 +547,7 @@ public Builder clearDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -569,7 +569,7 @@ public Builder setDescriptionBytes( * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; * @return Whether the flows field is set. */ public boolean hasFlows() { @@ -580,7 +580,7 @@ public boolean hasFlows() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; * @return The flows. */ public io.a2a.grpc.OAuthFlows getFlows() { @@ -595,7 +595,7 @@ public io.a2a.grpc.OAuthFlows getFlows() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ public Builder setFlows(io.a2a.grpc.OAuthFlows value) { if (flowsBuilder_ == null) { @@ -615,7 +615,7 @@ public Builder setFlows(io.a2a.grpc.OAuthFlows value) { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ public Builder setFlows( io.a2a.grpc.OAuthFlows.Builder builderForValue) { @@ -633,7 +633,7 @@ public Builder setFlows( * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ public Builder mergeFlows(io.a2a.grpc.OAuthFlows value) { if (flowsBuilder_ == null) { @@ -658,7 +658,7 @@ public Builder mergeFlows(io.a2a.grpc.OAuthFlows value) { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ public Builder clearFlows() { bitField0_ = (bitField0_ & ~0x00000002); @@ -675,7 +675,7 @@ public Builder clearFlows() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ public io.a2a.grpc.OAuthFlows.Builder getFlowsBuilder() { bitField0_ |= 0x00000002; @@ -687,7 +687,7 @@ public io.a2a.grpc.OAuthFlows.Builder getFlowsBuilder() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { if (flowsBuilder_ != null) { @@ -702,7 +702,7 @@ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OAuthFlows, io.a2a.grpc.OAuthFlows.Builder, io.a2a.grpc.OAuthFlowsOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java index 40048a192..76620c1ba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java @@ -15,7 +15,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ java.lang.String getDescription(); @@ -24,7 +24,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; * @return Whether the flows field is set. */ boolean hasFlows(); @@ -44,7 +44,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; * @return The flows. */ io.a2a.grpc.OAuthFlows getFlows(); @@ -53,7 +53,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends * An object containing configuration information for the flow types supported * * - * .a2a.v1.OAuthFlows flows = 2 [json_name = "flows"]; + * .a2a.v1.OAuthFlows flows = 2; */ io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java index 5d5927d17..4da07493d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java @@ -91,7 +91,7 @@ public int getNumber() { public static final int AUTHORIZATION_CODE_FIELD_NUMBER = 1; /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return Whether the authorizationCode field is set. */ @java.lang.Override @@ -99,7 +99,7 @@ public boolean hasAuthorizationCode() { return flowCase_ == 1; } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return The authorizationCode. */ @java.lang.Override @@ -110,7 +110,7 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode() { return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ @java.lang.Override public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder() { @@ -122,7 +122,7 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBui public static final int CLIENT_CREDENTIALS_FIELD_NUMBER = 2; /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return Whether the clientCredentials field is set. */ @java.lang.Override @@ -130,7 +130,7 @@ public boolean hasClientCredentials() { return flowCase_ == 2; } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return The clientCredentials. */ @java.lang.Override @@ -141,7 +141,7 @@ public io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials() { return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ @java.lang.Override public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder() { @@ -153,7 +153,7 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui public static final int IMPLICIT_FIELD_NUMBER = 3; /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return Whether the implicit field is set. */ @java.lang.Override @@ -161,7 +161,7 @@ public boolean hasImplicit() { return flowCase_ == 3; } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return The implicit. */ @java.lang.Override @@ -172,7 +172,7 @@ public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ @java.lang.Override public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { @@ -184,7 +184,7 @@ public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { public static final int PASSWORD_FIELD_NUMBER = 4; /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; * @return Whether the password field is set. */ @java.lang.Override @@ -192,7 +192,7 @@ public boolean hasPassword() { return flowCase_ == 4; } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; * @return The password. */ @java.lang.Override @@ -203,7 +203,7 @@ public io.a2a.grpc.PasswordOAuthFlow getPassword() { return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ @java.lang.Override public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { @@ -655,7 +655,7 @@ public Builder clearFlow() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AuthorizationCodeOAuthFlow, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder, io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder> authorizationCodeBuilder_; /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return Whether the authorizationCode field is set. */ @java.lang.Override @@ -663,7 +663,7 @@ public boolean hasAuthorizationCode() { return flowCase_ == 1; } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return The authorizationCode. */ @java.lang.Override @@ -681,7 +681,7 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode() { } } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder setAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value) { if (authorizationCodeBuilder_ == null) { @@ -697,7 +697,7 @@ public Builder setAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value return this; } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder setAuthorizationCode( io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder builderForValue) { @@ -711,7 +711,7 @@ public Builder setAuthorizationCode( return this; } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder mergeAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value) { if (authorizationCodeBuilder_ == null) { @@ -734,7 +734,7 @@ public Builder mergeAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow val return this; } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder clearAuthorizationCode() { if (authorizationCodeBuilder_ == null) { @@ -753,13 +753,13 @@ public Builder clearAuthorizationCode() { return this; } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder getAuthorizationCodeBuilder() { return internalGetAuthorizationCodeFieldBuilder().getBuilder(); } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ @java.lang.Override public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder() { @@ -773,7 +773,7 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBui } } /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AuthorizationCodeOAuthFlow, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder, io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder> @@ -797,7 +797,7 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBui private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.ClientCredentialsOAuthFlow, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder, io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder> clientCredentialsBuilder_; /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return Whether the clientCredentials field is set. */ @java.lang.Override @@ -805,7 +805,7 @@ public boolean hasClientCredentials() { return flowCase_ == 2; } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return The clientCredentials. */ @java.lang.Override @@ -823,7 +823,7 @@ public io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials() { } } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder setClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value) { if (clientCredentialsBuilder_ == null) { @@ -839,7 +839,7 @@ public Builder setClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value return this; } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder setClientCredentials( io.a2a.grpc.ClientCredentialsOAuthFlow.Builder builderForValue) { @@ -853,7 +853,7 @@ public Builder setClientCredentials( return this; } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder mergeClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value) { if (clientCredentialsBuilder_ == null) { @@ -876,7 +876,7 @@ public Builder mergeClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow val return this; } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder clearClientCredentials() { if (clientCredentialsBuilder_ == null) { @@ -895,13 +895,13 @@ public Builder clearClientCredentials() { return this; } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public io.a2a.grpc.ClientCredentialsOAuthFlow.Builder getClientCredentialsBuilder() { return internalGetClientCredentialsFieldBuilder().getBuilder(); } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ @java.lang.Override public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder() { @@ -915,7 +915,7 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui } } /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.ClientCredentialsOAuthFlow, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder, io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder> @@ -939,7 +939,7 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> implicitBuilder_; /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return Whether the implicit field is set. */ @java.lang.Override @@ -947,7 +947,7 @@ public boolean hasImplicit() { return flowCase_ == 3; } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return The implicit. */ @java.lang.Override @@ -965,7 +965,7 @@ public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { } } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { if (implicitBuilder_ == null) { @@ -981,7 +981,7 @@ public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { return this; } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder setImplicit( io.a2a.grpc.ImplicitOAuthFlow.Builder builderForValue) { @@ -995,7 +995,7 @@ public Builder setImplicit( return this; } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { if (implicitBuilder_ == null) { @@ -1018,7 +1018,7 @@ public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { return this; } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder clearImplicit() { if (implicitBuilder_ == null) { @@ -1037,13 +1037,13 @@ public Builder clearImplicit() { return this; } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public io.a2a.grpc.ImplicitOAuthFlow.Builder getImplicitBuilder() { return internalGetImplicitFieldBuilder().getBuilder(); } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ @java.lang.Override public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { @@ -1057,7 +1057,7 @@ public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { } } /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> @@ -1081,7 +1081,7 @@ public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> passwordBuilder_; /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; * @return Whether the password field is set. */ @java.lang.Override @@ -1089,7 +1089,7 @@ public boolean hasPassword() { return flowCase_ == 4; } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; * @return The password. */ @java.lang.Override @@ -1107,7 +1107,7 @@ public io.a2a.grpc.PasswordOAuthFlow getPassword() { } } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) { if (passwordBuilder_ == null) { @@ -1123,7 +1123,7 @@ public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) { return this; } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder setPassword( io.a2a.grpc.PasswordOAuthFlow.Builder builderForValue) { @@ -1137,7 +1137,7 @@ public Builder setPassword( return this; } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) { if (passwordBuilder_ == null) { @@ -1160,7 +1160,7 @@ public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) { return this; } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder clearPassword() { if (passwordBuilder_ == null) { @@ -1179,13 +1179,13 @@ public Builder clearPassword() { return this; } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ public io.a2a.grpc.PasswordOAuthFlow.Builder getPasswordBuilder() { return internalGetPasswordFieldBuilder().getBuilder(); } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ @java.lang.Override public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { @@ -1199,7 +1199,7 @@ public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { } } /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java index a8ecd460b..804d3a826 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java @@ -11,62 +11,62 @@ public interface OAuthFlowsOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return Whether the authorizationCode field is set. */ boolean hasAuthorizationCode(); /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return The authorizationCode. */ io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode(); /** - * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1 [json_name = "authorizationCode"]; + * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder(); /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return Whether the clientCredentials field is set. */ boolean hasClientCredentials(); /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return The clientCredentials. */ io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials(); /** - * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2 [json_name = "clientCredentials"]; + * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder(); /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return Whether the implicit field is set. */ boolean hasImplicit(); /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return The implicit. */ io.a2a.grpc.ImplicitOAuthFlow getImplicit(); /** - * .a2a.v1.ImplicitOAuthFlow implicit = 3 [json_name = "implicit"]; + * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder(); /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; * @return Whether the password field is set. */ boolean hasPassword(); /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; * @return The password. */ io.a2a.grpc.PasswordOAuthFlow getPassword(); /** - * .a2a.v1.PasswordOAuthFlow password = 4 [json_name = "password"]; + * .a2a.v1.PasswordOAuthFlow password = 4; */ io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java index f06f91dd9..c2f01d139 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java @@ -53,7 +53,7 @@ private OpenIdConnectSecurityScheme() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ @java.lang.Override @@ -74,7 +74,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ @java.lang.Override @@ -101,7 +101,7 @@ public java.lang.String getDescription() { * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return The openIdConnectUrl. */ @java.lang.Override @@ -123,7 +123,7 @@ public java.lang.String getOpenIdConnectUrl() { * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return The bytes for openIdConnectUrl. */ @java.lang.Override @@ -465,7 +465,7 @@ public Builder mergeFrom( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ public java.lang.String getDescription() { @@ -485,7 +485,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -506,7 +506,7 @@ public java.lang.String getDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The description to set. * @return This builder for chaining. */ @@ -523,7 +523,7 @@ public Builder setDescription( * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return This builder for chaining. */ public Builder clearDescription() { @@ -537,7 +537,7 @@ public Builder clearDescription() { * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -558,7 +558,7 @@ public Builder setDescriptionBytes( * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return The openIdConnectUrl. */ public java.lang.String getOpenIdConnectUrl() { @@ -579,7 +579,7 @@ public java.lang.String getOpenIdConnectUrl() { * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return The bytes for openIdConnectUrl. */ public com.google.protobuf.ByteString @@ -601,7 +601,7 @@ public java.lang.String getOpenIdConnectUrl() { * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @param value The openIdConnectUrl to set. * @return This builder for chaining. */ @@ -619,7 +619,7 @@ public Builder setOpenIdConnectUrl( * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return This builder for chaining. */ public Builder clearOpenIdConnectUrl() { @@ -634,7 +634,7 @@ public Builder clearOpenIdConnectUrl() { * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @param value The bytes for openIdConnectUrl to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java index 0682b5f93..20e04a27c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java @@ -15,7 +15,7 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The description. */ java.lang.String getDescription(); @@ -24,7 +24,7 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends * Description of this security scheme. * * - * string description = 1 [json_name = "description"]; + * string description = 1; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -36,7 +36,7 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return The openIdConnectUrl. */ java.lang.String getOpenIdConnectUrl(); @@ -46,7 +46,7 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends * metadata. * * - * string open_id_connect_url = 2 [json_name = "openIdConnectUrl"]; + * string open_id_connect_url = 2; * @return The bytes for openIdConnectUrl. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Part.java b/spec-grpc/src/main/java/io/a2a/grpc/Part.java index 5582d8d5e..dbb3015a3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Part.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Part.java @@ -95,14 +95,14 @@ public int getNumber() { public static final int TEXT_FIELD_NUMBER = 1; /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return Whether the text field is set. */ public boolean hasText() { return partCase_ == 1; } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return The text. */ public java.lang.String getText() { @@ -123,7 +123,7 @@ public java.lang.String getText() { } } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return The bytes for text. */ public com.google.protobuf.ByteString @@ -147,7 +147,7 @@ public java.lang.String getText() { public static final int FILE_FIELD_NUMBER = 2; /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; * @return Whether the file field is set. */ @java.lang.Override @@ -155,7 +155,7 @@ public boolean hasFile() { return partCase_ == 2; } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; * @return The file. */ @java.lang.Override @@ -166,7 +166,7 @@ public io.a2a.grpc.FilePart getFile() { return io.a2a.grpc.FilePart.getDefaultInstance(); } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ @java.lang.Override public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { @@ -178,7 +178,7 @@ public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { public static final int DATA_FIELD_NUMBER = 3; /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; * @return Whether the data field is set. */ @java.lang.Override @@ -186,7 +186,7 @@ public boolean hasData() { return partCase_ == 3; } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; * @return The data. */ @java.lang.Override @@ -197,7 +197,7 @@ public io.a2a.grpc.DataPart getData() { return io.a2a.grpc.DataPart.getDefaultInstance(); } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ @java.lang.Override public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { @@ -613,7 +613,7 @@ public Builder clearPart() { private int bitField0_; /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return Whether the text field is set. */ @java.lang.Override @@ -621,7 +621,7 @@ public boolean hasText() { return partCase_ == 1; } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return The text. */ @java.lang.Override @@ -643,7 +643,7 @@ public java.lang.String getText() { } } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return The bytes for text. */ @java.lang.Override @@ -666,7 +666,7 @@ public java.lang.String getText() { } } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @param value The text to set. * @return This builder for chaining. */ @@ -679,7 +679,7 @@ public Builder setText( return this; } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return This builder for chaining. */ public Builder clearText() { @@ -691,7 +691,7 @@ public Builder clearText() { return this; } /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @param value The bytes for text to set. * @return This builder for chaining. */ @@ -708,7 +708,7 @@ public Builder setTextBytes( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> fileBuilder_; /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; * @return Whether the file field is set. */ @java.lang.Override @@ -716,7 +716,7 @@ public boolean hasFile() { return partCase_ == 2; } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; * @return The file. */ @java.lang.Override @@ -734,7 +734,7 @@ public io.a2a.grpc.FilePart getFile() { } } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ public Builder setFile(io.a2a.grpc.FilePart value) { if (fileBuilder_ == null) { @@ -750,7 +750,7 @@ public Builder setFile(io.a2a.grpc.FilePart value) { return this; } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ public Builder setFile( io.a2a.grpc.FilePart.Builder builderForValue) { @@ -764,7 +764,7 @@ public Builder setFile( return this; } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ public Builder mergeFile(io.a2a.grpc.FilePart value) { if (fileBuilder_ == null) { @@ -787,7 +787,7 @@ public Builder mergeFile(io.a2a.grpc.FilePart value) { return this; } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ public Builder clearFile() { if (fileBuilder_ == null) { @@ -806,13 +806,13 @@ public Builder clearFile() { return this; } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ public io.a2a.grpc.FilePart.Builder getFileBuilder() { return internalGetFileFieldBuilder().getBuilder(); } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ @java.lang.Override public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { @@ -826,7 +826,7 @@ public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { } } /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> @@ -850,7 +850,7 @@ public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> dataBuilder_; /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; * @return Whether the data field is set. */ @java.lang.Override @@ -858,7 +858,7 @@ public boolean hasData() { return partCase_ == 3; } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; * @return The data. */ @java.lang.Override @@ -876,7 +876,7 @@ public io.a2a.grpc.DataPart getData() { } } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ public Builder setData(io.a2a.grpc.DataPart value) { if (dataBuilder_ == null) { @@ -892,7 +892,7 @@ public Builder setData(io.a2a.grpc.DataPart value) { return this; } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ public Builder setData( io.a2a.grpc.DataPart.Builder builderForValue) { @@ -906,7 +906,7 @@ public Builder setData( return this; } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ public Builder mergeData(io.a2a.grpc.DataPart value) { if (dataBuilder_ == null) { @@ -929,7 +929,7 @@ public Builder mergeData(io.a2a.grpc.DataPart value) { return this; } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ public Builder clearData() { if (dataBuilder_ == null) { @@ -948,13 +948,13 @@ public Builder clearData() { return this; } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ public io.a2a.grpc.DataPart.Builder getDataBuilder() { return internalGetDataFieldBuilder().getBuilder(); } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ @java.lang.Override public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { @@ -968,7 +968,7 @@ public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { } } /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java index 5840438a3..773988036 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java @@ -11,49 +11,49 @@ public interface PartOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return Whether the text field is set. */ boolean hasText(); /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return The text. */ java.lang.String getText(); /** - * string text = 1 [json_name = "text"]; + * string text = 1; * @return The bytes for text. */ com.google.protobuf.ByteString getTextBytes(); /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; * @return Whether the file field is set. */ boolean hasFile(); /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; * @return The file. */ io.a2a.grpc.FilePart getFile(); /** - * .a2a.v1.FilePart file = 2 [json_name = "file"]; + * .a2a.v1.FilePart file = 2; */ io.a2a.grpc.FilePartOrBuilder getFileOrBuilder(); /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; * @return Whether the data field is set. */ boolean hasData(); /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; * @return The data. */ io.a2a.grpc.DataPart getData(); /** - * .a2a.v1.DataPart data = 3 [json_name = "data"]; + * .a2a.v1.DataPart data = 3; */ io.a2a.grpc.DataPartOrBuilder getDataOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java index 3801ef006..c9c1c4b0a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java @@ -66,7 +66,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The tokenUrl. */ @java.lang.Override @@ -88,7 +88,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -115,7 +115,7 @@ public java.lang.String getTokenUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ @java.lang.Override @@ -137,7 +137,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ @java.lang.Override @@ -187,7 +187,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public boolean containsScopes( @@ -209,7 +209,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -221,7 +221,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public /* nullable */ @@ -240,7 +240,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -640,7 +640,7 @@ public Builder mergeFrom( * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -661,7 +661,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -683,7 +683,7 @@ public java.lang.String getTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -701,7 +701,7 @@ public Builder setTokenUrl( * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return This builder for chaining. */ public Builder clearTokenUrl() { @@ -716,7 +716,7 @@ public Builder clearTokenUrl() { * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -737,7 +737,7 @@ public Builder setTokenUrlBytes( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ public java.lang.String getRefreshUrl() { @@ -758,7 +758,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ public com.google.protobuf.ByteString @@ -780,7 +780,7 @@ public java.lang.String getRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @param value The refreshUrl to set. * @return This builder for chaining. */ @@ -798,7 +798,7 @@ public Builder setRefreshUrl( * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return This builder for chaining. */ public Builder clearRefreshUrl() { @@ -813,7 +813,7 @@ public Builder clearRefreshUrl() { * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @param value The bytes for refreshUrl to set. * @return This builder for chaining. */ @@ -859,7 +859,7 @@ public int getScopesCount() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public boolean containsScopes( @@ -881,7 +881,7 @@ public java.util.Map getScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -893,7 +893,7 @@ public java.util.Map getScopesMap() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public /* nullable */ @@ -912,7 +912,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -937,7 +937,7 @@ public Builder clearScopes() { * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder removeScopes( java.lang.String key) { @@ -961,7 +961,7 @@ public Builder removeScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder putScopes( java.lang.String key, @@ -979,7 +979,7 @@ public Builder putScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java index 73c88685f..1eea1da0f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java @@ -16,7 +16,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The tokenUrl. */ java.lang.String getTokenUrl(); @@ -26,7 +26,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The OAuth2 standard requires the use of TLS. * * - * string token_url = 1 [json_name = "tokenUrl"]; + * string token_url = 1; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface PasswordOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The refreshUrl. */ java.lang.String getRefreshUrl(); @@ -48,7 +48,7 @@ public interface PasswordOAuthFlowOrBuilder extends * form of a URL. The OAuth2 standard requires the use of TLS. * * - * string refresh_url = 2 [json_name = "refreshUrl"]; + * string refresh_url = 2; * @return The bytes for refreshUrl. */ com.google.protobuf.ByteString @@ -60,7 +60,7 @@ public interface PasswordOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ int getScopesCount(); /** @@ -69,7 +69,7 @@ public interface PasswordOAuthFlowOrBuilder extends * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ boolean containsScopes( java.lang.String key); @@ -85,7 +85,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ java.util.Map getScopesMap(); @@ -95,7 +95,7 @@ boolean containsScopes( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -108,7 +108,7 @@ java.lang.String getScopesOrDefault( * scope name and a short description for it. The map MAY be empty. * * - * map<string, string> scopes = 3 [json_name = "scopes"]; + * map<string, string> scopes = 3; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java index b9b1909f5..5942397ba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java @@ -59,7 +59,7 @@ private PushNotificationConfig() { * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ @java.lang.Override @@ -80,7 +80,7 @@ public java.lang.String getId() { * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ @java.lang.Override @@ -106,7 +106,7 @@ public java.lang.String getId() { * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return The url. */ @java.lang.Override @@ -127,7 +127,7 @@ public java.lang.String getUrl() { * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return The bytes for url. */ @java.lang.Override @@ -153,7 +153,7 @@ public java.lang.String getUrl() { * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return The token. */ @java.lang.Override @@ -174,7 +174,7 @@ public java.lang.String getToken() { * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return The bytes for token. */ @java.lang.Override @@ -199,7 +199,7 @@ public java.lang.String getToken() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; * @return Whether the authentication field is set. */ @java.lang.Override @@ -211,7 +211,7 @@ public boolean hasAuthentication() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; * @return The authentication. */ @java.lang.Override @@ -223,7 +223,7 @@ public io.a2a.grpc.AuthenticationInfo getAuthentication() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ @java.lang.Override public io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder() { @@ -627,7 +627,7 @@ public Builder mergeFrom( * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ public java.lang.String getId() { @@ -647,7 +647,7 @@ public java.lang.String getId() { * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ public com.google.protobuf.ByteString @@ -668,7 +668,7 @@ public java.lang.String getId() { * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @param value The id to set. * @return This builder for chaining. */ @@ -685,7 +685,7 @@ public Builder setId( * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return This builder for chaining. */ public Builder clearId() { @@ -699,7 +699,7 @@ public Builder clearId() { * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @param value The bytes for id to set. * @return This builder for chaining. */ @@ -719,7 +719,7 @@ public Builder setIdBytes( * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return The url. */ public java.lang.String getUrl() { @@ -739,7 +739,7 @@ public java.lang.String getUrl() { * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -760,7 +760,7 @@ public java.lang.String getUrl() { * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @param value The url to set. * @return This builder for chaining. */ @@ -777,7 +777,7 @@ public Builder setUrl( * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return This builder for chaining. */ public Builder clearUrl() { @@ -791,7 +791,7 @@ public Builder clearUrl() { * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -811,7 +811,7 @@ public Builder setUrlBytes( * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return The token. */ public java.lang.String getToken() { @@ -831,7 +831,7 @@ public java.lang.String getToken() { * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return The bytes for token. */ public com.google.protobuf.ByteString @@ -852,7 +852,7 @@ public java.lang.String getToken() { * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @param value The token to set. * @return This builder for chaining. */ @@ -869,7 +869,7 @@ public Builder setToken( * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return This builder for chaining. */ public Builder clearToken() { @@ -883,7 +883,7 @@ public Builder clearToken() { * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @param value The bytes for token to set. * @return This builder for chaining. */ @@ -905,7 +905,7 @@ public Builder setTokenBytes( * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; * @return Whether the authentication field is set. */ public boolean hasAuthentication() { @@ -916,7 +916,7 @@ public boolean hasAuthentication() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; * @return The authentication. */ public io.a2a.grpc.AuthenticationInfo getAuthentication() { @@ -931,7 +931,7 @@ public io.a2a.grpc.AuthenticationInfo getAuthentication() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ public Builder setAuthentication(io.a2a.grpc.AuthenticationInfo value) { if (authenticationBuilder_ == null) { @@ -951,7 +951,7 @@ public Builder setAuthentication(io.a2a.grpc.AuthenticationInfo value) { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ public Builder setAuthentication( io.a2a.grpc.AuthenticationInfo.Builder builderForValue) { @@ -969,7 +969,7 @@ public Builder setAuthentication( * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ public Builder mergeAuthentication(io.a2a.grpc.AuthenticationInfo value) { if (authenticationBuilder_ == null) { @@ -994,7 +994,7 @@ public Builder mergeAuthentication(io.a2a.grpc.AuthenticationInfo value) { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ public Builder clearAuthentication() { bitField0_ = (bitField0_ & ~0x00000008); @@ -1011,7 +1011,7 @@ public Builder clearAuthentication() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ public io.a2a.grpc.AuthenticationInfo.Builder getAuthenticationBuilder() { bitField0_ |= 0x00000008; @@ -1023,7 +1023,7 @@ public io.a2a.grpc.AuthenticationInfo.Builder getAuthenticationBuilder() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ public io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder() { if (authenticationBuilder_ != null) { @@ -1038,7 +1038,7 @@ public io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder() { * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AuthenticationInfo, io.a2a.grpc.AuthenticationInfo.Builder, io.a2a.grpc.AuthenticationInfoOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java index 9aa257747..a04532954 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java @@ -15,7 +15,7 @@ public interface PushNotificationConfigOrBuilder extends * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ java.lang.String getId(); @@ -24,7 +24,7 @@ public interface PushNotificationConfigOrBuilder extends * A unique id for this push notification. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface PushNotificationConfigOrBuilder extends * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return The url. */ java.lang.String getUrl(); @@ -44,7 +44,7 @@ public interface PushNotificationConfigOrBuilder extends * Url to send the notification too * * - * string url = 2 [json_name = "url"]; + * string url = 2; * @return The bytes for url. */ com.google.protobuf.ByteString @@ -55,7 +55,7 @@ public interface PushNotificationConfigOrBuilder extends * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return The token. */ java.lang.String getToken(); @@ -64,7 +64,7 @@ public interface PushNotificationConfigOrBuilder extends * Token unique for this task/session * * - * string token = 3 [json_name = "token"]; + * string token = 3; * @return The bytes for token. */ com.google.protobuf.ByteString @@ -75,7 +75,7 @@ public interface PushNotificationConfigOrBuilder extends * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; * @return Whether the authentication field is set. */ boolean hasAuthentication(); @@ -84,7 +84,7 @@ public interface PushNotificationConfigOrBuilder extends * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; * @return The authentication. */ io.a2a.grpc.AuthenticationInfo getAuthentication(); @@ -93,7 +93,7 @@ public interface PushNotificationConfigOrBuilder extends * Information about the authentication to sent with the notification * * - * .a2a.v1.AuthenticationInfo authentication = 4 [json_name = "authentication"]; + * .a2a.v1.AuthenticationInfo authentication = 4; */ io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Security.java b/spec-grpc/src/main/java/io/a2a/grpc/Security.java index f19e94a20..3602a93ad 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Security.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Security.java @@ -82,7 +82,7 @@ public int getSchemesCount() { return internalGetSchemes().getMap().size(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public boolean containsSchemes( @@ -99,14 +99,14 @@ public java.util.Map getSchemes() { return getSchemesMap(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public java.util.Map getSchemesMap() { return internalGetSchemes().getMap(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public /* nullable */ @@ -120,7 +120,7 @@ io.a2a.grpc.StringList getSchemesOrDefault( return map.containsKey(key) ? map.get(key) : defaultValue; } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public io.a2a.grpc.StringList getSchemesOrThrow( @@ -500,7 +500,7 @@ public int getSchemesCount() { return internalGetSchemes().ensureBuilderMap().size(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public boolean containsSchemes( @@ -517,14 +517,14 @@ public java.util.Map getSchemes() { return getSchemesMap(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public java.util.Map getSchemesMap() { return internalGetSchemes().getImmutableMap(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public /* nullable */ @@ -537,7 +537,7 @@ io.a2a.grpc.StringList getSchemesOrDefault( return map.containsKey(key) ? schemesConverter.build(map.get(key)) : defaultValue; } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ @java.lang.Override public io.a2a.grpc.StringList getSchemesOrThrow( @@ -555,7 +555,7 @@ public Builder clearSchemes() { return this; } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ public Builder removeSchemes( java.lang.String key) { @@ -574,7 +574,7 @@ public Builder removeSchemes( return internalGetMutableSchemes().ensureMessageMap(); } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ public Builder putSchemes( java.lang.String key, @@ -587,7 +587,7 @@ public Builder putSchemes( return this; } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ public Builder putAllSchemes( java.util.Map values) { @@ -602,7 +602,7 @@ public Builder putAllSchemes( return this; } /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ public io.a2a.grpc.StringList.Builder putSchemesBuilderIfAbsent( java.lang.String key) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java index c6cad1a13..1b3897669 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java @@ -11,11 +11,11 @@ public interface SecurityOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ int getSchemesCount(); /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ boolean containsSchemes( java.lang.String key); @@ -26,12 +26,12 @@ boolean containsSchemes( java.util.Map getSchemes(); /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ java.util.Map getSchemesMap(); /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ /* nullable */ io.a2a.grpc.StringList getSchemesOrDefault( @@ -39,7 +39,7 @@ io.a2a.grpc.StringList getSchemesOrDefault( /* nullable */ io.a2a.grpc.StringList defaultValue); /** - * map<string, .a2a.v1.StringList> schemes = 1 [json_name = "schemes"]; + * map<string, .a2a.v1.StringList> schemes = 1; */ io.a2a.grpc.StringList getSchemesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java index a0de79755..b6726d097 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java @@ -91,7 +91,7 @@ public int getNumber() { public static final int API_KEY_SECURITY_SCHEME_FIELD_NUMBER = 1; /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return Whether the apiKeySecurityScheme field is set. */ @java.lang.Override @@ -99,7 +99,7 @@ public boolean hasApiKeySecurityScheme() { return schemeCase_ == 1; } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return The apiKeySecurityScheme. */ @java.lang.Override @@ -110,7 +110,7 @@ public io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme() { return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ @java.lang.Override public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder() { @@ -122,7 +122,7 @@ public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilde public static final int HTTP_AUTH_SECURITY_SCHEME_FIELD_NUMBER = 2; /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return Whether the httpAuthSecurityScheme field is set. */ @java.lang.Override @@ -130,7 +130,7 @@ public boolean hasHttpAuthSecurityScheme() { return schemeCase_ == 2; } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return The httpAuthSecurityScheme. */ @java.lang.Override @@ -141,7 +141,7 @@ public io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme() { return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ @java.lang.Override public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder() { @@ -153,7 +153,7 @@ public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBu public static final int OAUTH2_SECURITY_SCHEME_FIELD_NUMBER = 3; /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return Whether the oauth2SecurityScheme field is set. */ @java.lang.Override @@ -161,7 +161,7 @@ public boolean hasOauth2SecurityScheme() { return schemeCase_ == 3; } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return The oauth2SecurityScheme. */ @java.lang.Override @@ -172,7 +172,7 @@ public io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme() { return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ @java.lang.Override public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder() { @@ -184,7 +184,7 @@ public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilde public static final int OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_NUMBER = 4; /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return Whether the openIdConnectSecurityScheme field is set. */ @java.lang.Override @@ -192,7 +192,7 @@ public boolean hasOpenIdConnectSecurityScheme() { return schemeCase_ == 4; } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return The openIdConnectSecurityScheme. */ @java.lang.Override @@ -203,7 +203,7 @@ public io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme() return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ @java.lang.Override public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder() { @@ -655,7 +655,7 @@ public Builder clearScheme() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.APIKeySecurityScheme, io.a2a.grpc.APIKeySecurityScheme.Builder, io.a2a.grpc.APIKeySecuritySchemeOrBuilder> apiKeySecuritySchemeBuilder_; /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return Whether the apiKeySecurityScheme field is set. */ @java.lang.Override @@ -663,7 +663,7 @@ public boolean hasApiKeySecurityScheme() { return schemeCase_ == 1; } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return The apiKeySecurityScheme. */ @java.lang.Override @@ -681,7 +681,7 @@ public io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme() { } } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder setApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { if (apiKeySecuritySchemeBuilder_ == null) { @@ -697,7 +697,7 @@ public Builder setApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { return this; } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder setApiKeySecurityScheme( io.a2a.grpc.APIKeySecurityScheme.Builder builderForValue) { @@ -711,7 +711,7 @@ public Builder setApiKeySecurityScheme( return this; } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder mergeApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { if (apiKeySecuritySchemeBuilder_ == null) { @@ -734,7 +734,7 @@ public Builder mergeApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) return this; } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder clearApiKeySecurityScheme() { if (apiKeySecuritySchemeBuilder_ == null) { @@ -753,13 +753,13 @@ public Builder clearApiKeySecurityScheme() { return this; } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public io.a2a.grpc.APIKeySecurityScheme.Builder getApiKeySecuritySchemeBuilder() { return internalGetApiKeySecuritySchemeFieldBuilder().getBuilder(); } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ @java.lang.Override public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder() { @@ -773,7 +773,7 @@ public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilde } } /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.APIKeySecurityScheme, io.a2a.grpc.APIKeySecurityScheme.Builder, io.a2a.grpc.APIKeySecuritySchemeOrBuilder> @@ -797,7 +797,7 @@ public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilde private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.HTTPAuthSecurityScheme, io.a2a.grpc.HTTPAuthSecurityScheme.Builder, io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder> httpAuthSecuritySchemeBuilder_; /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return Whether the httpAuthSecurityScheme field is set. */ @java.lang.Override @@ -805,7 +805,7 @@ public boolean hasHttpAuthSecurityScheme() { return schemeCase_ == 2; } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return The httpAuthSecurityScheme. */ @java.lang.Override @@ -823,7 +823,7 @@ public io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme() { } } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder setHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme value) { if (httpAuthSecuritySchemeBuilder_ == null) { @@ -839,7 +839,7 @@ public Builder setHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme valu return this; } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder setHttpAuthSecurityScheme( io.a2a.grpc.HTTPAuthSecurityScheme.Builder builderForValue) { @@ -853,7 +853,7 @@ public Builder setHttpAuthSecurityScheme( return this; } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder mergeHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme value) { if (httpAuthSecuritySchemeBuilder_ == null) { @@ -876,7 +876,7 @@ public Builder mergeHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme va return this; } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder clearHttpAuthSecurityScheme() { if (httpAuthSecuritySchemeBuilder_ == null) { @@ -895,13 +895,13 @@ public Builder clearHttpAuthSecurityScheme() { return this; } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public io.a2a.grpc.HTTPAuthSecurityScheme.Builder getHttpAuthSecuritySchemeBuilder() { return internalGetHttpAuthSecuritySchemeFieldBuilder().getBuilder(); } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ @java.lang.Override public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder() { @@ -915,7 +915,7 @@ public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBu } } /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.HTTPAuthSecurityScheme, io.a2a.grpc.HTTPAuthSecurityScheme.Builder, io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder> @@ -939,7 +939,7 @@ public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBu private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OAuth2SecurityScheme, io.a2a.grpc.OAuth2SecurityScheme.Builder, io.a2a.grpc.OAuth2SecuritySchemeOrBuilder> oauth2SecuritySchemeBuilder_; /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return Whether the oauth2SecurityScheme field is set. */ @java.lang.Override @@ -947,7 +947,7 @@ public boolean hasOauth2SecurityScheme() { return schemeCase_ == 3; } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return The oauth2SecurityScheme. */ @java.lang.Override @@ -965,7 +965,7 @@ public io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme() { } } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder setOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { if (oauth2SecuritySchemeBuilder_ == null) { @@ -981,7 +981,7 @@ public Builder setOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { return this; } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder setOauth2SecurityScheme( io.a2a.grpc.OAuth2SecurityScheme.Builder builderForValue) { @@ -995,7 +995,7 @@ public Builder setOauth2SecurityScheme( return this; } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder mergeOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { if (oauth2SecuritySchemeBuilder_ == null) { @@ -1018,7 +1018,7 @@ public Builder mergeOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) return this; } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder clearOauth2SecurityScheme() { if (oauth2SecuritySchemeBuilder_ == null) { @@ -1037,13 +1037,13 @@ public Builder clearOauth2SecurityScheme() { return this; } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public io.a2a.grpc.OAuth2SecurityScheme.Builder getOauth2SecuritySchemeBuilder() { return internalGetOauth2SecuritySchemeFieldBuilder().getBuilder(); } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ @java.lang.Override public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder() { @@ -1057,7 +1057,7 @@ public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilde } } /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OAuth2SecurityScheme, io.a2a.grpc.OAuth2SecurityScheme.Builder, io.a2a.grpc.OAuth2SecuritySchemeOrBuilder> @@ -1081,7 +1081,7 @@ public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilde private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OpenIdConnectSecurityScheme, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder, io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder> openIdConnectSecuritySchemeBuilder_; /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return Whether the openIdConnectSecurityScheme field is set. */ @java.lang.Override @@ -1089,7 +1089,7 @@ public boolean hasOpenIdConnectSecurityScheme() { return schemeCase_ == 4; } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return The openIdConnectSecurityScheme. */ @java.lang.Override @@ -1107,7 +1107,7 @@ public io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme() } } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder setOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityScheme value) { if (openIdConnectSecuritySchemeBuilder_ == null) { @@ -1123,7 +1123,7 @@ public Builder setOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityS return this; } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder setOpenIdConnectSecurityScheme( io.a2a.grpc.OpenIdConnectSecurityScheme.Builder builderForValue) { @@ -1137,7 +1137,7 @@ public Builder setOpenIdConnectSecurityScheme( return this; } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder mergeOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityScheme value) { if (openIdConnectSecuritySchemeBuilder_ == null) { @@ -1160,7 +1160,7 @@ public Builder mergeOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurit return this; } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder clearOpenIdConnectSecurityScheme() { if (openIdConnectSecuritySchemeBuilder_ == null) { @@ -1179,13 +1179,13 @@ public Builder clearOpenIdConnectSecurityScheme() { return this; } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public io.a2a.grpc.OpenIdConnectSecurityScheme.Builder getOpenIdConnectSecuritySchemeBuilder() { return internalGetOpenIdConnectSecuritySchemeFieldBuilder().getBuilder(); } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ @java.lang.Override public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder() { @@ -1199,7 +1199,7 @@ public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecurity } } /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OpenIdConnectSecurityScheme, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder, io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java index ed4aa69c0..b79be8faf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java @@ -11,62 +11,62 @@ public interface SecuritySchemeOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return Whether the apiKeySecurityScheme field is set. */ boolean hasApiKeySecurityScheme(); /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return The apiKeySecurityScheme. */ io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme(); /** - * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1 [json_name = "apiKeySecurityScheme"]; + * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder(); /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return Whether the httpAuthSecurityScheme field is set. */ boolean hasHttpAuthSecurityScheme(); /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return The httpAuthSecurityScheme. */ io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme(); /** - * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2 [json_name = "httpAuthSecurityScheme"]; + * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder(); /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return Whether the oauth2SecurityScheme field is set. */ boolean hasOauth2SecurityScheme(); /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return The oauth2SecurityScheme. */ io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme(); /** - * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3 [json_name = "oauth2SecurityScheme"]; + * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder(); /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return Whether the openIdConnectSecurityScheme field is set. */ boolean hasOpenIdConnectSecurityScheme(); /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return The openIdConnectSecurityScheme. */ io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme(); /** - * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4 [json_name = "openIdConnectSecurityScheme"]; + * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index 42bd62bcc..75db4a2b8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -59,7 +59,7 @@ private SendMessageConfiguration() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return A list containing the acceptedOutputModes. */ public com.google.protobuf.ProtocolStringList @@ -71,7 +71,7 @@ private SendMessageConfiguration() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return The count of acceptedOutputModes. */ public int getAcceptedOutputModesCount() { @@ -82,7 +82,7 @@ public int getAcceptedOutputModesCount() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index of the element to return. * @return The acceptedOutputModes at the given index. */ @@ -94,7 +94,7 @@ public java.lang.String getAcceptedOutputModes(int index) { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index of the value to return. * @return The bytes of the acceptedOutputModes at the given index. */ @@ -110,7 +110,7 @@ public java.lang.String getAcceptedOutputModes(int index) { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; * @return Whether the pushNotification field is set. */ @java.lang.Override @@ -122,7 +122,7 @@ public boolean hasPushNotification() { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; * @return The pushNotification. */ @java.lang.Override @@ -134,7 +134,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotification() { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ @java.lang.Override public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder() { @@ -149,7 +149,7 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder( * history will be unlimited. * * - * int32 history_length = 3 [json_name = "historyLength"]; + * int32 history_length = 3; * @return The historyLength. */ @java.lang.Override @@ -167,7 +167,7 @@ public int getHistoryLength() { * updates. * * - * bool blocking = 4 [json_name = "blocking"]; + * bool blocking = 4; * @return The blocking. */ @java.lang.Override @@ -593,7 +593,7 @@ private void ensureAcceptedOutputModesIsMutable() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return A list containing the acceptedOutputModes. */ public com.google.protobuf.ProtocolStringList @@ -606,7 +606,7 @@ private void ensureAcceptedOutputModesIsMutable() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return The count of acceptedOutputModes. */ public int getAcceptedOutputModesCount() { @@ -617,7 +617,7 @@ public int getAcceptedOutputModesCount() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index of the element to return. * @return The acceptedOutputModes at the given index. */ @@ -629,7 +629,7 @@ public java.lang.String getAcceptedOutputModes(int index) { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index of the value to return. * @return The bytes of the acceptedOutputModes at the given index. */ @@ -642,7 +642,7 @@ public java.lang.String getAcceptedOutputModes(int index) { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index to set the value at. * @param value The acceptedOutputModes to set. * @return This builder for chaining. @@ -661,7 +661,7 @@ public Builder setAcceptedOutputModes( * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param value The acceptedOutputModes to add. * @return This builder for chaining. */ @@ -679,7 +679,7 @@ public Builder addAcceptedOutputModes( * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param values The acceptedOutputModes to add. * @return This builder for chaining. */ @@ -697,7 +697,7 @@ public Builder addAllAcceptedOutputModes( * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return This builder for chaining. */ public Builder clearAcceptedOutputModes() { @@ -712,7 +712,7 @@ public Builder clearAcceptedOutputModes() { * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param value The bytes of the acceptedOutputModes to add. * @return This builder for chaining. */ @@ -735,7 +735,7 @@ public Builder addAcceptedOutputModesBytes( * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; * @return Whether the pushNotification field is set. */ public boolean hasPushNotification() { @@ -746,7 +746,7 @@ public boolean hasPushNotification() { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; * @return The pushNotification. */ public io.a2a.grpc.PushNotificationConfig getPushNotification() { @@ -761,7 +761,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotification() { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ public Builder setPushNotification(io.a2a.grpc.PushNotificationConfig value) { if (pushNotificationBuilder_ == null) { @@ -781,7 +781,7 @@ public Builder setPushNotification(io.a2a.grpc.PushNotificationConfig value) { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ public Builder setPushNotification( io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { @@ -799,7 +799,7 @@ public Builder setPushNotification( * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ public Builder mergePushNotification(io.a2a.grpc.PushNotificationConfig value) { if (pushNotificationBuilder_ == null) { @@ -824,7 +824,7 @@ public Builder mergePushNotification(io.a2a.grpc.PushNotificationConfig value) { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ public Builder clearPushNotification() { bitField0_ = (bitField0_ & ~0x00000002); @@ -841,7 +841,7 @@ public Builder clearPushNotification() { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationBuilder() { bitField0_ |= 0x00000002; @@ -853,7 +853,7 @@ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationBuilder() { * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder() { if (pushNotificationBuilder_ != null) { @@ -868,7 +868,7 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder( * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> @@ -891,7 +891,7 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder( * history will be unlimited. * * - * int32 history_length = 3 [json_name = "historyLength"]; + * int32 history_length = 3; * @return The historyLength. */ @java.lang.Override @@ -904,7 +904,7 @@ public int getHistoryLength() { * history will be unlimited. * * - * int32 history_length = 3 [json_name = "historyLength"]; + * int32 history_length = 3; * @param value The historyLength to set. * @return This builder for chaining. */ @@ -921,7 +921,7 @@ public Builder setHistoryLength(int value) { * history will be unlimited. * * - * int32 history_length = 3 [json_name = "historyLength"]; + * int32 history_length = 3; * @return This builder for chaining. */ public Builder clearHistoryLength() { @@ -940,7 +940,7 @@ public Builder clearHistoryLength() { * updates. * * - * bool blocking = 4 [json_name = "blocking"]; + * bool blocking = 4; * @return The blocking. */ @java.lang.Override @@ -955,7 +955,7 @@ public boolean getBlocking() { * updates. * * - * bool blocking = 4 [json_name = "blocking"]; + * bool blocking = 4; * @param value The blocking to set. * @return This builder for chaining. */ @@ -974,7 +974,7 @@ public Builder setBlocking(boolean value) { * updates. * * - * bool blocking = 4 [json_name = "blocking"]; + * bool blocking = 4; * @return This builder for chaining. */ public Builder clearBlocking() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java index 7df0eb45a..ff22f3f2a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -15,7 +15,7 @@ public interface SendMessageConfigurationOrBuilder extends * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return A list containing the acceptedOutputModes. */ java.util.List @@ -25,7 +25,7 @@ public interface SendMessageConfigurationOrBuilder extends * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @return The count of acceptedOutputModes. */ int getAcceptedOutputModesCount(); @@ -34,7 +34,7 @@ public interface SendMessageConfigurationOrBuilder extends * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index of the element to return. * @return The acceptedOutputModes at the given index. */ @@ -44,7 +44,7 @@ public interface SendMessageConfigurationOrBuilder extends * The output modes that the agent is expected to respond with. * * - * repeated string accepted_output_modes = 1 [json_name = "acceptedOutputModes"]; + * repeated string accepted_output_modes = 1; * @param index The index of the value to return. * @return The bytes of the acceptedOutputModes at the given index. */ @@ -56,7 +56,7 @@ public interface SendMessageConfigurationOrBuilder extends * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; * @return Whether the pushNotification field is set. */ boolean hasPushNotification(); @@ -65,7 +65,7 @@ public interface SendMessageConfigurationOrBuilder extends * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; * @return The pushNotification. */ io.a2a.grpc.PushNotificationConfig getPushNotification(); @@ -74,7 +74,7 @@ public interface SendMessageConfigurationOrBuilder extends * A configuration of a webhook that can be used to receive updates * * - * .a2a.v1.PushNotificationConfig push_notification = 2 [json_name = "pushNotification"]; + * .a2a.v1.PushNotificationConfig push_notification = 2; */ io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder(); @@ -84,7 +84,7 @@ public interface SendMessageConfigurationOrBuilder extends * history will be unlimited. * * - * int32 history_length = 3 [json_name = "historyLength"]; + * int32 history_length = 3; * @return The historyLength. */ int getHistoryLength(); @@ -97,7 +97,7 @@ public interface SendMessageConfigurationOrBuilder extends * updates. * * - * bool blocking = 4 [json_name = "blocking"]; + * bool blocking = 4; * @return The blocking. */ boolean getBlocking(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index 7b3c9cb87..53e563640 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -51,7 +51,7 @@ private SendMessageRequest() { public static final int REQUEST_FIELD_NUMBER = 1; private io.a2a.grpc.Message request_; /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ @java.lang.Override @@ -59,7 +59,7 @@ public boolean hasRequest() { return ((bitField0_ & 0x00000001) != 0); } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The request. */ @java.lang.Override @@ -67,7 +67,7 @@ public io.a2a.grpc.Message getRequest() { return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { @@ -77,7 +77,7 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { public static final int CONFIGURATION_FIELD_NUMBER = 2; private io.a2a.grpc.SendMessageConfiguration configuration_; /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; * @return Whether the configuration field is set. */ @java.lang.Override @@ -85,7 +85,7 @@ public boolean hasConfiguration() { return ((bitField0_ & 0x00000002) != 0); } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; * @return The configuration. */ @java.lang.Override @@ -93,7 +93,7 @@ public io.a2a.grpc.SendMessageConfiguration getConfiguration() { return configuration_ == null ? io.a2a.grpc.SendMessageConfiguration.getDefaultInstance() : configuration_; } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ @java.lang.Override public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() { @@ -103,7 +103,7 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() public static final int METADATA_FIELD_NUMBER = 3; private com.google.protobuf.Struct metadata_; /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ @java.lang.Override @@ -111,7 +111,7 @@ public boolean hasMetadata() { return ((bitField0_ & 0x00000004) != 0); } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; * @return The metadata. */ @java.lang.Override @@ -119,7 +119,7 @@ public com.google.protobuf.Struct getMetadata() { return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -525,14 +525,14 @@ public Builder mergeFrom( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> requestBuilder_; /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ public boolean hasRequest() { return ((bitField0_ & 0x00000001) != 0); } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The request. */ public io.a2a.grpc.Message getRequest() { @@ -543,7 +543,7 @@ public io.a2a.grpc.Message getRequest() { } } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setRequest(io.a2a.grpc.Message value) { if (requestBuilder_ == null) { @@ -559,7 +559,7 @@ public Builder setRequest(io.a2a.grpc.Message value) { return this; } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setRequest( io.a2a.grpc.Message.Builder builderForValue) { @@ -573,7 +573,7 @@ public Builder setRequest( return this; } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeRequest(io.a2a.grpc.Message value) { if (requestBuilder_ == null) { @@ -594,7 +594,7 @@ public Builder mergeRequest(io.a2a.grpc.Message value) { return this; } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearRequest() { bitField0_ = (bitField0_ & ~0x00000001); @@ -607,7 +607,7 @@ public Builder clearRequest() { return this; } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Message.Builder getRequestBuilder() { bitField0_ |= 0x00000001; @@ -615,7 +615,7 @@ public io.a2a.grpc.Message.Builder getRequestBuilder() { return internalGetRequestFieldBuilder().getBuilder(); } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { if (requestBuilder_ != null) { @@ -626,7 +626,7 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { } } /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> @@ -646,14 +646,14 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.SendMessageConfiguration, io.a2a.grpc.SendMessageConfiguration.Builder, io.a2a.grpc.SendMessageConfigurationOrBuilder> configurationBuilder_; /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; * @return Whether the configuration field is set. */ public boolean hasConfiguration() { return ((bitField0_ & 0x00000002) != 0); } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; * @return The configuration. */ public io.a2a.grpc.SendMessageConfiguration getConfiguration() { @@ -664,7 +664,7 @@ public io.a2a.grpc.SendMessageConfiguration getConfiguration() { } } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder setConfiguration(io.a2a.grpc.SendMessageConfiguration value) { if (configurationBuilder_ == null) { @@ -680,7 +680,7 @@ public Builder setConfiguration(io.a2a.grpc.SendMessageConfiguration value) { return this; } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder setConfiguration( io.a2a.grpc.SendMessageConfiguration.Builder builderForValue) { @@ -694,7 +694,7 @@ public Builder setConfiguration( return this; } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) { if (configurationBuilder_ == null) { @@ -715,7 +715,7 @@ public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) { return this; } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder clearConfiguration() { bitField0_ = (bitField0_ & ~0x00000002); @@ -728,7 +728,7 @@ public Builder clearConfiguration() { return this; } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ public io.a2a.grpc.SendMessageConfiguration.Builder getConfigurationBuilder() { bitField0_ |= 0x00000002; @@ -736,7 +736,7 @@ public io.a2a.grpc.SendMessageConfiguration.Builder getConfigurationBuilder() { return internalGetConfigurationFieldBuilder().getBuilder(); } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() { if (configurationBuilder_ != null) { @@ -747,7 +747,7 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() } } /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.SendMessageConfiguration, io.a2a.grpc.SendMessageConfiguration.Builder, io.a2a.grpc.SendMessageConfigurationOrBuilder> @@ -767,14 +767,14 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ public boolean hasMetadata() { return ((bitField0_ & 0x00000004) != 0); } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -785,7 +785,7 @@ public com.google.protobuf.Struct getMetadata() { } } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -801,7 +801,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { return this; } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -815,7 +815,7 @@ public Builder setMetadata( return this; } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -836,7 +836,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { return this; } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000004); @@ -849,7 +849,7 @@ public Builder clearMetadata() { return this; } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { bitField0_ |= 0x00000004; @@ -857,7 +857,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { return internalGetMetadataFieldBuilder().getBuilder(); } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -868,7 +868,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { } } /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java index 824622db1..fc3632f6b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java @@ -11,47 +11,47 @@ public interface SendMessageRequestOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ boolean hasRequest(); /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The request. */ io.a2a.grpc.Message getRequest(); /** - * .a2a.v1.Message request = 1 [json_name = "request", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.MessageOrBuilder getRequestOrBuilder(); /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; * @return Whether the configuration field is set. */ boolean hasConfiguration(); /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; * @return The configuration. */ io.a2a.grpc.SendMessageConfiguration getConfiguration(); /** - * .a2a.v1.SendMessageConfiguration configuration = 2 [json_name = "configuration"]; + * .a2a.v1.SendMessageConfiguration configuration = 2; */ io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder(); /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ boolean hasMetadata(); /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); /** - * .google.protobuf.Struct metadata = 3 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 3; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java index fbad3f50e..365bbcda2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java @@ -91,7 +91,7 @@ public int getNumber() { public static final int TASK_FIELD_NUMBER = 1; /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ @java.lang.Override @@ -99,7 +99,7 @@ public boolean hasTask() { return payloadCase_ == 1; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return The task. */ @java.lang.Override @@ -110,7 +110,7 @@ public io.a2a.grpc.Task getTask() { return io.a2a.grpc.Task.getDefaultInstance(); } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ @java.lang.Override public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { @@ -531,7 +531,7 @@ public Builder clearPayload() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> taskBuilder_; /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ @java.lang.Override @@ -539,7 +539,7 @@ public boolean hasTask() { return payloadCase_ == 1; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return The task. */ @java.lang.Override @@ -557,7 +557,7 @@ public io.a2a.grpc.Task getTask() { } } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder setTask(io.a2a.grpc.Task value) { if (taskBuilder_ == null) { @@ -573,7 +573,7 @@ public Builder setTask(io.a2a.grpc.Task value) { return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder setTask( io.a2a.grpc.Task.Builder builderForValue) { @@ -587,7 +587,7 @@ public Builder setTask( return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder mergeTask(io.a2a.grpc.Task value) { if (taskBuilder_ == null) { @@ -610,7 +610,7 @@ public Builder mergeTask(io.a2a.grpc.Task value) { return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder clearTask() { if (taskBuilder_ == null) { @@ -629,13 +629,13 @@ public Builder clearTask() { return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public io.a2a.grpc.Task.Builder getTaskBuilder() { return internalGetTaskFieldBuilder().getBuilder(); } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ @java.lang.Override public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { @@ -649,7 +649,7 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { } } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java index b0032bac7..deb31c6cf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java @@ -11,17 +11,17 @@ public interface SendMessageResponseOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ boolean hasTask(); /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return The task. */ io.a2a.grpc.Task getTask(); /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ io.a2a.grpc.TaskOrBuilder getTaskOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java index f9a76e2a7..97291a324 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java @@ -103,7 +103,7 @@ public int getNumber() { public static final int TASK_FIELD_NUMBER = 1; /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ @java.lang.Override @@ -111,7 +111,7 @@ public boolean hasTask() { return payloadCase_ == 1; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return The task. */ @java.lang.Override @@ -122,7 +122,7 @@ public io.a2a.grpc.Task getTask() { return io.a2a.grpc.Task.getDefaultInstance(); } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ @java.lang.Override public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { @@ -165,7 +165,7 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { public static final int STATUS_UPDATE_FIELD_NUMBER = 3; /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return Whether the statusUpdate field is set. */ @java.lang.Override @@ -173,7 +173,7 @@ public boolean hasStatusUpdate() { return payloadCase_ == 3; } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return The statusUpdate. */ @java.lang.Override @@ -184,7 +184,7 @@ public io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate() { return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ @java.lang.Override public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { @@ -196,7 +196,7 @@ public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { public static final int ARTIFACT_UPDATE_FIELD_NUMBER = 4; /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return Whether the artifactUpdate field is set. */ @java.lang.Override @@ -204,7 +204,7 @@ public boolean hasArtifactUpdate() { return payloadCase_ == 4; } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return The artifactUpdate. */ @java.lang.Override @@ -215,7 +215,7 @@ public io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate() { return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ @java.lang.Override public io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder() { @@ -679,7 +679,7 @@ public Builder clearPayload() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> taskBuilder_; /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ @java.lang.Override @@ -687,7 +687,7 @@ public boolean hasTask() { return payloadCase_ == 1; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return The task. */ @java.lang.Override @@ -705,7 +705,7 @@ public io.a2a.grpc.Task getTask() { } } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder setTask(io.a2a.grpc.Task value) { if (taskBuilder_ == null) { @@ -721,7 +721,7 @@ public Builder setTask(io.a2a.grpc.Task value) { return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder setTask( io.a2a.grpc.Task.Builder builderForValue) { @@ -735,7 +735,7 @@ public Builder setTask( return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder mergeTask(io.a2a.grpc.Task value) { if (taskBuilder_ == null) { @@ -758,7 +758,7 @@ public Builder mergeTask(io.a2a.grpc.Task value) { return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public Builder clearTask() { if (taskBuilder_ == null) { @@ -777,13 +777,13 @@ public Builder clearTask() { return this; } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ public io.a2a.grpc.Task.Builder getTaskBuilder() { return internalGetTaskFieldBuilder().getBuilder(); } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ @java.lang.Override public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { @@ -797,7 +797,7 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { } } /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> @@ -963,7 +963,7 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatusUpdateEvent, io.a2a.grpc.TaskStatusUpdateEvent.Builder, io.a2a.grpc.TaskStatusUpdateEventOrBuilder> statusUpdateBuilder_; /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return Whether the statusUpdate field is set. */ @java.lang.Override @@ -971,7 +971,7 @@ public boolean hasStatusUpdate() { return payloadCase_ == 3; } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return The statusUpdate. */ @java.lang.Override @@ -989,7 +989,7 @@ public io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate() { } } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder setStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { if (statusUpdateBuilder_ == null) { @@ -1005,7 +1005,7 @@ public Builder setStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { return this; } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder setStatusUpdate( io.a2a.grpc.TaskStatusUpdateEvent.Builder builderForValue) { @@ -1019,7 +1019,7 @@ public Builder setStatusUpdate( return this; } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder mergeStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { if (statusUpdateBuilder_ == null) { @@ -1042,7 +1042,7 @@ public Builder mergeStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { return this; } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder clearStatusUpdate() { if (statusUpdateBuilder_ == null) { @@ -1061,13 +1061,13 @@ public Builder clearStatusUpdate() { return this; } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public io.a2a.grpc.TaskStatusUpdateEvent.Builder getStatusUpdateBuilder() { return internalGetStatusUpdateFieldBuilder().getBuilder(); } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ @java.lang.Override public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { @@ -1081,7 +1081,7 @@ public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { } } /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatusUpdateEvent, io.a2a.grpc.TaskStatusUpdateEvent.Builder, io.a2a.grpc.TaskStatusUpdateEventOrBuilder> @@ -1105,7 +1105,7 @@ public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskArtifactUpdateEvent, io.a2a.grpc.TaskArtifactUpdateEvent.Builder, io.a2a.grpc.TaskArtifactUpdateEventOrBuilder> artifactUpdateBuilder_; /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return Whether the artifactUpdate field is set. */ @java.lang.Override @@ -1113,7 +1113,7 @@ public boolean hasArtifactUpdate() { return payloadCase_ == 4; } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return The artifactUpdate. */ @java.lang.Override @@ -1131,7 +1131,7 @@ public io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate() { } } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder setArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { if (artifactUpdateBuilder_ == null) { @@ -1147,7 +1147,7 @@ public Builder setArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { return this; } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder setArtifactUpdate( io.a2a.grpc.TaskArtifactUpdateEvent.Builder builderForValue) { @@ -1161,7 +1161,7 @@ public Builder setArtifactUpdate( return this; } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder mergeArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { if (artifactUpdateBuilder_ == null) { @@ -1184,7 +1184,7 @@ public Builder mergeArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { return this; } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder clearArtifactUpdate() { if (artifactUpdateBuilder_ == null) { @@ -1203,13 +1203,13 @@ public Builder clearArtifactUpdate() { return this; } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public io.a2a.grpc.TaskArtifactUpdateEvent.Builder getArtifactUpdateBuilder() { return internalGetArtifactUpdateFieldBuilder().getBuilder(); } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ @java.lang.Override public io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder() { @@ -1223,7 +1223,7 @@ public io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder() } } /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskArtifactUpdateEvent, io.a2a.grpc.TaskArtifactUpdateEvent.Builder, io.a2a.grpc.TaskArtifactUpdateEventOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java index 3a17929cd..c606a8751 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java @@ -11,17 +11,17 @@ public interface StreamResponseOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ boolean hasTask(); /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; * @return The task. */ io.a2a.grpc.Task getTask(); /** - * .a2a.v1.Task task = 1 [json_name = "task"]; + * .a2a.v1.Task task = 1; */ io.a2a.grpc.TaskOrBuilder getTaskOrBuilder(); @@ -41,32 +41,32 @@ public interface StreamResponseOrBuilder extends io.a2a.grpc.MessageOrBuilder getMsgOrBuilder(); /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return Whether the statusUpdate field is set. */ boolean hasStatusUpdate(); /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return The statusUpdate. */ io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate(); /** - * .a2a.v1.TaskStatusUpdateEvent status_update = 3 [json_name = "statusUpdate"]; + * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder(); /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return Whether the artifactUpdate field is set. */ boolean hasArtifactUpdate(); /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return The artifactUpdate. */ io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate(); /** - * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4 [json_name = "artifactUpdate"]; + * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StringList.java b/spec-grpc/src/main/java/io/a2a/grpc/StringList.java index 4ee06422d..c283c87fa 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StringList.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StringList.java @@ -54,7 +54,7 @@ private StringList() { private com.google.protobuf.LazyStringArrayList list_ = com.google.protobuf.LazyStringArrayList.emptyList(); /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return A list containing the list. */ public com.google.protobuf.ProtocolStringList @@ -62,14 +62,14 @@ private StringList() { return list_; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return The count of list. */ public int getListCount() { return list_.size(); } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index of the element to return. * @return The list at the given index. */ @@ -77,7 +77,7 @@ public java.lang.String getList(int index) { return list_.get(index); } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index of the value to return. * @return The bytes of the list at the given index. */ @@ -408,7 +408,7 @@ private void ensureListIsMutable() { bitField0_ |= 0x00000001; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return A list containing the list. */ public com.google.protobuf.ProtocolStringList @@ -417,14 +417,14 @@ private void ensureListIsMutable() { return list_; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return The count of list. */ public int getListCount() { return list_.size(); } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index of the element to return. * @return The list at the given index. */ @@ -432,7 +432,7 @@ public java.lang.String getList(int index) { return list_.get(index); } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index of the value to return. * @return The bytes of the list at the given index. */ @@ -441,7 +441,7 @@ public java.lang.String getList(int index) { return list_.getByteString(index); } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index to set the value at. * @param value The list to set. * @return This builder for chaining. @@ -456,7 +456,7 @@ public Builder setList( return this; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param value The list to add. * @return This builder for chaining. */ @@ -470,7 +470,7 @@ public Builder addList( return this; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param values The list to add. * @return This builder for chaining. */ @@ -484,7 +484,7 @@ public Builder addAllList( return this; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return This builder for chaining. */ public Builder clearList() { @@ -495,7 +495,7 @@ public Builder clearList() { return this; } /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param value The bytes of the list to add. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java index fbc8bf9db..36007c638 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java @@ -11,24 +11,24 @@ public interface StringListOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return A list containing the list. */ java.util.List getListList(); /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @return The count of list. */ int getListCount(); /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index of the element to return. * @return The list at the given index. */ java.lang.String getList(int index); /** - * repeated string list = 1 [json_name = "list"]; + * repeated string list = 1; * @param index The index of the value to return. * @return The bytes of the list at the given index. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Task.java b/spec-grpc/src/main/java/io/a2a/grpc/Task.java index 74605278b..05a007f63 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Task.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Task.java @@ -63,7 +63,7 @@ private Task() { * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ @java.lang.Override @@ -84,7 +84,7 @@ public java.lang.String getId() { * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ @java.lang.Override @@ -111,7 +111,7 @@ public java.lang.String getId() { * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ @java.lang.Override @@ -133,7 +133,7 @@ public java.lang.String getContextId() { * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ @java.lang.Override @@ -158,7 +158,7 @@ public java.lang.String getContextId() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return Whether the status field is set. */ @java.lang.Override @@ -170,7 +170,7 @@ public boolean hasStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return The status. */ @java.lang.Override @@ -182,7 +182,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ @java.lang.Override public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { @@ -197,7 +197,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ @java.lang.Override public java.util.List getArtifactsList() { @@ -208,7 +208,7 @@ public java.util.List getArtifactsList() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ @java.lang.Override public java.util.List @@ -220,7 +220,7 @@ public java.util.List getArtifactsList() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ @java.lang.Override public int getArtifactsCount() { @@ -231,7 +231,7 @@ public int getArtifactsCount() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ @java.lang.Override public io.a2a.grpc.Artifact getArtifacts(int index) { @@ -242,7 +242,7 @@ public io.a2a.grpc.Artifact getArtifacts(int index) { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ @java.lang.Override public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( @@ -259,7 +259,7 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ @java.lang.Override public java.util.List getHistoryList() { @@ -271,7 +271,7 @@ public java.util.List getHistoryList() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ @java.lang.Override public java.util.List @@ -284,7 +284,7 @@ public java.util.List getHistoryList() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ @java.lang.Override public int getHistoryCount() { @@ -296,7 +296,7 @@ public int getHistoryCount() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ @java.lang.Override public io.a2a.grpc.Message getHistory(int index) { @@ -308,7 +308,7 @@ public io.a2a.grpc.Message getHistory(int index) { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ @java.lang.Override public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( @@ -324,7 +324,7 @@ public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ @java.lang.Override @@ -337,7 +337,7 @@ public boolean hasMetadata() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ @java.lang.Override @@ -350,7 +350,7 @@ public com.google.protobuf.Struct getMetadata() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -913,7 +913,7 @@ public Builder mergeFrom( * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ public java.lang.String getId() { @@ -933,7 +933,7 @@ public java.lang.String getId() { * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ public com.google.protobuf.ByteString @@ -954,7 +954,7 @@ public java.lang.String getId() { * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @param value The id to set. * @return This builder for chaining. */ @@ -971,7 +971,7 @@ public Builder setId( * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return This builder for chaining. */ public Builder clearId() { @@ -985,7 +985,7 @@ public Builder clearId() { * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @param value The bytes for id to set. * @return This builder for chaining. */ @@ -1006,7 +1006,7 @@ public Builder setIdBytes( * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ public java.lang.String getContextId() { @@ -1027,7 +1027,7 @@ public java.lang.String getContextId() { * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -1049,7 +1049,7 @@ public java.lang.String getContextId() { * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The contextId to set. * @return This builder for chaining. */ @@ -1067,7 +1067,7 @@ public Builder setContextId( * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return This builder for chaining. */ public Builder clearContextId() { @@ -1082,7 +1082,7 @@ public Builder clearContextId() { * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -1104,7 +1104,7 @@ public Builder setContextIdBytes( * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return Whether the status field is set. */ public boolean hasStatus() { @@ -1115,7 +1115,7 @@ public boolean hasStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return The status. */ public io.a2a.grpc.TaskStatus getStatus() { @@ -1130,7 +1130,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder setStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -1150,7 +1150,7 @@ public Builder setStatus(io.a2a.grpc.TaskStatus value) { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder setStatus( io.a2a.grpc.TaskStatus.Builder builderForValue) { @@ -1168,7 +1168,7 @@ public Builder setStatus( * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -1193,7 +1193,7 @@ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder clearStatus() { bitField0_ = (bitField0_ & ~0x00000004); @@ -1210,7 +1210,7 @@ public Builder clearStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { bitField0_ |= 0x00000004; @@ -1222,7 +1222,7 @@ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { if (statusBuilder_ != null) { @@ -1237,7 +1237,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> @@ -1270,7 +1270,7 @@ private void ensureArtifactsIsMutable() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public java.util.List getArtifactsList() { if (artifactsBuilder_ == null) { @@ -1284,7 +1284,7 @@ public java.util.List getArtifactsList() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public int getArtifactsCount() { if (artifactsBuilder_ == null) { @@ -1298,7 +1298,7 @@ public int getArtifactsCount() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public io.a2a.grpc.Artifact getArtifacts(int index) { if (artifactsBuilder_ == null) { @@ -1312,7 +1312,7 @@ public io.a2a.grpc.Artifact getArtifacts(int index) { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder setArtifacts( int index, io.a2a.grpc.Artifact value) { @@ -1333,7 +1333,7 @@ public Builder setArtifacts( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder setArtifacts( int index, io.a2a.grpc.Artifact.Builder builderForValue) { @@ -1351,7 +1351,7 @@ public Builder setArtifacts( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder addArtifacts(io.a2a.grpc.Artifact value) { if (artifactsBuilder_ == null) { @@ -1371,7 +1371,7 @@ public Builder addArtifacts(io.a2a.grpc.Artifact value) { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder addArtifacts( int index, io.a2a.grpc.Artifact value) { @@ -1392,7 +1392,7 @@ public Builder addArtifacts( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder addArtifacts( io.a2a.grpc.Artifact.Builder builderForValue) { @@ -1410,7 +1410,7 @@ public Builder addArtifacts( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder addArtifacts( int index, io.a2a.grpc.Artifact.Builder builderForValue) { @@ -1428,7 +1428,7 @@ public Builder addArtifacts( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder addAllArtifacts( java.lang.Iterable values) { @@ -1447,7 +1447,7 @@ public Builder addAllArtifacts( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder clearArtifacts() { if (artifactsBuilder_ == null) { @@ -1464,7 +1464,7 @@ public Builder clearArtifacts() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public Builder removeArtifacts(int index) { if (artifactsBuilder_ == null) { @@ -1481,7 +1481,7 @@ public Builder removeArtifacts(int index) { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public io.a2a.grpc.Artifact.Builder getArtifactsBuilder( int index) { @@ -1492,7 +1492,7 @@ public io.a2a.grpc.Artifact.Builder getArtifactsBuilder( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( int index) { @@ -1506,7 +1506,7 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public java.util.List getArtifactsOrBuilderList() { @@ -1521,7 +1521,7 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public io.a2a.grpc.Artifact.Builder addArtifactsBuilder() { return internalGetArtifactsFieldBuilder().addBuilder( @@ -1532,7 +1532,7 @@ public io.a2a.grpc.Artifact.Builder addArtifactsBuilder() { * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public io.a2a.grpc.Artifact.Builder addArtifactsBuilder( int index) { @@ -1544,7 +1544,7 @@ public io.a2a.grpc.Artifact.Builder addArtifactsBuilder( * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ public java.util.List getArtifactsBuilderList() { @@ -1583,7 +1583,7 @@ private void ensureHistoryIsMutable() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public java.util.List getHistoryList() { if (historyBuilder_ == null) { @@ -1598,7 +1598,7 @@ public java.util.List getHistoryList() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public int getHistoryCount() { if (historyBuilder_ == null) { @@ -1613,7 +1613,7 @@ public int getHistoryCount() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public io.a2a.grpc.Message getHistory(int index) { if (historyBuilder_ == null) { @@ -1628,7 +1628,7 @@ public io.a2a.grpc.Message getHistory(int index) { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder setHistory( int index, io.a2a.grpc.Message value) { @@ -1650,7 +1650,7 @@ public Builder setHistory( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder setHistory( int index, io.a2a.grpc.Message.Builder builderForValue) { @@ -1669,7 +1669,7 @@ public Builder setHistory( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder addHistory(io.a2a.grpc.Message value) { if (historyBuilder_ == null) { @@ -1690,7 +1690,7 @@ public Builder addHistory(io.a2a.grpc.Message value) { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder addHistory( int index, io.a2a.grpc.Message value) { @@ -1712,7 +1712,7 @@ public Builder addHistory( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder addHistory( io.a2a.grpc.Message.Builder builderForValue) { @@ -1731,7 +1731,7 @@ public Builder addHistory( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder addHistory( int index, io.a2a.grpc.Message.Builder builderForValue) { @@ -1750,7 +1750,7 @@ public Builder addHistory( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder addAllHistory( java.lang.Iterable values) { @@ -1770,7 +1770,7 @@ public Builder addAllHistory( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder clearHistory() { if (historyBuilder_ == null) { @@ -1788,7 +1788,7 @@ public Builder clearHistory() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public Builder removeHistory(int index) { if (historyBuilder_ == null) { @@ -1806,7 +1806,7 @@ public Builder removeHistory(int index) { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public io.a2a.grpc.Message.Builder getHistoryBuilder( int index) { @@ -1818,7 +1818,7 @@ public io.a2a.grpc.Message.Builder getHistoryBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( int index) { @@ -1833,7 +1833,7 @@ public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public java.util.List getHistoryOrBuilderList() { @@ -1849,7 +1849,7 @@ public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public io.a2a.grpc.Message.Builder addHistoryBuilder() { return internalGetHistoryFieldBuilder().addBuilder( @@ -1861,7 +1861,7 @@ public io.a2a.grpc.Message.Builder addHistoryBuilder() { * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public io.a2a.grpc.Message.Builder addHistoryBuilder( int index) { @@ -1874,7 +1874,7 @@ public io.a2a.grpc.Message.Builder addHistoryBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ public java.util.List getHistoryBuilderList() { @@ -1904,7 +1904,7 @@ public io.a2a.grpc.Message.Builder addHistoryBuilder( * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ public boolean hasMetadata() { @@ -1916,7 +1916,7 @@ public boolean hasMetadata() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -1932,7 +1932,7 @@ public com.google.protobuf.Struct getMetadata() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1953,7 +1953,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -1972,7 +1972,7 @@ public Builder setMetadata( * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1998,7 +1998,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000020); @@ -2016,7 +2016,7 @@ public Builder clearMetadata() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { bitField0_ |= 0x00000020; @@ -2029,7 +2029,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -2045,7 +2045,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java index 2019adc0c..379413bb4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java @@ -59,7 +59,7 @@ private TaskArtifactUpdateEvent() { * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The taskId. */ @java.lang.Override @@ -80,7 +80,7 @@ public java.lang.String getTaskId() { * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The bytes for taskId. */ @java.lang.Override @@ -106,7 +106,7 @@ public java.lang.String getTaskId() { * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ @java.lang.Override @@ -127,7 +127,7 @@ public java.lang.String getContextId() { * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ @java.lang.Override @@ -152,7 +152,7 @@ public java.lang.String getContextId() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; * @return Whether the artifact field is set. */ @java.lang.Override @@ -164,7 +164,7 @@ public boolean hasArtifact() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; * @return The artifact. */ @java.lang.Override @@ -176,7 +176,7 @@ public io.a2a.grpc.Artifact getArtifact() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ @java.lang.Override public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { @@ -190,7 +190,7 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { * Whether this should be appended to a prior one produced * * - * bool append = 4 [json_name = "append"]; + * bool append = 4; * @return The append. */ @java.lang.Override @@ -205,7 +205,7 @@ public boolean getAppend() { * Whether this represents the last part of an artifact * * - * bool last_chunk = 5 [json_name = "lastChunk"]; + * bool last_chunk = 5; * @return The lastChunk. */ @java.lang.Override @@ -220,7 +220,7 @@ public boolean getLastChunk() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ @java.lang.Override @@ -232,7 +232,7 @@ public boolean hasMetadata() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ @java.lang.Override @@ -244,7 +244,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -711,7 +711,7 @@ public Builder mergeFrom( * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The taskId. */ public java.lang.String getTaskId() { @@ -731,7 +731,7 @@ public java.lang.String getTaskId() { * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The bytes for taskId. */ public com.google.protobuf.ByteString @@ -752,7 +752,7 @@ public java.lang.String getTaskId() { * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @param value The taskId to set. * @return This builder for chaining. */ @@ -769,7 +769,7 @@ public Builder setTaskId( * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return This builder for chaining. */ public Builder clearTaskId() { @@ -783,7 +783,7 @@ public Builder clearTaskId() { * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @param value The bytes for taskId to set. * @return This builder for chaining. */ @@ -803,7 +803,7 @@ public Builder setTaskIdBytes( * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ public java.lang.String getContextId() { @@ -823,7 +823,7 @@ public java.lang.String getContextId() { * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -844,7 +844,7 @@ public java.lang.String getContextId() { * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The contextId to set. * @return This builder for chaining. */ @@ -861,7 +861,7 @@ public Builder setContextId( * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return This builder for chaining. */ public Builder clearContextId() { @@ -875,7 +875,7 @@ public Builder clearContextId() { * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -897,7 +897,7 @@ public Builder setContextIdBytes( * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; * @return Whether the artifact field is set. */ public boolean hasArtifact() { @@ -908,7 +908,7 @@ public boolean hasArtifact() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; * @return The artifact. */ public io.a2a.grpc.Artifact getArtifact() { @@ -923,7 +923,7 @@ public io.a2a.grpc.Artifact getArtifact() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ public Builder setArtifact(io.a2a.grpc.Artifact value) { if (artifactBuilder_ == null) { @@ -943,7 +943,7 @@ public Builder setArtifact(io.a2a.grpc.Artifact value) { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ public Builder setArtifact( io.a2a.grpc.Artifact.Builder builderForValue) { @@ -961,7 +961,7 @@ public Builder setArtifact( * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ public Builder mergeArtifact(io.a2a.grpc.Artifact value) { if (artifactBuilder_ == null) { @@ -986,7 +986,7 @@ public Builder mergeArtifact(io.a2a.grpc.Artifact value) { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ public Builder clearArtifact() { bitField0_ = (bitField0_ & ~0x00000004); @@ -1003,7 +1003,7 @@ public Builder clearArtifact() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ public io.a2a.grpc.Artifact.Builder getArtifactBuilder() { bitField0_ |= 0x00000004; @@ -1015,7 +1015,7 @@ public io.a2a.grpc.Artifact.Builder getArtifactBuilder() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { if (artifactBuilder_ != null) { @@ -1030,7 +1030,7 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> @@ -1052,7 +1052,7 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { * Whether this should be appended to a prior one produced * * - * bool append = 4 [json_name = "append"]; + * bool append = 4; * @return The append. */ @java.lang.Override @@ -1064,7 +1064,7 @@ public boolean getAppend() { * Whether this should be appended to a prior one produced * * - * bool append = 4 [json_name = "append"]; + * bool append = 4; * @param value The append to set. * @return This builder for chaining. */ @@ -1080,7 +1080,7 @@ public Builder setAppend(boolean value) { * Whether this should be appended to a prior one produced * * - * bool append = 4 [json_name = "append"]; + * bool append = 4; * @return This builder for chaining. */ public Builder clearAppend() { @@ -1096,7 +1096,7 @@ public Builder clearAppend() { * Whether this represents the last part of an artifact * * - * bool last_chunk = 5 [json_name = "lastChunk"]; + * bool last_chunk = 5; * @return The lastChunk. */ @java.lang.Override @@ -1108,7 +1108,7 @@ public boolean getLastChunk() { * Whether this represents the last part of an artifact * * - * bool last_chunk = 5 [json_name = "lastChunk"]; + * bool last_chunk = 5; * @param value The lastChunk to set. * @return This builder for chaining. */ @@ -1124,7 +1124,7 @@ public Builder setLastChunk(boolean value) { * Whether this represents the last part of an artifact * * - * bool last_chunk = 5 [json_name = "lastChunk"]; + * bool last_chunk = 5; * @return This builder for chaining. */ public Builder clearLastChunk() { @@ -1142,7 +1142,7 @@ public Builder clearLastChunk() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ public boolean hasMetadata() { @@ -1153,7 +1153,7 @@ public boolean hasMetadata() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -1168,7 +1168,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1188,7 +1188,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -1206,7 +1206,7 @@ public Builder setMetadata( * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1231,7 +1231,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000020); @@ -1248,7 +1248,7 @@ public Builder clearMetadata() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { bitField0_ |= 0x00000020; @@ -1260,7 +1260,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -1275,7 +1275,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java index 5e0c4c98f..8d208de11 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The taskId. */ java.lang.String getTaskId(); @@ -24,7 +24,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The id of the task for this artifact * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The bytes for taskId. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ java.lang.String getContextId(); @@ -44,7 +44,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The id of the context that this task belongs too * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -55,7 +55,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; * @return Whether the artifact field is set. */ boolean hasArtifact(); @@ -64,7 +64,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; * @return The artifact. */ io.a2a.grpc.Artifact getArtifact(); @@ -73,7 +73,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * The artifact itself * * - * .a2a.v1.Artifact artifact = 3 [json_name = "artifact"]; + * .a2a.v1.Artifact artifact = 3; */ io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder(); @@ -82,7 +82,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * Whether this should be appended to a prior one produced * * - * bool append = 4 [json_name = "append"]; + * bool append = 4; * @return The append. */ boolean getAppend(); @@ -92,7 +92,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * Whether this represents the last part of an artifact * * - * bool last_chunk = 5 [json_name = "lastChunk"]; + * bool last_chunk = 5; * @return The lastChunk. */ boolean getLastChunk(); @@ -102,7 +102,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ boolean hasMetadata(); @@ -111,7 +111,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); @@ -120,7 +120,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends * Optional metadata associated with the artifact update. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java index d4bcf9727..3bbe9e999 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskOrBuilder extends * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The id. */ java.lang.String getId(); @@ -24,7 +24,7 @@ public interface TaskOrBuilder extends * Unique identifier for a task, created by the A2A server. * * - * string id = 1 [json_name = "id"]; + * string id = 1; * @return The bytes for id. */ com.google.protobuf.ByteString @@ -36,7 +36,7 @@ public interface TaskOrBuilder extends * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ java.lang.String getContextId(); @@ -46,7 +46,7 @@ public interface TaskOrBuilder extends * and messages). Created by the A2A server. * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -57,7 +57,7 @@ public interface TaskOrBuilder extends * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return Whether the status field is set. */ boolean hasStatus(); @@ -66,7 +66,7 @@ public interface TaskOrBuilder extends * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return The status. */ io.a2a.grpc.TaskStatus getStatus(); @@ -75,7 +75,7 @@ public interface TaskOrBuilder extends * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder(); @@ -84,7 +84,7 @@ public interface TaskOrBuilder extends * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ java.util.List getArtifactsList(); @@ -93,7 +93,7 @@ public interface TaskOrBuilder extends * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ io.a2a.grpc.Artifact getArtifacts(int index); /** @@ -101,7 +101,7 @@ public interface TaskOrBuilder extends * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ int getArtifactsCount(); /** @@ -109,7 +109,7 @@ public interface TaskOrBuilder extends * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ java.util.List getArtifactsOrBuilderList(); @@ -118,7 +118,7 @@ public interface TaskOrBuilder extends * A set of output artifacts for a Task. * * - * repeated .a2a.v1.Artifact artifacts = 4 [json_name = "artifacts"]; + * repeated .a2a.v1.Artifact artifacts = 4; */ io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( int index); @@ -129,7 +129,7 @@ io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ java.util.List getHistoryList(); @@ -139,7 +139,7 @@ io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ io.a2a.grpc.Message getHistory(int index); /** @@ -148,7 +148,7 @@ io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ int getHistoryCount(); /** @@ -157,7 +157,7 @@ io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ java.util.List getHistoryOrBuilderList(); @@ -167,7 +167,7 @@ io.a2a.grpc.ArtifactOrBuilder getArtifactsOrBuilder( * The history of interactions from a task. * * - * repeated .a2a.v1.Message history = 5 [json_name = "history"]; + * repeated .a2a.v1.Message history = 5; */ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( int index); @@ -178,7 +178,7 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return Whether the metadata field is set. */ boolean hasMetadata(); @@ -188,7 +188,7 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); @@ -198,7 +198,7 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( * A key/value object to store custom metadata about a task. * * - * .google.protobuf.Struct metadata = 6 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 6; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java index 89d2fdd85..b7dabc6e8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java @@ -53,7 +53,7 @@ private TaskPushNotificationConfig() { * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ @java.lang.Override @@ -74,7 +74,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ @java.lang.Override @@ -95,7 +95,7 @@ public java.lang.String getName() { public static final int PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER = 2; private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return Whether the pushNotificationConfig field is set. */ @java.lang.Override @@ -103,7 +103,7 @@ public boolean hasPushNotificationConfig() { return ((bitField0_ & 0x00000001) != 0); } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return The pushNotificationConfig. */ @java.lang.Override @@ -111,7 +111,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ @java.lang.Override public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { @@ -463,7 +463,7 @@ public Builder mergeFrom( * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ public java.lang.String getName() { @@ -483,7 +483,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -504,7 +504,7 @@ public java.lang.String getName() { * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The name to set. * @return This builder for chaining. */ @@ -521,7 +521,7 @@ public Builder setName( * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return This builder for chaining. */ public Builder clearName() { @@ -535,7 +535,7 @@ public Builder clearName() { * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -553,14 +553,14 @@ public Builder setNameBytes( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationConfigBuilder_; /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return Whether the pushNotificationConfig field is set. */ public boolean hasPushNotificationConfig() { return ((bitField0_ & 0x00000002) != 0); } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return The pushNotificationConfig. */ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { @@ -571,7 +571,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { } } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { if (pushNotificationConfigBuilder_ == null) { @@ -587,7 +587,7 @@ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig valu return this; } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder setPushNotificationConfig( io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { @@ -601,7 +601,7 @@ public Builder setPushNotificationConfig( return this; } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { if (pushNotificationConfigBuilder_ == null) { @@ -622,7 +622,7 @@ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig va return this; } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder clearPushNotificationConfig() { bitField0_ = (bitField0_ & ~0x00000002); @@ -635,7 +635,7 @@ public Builder clearPushNotificationConfig() { return this; } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuilder() { bitField0_ |= 0x00000002; @@ -643,7 +643,7 @@ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuild return internalGetPushNotificationConfigFieldBuilder().getBuilder(); } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { if (pushNotificationConfigBuilder_ != null) { @@ -654,7 +654,7 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu } } /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java index 923cdb062..660399e6d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskPushNotificationConfigOrBuilder extends * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ java.lang.String getName(); @@ -24,24 +24,24 @@ public interface TaskPushNotificationConfigOrBuilder extends * name=tasks/{id}/pushNotificationConfigs/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString getNameBytes(); /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return Whether the pushNotificationConfig field is set. */ boolean hasPushNotificationConfig(); /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return The pushNotificationConfig. */ io.a2a.grpc.PushNotificationConfig getPushNotificationConfig(); /** - * .a2a.v1.PushNotificationConfig push_notification_config = 2 [json_name = "pushNotificationConfig"]; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java index 5ff2e72a7..684be6c4a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java @@ -56,7 +56,7 @@ private TaskStatus() { * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return The enum numeric value on the wire for state. */ @java.lang.Override public int getStateValue() { @@ -67,7 +67,7 @@ private TaskStatus() { * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return The state. */ @java.lang.Override public io.a2a.grpc.TaskState getState() { @@ -121,7 +121,7 @@ public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; * @return Whether the timestamp field is set. */ @java.lang.Override @@ -134,7 +134,7 @@ public boolean hasTimestamp() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; * @return The timestamp. */ @java.lang.Override @@ -147,7 +147,7 @@ public com.google.protobuf.Timestamp getTimestamp() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ @java.lang.Override public com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder() { @@ -539,7 +539,7 @@ public Builder mergeFrom( * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return The enum numeric value on the wire for state. */ @java.lang.Override public int getStateValue() { @@ -550,7 +550,7 @@ public Builder mergeFrom( * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @param value The enum numeric value on the wire for state to set. * @return This builder for chaining. */ @@ -565,7 +565,7 @@ public Builder setStateValue(int value) { * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return The state. */ @java.lang.Override @@ -578,7 +578,7 @@ public io.a2a.grpc.TaskState getState() { * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @param value The state to set. * @return This builder for chaining. */ @@ -594,7 +594,7 @@ public Builder setState(io.a2a.grpc.TaskState value) { * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return This builder for chaining. */ public Builder clearState() { @@ -770,7 +770,7 @@ public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; * @return Whether the timestamp field is set. */ public boolean hasTimestamp() { @@ -782,7 +782,7 @@ public boolean hasTimestamp() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; * @return The timestamp. */ public com.google.protobuf.Timestamp getTimestamp() { @@ -798,7 +798,7 @@ public com.google.protobuf.Timestamp getTimestamp() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ public Builder setTimestamp(com.google.protobuf.Timestamp value) { if (timestampBuilder_ == null) { @@ -819,7 +819,7 @@ public Builder setTimestamp(com.google.protobuf.Timestamp value) { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ public Builder setTimestamp( com.google.protobuf.Timestamp.Builder builderForValue) { @@ -838,7 +838,7 @@ public Builder setTimestamp( * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ public Builder mergeTimestamp(com.google.protobuf.Timestamp value) { if (timestampBuilder_ == null) { @@ -864,7 +864,7 @@ public Builder mergeTimestamp(com.google.protobuf.Timestamp value) { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ public Builder clearTimestamp() { bitField0_ = (bitField0_ & ~0x00000004); @@ -882,7 +882,7 @@ public Builder clearTimestamp() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ public com.google.protobuf.Timestamp.Builder getTimestampBuilder() { bitField0_ |= 0x00000004; @@ -895,7 +895,7 @@ public com.google.protobuf.Timestamp.Builder getTimestampBuilder() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ public com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder() { if (timestampBuilder_ != null) { @@ -911,7 +911,7 @@ public com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder() { * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java index 2fc574154..4f5a1670a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskStatusOrBuilder extends * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return The enum numeric value on the wire for state. */ int getStateValue(); @@ -24,7 +24,7 @@ public interface TaskStatusOrBuilder extends * The current state of this task * * - * .a2a.v1.TaskState state = 1 [json_name = "state"]; + * .a2a.v1.TaskState state = 1; * @return The state. */ io.a2a.grpc.TaskState getState(); @@ -62,7 +62,7 @@ public interface TaskStatusOrBuilder extends * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; * @return Whether the timestamp field is set. */ boolean hasTimestamp(); @@ -72,7 +72,7 @@ public interface TaskStatusOrBuilder extends * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; * @return The timestamp. */ com.google.protobuf.Timestamp getTimestamp(); @@ -82,7 +82,7 @@ public interface TaskStatusOrBuilder extends * Example: "2023-10-27T10:00:00Z" * * - * .google.protobuf.Timestamp timestamp = 3 [json_name = "timestamp"]; + * .google.protobuf.Timestamp timestamp = 3; */ com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java index 61cf2a157..72d6c57bc 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java @@ -59,7 +59,7 @@ private TaskStatusUpdateEvent() { * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The taskId. */ @java.lang.Override @@ -80,7 +80,7 @@ public java.lang.String getTaskId() { * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The bytes for taskId. */ @java.lang.Override @@ -106,7 +106,7 @@ public java.lang.String getTaskId() { * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ @java.lang.Override @@ -127,7 +127,7 @@ public java.lang.String getContextId() { * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ @java.lang.Override @@ -152,7 +152,7 @@ public java.lang.String getContextId() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return Whether the status field is set. */ @java.lang.Override @@ -164,7 +164,7 @@ public boolean hasStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return The status. */ @java.lang.Override @@ -176,7 +176,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ @java.lang.Override public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { @@ -190,7 +190,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * Whether this is the last status update expected for this task. * * - * bool final = 4 [json_name = "final"]; + * bool final = 4; * @return The final. */ @java.lang.Override @@ -205,7 +205,7 @@ public boolean getFinal() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; * @return Whether the metadata field is set. */ @java.lang.Override @@ -217,7 +217,7 @@ public boolean hasMetadata() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; * @return The metadata. */ @java.lang.Override @@ -229,7 +229,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -672,7 +672,7 @@ public Builder mergeFrom( * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The taskId. */ public java.lang.String getTaskId() { @@ -692,7 +692,7 @@ public java.lang.String getTaskId() { * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The bytes for taskId. */ public com.google.protobuf.ByteString @@ -713,7 +713,7 @@ public java.lang.String getTaskId() { * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @param value The taskId to set. * @return This builder for chaining. */ @@ -730,7 +730,7 @@ public Builder setTaskId( * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return This builder for chaining. */ public Builder clearTaskId() { @@ -744,7 +744,7 @@ public Builder clearTaskId() { * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @param value The bytes for taskId to set. * @return This builder for chaining. */ @@ -764,7 +764,7 @@ public Builder setTaskIdBytes( * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ public java.lang.String getContextId() { @@ -784,7 +784,7 @@ public java.lang.String getContextId() { * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -805,7 +805,7 @@ public java.lang.String getContextId() { * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The contextId to set. * @return This builder for chaining. */ @@ -822,7 +822,7 @@ public Builder setContextId( * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return This builder for chaining. */ public Builder clearContextId() { @@ -836,7 +836,7 @@ public Builder clearContextId() { * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -858,7 +858,7 @@ public Builder setContextIdBytes( * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return Whether the status field is set. */ public boolean hasStatus() { @@ -869,7 +869,7 @@ public boolean hasStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return The status. */ public io.a2a.grpc.TaskStatus getStatus() { @@ -884,7 +884,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder setStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -904,7 +904,7 @@ public Builder setStatus(io.a2a.grpc.TaskStatus value) { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder setStatus( io.a2a.grpc.TaskStatus.Builder builderForValue) { @@ -922,7 +922,7 @@ public Builder setStatus( * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -947,7 +947,7 @@ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public Builder clearStatus() { bitField0_ = (bitField0_ & ~0x00000004); @@ -964,7 +964,7 @@ public Builder clearStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { bitField0_ |= 0x00000004; @@ -976,7 +976,7 @@ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { if (statusBuilder_ != null) { @@ -991,7 +991,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> @@ -1013,7 +1013,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * Whether this is the last status update expected for this task. * * - * bool final = 4 [json_name = "final"]; + * bool final = 4; * @return The final. */ @java.lang.Override @@ -1025,7 +1025,7 @@ public boolean getFinal() { * Whether this is the last status update expected for this task. * * - * bool final = 4 [json_name = "final"]; + * bool final = 4; * @param value The final to set. * @return This builder for chaining. */ @@ -1041,7 +1041,7 @@ public Builder setFinal(boolean value) { * Whether this is the last status update expected for this task. * * - * bool final = 4 [json_name = "final"]; + * bool final = 4; * @return This builder for chaining. */ public Builder clearFinal() { @@ -1059,7 +1059,7 @@ public Builder clearFinal() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; * @return Whether the metadata field is set. */ public boolean hasMetadata() { @@ -1070,7 +1070,7 @@ public boolean hasMetadata() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -1085,7 +1085,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1105,7 +1105,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -1123,7 +1123,7 @@ public Builder setMetadata( * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1148,7 +1148,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000010); @@ -1165,7 +1165,7 @@ public Builder clearMetadata() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { bitField0_ |= 0x00000010; @@ -1177,7 +1177,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -1192,7 +1192,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java index 42fd0d67f..264b19295 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The taskId. */ java.lang.String getTaskId(); @@ -24,7 +24,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the task that is changed * * - * string task_id = 1 [json_name = "taskId"]; + * string task_id = 1; * @return The bytes for taskId. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The contextId. */ java.lang.String getContextId(); @@ -44,7 +44,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the context that the task belongs to * * - * string context_id = 2 [json_name = "contextId"]; + * string context_id = 2; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -55,7 +55,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return Whether the status field is set. */ boolean hasStatus(); @@ -64,7 +64,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; * @return The status. */ io.a2a.grpc.TaskStatus getStatus(); @@ -73,7 +73,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3 [json_name = "status"]; + * .a2a.v1.TaskStatus status = 3; */ io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder(); @@ -82,7 +82,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * Whether this is the last status update expected for this task. * * - * bool final = 4 [json_name = "final"]; + * bool final = 4; * @return The final. */ boolean getFinal(); @@ -92,7 +92,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; * @return Whether the metadata field is set. */ boolean hasMetadata(); @@ -101,7 +101,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); @@ -110,7 +110,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * Optional metadata to associate with the task update. * * - * .google.protobuf.Struct metadata = 5 [json_name = "metadata"]; + * .google.protobuf.Struct metadata = 5; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java index 5f4020608..fb5317c33 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java @@ -52,7 +52,7 @@ private TaskSubscriptionRequest() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ @java.lang.Override @@ -73,7 +73,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ @java.lang.Override @@ -391,7 +391,7 @@ public Builder mergeFrom( * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ public java.lang.String getName() { @@ -411,7 +411,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -432,7 +432,7 @@ public java.lang.String getName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The name to set. * @return This builder for chaining. */ @@ -449,7 +449,7 @@ public Builder setName( * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return This builder for chaining. */ public Builder clearName() { @@ -463,7 +463,7 @@ public Builder clearName() { * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @param value The bytes for name to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java index da8c0e621..db4c6f9a7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskSubscriptionRequestOrBuilder extends * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The name. */ java.lang.String getName(); @@ -24,7 +24,7 @@ public interface TaskSubscriptionRequestOrBuilder extends * name=tasks/{id} * * - * string name = 1 [json_name = "name"]; + * string name = 1; * @return The bytes for name. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto new file mode 100644 index 000000000..8bada0795 --- /dev/null +++ b/spec-grpc/src/main/proto/a2a.proto @@ -0,0 +1,674 @@ +// Older protoc compilers don't understand edition yet. +syntax = "proto3"; +package a2a.v1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; + +// Copied from https://github.com/a2aproject/A2A/blob/v0.2.6/specification/grpc/a2a.proto +// The only change is the 'java_package' update + +option csharp_namespace = "A2a.V1"; +option go_package = "google.golang.org/a2a/v1"; +option java_multiple_files = true; +option java_outer_classname = "A2A"; +// Update package from the spec version +option java_package = "io.a2a.grpc"; + +// A2AService defines the gRPC version of the A2A protocol. This has a slightly +// different shape than the JSONRPC version to better conform to AIP-127, +// where appropriate. The nouns are AgentCard, Message, Task and +// TaskPushNotificationConfig. +// - Messages are not a standard resource so there is no get/delete/update/list +// interface, only a send and stream custom methods. +// - Tasks have a get interface and custom cancel and subscribe methods. +// - TaskPushNotificationConfig are a resource whose parent is a task. +// They have get, list and create methods. +// - AgentCard is a static resource with only a get method. +// fields are not present as they don't comply with AIP rules, and the +// optional history_length on the get task method is not present as it also +// violates AIP-127 and AIP-131. +service A2AService { + // Send a message to the agent. This is a blocking call that will return the + // task once it is completed, or a LRO if requested. + rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { + option (google.api.http) = { + post: "/v1/message:send" + body: "*" + }; + } + // SendStreamingMessage is a streaming call that will return a stream of + // task update events until the Task is in an interrupted or terminal state. + rpc SendStreamingMessage(SendMessageRequest) returns (stream StreamResponse) { + option (google.api.http) = { + post: "/v1/message:stream" + body: "*" + }; + } + + // Get the current state of a task from the agent. + rpc GetTask(GetTaskRequest) returns (Task) { + option (google.api.http) = { + get: "/v1/{name=tasks/*}" + }; + option (google.api.method_signature) = "name"; + } + // Cancel a task from the agent. If supported one should expect no + // more task updates for the task. + rpc CancelTask(CancelTaskRequest) returns (Task) { + option (google.api.http) = { + post: "/v1/{name=tasks/*}:cancel" + body: "*" + }; + } + // TaskSubscription is a streaming call that will return a stream of task + // update events. This attaches the stream to an existing in process task. + // If the task is complete the stream will return the completed task (like + // GetTask) and close the stream. + rpc TaskSubscription(TaskSubscriptionRequest) + returns (stream StreamResponse) { + option (google.api.http) = { + get: "/v1/{name=tasks/*}:subscribe" + }; + } + + // Set a push notification config for a task. + rpc CreateTaskPushNotificationConfig(CreateTaskPushNotificationConfigRequest) + returns (TaskPushNotificationConfig) { + option (google.api.http) = { + post: "/v1/{parent=task/*/pushNotificationConfigs}" + body: "config" + }; + option (google.api.method_signature) = "parent,config"; + } + // Get a push notification config for a task. + rpc GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest) + returns (TaskPushNotificationConfig) { + option (google.api.http) = { + get: "/v1/{name=tasks/*/pushNotificationConfigs/*}" + }; + option (google.api.method_signature) = "name"; + } + // Get a list of push notifications configured for a task. + rpc ListTaskPushNotificationConfig(ListTaskPushNotificationConfigRequest) + returns (ListTaskPushNotificationConfigResponse) { + option (google.api.http) = { + get: "/v1/{parent=tasks/*}/pushNotificationConfigs" + }; + option (google.api.method_signature) = "parent"; + } + // GetAgentCard returns the agent card for the agent. + rpc GetAgentCard(GetAgentCardRequest) returns (AgentCard) { + option (google.api.http) = { + get: "/v1/card" + }; + } + // Delete a push notification config for a task. + rpc DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest) + returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/{name=tasks/*/pushNotificationConfigs/*}" + }; + option (google.api.method_signature) = "name"; + } +} + +///////// Data Model //////////// + +// Configuration of a send message request. +message SendMessageConfiguration { + // The output modes that the agent is expected to respond with. + repeated string accepted_output_modes = 1; + // A configuration of a webhook that can be used to receive updates + PushNotificationConfig push_notification = 2; + // The maximum number of messages to include in the history. if 0, the + // history will be unlimited. + int32 history_length = 3; + // If true, the message will be blocking until the task is completed. If + // false, the message will be non-blocking and the task will be returned + // immediately. It is the caller's responsibility to check for any task + // updates. + bool blocking = 4; +} + +// Task is the core unit of action for A2A. It has a current status +// and when results are created for the task they are stored in the +// artifact. If there are multiple turns for a task, these are stored in +// history. +message Task { + // Unique identifier for a task, created by the A2A server. + string id = 1; + // Unique identifier for the contextual collection of interactions (tasks + // and messages). Created by the A2A server. + string context_id = 2; + // The current status of a Task, including state and a message. + TaskStatus status = 3; + // A set of output artifacts for a Task. + repeated Artifact artifacts = 4; + // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED + // The history of interactions from a task. + repeated Message history = 5; + // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED + // A key/value object to store custom metadata about a task. + google.protobuf.Struct metadata = 6; +} + +// The set of states a Task can be in. +enum TaskState { + TASK_STATE_UNSPECIFIED = 0; + // Represents the status that acknowledges a task is created + TASK_STATE_SUBMITTED = 1; + // Represents the status that a task is actively being processed + TASK_STATE_WORKING = 2; + // Represents the status a task is finished. This is a terminal state + TASK_STATE_COMPLETED = 3; + // Represents the status a task is done but failed. This is a terminal state + TASK_STATE_FAILED = 4; + // Represents the status a task was cancelled before it finished. + // This is a terminal state. + TASK_STATE_CANCELLED = 5; + // Represents the status that the task requires information to complete. + // This is an interrupted state. + TASK_STATE_INPUT_REQUIRED = 6; + // Represents the status that the agent has decided to not perform the task. + // This may be done during initial task creation or later once an agent + // has determined it can't or won't proceed. This is a terminal state. + TASK_STATE_REJECTED = 7; + // Represents the state that some authentication is needed from the upstream + // client. Authentication is expected to come out-of-band thus this is not + // an interrupted or terminal state. + TASK_STATE_AUTH_REQUIRED = 8; +} + +// A container for the status of a task +message TaskStatus { + // The current state of this task + TaskState state = 1; + // A message associated with the status. + Message update = 2 [json_name = "message"]; + // Timestamp when the status was recorded. + // Example: "2023-10-27T10:00:00Z" + google.protobuf.Timestamp timestamp = 3; +} + +// Part represents a container for a section of communication content. +// Parts can be purely textual, some sort of file (image, video, etc) or +// a structured data blob (i.e. JSON). +message Part { + oneof part { + string text = 1; + FilePart file = 2; + DataPart data = 3; + } +} + +// FilePart represents the different ways files can be provided. If files are +// small, directly feeding the bytes is supported via file_with_bytes. If the +// file is large, the agent should read the content as appropriate directly +// from the file_with_uri source. +message FilePart { + oneof file { + string file_with_uri = 1; + bytes file_with_bytes = 2; + } + string mime_type = 3; +} + +// DataPart represents a structured blob. This is most commonly a JSON payload. +message DataPart { + google.protobuf.Struct data = 1; +} + +enum Role { + ROLE_UNSPECIFIED = 0; + // USER role refers to communication from the client to the server. + ROLE_USER = 1; + // AGENT role refers to communication from the server to the client. + ROLE_AGENT = 2; +} + +// Message is one unit of communication between client and server. It is +// associated with a context and optionally a task. Since the server is +// responsible for the context definition, it must always provide a context_id +// in its messages. The client can optionally provide the context_id if it +// knows the context to associate the message to. Similarly for task_id, +// except the server decides if a task is created and whether to include the +// task_id. +message Message { + // The message id of the message. This is required and created by the + // message creator. + string message_id = 1; + // The context id of the message. This is optional and if set, the message + // will be associated with the given context. + string context_id = 2; + // The task id of the message. This is optional and if set, the message + // will be associated with the given task. + string task_id = 3; + // A role for the message. + Role role = 4; + // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED + // Content is the container of the message content. + repeated Part content = 5; + // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED + // Any optional metadata to provide along with the message. + google.protobuf.Struct metadata = 6; + // The URIs of extensions that are present or contributed to this Message. + repeated string extensions = 7; +} + +// Artifacts are the container for task completed results. These are similar +// to Messages but are intended to be the product of a task, as opposed to +// point-to-point communication. +message Artifact { + // Unique id for the artifact. It must be at least unique within a task. + string artifact_id = 1; + // A human readable name for the artifact. + string name = 3; + // A human readable description of the artifact, optional. + string description = 4; + // The content of the artifact. + repeated Part parts = 5; + // Optional metadata included with the artifact. + google.protobuf.Struct metadata = 6; + // The URIs of extensions that are present or contributed to this Artifact. + repeated string extensions = 7; +} + +// TaskStatusUpdateEvent is a delta even on a task indicating that a task +// has changed. +message TaskStatusUpdateEvent { + // The id of the task that is changed + string task_id = 1; + // The id of the context that the task belongs to + string context_id = 2; + // The new status of the task. + TaskStatus status = 3; + // Whether this is the last status update expected for this task. + bool final = 4; + // Optional metadata to associate with the task update. + google.protobuf.Struct metadata = 5; +} + +// TaskArtifactUpdateEvent represents a task delta where an artifact has +// been generated. +message TaskArtifactUpdateEvent { + // The id of the task for this artifact + string task_id = 1; + // The id of the context that this task belongs too + string context_id = 2; + // The artifact itself + Artifact artifact = 3; + // Whether this should be appended to a prior one produced + bool append = 4; + // Whether this represents the last part of an artifact + bool last_chunk = 5; + // Optional metadata associated with the artifact update. + google.protobuf.Struct metadata = 6; +} + +// Configuration for setting up push notifications for task updates. +message PushNotificationConfig { + // A unique id for this push notification. + string id = 1; + // Url to send the notification too + string url = 2; + // Token unique for this task/session + string token = 3; + // Information about the authentication to sent with the notification + AuthenticationInfo authentication = 4; +} + +// Defines authentication details, used for push notifications. +message AuthenticationInfo { + // Supported authentication schemes - e.g. Basic, Bearer, etc + repeated string schemes = 1; + // Optional credentials + string credentials = 2; +} + +// Defines additional transport information for the agent. +message AgentInterface { + // The url this interface is found at. + string url = 1; + // The transport supported this url. This is an open form string, to be + // easily extended for many transport protocols. The core ones officially + // supported are JSONRPC, GRPC and HTTP+JSON. + string transport = 2; +} + +// AgentCard conveys key information: +// - Overall details (version, name, description, uses) +// - Skills; a set of actions/solutions the agent can perform +// - Default modalities/content types supported by the agent. +// - Authentication requirements +message AgentCard { + // The version of the A2A protocol this agent supports. + string protocol_version = 16; + // A human readable name for the agent. + // Example: "Recipe Agent" + string name = 1; + // A description of the agent's domain of action/solution space. + // Example: "Agent that helps users with recipes and cooking." + string description = 2; + // A URL to the address the agent is hosted at. This represents the + // preferred endpoint as declared by the agent. + string url = 3; + // The transport of the preferred endpoint. If empty, defaults to JSONRPC. + string preferred_transport = 14; + // Announcement of additional supported transports. Client can use any of + // the supported transports. + repeated AgentInterface additional_interfaces = 15; + // The service provider of the agent. + AgentProvider provider = 4; + // The version of the agent. + // Example: "1.0.0" + string version = 5; + // A url to provide additional documentation about the agent. + string documentation_url = 6; + // A2A Capability set supported by the agent. + AgentCapabilities capabilities = 7; + // The security scheme details used for authenticating with this agent. + map security_schemes = 8; + // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED + // Security requirements for contacting the agent. + repeated Security security = 9; + // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED + // The set of interaction modes that the agent supports across all skills. + // This can be overridden per skill. Defined as mime types. + repeated string default_input_modes = 10; + // The mime types supported as outputs from this agent. + repeated string default_output_modes = 11; + // Skills represent a unit of ability an agent can perform. This may + // somewhat abstract but represents a more focused set of actions that the + // agent is highly likely to succeed at. + repeated AgentSkill skills = 12; + // Whether the agent supports providing an extended agent card when + // the user is authenticated, i.e. is the card from .well-known + // different than the card from GetAgentCard. + bool supports_authenticated_extended_card = 13; +} + +// Represents information about the service provider of an agent. +message AgentProvider { + // The providers reference url + // Example: "https://ai.google.dev" + string url = 1; + // The providers organization name + // Example: "Google" + string organization = 2; +} + +// Defines the A2A feature set supported by the agent +message AgentCapabilities { + // If the agent will support streaming responses + bool streaming = 1; + // If the agent can send push notifications to the clients webhook + bool push_notifications = 2; + // Extensions supported by this agent. + repeated AgentExtension extensions = 3; +} + +// A declaration of an extension supported by an Agent. +message AgentExtension { + // The URI of the extension. + // Example: "https://developers.google.com/identity/protocols/oauth2" + string uri = 1; + // A description of how this agent uses this extension. + // Example: "Google OAuth 2.0 authentication" + string description = 2; + // Whether the client must follow specific requirements of the extension. + // Example: false + bool required = 3; + // Optional configuration for the extension. + google.protobuf.Struct params = 4; +} + +// AgentSkill represents a unit of action/solution that the agent can perform. +// One can think of this as a type of highly reliable solution that an agent +// can be tasked to provide. Agents have the autonomy to choose how and when +// to use specific skills, but clients should have confidence that if the +// skill is defined that unit of action can be reliably performed. +message AgentSkill { + // Unique id of the skill within this agent. + string id = 1; + // A human readable name for the skill. + string name = 2; + // A human (or llm) readable description of the skill + // details and behaviors. + string description = 3; + // A set of tags for the skill to enhance categorization/utilization. + // Example: ["cooking", "customer support", "billing"] + repeated string tags = 4; + // A set of example queries that this skill is designed to address. + // These examples should help the caller to understand how to craft requests + // to the agent to achieve specific goals. + // Example: ["I need a recipe for bread"] + repeated string examples = 5; + // Possible input modalities supported. + repeated string input_modes = 6; + // Possible output modalities produced + repeated string output_modes = 7; +} + +message TaskPushNotificationConfig { + // name=tasks/{id}/pushNotificationConfigs/{id} + string name = 1; + PushNotificationConfig push_notification_config = 2; +} + +// protolint:disable REPEATED_FIELD_NAMES_PLURALIZED +message StringList { + repeated string list = 1; +} +// protolint:enable REPEATED_FIELD_NAMES_PLURALIZED + +message Security { + map schemes = 1; +} + +message SecurityScheme { + oneof scheme { + APIKeySecurityScheme api_key_security_scheme = 1; + HTTPAuthSecurityScheme http_auth_security_scheme = 2; + OAuth2SecurityScheme oauth2_security_scheme = 3; + OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; + } +} + +message APIKeySecurityScheme { + // Description of this security scheme. + string description = 1; + // Location of the API key, valid values are "query", "header", or "cookie" + string location = 2; + // Name of the header, query or cookie parameter to be used. + string name = 3; +} + +message HTTPAuthSecurityScheme { + // Description of this security scheme. + string description = 1; + // The name of the HTTP Authentication scheme to be used in the + // Authorization header as defined in RFC7235. The values used SHOULD be + // registered in the IANA Authentication Scheme registry. + // The value is case-insensitive, as defined in RFC7235. + string scheme = 2; + // A hint to the client to identify how the bearer token is formatted. + // Bearer tokens are usually generated by an authorization server, so + // this information is primarily for documentation purposes. + string bearer_format = 3; +} + +message OAuth2SecurityScheme { + // Description of this security scheme. + string description = 1; + // An object containing configuration information for the flow types supported + OAuthFlows flows = 2; +} + +message OpenIdConnectSecurityScheme { + // Description of this security scheme. + string description = 1; + // Well-known URL to discover the [[OpenID-Connect-Discovery]] provider + // metadata. + string open_id_connect_url = 2; +} + +message OAuthFlows { + oneof flow { + AuthorizationCodeOAuthFlow authorization_code = 1; + ClientCredentialsOAuthFlow client_credentials = 2; + ImplicitOAuthFlow implicit = 3; + PasswordOAuthFlow password = 4; + } +} + +message AuthorizationCodeOAuthFlow { + // The authorization URL to be used for this flow. This MUST be in the + // form of a URL. The OAuth2 standard requires the use of TLS + string authorization_url = 1; + // The token URL to be used for this flow. This MUST be in the form of a URL. + // The OAuth2 standard requires the use of TLS. + string token_url = 2; + // The URL to be used for obtaining refresh tokens. This MUST be in the + // form of a URL. The OAuth2 standard requires the use of TLS. + string refresh_url = 3; + // The available scopes for the OAuth2 security scheme. A map between the + // scope name and a short description for it. The map MAY be empty. + map scopes = 4; +} + +message ClientCredentialsOAuthFlow { + // The token URL to be used for this flow. This MUST be in the form of a URL. + // The OAuth2 standard requires the use of TLS. + string token_url = 1; + // The URL to be used for obtaining refresh tokens. This MUST be in the + // form of a URL. The OAuth2 standard requires the use of TLS. + string refresh_url = 2; + // The available scopes for the OAuth2 security scheme. A map between the + // scope name and a short description for it. The map MAY be empty. + map scopes = 3; +} + +message ImplicitOAuthFlow { + // The authorization URL to be used for this flow. This MUST be in the + // form of a URL. The OAuth2 standard requires the use of TLS + string authorization_url = 1; + // The URL to be used for obtaining refresh tokens. This MUST be in the + // form of a URL. The OAuth2 standard requires the use of TLS. + string refresh_url = 2; + // The available scopes for the OAuth2 security scheme. A map between the + // scope name and a short description for it. The map MAY be empty. + map scopes = 3; +} + +message PasswordOAuthFlow { + // The token URL to be used for this flow. This MUST be in the form of a URL. + // The OAuth2 standard requires the use of TLS. + string token_url = 1; + // The URL to be used for obtaining refresh tokens. This MUST be in the + // form of a URL. The OAuth2 standard requires the use of TLS. + string refresh_url = 2; + // The available scopes for the OAuth2 security scheme. A map between the + // scope name and a short description for it. The map MAY be empty. + map scopes = 3; +} + +///////////// Request Messages /////////// +message SendMessageRequest { + Message request = 1 [(google.api.field_behavior) = REQUIRED]; + SendMessageConfiguration configuration = 2; + google.protobuf.Struct metadata = 3; +} + +message GetTaskRequest { + // name=tasks/{id} + string name = 1 [(google.api.field_behavior) = REQUIRED]; + int32 history_length = 2; +} + +message CancelTaskRequest { + // name=tasks/{id} + string name = 1; +} + +message GetTaskPushNotificationConfigRequest { + // name=tasks/{id}/pushNotificationConfigs/{push_id} + string name = 1; +} + +message DeleteTaskPushNotificationConfigRequest { + // name=tasks/{id}/pushNotificationConfigs/{push_id} + string name = 1; +} + +message CreateTaskPushNotificationConfigRequest { + // The task resource for this config. + // Format: tasks/{id} + string parent = 1 [(google.api.field_behavior) = REQUIRED]; + string config_id = 2 [(google.api.field_behavior) = REQUIRED]; + TaskPushNotificationConfig config = 3 + [(google.api.field_behavior) = REQUIRED]; +} + +message TaskSubscriptionRequest { + // name=tasks/{id} + string name = 1; +} + +message ListTaskPushNotificationConfigRequest { + // parent=tasks/{id} + string parent = 1; + // For AIP-158 these fields are present. Usually not used/needed. + // The maximum number of configurations to return. + // If unspecified, all configs will be returned. + int32 page_size = 2; + + // A page token received from a previous + // ListTaskPushNotificationConfigRequest call. + // Provide this to retrieve the subsequent page. + // When paginating, all other parameters provided to + // `ListTaskPushNotificationConfigRequest` must match the call that provided + // the page token. + string page_token = 3; +} + +message GetAgentCardRequest { + // Empty. Added to fix linter violation. +} + +//////// Response Messages /////////// +message SendMessageResponse { + oneof payload { + Task task = 1; + Message msg = 2 [json_name = "message"]; + } +} + +// The stream response for a message. The stream should be one of the following +// sequences: +// If the response is a message, the stream should contain one, and only one, +// message and then close +// If the response is a task lifecycle, the first response should be a Task +// object followed by zero or more TaskStatusUpdateEvents and +// TaskArtifactUpdateEvents. The stream should complete when the Task +// if in an interrupted or terminal state. A stream that ends before these +// conditions are met are +message StreamResponse { + oneof payload { + Task task = 1; + Message msg = 2 [json_name = "message"]; + TaskStatusUpdateEvent status_update = 3; + TaskArtifactUpdateEvent artifact_update = 4; + } +} + +message ListTaskPushNotificationConfigResponse { + repeated TaskPushNotificationConfig configs = 1; + // A token, which can be sent as `page_token` to retrieve the next page. + // If this field is omitted, there are no subsequent pages. + string next_page_token = 2; +} diff --git a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java index 75956f8b8..6e269026b 100644 --- a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java @@ -1,10 +1,6 @@ package io.a2a.grpc.handler; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import java.util.ArrayList; import java.util.List; @@ -13,7 +9,6 @@ import com.google.protobuf.Empty; import com.google.protobuf.Struct; - import io.a2a.grpc.AuthenticationInfo; import io.a2a.grpc.CancelTaskRequest; import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; @@ -53,7 +48,6 @@ import io.grpc.internal.testing.StreamRecorder; import io.grpc.stub.StreamObserver; import mutiny.zero.ZeroPublisher; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; From dd29e9b50535818b973158c345cef78f52e00719 Mon Sep 17 00:00:00 2001 From: Holt Skinner <13262395+holtskinner@users.noreply.github.com> Date: Fri, 8 Aug 2025 12:39:23 +0100 Subject: [PATCH 060/493] ci: Convert Conventional Commits and Release Please to GitHub Actions (#218) Google GitHub Apps will be shut down --- .github/conventional-commit-lint.yaml | 2 -- .github/release-please.yml | 4 ---- .github/release-trigger.yml | 1 - .github/workflows/conventional-commits.yml | 26 ++++++++++++++++++++++ .github/workflows/release-please.yml | 19 ++++++++++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) delete mode 100644 .github/conventional-commit-lint.yaml delete mode 100644 .github/release-please.yml delete mode 100644 .github/release-trigger.yml create mode 100644 .github/workflows/conventional-commits.yml create mode 100644 .github/workflows/release-please.yml diff --git a/.github/conventional-commit-lint.yaml b/.github/conventional-commit-lint.yaml deleted file mode 100644 index c967ffa6f..000000000 --- a/.github/conventional-commit-lint.yaml +++ /dev/null @@ -1,2 +0,0 @@ -enabled: true -always_check_pr_title: true diff --git a/.github/release-please.yml b/.github/release-please.yml deleted file mode 100644 index f411bac3e..000000000 --- a/.github/release-please.yml +++ /dev/null @@ -1,4 +0,0 @@ -releaseType: maven -handleGHRelease: true -bumpMinorPreMajor: false -bumpPatchForMinorPreMajor: true diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml deleted file mode 100644 index d4ca94189..000000000 --- a/.github/release-trigger.yml +++ /dev/null @@ -1 +0,0 @@ -enabled: true diff --git a/.github/workflows/conventional-commits.yml b/.github/workflows/conventional-commits.yml new file mode 100644 index 000000000..d23da45d7 --- /dev/null +++ b/.github/workflows/conventional-commits.yml @@ -0,0 +1,26 @@ +name: "Conventional Commits" + +on: + pull_request: + types: + - opened + - edited + - synchronize + +permissions: + contents: read + +jobs: + main: + permissions: + pull-requests: read + statuses: write + name: Validate PR Title + runs-on: ubuntu-latest + steps: + - name: semantic-pull-request + uses: amannn/action-semantic-pull-request@v5.5.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + validateSingleCommit: false diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 000000000..a38b0f7be --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,19 @@ +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +name: release-please + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.A2A_BOT_PAT }} + release-type: java From 81b4a6baeea53a618fda1375e8b1e3874079dfa2 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 8 Aug 2025 16:11:49 +0200 Subject: [PATCH 061/493] feat: Refactor GrpcHandler to not use CDI and be more a utility (#219) --- .../grpc/quarkus/QuarkusGrpcHandler.java | 31 ++++++- .../src/test/resources/application.properties | 5 +- .../server/apps/common/AgentCardProducer.java | 3 +- .../java/io/a2a/grpc/handler/GrpcHandler.java | 67 ++++++-------- .../io/a2a/grpc/handler/GrpcHandlerTest.java | 87 ++++++++++++------- 5 files changed, 115 insertions(+), 78 deletions(-) diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 40abbc95c..4a98ace48 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -1,9 +1,11 @@ package io.a2a.server.grpc.quarkus; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import io.a2a.server.PublicAgentCard; import io.a2a.grpc.handler.GrpcHandler; +import io.a2a.server.PublicAgentCard; +import io.a2a.server.requesthandlers.CallContextFactory; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; import io.quarkus.grpc.GrpcService; @@ -11,8 +13,31 @@ @GrpcService public class QuarkusGrpcHandler extends GrpcHandler { + private final AgentCard agentCard; + private final RequestHandler requestHandler; + private final Instance callContextFactoryInstance; + @Inject - public QuarkusGrpcHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { - super(agentCard, requestHandler); + public QuarkusGrpcHandler(@PublicAgentCard AgentCard agentCard, + RequestHandler requestHandler, + Instance callContextFactoryInstance) { + this.agentCard = agentCard; + this.requestHandler = requestHandler; + this.callContextFactoryInstance = callContextFactoryInstance; + } + + @Override + protected RequestHandler getRequestHandler() { + return requestHandler; + } + + @Override + protected AgentCard getAgentCard() { + return agentCard; + } + + @Override + protected CallContextFactory getCallContextFactory() { + return callContextFactoryInstance.isUnsatisfied() ? null : callContextFactoryInstance.get(); } } \ No newline at end of file diff --git a/reference/grpc/src/test/resources/application.properties b/reference/grpc/src/test/resources/application.properties index 840386426..2ddb0ae2a 100644 --- a/reference/grpc/src/test/resources/application.properties +++ b/reference/grpc/src/test/resources/application.properties @@ -1,5 +1,2 @@ quarkus.grpc.clients.a2a-service.host=localhost -quarkus.grpc.clients.a2a-service.port=9001 - -# The GrpcHandler @ApplicationScoped annotation is not compatible with Quarkus -quarkus.arc.exclude-types=io.a2a.grpc.handler.GrpcHandler \ No newline at end of file +quarkus.grpc.clients.a2a-service.port=9001 \ No newline at end of file diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index 3354c1522..7a50b7697 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -18,10 +18,11 @@ public class AgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { + String port = System.getProperty("test.agent.card.port", "8081"); return new AgentCard.Builder() .name("test-card") .description("A test agent card") - .url("http://localhost:8081") + .url("http://localhost:" + port) .version("1.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() diff --git a/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java index 68311009f..15aa84209 100644 --- a/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java @@ -3,18 +3,15 @@ import static io.a2a.grpc.utils.ProtoUtils.FromProto; import static io.a2a.grpc.utils.ProtoUtils.ToProto; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; - +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import com.google.protobuf.Empty; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.StreamResponse; -import io.a2a.server.PublicAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; @@ -46,29 +43,14 @@ import io.grpc.Status; import io.grpc.stub.StreamObserver; -import java.util.HashMap; -import java.util.Map; - -@ApplicationScoped -public class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { - - private AgentCard agentCard; - private RequestHandler requestHandler; +public abstract class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { // Hook so testing can wait until streaming subscriptions are established. // Without this we get intermittent failures private static volatile Runnable streamingSubscribedRunnable; - @Inject - Instance callContextFactory; - - protected GrpcHandler() { - } - @Inject - public GrpcHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { - this.agentCard = agentCard; - this.requestHandler = requestHandler; + public GrpcHandler() { } @Override @@ -77,7 +59,7 @@ public void sendMessage(io.a2a.grpc.SendMessageRequest request, try { ServerCallContext context = createCallContext(responseObserver); MessageSendParams params = FromProto.messageSendParams(request); - EventKind taskOrMessage = requestHandler.onMessageSend(params, context); + EventKind taskOrMessage = getRequestHandler().onMessageSend(params, context); io.a2a.grpc.SendMessageResponse response = ToProto.taskOrMessage(taskOrMessage); responseObserver.onNext(response); responseObserver.onCompleted(); @@ -94,7 +76,7 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, try { ServerCallContext context = createCallContext(responseObserver); TaskQueryParams params = FromProto.taskQueryParams(request); - Task task = requestHandler.onGetTask(params, context); + Task task = getRequestHandler().onGetTask(params, context); if (task != null) { responseObserver.onNext(ToProto.task(task)); responseObserver.onCompleted(); @@ -114,7 +96,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, try { ServerCallContext context = createCallContext(responseObserver); TaskIdParams params = FromProto.taskIdParams(request); - Task task = requestHandler.onCancelTask(params, context); + Task task = getRequestHandler().onCancelTask(params, context); if (task != null) { responseObserver.onNext(ToProto.task(task)); responseObserver.onCompleted(); @@ -131,7 +113,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, @Override public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (! agentCard.capabilities().pushNotifications()) { + if (!getAgentCard().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -139,7 +121,7 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat try { ServerCallContext context = createCallContext(responseObserver); TaskPushNotificationConfig config = FromProto.taskPushNotificationConfig(request); - TaskPushNotificationConfig responseConfig = requestHandler.onSetTaskPushNotificationConfig(config, context); + TaskPushNotificationConfig responseConfig = getRequestHandler().onSetTaskPushNotificationConfig(config, context); responseObserver.onNext(ToProto.taskPushNotificationConfig(responseConfig)); responseObserver.onCompleted(); } catch (JSONRPCError e) { @@ -152,7 +134,7 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat @Override public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (! agentCard.capabilities().pushNotifications()) { + if (!getAgentCard().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -160,7 +142,7 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon try { ServerCallContext context = createCallContext(responseObserver); GetTaskPushNotificationConfigParams params = FromProto.getTaskPushNotificationConfigParams(request); - TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(params, context); + TaskPushNotificationConfig config = getRequestHandler().onGetTaskPushNotificationConfig(params, context); responseObserver.onNext(ToProto.taskPushNotificationConfig(config)); responseObserver.onCompleted(); } catch (JSONRPCError e) { @@ -173,7 +155,7 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon @Override public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (! agentCard.capabilities().pushNotifications()) { + if (!getAgentCard().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -181,7 +163,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC try { ServerCallContext context = createCallContext(responseObserver); ListTaskPushNotificationConfigParams params = FromProto.listTaskPushNotificationConfigParams(request); - List configList = requestHandler.onListTaskPushNotificationConfig(params, context); + List configList = getRequestHandler().onListTaskPushNotificationConfig(params, context); io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); for (TaskPushNotificationConfig config : configList) { @@ -199,7 +181,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC @Override public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, StreamObserver responseObserver) { - if (! agentCard.capabilities().streaming()) { + if (!getAgentCard().capabilities().streaming()) { handleError(responseObserver, new InvalidRequestError()); return; } @@ -207,7 +189,7 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, try { ServerCallContext context = createCallContext(responseObserver); MessageSendParams params = FromProto.messageSendParams(request); - Flow.Publisher publisher = requestHandler.onMessageSendStream(params, context); + Flow.Publisher publisher = getRequestHandler().onMessageSendStream(params, context); convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); @@ -219,7 +201,7 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, @Override public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, StreamObserver responseObserver) { - if (! agentCard.capabilities().streaming()) { + if (!getAgentCard().capabilities().streaming()) { handleError(responseObserver, new InvalidRequestError()); return; } @@ -227,7 +209,7 @@ public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, try { ServerCallContext context = createCallContext(responseObserver); TaskIdParams params = FromProto.taskIdParams(request); - Flow.Publisher publisher = requestHandler.onResubscribeToTask(params, context); + Flow.Publisher publisher = getRequestHandler().onResubscribeToTask(params, context); convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); @@ -287,7 +269,7 @@ public void onComplete() { public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, StreamObserver responseObserver) { try { - responseObserver.onNext(ToProto.agentCard(agentCard)); + responseObserver.onNext(ToProto.agentCard(getAgentCard())); responseObserver.onCompleted(); } catch (Throwable t) { handleInternalError(responseObserver, t); @@ -297,7 +279,7 @@ public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, @Override public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (! agentCard.capabilities().pushNotifications()) { + if (!getAgentCard().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -305,7 +287,7 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat try { ServerCallContext context = createCallContext(responseObserver); DeleteTaskPushNotificationConfigParams params = FromProto.deleteTaskPushNotificationConfigParams(request); - requestHandler.onDeleteTaskPushNotificationConfig(params, context); + getRequestHandler().onDeleteTaskPushNotificationConfig(params, context); // void response responseObserver.onNext(Empty.getDefaultInstance()); responseObserver.onCompleted(); @@ -317,7 +299,8 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat } private ServerCallContext createCallContext(StreamObserver responseObserver) { - if (callContextFactory == null || callContextFactory.isUnsatisfied()) { + CallContextFactory factory = getCallContextFactory(); + if (factory == null) { // Default implementation when no custom CallContextFactory is provided // This handles both CDI injection scenarios and test scenarios where callContextFactory is null User user = UnauthenticatedUser.INSTANCE; @@ -335,7 +318,6 @@ private ServerCallContext createCallContext(StreamObserver responseObserv return new ServerCallContext(user, state); } else { - CallContextFactory factory = callContextFactory.get(); // TODO: CallContextFactory interface expects ServerCall + Metadata, but we only have StreamObserver // This is another manifestation of the architectural limitation mentioned above return factory.create(responseObserver); // Fall back to basic create() method for now @@ -393,4 +375,9 @@ public static void setStreamingSubscribedRunnable(Runnable runnable) { streamingSubscribedRunnable = runnable; } + protected abstract RequestHandler getRequestHandler(); + + protected abstract AgentCard getAgentCard(); + + protected abstract CallContextFactory getCallContextFactory(); } diff --git a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java index 6e269026b..1cfeb6626 100644 --- a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java @@ -32,7 +32,9 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.events.EventConsumer; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; +import io.a2a.server.requesthandlers.CallContextFactory; import io.a2a.server.requesthandlers.DefaultRequestHandler; +import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; @@ -67,7 +69,7 @@ public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testOnGetTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); GetTaskRequest request = GetTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) @@ -89,7 +91,7 @@ public void testOnGetTaskSuccess() throws Exception { @Test public void testOnGetTaskNotFound() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); GetTaskRequest request = GetTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -103,7 +105,7 @@ public void testOnGetTaskNotFound() throws Exception { @Test public void testOnCancelTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -134,7 +136,7 @@ public void testOnCancelTaskSuccess() throws Exception { @Test public void testOnCancelTaskNotSupported() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -153,7 +155,7 @@ public void testOnCancelTaskNotSupported() throws Exception { @Test public void testOnCancelTaskNotFound() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); CancelTaskRequest request = CancelTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -166,7 +168,7 @@ public void testOnCancelTaskNotFound() throws Exception { @Test public void testOnMessageNewMessageSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -182,7 +184,7 @@ public void testOnMessageNewMessageSuccess() throws Exception { @Test public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); @@ -198,7 +200,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { @Test public void testOnMessageError() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(new UnsupportedOperationError()); }; @@ -208,7 +210,7 @@ public void testOnMessageError() throws Exception { @Test public void testSetPushNotificationConfigSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); @@ -227,7 +229,7 @@ public void testSetPushNotificationConfigSuccess() throws Exception { @Test public void testGetPushNotificationConfigSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -256,7 +258,7 @@ public void testGetPushNotificationConfigSuccess() throws Exception { @Test public void testPushNotificationsNotSupportedError() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); @@ -268,7 +270,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); @@ -280,7 +282,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); @@ -288,7 +290,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { @Test public void testOnMessageStreamNewMessageSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -306,7 +308,7 @@ public void testOnMessageStreamNewMessageSuccess() throws Exception { @Test public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -358,7 +360,7 @@ public void onCompleted() { @Test public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK) .history(new ArrayList<>()) @@ -408,7 +410,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep @Test public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); List events = List.of( AbstractA2ARequestHandlerTest.MINIMAL_TASK, new TaskArtifactUpdateEvent.Builder() @@ -488,7 +490,7 @@ public void onCompleted() { @Test public void testOnResubscribeNoExistingTaskError() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -500,7 +502,7 @@ public void testOnResubscribeNoExistingTaskError() throws Exception { @Test public void testOnResubscribeExistingTaskSuccess() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); @@ -535,7 +537,7 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); @@ -580,7 +582,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { @Test public void testStreamingNotSupportedError() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); } @@ -589,7 +591,7 @@ public void testStreamingNotSupportedError() throws Exception { public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception { // This test does not exist in the Python implementation AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -603,14 +605,14 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception public void testOnMessageStreamInternalError() throws Exception { DefaultRequestHandler mocked = Mockito.mock(DefaultRequestHandler.class); Mockito.doThrow(new InternalError("Internal Error")).when(mocked).onMessageSendStream(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, mocked); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, mocked); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INTERNAL); } @Test public void testListPushNotificationConfig() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -636,7 +638,7 @@ public void testListPushNotificationConfig() throws Exception { @Test public void testListPushNotificationConfigNotSupported() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -654,7 +656,7 @@ public void testListPushNotificationConfigNotSupported() throws Exception { public void testListPushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -670,7 +672,7 @@ public void testListPushNotificationConfigNoPushConfigStore() { @Test public void testListPushNotificationConfigTaskNotFound() { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -685,7 +687,7 @@ public void testListPushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfig() throws Exception { - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -708,7 +710,7 @@ public void testDeletePushNotificationConfig() throws Exception { @Test public void testDeletePushNotificationConfigNotSupported() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); - GrpcHandler handler = new GrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -727,7 +729,7 @@ public void testDeletePushNotificationConfigNotSupported() throws Exception { public void testDeletePushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - GrpcHandler handler = new GrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) @@ -799,4 +801,29 @@ private void assertGrpcError(StreamRecorder streamRecorder, Status.Code e Assertions.assertEquals(expectedStatusCode, ((StatusRuntimeException) streamRecorder.getError()).getStatus().getCode()); Assertions.assertTrue(streamRecorder.getValues().isEmpty()); } + + private static class TestGrpcHandler extends GrpcHandler { + private final AgentCard card; + private final RequestHandler handler; + + TestGrpcHandler(AgentCard card, RequestHandler handler) { + this.card = card; + this.handler = handler; + } + + @Override + protected RequestHandler getRequestHandler() { + return handler; + } + + @Override + protected AgentCard getAgentCard() { + return card; + } + + @Override + protected CallContextFactory getCallContextFactory() { + return null; + } + } } From 77b0c7b72b09215d891d20687db145c30a5cd7d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Tue, 12 Aug 2025 18:24:20 +0200 Subject: [PATCH 062/493] chore: Adding some testing on toProto conversion. (#223) Checking that optional parameters not being set doesn't lead to a NullPointerException. Signed-off-by: Emmanuel Hugonnet --- .gitignore | 2 + spec-grpc/pom.xml | 15 + .../java/io/a2a/grpc/utils/ProtoUtils.java | 100 ++++--- .../java/io/a2a/grpc/utils/ToProtoTest.java | 272 ++++++++++++++++++ 4 files changed, 355 insertions(+), 34 deletions(-) create mode 100644 spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java diff --git a/.gitignore b/.gitignore index 91a800a18..d8908d914 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ nb-configuration.xml /.quarkus/cli/plugins/ # TLS Certificates .certs/ +nbproject/ + diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index cb97c6ca2..879007ace 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -53,6 +53,21 @@ javax.annotation-api provided + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index a3e36ce3f..73158ecf2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -1,5 +1,6 @@ package io.a2a.grpc.utils; + import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDateTime; @@ -61,6 +62,7 @@ * Utility class to convert between GRPC and Spec objects. */ public class ProtoUtils { + public static class ToProto { public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { @@ -143,8 +145,12 @@ public static io.a2a.grpc.Task task(Task task) { public static io.a2a.grpc.Message message(Message message) { io.a2a.grpc.Message.Builder builder = io.a2a.grpc.Message.newBuilder(); builder.setMessageId(message.getMessageId()); - builder.setContextId(message.getContextId()); - builder.setTaskId(message.getTaskId()); + if (message.getContextId() != null) { + builder.setContextId(message.getContextId()); + } + if (message.getTaskId() != null) { + builder.setTaskId(message.getTaskId()); + } builder.setRole(role(message.getRole())); if (message.getParts() != null) { builder.addAllContent(message.getParts().stream().map(ToProto::part).collect(Collectors.toList())); @@ -171,7 +177,7 @@ private static io.a2a.grpc.PushNotificationConfig pushNotificationConfig(PushNot if (config.authentication() != null) { builder.setAuthentication(authenticationInfo(config.authentication())); } - if (config.id() != null) { + if (config.id() != null) { builder.setId(config.id()); } return builder.build(); @@ -182,8 +188,12 @@ public static io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent(TaskAr builder.setTaskId(event.getTaskId()); builder.setContextId(event.getContextId()); builder.setArtifact(artifact(event.getArtifact())); - builder.setAppend(event.isAppend() == null ? false : event.isAppend()); - builder.setLastChunk(event.isLastChunk() == null ? false : event.isLastChunk()); + if (event.isAppend() != null) { + builder.setAppend(event.isAppend()); + } + if (event.isLastChunk() != null) { + builder.setLastChunk(event.isLastChunk()); + } if (event.getMetadata() != null) { builder.setMetadata(struct(event.getMetadata())); } @@ -258,8 +268,10 @@ private static io.a2a.grpc.Role role(Message.Role role) { return io.a2a.grpc.Role.ROLE_UNSPECIFIED; } return switch (role) { - case USER -> io.a2a.grpc.Role.ROLE_USER; - case AGENT -> io.a2a.grpc.Role.ROLE_AGENT; + case USER -> + io.a2a.grpc.Role.ROLE_USER; + case AGENT -> + io.a2a.grpc.Role.ROLE_AGENT; }; } @@ -283,15 +295,24 @@ private static io.a2a.grpc.TaskState taskState(TaskState taskState) { return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; } return switch (taskState) { - case SUBMITTED -> io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED; - case WORKING -> io.a2a.grpc.TaskState.TASK_STATE_WORKING; - case INPUT_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED; - case AUTH_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED; - case COMPLETED -> io.a2a.grpc.TaskState.TASK_STATE_COMPLETED; - case CANCELED -> io.a2a.grpc.TaskState.TASK_STATE_CANCELLED; - case FAILED -> io.a2a.grpc.TaskState.TASK_STATE_FAILED; - case REJECTED -> io.a2a.grpc.TaskState.TASK_STATE_REJECTED; - default -> io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; + case SUBMITTED -> + io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED; + case WORKING -> + io.a2a.grpc.TaskState.TASK_STATE_WORKING; + case INPUT_REQUIRED -> + io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED; + case AUTH_REQUIRED -> + io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED; + case COMPLETED -> + io.a2a.grpc.TaskState.TASK_STATE_COMPLETED; + case CANCELED -> + io.a2a.grpc.TaskState.TASK_STATE_CANCELLED; + case FAILED -> + io.a2a.grpc.TaskState.TASK_STATE_FAILED; + case REJECTED -> + io.a2a.grpc.TaskState.TASK_STATE_REJECTED; + default -> + io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; }; } @@ -596,7 +617,6 @@ public static io.a2a.grpc.SendMessageResponse taskOrMessage(EventKind eventKind) } } - } public static class FromProto { @@ -734,8 +754,8 @@ public static Message message(io.a2a.grpc.Message message) { return new Message( role(message.getRole()), message.getContentList().stream().map(item -> part(item)).collect(Collectors.toList()), - message.getMessageId(), - message.getContextId(), + message.getMessageId().isEmpty() ? null : message.getMessageId(), + message.getContextId().isEmpty() ? null : message.getContextId(), message.getTaskId(), null, // referenceTaskIds is not in grpc message struct(message.getMetadata()) @@ -814,9 +834,12 @@ private static Message.Role role(io.a2a.grpc.Role role) { return null; } return switch (role) { - case ROLE_USER -> Message.Role.USER; - case ROLE_AGENT -> Message.Role.AGENT; - default -> null; + case ROLE_USER -> + Message.Role.USER; + case ROLE_AGENT -> + Message.Role.AGENT; + default -> + null; }; } @@ -825,16 +848,26 @@ private static TaskState taskState(io.a2a.grpc.TaskState taskState) { return null; } return switch (taskState) { - case TASK_STATE_SUBMITTED -> TaskState.SUBMITTED; - case TASK_STATE_WORKING -> TaskState.WORKING; - case TASK_STATE_INPUT_REQUIRED -> TaskState.INPUT_REQUIRED; - case TASK_STATE_AUTH_REQUIRED -> TaskState.AUTH_REQUIRED; - case TASK_STATE_COMPLETED -> TaskState.COMPLETED; - case TASK_STATE_CANCELLED -> TaskState.CANCELED; - case TASK_STATE_FAILED -> TaskState.FAILED; - case TASK_STATE_REJECTED -> TaskState.REJECTED; - case TASK_STATE_UNSPECIFIED -> null; - case UNRECOGNIZED -> null; + case TASK_STATE_SUBMITTED -> + TaskState.SUBMITTED; + case TASK_STATE_WORKING -> + TaskState.WORKING; + case TASK_STATE_INPUT_REQUIRED -> + TaskState.INPUT_REQUIRED; + case TASK_STATE_AUTH_REQUIRED -> + TaskState.AUTH_REQUIRED; + case TASK_STATE_COMPLETED -> + TaskState.COMPLETED; + case TASK_STATE_CANCELLED -> + TaskState.CANCELED; + case TASK_STATE_FAILED -> + TaskState.FAILED; + case TASK_STATE_REJECTED -> + TaskState.REJECTED; + case TASK_STATE_UNSPECIFIED -> + null; + case UNRECOGNIZED -> + null; }; } @@ -867,5 +900,4 @@ private static Object value(Value value) { } } - -} \ No newline at end of file +} diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java new file mode 100644 index 000000000..aca9caaad --- /dev/null +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -0,0 +1,272 @@ +package io.a2a.grpc.utils; + +import static io.a2a.grpc.Role.ROLE_AGENT; +import static io.a2a.grpc.Role.ROLE_USER; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.a2a.grpc.SendMessageConfiguration; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentSkill; +import io.a2a.spec.Artifact; +import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendConfiguration; +import io.a2a.spec.PushNotificationAuthenticationInfo; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class ToProtoTest { + + private static final Message SIMPLE_MESSAGE = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("tell me a joke"))) + .contextId("context-1234") + .messageId("message-1234") + .build(); + + @Test + public void convertAgentCard() { + AgentCard agentCard = new AgentCard.Builder() + .name("Hello World Agent") + .description("Just a hello world agent") + .url("http://localhost:9999") + .version("1.0.0") + .documentationUrl("http://example.com/docs") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(true) + .stateTransitionHistory(true) + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("hello_world") + .name("Returns hello world") + .description("just returns hello world") + .tags(Collections.singletonList("hello world")) + .examples(List.of("hi", "hello world")) + .build())) + .protocolVersion("0.2.5") + .build(); + io.a2a.grpc.AgentCard result = ProtoUtils.ToProto.agentCard(agentCard); + assertEquals("Hello World Agent", result.getName()); + assertEquals("Just a hello world agent", result.getDescription()); + assertEquals("http://localhost:9999", result.getUrl()); + assertEquals("1.0.0", result.getVersion()); + assertEquals("http://example.com/docs", result.getDocumentationUrl()); + assertEquals(1, result.getDefaultInputModesCount()); + assertEquals("text", result.getDefaultInputModes(0)); + assertEquals(1, result.getDefaultOutputModesCount()); + assertEquals("text", result.getDefaultOutputModes(0)); + assertEquals("0.2.5", result.getProtocolVersion()); + agentCard = new AgentCard.Builder() + .name("Hello World Agent") + .description("Just a hello world agent") + .url("http://localhost:9999") + .version("1.0.0") + .documentationUrl("http://example.com/docs") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(true) + .stateTransitionHistory(true) + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("hello_world") + .name("Returns hello world") + .description("just returns hello world") + .tags(Collections.singletonList("hello world")) + .examples(List.of("hi", "hello world")) + .build())) + .preferredTransport("HTTP+JSON") +// .iconUrl("http://example.com/icon.svg") + .securitySchemes(Map.of("basic", new HTTPAuthSecurityScheme.Builder().scheme("basic").description("Basic Auth").build())) + .security(List.of(Map.of("oauth", List.of("read")))) + .protocolVersion("0.2.5") + .build(); + result = ProtoUtils.ToProto.agentCard(agentCard); + assertEquals("Hello World Agent", result.getName()); + assertEquals("Just a hello world agent", result.getDescription()); + assertEquals("http://localhost:9999", result.getUrl()); + assertEquals("1.0.0", result.getVersion()); + assertEquals("http://example.com/docs", result.getDocumentationUrl()); + assertEquals(1, result.getDefaultInputModesCount()); + assertEquals("text", result.getDefaultInputModes(0)); + assertEquals(1, result.getDefaultOutputModesCount()); + assertEquals("text", result.getDefaultOutputModes(0)); + assertEquals("0.2.5", result.getProtocolVersion()); + assertEquals("HTTP+JSON", result.getPreferredTransport()); + assertEquals(1, result.getSecurityCount()); + assertEquals(1, result.getSecurity(0).getSchemesMap().size()); + assertEquals(true, result.getSecurity(0).getSchemesMap().containsKey("oauth")); + assertEquals(1, result.getSecurity(0).getSchemesMap().get("oauth").getListCount()); + assertEquals("read", result.getSecurity(0).getSchemesMap().get("oauth").getList(0)); + assertEquals(1, result.getSecuritySchemesMap().size()); + assertEquals(true, result.getSecuritySchemesMap().containsKey("basic")); + assertEquals(result.getSecuritySchemesMap().get("basic").getApiKeySecurityScheme().getDefaultInstanceForType(), result.getSecuritySchemesMap().get("basic").getApiKeySecurityScheme()); + assertEquals(result.getSecuritySchemesMap().get("basic").getOauth2SecurityScheme().getDefaultInstanceForType(), result.getSecuritySchemesMap().get("basic").getOauth2SecurityScheme()); + assertEquals("basic", result.getSecuritySchemesMap().get("basic").getHttpAuthSecurityScheme().getScheme()); + assertEquals("Basic Auth", result.getSecuritySchemesMap().get("basic").getHttpAuthSecurityScheme().getDescription()); + } + + @Test + public void convertTask() { + Task task = new Task.Builder().id("cancel-task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + io.a2a.grpc.Task result = ProtoUtils.ToProto.task(task); + assertEquals("session-xyz", result.getContextId()); + assertEquals("cancel-task-123", result.getId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED, result.getStatus().getState()); + assertEquals(0, result.getArtifactsCount()); + assertEquals(0, result.getHistoryCount()); + task = new Task.Builder().id("cancel-task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .artifacts(List.of(new Artifact.Builder() + .artifactId("11") + .name("artefact") + .parts(new TextPart("text")) + .build())) + .history(List.of(SIMPLE_MESSAGE)) + .metadata(Collections.emptyMap()) + .build(); + result = ProtoUtils.ToProto.task(task); + assertEquals("session-xyz", result.getContextId()); + assertEquals("cancel-task-123", result.getId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED, result.getStatus().getState()); + assertEquals(1, result.getArtifactsCount()); + assertEquals("11", result.getArtifacts(0).getArtifactId()); + assertEquals("artefact", result.getArtifacts(0).getName()); + assertEquals(1, result.getArtifacts(0).getPartsCount()); + assertEquals(true, result.getArtifacts(0).getParts(0).hasText()); + assertEquals(false, result.getArtifacts(0).getParts(0).hasFile()); + assertEquals(false, result.getArtifacts(0).getParts(0).hasData()); + assertEquals("text", result.getArtifacts(0).getParts(0).getText()); + assertEquals(1, result.getHistoryCount()); + assertEquals("context-1234", result.getHistory(0).getContextId()); + assertEquals("message-1234", result.getHistory(0).getMessageId()); + assertEquals(ROLE_USER, result.getHistory(0).getRole()); + assertEquals(1, result.getHistory(0).getContentCount()); + assertEquals("tell me a joke", result.getHistory(0).getContent(0).getText()); + assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getHistory(0).getContent(0).getFile()); + assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getHistory(0).getContent(0).getData()); + } + + @Test + public void convertMessage() { + io.a2a.grpc.Message result = ProtoUtils.ToProto.message(SIMPLE_MESSAGE); + assertEquals("context-1234", result.getContextId()); + assertEquals("message-1234", result.getMessageId()); + assertEquals(ROLE_USER, result.getRole()); + assertEquals(1, result.getContentCount()); + assertEquals("tell me a joke", result.getContent(0).getText()); + assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getContent(0).getFile()); + assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getContent(0).getData()); + Message message = new Message.Builder() + .role(Message.Role.AGENT) + .parts(Collections.singletonList(new TextPart("tell me a joke"))) + .messageId("message-1234") + .build(); + result = ProtoUtils.ToProto.message(message); + assertEquals("", result.getContextId()); + assertEquals("message-1234", result.getMessageId()); + assertEquals(ROLE_AGENT, result.getRole()); + assertEquals(1, result.getContentCount()); + assertEquals("tell me a joke", result.getContent(0).getText()); + assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getContent(0).getFile()); + assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getContent(0).getData()); + } + + @Test + public void convertTaskPushNotificationConfig() { + TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig("push-task-123", + new PushNotificationConfig.Builder() + .url("http://example.com") + .id("xyz") + .build()); + io.a2a.grpc.TaskPushNotificationConfig result = ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig); + assertEquals("tasks/push-task-123/pushNotificationConfigs/xyz", result.getName()); + assertNotNull(result.getPushNotificationConfig()); + assertEquals("http://example.com", result.getPushNotificationConfig().getUrl()); + assertEquals("xyz", result.getPushNotificationConfig().getId()); + assertEquals(false, result.getPushNotificationConfig().hasAuthentication()); + taskPushConfig + = new TaskPushNotificationConfig("push-task-123", + new PushNotificationConfig.Builder() + .token("AAAAAA") + .authenticationInfo(new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), "credentials")) + .url("http://example.com") + .id("xyz") + .build()); + result = ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig); + assertEquals("tasks/push-task-123/pushNotificationConfigs/xyz", result.getName()); + assertNotNull(result.getPushNotificationConfig()); + assertEquals("http://example.com", result.getPushNotificationConfig().getUrl()); + assertEquals("xyz", result.getPushNotificationConfig().getId()); + assertEquals("AAAAAA", result.getPushNotificationConfig().getToken()); + assertEquals(true, result.getPushNotificationConfig().hasAuthentication()); + assertEquals("credentials", result.getPushNotificationConfig().getAuthentication().getCredentials()); + assertEquals(1, result.getPushNotificationConfig().getAuthentication().getSchemesCount()); + assertEquals("jwt", result.getPushNotificationConfig().getAuthentication().getSchemes(0)); + } + + @Test + public void convertTaskArtifactUpdateEvent() { + TaskArtifactUpdateEvent task = new TaskArtifactUpdateEvent.Builder() + .taskId("task-123") + .contextId("session-123") + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()).build(); + io.a2a.grpc.TaskArtifactUpdateEvent result = ProtoUtils.ToProto.taskArtifactUpdateEvent(task); + assertEquals("task-123", result.getTaskId()); + assertEquals("session-123", result.getContextId()); + assertNotNull(result.getArtifact()); + assertEquals("11", result.getArtifact().getArtifactId()); + assertEquals(1, result.getArtifact().getPartsCount()); + assertEquals("text", result.getArtifact().getParts(0).getText()); + } + + @Test + public void convertTaskStatusUpdateEvent() { + TaskStatusUpdateEvent tsue = new TaskStatusUpdateEvent.Builder() + .taskId("1234") + .contextId("xyz") + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(); + io.a2a.grpc.TaskStatusUpdateEvent result = ProtoUtils.ToProto.taskStatusUpdateEvent(tsue); + assertEquals("1234", result.getTaskId()); + assertEquals("xyz", result.getContextId()); + assertEquals(true, result.getFinal()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_COMPLETED, result.getStatus().getState()); + } + + @Test + public void convertSendMessageConfiguration() { + MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(List.of("text")) + .blocking(false) + .build(); + SendMessageConfiguration result = ProtoUtils.ToProto.messageSendConfiguration(configuration); + assertEquals(false, result.getBlocking()); + assertEquals(1, result.getAcceptedOutputModesCount()); + assertEquals("text", result.getAcceptedOutputModesBytes(0).toStringUtf8()); + } +} From 198504b20c27faf13ad30166e16587bbd0004322 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 19 Aug 2025 09:25:26 -0400 Subject: [PATCH 063/493] feat: Implement the spec updates for v0.3.0 (#212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This PR introduces some new spec classes and updates some existing ones based off the updates in the [v0.3.0 spec](https://github.com/a2aproject/A2A/releases/tag/v0.3.0). Note that the TCK job will fail until the v0.3.0 TCK is available. - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes https://github.com/a2aproject/a2a-java/issues/209 🦕 --- .github/workflows/run-tck.yml | 4 +- client/pom.xml | 2 +- client/src/main/java/io/a2a/A2A.java | 4 +- .../java/io/a2a/client/A2ACardResolver.java | 6 +- .../main/java/io/a2a/client/A2AClient.java | 61 +- .../io/a2a/client/A2ACardResolverTest.java | 2 +- .../java/io/a2a/client/A2AClientTest.java | 39 +- .../test/java/io/a2a/client/JsonMessages.java | 161 +-- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../examples/helloworld/HelloWorldRunner.java | 2 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- .../src/main/resources/META-INF/beans.xml | 0 reference/jsonrpc/pom.xml | 4 +- .../server/apps/quarkus/A2AServerRoutes.java | 38 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 353 ++++--- .../src/main/java/io/a2a/grpc/AgentCard.java | 721 +++++++++++++ .../java/io/a2a/grpc/AgentCardOrBuilder.java | 104 ++ .../java/io/a2a/grpc/AgentCardSignature.java | 952 ++++++++++++++++++ .../a2a/grpc/AgentCardSignatureOrBuilder.java | 81 ++ .../src/main/java/io/a2a/grpc/AgentSkill.java | 538 ++++++++++ .../java/io/a2a/grpc/AgentSkillOrBuilder.java | 64 ++ .../io/a2a/grpc/MutualTlsSecurityScheme.java | 530 ++++++++++ .../MutualTlsSecuritySchemeOrBuilder.java | 32 + .../io/a2a/grpc/OAuth2SecurityScheme.java | 171 ++++ .../grpc/OAuth2SecuritySchemeOrBuilder.java | 22 + .../main/java/io/a2a/grpc/SecurityScheme.java | 208 ++++ .../io/a2a/grpc/SecuritySchemeOrBuilder.java | 15 + .../java/io/a2a/grpc/SendMessageRequest.java | 24 +- .../a2a/grpc/SendMessageRequestOrBuilder.java | 6 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 54 +- spec-grpc/src/main/proto/a2a.proto | 47 +- .../src/main/resources/META-INF/beans.xml | 6 - spec/pom.xml | 2 +- .../io/a2a/spec/APIKeySecurityScheme.java | 2 +- .../java/io/a2a/spec/AgentCapabilities.java | 2 +- spec/src/main/java/io/a2a/spec/AgentCard.java | 38 +- .../java/io/a2a/spec/AgentCardSignature.java | 49 + .../main/java/io/a2a/spec/AgentExtension.java | 3 + .../main/java/io/a2a/spec/AgentInterface.java | 2 +- .../main/java/io/a2a/spec/AgentProvider.java | 2 +- .../src/main/java/io/a2a/spec/AgentSkill.java | 14 +- spec/src/main/java/io/a2a/spec/Artifact.java | 3 +- ...ticatedExtendedCardNotConfiguredError.java | 35 + .../a2a/spec/AuthorizationCodeOAuthFlow.java | 2 +- .../a2a/spec/ClientCredentialsOAuthFlow.java | 2 +- .../spec/ContentTypeNotSupportedError.java | 4 + spec/src/main/java/io/a2a/spec/DataPart.java | 2 +- spec/src/main/java/io/a2a/spec/FilePart.java | 3 +- .../main/java/io/a2a/spec/FileWithBytes.java | 3 + .../main/java/io/a2a/spec/FileWithUri.java | 3 + .../GetAuthenticatedExtendedCardRequest.java | 70 ++ .../GetAuthenticatedExtendedCardResponse.java | 30 + .../io/a2a/spec/HTTPAuthSecurityScheme.java | 2 +- .../java/io/a2a/spec/ImplicitOAuthFlow.java | 2 +- .../main/java/io/a2a/spec/InternalError.java | 3 + .../a2a/spec/InvalidAgentResponseError.java | 3 +- .../java/io/a2a/spec/InvalidParamsError.java | 3 + .../java/io/a2a/spec/InvalidRequestError.java | 3 + .../main/java/io/a2a/spec/JSONParseError.java | 3 + .../main/java/io/a2a/spec/JSONRPCError.java | 2 +- .../main/java/io/a2a/spec/JSONRPCMessage.java | 2 +- .../spec/JSONRPCRequestDeserializerBase.java | 3 +- .../java/io/a2a/spec/JSONRPCResponse.java | 4 +- spec/src/main/java/io/a2a/spec/Message.java | 2 +- .../io/a2a/spec/MessageSendConfiguration.java | 6 +- .../java/io/a2a/spec/MessageSendParams.java | 3 +- .../java/io/a2a/spec/MethodNotFoundError.java | 3 + .../io/a2a/spec/MutualTLSSecurityScheme.java | 46 + .../a2a/spec/NonStreamingJSONRPCRequest.java | 3 +- ...onStreamingJSONRPCRequestDeserializer.java | 3 + .../io/a2a/spec/OAuth2SecurityScheme.java | 22 +- .../src/main/java/io/a2a/spec/OAuthFlows.java | 2 +- .../a2a/spec/OpenIdConnectSecurityScheme.java | 2 +- .../java/io/a2a/spec/PasswordOAuthFlow.java | 2 +- .../PushNotificationAuthenticationInfo.java | 2 +- .../io/a2a/spec/PushNotificationConfig.java | 2 +- .../PushNotificationNotSupportedError.java | 3 + .../main/java/io/a2a/spec/SecurityScheme.java | 10 +- spec/src/main/java/io/a2a/spec/Task.java | 2 +- .../io/a2a/spec/TaskArtifactUpdateEvent.java | 3 +- .../main/java/io/a2a/spec/TaskIdParams.java | 2 +- .../io/a2a/spec/TaskNotCancelableError.java | 3 + .../java/io/a2a/spec/TaskNotFoundError.java | 3 + .../a2a/spec/TaskPushNotificationConfig.java | 2 +- .../java/io/a2a/spec/TaskQueryParams.java | 3 +- spec/src/main/java/io/a2a/spec/TaskState.java | 2 +- .../src/main/java/io/a2a/spec/TaskStatus.java | 2 +- .../io/a2a/spec/TaskStatusUpdateEvent.java | 3 +- spec/src/main/java/io/a2a/spec/TextPart.java | 2 +- .../java/io/a2a/spec/TransportProtocol.java | 38 + .../a2a/spec/UnsupportedOperationError.java | 3 + tck/pom.xml | 9 +- .../io/a2a/tck/server/AgentCardProducer.java | 8 +- tck/src/main/resources/application.properties | 2 +- tests/server-common/pom.xml | 2 +- .../apps/common/AbstractA2AServerTest.java | 20 +- transport/grpc/pom.xml | 2 +- .../java/io/a2a/grpc/handler/GrpcHandler.java | 3 + .../src/main/resources/META-INF/beans.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- .../a2a/jsonrpc/handler/JSONRPCHandler.java | 30 + .../jsonrpc/handler/JSONRPCHandlerTest.java | 211 ++-- 109 files changed, 4555 insertions(+), 485 deletions(-) create mode 100644 reference/grpc/src/main/resources/META-INF/beans.xml create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java create mode 100644 spec/src/main/java/io/a2a/spec/AgentCardSignature.java create mode 100644 spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java create mode 100644 spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java create mode 100644 spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java create mode 100644 spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java create mode 100644 spec/src/main/java/io/a2a/spec/TransportProtocol.java diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 7793157dc..ed5829956 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -12,7 +12,7 @@ on: env: # Tag of the TCK - TCK_VERSION: v0.2.5 + TCK_VERSION: 0.3.0.alpha # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 @@ -57,7 +57,7 @@ jobs: working-directory: tck - name: Wait for SUT to start run: | - URL="http://localhost:9999/.well-known/agent.json" + URL="http://localhost:9999/.well-known/agent-card.json" EXPECTED_STATUS=200 TIMEOUT=120 RETRY_INTERVAL=2 diff --git a/client/pom.xml b/client/pom.xml index 0a71440b4..fa381e65e 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-client diff --git a/client/src/main/java/io/a2a/A2A.java b/client/src/main/java/io/a2a/A2A.java index c8dca80e4..1d1f6260e 100644 --- a/client/src/main/java/io/a2a/A2A.java +++ b/client/src/main/java/io/a2a/A2A.java @@ -101,7 +101,7 @@ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl) * * @param agentUrl the base URL for the agent whose agent card we want to retrieve * @param relativeCardPath optional path to the agent card endpoint relative to the base - * agent URL, defaults to ".well-known/agent.json" + * agent URL, defaults to ".well-known/agent-card.json" * @param authHeaders the HTTP authentication headers to use * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card @@ -117,7 +117,7 @@ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, M * @param httpClient the http client to use * @param agentUrl the base URL for the agent whose agent card we want to retrieve * @param relativeCardPath optional path to the agent card endpoint relative to the base - * agent URL, defaults to ".well-known/agent.json" + * agent URL, defaults to ".well-known/agent-card.json" * @param authHeaders the HTTP authentication headers to use * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card diff --git a/client/src/main/java/io/a2a/client/A2ACardResolver.java b/client/src/main/java/io/a2a/client/A2ACardResolver.java index 88d1e351f..8ec181647 100644 --- a/client/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/client/src/main/java/io/a2a/client/A2ACardResolver.java @@ -20,7 +20,7 @@ public class A2ACardResolver { private final String url; private final Map authHeaders; - private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent.json"; + private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent-card.json"; private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; @@ -37,7 +37,7 @@ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl) throws A2AClien * @param httpClient the http client to use * @param baseUrl the base URL for the agent whose agent card we want to retrieve * @param agentCardPath optional path to the agent card endpoint relative to the base - * agent URL, defaults to ".well-known/agent.json" + * agent URL, defaults to ".well-known/agent-card.json" * @throws A2AClientError if the URL for the agent is invalid */ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCardPath) throws A2AClientError { @@ -48,7 +48,7 @@ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCar * @param httpClient the http client to use * @param baseUrl the base URL for the agent whose agent card we want to retrieve * @param agentCardPath optional path to the agent card endpoint relative to the base - * agent URL, defaults to ".well-known/agent.json" + * agent URL, defaults to ".well-known/agent-card.json" * @param authHeaders the HTTP authentication headers to use. May be {@code null} * @throws A2AClientError if the URL for the agent is invalid */ diff --git a/client/src/main/java/io/a2a/client/A2AClient.java b/client/src/main/java/io/a2a/client/A2AClient.java index 80f8c401c..146e1bc58 100644 --- a/client/src/main/java/io/a2a/client/A2AClient.java +++ b/client/src/main/java/io/a2a/client/A2AClient.java @@ -24,6 +24,8 @@ import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; @@ -61,6 +63,7 @@ public class A2AClient { private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE = new TypeReference<>() {}; private final A2AHttpClient httpClient; private final String agentUrl; private AgentCard agentCard; @@ -632,8 +635,51 @@ public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consu } } + /** + * Retrieve the authenticated extended agent card. + * + * @param authHeaders the HTTP authentication headers to use + * @return the response + * @throws A2AServerException if retrieving the authenticated extended agent card fails for any reason + */ + public GetAuthenticatedExtendedCardResponse getAuthenticatedExtendedCard(Map authHeaders) throws A2AServerException { + return getAuthenticatedExtendedCard(null, authHeaders); + } + + /** + * Retrieve the authenticated extended agent card. + * + * @param requestId the request ID to use + * @param authHeaders the HTTP authentication headers to use + * @return the response + * @throws A2AServerException if retrieving the authenticated extended agent card fails for any reason + */ + public GetAuthenticatedExtendedCardResponse getAuthenticatedExtendedCard(String requestId, + Map authHeaders) throws A2AServerException { + GetAuthenticatedExtendedCardRequest.Builder requestBuilder = new GetAuthenticatedExtendedCardRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetAuthenticatedExtendedCardRequest.METHOD); + + if (requestId != null) { + requestBuilder.id(requestId); + } + + GetAuthenticatedExtendedCardRequest request = requestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(request, authHeaders); + return unmarshalResponse(httpResponseBody, GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to get authenticated extended agent card: " + e, e); + } + } + private String sendPostRequest(Object value) throws IOException, InterruptedException { - A2AHttpClient.PostBuilder builder = createPostBuilder(value); + return sendPostRequest(value, null); + } + + private String sendPostRequest(Object value, Map authHeaders) throws IOException, InterruptedException { + A2AHttpClient.PostBuilder builder = createPostBuilder(value, authHeaders); A2AHttpResponse response = builder.post(); if (!response.success()) { throw new IOException("Request failed " + response.status()); @@ -642,11 +688,20 @@ private String sendPostRequest(Object value) throws IOException, InterruptedExce } private A2AHttpClient.PostBuilder createPostBuilder(Object value) throws JsonProcessingException { - return httpClient.createPost() + return createPostBuilder(value, null); + } + + private A2AHttpClient.PostBuilder createPostBuilder(Object value, Map authHeaders) throws JsonProcessingException { + A2AHttpClient.PostBuilder builder = httpClient.createPost() .url(agentUrl) .addHeader("Content-Type", "application/json") .body(Utils.OBJECT_MAPPER.writeValueAsString(value)); - + if (authHeaders != null) { + for (Map.Entry entry : authHeaders.entrySet()) { + builder.addHeader(entry.getKey(), entry.getValue()); + } + } + return builder; } private T unmarshalResponse(String response, TypeReference typeReference) diff --git a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java index 8d9ff0f5b..c9ce509d3 100644 --- a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java +++ b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java @@ -20,7 +20,7 @@ public class A2ACardResolverTest { - private static final String AGENT_CARD_PATH = "/.well-known/agent.json"; + private static final String AGENT_CARD_PATH = "/.well-known/agent-card.json"; private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; @Test diff --git a/client/src/test/java/io/a2a/client/A2AClientTest.java b/client/src/test/java/io/a2a/client/A2AClientTest.java index eda03e02a..373aa3d9f 100644 --- a/client/src/test/java/io/a2a/client/A2AClientTest.java +++ b/client/src/test/java/io/a2a/client/A2AClientTest.java @@ -4,6 +4,8 @@ import static io.a2a.client.JsonMessages.AUTHENTICATION_EXTENDED_AGENT_CARD; import static io.a2a.client.JsonMessages.CANCEL_TASK_TEST_REQUEST; import static io.a2a.client.JsonMessages.CANCEL_TASK_TEST_RESPONSE; +import static io.a2a.client.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST; +import static io.a2a.client.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE; import static io.a2a.client.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static io.a2a.client.JsonMessages.GET_TASK_TEST_REQUEST; @@ -38,6 +40,8 @@ import io.a2a.spec.A2AServerException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentCardSignature; +import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; import io.a2a.spec.CancelTaskResponse; @@ -46,6 +50,7 @@ import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskResponse; @@ -65,6 +70,8 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; +import io.a2a.spec.TransportProtocol; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -382,7 +389,7 @@ public void testA2AClientGetAgentCard() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/.well-known/agent.json") + .withPath("/.well-known/agent-card.json") ) .respond( response() @@ -443,24 +450,36 @@ public void testA2AClientGetAgentCard() throws Exception { assertEquals(outputModes, skills.get(1).outputModes()); assertTrue(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.5", agentCard.protocolVersion()); + assertEquals("0.2.9", agentCard.protocolVersion()); + assertEquals("JSONRPC", agentCard.preferredTransport()); + List additionalInterfaces = agentCard.additionalInterfaces(); + assertEquals(3, additionalInterfaces.size()); + AgentInterface jsonrpc = new AgentInterface(TransportProtocol.JSONRPC.asString(), "https://georoute-agent.example.com/a2a/v1"); + AgentInterface grpc = new AgentInterface(TransportProtocol.GRPC.asString(), "https://georoute-agent.example.com/a2a/grpc"); + AgentInterface httpJson = new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "https://georoute-agent.example.com/a2a/json"); + assertEquals(jsonrpc, additionalInterfaces.get(0)); + assertEquals(grpc, additionalInterfaces.get(1)); + assertEquals(httpJson, additionalInterfaces.get(2)); } @Test public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { this.server.when( request() - .withMethod("GET") - .withPath("/agent/authenticatedExtendedCard") + .withMethod("POST") + .withPath("/") + .withBody(JsonBody.json(GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST, MatchType.STRICT)) + ) .respond( response() .withStatusCode(200) - .withBody(AUTHENTICATION_EXTENDED_AGENT_CARD) + .withBody(GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE) ); A2AClient client = new A2AClient("http://localhost:4001"); - AgentCard agentCard = client.getAgentCard("/agent/authenticatedExtendedCard", null); + GetAuthenticatedExtendedCardResponse response = client.getAuthenticatedExtendedCard("1", null); + AgentCard agentCard = response.getResult(); assertEquals("GeoSpatial Route Planner Agent Extended", agentCard.name()); assertEquals("Extended description", agentCard.description()); assertEquals("https://georoute-agent.example.com/a2a/v1", agentCard.url()); @@ -516,7 +535,13 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertEquals(List.of("extended"), skills.get(2).tags()); assertTrue(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.5", agentCard.protocolVersion()); + assertEquals("0.2.9", agentCard.protocolVersion()); + List signatures = agentCard.signatures(); + assertEquals(1, signatures.size()); + AgentCardSignature signature = new AgentCardSignature(null, + "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", + "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ"); + assertEquals(signature, signatures.get(0)); } @Test diff --git a/client/src/test/java/io/a2a/client/JsonMessages.java b/client/src/test/java/io/a2a/client/JsonMessages.java index fecf216d0..d59ee0146 100644 --- a/client/src/test/java/io/a2a/client/JsonMessages.java +++ b/client/src/test/java/io/a2a/client/JsonMessages.java @@ -8,68 +8,80 @@ public class JsonMessages { static final String AGENT_CARD = """ { - "name": "GeoSpatial Route Planner Agent", - "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", - "provider": { - "organization": "Example Geo Services Inc.", - "url": "https://www.examplegeoservices.com" - }, - "iconUrl": "https://georoute-agent.example.com/icon.png", - "version": "1.2.0", - "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", - "capabilities": { - "streaming": true, - "pushNotifications": true, - "stateTransitionHistory": false - }, - "securitySchemes": { - "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" - } - }, - "security": [{ "google": ["openid", "profile", "email"] }], - "defaultInputModes": ["application/json", "text/plain"], - "defaultOutputModes": ["application/json", "image/png"], - "skills": [ - { - "id": "route-optimizer-traffic", - "name": "Traffic-Aware Route Optimizer", - "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", - "tags": ["maps", "routing", "navigation", "directions", "traffic"], - "examples": [ - "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", - "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" - ], - "inputModes": ["application/json", "text/plain"], - "outputModes": [ - "application/json", - "application/vnd.geo+json", - "text/html" - ] - }, - { - "id": "custom-map-generator", - "name": "Personalized Map Generator", - "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", - "tags": ["maps", "customization", "visualization", "cartography"], - "examples": [ - "Generate a map of my upcoming road trip with all planned stops highlighted.", - "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." - ], - "inputModes": ["application/json"], - "outputModes": [ - "image/png", - "image/jpeg", - "application/json", - "text/html" - ] - } - ], - "supportsAuthenticatedExtendedCard": true, - "protocolVersion": "0.2.5" - }"""; + "protocolVersion": "0.2.9", + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "preferredTransport": "JSONRPC", + "additionalInterfaces" : [ + {"url": "https://georoute-agent.example.com/a2a/v1", "transport": "JSONRPC"}, + {"url": "https://georoute-agent.example.com/a2a/grpc", "transport": "GRPC"}, + {"url": "https://georoute-agent.example.com/a2a/json", "transport": "HTTP+JSON"} + ], + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": true, + "signatures": [ + { + "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", + "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" + } + ] + }"""; static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """ { @@ -139,7 +151,13 @@ public class JsonMessages { } ], "supportsAuthenticatedExtendedCard": true, - "protocolVersion": "0.2.5" + "protocolVersion": "0.2.9", + "signatures": [ + { + "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", + "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" + } + ] }"""; @@ -615,4 +633,21 @@ public class JsonMessages { } }"""; + static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST = """ + { + "jsonrpc": "2.0", + "id": "1", + "method": "agent/getAuthenticatedExtendedCard" + } + """; + + static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": "1", + "result": + """ + AUTHENTICATION_EXTENDED_AGENT_CARD + + """ + } + """; } diff --git a/common/pom.xml b/common/pom.xml index e4c01920e..504856d3b 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 8edb46cb6..3aaa5c221 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 144972238..84a489367 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.2.6.Beta1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index d026edb21..a60b123d9 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 3a051dbab..2b0b15652 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/pom.xml b/pom.xml index 0a3bfba88..22e44da39 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 84f4ff74c..8b2af244f 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 2f2d7edd6..61d44ca8a 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml diff --git a/reference/grpc/src/main/resources/META-INF/beans.xml b/reference/grpc/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 3f0116ce2..a4342f96c 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc @@ -83,4 +83,4 @@ test - \ No newline at end of file + diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 3ebf22ccd..2cd411409 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -29,6 +29,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskRequest; import io.a2a.spec.IdJsonMappingException; @@ -72,10 +73,6 @@ public class A2AServerRoutes { @Inject JSONRPCHandler jsonRpcHandler; - @Inject - @ExtendedAgentCard - Instance extendedAgentCard; - // Hook so testing can wait until the MultiSseSupport is subscribed. // Without this we get intermittent failures private static volatile Runnable streamingMultiSseSupportSubscribedRunnable; @@ -159,40 +156,11 @@ private JSONRPCErrorResponse handleError(JsonProcessingException exception) { * * @return the agent card */ - @Route(path = "/.well-known/agent.json", methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) + @Route(path = "/.well-known/agent-card.json", methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) public AgentCard getAgentCard() { return jsonRpcHandler.getAgentCard(); } - /** - * Handles incoming GET requests to the authenticated extended agent card endpoint. - * Returns the agent card in JSON format. - * - */ - @Route(path = "/agent/authenticatedExtendedCard", methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) - public void getAuthenticatedExtendedAgentCard(RoutingExchange re) { - // TODO need to add authentication for this endpoint - // https://github.com/a2aproject/a2a-java/issues/77 - try { - if (! jsonRpcHandler.getAgentCard().supportsAuthenticatedExtendedCard()) { - JSONErrorResponse errorResponse = new JSONErrorResponse("Extended agent card not supported or not enabled."); - re.response().setStatusCode(Response.Status.NOT_FOUND.getStatusCode()) - .end(Utils.OBJECT_MAPPER.writeValueAsString(errorResponse)); - return; - } - if (! extendedAgentCard.isResolvable()) { - JSONErrorResponse errorResponse = new JSONErrorResponse("Authenticated extended agent card is supported but not configured on the server."); - re.response().setStatusCode(Response.Status.NOT_FOUND.getStatusCode()) - .end(Utils.OBJECT_MAPPER.writeValueAsString(errorResponse)); - return; - } - - re.response().end(Utils.OBJECT_MAPPER.writeValueAsString(extendedAgentCard.get())); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - private JSONRPCResponse processNonStreamingRequest( NonStreamingJSONRPCRequest request, ServerCallContext context) { if (request instanceof GetTaskRequest req) { @@ -209,6 +177,8 @@ private JSONRPCResponse processNonStreamingRequest( return jsonRpcHandler.listPushNotificationConfig(req, context); } else if (request instanceof DeleteTaskPushNotificationConfigRequest req) { return jsonRpcHandler.deletePushNotificationConfig(req, context); + } else if (request instanceof GetAuthenticatedExtendedCardRequest req) { + return jsonRpcHandler.onGetAuthenticatedExtendedCardRequest(req, context); } else { return generateErrorResponse(request, new UnsupportedOperationError()); } diff --git a/server-common/pom.xml b/server-common/pom.xml index caf58b2d1..5677d4d02 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 879007ace..d72663392 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index ae331178e..69060d5aa 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -121,6 +121,11 @@ public static void registerAllExtensions( static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_AgentSkill_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_AgentCardSignature_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_AgentCardSignature_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_TaskPushNotificationConfig_descriptor; static final @@ -166,6 +171,11 @@ public static void registerAllExtensions( static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_MutualTlsSecurityScheme_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_OAuthFlows_descriptor; static final @@ -326,7 +336,7 @@ public static void registerAllExtensions( "AuthenticationInfo\":\n\022AuthenticationInfo" + "\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credentials\030\002 \001(\t\"0" + "\n\016AgentInterface\022\013\n\003url\030\001 \001(\t\022\021\n\ttranspo" + - "rt\030\002 \001(\t\"\362\004\n\tAgentCard\022\030\n\020protocol_versi" + + "rt\030\002 \001(\t\"\242\005\n\tAgentCard\022\030\n\020protocol_versi" + "on\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013description\030\002 " + "\001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred_transport\030" + "\016 \001(\t\0225\n\025additional_interfaces\030\017 \003(\0132\026.a" + @@ -339,137 +349,146 @@ public static void registerAllExtensions( ".a2a.v1.Security\022\033\n\023default_input_modes\030" + "\n \003(\t\022\034\n\024default_output_modes\030\013 \003(\t\022\"\n\006s" + "kills\030\014 \003(\0132\022.a2a.v1.AgentSkill\022,\n$suppo" + - "rts_authenticated_extended_card\030\r \001(\010\032N\n" + - "\024SecuritySchemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005va" + - "lue\030\002 \001(\0132\026.a2a.v1.SecurityScheme:\0028\001\"2\n" + - "\rAgentProvider\022\013\n\003url\030\001 \001(\t\022\024\n\014organizat" + - "ion\030\002 \001(\t\"n\n\021AgentCapabilities\022\021\n\tstream" + - "ing\030\001 \001(\010\022\032\n\022push_notifications\030\002 \001(\010\022*\n" + - "\nextensions\030\003 \003(\0132\026.a2a.v1.AgentExtensio" + - "n\"m\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013desc" + - "ription\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006param" + - "s\030\004 \001(\0132\027.google.protobuf.Struct\"\206\001\n\nAge" + - "ntSkill\022\n\n\002id\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\022\023\n\013des" + - "cription\030\003 \001(\t\022\014\n\004tags\030\004 \003(\t\022\020\n\010examples" + - "\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014output_mo" + - "des\030\007 \003(\t\"l\n\032TaskPushNotificationConfig\022" + - "\014\n\004name\030\001 \001(\t\022@\n\030push_notification_confi" + - "g\030\002 \001(\0132\036.a2a.v1.PushNotificationConfig\"" + - "\032\n\nStringList\022\014\n\004list\030\001 \003(\t\"~\n\010Security\022" + - ".\n\007schemes\030\001 \003(\0132\035.a2a.v1.Security.Schem" + - "esEntry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001(\t\022!\n\005" + - "value\030\002 \001(\0132\022.a2a.v1.StringList:\0028\001\"\260\002\n\016" + - "SecurityScheme\022?\n\027api_key_security_schem" + - "e\030\001 \001(\0132\034.a2a.v1.APIKeySecuritySchemeH\000\022" + - "C\n\031http_auth_security_scheme\030\002 \001(\0132\036.a2a" + - ".v1.HTTPAuthSecuritySchemeH\000\022>\n\026oauth2_s" + - "ecurity_scheme\030\003 \001(\0132\034.a2a.v1.OAuth2Secu" + - "ritySchemeH\000\022N\n\037open_id_connect_security" + - "_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConnectSecu" + - "ritySchemeH\000B\010\n\006scheme\"K\n\024APIKeySecurity" + - "Scheme\022\023\n\013description\030\001 \001(\t\022\020\n\010location\030" + - "\002 \001(\t\022\014\n\004name\030\003 \001(\t\"T\n\026HTTPAuthSecurityS" + - "cheme\022\023\n\013description\030\001 \001(\t\022\016\n\006scheme\030\002 \001" + - "(\t\022\025\n\rbearer_format\030\003 \001(\t\"N\n\024OAuth2Secur" + - "ityScheme\022\023\n\013description\030\001 \001(\t\022!\n\005flows\030" + - "\002 \001(\0132\022.a2a.v1.OAuthFlows\"O\n\033OpenIdConne" + - "ctSecurityScheme\022\023\n\013description\030\001 \001(\t\022\033\n" + - "\023open_id_connect_url\030\002 \001(\t\"\366\001\n\nOAuthFlow" + - "s\022@\n\022authorization_code\030\001 \001(\0132\".a2a.v1.A" + - "uthorizationCodeOAuthFlowH\000\022@\n\022client_cr" + - "edentials\030\002 \001(\0132\".a2a.v1.ClientCredentia" + - "lsOAuthFlowH\000\022-\n\010implicit\030\003 \001(\0132\031.a2a.v1" + - ".ImplicitOAuthFlowH\000\022-\n\010password\030\004 \001(\0132\031" + - ".a2a.v1.PasswordOAuthFlowH\000B\006\n\004flow\"\316\001\n\032" + - "AuthorizationCodeOAuthFlow\022\031\n\021authorizat" + - "ion_url\030\001 \001(\t\022\021\n\ttoken_url\030\002 \001(\t\022\023\n\013refr" + - "esh_url\030\003 \001(\t\022>\n\006scopes\030\004 \003(\0132..a2a.v1.A" + - "uthorizationCodeOAuthFlow.ScopesEntry\032-\n" + - "\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t" + - ":\0028\001\"\263\001\n\032ClientCredentialsOAuthFlow\022\021\n\tt" + - "oken_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\022>\n\006s" + - "copes\030\003 \003(\0132..a2a.v1.ClientCredentialsOA" + - "uthFlow.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003ke" + - "y\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021ImplicitO" + - "AuthFlow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013r" + - "efresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a.v" + - "1.ImplicitOAuthFlow.ScopesEntry\032-\n\013Scope" + - "sEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\241" + - "\001\n\021PasswordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t\022" + - "\023\n\013refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a" + - "2a.v1.PasswordOAuthFlow.ScopesEntry\032-\n\013S" + - "copesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\002" + - "8\001\"\237\001\n\022SendMessageRequest\022%\n\007request\030\001 \001" + - "(\0132\017.a2a.v1.MessageB\003\340A\002\0227\n\rconfiguratio" + - "n\030\002 \001(\0132 .a2a.v1.SendMessageConfiguratio" + - "n\022)\n\010metadata\030\003 \001(\0132\027.google.protobuf.St" + - "ruct\";\n\016GetTaskRequest\022\021\n\004name\030\001 \001(\tB\003\340A" + - "\002\022\026\n\016history_length\030\002 \001(\005\"!\n\021CancelTaskR" + - "equest\022\014\n\004name\030\001 \001(\t\"4\n$GetTaskPushNotif" + - "icationConfigRequest\022\014\n\004name\030\001 \001(\t\"7\n\'De" + - "leteTaskPushNotificationConfigRequest\022\014\n" + - "\004name\030\001 \001(\t\"\217\001\n\'CreateTaskPushNotificati" + - "onConfigRequest\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\t" + - "config_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a" + - "2a.v1.TaskPushNotificationConfigB\003\340A\002\"\'\n" + - "\027TaskSubscriptionRequest\022\014\n\004name\030\001 \001(\t\"^" + - "\n%ListTaskPushNotificationConfigRequest\022" + - "\016\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npa" + - "ge_token\030\003 \001(\t\"\025\n\023GetAgentCardRequest\"g\n" + - "\023SendMessageResponse\022\034\n\004task\030\001 \001(\0132\014.a2a" + - ".v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.Message" + - "H\000R\007messageB\t\n\007payload\"\326\001\n\016StreamRespons" + - "e\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002" + - " \001(\0132\017.a2a.v1.MessageH\000R\007message\0226\n\rstat" + - "us_update\030\003 \001(\0132\035.a2a.v1.TaskStatusUpdat" + - "eEventH\000\022:\n\017artifact_update\030\004 \001(\0132\037.a2a." + - "v1.TaskArtifactUpdateEventH\000B\t\n\007payload\"" + - "v\n&ListTaskPushNotificationConfigRespons" + - "e\0223\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPushNoti" + - "ficationConfig\022\027\n\017next_page_token\030\002 \001(\t*" + - "\372\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020" + - "\000\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STAT" + - "E_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n" + - "\021TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCE" + - "LLED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n" + - "\023TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUT" + - "H_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED" + - "\020\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\272\n\n\nA2" + - "AService\022c\n\013SendMessage\022\032.a2a.v1.SendMes" + - "sageRequest\032\033.a2a.v1.SendMessageResponse" + - "\"\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024SendStr" + - "eamingMessage\022\032.a2a.v1.SendMessageReques" + - "t\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"\022/v1/m" + - "essage:stream:\001*0\001\022R\n\007GetTask\022\026.a2a.v1.G" + - "etTaskRequest\032\014.a2a.v1.Task\"!\332A\004name\202\323\344\223" + - "\002\024\022\022/v1/{name=tasks/*}\022[\n\nCancelTask\022\031.a" + - "2a.v1.CancelTaskRequest\032\014.a2a.v1.Task\"$\202" + - "\323\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*\022s\n\020T" + - "askSubscription\022\037.a2a.v1.TaskSubscriptio" + - "nRequest\032\026.a2a.v1.StreamResponse\"$\202\323\344\223\002\036" + - "\022\034/v1/{name=tasks/*}:subscribe0\001\022\304\001\n Cre" + - "ateTaskPushNotificationConfig\022/.a2a.v1.C" + - "reateTaskPushNotificationConfigRequest\032\"" + - ".a2a.v1.TaskPushNotificationConfig\"K\332A\rp" + - "arent,config\202\323\344\223\0025\"+/v1/{parent=task/*/p" + - "ushNotificationConfigs}:\006config\022\256\001\n\035GetT" + - "askPushNotificationConfig\022,.a2a.v1.GetTa" + - "skPushNotificationConfigRequest\032\".a2a.v1" + - ".TaskPushNotificationConfig\";\332A\004name\202\323\344\223" + - "\002.\022,/v1/{name=tasks/*/pushNotificationCo" + - "nfigs/*}\022\276\001\n\036ListTaskPushNotificationCon" + - "fig\022-.a2a.v1.ListTaskPushNotificationCon" + - "figRequest\032..a2a.v1.ListTaskPushNotifica" + - "tionConfigResponse\"=\332A\006parent\202\323\344\223\002.\022,/v1" + - "/{parent=tasks/*}/pushNotificationConfig" + - "s\022P\n\014GetAgentCard\022\033.a2a.v1.GetAgentCardR" + - "equest\032\021.a2a.v1.AgentCard\"\020\202\323\344\223\002\n\022\010/v1/c" + - "ard\022\250\001\n DeleteTaskPushNotificationConfig" + - "\022/.a2a.v1.DeleteTaskPushNotificationConf" + - "igRequest\032\026.google.protobuf.Empty\";\332A\004na" + - "me\202\323\344\223\002.*,/v1/{name=tasks/*/pushNotifica" + - "tionConfigs/*}B7\n\013io.a2a.grpcB\003A2AP\001Z\030go" + - "ogle.golang.org/a2a/v1\252\002\006A2a.V1b\006proto3" + "rts_authenticated_extended_card\030\r \001(\010\022.\n" + + "\nsignatures\030\021 \003(\0132\032.a2a.v1.AgentCardSign" + + "ature\032N\n\024SecuritySchemesEntry\022\013\n\003key\030\001 \001" + + "(\t\022%\n\005value\030\002 \001(\0132\026.a2a.v1.SecuritySchem" + + "e:\0028\001\"2\n\rAgentProvider\022\013\n\003url\030\001 \001(\t\022\024\n\014o" + + "rganization\030\002 \001(\t\"n\n\021AgentCapabilities\022\021" + + "\n\tstreaming\030\001 \001(\010\022\032\n\022push_notifications\030" + + "\002 \001(\010\022*\n\nextensions\030\003 \003(\0132\026.a2a.v1.Agent" + + "Extension\"m\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t" + + "\022\023\n\013description\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022" + + "\'\n\006params\030\004 \001(\0132\027.google.protobuf.Struct" + + "\"\252\001\n\nAgentSkill\022\n\n\002id\030\001 \001(\t\022\014\n\004name\030\002 \001(" + + "\t\022\023\n\013description\030\003 \001(\t\022\014\n\004tags\030\004 \003(\t\022\020\n\010" + + "examples\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014o" + + "utput_modes\030\007 \003(\t\022\"\n\010security\030\010 \003(\0132\020.a2" + + "a.v1.Security\"m\n\022AgentCardSignature\022\026\n\tp" + + "rotected\030\001 \001(\tB\003\340A\002\022\026\n\tsignature\030\002 \001(\tB\003" + + "\340A\002\022\'\n\006header\030\003 \001(\0132\027.google.protobuf.St" + + "ruct\"l\n\032TaskPushNotificationConfig\022\014\n\004na" + + "me\030\001 \001(\t\022@\n\030push_notification_config\030\002 \001" + + "(\0132\036.a2a.v1.PushNotificationConfig\"\032\n\nSt" + + "ringList\022\014\n\004list\030\001 \003(\t\"~\n\010Security\022.\n\007sc" + + "hemes\030\001 \003(\0132\035.a2a.v1.Security.SchemesEnt" + + "ry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001(\t\022!\n\005value" + + "\030\002 \001(\0132\022.a2a.v1.StringList:\0028\001\"\361\002\n\016Secur" + + "ityScheme\022?\n\027api_key_security_scheme\030\001 \001" + + "(\0132\034.a2a.v1.APIKeySecuritySchemeH\000\022C\n\031ht" + + "tp_auth_security_scheme\030\002 \001(\0132\036.a2a.v1.H" + + "TTPAuthSecuritySchemeH\000\022>\n\026oauth2_securi" + + "ty_scheme\030\003 \001(\0132\034.a2a.v1.OAuth2SecurityS" + + "chemeH\000\022N\n\037open_id_connect_security_sche" + + "me\030\004 \001(\0132#.a2a.v1.OpenIdConnectSecurityS" + + "chemeH\000\022?\n\024mtls_security_scheme\030\005 \001(\0132\037." + + "a2a.v1.MutualTlsSecuritySchemeH\000B\010\n\006sche" + + "me\"K\n\024APIKeySecurityScheme\022\023\n\013descriptio" + + "n\030\001 \001(\t\022\020\n\010location\030\002 \001(\t\022\014\n\004name\030\003 \001(\t\"" + + "T\n\026HTTPAuthSecurityScheme\022\023\n\013description" + + "\030\001 \001(\t\022\016\n\006scheme\030\002 \001(\t\022\025\n\rbearer_format\030" + + "\003 \001(\t\"k\n\024OAuth2SecurityScheme\022\023\n\013descrip" + + "tion\030\001 \001(\t\022!\n\005flows\030\002 \001(\0132\022.a2a.v1.OAuth" + + "Flows\022\033\n\023oauth2_metadata_url\030\003 \001(\t\"O\n\033Op" + + "enIdConnectSecurityScheme\022\023\n\013description" + + "\030\001 \001(\t\022\033\n\023open_id_connect_url\030\002 \001(\t\".\n\027M" + + "utualTlsSecurityScheme\022\023\n\013description\030\001 " + + "\001(\t\"\366\001\n\nOAuthFlows\022@\n\022authorization_code" + + "\030\001 \001(\0132\".a2a.v1.AuthorizationCodeOAuthFl" + + "owH\000\022@\n\022client_credentials\030\002 \001(\0132\".a2a.v" + + "1.ClientCredentialsOAuthFlowH\000\022-\n\010implic" + + "it\030\003 \001(\0132\031.a2a.v1.ImplicitOAuthFlowH\000\022-\n" + + "\010password\030\004 \001(\0132\031.a2a.v1.PasswordOAuthFl" + + "owH\000B\006\n\004flow\"\316\001\n\032AuthorizationCodeOAuthF" + + "low\022\031\n\021authorization_url\030\001 \001(\t\022\021\n\ttoken_" + + "url\030\002 \001(\t\022\023\n\013refresh_url\030\003 \001(\t\022>\n\006scopes" + + "\030\004 \003(\0132..a2a.v1.AuthorizationCodeOAuthFl" + + "ow.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001" + + "(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\263\001\n\032ClientCredenti" + + "alsOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t\022\023\n\013refre" + + "sh_url\030\002 \001(\t\022>\n\006scopes\030\003 \003(\0132..a2a.v1.Cl" + + "ientCredentialsOAuthFlow.ScopesEntry\032-\n\013" + + "ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:" + + "\0028\001\"\251\001\n\021ImplicitOAuthFlow\022\031\n\021authorizati" + + "on_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\0225\n\006sco" + + "pes\030\003 \003(\0132%.a2a.v1.ImplicitOAuthFlow.Sco" + + "pesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005" + + "value\030\002 \001(\t:\0028\001\"\241\001\n\021PasswordOAuthFlow\022\021\n" + + "\ttoken_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\0225\n" + + "\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuthFlow" + + ".ScopesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t" + + "\022\r\n\005value\030\002 \001(\t:\0028\001\"\250\001\n\022SendMessageReque" + + "st\022.\n\007request\030\001 \001(\0132\017.a2a.v1.MessageB\003\340A" + + "\002R\007message\0227\n\rconfiguration\030\002 \001(\0132 .a2a." + + "v1.SendMessageConfiguration\022)\n\010metadata\030" + + "\003 \001(\0132\027.google.protobuf.Struct\";\n\016GetTas" + + "kRequest\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\026\n\016history_l" + + "ength\030\002 \001(\005\"!\n\021CancelTaskRequest\022\014\n\004name" + + "\030\001 \001(\t\"4\n$GetTaskPushNotificationConfigR" + + "equest\022\014\n\004name\030\001 \001(\t\"7\n\'DeleteTaskPushNo" + + "tificationConfigRequest\022\014\n\004name\030\001 \001(\t\"\217\001" + + "\n\'CreateTaskPushNotificationConfigReques" + + "t\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\tconfig_id\030\002 \001(" + + "\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a2a.v1.TaskPush" + + "NotificationConfigB\003\340A\002\"\'\n\027TaskSubscript" + + "ionRequest\022\014\n\004name\030\001 \001(\t\"^\n%ListTaskPush" + + "NotificationConfigRequest\022\016\n\006parent\030\001 \001(" + + "\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t" + + "\"\025\n\023GetAgentCardRequest\"g\n\023SendMessageRe" + + "sponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003" + + "msg\030\002 \001(\0132\017.a2a.v1.MessageH\000R\007messageB\t\n" + + "\007payload\"\326\001\n\016StreamResponse\022\034\n\004task\030\001 \001(" + + "\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1." + + "MessageH\000R\007message\0226\n\rstatus_update\030\003 \001(" + + "\0132\035.a2a.v1.TaskStatusUpdateEventH\000\022:\n\017ar" + + "tifact_update\030\004 \001(\0132\037.a2a.v1.TaskArtifac" + + "tUpdateEventH\000B\t\n\007payload\"v\n&ListTaskPus" + + "hNotificationConfigResponse\0223\n\007configs\030\001" + + " \003(\0132\".a2a.v1.TaskPushNotificationConfig" + + "\022\027\n\017next_page_token\030\002 \001(\t*\372\001\n\tTaskState\022" + + "\032\n\026TASK_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STAT" + + "E_SUBMITTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n" + + "\024TASK_STATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FA" + + "ILED\020\004\022\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK" + + "_STATE_INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_RE" + + "JECTED\020\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;" + + "\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USE" + + "R\020\001\022\016\n\nROLE_AGENT\020\0022\272\n\n\nA2AService\022c\n\013Se" + + "ndMessage\022\032.a2a.v1.SendMessageRequest\032\033." + + "a2a.v1.SendMessageResponse\"\033\202\323\344\223\002\025\"\020/v1/" + + "message:send:\001*\022k\n\024SendStreamingMessage\022" + + "\032.a2a.v1.SendMessageRequest\032\026.a2a.v1.Str" + + "eamResponse\"\035\202\323\344\223\002\027\"\022/v1/message:stream:" + + "\001*0\001\022R\n\007GetTask\022\026.a2a.v1.GetTaskRequest\032" + + "\014.a2a.v1.Task\"!\332A\004name\202\323\344\223\002\024\022\022/v1/{name=" + + "tasks/*}\022[\n\nCancelTask\022\031.a2a.v1.CancelTa" + + "skRequest\032\014.a2a.v1.Task\"$\202\323\344\223\002\036\"\031/v1/{na" + + "me=tasks/*}:cancel:\001*\022s\n\020TaskSubscriptio" + + "n\022\037.a2a.v1.TaskSubscriptionRequest\032\026.a2a" + + ".v1.StreamResponse\"$\202\323\344\223\002\036\022\034/v1/{name=ta" + + "sks/*}:subscribe0\001\022\304\001\n CreateTaskPushNot" + + "ificationConfig\022/.a2a.v1.CreateTaskPushN" + + "otificationConfigRequest\032\".a2a.v1.TaskPu" + + "shNotificationConfig\"K\332A\rparent,config\202\323" + + "\344\223\0025\"+/v1/{parent=task/*/pushNotificatio" + + "nConfigs}:\006config\022\256\001\n\035GetTaskPushNotific" + + "ationConfig\022,.a2a.v1.GetTaskPushNotifica" + + "tionConfigRequest\032\".a2a.v1.TaskPushNotif" + + "icationConfig\";\332A\004name\202\323\344\223\002.\022,/v1/{name=" + + "tasks/*/pushNotificationConfigs/*}\022\276\001\n\036L" + + "istTaskPushNotificationConfig\022-.a2a.v1.L" + + "istTaskPushNotificationConfigRequest\032..a" + + "2a.v1.ListTaskPushNotificationConfigResp" + + "onse\"=\332A\006parent\202\323\344\223\002.\022,/v1/{parent=tasks" + + "/*}/pushNotificationConfigs\022P\n\014GetAgentC" + + "ard\022\033.a2a.v1.GetAgentCardRequest\032\021.a2a.v" + + "1.AgentCard\"\020\202\323\344\223\002\n\022\010/v1/card\022\250\001\n Delete" + + "TaskPushNotificationConfig\022/.a2a.v1.Dele" + + "teTaskPushNotificationConfigRequest\032\026.go" + + "ogle.protobuf.Empty\";\332A\004name\202\323\344\223\002.*,/v1/" + + "{name=tasks/*/pushNotificationConfigs/*}" + + "B7\n\013io.a2a.grpcB\003A2AP\001Z\030google.golang.or" + + "g/a2a/v1\252\002\006A2a.V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -564,7 +583,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCard_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCard_descriptor, - new java.lang.String[] { "ProtocolVersion", "Name", "Description", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", }); + new java.lang.String[] { "ProtocolVersion", "Name", "Description", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", "Signatures", }); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor = internal_static_a2a_v1_AgentCard_descriptor.getNestedTypes().get(0); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new @@ -594,21 +613,27 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentSkill_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentSkill_descriptor, - new java.lang.String[] { "Id", "Name", "Description", "Tags", "Examples", "InputModes", "OutputModes", }); - internal_static_a2a_v1_TaskPushNotificationConfig_descriptor = + new java.lang.String[] { "Id", "Name", "Description", "Tags", "Examples", "InputModes", "OutputModes", "Security", }); + internal_static_a2a_v1_AgentCardSignature_descriptor = getDescriptor().getMessageTypes().get(18); + internal_static_a2a_v1_AgentCardSignature_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_AgentCardSignature_descriptor, + new java.lang.String[] { "Protected", "Signature", "Header", }); + internal_static_a2a_v1_TaskPushNotificationConfig_descriptor = + getDescriptor().getMessageTypes().get(19); internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskPushNotificationConfig_descriptor, new java.lang.String[] { "Name", "PushNotificationConfig", }); internal_static_a2a_v1_StringList_descriptor = - getDescriptor().getMessageTypes().get(19); + getDescriptor().getMessageTypes().get(20); internal_static_a2a_v1_StringList_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_StringList_descriptor, new java.lang.String[] { "List", }); internal_static_a2a_v1_Security_descriptor = - getDescriptor().getMessageTypes().get(20); + getDescriptor().getMessageTypes().get(21); internal_static_a2a_v1_Security_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_Security_descriptor, @@ -620,43 +645,49 @@ public static void registerAllExtensions( internal_static_a2a_v1_Security_SchemesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_SecurityScheme_descriptor = - getDescriptor().getMessageTypes().get(21); + getDescriptor().getMessageTypes().get(22); internal_static_a2a_v1_SecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SecurityScheme_descriptor, - new java.lang.String[] { "ApiKeySecurityScheme", "HttpAuthSecurityScheme", "Oauth2SecurityScheme", "OpenIdConnectSecurityScheme", "Scheme", }); + new java.lang.String[] { "ApiKeySecurityScheme", "HttpAuthSecurityScheme", "Oauth2SecurityScheme", "OpenIdConnectSecurityScheme", "MtlsSecurityScheme", "Scheme", }); internal_static_a2a_v1_APIKeySecurityScheme_descriptor = - getDescriptor().getMessageTypes().get(22); + getDescriptor().getMessageTypes().get(23); internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_APIKeySecurityScheme_descriptor, new java.lang.String[] { "Description", "Location", "Name", }); internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor = - getDescriptor().getMessageTypes().get(23); + getDescriptor().getMessageTypes().get(24); internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor, new java.lang.String[] { "Description", "Scheme", "BearerFormat", }); internal_static_a2a_v1_OAuth2SecurityScheme_descriptor = - getDescriptor().getMessageTypes().get(24); + getDescriptor().getMessageTypes().get(25); internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OAuth2SecurityScheme_descriptor, - new java.lang.String[] { "Description", "Flows", }); + new java.lang.String[] { "Description", "Flows", "Oauth2MetadataUrl", }); internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor = - getDescriptor().getMessageTypes().get(25); + getDescriptor().getMessageTypes().get(26); internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor, new java.lang.String[] { "Description", "OpenIdConnectUrl", }); + internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor = + getDescriptor().getMessageTypes().get(27); + internal_static_a2a_v1_MutualTlsSecurityScheme_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor, + new java.lang.String[] { "Description", }); internal_static_a2a_v1_OAuthFlows_descriptor = - getDescriptor().getMessageTypes().get(26); + getDescriptor().getMessageTypes().get(28); internal_static_a2a_v1_OAuthFlows_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OAuthFlows_descriptor, new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "Implicit", "Password", "Flow", }); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor = - getDescriptor().getMessageTypes().get(27); + getDescriptor().getMessageTypes().get(29); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor, @@ -668,7 +699,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor = - getDescriptor().getMessageTypes().get(28); + getDescriptor().getMessageTypes().get(30); internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor, @@ -680,7 +711,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_ImplicitOAuthFlow_descriptor = - getDescriptor().getMessageTypes().get(29); + getDescriptor().getMessageTypes().get(31); internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ImplicitOAuthFlow_descriptor, @@ -692,7 +723,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_PasswordOAuthFlow_descriptor = - getDescriptor().getMessageTypes().get(30); + getDescriptor().getMessageTypes().get(32); internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_PasswordOAuthFlow_descriptor, @@ -704,73 +735,73 @@ public static void registerAllExtensions( internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_SendMessageRequest_descriptor = - getDescriptor().getMessageTypes().get(31); + getDescriptor().getMessageTypes().get(33); internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SendMessageRequest_descriptor, new java.lang.String[] { "Request", "Configuration", "Metadata", }); internal_static_a2a_v1_GetTaskRequest_descriptor = - getDescriptor().getMessageTypes().get(32); + getDescriptor().getMessageTypes().get(34); internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskRequest_descriptor, new java.lang.String[] { "Name", "HistoryLength", }); internal_static_a2a_v1_CancelTaskRequest_descriptor = - getDescriptor().getMessageTypes().get(33); + getDescriptor().getMessageTypes().get(35); internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CancelTaskRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(34); + getDescriptor().getMessageTypes().get(36); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(35); + getDescriptor().getMessageTypes().get(37); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(36); + getDescriptor().getMessageTypes().get(38); internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Parent", "ConfigId", "Config", }); internal_static_a2a_v1_TaskSubscriptionRequest_descriptor = - getDescriptor().getMessageTypes().get(37); + getDescriptor().getMessageTypes().get(39); internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskSubscriptionRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(38); + getDescriptor().getMessageTypes().get(40); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Parent", "PageSize", "PageToken", }); internal_static_a2a_v1_GetAgentCardRequest_descriptor = - getDescriptor().getMessageTypes().get(39); + getDescriptor().getMessageTypes().get(41); internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetAgentCardRequest_descriptor, new java.lang.String[] { }); internal_static_a2a_v1_SendMessageResponse_descriptor = - getDescriptor().getMessageTypes().get(40); + getDescriptor().getMessageTypes().get(42); internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SendMessageResponse_descriptor, new java.lang.String[] { "Task", "Msg", "Payload", }); internal_static_a2a_v1_StreamResponse_descriptor = - getDescriptor().getMessageTypes().get(41); + getDescriptor().getMessageTypes().get(43); internal_static_a2a_v1_StreamResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_StreamResponse_descriptor, new java.lang.String[] { "Task", "Msg", "StatusUpdate", "ArtifactUpdate", "Payload", }); internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor = - getDescriptor().getMessageTypes().get(42); + getDescriptor().getMessageTypes().get(44); internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor, diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index 5862d4ef9..60d2cfb51 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -12,6 +12,7 @@ * - Skills; a set of actions/solutions the agent can perform * - Default modalities/content types supported by the agent. * - Authentication requirements + * Next ID: 18 * * * Protobuf type {@code a2a.v1.AgentCard} @@ -50,6 +51,7 @@ private AgentCard() { defaultOutputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); skills_ = java.util.Collections.emptyList(); + signatures_ = java.util.Collections.emptyList(); } public static final com.google.protobuf.Descriptors.Descriptor @@ -659,6 +661,18 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -671,6 +685,18 @@ public java.util.List getSecurityList() { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -684,6 +710,18 @@ public java.util.List getSecurityList() { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -696,6 +734,18 @@ public int getSecurityCount() { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -708,6 +758,18 @@ public io.a2a.grpc.Security getSecurity(int index) { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -920,6 +982,67 @@ public boolean getSupportsAuthenticatedExtendedCard() { return supportsAuthenticatedExtendedCard_; } + public static final int SIGNATURES_FIELD_NUMBER = 17; + @SuppressWarnings("serial") + private java.util.List signatures_; + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + @java.lang.Override + public java.util.List getSignaturesList() { + return signatures_; + } + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + @java.lang.Override + public java.util.List + getSignaturesOrBuilderList() { + return signatures_; + } + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + @java.lang.Override + public int getSignaturesCount() { + return signatures_.size(); + } + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + @java.lang.Override + public io.a2a.grpc.AgentCardSignature getSignatures(int index) { + return signatures_.get(index); + } + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + @java.lang.Override + public io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( + int index) { + return signatures_.get(index); + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -985,6 +1108,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { com.google.protobuf.GeneratedMessage.writeString(output, 16, protocolVersion_); } + for (int i = 0; i < signatures_.size(); i++) { + output.writeMessage(17, signatures_.get(i)); + } getUnknownFields().writeTo(output); } @@ -1065,6 +1191,10 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(16, protocolVersion_); } + for (int i = 0; i < signatures_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(17, signatures_.get(i)); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -1118,6 +1248,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getSkillsList())) return false; if (getSupportsAuthenticatedExtendedCard() != other.getSupportsAuthenticatedExtendedCard()) return false; + if (!getSignaturesList() + .equals(other.getSignaturesList())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -1178,6 +1310,10 @@ public int hashCode() { hash = (37 * hash) + SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( getSupportsAuthenticatedExtendedCard()); + if (getSignaturesCount() > 0) { + hash = (37 * hash) + SIGNATURES_FIELD_NUMBER; + hash = (53 * hash) + getSignaturesList().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -1282,6 +1418,7 @@ protected Builder newBuilderForType( * - Skills; a set of actions/solutions the agent can perform * - Default modalities/content types supported by the agent. * - Authentication requirements + * Next ID: 18 * * * Protobuf type {@code a2a.v1.AgentCard} @@ -1343,6 +1480,7 @@ private void maybeForceBuilderInitialization() { internalGetCapabilitiesFieldBuilder(); internalGetSecurityFieldBuilder(); internalGetSkillsFieldBuilder(); + internalGetSignaturesFieldBuilder(); } } @java.lang.Override @@ -1393,6 +1531,13 @@ public Builder clear() { } bitField0_ = (bitField0_ & ~0x00004000); supportsAuthenticatedExtendedCard_ = false; + if (signaturesBuilder_ == null) { + signatures_ = java.util.Collections.emptyList(); + } else { + signatures_ = null; + signaturesBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00010000); return this; } @@ -1453,6 +1598,15 @@ private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) { } else { result.skills_ = skillsBuilder_.build(); } + if (signaturesBuilder_ == null) { + if (((bitField0_ & 0x00010000) != 0)) { + signatures_ = java.util.Collections.unmodifiableList(signatures_); + bitField0_ = (bitField0_ & ~0x00010000); + } + result.signatures_ = signatures_; + } else { + result.signatures_ = signaturesBuilder_.build(); + } } private void buildPartial0(io.a2a.grpc.AgentCard result) { @@ -1665,6 +1819,32 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (other.getSupportsAuthenticatedExtendedCard() != false) { setSupportsAuthenticatedExtendedCard(other.getSupportsAuthenticatedExtendedCard()); } + if (signaturesBuilder_ == null) { + if (!other.signatures_.isEmpty()) { + if (signatures_.isEmpty()) { + signatures_ = other.signatures_; + bitField0_ = (bitField0_ & ~0x00010000); + } else { + ensureSignaturesIsMutable(); + signatures_.addAll(other.signatures_); + } + onChanged(); + } + } else { + if (!other.signatures_.isEmpty()) { + if (signaturesBuilder_.isEmpty()) { + signaturesBuilder_.dispose(); + signaturesBuilder_ = null; + signatures_ = other.signatures_; + bitField0_ = (bitField0_ & ~0x00010000); + signaturesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetSignaturesFieldBuilder() : null; + } else { + signaturesBuilder_.addAllMessages(other.signatures_); + } + } + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -1805,6 +1985,19 @@ public Builder mergeFrom( bitField0_ |= 0x00000001; break; } // case 130 + case 138: { + io.a2a.grpc.AgentCardSignature m = + input.readMessage( + io.a2a.grpc.AgentCardSignature.parser(), + extensionRegistry); + if (signaturesBuilder_ == null) { + ensureSignaturesIsMutable(); + signatures_.add(m); + } else { + signaturesBuilder_.addMessage(m); + } + break; + } // case 138 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -3333,6 +3526,18 @@ private void ensureSecurityIsMutable() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3348,6 +3553,18 @@ public java.util.List getSecurityList() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3363,6 +3580,18 @@ public int getSecurityCount() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3378,6 +3607,18 @@ public io.a2a.grpc.Security getSecurity(int index) { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3400,6 +3641,18 @@ public Builder setSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3419,6 +3672,18 @@ public Builder setSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3440,6 +3705,18 @@ public Builder addSecurity(io.a2a.grpc.Security value) { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3462,6 +3739,18 @@ public Builder addSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3481,6 +3770,18 @@ public Builder addSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3500,6 +3801,18 @@ public Builder addSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3520,6 +3833,18 @@ public Builder addAllSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3538,6 +3863,18 @@ public Builder clearSecurity() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3556,6 +3893,18 @@ public Builder removeSecurity(int index) { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3568,6 +3917,18 @@ public io.a2a.grpc.Security.Builder getSecurityBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3583,6 +3944,18 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3599,6 +3972,18 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3611,6 +3996,18 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3624,6 +4021,18 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
+     * This list can be seen as an OR of ANDs. Each object in the list describes
+     * one possible set of security requirements that must be present on a
+     * request. This allows specifying, for example, "callers must either use
+     * OAuth OR an API Key AND mTLS."
+     * Example:
+     * security {
+     * schemes { key: "oauth" value { list: ["read"] } }
+     * }
+     * security {
+     * schemes { key: "api-key" }
+     * schemes { key: "mtls" }
+     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -4357,6 +4766,318 @@ public Builder clearSupportsAuthenticatedExtendedCard() { return this; } + private java.util.List signatures_ = + java.util.Collections.emptyList(); + private void ensureSignaturesIsMutable() { + if (!((bitField0_ & 0x00010000) != 0)) { + signatures_ = new java.util.ArrayList(signatures_); + bitField0_ |= 0x00010000; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentCardSignature, io.a2a.grpc.AgentCardSignature.Builder, io.a2a.grpc.AgentCardSignatureOrBuilder> signaturesBuilder_; + + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public java.util.List getSignaturesList() { + if (signaturesBuilder_ == null) { + return java.util.Collections.unmodifiableList(signatures_); + } else { + return signaturesBuilder_.getMessageList(); + } + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public int getSignaturesCount() { + if (signaturesBuilder_ == null) { + return signatures_.size(); + } else { + return signaturesBuilder_.getCount(); + } + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public io.a2a.grpc.AgentCardSignature getSignatures(int index) { + if (signaturesBuilder_ == null) { + return signatures_.get(index); + } else { + return signaturesBuilder_.getMessage(index); + } + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder setSignatures( + int index, io.a2a.grpc.AgentCardSignature value) { + if (signaturesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSignaturesIsMutable(); + signatures_.set(index, value); + onChanged(); + } else { + signaturesBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder setSignatures( + int index, io.a2a.grpc.AgentCardSignature.Builder builderForValue) { + if (signaturesBuilder_ == null) { + ensureSignaturesIsMutable(); + signatures_.set(index, builderForValue.build()); + onChanged(); + } else { + signaturesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder addSignatures(io.a2a.grpc.AgentCardSignature value) { + if (signaturesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSignaturesIsMutable(); + signatures_.add(value); + onChanged(); + } else { + signaturesBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder addSignatures( + int index, io.a2a.grpc.AgentCardSignature value) { + if (signaturesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSignaturesIsMutable(); + signatures_.add(index, value); + onChanged(); + } else { + signaturesBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder addSignatures( + io.a2a.grpc.AgentCardSignature.Builder builderForValue) { + if (signaturesBuilder_ == null) { + ensureSignaturesIsMutable(); + signatures_.add(builderForValue.build()); + onChanged(); + } else { + signaturesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder addSignatures( + int index, io.a2a.grpc.AgentCardSignature.Builder builderForValue) { + if (signaturesBuilder_ == null) { + ensureSignaturesIsMutable(); + signatures_.add(index, builderForValue.build()); + onChanged(); + } else { + signaturesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder addAllSignatures( + java.lang.Iterable values) { + if (signaturesBuilder_ == null) { + ensureSignaturesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, signatures_); + onChanged(); + } else { + signaturesBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder clearSignatures() { + if (signaturesBuilder_ == null) { + signatures_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00010000); + onChanged(); + } else { + signaturesBuilder_.clear(); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public Builder removeSignatures(int index) { + if (signaturesBuilder_ == null) { + ensureSignaturesIsMutable(); + signatures_.remove(index); + onChanged(); + } else { + signaturesBuilder_.remove(index); + } + return this; + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public io.a2a.grpc.AgentCardSignature.Builder getSignaturesBuilder( + int index) { + return internalGetSignaturesFieldBuilder().getBuilder(index); + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( + int index) { + if (signaturesBuilder_ == null) { + return signatures_.get(index); } else { + return signaturesBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public java.util.List + getSignaturesOrBuilderList() { + if (signaturesBuilder_ != null) { + return signaturesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(signatures_); + } + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder() { + return internalGetSignaturesFieldBuilder().addBuilder( + io.a2a.grpc.AgentCardSignature.getDefaultInstance()); + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder( + int index) { + return internalGetSignaturesFieldBuilder().addBuilder( + index, io.a2a.grpc.AgentCardSignature.getDefaultInstance()); + } + /** + *
+     * JSON Web Signatures computed for this AgentCard.
+     * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + public java.util.List + getSignaturesBuilderList() { + return internalGetSignaturesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentCardSignature, io.a2a.grpc.AgentCardSignature.Builder, io.a2a.grpc.AgentCardSignatureOrBuilder> + internalGetSignaturesFieldBuilder() { + if (signaturesBuilder_ == null) { + signaturesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentCardSignature, io.a2a.grpc.AgentCardSignature.Builder, io.a2a.grpc.AgentCardSignatureOrBuilder>( + signatures_, + ((bitField0_ & 0x00010000) != 0), + getParentForChildren(), + isClean()); + signatures_ = null; + } + return signaturesBuilder_; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCard) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java index 6a992f83e..3ad50a5fd 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java @@ -319,6 +319,18 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -329,6 +341,18 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -338,6 +362,18 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -347,6 +383,18 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -357,6 +405,18 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
+   * This list can be seen as an OR of ANDs. Each object in the list describes
+   * one possible set of security requirements that must be present on a
+   * request. This allows specifying, for example, "callers must either use
+   * OAuth OR an API Key AND mTLS."
+   * Example:
+   * security {
+   * schemes { key: "oauth" value { list: ["read"] } }
+   * }
+   * security {
+   * schemes { key: "api-key" }
+   * schemes { key: "mtls" }
+   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -519,4 +579,48 @@ io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * @return The supportsAuthenticatedExtendedCard. */ boolean getSupportsAuthenticatedExtendedCard(); + + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + java.util.List + getSignaturesList(); + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + io.a2a.grpc.AgentCardSignature getSignatures(int index); + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + int getSignaturesCount(); + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + java.util.List + getSignaturesOrBuilderList(); + /** + *
+   * JSON Web Signatures computed for this AgentCard.
+   * 
+ * + * repeated .a2a.v1.AgentCardSignature signatures = 17; + */ + io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( + int index); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java new file mode 100644 index 000000000..790bcd941 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java @@ -0,0 +1,952 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * AgentCardSignature represents a JWS signature of an AgentCard.
+ * This follows the JSON format of an RFC 7515 JSON Web Signature (JWS).
+ * 
+ * + * Protobuf type {@code a2a.v1.AgentCardSignature} + */ +@com.google.protobuf.Generated +public final class AgentCardSignature extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.AgentCardSignature) + AgentCardSignatureOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + AgentCardSignature.class.getName()); + } + // Use AgentCardSignature.newBuilder() to construct. + private AgentCardSignature(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private AgentCardSignature() { + protected_ = ""; + signature_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCardSignature_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCardSignature_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentCardSignature.class, io.a2a.grpc.AgentCardSignature.Builder.class); + } + + private int bitField0_; + public static final int PROTECTED_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object protected_ = ""; + /** + *
+   * The protected JWS header for the signature. This is always a
+   * base64url-encoded JSON object. Required.
+   * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The protected. + */ + @java.lang.Override + public java.lang.String getProtected() { + java.lang.Object ref = protected_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protected_ = s; + return s; + } + } + /** + *
+   * The protected JWS header for the signature. This is always a
+   * base64url-encoded JSON object. Required.
+   * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protected. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getProtectedBytes() { + java.lang.Object ref = protected_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + protected_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SIGNATURE_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object signature_ = ""; + /** + *
+   * The computed signature, base64url-encoded. Required.
+   * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The signature. + */ + @java.lang.Override + public java.lang.String getSignature() { + java.lang.Object ref = signature_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + signature_ = s; + return s; + } + } + /** + *
+   * The computed signature, base64url-encoded. Required.
+   * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for signature. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getSignatureBytes() { + java.lang.Object ref = signature_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + signature_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int HEADER_FIELD_NUMBER = 3; + private com.google.protobuf.Struct header_; + /** + *
+   * The unprotected JWS header values.
+   * 
+ * + * .google.protobuf.Struct header = 3; + * @return Whether the header field is set. + */ + @java.lang.Override + public boolean hasHeader() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The unprotected JWS header values.
+   * 
+ * + * .google.protobuf.Struct header = 3; + * @return The header. + */ + @java.lang.Override + public com.google.protobuf.Struct getHeader() { + return header_ == null ? com.google.protobuf.Struct.getDefaultInstance() : header_; + } + /** + *
+   * The unprotected JWS header values.
+   * 
+ * + * .google.protobuf.Struct header = 3; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getHeaderOrBuilder() { + return header_ == null ? com.google.protobuf.Struct.getDefaultInstance() : header_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protected_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, protected_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(signature_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, signature_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(3, getHeader()); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protected_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, protected_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(signature_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, signature_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, getHeader()); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.AgentCardSignature)) { + return super.equals(obj); + } + io.a2a.grpc.AgentCardSignature other = (io.a2a.grpc.AgentCardSignature) obj; + + if (!getProtected() + .equals(other.getProtected())) return false; + if (!getSignature() + .equals(other.getSignature())) return false; + if (hasHeader() != other.hasHeader()) return false; + if (hasHeader()) { + if (!getHeader() + .equals(other.getHeader())) return false; + } + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + PROTECTED_FIELD_NUMBER; + hash = (53 * hash) + getProtected().hashCode(); + hash = (37 * hash) + SIGNATURE_FIELD_NUMBER; + hash = (53 * hash) + getSignature().hashCode(); + if (hasHeader()) { + hash = (37 * hash) + HEADER_FIELD_NUMBER; + hash = (53 * hash) + getHeader().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.AgentCardSignature parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCardSignature parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.AgentCardSignature parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.AgentCardSignature parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.AgentCardSignature parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.AgentCardSignature parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.AgentCardSignature prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * AgentCardSignature represents a JWS signature of an AgentCard.
+   * This follows the JSON format of an RFC 7515 JSON Web Signature (JWS).
+   * 
+ * + * Protobuf type {@code a2a.v1.AgentCardSignature} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.AgentCardSignature) + io.a2a.grpc.AgentCardSignatureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCardSignature_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCardSignature_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.AgentCardSignature.class, io.a2a.grpc.AgentCardSignature.Builder.class); + } + + // Construct using io.a2a.grpc.AgentCardSignature.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetHeaderFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + protected_ = ""; + signature_ = ""; + header_ = null; + if (headerBuilder_ != null) { + headerBuilder_.dispose(); + headerBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_AgentCardSignature_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.AgentCardSignature getDefaultInstanceForType() { + return io.a2a.grpc.AgentCardSignature.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.AgentCardSignature build() { + io.a2a.grpc.AgentCardSignature result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.AgentCardSignature buildPartial() { + io.a2a.grpc.AgentCardSignature result = new io.a2a.grpc.AgentCardSignature(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.AgentCardSignature result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.protected_ = protected_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.signature_ = signature_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000004) != 0)) { + result.header_ = headerBuilder_ == null + ? header_ + : headerBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.AgentCardSignature) { + return mergeFrom((io.a2a.grpc.AgentCardSignature)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.AgentCardSignature other) { + if (other == io.a2a.grpc.AgentCardSignature.getDefaultInstance()) return this; + if (!other.getProtected().isEmpty()) { + protected_ = other.protected_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getSignature().isEmpty()) { + signature_ = other.signature_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.hasHeader()) { + mergeHeader(other.getHeader()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + protected_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + signature_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + input.readMessage( + internalGetHeaderFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object protected_ = ""; + /** + *
+     * The protected JWS header for the signature. This is always a
+     * base64url-encoded JSON object. Required.
+     * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The protected. + */ + public java.lang.String getProtected() { + java.lang.Object ref = protected_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protected_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The protected JWS header for the signature. This is always a
+     * base64url-encoded JSON object. Required.
+     * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protected. + */ + public com.google.protobuf.ByteString + getProtectedBytes() { + java.lang.Object ref = protected_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + protected_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The protected JWS header for the signature. This is always a
+     * base64url-encoded JSON object. Required.
+     * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The protected to set. + * @return This builder for chaining. + */ + public Builder setProtected( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + protected_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * The protected JWS header for the signature. This is always a
+     * base64url-encoded JSON object. Required.
+     * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearProtected() { + protected_ = getDefaultInstance().getProtected(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * The protected JWS header for the signature. This is always a
+     * base64url-encoded JSON object. Required.
+     * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for protected to set. + * @return This builder for chaining. + */ + public Builder setProtectedBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + protected_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object signature_ = ""; + /** + *
+     * The computed signature, base64url-encoded. Required.
+     * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The signature. + */ + public java.lang.String getSignature() { + java.lang.Object ref = signature_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + signature_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * The computed signature, base64url-encoded. Required.
+     * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for signature. + */ + public com.google.protobuf.ByteString + getSignatureBytes() { + java.lang.Object ref = signature_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + signature_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * The computed signature, base64url-encoded. Required.
+     * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @param value The signature to set. + * @return This builder for chaining. + */ + public Builder setSignature( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + signature_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * The computed signature, base64url-encoded. Required.
+     * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearSignature() { + signature_ = getDefaultInstance().getSignature(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * The computed signature, base64url-encoded. Required.
+     * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for signature to set. + * @return This builder for chaining. + */ + public Builder setSignatureBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + signature_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private com.google.protobuf.Struct header_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> headerBuilder_; + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + * @return Whether the header field is set. + */ + public boolean hasHeader() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + * @return The header. + */ + public com.google.protobuf.Struct getHeader() { + if (headerBuilder_ == null) { + return header_ == null ? com.google.protobuf.Struct.getDefaultInstance() : header_; + } else { + return headerBuilder_.getMessage(); + } + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + public Builder setHeader(com.google.protobuf.Struct value) { + if (headerBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + header_ = value; + } else { + headerBuilder_.setMessage(value); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + public Builder setHeader( + com.google.protobuf.Struct.Builder builderForValue) { + if (headerBuilder_ == null) { + header_ = builderForValue.build(); + } else { + headerBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + public Builder mergeHeader(com.google.protobuf.Struct value) { + if (headerBuilder_ == null) { + if (((bitField0_ & 0x00000004) != 0) && + header_ != null && + header_ != com.google.protobuf.Struct.getDefaultInstance()) { + getHeaderBuilder().mergeFrom(value); + } else { + header_ = value; + } + } else { + headerBuilder_.mergeFrom(value); + } + if (header_ != null) { + bitField0_ |= 0x00000004; + onChanged(); + } + return this; + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + public Builder clearHeader() { + bitField0_ = (bitField0_ & ~0x00000004); + header_ = null; + if (headerBuilder_ != null) { + headerBuilder_.dispose(); + headerBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + public com.google.protobuf.Struct.Builder getHeaderBuilder() { + bitField0_ |= 0x00000004; + onChanged(); + return internalGetHeaderFieldBuilder().getBuilder(); + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + public com.google.protobuf.StructOrBuilder getHeaderOrBuilder() { + if (headerBuilder_ != null) { + return headerBuilder_.getMessageOrBuilder(); + } else { + return header_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : header_; + } + } + /** + *
+     * The unprotected JWS header values.
+     * 
+ * + * .google.protobuf.Struct header = 3; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetHeaderFieldBuilder() { + if (headerBuilder_ == null) { + headerBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getHeader(), + getParentForChildren(), + isClean()); + header_ = null; + } + return headerBuilder_; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCardSignature) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.AgentCardSignature) + private static final io.a2a.grpc.AgentCardSignature DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.AgentCardSignature(); + } + + public static io.a2a.grpc.AgentCardSignature getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AgentCardSignature parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.AgentCardSignature getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java new file mode 100644 index 000000000..d863a9917 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java @@ -0,0 +1,81 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface AgentCardSignatureOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.AgentCardSignature) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * The protected JWS header for the signature. This is always a
+   * base64url-encoded JSON object. Required.
+   * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The protected. + */ + java.lang.String getProtected(); + /** + *
+   * The protected JWS header for the signature. This is always a
+   * base64url-encoded JSON object. Required.
+   * 
+ * + * string protected = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protected. + */ + com.google.protobuf.ByteString + getProtectedBytes(); + + /** + *
+   * The computed signature, base64url-encoded. Required.
+   * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The signature. + */ + java.lang.String getSignature(); + /** + *
+   * The computed signature, base64url-encoded. Required.
+   * 
+ * + * string signature = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for signature. + */ + com.google.protobuf.ByteString + getSignatureBytes(); + + /** + *
+   * The unprotected JWS header values.
+   * 
+ * + * .google.protobuf.Struct header = 3; + * @return Whether the header field is set. + */ + boolean hasHeader(); + /** + *
+   * The unprotected JWS header values.
+   * 
+ * + * .google.protobuf.Struct header = 3; + * @return The header. + */ + com.google.protobuf.Struct getHeader(); + /** + *
+   * The unprotected JWS header values.
+   * 
+ * + * .google.protobuf.Struct header = 3; + */ + com.google.protobuf.StructOrBuilder getHeaderOrBuilder(); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java index 1d6fed46c..794c3db57 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -47,6 +47,7 @@ private AgentSkill() { com.google.protobuf.LazyStringArrayList.emptyList(); outputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); + security_ = java.util.Collections.emptyList(); } public static final com.google.protobuf.Descriptors.Descriptor @@ -433,6 +434,87 @@ public java.lang.String getOutputModes(int index) { return outputModes_.getByteString(index); } + public static final int SECURITY_FIELD_NUMBER = 8; + @SuppressWarnings("serial") + private java.util.List security_; + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + @java.lang.Override + public java.util.List getSecurityList() { + return security_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + @java.lang.Override + public java.util.List + getSecurityOrBuilderList() { + return security_; + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + @java.lang.Override + public int getSecurityCount() { + return security_.size(); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + @java.lang.Override + public io.a2a.grpc.Security getSecurity(int index) { + return security_.get(index); + } + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + @java.lang.Override + public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + int index) { + return security_.get(index); + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -468,6 +550,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < outputModes_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 7, outputModes_.getRaw(i)); } + for (int i = 0; i < security_.size(); i++) { + output.writeMessage(8, security_.get(i)); + } getUnknownFields().writeTo(output); } @@ -518,6 +603,10 @@ public int getSerializedSize() { size += dataSize; size += 1 * getOutputModesList().size(); } + for (int i = 0; i < security_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, security_.get(i)); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -547,6 +636,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getInputModesList())) return false; if (!getOutputModesList() .equals(other.getOutputModesList())) return false; + if (!getSecurityList() + .equals(other.getSecurityList())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -580,6 +671,10 @@ public int hashCode() { hash = (37 * hash) + OUTPUT_MODES_FIELD_NUMBER; hash = (53 * hash) + getOutputModesList().hashCode(); } + if (getSecurityCount() > 0) { + hash = (37 * hash) + SECURITY_FIELD_NUMBER; + hash = (53 * hash) + getSecurityList().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -730,6 +825,13 @@ public Builder clear() { com.google.protobuf.LazyStringArrayList.emptyList(); outputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); + if (securityBuilder_ == null) { + security_ = java.util.Collections.emptyList(); + } else { + security_ = null; + securityBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000080); return this; } @@ -756,11 +858,24 @@ public io.a2a.grpc.AgentSkill build() { @java.lang.Override public io.a2a.grpc.AgentSkill buildPartial() { io.a2a.grpc.AgentSkill result = new io.a2a.grpc.AgentSkill(this); + buildPartialRepeatedFields(result); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } + private void buildPartialRepeatedFields(io.a2a.grpc.AgentSkill result) { + if (securityBuilder_ == null) { + if (((bitField0_ & 0x00000080) != 0)) { + security_ = java.util.Collections.unmodifiableList(security_); + bitField0_ = (bitField0_ & ~0x00000080); + } + result.security_ = security_; + } else { + result.security_ = securityBuilder_.build(); + } + } + private void buildPartial0(io.a2a.grpc.AgentSkill result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { @@ -857,6 +972,32 @@ public Builder mergeFrom(io.a2a.grpc.AgentSkill other) { } onChanged(); } + if (securityBuilder_ == null) { + if (!other.security_.isEmpty()) { + if (security_.isEmpty()) { + security_ = other.security_; + bitField0_ = (bitField0_ & ~0x00000080); + } else { + ensureSecurityIsMutable(); + security_.addAll(other.security_); + } + onChanged(); + } + } else { + if (!other.security_.isEmpty()) { + if (securityBuilder_.isEmpty()) { + securityBuilder_.dispose(); + securityBuilder_ = null; + security_ = other.security_; + bitField0_ = (bitField0_ & ~0x00000080); + securityBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetSecurityFieldBuilder() : null; + } else { + securityBuilder_.addAllMessages(other.security_); + } + } + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -922,6 +1063,19 @@ public Builder mergeFrom( outputModes_.add(s); break; } // case 58 + case 66: { + io.a2a.grpc.Security m = + input.readMessage( + io.a2a.grpc.Security.parser(), + extensionRegistry); + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.add(m); + } else { + securityBuilder_.addMessage(m); + } + break; + } // case 66 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -1844,6 +1998,390 @@ public Builder addOutputModesBytes( return this; } + private java.util.List security_ = + java.util.Collections.emptyList(); + private void ensureSecurityIsMutable() { + if (!((bitField0_ & 0x00000080) != 0)) { + security_ = new java.util.ArrayList(security_); + bitField0_ |= 0x00000080; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> securityBuilder_; + + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public java.util.List getSecurityList() { + if (securityBuilder_ == null) { + return java.util.Collections.unmodifiableList(security_); + } else { + return securityBuilder_.getMessageList(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public int getSecurityCount() { + if (securityBuilder_ == null) { + return security_.size(); + } else { + return securityBuilder_.getCount(); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public io.a2a.grpc.Security getSecurity(int index) { + if (securityBuilder_ == null) { + return security_.get(index); + } else { + return securityBuilder_.getMessage(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder setSecurity( + int index, io.a2a.grpc.Security value) { + if (securityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSecurityIsMutable(); + security_.set(index, value); + onChanged(); + } else { + securityBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder setSecurity( + int index, io.a2a.grpc.Security.Builder builderForValue) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.set(index, builderForValue.build()); + onChanged(); + } else { + securityBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder addSecurity(io.a2a.grpc.Security value) { + if (securityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSecurityIsMutable(); + security_.add(value); + onChanged(); + } else { + securityBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder addSecurity( + int index, io.a2a.grpc.Security value) { + if (securityBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSecurityIsMutable(); + security_.add(index, value); + onChanged(); + } else { + securityBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder addSecurity( + io.a2a.grpc.Security.Builder builderForValue) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.add(builderForValue.build()); + onChanged(); + } else { + securityBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder addSecurity( + int index, io.a2a.grpc.Security.Builder builderForValue) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.add(index, builderForValue.build()); + onChanged(); + } else { + securityBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder addAllSecurity( + java.lang.Iterable values) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, security_); + onChanged(); + } else { + securityBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder clearSecurity() { + if (securityBuilder_ == null) { + security_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000080); + onChanged(); + } else { + securityBuilder_.clear(); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public Builder removeSecurity(int index) { + if (securityBuilder_ == null) { + ensureSecurityIsMutable(); + security_.remove(index); + onChanged(); + } else { + securityBuilder_.remove(index); + } + return this; + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public io.a2a.grpc.Security.Builder getSecurityBuilder( + int index) { + return internalGetSecurityFieldBuilder().getBuilder(index); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + int index) { + if (securityBuilder_ == null) { + return security_.get(index); } else { + return securityBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public java.util.List + getSecurityOrBuilderList() { + if (securityBuilder_ != null) { + return securityBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(security_); + } + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public io.a2a.grpc.Security.Builder addSecurityBuilder() { + return internalGetSecurityFieldBuilder().addBuilder( + io.a2a.grpc.Security.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public io.a2a.grpc.Security.Builder addSecurityBuilder( + int index) { + return internalGetSecurityFieldBuilder().addBuilder( + index, io.a2a.grpc.Security.getDefaultInstance()); + } + /** + *
+     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+     * Security schemes necessary for the agent to leverage this skill.
+     * As in the overall AgentCard.security, this list represents a logical OR of
+     * security requirement objects. Each object is a set of security schemes
+     * that must be used together (a logical AND).
+     * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + public java.util.List + getSecurityBuilderList() { + return internalGetSecurityFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> + internalGetSecurityFieldBuilder() { + if (securityBuilder_ == null) { + securityBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder>( + security_, + ((bitField0_ & 0x00000080) != 0), + getParentForChildren(), + isClean()); + security_ = null; + } + return securityBuilder_; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentSkill) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java index d776b3112..88ebf7448 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -251,4 +251,68 @@ public interface AgentSkillOrBuilder extends */ com.google.protobuf.ByteString getOutputModesBytes(int index); + + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + java.util.List + getSecurityList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + io.a2a.grpc.Security getSecurity(int index); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + int getSecurityCount(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + java.util.List + getSecurityOrBuilderList(); + /** + *
+   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
+   * Security schemes necessary for the agent to leverage this skill.
+   * As in the overall AgentCard.security, this list represents a logical OR of
+   * security requirement objects. Each object is a set of security schemes
+   * that must be used together (a logical AND).
+   * 
+ * + * repeated .a2a.v1.Security security = 8; + */ + io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + int index); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java new file mode 100644 index 000000000..87ef07b99 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java @@ -0,0 +1,530 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + * Protobuf type {@code a2a.v1.MutualTlsSecurityScheme} + */ +@com.google.protobuf.Generated +public final class MutualTlsSecurityScheme extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.MutualTlsSecurityScheme) + MutualTlsSecuritySchemeOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + MutualTlsSecurityScheme.class.getName()); + } + // Use MutualTlsSecurityScheme.newBuilder() to construct. + private MutualTlsSecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private MutualTlsSecurityScheme() { + description_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_MutualTlsSecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.MutualTlsSecurityScheme.class, io.a2a.grpc.MutualTlsSecurityScheme.Builder.class); + } + + public static final int DESCRIPTION_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object description_ = ""; + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1; + * @return The description. + */ + @java.lang.Override + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } + } + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1; + * @return The bytes for description. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, description_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, description_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.MutualTlsSecurityScheme)) { + return super.equals(obj); + } + io.a2a.grpc.MutualTlsSecurityScheme other = (io.a2a.grpc.MutualTlsSecurityScheme) obj; + + if (!getDescription() + .equals(other.getDescription())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; + hash = (53 * hash) + getDescription().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.MutualTlsSecurityScheme parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.MutualTlsSecurityScheme parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.MutualTlsSecurityScheme parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.MutualTlsSecurityScheme prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code a2a.v1.MutualTlsSecurityScheme} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.MutualTlsSecurityScheme) + io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_MutualTlsSecurityScheme_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.MutualTlsSecurityScheme.class, io.a2a.grpc.MutualTlsSecurityScheme.Builder.class); + } + + // Construct using io.a2a.grpc.MutualTlsSecurityScheme.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + description_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.MutualTlsSecurityScheme getDefaultInstanceForType() { + return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.MutualTlsSecurityScheme build() { + io.a2a.grpc.MutualTlsSecurityScheme result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.MutualTlsSecurityScheme buildPartial() { + io.a2a.grpc.MutualTlsSecurityScheme result = new io.a2a.grpc.MutualTlsSecurityScheme(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.MutualTlsSecurityScheme result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.description_ = description_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.MutualTlsSecurityScheme) { + return mergeFrom((io.a2a.grpc.MutualTlsSecurityScheme)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.MutualTlsSecurityScheme other) { + if (other == io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance()) return this; + if (!other.getDescription().isEmpty()) { + description_ = other.description_; + bitField0_ |= 0x00000001; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + description_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object description_ = ""; + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1; + * @return The description. + */ + public java.lang.String getDescription() { + java.lang.Object ref = description_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + description_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1; + * @return The bytes for description. + */ + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + description_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1; + * @param value The description to set. + * @return This builder for chaining. + */ + public Builder setDescription( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1; + * @return This builder for chaining. + */ + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Description of this security scheme.
+     * 
+ * + * string description = 1; + * @param value The bytes for description to set. + * @return This builder for chaining. + */ + public Builder setDescriptionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + description_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.MutualTlsSecurityScheme) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.MutualTlsSecurityScheme) + private static final io.a2a.grpc.MutualTlsSecurityScheme DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.MutualTlsSecurityScheme(); + } + + public static io.a2a.grpc.MutualTlsSecurityScheme getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public MutualTlsSecurityScheme parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.MutualTlsSecurityScheme getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java new file mode 100644 index 000000000..a18e1311a --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java @@ -0,0 +1,32 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface MutualTlsSecuritySchemeOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.MutualTlsSecurityScheme) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1; + * @return The description. + */ + java.lang.String getDescription(); + /** + *
+   * Description of this security scheme.
+   * 
+ * + * string description = 1; + * @return The bytes for description. + */ + com.google.protobuf.ByteString + getDescriptionBytes(); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java index 11065d682..9de35587e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java @@ -29,6 +29,7 @@ private OAuth2SecurityScheme(com.google.protobuf.GeneratedMessage.Builder bui } private OAuth2SecurityScheme() { description_ = ""; + oauth2MetadataUrl_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -130,6 +131,55 @@ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { return flows_ == null ? io.a2a.grpc.OAuthFlows.getDefaultInstance() : flows_; } + public static final int OAUTH2_METADATA_URL_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object oauth2MetadataUrl_ = ""; + /** + *
+   * URL to the oauth2 authorization server metadata
+   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * 
+ * + * string oauth2_metadata_url = 3; + * @return The oauth2MetadataUrl. + */ + @java.lang.Override + public java.lang.String getOauth2MetadataUrl() { + java.lang.Object ref = oauth2MetadataUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + oauth2MetadataUrl_ = s; + return s; + } + } + /** + *
+   * URL to the oauth2 authorization server metadata
+   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * 
+ * + * string oauth2_metadata_url = 3; + * @return The bytes for oauth2MetadataUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getOauth2MetadataUrlBytes() { + java.lang.Object ref = oauth2MetadataUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + oauth2MetadataUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -150,6 +200,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) != 0)) { output.writeMessage(2, getFlows()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(oauth2MetadataUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, oauth2MetadataUrl_); + } getUnknownFields().writeTo(output); } @@ -166,6 +219,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(2, getFlows()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(oauth2MetadataUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, oauth2MetadataUrl_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -188,6 +244,8 @@ public boolean equals(final java.lang.Object obj) { if (!getFlows() .equals(other.getFlows())) return false; } + if (!getOauth2MetadataUrl() + .equals(other.getOauth2MetadataUrl())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -205,6 +263,8 @@ public int hashCode() { hash = (37 * hash) + FLOWS_FIELD_NUMBER; hash = (53 * hash) + getFlows().hashCode(); } + hash = (37 * hash) + OAUTH2_METADATA_URL_FIELD_NUMBER; + hash = (53 * hash) + getOauth2MetadataUrl().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -348,6 +408,7 @@ public Builder clear() { flowsBuilder_.dispose(); flowsBuilder_ = null; } + oauth2MetadataUrl_ = ""; return this; } @@ -391,6 +452,9 @@ private void buildPartial0(io.a2a.grpc.OAuth2SecurityScheme result) { : flowsBuilder_.build(); to_bitField0_ |= 0x00000001; } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.oauth2MetadataUrl_ = oauth2MetadataUrl_; + } result.bitField0_ |= to_bitField0_; } @@ -414,6 +478,11 @@ public Builder mergeFrom(io.a2a.grpc.OAuth2SecurityScheme other) { if (other.hasFlows()) { mergeFlows(other.getFlows()); } + if (!other.getOauth2MetadataUrl().isEmpty()) { + oauth2MetadataUrl_ = other.oauth2MetadataUrl_; + bitField0_ |= 0x00000004; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -452,6 +521,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000002; break; } // case 18 + case 26: { + oauth2MetadataUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -718,6 +792,103 @@ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { return flowsBuilder_; } + private java.lang.Object oauth2MetadataUrl_ = ""; + /** + *
+     * URL to the oauth2 authorization server metadata
+     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * 
+ * + * string oauth2_metadata_url = 3; + * @return The oauth2MetadataUrl. + */ + public java.lang.String getOauth2MetadataUrl() { + java.lang.Object ref = oauth2MetadataUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + oauth2MetadataUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * URL to the oauth2 authorization server metadata
+     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * 
+ * + * string oauth2_metadata_url = 3; + * @return The bytes for oauth2MetadataUrl. + */ + public com.google.protobuf.ByteString + getOauth2MetadataUrlBytes() { + java.lang.Object ref = oauth2MetadataUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + oauth2MetadataUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * URL to the oauth2 authorization server metadata
+     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * 
+ * + * string oauth2_metadata_url = 3; + * @param value The oauth2MetadataUrl to set. + * @return This builder for chaining. + */ + public Builder setOauth2MetadataUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + oauth2MetadataUrl_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * URL to the oauth2 authorization server metadata
+     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * 
+ * + * string oauth2_metadata_url = 3; + * @return This builder for chaining. + */ + public Builder clearOauth2MetadataUrl() { + oauth2MetadataUrl_ = getDefaultInstance().getOauth2MetadataUrl(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
+     * URL to the oauth2 authorization server metadata
+     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * 
+ * + * string oauth2_metadata_url = 3; + * @param value The bytes for oauth2MetadataUrl to set. + * @return This builder for chaining. + */ + public Builder setOauth2MetadataUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + oauth2MetadataUrl_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.OAuth2SecurityScheme) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java index 76620c1ba..84c35ce0c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java @@ -56,4 +56,26 @@ public interface OAuth2SecuritySchemeOrBuilder extends * .a2a.v1.OAuthFlows flows = 2; */ io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder(); + + /** + *
+   * URL to the oauth2 authorization server metadata
+   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * 
+ * + * string oauth2_metadata_url = 3; + * @return The oauth2MetadataUrl. + */ + java.lang.String getOauth2MetadataUrl(); + /** + *
+   * URL to the oauth2 authorization server metadata
+   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * 
+ * + * string oauth2_metadata_url = 3; + * @return The bytes for oauth2MetadataUrl. + */ + com.google.protobuf.ByteString + getOauth2MetadataUrlBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java index b6726d097..90bc8672d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java @@ -53,6 +53,7 @@ public enum SchemeCase HTTP_AUTH_SECURITY_SCHEME(2), OAUTH2_SECURITY_SCHEME(3), OPEN_ID_CONNECT_SECURITY_SCHEME(4), + MTLS_SECURITY_SCHEME(5), SCHEME_NOT_SET(0); private final int value; private SchemeCase(int value) { @@ -74,6 +75,7 @@ public static SchemeCase forNumber(int value) { case 2: return HTTP_AUTH_SECURITY_SCHEME; case 3: return OAUTH2_SECURITY_SCHEME; case 4: return OPEN_ID_CONNECT_SECURITY_SCHEME; + case 5: return MTLS_SECURITY_SCHEME; case 0: return SCHEME_NOT_SET; default: return null; } @@ -213,6 +215,37 @@ public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecurity return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); } + public static final int MTLS_SECURITY_SCHEME_FIELD_NUMBER = 5; + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + * @return Whether the mtlsSecurityScheme field is set. + */ + @java.lang.Override + public boolean hasMtlsSecurityScheme() { + return schemeCase_ == 5; + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + * @return The mtlsSecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.MutualTlsSecurityScheme getMtlsSecurityScheme() { + if (schemeCase_ == 5) { + return (io.a2a.grpc.MutualTlsSecurityScheme) scheme_; + } + return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + @java.lang.Override + public io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder getMtlsSecuritySchemeOrBuilder() { + if (schemeCase_ == 5) { + return (io.a2a.grpc.MutualTlsSecurityScheme) scheme_; + } + return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -239,6 +272,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (schemeCase_ == 4) { output.writeMessage(4, (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_); } + if (schemeCase_ == 5) { + output.writeMessage(5, (io.a2a.grpc.MutualTlsSecurityScheme) scheme_); + } getUnknownFields().writeTo(output); } @@ -264,6 +300,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(4, (io.a2a.grpc.OpenIdConnectSecurityScheme) scheme_); } + if (schemeCase_ == 5) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, (io.a2a.grpc.MutualTlsSecurityScheme) scheme_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -297,6 +337,10 @@ public boolean equals(final java.lang.Object obj) { if (!getOpenIdConnectSecurityScheme() .equals(other.getOpenIdConnectSecurityScheme())) return false; break; + case 5: + if (!getMtlsSecurityScheme() + .equals(other.getMtlsSecurityScheme())) return false; + break; case 0: default: } @@ -328,6 +372,10 @@ public int hashCode() { hash = (37 * hash) + OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_NUMBER; hash = (53 * hash) + getOpenIdConnectSecurityScheme().hashCode(); break; + case 5: + hash = (37 * hash) + MTLS_SECURITY_SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getMtlsSecurityScheme().hashCode(); + break; case 0: default: } @@ -474,6 +522,9 @@ public Builder clear() { if (openIdConnectSecuritySchemeBuilder_ != null) { openIdConnectSecuritySchemeBuilder_.clear(); } + if (mtlsSecuritySchemeBuilder_ != null) { + mtlsSecuritySchemeBuilder_.clear(); + } schemeCase_ = 0; scheme_ = null; return this; @@ -531,6 +582,10 @@ private void buildPartialOneofs(io.a2a.grpc.SecurityScheme result) { openIdConnectSecuritySchemeBuilder_ != null) { result.scheme_ = openIdConnectSecuritySchemeBuilder_.build(); } + if (schemeCase_ == 5 && + mtlsSecuritySchemeBuilder_ != null) { + result.scheme_ = mtlsSecuritySchemeBuilder_.build(); + } } @java.lang.Override @@ -562,6 +617,10 @@ public Builder mergeFrom(io.a2a.grpc.SecurityScheme other) { mergeOpenIdConnectSecurityScheme(other.getOpenIdConnectSecurityScheme()); break; } + case MTLS_SECURITY_SCHEME: { + mergeMtlsSecurityScheme(other.getMtlsSecurityScheme()); + break; + } case SCHEME_NOT_SET: { break; } @@ -620,6 +679,13 @@ public Builder mergeFrom( schemeCase_ = 4; break; } // case 34 + case 42: { + input.readMessage( + internalGetMtlsSecuritySchemeFieldBuilder().getBuilder(), + extensionRegistry); + schemeCase_ = 5; + break; + } // case 42 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -1220,6 +1286,148 @@ public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecurity return openIdConnectSecuritySchemeBuilder_; } + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.MutualTlsSecurityScheme, io.a2a.grpc.MutualTlsSecurityScheme.Builder, io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder> mtlsSecuritySchemeBuilder_; + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + * @return Whether the mtlsSecurityScheme field is set. + */ + @java.lang.Override + public boolean hasMtlsSecurityScheme() { + return schemeCase_ == 5; + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + * @return The mtlsSecurityScheme. + */ + @java.lang.Override + public io.a2a.grpc.MutualTlsSecurityScheme getMtlsSecurityScheme() { + if (mtlsSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 5) { + return (io.a2a.grpc.MutualTlsSecurityScheme) scheme_; + } + return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } else { + if (schemeCase_ == 5) { + return mtlsSecuritySchemeBuilder_.getMessage(); + } + return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + public Builder setMtlsSecurityScheme(io.a2a.grpc.MutualTlsSecurityScheme value) { + if (mtlsSecuritySchemeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + scheme_ = value; + onChanged(); + } else { + mtlsSecuritySchemeBuilder_.setMessage(value); + } + schemeCase_ = 5; + return this; + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + public Builder setMtlsSecurityScheme( + io.a2a.grpc.MutualTlsSecurityScheme.Builder builderForValue) { + if (mtlsSecuritySchemeBuilder_ == null) { + scheme_ = builderForValue.build(); + onChanged(); + } else { + mtlsSecuritySchemeBuilder_.setMessage(builderForValue.build()); + } + schemeCase_ = 5; + return this; + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + public Builder mergeMtlsSecurityScheme(io.a2a.grpc.MutualTlsSecurityScheme value) { + if (mtlsSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 5 && + scheme_ != io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance()) { + scheme_ = io.a2a.grpc.MutualTlsSecurityScheme.newBuilder((io.a2a.grpc.MutualTlsSecurityScheme) scheme_) + .mergeFrom(value).buildPartial(); + } else { + scheme_ = value; + } + onChanged(); + } else { + if (schemeCase_ == 5) { + mtlsSecuritySchemeBuilder_.mergeFrom(value); + } else { + mtlsSecuritySchemeBuilder_.setMessage(value); + } + } + schemeCase_ = 5; + return this; + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + public Builder clearMtlsSecurityScheme() { + if (mtlsSecuritySchemeBuilder_ == null) { + if (schemeCase_ == 5) { + schemeCase_ = 0; + scheme_ = null; + onChanged(); + } + } else { + if (schemeCase_ == 5) { + schemeCase_ = 0; + scheme_ = null; + } + mtlsSecuritySchemeBuilder_.clear(); + } + return this; + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + public io.a2a.grpc.MutualTlsSecurityScheme.Builder getMtlsSecuritySchemeBuilder() { + return internalGetMtlsSecuritySchemeFieldBuilder().getBuilder(); + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + @java.lang.Override + public io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder getMtlsSecuritySchemeOrBuilder() { + if ((schemeCase_ == 5) && (mtlsSecuritySchemeBuilder_ != null)) { + return mtlsSecuritySchemeBuilder_.getMessageOrBuilder(); + } else { + if (schemeCase_ == 5) { + return (io.a2a.grpc.MutualTlsSecurityScheme) scheme_; + } + return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } + } + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.MutualTlsSecurityScheme, io.a2a.grpc.MutualTlsSecurityScheme.Builder, io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder> + internalGetMtlsSecuritySchemeFieldBuilder() { + if (mtlsSecuritySchemeBuilder_ == null) { + if (!(schemeCase_ == 5)) { + scheme_ = io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); + } + mtlsSecuritySchemeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.MutualTlsSecurityScheme, io.a2a.grpc.MutualTlsSecurityScheme.Builder, io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder>( + (io.a2a.grpc.MutualTlsSecurityScheme) scheme_, + getParentForChildren(), + isClean()); + scheme_ = null; + } + schemeCase_ = 5; + onChanged(); + return mtlsSecuritySchemeBuilder_; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.SecurityScheme) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java index b79be8faf..83a6c0f00 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java @@ -70,5 +70,20 @@ public interface SecuritySchemeOrBuilder extends */ io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder(); + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + * @return Whether the mtlsSecurityScheme field is set. + */ + boolean hasMtlsSecurityScheme(); + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + * @return The mtlsSecurityScheme. + */ + io.a2a.grpc.MutualTlsSecurityScheme getMtlsSecurityScheme(); + /** + * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; + */ + io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder getMtlsSecuritySchemeOrBuilder(); + io.a2a.grpc.SecurityScheme.SchemeCase getSchemeCase(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index 53e563640..8882b6d6a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -51,7 +51,7 @@ private SendMessageRequest() { public static final int REQUEST_FIELD_NUMBER = 1; private io.a2a.grpc.Message request_; /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ @java.lang.Override @@ -59,7 +59,7 @@ public boolean hasRequest() { return ((bitField0_ & 0x00000001) != 0); } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return The request. */ @java.lang.Override @@ -67,7 +67,7 @@ public io.a2a.grpc.Message getRequest() { return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { @@ -525,14 +525,14 @@ public Builder mergeFrom( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> requestBuilder_; /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ public boolean hasRequest() { return ((bitField0_ & 0x00000001) != 0); } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return The request. */ public io.a2a.grpc.Message getRequest() { @@ -543,7 +543,7 @@ public io.a2a.grpc.Message getRequest() { } } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder setRequest(io.a2a.grpc.Message value) { if (requestBuilder_ == null) { @@ -559,7 +559,7 @@ public Builder setRequest(io.a2a.grpc.Message value) { return this; } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder setRequest( io.a2a.grpc.Message.Builder builderForValue) { @@ -573,7 +573,7 @@ public Builder setRequest( return this; } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder mergeRequest(io.a2a.grpc.Message value) { if (requestBuilder_ == null) { @@ -594,7 +594,7 @@ public Builder mergeRequest(io.a2a.grpc.Message value) { return this; } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder clearRequest() { bitField0_ = (bitField0_ & ~0x00000001); @@ -607,7 +607,7 @@ public Builder clearRequest() { return this; } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Message.Builder getRequestBuilder() { bitField0_ |= 0x00000001; @@ -615,7 +615,7 @@ public io.a2a.grpc.Message.Builder getRequestBuilder() { return internalGetRequestFieldBuilder().getBuilder(); } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { if (requestBuilder_ != null) { @@ -626,7 +626,7 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { } } /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java index fc3632f6b..13dae6c0e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java @@ -11,17 +11,17 @@ public interface SendMessageRequestOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ boolean hasRequest(); /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return The request. */ io.a2a.grpc.Message getRequest(); /** - * .a2a.v1.Message request = 1 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.MessageOrBuilder getRequestOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 73158ecf2..52a6b6719 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -18,6 +18,7 @@ import io.a2a.spec.APIKeySecurityScheme; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentCardSignature; import io.a2a.spec.AgentExtension; import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentProvider; @@ -35,10 +36,13 @@ import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.HTTPAuthSecurityScheme; import io.a2a.spec.ImplicitOAuthFlow; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; +import io.a2a.spec.MutualTLSSecurityScheme; import io.a2a.spec.OAuth2SecurityScheme; import io.a2a.spec.OAuthFlows; import io.a2a.spec.OpenIdConnectSecurityScheme; @@ -124,6 +128,9 @@ public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { builder.addAllSkills(agentCard.skills().stream().map(ToProto::agentSkill).collect(Collectors.toList())); } builder.setSupportsAuthenticatedExtendedCard(agentCard.supportsAuthenticatedExtendedCard()); + if (agentCard.signatures() != null) { + builder.addAllSignatures(agentCard.signatures().stream().map(ToProto::agentCardSignature).collect(Collectors.toList())); + } return builder.build(); } @@ -395,6 +402,27 @@ private static io.a2a.grpc.AgentSkill agentSkill(AgentSkill agentSkill) { if (agentSkill.outputModes() != null) { builder.addAllOutputModes(agentSkill.outputModes()); } + if (agentSkill.security() != null) { + builder.addAllSecurity(agentSkill.security().stream().map(s -> { + io.a2a.grpc.Security.Builder securityBuilder = io.a2a.grpc.Security.newBuilder(); + s.forEach((key, value) -> { + io.a2a.grpc.StringList.Builder stringListBuilder = io.a2a.grpc.StringList.newBuilder(); + stringListBuilder.addAllList(value); + securityBuilder.putSchemes(key, stringListBuilder.build()); + }); + return securityBuilder.build(); + }).collect(Collectors.toList())); + } + return builder.build(); + } + + private static io.a2a.grpc.AgentCardSignature agentCardSignature(AgentCardSignature agentCardSignature) { + io.a2a.grpc.AgentCardSignature.Builder builder = io.a2a.grpc.AgentCardSignature.newBuilder(); + builder.setProtected(agentCardSignature.protectedHeader()); + builder.setSignature(agentCardSignature.signature()); + if (agentCardSignature.header() != null) { + builder.setHeader(struct(agentCardSignature.header())); + } return builder.build(); } @@ -408,6 +436,8 @@ private static io.a2a.grpc.SecurityScheme securityScheme(SecurityScheme security builder.setOauth2SecurityScheme(oauthSecurityScheme((OAuth2SecurityScheme) securityScheme)); } else if (securityScheme instanceof OpenIdConnectSecurityScheme) { builder.setOpenIdConnectSecurityScheme(openIdConnectSecurityScheme((OpenIdConnectSecurityScheme) securityScheme)); + } else if (securityScheme instanceof MutualTLSSecurityScheme) { + builder.setMtlsSecurityScheme(mutualTlsSecurityScheme((MutualTLSSecurityScheme) securityScheme)); } return builder.build(); } @@ -448,6 +478,9 @@ private static io.a2a.grpc.OAuth2SecurityScheme oauthSecurityScheme(OAuth2Securi if (oauth2SecurityScheme.getFlows() != null) { builder.setFlows(oauthFlows(oauth2SecurityScheme.getFlows())); } + if (oauth2SecurityScheme.getOauth2MetadataUrl() != null) { + builder.setOauth2MetadataUrl(oauth2SecurityScheme.getOauth2MetadataUrl()); + } return builder.build(); } @@ -538,6 +571,14 @@ private static io.a2a.grpc.OpenIdConnectSecurityScheme openIdConnectSecuritySche return builder.build(); } + private static io.a2a.grpc.MutualTlsSecurityScheme mutualTlsSecurityScheme(MutualTLSSecurityScheme mutualTlsSecurityScheme) { + io.a2a.grpc.MutualTlsSecurityScheme.Builder builder = io.a2a.grpc.MutualTlsSecurityScheme.newBuilder(); + if (mutualTlsSecurityScheme.getDescription() != null) { + builder.setDescription(mutualTlsSecurityScheme.getDescription()); + } + return builder.build(); + } + private static io.a2a.grpc.AgentInterface agentInterface(AgentInterface agentInterface) { io.a2a.grpc.AgentInterface.Builder builder = io.a2a.grpc.AgentInterface.newBuilder(); if (agentInterface.transport() != null) { @@ -751,12 +792,15 @@ public static Task task(io.a2a.grpc.Task task) { } public static Message message(io.a2a.grpc.Message message) { + if (message.getMessageId().isEmpty()) { + throw new InvalidParamsError(); + } return new Message( role(message.getRole()), message.getContentList().stream().map(item -> part(item)).collect(Collectors.toList()), message.getMessageId().isEmpty() ? null : message.getMessageId(), message.getContextId().isEmpty() ? null : message.getContextId(), - message.getTaskId(), + message.getTaskId().isEmpty() ? null : message.getTaskId(), null, // referenceTaskIds is not in grpc message struct(message.getMetadata()) ); @@ -801,7 +845,7 @@ private static Part part(io.a2a.grpc.Part part) { } else if (part.hasData()) { return dataPart(part.getData()); } - return null; + throw new InvalidRequestError(); } private static TextPart textPart(String text) { @@ -814,7 +858,7 @@ private static FilePart filePart(io.a2a.grpc.FilePart filePart) { } else if (filePart.hasFileWithUri()) { return new FilePart(new FileWithUri(filePart.getMimeType(), null, filePart.getFileWithUri())); } - return null; + throw new InvalidRequestError(); } private static DataPart dataPart(io.a2a.grpc.DataPart dataPart) { @@ -839,7 +883,7 @@ private static Message.Role role(io.a2a.grpc.Role role) { case ROLE_AGENT -> Message.Role.AGENT; default -> - null; + throw new InvalidRequestError(); }; } @@ -895,7 +939,7 @@ private static Object value(Value value) { return value.getStringValue(); case NULL_VALUE: default: - return null; + throw new InvalidRequestError(); } } } diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index 8bada0795..8b1230059 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -9,7 +9,7 @@ import "google/protobuf/empty.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; -// Copied from https://github.com/a2aproject/A2A/blob/v0.2.6/specification/grpc/a2a.proto +// Copied from https://github.com/a2aproject/A2A/blob/v0.3.0/specification/grpc/a2a.proto // The only change is the 'java_package' update option csharp_namespace = "A2a.V1"; @@ -345,6 +345,7 @@ message AgentInterface { // - Skills; a set of actions/solutions the agent can perform // - Default modalities/content types supported by the agent. // - Authentication requirements +// Next ID: 18 message AgentCard { // The version of the A2A protocol this agent supports. string protocol_version = 16; @@ -375,6 +376,18 @@ message AgentCard { map security_schemes = 8; // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED // Security requirements for contacting the agent. + // This list can be seen as an OR of ANDs. Each object in the list describes + // one possible set of security requirements that must be present on a + // request. This allows specifying, for example, "callers must either use + // OAuth OR an API Key AND mTLS." + // Example: + // security { + // schemes { key: "oauth" value { list: ["read"] } } + // } + // security { + // schemes { key: "api-key" } + // schemes { key: "mtls" } + // } repeated Security security = 9; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED // The set of interaction modes that the agent supports across all skills. @@ -390,6 +403,8 @@ message AgentCard { // the user is authenticated, i.e. is the card from .well-known // different than the card from GetAgentCard. bool supports_authenticated_extended_card = 13; + // JSON Web Signatures computed for this AgentCard. + repeated AgentCardSignature signatures = 17; } // Represents information about the service provider of an agent. @@ -452,6 +467,24 @@ message AgentSkill { repeated string input_modes = 6; // Possible output modalities produced repeated string output_modes = 7; + // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED + // Security schemes necessary for the agent to leverage this skill. + // As in the overall AgentCard.security, this list represents a logical OR of + // security requirement objects. Each object is a set of security schemes + // that must be used together (a logical AND). + repeated Security security = 8; +} + +// AgentCardSignature represents a JWS signature of an AgentCard. +// This follows the JSON format of an RFC 7515 JSON Web Signature (JWS). +message AgentCardSignature { + // The protected JWS header for the signature. This is always a + // base64url-encoded JSON object. Required. + string protected = 1 [(google.api.field_behavior) = REQUIRED]; + // The computed signature, base64url-encoded. Required. + string signature = 2 [(google.api.field_behavior) = REQUIRED]; + // The unprotected JWS header values. + google.protobuf.Struct header = 3; } message TaskPushNotificationConfig { @@ -476,6 +509,7 @@ message SecurityScheme { HTTPAuthSecurityScheme http_auth_security_scheme = 2; OAuth2SecurityScheme oauth2_security_scheme = 3; OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; + MutualTlsSecurityScheme mtls_security_scheme = 5; } } @@ -507,6 +541,9 @@ message OAuth2SecurityScheme { string description = 1; // An object containing configuration information for the flow types supported OAuthFlows flows = 2; + // URL to the oauth2 authorization server metadata + // [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required. + string oauth2_metadata_url = 3; } message OpenIdConnectSecurityScheme { @@ -517,6 +554,11 @@ message OpenIdConnectSecurityScheme { string open_id_connect_url = 2; } +message MutualTlsSecurityScheme { + // Description of this security scheme. + string description = 1; +} + message OAuthFlows { oneof flow { AuthorizationCodeOAuthFlow authorization_code = 1; @@ -579,7 +621,8 @@ message PasswordOAuthFlow { ///////////// Request Messages /////////// message SendMessageRequest { - Message request = 1 [(google.api.field_behavior) = REQUIRED]; + Message request = 1 + [(google.api.field_behavior) = REQUIRED, json_name = "message"]; SendMessageConfiguration configuration = 2; google.protobuf.Struct metadata = 3; } diff --git a/spec-grpc/src/main/resources/META-INF/beans.xml b/spec-grpc/src/main/resources/META-INF/beans.xml index be7990041..e69de29bb 100644 --- a/spec-grpc/src/main/resources/META-INF/beans.xml +++ b/spec-grpc/src/main/resources/META-INF/beans.xml @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/spec/pom.xml b/spec/pom.xml index ce67def18..ee377b421 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-spec diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index acf33feba..73ecd134c 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -9,7 +9,7 @@ import io.a2a.util.Assert; /** - * Represents an API Key security scheme. + * Defines a security scheme using an API key. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java index 641f56ccb..1c6fdf1b2 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; /** - * An agent's capabilities. + * Defines optional capabilities supported by an agent. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index e394fb626..a2e806ffc 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -8,8 +9,9 @@ import io.a2a.util.Assert; /** - * A public metadata file that describes an agent's capabilities, skills, endpoint URL, and - * authentication requirements. Clients use this for discovery. + * The AgentCard is a self-describing manifest for an agent. It provides essential + * metadata including the agent's identity, capabilities, skills, supported + * communication methods, and security requirements. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -18,9 +20,11 @@ public record AgentCard(String name, String description, String url, AgentProvid List defaultInputModes, List defaultOutputModes, List skills, boolean supportsAuthenticatedExtendedCard, Map securitySchemes, List>> security, String iconUrl, List additionalInterfaces, - String preferredTransport, String protocolVersion) { + String preferredTransport, String protocolVersion, List signatures) { private static final String TEXT_MODE = "text"; + private static final String DEFAULT_PROTOCOL_VERSION = "0.3.0"; + private static final TransportProtocol DEFAULT_TRANSPORT = TransportProtocol.JSONRPC; public AgentCard { Assert.checkNotNullParam("capabilities", capabilities); @@ -31,7 +35,12 @@ public record AgentCard(String name, String description, String url, AgentProvid Assert.checkNotNullParam("skills", skills); Assert.checkNotNullParam("url", url); Assert.checkNotNullParam("version", version); - Assert.checkNotNullParam("protocolVersion", protocolVersion); + if (protocolVersion == null) { + protocolVersion = DEFAULT_PROTOCOL_VERSION; + } + if (preferredTransport == null) { + preferredTransport = DEFAULT_TRANSPORT.asString(); + } } public static class Builder { @@ -50,8 +59,10 @@ public static class Builder { private List>> security; private String iconUrl; private List additionalInterfaces; - String preferredTransport; - String protocolVersion; + private String preferredTransport; + private String protocolVersion; + private List signatures; + public Builder name(String name) { this.name = name; @@ -138,11 +149,24 @@ public Builder protocolVersion(String protocolVersion) { return this; } + public Builder signatures(List signatures) { + this.signatures = signatures; + return this; + } + public AgentCard build() { + if (preferredTransport == null) { + preferredTransport = DEFAULT_TRANSPORT.asString(); + } + if (additionalInterfaces == null) { + // should include an entry matching the main 'url' and 'preferredTransport' + additionalInterfaces = new ArrayList<>(); + additionalInterfaces.add(new AgentInterface(preferredTransport, url)); + } return new AgentCard(name, description, url, provider, version, documentationUrl, capabilities, defaultInputModes, defaultOutputModes, skills, supportsAuthenticatedExtendedCard, securitySchemes, security, iconUrl, - additionalInterfaces, preferredTransport, protocolVersion); + additionalInterfaces, preferredTransport, protocolVersion, signatures); } } } diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java new file mode 100644 index 000000000..4e383d998 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -0,0 +1,49 @@ +package io.a2a.spec; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.a2a.util.Assert; + +/** + * Represents a JWS signature of an AgentCard. + * This follows the JSON format of an RFC 7515 JSON Web Signature (JWS). + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AgentCardSignature(Map header, @JsonProperty("protected") String protectedHeader, + String signature) { + + public AgentCardSignature { + Assert.checkNotNullParam("protectedHeader", protectedHeader); + Assert.checkNotNullParam("signature", signature); + } + + public static class Builder { + private Map header; + String protectedHeader; + String signature; + + public Builder header(Map header) { + this.header = header; + return this; + } + + public Builder protectedHeader(String protectedHeader) { + this.protectedHeader = protectedHeader; + return this; + } + + public Builder signature(String signature) { + this.signature = signature; + return this; + } + + public AgentCardSignature build() { + return new AgentCardSignature(header, protectedHeader, signature); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/AgentExtension.java b/spec/src/main/java/io/a2a/spec/AgentExtension.java index 931bc1c16..053855976 100644 --- a/spec/src/main/java/io/a2a/spec/AgentExtension.java +++ b/spec/src/main/java/io/a2a/spec/AgentExtension.java @@ -4,6 +4,9 @@ import io.a2a.util.Assert; +/** + * A declaration of a protocol extension supported by an Agent. + */ public record AgentExtension (String description, Map params, boolean required, String uri) { public AgentExtension { diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index ab2b7307d..db81ce8f0 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -5,7 +5,7 @@ import io.a2a.util.Assert; /** - * Provides a declaration of the target url and the supported transport to interact with the agent. + * Declares a combination of a target URL and a transport protocol for interacting with the agent. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AgentProvider.java b/spec/src/main/java/io/a2a/spec/AgentProvider.java index 363d42b03..fa57b4478 100644 --- a/spec/src/main/java/io/a2a/spec/AgentProvider.java +++ b/spec/src/main/java/io/a2a/spec/AgentProvider.java @@ -5,7 +5,7 @@ import io.a2a.util.Assert; /** - * An agent provider. + * Represents the service provider of an agent. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java index ce9f4811e..3802b3418 100644 --- a/spec/src/main/java/io/a2a/spec/AgentSkill.java +++ b/spec/src/main/java/io/a2a/spec/AgentSkill.java @@ -1,18 +1,20 @@ package io.a2a.spec; import java.util.List; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** - * An agent skill. + * The set of skills, or distinct capabilities, that the agent can perform. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record AgentSkill(String id, String name, String description, List tags, - List examples, List inputModes, List outputModes) { + List examples, List inputModes, List outputModes, + List>> security) { public AgentSkill { Assert.checkNotNullParam("description", description); @@ -30,6 +32,7 @@ public static class Builder { private List examples; private List inputModes; private List outputModes; + private List>> security; public Builder id(String id) { this.id = id; @@ -66,8 +69,13 @@ public Builder outputModes(List outputModes) { return this; } + public Builder security(List>> security) { + this.security = security; + return this; + } + public AgentSkill build() { - return new AgentSkill(id, name, description, tags, examples, inputModes, outputModes); + return new AgentSkill(id, name, description, tags, examples, inputModes, outputModes, security); } } } diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java index 5a5baa063..cb4d1504b 100644 --- a/spec/src/main/java/io/a2a/spec/Artifact.java +++ b/spec/src/main/java/io/a2a/spec/Artifact.java @@ -8,8 +8,7 @@ import io.a2a.util.Assert; /** - * Represents outputs generated by an agent during a task (e.g., generated files or final structured - * data). Contains parts. + * Represents a file, data structure, or other resource generated by an agent during a task. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java new file mode 100644 index 000000000..323cd147d --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java @@ -0,0 +1,35 @@ +package io.a2a.spec; + +import static io.a2a.util.Utils.defaultIfNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * An A2A-specific error indicating that the agent does not have an + * Authenticated Extended Card configured + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AuthenticatedExtendedCardNotConfiguredError extends JSONRPCError { + + public final static Integer DEFAULT_CODE = -32007; + + @JsonCreator + public AuthenticatedExtendedCardNotConfiguredError( + @JsonProperty("code") Integer code, + @JsonProperty("message") String message, + @JsonProperty("data") Object data) { + super( + defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(message, "Authenticated Extended Card not configured"), + data); + } + + public AuthenticatedExtendedCardNotConfiguredError() { + this(null, null, null); + } + +} diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index 0723a1281..afa6fed72 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -8,7 +8,7 @@ import io.a2a.util.Assert; /** - * Configuration for the OAuth Authorization Code flow. + * Defines configuration details for the OAuth 2.0 Authorization Code flow. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java index 05322cc0b..372b57d78 100644 --- a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java @@ -9,7 +9,7 @@ import io.a2a.util.Assert; /** - * Configuration for the OAuth Client Credentials flow. + * Defines configuration details for the OAuth 2.0 Client Credentials flow. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index d05ee3202..3aa245ca2 100644 --- a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -7,6 +7,10 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An A2A-specific error indicating an incompatibility between the requested + * content types and the agent's capabilities. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class ContentTypeNotSupportedError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 1449f55ac..b40cbb839 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -9,7 +9,7 @@ import io.a2a.util.Assert; /** - * A fundamental data unit within a Message or Artifact. + * Represents a structured data segment (e.g., JSON) within a message or artifact. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index f4c1d0f8e..82c2a4f5f 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -9,7 +9,8 @@ import io.a2a.util.Assert; /** - * A fundamental file unit within a Message or Artifact. + * Represents a file segment within a message or artifact. The file content can be + * provided either directly as bytes or as a URI. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/FileWithBytes.java b/spec/src/main/java/io/a2a/spec/FileWithBytes.java index e2259e902..782bc2c02 100644 --- a/spec/src/main/java/io/a2a/spec/FileWithBytes.java +++ b/spec/src/main/java/io/a2a/spec/FileWithBytes.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +/** + * Represents a file with its content provided directly as a base64-encoded string. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record FileWithBytes(String mimeType, String name, String bytes) implements FileContent { diff --git a/spec/src/main/java/io/a2a/spec/FileWithUri.java b/spec/src/main/java/io/a2a/spec/FileWithUri.java index 65db42dc5..afb3a87d8 100644 --- a/spec/src/main/java/io/a2a/spec/FileWithUri.java +++ b/spec/src/main/java/io/a2a/spec/FileWithUri.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +/** + * Represents a file with its content located at a specific URI. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record FileWithUri(String mimeType, String name, String uri) implements FileContent { diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java new file mode 100644 index 000000000..9d561c2d2 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -0,0 +1,70 @@ +package io.a2a.spec; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.a2a.util.Assert; +import io.a2a.util.Utils; + +/** + * Represents a JSON-RPC request for the `agent/getAuthenticatedExtendedCard` method. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONRPCRequest { + + public static final String METHOD = "agent/getAuthenticatedExtendedCard"; + + @JsonCreator + public GetAuthenticatedExtendedCardRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("method") String method, @JsonProperty("params") Void params) { + if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { + throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); + } + Assert.checkNotNullParam("method", method); + if (! method.equals(METHOD)) { + throw new IllegalArgumentException("Invalid GetAuthenticatedExtendedCardRequest method"); + } + Assert.isNullOrStringOrInteger(id); + this.jsonrpc = Utils.defaultIfNull(jsonrpc, JSONRPC_VERSION); + this.id = id; + this.method = method; + this.params = params; + } + + public GetAuthenticatedExtendedCardRequest(String id) { + this(null, id, METHOD, null); + } + + public static class Builder { + private String jsonrpc; + private Object id; + private String method; + + public GetAuthenticatedExtendedCardRequest.Builder jsonrpc(String jsonrpc) { + this.jsonrpc = jsonrpc; + return this; + } + + public GetAuthenticatedExtendedCardRequest.Builder id(Object id) { + this.id = id; + return this; + } + + public GetAuthenticatedExtendedCardRequest.Builder method(String method) { + this.method = method; + return this; + } + + public GetAuthenticatedExtendedCardRequest build() { + if (id == null) { + id = UUID.randomUUID().toString(); + } + return new GetAuthenticatedExtendedCardRequest(jsonrpc, id, method, null); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java new file mode 100644 index 000000000..ec624e77d --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java @@ -0,0 +1,30 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * A response for the `agent/getAuthenticatedExtendedCard` method. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class GetAuthenticatedExtendedCardResponse extends JSONRPCResponse { + + @JsonCreator + public GetAuthenticatedExtendedCardResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("result") AgentCard result, + @JsonProperty("error") JSONRPCError error) { + super(jsonrpc, id, result, error, AgentCard.class); + } + + public GetAuthenticatedExtendedCardResponse(Object id, JSONRPCError error) { + this(null, id, null, error); + } + + public GetAuthenticatedExtendedCardResponse(Object id, AgentCard result) { + this(null, id, result, null); + } + +} diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 029419a19..0d6ed417f 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -8,7 +8,7 @@ import io.a2a.util.Assert; /** - * Represents an HTTP authentication security scheme. + * Defines a security scheme using HTTP authentication. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java index 46e76cc84..8e2d529ea 100644 --- a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java @@ -8,7 +8,7 @@ import io.a2a.util.Assert; /** - * Configuration for the OAuth Implicit flow. + * Defines configuration details for the OAuth 2.0 Implicit flow. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java index 47f1a332e..ae52ecb70 100644 --- a/spec/src/main/java/io/a2a/spec/InternalError.java +++ b/spec/src/main/java/io/a2a/spec/InternalError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An error indicating an internal error on the server. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class InternalError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index 7b0476e2a..faab71a96 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -8,7 +8,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * A2A specific error indicating agent returned invalid response for the current method. + * An A2A-specific error indicating that the agent returned a response that + * does not conform to the specification for the current method. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java index 69cd1fadc..c71ea14bf 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An error indicating that the method parameters are invalid. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidParamsError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java index 17d3227dc..4d9e50779 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An error indicating that the JSON sent is not a valid Request object. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidRequestError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java index 2e3e96dbe..d596c0701 100644 --- a/spec/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -7,6 +7,9 @@ import static io.a2a.util.Utils.defaultIfNull; +/** + * An error indicating that the server received invalid JSON. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class JSONParseError extends JSONRPCError implements A2AError { diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCError.java b/spec/src/main/java/io/a2a/spec/JSONRPCError.java index b682203d4..776b9c8c6 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCError.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCError.java @@ -10,7 +10,7 @@ import io.a2a.util.Assert; /** - * Represents a JSONRPC error. + * Represents a JSON-RPC 2.0 Error object, included in an error response. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonDeserialize(using = JSONRPCErrorDeserializer.class) diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java b/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java index 20d3a117a..68e2d16fd 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java @@ -1,7 +1,7 @@ package io.a2a.spec; /** - * Represents a JSONRPC message. + * Defines the base structure for any JSON-RPC 2.0 request, response, or notification. */ public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCResponse { diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java index cf0134efe..2af7d0513 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java @@ -82,7 +82,8 @@ protected static boolean isValidMethodName(String methodName) { || methodName.equals(SendMessageRequest.METHOD) || methodName.equals(SendStreamingMessageRequest.METHOD) || methodName.equals(ListTaskPushNotificationConfigRequest.METHOD) - || methodName.equals(DeleteTaskPushNotificationConfigRequest.METHOD)); + || methodName.equals(DeleteTaskPushNotificationConfigRequest.METHOD) + || methodName.equals(GetAuthenticatedExtendedCardRequest.METHOD)); } } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 3a382b1a7..858a4541e 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -2,7 +2,6 @@ import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -15,7 +14,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits SendStreamingMessageResponse, GetTaskResponse, CancelTaskResponse, SetTaskPushNotificationConfigResponse, GetTaskPushNotificationConfigResponse, - SendMessageResponse, DeleteTaskPushNotificationConfigResponse, ListTaskPushNotificationConfigResponse, JSONRPCErrorResponse { + SendMessageResponse, DeleteTaskPushNotificationConfigResponse, ListTaskPushNotificationConfigResponse, JSONRPCErrorResponse, + GetAuthenticatedExtendedCardResponse { protected String jsonrpc; protected Object id; diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index f17646a0f..ec21cf06b 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -13,7 +13,7 @@ import io.a2a.util.Assert; /** - * An A2A message. + * Represents a single message in the conversation between a user and an agent. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index f34af9dcf..9693b221b 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -8,10 +8,7 @@ import io.a2a.util.Assert; /** - * Represents the configuration of the message to be sent. - * - * If {@code blocking} is true, {@code pushNotification} is ignored. - * Both {@code blocking} and {@code pushNotification} are ignored in streaming interactions. + * Defines configuration options for a `message/send` or `message/stream` request. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -19,7 +16,6 @@ public record MessageSendConfiguration(List acceptedOutputModes, Integer PushNotificationConfig pushNotification, boolean blocking) { public MessageSendConfiguration { - Assert.checkNotNullParam("acceptedOutputModes", acceptedOutputModes); if (historyLength != null && historyLength < 0) { throw new IllegalArgumentException("Invalid history length"); } diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index a217539b1..7cba6f1ae 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -7,7 +7,8 @@ import io.a2a.util.Assert; /** - * Used to specify parameters when creating a message. + * Defines the parameters for a request to send a message to an agent. This can be used + * to create a new task, continue an existing one, or restart a task. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java index a8d9d9289..5a46b336f 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An error indicating that the requested method does not exist or is not available. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class MethodNotFoundError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java new file mode 100644 index 000000000..78f389f38 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -0,0 +1,46 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Defines a security scheme using mTLS authentication. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class MutualTLSSecurityScheme implements SecurityScheme { + + public static final String MUTUAL_TLS = "mutualTLS"; + private final String description; + private final String type; + + public MutualTLSSecurityScheme(String description) { + this(description, MUTUAL_TLS); + } + + public MutualTLSSecurityScheme() { + this(null, MUTUAL_TLS); + } + + @JsonCreator + public MutualTLSSecurityScheme(@JsonProperty("description") String description, + @JsonProperty("type") String type) { + if (!type.equals(MUTUAL_TLS)) { + throw new IllegalArgumentException("Invalid type for MutualTLSSecurityScheme"); + } + this.description = description; + this.type = type; + } + + @Override + public String getDescription() { + return description; + } + + public String getType() { + return type; + } + +} diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java index 1c0a696e7..f3ac7d2cb 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java @@ -12,5 +12,6 @@ @JsonDeserialize(using = NonStreamingJSONRPCRequestDeserializer.class) public abstract sealed class NonStreamingJSONRPCRequest extends JSONRPCRequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, - SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest { + SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest, + GetAuthenticatedExtendedCardRequest { } diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java index c97c524c5..37366b9b7 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java @@ -48,6 +48,9 @@ public NonStreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserial case DeleteTaskPushNotificationConfigRequest.METHOD: return new DeleteTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, DeleteTaskPushNotificationConfigParams.class)); + case GetAuthenticatedExtendedCardRequest.METHOD: + return new GetAuthenticatedExtendedCardRequest(jsonrpc, id, method, + getAndValidateParams(paramsNode, jsonParser, treeNode, Void.class)); default: throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); } diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 10141ffb7..fd5c746a1 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -8,7 +8,7 @@ import io.a2a.util.Assert; /** - * Represents an OAuth2 security scheme. + * Defines a security scheme using OAuth 2.0. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -18,20 +18,22 @@ public final class OAuth2SecurityScheme implements SecurityScheme { private final OAuthFlows flows; private final String description; private final String type; + private final String oauth2MetadataUrl; - public OAuth2SecurityScheme(OAuthFlows flows, String description) { - this(flows, description, OAUTH2); + public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2MetadataUrl) { + this(flows, description, oauth2MetadataUrl, OAUTH2); } @JsonCreator public OAuth2SecurityScheme(@JsonProperty("flows") OAuthFlows flows, @JsonProperty("description") String description, - @JsonProperty("type") String type) { + @JsonProperty("oauth2MetadataUrl") String oauth2MetadataUrl, @JsonProperty("type") String type) { Assert.checkNotNullParam("flows", flows); if (!type.equals(OAUTH2)) { throw new IllegalArgumentException("Invalid type for OAuth2SecurityScheme"); } this.flows = flows; this.description = description; + this.oauth2MetadataUrl = oauth2MetadataUrl; this.type = type; } @@ -48,9 +50,14 @@ public String getType() { return type; } + public String getOauth2MetadataUrl() { + return oauth2MetadataUrl; + } + public static class Builder { private OAuthFlows flows; private String description; + private String oauth2MetadataUrl; public Builder flows(OAuthFlows flows) { this.flows = flows; @@ -62,8 +69,13 @@ public Builder description(String description) { return this; } + public Builder oauth2MetadataUrl(String oauth2MetadataUrl) { + this.oauth2MetadataUrl = oauth2MetadataUrl; + return this; + } + public OAuth2SecurityScheme build() { - return new OAuth2SecurityScheme(flows, description); + return new OAuth2SecurityScheme(flows, description, oauth2MetadataUrl); } } } diff --git a/spec/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java index fcd89bc3d..8c5015893 100644 --- a/spec/src/main/java/io/a2a/spec/OAuthFlows.java +++ b/spec/src/main/java/io/a2a/spec/OAuthFlows.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; /** - * Allows configuration of the supported OAuth Flows. + * Defines the configuration for the supported OAuth 2.0 flows. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index f8f059792..26a07d67a 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * Represents an OpenID Connect security scheme. + * Defines a security scheme using OpenID Connect. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java index a42e74942..424a4817f 100644 --- a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java @@ -8,7 +8,7 @@ import io.a2a.util.Assert; /** - * Configuration for the OAuth Resource Owner Password flow. + * Defines configuration details for the OAuth 2.0 Resource Owner Password flow. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java index b5b2bacd2..1ef29ccd4 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java @@ -7,7 +7,7 @@ import io.a2a.util.Assert; /** - * Defines authentication details for push notifications. + * Defines authentication details for a push notification endpoint. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index 34270637f..7d577ffdb 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -5,7 +5,7 @@ import io.a2a.util.Assert; /** - * Represents a push notification. + * Defines the configuration for setting up push notifications for task updates. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index d639b7bab..c618e2b78 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An A2A-specific error indicating that the agent does not support push notifications. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class PushNotificationNotSupportedError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index 5003d01aa..37895d146 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -15,9 +15,15 @@ @JsonSubTypes.Type(value = APIKeySecurityScheme.class, name = API_KEY), @JsonSubTypes.Type(value = HTTPAuthSecurityScheme.class, name = HTTPAuthSecurityScheme.HTTP), @JsonSubTypes.Type(value = OAuth2SecurityScheme.class, name = OAuth2SecurityScheme.OAUTH2), - @JsonSubTypes.Type(value = OpenIdConnectSecurityScheme.class, name = OpenIdConnectSecurityScheme.OPENID_CONNECT) + @JsonSubTypes.Type(value = OpenIdConnectSecurityScheme.class, name = OpenIdConnectSecurityScheme.OPENID_CONNECT), + @JsonSubTypes.Type(value = MutualTLSSecurityScheme.class, name = MutualTLSSecurityScheme.MUTUAL_TLS) }) -public sealed interface SecurityScheme permits APIKeySecurityScheme, HTTPAuthSecurityScheme, OAuth2SecurityScheme, OpenIdConnectSecurityScheme { +/** + * Defines a security scheme that can be used to secure an agent's endpoints. + * This is a discriminated union type based on the OpenAPI 3.0 Security Scheme Object. + */ +public sealed interface SecurityScheme permits APIKeySecurityScheme, HTTPAuthSecurityScheme, OAuth2SecurityScheme, + OpenIdConnectSecurityScheme, MutualTLSSecurityScheme { String getDescription(); } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index 86c92473e..00f757f31 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -11,7 +11,7 @@ import io.a2a.util.Assert; /** - * A central unit of work. + * Represents a single, stateful operation or conversation between a client and an agent. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 49485577b..03269bf33 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -9,7 +9,8 @@ import io.a2a.util.Assert; /** - * A task artifact update event. + * An event sent by the agent to notify the client that an artifact has been + * generated or updated. This is typically used in streaming models. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index 816550eb9..237fe727a 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -7,7 +7,7 @@ import io.a2a.util.Assert; /** - * Task id parameters. + * Defines parameters containing a task ID, used for simple task operations. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index aa40a81d3..eb8668140 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An A2A-specific error indicating that the task is in a state where it cannot be canceled. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotCancelableError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index 26ee264e3..c21998d2d 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -7,6 +7,9 @@ import static io.a2a.util.Utils.defaultIfNull; +/** + * An A2A-specific error indicating that the requested task ID was not found. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotFoundError extends JSONRPCError { diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index 0e4163212..4cda7a8f3 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -5,7 +5,7 @@ import io.a2a.util.Assert; /** - * Task push notification configuration. + * A container associating a push notification configuration with a specific task. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index 2587e5fb5..eb9892fe2 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -7,12 +7,13 @@ import io.a2a.util.Assert; /** - * Task query parameters. + * Defines parameters for querying a task, with an option to limit history length. * * @param id the ID for the task to be queried * @param historyLength the maximum number of items of history for the task to include in the response * @param metadata additional properties */ + @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record TaskQueryParams(String id, Integer historyLength, Map metadata) { diff --git a/spec/src/main/java/io/a2a/spec/TaskState.java b/spec/src/main/java/io/a2a/spec/TaskState.java index 30d3c1a23..b2d296980 100644 --- a/spec/src/main/java/io/a2a/spec/TaskState.java +++ b/spec/src/main/java/io/a2a/spec/TaskState.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonValue; /** - * Represents the state of a task. + * Defines the lifecycle states of a Task. */ public enum TaskState { SUBMITTED("submitted"), diff --git a/spec/src/main/java/io/a2a/spec/TaskStatus.java b/spec/src/main/java/io/a2a/spec/TaskStatus.java index 2befdfcbf..d41455d04 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatus.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatus.java @@ -9,7 +9,7 @@ import io.a2a.util.Assert; /** - * Represents the status of a task. + * Represents the status of a task at a specific point in time. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 7a44480da..21726d607 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -9,7 +9,8 @@ import io.a2a.util.Assert; /** - * A task status update event. + * An event sent by the agent to notify the client of a change in a task's status. + * This is typically used in streaming or subscription models. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index 1b62cf747..be0a4b927 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -9,7 +9,7 @@ import io.a2a.util.Assert; /** - * A fundamental text unit of an Artifact or Message. + * Represents a text segment within a message or artifact. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TransportProtocol.java b/spec/src/main/java/io/a2a/spec/TransportProtocol.java new file mode 100644 index 000000000..289e9f5de --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/TransportProtocol.java @@ -0,0 +1,38 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Supported A2A transport protocols. + */ +public enum TransportProtocol { + JSONRPC("JSONRPC"), + GRPC("GRPC"), + HTTP_JSON("HTTP+JSON"); + + private final String transport; + + TransportProtocol(String transport) { + this.transport = transport; + } + + @JsonValue + public String asString() { + return transport; + } + + @JsonCreator + public static TransportProtocol fromString(String transport) { + switch (transport) { + case "JSONRPC": + return JSONRPC; + case "GRPC": + return GRPC; + case "HTTP+JSON": + return HTTP_JSON; + default: + throw new IllegalArgumentException("Invalid transport: " + transport); + } + } +} \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java index d5ee0fb44..9fe055e9c 100644 --- a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +/** + * An A2A-specific error indicating that the requested operation is not supported by the agent. + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class UnsupportedOperationError extends JSONRPCError { diff --git a/tck/pom.xml b/tck/pom.xml index 5272c1a1c..e03ef70da 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-tck-server @@ -21,6 +21,11 @@ a2a-java-sdk-reference-jsonrpc ${project.version} + + io.github.a2asdk + a2a-java-sdk-reference-grpc + ${project.version} + io.quarkus quarkus-rest-jackson @@ -55,4 +60,4 @@
- \ No newline at end of file + diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 610443ac8..54c9654da 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -1,5 +1,6 @@ package io.a2a.tck.server; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,7 +10,9 @@ import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; +import io.a2a.spec.TransportProtocol; @ApplicationScoped public class AgentCardProducer { @@ -37,7 +40,10 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.2.5") + .protocolVersion("0.3.0") + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"), + new AgentInterface(TransportProtocol.GRPC.asString(), "http://localhost:9000"))) .build(); } } diff --git a/tck/src/main/resources/application.properties b/tck/src/main/resources/application.properties index a2452b339..e850dd2f2 100644 --- a/tck/src/main/resources/application.properties +++ b/tck/src/main/resources/application.properties @@ -1 +1 @@ -%dev.quarkus.http.port=9999 \ No newline at end of file +%dev.quarkus.http.port=9999 diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 831dcc86c..5b1cc3d1e 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 3671da130..481767a2e 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -34,10 +34,13 @@ import io.a2a.spec.A2AServerException; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; @@ -425,7 +428,7 @@ public void testGetAgentCard() { AgentCard agentCard = given() .contentType(MediaType.APPLICATION_JSON) .when() - .get("/.well-known/agent.json") + .get("/.well-known/agent-card.json") .then() .statusCode(200) .extract() @@ -444,13 +447,20 @@ public void testGetAgentCard() { @Test public void testGetExtendAgentCardNotSupported() { - given() + GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); + GetAuthenticatedExtendedCardResponse response = given() .contentType(MediaType.APPLICATION_JSON) + .body(request) .when() - .get("/agent/authenticatedExtendedCard") + .post("/") .then() - .statusCode(404) - .body("error", equalTo("Extended agent card not supported or not enabled.")); + .statusCode(200) + .extract() + .as(GetAuthenticatedExtendedCardResponse.class); + assertEquals("1", response.getId()); + assertInstanceOf(JSONRPCError.class, response.getError()); + assertEquals(new AuthenticatedExtendedCardNotConfiguredError().getCode(), response.getError().getCode()); + assertNull(response.getResult()); } @Test diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 982ae3b8e..93ec2a095 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java index 15aa84209..69f9d2c1d 100644 --- a/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java @@ -3,6 +3,8 @@ import static io.a2a.grpc.utils.ProtoUtils.FromProto; import static io.a2a.grpc.utils.ProtoUtils.ToProto; +import jakarta.enterprise.inject.Vetoed; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,6 +45,7 @@ import io.grpc.Status; import io.grpc.stub.StreamObserver; +@Vetoed public abstract class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { // Hook so testing can wait until streaming subscriptions are established. diff --git a/transport/grpc/src/main/resources/META-INF/beans.xml b/transport/grpc/src/main/resources/META-INF/beans.xml index be7990041..9b2940fc2 100644 --- a/transport/grpc/src/main/resources/META-INF/beans.xml +++ b/transport/grpc/src/main/resources/META-INF/beans.xml @@ -2,5 +2,5 @@ + bean-discovery-mode="annotated"> \ No newline at end of file diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index b66e3b1f5..15eecd06e 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java index b2928dfd5..e4e11f294 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java @@ -2,20 +2,25 @@ import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import java.util.List; import java.util.concurrent.Flow; +import io.a2a.server.ExtendedAgentCard; import io.a2a.server.PublicAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.EventKind; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; @@ -43,14 +48,23 @@ public class JSONRPCHandler { private AgentCard agentCard; + private Instance extendedAgentCard; private RequestHandler requestHandler; protected JSONRPCHandler() { } @Inject + public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard Instance extendedAgentCard, + RequestHandler requestHandler) { + this.agentCard = agentCard; + this.extendedAgentCard = extendedAgentCard; + this.requestHandler = requestHandler; + } + public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { this.agentCard = agentCard; + this.extendedAgentCard = null; this.requestHandler = requestHandler; } @@ -202,6 +216,22 @@ public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig( } } + // TODO: Add authentication (https://github.com/a2aproject/a2a-java/issues/77) + public GetAuthenticatedExtendedCardResponse onGetAuthenticatedExtendedCardRequest( + GetAuthenticatedExtendedCardRequest request, ServerCallContext context) { + if ( !agentCard.supportsAuthenticatedExtendedCard() || !extendedAgentCard.isResolvable()) { + return new GetAuthenticatedExtendedCardResponse(request.getId(), + new AuthenticatedExtendedCardNotConfiguredError()); + } + try { + return new GetAuthenticatedExtendedCardResponse(request.getId(), extendedAgentCard.get()); + } catch (JSONRPCError e) { + return new GetAuthenticatedExtendedCardResponse(request.getId(), e); + } catch (Throwable t) { + return new GetAuthenticatedExtendedCardResponse(request.getId(), new InternalError(t.getMessage())); + } + } + public AgentCard getAgentCard() { return agentCard; } diff --git a/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java index 070df7aa1..a645c9b37 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1,5 +1,9 @@ package io.a2a.jsonrpc.handler; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -19,12 +23,15 @@ import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; @@ -75,9 +82,9 @@ public void testOnGetTaskSuccess() throws Exception { taskStore.save(MINIMAL_TASK); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); GetTaskResponse response = handler.onGetTask(request, callContext); - Assertions.assertEquals(request.getId(), response.getId()); + assertEquals(request.getId(), response.getId()); Assertions.assertSame(MINIMAL_TASK, response.getResult()); - Assertions.assertNull(response.getError()); + assertNull(response.getError()); } @Test @@ -85,9 +92,9 @@ public void testOnGetTaskNotFound() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); GetTaskResponse response = handler.onGetTask(request, callContext); - Assertions.assertEquals(request.getId(), response.getId()); - Assertions.assertInstanceOf(TaskNotFoundError.class, response.getError()); - Assertions.assertNull(response.getResult()); + assertEquals(request.getId(), response.getId()); + assertInstanceOf(TaskNotFoundError.class, response.getError()); + assertNull(response.getResult()); } @Test @@ -107,12 +114,12 @@ public void testOnCancelTaskSuccess() throws Exception { CancelTaskRequest request = new CancelTaskRequest("111", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); - Assertions.assertNull(response.getError()); - Assertions.assertEquals(request.getId(), response.getId()); + assertNull(response.getError()); + assertEquals(request.getId(), response.getId()); Task task = response.getResult(); - Assertions.assertEquals(MINIMAL_TASK.getId(), task.getId()); - Assertions.assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); - Assertions.assertEquals(TaskState.CANCELED, task.getStatus().state()); + assertEquals(MINIMAL_TASK.getId(), task.getId()); + assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(TaskState.CANCELED, task.getStatus().state()); } @Test @@ -126,9 +133,9 @@ public void testOnCancelTaskNotSupported() { CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); - Assertions.assertEquals(request.getId(), response.getId()); - Assertions.assertNull(response.getResult()); - Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); + assertEquals(request.getId(), response.getId()); + assertNull(response.getResult()); + assertInstanceOf(UnsupportedOperationError.class, response.getError()); } @Test @@ -136,9 +143,9 @@ public void testOnCancelTaskNotFound() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); - Assertions.assertEquals(request.getId(), response.getId()); - Assertions.assertNull(response.getResult()); - Assertions.assertInstanceOf(TaskNotFoundError.class, response.getError()); + assertEquals(request.getId(), response.getId()); + assertNull(response.getResult()); + assertInstanceOf(TaskNotFoundError.class, response.getError()); } @Test @@ -153,7 +160,7 @@ public void testOnMessageNewMessageSuccess() { .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - Assertions.assertNull(response.getError()); + assertNull(response.getError()); // The Python implementation returns a Task here, but then again they are using hardcoded mocks and // bypassing the whole EventQueue. // If we were to send a Task in agentExecutorExecute EventConsumer.consumeAll() would not exit due to @@ -179,7 +186,7 @@ public void testOnMessageNewMessageSuccessMocks() { (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } - Assertions.assertNull(response.getError()); + assertNull(response.getError()); Assertions.assertSame(MINIMAL_TASK, response.getResult()); } @@ -196,7 +203,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() { .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - Assertions.assertNull(response.getError()); + assertNull(response.getError()); // The Python implementation returns a Task here, but then again they are using hardcoded mocks and // bypassing the whole EventQueue. // If we were to send a Task in agentExecutorExecute EventConsumer.consumeAll() would not exit due to @@ -223,7 +230,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } - Assertions.assertNull(response.getError()); + assertNull(response.getError()); Assertions.assertSame(MINIMAL_TASK, response.getResult()); } @@ -243,8 +250,8 @@ public void testOnMessageError() { SendMessageRequest request = new SendMessageRequest( "1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); - Assertions.assertNull(response.getResult()); + assertInstanceOf(UnsupportedOperationError.class, response.getError()); + assertNull(response.getResult()); } @Test @@ -264,8 +271,8 @@ public void testOnMessageErrorMocks() { response = handler.onMessageSend(request, callContext); } - Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); - Assertions.assertNull(response.getResult()); + assertInstanceOf(UnsupportedOperationError.class, response.getError()); + assertNull(response.getResult()); } @Test @@ -319,7 +326,7 @@ public void onComplete() { // so there would be no more Events. // // See testOnMessageStreamNewMessageSuccessMocks() for a test more similar to the Python implementation - Assertions.assertEquals(1, results.size()); + assertEquals(1, results.size()); Assertions.assertSame(message, results.get(0)); } @@ -387,7 +394,7 @@ public void onComplete() { } }); - Assertions.assertEquals(events, results); + assertEquals(events, results); } @Test @@ -454,14 +461,14 @@ public void onComplete() { Task expected = new Task.Builder(task) .history(message) .build(); - Assertions.assertEquals(1, results.size()); + assertEquals(1, results.size()); StreamingEventKind receivedType = results.get(0); - Assertions.assertInstanceOf(Task.class, receivedType); + assertInstanceOf(Task.class, receivedType); Task received = (Task) receivedType; - Assertions.assertEquals(expected.getId(), received.getId()); - Assertions.assertEquals(expected.getContextId(), received.getContextId()); - Assertions.assertEquals(expected.getStatus(), received.getStatus()); - Assertions.assertEquals(expected.getHistory(), received.getHistory()); + assertEquals(expected.getId(), received.getId()); + assertEquals(expected.getContextId(), received.getContextId()); + assertEquals(expected.getStatus(), received.getStatus()); + assertEquals(expected.getHistory(), received.getHistory()); } @Test @@ -535,7 +542,7 @@ public void onComplete() { } }); - Assertions.assertEquals(events, results); + assertEquals(events, results); } @@ -574,7 +581,7 @@ public void testGetPushNotificationConfigSuccess() { TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id(MINIMAL_TASK.getId()).url("http://example.com").build()); - Assertions.assertEquals(expectedConfig, getResponse.getResult()); + assertEquals(expectedConfig, getResponse.getResult()); } @Test @@ -612,7 +619,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); - Assertions.assertNull(stpnResponse.getError()); + assertNull(stpnResponse.getError()); Message msg = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) @@ -656,30 +663,30 @@ public void onComplete() { Assertions.assertTrue(latch.await(5, TimeUnit.SECONDS)); subscriptionRef.get().cancel(); - Assertions.assertEquals(3, results.size()); - Assertions.assertEquals(3, httpClient.tasks.size()); + assertEquals(3, results.size()); + assertEquals(3, httpClient.tasks.size()); Task curr = httpClient.tasks.get(0); - Assertions.assertEquals(MINIMAL_TASK.getId(), curr.getId()); - Assertions.assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - Assertions.assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - Assertions.assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); + assertEquals(MINIMAL_TASK.getId(), curr.getId()); + assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); curr = httpClient.tasks.get(1); - Assertions.assertEquals(MINIMAL_TASK.getId(), curr.getId()); - Assertions.assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - Assertions.assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - Assertions.assertEquals(1, curr.getArtifacts().size()); - Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); - Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals(MINIMAL_TASK.getId(), curr.getId()); + assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); + assertEquals(1, curr.getArtifacts().size()); + assertEquals(1, curr.getArtifacts().get(0).parts().size()); + assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); curr = httpClient.tasks.get(2); - Assertions.assertEquals(MINIMAL_TASK.getId(), curr.getId()); - Assertions.assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - Assertions.assertEquals(TaskState.COMPLETED, curr.getStatus().state()); - Assertions.assertEquals(1, curr.getArtifacts().size()); - Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); - Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals(MINIMAL_TASK.getId(), curr.getId()); + assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); + assertEquals(TaskState.COMPLETED, curr.getStatus().state()); + assertEquals(1, curr.getArtifacts().size()); + assertEquals(1, curr.getArtifacts().get(0).parts().size()); + assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); } @Test @@ -708,7 +715,7 @@ public void testOnResubscribeExistingTaskSuccess() { handler.onMessageSend( new SendMessageRequest("1", new MessageSendParams(message, null, null)), callContext); - Assertions.assertNull(smr.getError()); + assertNull(smr.getError()); List results = new ArrayList<>(); @@ -742,7 +749,7 @@ public void onComplete() { // The Python implementation has several events emitted since it uses mocks. // // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation - Assertions.assertEquals(1, results.size()); + assertEquals(1, results.size()); } @@ -810,7 +817,7 @@ public void onComplete() { // The Python implementation has several events emitted since it uses mocks. // // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation - Assertions.assertEquals(events, results); + assertEquals(events, results); } @Test @@ -850,9 +857,9 @@ public void onComplete() { } }); - Assertions.assertEquals(1, results.size()); - Assertions.assertNull(results.get(0).getResult()); - Assertions.assertInstanceOf(TaskNotFoundError.class, results.get(0).getError()); + assertEquals(1, results.size()); + assertNull(results.get(0).getResult()); + assertInstanceOf(TaskNotFoundError.class, results.get(0).getError()); } @Test @@ -897,9 +904,9 @@ public void onComplete() { } }); - Assertions.assertEquals(1, results.size()); + assertEquals(1, results.size()); if (results.get(0).getError() != null && results.get(0).getError() instanceof InvalidRequestError ire) { - Assertions.assertEquals("Streaming is not supported by the agent", ire.getMessage()); + assertEquals("Streaming is not supported by the agent", ire.getMessage()); } else { Assertions.fail("Expected a response containing an error"); } @@ -943,9 +950,9 @@ public void onComplete() { } }); - Assertions.assertEquals(1, results.size()); + assertEquals(1, results.size()); if (results.get(0).getError() != null && results.get(0).getError() instanceof InvalidRequestError ire) { - Assertions.assertEquals("Streaming is not supported by the agent", ire.getMessage()); + assertEquals("Streaming is not supported by the agent", ire.getMessage()); } else { Assertions.fail("Expected a response containing an error"); } @@ -969,7 +976,7 @@ public void testPushNotificationsNotSupportedError() { .params(config) .build(); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - Assertions.assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); + assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); } @Test @@ -987,8 +994,8 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request, callContext); Assertions.assertNotNull(response.getError()); - Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); - Assertions.assertEquals("This operation is not supported", response.getError().getMessage()); + assertInstanceOf(UnsupportedOperationError.class, response.getError()); + assertEquals("This operation is not supported", response.getError().getMessage()); } @Test @@ -1013,8 +1020,8 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { .build(); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); - Assertions.assertEquals("This operation is not supported", response.getError().getMessage()); + assertInstanceOf(UnsupportedOperationError.class, response.getError()); + assertEquals("This operation is not supported", response.getError().getMessage()); } @Test @@ -1028,7 +1035,7 @@ public void testOnMessageSendInternalError() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - Assertions.assertInstanceOf(InternalError.class, response.getError()); + assertInstanceOf(InternalError.class, response.getError()); } @Test @@ -1072,8 +1079,8 @@ public void onComplete() { } }); - Assertions.assertEquals(1, results.size()); - Assertions.assertInstanceOf(InternalError.class, results.get(0).getError()); + assertEquals(1, results.size()); + assertInstanceOf(InternalError.class, results.get(0).getError()); } @Test @@ -1109,7 +1116,7 @@ public void testOnMessageSendErrorHandling() { response = handler.onMessageSend(request, callContext); } - Assertions.assertInstanceOf(UnsupportedOperationError.class, response.getError()); + assertInstanceOf(UnsupportedOperationError.class, response.getError()); } @@ -1124,7 +1131,7 @@ public void testOnMessageSendTaskIdMismatch() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); - Assertions.assertInstanceOf(InternalError.class, response.getError()); + assertInstanceOf(InternalError.class, response.getError()); } @@ -1169,9 +1176,9 @@ public void onComplete() { } }); - Assertions.assertNull(error.get()); - Assertions.assertEquals(1, results.size()); - Assertions.assertInstanceOf(InternalError.class, results.get(0).getError()); + assertNull(error.get()); + assertEquals(1, results.size()); + assertInstanceOf(InternalError.class, results.get(0).getError()); } @Test @@ -1195,9 +1202,9 @@ public void testListPushNotificationConfig() { new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - Assertions.assertEquals("111", listResponse.getId()); - Assertions.assertEquals(1, listResponse.getResult().size()); - Assertions.assertEquals(taskPushConfig, listResponse.getResult().get(0)); + assertEquals("111", listResponse.getId()); + assertEquals(1, listResponse.getResult().size()); + assertEquals(taskPushConfig, listResponse.getResult().get(0)); } @Test @@ -1223,9 +1230,9 @@ public void testListPushNotificationConfigNotSupported() { ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - Assertions.assertEquals("111", listResponse.getId()); - Assertions.assertNull(listResponse.getResult()); - Assertions.assertInstanceOf(PushNotificationNotSupportedError.class, listResponse.getError()); + assertEquals("111", listResponse.getId()); + assertNull(listResponse.getResult()); + assertInstanceOf(PushNotificationNotSupportedError.class, listResponse.getError()); } @Test @@ -1243,9 +1250,9 @@ public void testListPushNotificationConfigNoPushConfigStore() { ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - Assertions.assertEquals("111", listResponse.getId()); - Assertions.assertNull(listResponse.getResult()); - Assertions.assertInstanceOf(UnsupportedOperationError.class, listResponse.getError()); + assertEquals("111", listResponse.getId()); + assertNull(listResponse.getResult()); + assertInstanceOf(UnsupportedOperationError.class, listResponse.getError()); } @Test @@ -1260,9 +1267,9 @@ public void testListPushNotificationConfigTaskNotFound() { ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); - Assertions.assertEquals("111", listResponse.getId()); - Assertions.assertNull(listResponse.getResult()); - Assertions.assertInstanceOf(TaskNotFoundError.class, listResponse.getError()); + assertEquals("111", listResponse.getId()); + assertNull(listResponse.getResult()); + assertInstanceOf(TaskNotFoundError.class, listResponse.getError()); } @Test @@ -1287,9 +1294,9 @@ public void testDeletePushNotificationConfig() { DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); - Assertions.assertEquals("111", deleteResponse.getId()); - Assertions.assertNull(deleteResponse.getError()); - Assertions.assertNull(deleteResponse.getResult()); + assertEquals("111", deleteResponse.getId()); + assertNull(deleteResponse.getError()); + assertNull(deleteResponse.getResult()); } @Test @@ -1315,9 +1322,9 @@ public void testDeletePushNotificationConfigNotSupported() { DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); - Assertions.assertEquals("111", deleteResponse.getId()); - Assertions.assertNull(deleteResponse.getResult()); - Assertions.assertInstanceOf(PushNotificationNotSupportedError.class, deleteResponse.getError()); + assertEquals("111", deleteResponse.getId()); + assertNull(deleteResponse.getResult()); + assertInstanceOf(PushNotificationNotSupportedError.class, deleteResponse.getError()); } @Test @@ -1344,9 +1351,19 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); - Assertions.assertEquals("111", deleteResponse.getId()); - Assertions.assertNull(deleteResponse.getResult()); - Assertions.assertInstanceOf(UnsupportedOperationError.class, deleteResponse.getError()); + assertEquals("111", deleteResponse.getId()); + assertNull(deleteResponse.getResult()); + assertInstanceOf(UnsupportedOperationError.class, deleteResponse.getError()); + } + + @Test + public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); + GetAuthenticatedExtendedCardResponse response = handler.onGetAuthenticatedExtendedCardRequest(request, callContext); + assertEquals(request.getId(), response.getId()); + assertInstanceOf(AuthenticatedExtendedCardNotConfiguredError.class, response.getError()); + assertNull(response.getResult()); } } From eb92ed19ecb9285dbfc71c6185ee2e9d25d2109a Mon Sep 17 00:00:00 2001 From: misselvexu Date: Fri, 11 Jul 2025 16:45:17 +0800 Subject: [PATCH 064/493] fix: Add A2AClient test variants for server-side testing --- .../apps/common/AbstractA2AServerTest.java | 769 +++++++----------- 1 file changed, 308 insertions(+), 461 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 481767a2e..783318c43 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -9,7 +9,6 @@ import static org.wildfly.common.Assert.assertNotNull; import static org.wildfly.common.Assert.assertTrue; -import java.io.EOFException; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -24,27 +23,21 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; import jakarta.ws.rs.core.MediaType; -import com.fasterxml.jackson.core.JsonProcessingException; - import io.a2a.client.A2AClient; import io.a2a.spec.A2AServerException; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; -import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; import io.a2a.spec.GetAuthenticatedExtendedCardRequest; import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; import io.a2a.spec.GetTaskResponse; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; @@ -57,28 +50,20 @@ import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.SetTaskPushNotificationConfigResponse; -import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; -import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -89,46 +74,45 @@ * which delegates to {@link TestUtilsBean}. */ public abstract class AbstractA2AServerTest { - private static final Task MINIMAL_TASK = new Task.Builder() .id("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Task CANCEL_TASK = new Task.Builder() .id("cancel-task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Task CANCEL_TASK_NOT_SUPPORTED = new Task.Builder() .id("cancel-task-not-supported-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Task SEND_MESSAGE_NOT_SUPPORTED = new Task.Builder() .id("task-not-supported-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Message MESSAGE = new Message.Builder() .messageId("111") .role(Message.Role.AGENT) .parts(new TextPart("test message")) .build(); public static final String APPLICATION_JSON = "application/json"; - + private final int serverPort; private A2AClient client; - + protected AbstractA2AServerTest(int serverPort) { this.serverPort = serverPort; this.client = new A2AClient("http://localhost:" + serverPort); } - + @Test public void testTaskStoreMethodsSanityTest() throws Exception { Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); @@ -137,139 +121,89 @@ public void testTaskStoreMethodsSanityTest() throws Exception { assertEquals(task.getId(), saved.getId()); assertEquals(task.getContextId(), saved.getContextId()); assertEquals(task.getStatus().state(), saved.getStatus().state()); - + deleteTaskInTaskStore(task.getId()); Task saved2 = getTaskFromTaskStore(task.getId()); assertNull(saved2); } - + @Test public void testGetTaskSuccess() throws Exception { testGetTask(); } - + private void testGetTask() throws Exception { testGetTask(null); } - + private void testGetTask(String mediaType) throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); - RequestSpecification requestSpecification = RestAssured.given() - .contentType(MediaType.APPLICATION_JSON) - .body(request); - if (mediaType != null) { - requestSpecification = requestSpecification.accept(mediaType); - } - GetTaskResponse response = requestSpecification - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetTaskResponse.class); + GetTaskResponse response = client.getTask("1", new TaskQueryParams(MINIMAL_TASK.getId())); assertEquals("1", response.getId()); assertEquals("task-123", response.getResult().getId()); assertEquals("session-xyz", response.getResult().getContextId()); assertEquals(TaskState.SUBMITTED, response.getResult().getStatus().state()); assertNull(response.getError()); + } catch (A2AServerException e) { + fail("Unexpected exception during getTask: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testGetTaskNotFound() throws Exception { assertTrue(getTaskFromTaskStore("non-existent-task") == null); - GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams("non-existent-task")); - GetTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetTaskResponse.class); - assertEquals("1", response.getId()); - // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); - assertNull(response.getResult()); + try { + client.getTask("1", new TaskQueryParams("non-existent-task")); + fail("Expected A2AServerException to be thrown"); + } catch (A2AServerException e) { + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } } - + @Test public void testCancelTaskSuccess() throws Exception { saveTaskInTaskStore(CANCEL_TASK); try { - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK.getId())); - CancelTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(CancelTaskResponse.class); + CancelTaskResponse response = client.cancelTask("1", new TaskIdParams(CANCEL_TASK.getId())); + assertEquals("1", response.getId()); assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); Task task = response.getResult(); assertEquals(CANCEL_TASK.getId(), task.getId()); assertEquals(CANCEL_TASK.getContextId(), task.getContextId()); assertEquals(TaskState.CANCELED, task.getStatus().state()); - } catch (Exception e) { + } catch (A2AServerException e) { + fail("Unexpected exception during cancel task success test: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(CANCEL_TASK.getId()); } } - + @Test public void testCancelTaskNotSupported() throws Exception { saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); try { - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); - CancelTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(CancelTaskResponse.class); - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); - } catch (Exception e) { + client.cancelTask("1", new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); + fail("Expected A2AServerException to be thrown"); + } catch (A2AServerException e) { + assertInstanceOf(UnsupportedOperationError.class, e.getCause()); } finally { deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); } } - + @Test public void testCancelTaskNotFound() { - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams("non-existent-task")); - CancelTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(CancelTaskResponse.class) - ; - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); + try { + client.cancelTask("1", new TaskIdParams("non-existent-task")); + fail("Expected A2AServerException to be thrown"); + } catch (A2AServerException e) { + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } } - + @Test public void testSendMessageNewMessageSuccess() throws Exception { assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); @@ -277,25 +211,23 @@ public void testSendMessageNewMessageSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SendMessageResponse.class); - assertNull(response.getError()); - Message messageResponse = (Message) response.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + MessageSendParams messageSendParams = new MessageSendParams(message, null, null); + + try { + SendMessageResponse response = client.sendMessage("1", messageSendParams); + assertEquals("1", response.getId()); + assertNull(response.getError()); + Message messageResponse = (Message) response.getResult(); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + } catch (A2AServerException e) { + fail("Unexpected exception during send new message test: " + e.getMessage(), e); + } } - + @Test public void testSendMessageExistingTaskSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -304,16 +236,10 @@ public void testSendMessageExistingTaskSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SendMessageResponse.class); + MessageSendParams messageSendParams = new MessageSendParams(message, null, null); + + SendMessageResponse response = client.sendMessage("1", messageSendParams); + assertEquals("1", response.getId()); assertNull(response.getError()); Message messageResponse = (Message) response.getResult(); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); @@ -321,108 +247,78 @@ public void testSendMessageExistingTaskSuccess() throws Exception { Part part = messageResponse.getParts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("test message", ((TextPart) part).getText()); - } catch (Exception e) { + } catch (A2AServerException e) { + fail("Unexpected exception during send message to existing task test: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testSetPushNotificationSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SetTaskPushNotificationConfigResponse.class); + PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Builder() + .url("http://example.com") + .build(); + SetTaskPushNotificationConfigResponse response = client.setTaskPushNotificationConfig("1", + MINIMAL_TASK.getId(), pushNotificationConfig); + assertEquals("1", response.getId()); assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); TaskPushNotificationConfig config = response.getResult(); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); - } catch (Exception e) { + } catch (A2AServerException e) { + fail("Unexpected exception during set push notification test: " + e.getMessage(), e); } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testGetPushNotificationSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); - - SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse setTaskPushNotificationResponse = given() - .contentType(MediaType.APPLICATION_JSON) - .body(setTaskPushNotificationRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SetTaskPushNotificationConfigResponse.class); - assertNotNull(setTaskPushNotificationResponse); - - GetTaskPushNotificationConfigRequest request = - new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - GetTaskPushNotificationConfigResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetTaskPushNotificationConfigResponse.class); + PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Builder() + .url("http://example.com") + .build(); + + // First set the push notification config + SetTaskPushNotificationConfigResponse setResponse = client.setTaskPushNotificationConfig("1", + MINIMAL_TASK.getId(), pushNotificationConfig); + assertNotNull(setResponse); + + // Then get the push notification config + GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("2", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + assertEquals("2", response.getId()); assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); TaskPushNotificationConfig config = response.getResult(); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); - } catch (Exception e) { + } catch (A2AServerException e) { + fail("Unexpected exception during get push notification test: " + e.getMessage(), e); } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testError() { Message message = new Message.Builder(MESSAGE) .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId()) .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId()) .build(); - SendMessageRequest request = new SendMessageRequest( - "1", new MessageSendParams(message, null, null)); - SendMessageResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SendMessageResponse.class); - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); - } + MessageSendParams messageSendParams = new MessageSendParams(message, null, null); + try { + client.sendMessage("1", messageSendParams); + fail("Expected A2AServerException to be thrown"); + } catch (A2AServerException e) { + assertInstanceOf(UnsupportedOperationError.class, e.getCause()); + } + } + @Test public void testGetAgentCard() { AgentCard agentCard = given() @@ -444,7 +340,7 @@ public void testGetAgentCard() { assertTrue(agentCard.capabilities().stateTransitionHistory()); assertTrue(agentCard.skills().isEmpty()); } - + @Test public void testGetExtendAgentCardNotSupported() { GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); @@ -462,7 +358,7 @@ public void testGetExtendAgentCardNotSupported() { assertEquals(new AuthenticatedExtendedCardNotConfiguredError().getCode(), response.getError().getCode()); assertNull(response.getResult()); } - + @Test public void testMalformedJSONRPCRequest() { // missing closing bracket @@ -479,20 +375,20 @@ public void testMalformedJSONRPCRequest() { assertNotNull(response.getError()); assertEquals(new JSONParseError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidParamsJSONRPCRequest() { String invalidParamsRequest = """ {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} """; testInvalidParams(invalidParamsRequest); - + invalidParamsRequest = """ {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} """; testInvalidParams(invalidParamsRequest); } - + private void testInvalidParams(String invalidParamsRequest) { JSONRPCErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) @@ -507,7 +403,7 @@ private void testInvalidParams(String invalidParamsRequest) { assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); assertEquals("1", response.getId()); } - + @Test public void testInvalidJSONRPCRequestMissingJsonrpc() { String invalidRequest = """ @@ -528,7 +424,7 @@ public void testInvalidJSONRPCRequestMissingJsonrpc() { assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidJSONRPCRequestMissingMethod() { String invalidRequest = """ @@ -546,7 +442,7 @@ public void testInvalidJSONRPCRequestMissingMethod() { assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidJSONRPCRequestInvalidId() { String invalidRequest = """ @@ -564,7 +460,7 @@ public void testInvalidJSONRPCRequestInvalidId() { assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidJSONRPCRequestNonExistentMethod() { String invalidRequest = """ @@ -582,13 +478,12 @@ public void testInvalidJSONRPCRequestNonExistentMethod() { assertNotNull(response.getError()); assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); } - + @Test public void testNonStreamingMethodWithAcceptHeader() throws Exception { testGetTask(MediaType.APPLICATION_JSON); } - - + @Test public void testSendMessageStreamExistingTaskSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -597,115 +492,121 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest( - "1", new MessageSendParams(message, null, null)); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, null); - + MessageSendParams messageSendParams = new MessageSendParams(message, null, null); + CountDownLatch latch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); - - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertNull(jsonResponse.getError()); - Message messageResponse = (Message) jsonResponse.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + AtomicReference messageResponseRef = new AtomicReference<>(); + + // Replace the native HttpClient with A2AClient's sendStreamingMessage method. + client.sendStreamingMessage( + "1", + messageSendParams, + // eventHandler + (streamingEvent) -> { + try { + if (streamingEvent instanceof Message) { + messageResponseRef.set((Message) streamingEvent); + latch.countDown(); + } + } catch (Exception e) { + errorRef.set(e); latch.countDown(); } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + }, + // errorHandler + (jsonRpcError) -> { + errorRef.set(new RuntimeException("JSON-RPC Error: " + jsonRpcError.getMessage())); + latch.countDown(); + }, + // failureHandler + () -> { + if (errorRef.get() == null) { + errorRef.set(new RuntimeException("Stream processing failed")); + } + latch.countDown(); } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); - } - latch.countDown(); - return null; - }); - + ); + boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); + + Message messageResponse = messageResponseRef.get(); + Assertions.assertNotNull(messageResponse); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); } catch (Exception e) { + fail("Unexpected exception during send message stream to existing task test: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void testResubscribeExistingTaskSuccess() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); saveTaskInTaskStore(MINIMAL_TASK); - + try { // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap // does not work because after the message is sent, the queue becomes null but task resubscription // requires the queue to still be active ensureQueueForTask(MINIMAL_TASK.getId()); - + CountDownLatch taskResubscriptionRequestSent = new CountDownLatch(1); CountDownLatch taskResubscriptionResponseReceived = new CountDownLatch(2); - AtomicReference firstResponse = new AtomicReference<>(); - AtomicReference secondResponse = new AtomicReference<>(); - + AtomicReference firstResponse = new AtomicReference<>(); + AtomicReference secondResponse = new AtomicReference<>(); + AtomicReference errorRef = new AtomicReference<>(); + // resubscribe to the task, requires the task and its queue to still be active - TaskResubscriptionRequest taskResubscriptionRequest = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - + TaskIdParams taskIdParams = new TaskIdParams(MINIMAL_TASK.getId()); + // Count down the latch when the MultiSseSupport on the server has started subscribing awaitStreamingSubscription() .whenComplete((unused, throwable) -> taskResubscriptionRequestSent.countDown()); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(taskResubscriptionRequest, null); - - AtomicReference errorRef = new AtomicReference<>(); - - responseFuture.thenAccept(response -> { - - if (response.statusCode() != 200) { - throw new IllegalStateException("Status code was " + response.statusCode()); - } - try { - response.body().forEach(line -> { + + // Use A2AClient-like resubscribeToTask Method + client.resubscribeToTask( + "1", // requestId + taskIdParams, + // eventHandler + (streamingEvent) -> { try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - SendStreamingMessageResponse sendStreamingMessageResponse = Utils.OBJECT_MAPPER.readValue(line.substring("data: ".length()).trim(), SendStreamingMessageResponse.class); - if (taskResubscriptionResponseReceived.getCount() == 2) { - firstResponse.set(sendStreamingMessageResponse); - } else { - secondResponse.set(sendStreamingMessageResponse); - } + if (streamingEvent instanceof TaskArtifactUpdateEvent artifactUpdateEvent) { + firstResponse.set(artifactUpdateEvent); + taskResubscriptionResponseReceived.countDown(); + } else if (streamingEvent instanceof TaskStatusUpdateEvent statusUpdateEvent) { + secondResponse.set(statusUpdateEvent); taskResubscriptionResponseReceived.countDown(); - if (taskResubscriptionResponseReceived.getCount() == 0) { - throw new BreakException(); - } } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + } catch (Exception e) { + errorRef.set(e); + taskResubscriptionResponseReceived.countDown(); + taskResubscriptionResponseReceived.countDown(); // Make sure the counter is zeroed } - }); - } catch (BreakException e) { - } - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); - } - return null; - }); - + }, + // errorHandler + (jsonRpcError) -> { + errorRef.set(new RuntimeException("JSON-RPC Error: " + jsonRpcError.getMessage())); + taskResubscriptionResponseReceived.countDown(); + taskResubscriptionResponseReceived.countDown(); // Make sure the counter is zeroed + }, + // failureHandler + () -> { + if (errorRef.get() == null) { + errorRef.set(new RuntimeException("Stream processing failed")); + } + taskResubscriptionResponseReceived.countDown(); + taskResubscriptionResponseReceived.countDown(); // Make sure the counter is zeroed + } + ); + try { taskResubscriptionRequestSent.await(); List events = List.of( @@ -723,37 +624,34 @@ public void testResubscribeExistingTaskSuccess() throws Exception { .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build()); - + for (Event event : events) { enqueueEventOnServer(event); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - + // wait for the client to receive the responses taskResubscriptionResponseReceived.await(); - + + Assertions.assertNull(errorRef.get()); + assertNotNull(firstResponse.get()); - SendStreamingMessageResponse sendStreamingMessageResponse = firstResponse.get(); - assertNull(sendStreamingMessageResponse.getError()); - TaskArtifactUpdateEvent taskArtifactUpdateEvent = (TaskArtifactUpdateEvent) sendStreamingMessageResponse.getResult(); + TaskArtifactUpdateEvent taskArtifactUpdateEvent = firstResponse.get(); assertEquals(MINIMAL_TASK.getId(), taskArtifactUpdateEvent.getTaskId()); assertEquals(MINIMAL_TASK.getContextId(), taskArtifactUpdateEvent.getContextId()); Part part = taskArtifactUpdateEvent.getArtifact().parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("text", ((TextPart) part).getText()); - + assertNotNull(secondResponse.get()); - sendStreamingMessageResponse = secondResponse.get(); - assertNull(sendStreamingMessageResponse.getError()); - TaskStatusUpdateEvent taskStatusUpdateEvent = (TaskStatusUpdateEvent) sendStreamingMessageResponse.getResult(); + TaskStatusUpdateEvent taskStatusUpdateEvent = secondResponse.get(); assertEquals(MINIMAL_TASK.getId(), taskStatusUpdateEvent.getTaskId()); assertEquals(MINIMAL_TASK.getContextId(), taskStatusUpdateEvent.getContextId()); assertEquals(TaskState.COMPLETED, taskStatusUpdateEvent.getStatus().state()); assertNotNull(taskStatusUpdateEvent.getStatus().timestamp()); } finally { - //setStreamingSubscribedRunnable(null); deleteTaskInTaskStore(MINIMAL_TASK.getId()); executorService.shutdown(); if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { @@ -761,109 +659,114 @@ public void testResubscribeExistingTaskSuccess() throws Exception { } } } - + @Test public void testResubscribeNoExistingTaskError() throws Exception { - TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams("non-existent-task")); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, null); - + TaskIdParams taskIdParams = new TaskIdParams("non-existent-task"); + CountDownLatch latch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); - - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertEquals(request.getId(), jsonResponse.getId()); - assertNull(jsonResponse.getResult()); - // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, jsonResponse.getError()); - assertEquals(new TaskNotFoundError().getCode(), jsonResponse.getError().getCode()); - latch.countDown(); + AtomicReference jsonRpcErrorRef = new AtomicReference<>(); + + // Use A2AClient-like resubscribeToTask Method + client.resubscribeToTask( + "1", // requestId + taskIdParams, + // eventHandler + (streamingEvent) -> { + // Do not expect to receive any success events, as the task does not exist + errorRef.set(new RuntimeException("Unexpected event received for non-existent task")); + latch.countDown(); + }, + // errorHandler + (jsonRpcError) -> { + jsonRpcErrorRef.set(jsonRpcError); + latch.countDown(); + }, + // failureHandler + () -> { + if (errorRef.get() == null && jsonRpcErrorRef.get() == null) { + errorRef.set(new RuntimeException("Expected error for non-existent task")); } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + latch.countDown(); } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); - } - latch.countDown(); - return null; - }); - + ); + boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); + + // Validation returns the expected TaskNotFoundError + JSONRPCError jsonRpcError = jsonRpcErrorRef.get(); + Assertions.assertNotNull(jsonRpcError); + assertEquals(new TaskNotFoundError().getCode(), jsonRpcError.getCode()); } - + @Test public void testStreamingMethodWithAcceptHeader() throws Exception { testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); } - + @Test public void testSendMessageStreamNewMessageSuccess() throws Exception { testSendStreamingMessage(null); } - + private void testSendStreamingMessage(String mediaType) throws Exception { Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest( - "1", new MessageSendParams(message, null, null)); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); - + MessageSendParams messageSendParams = new MessageSendParams(message, null, null); + CountDownLatch latch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); - - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertNull(jsonResponse.getError()); - Message messageResponse = (Message) jsonResponse.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + AtomicReference messageResponseRef = new AtomicReference<>(); + + // Using A2AClient's sendStreamingMessage method + client.sendStreamingMessage( + "1", // requestId + messageSendParams, + // eventHandler + (streamingEvent) -> { + try { + if (streamingEvent instanceof Message) { + messageResponseRef.set((Message) streamingEvent); + latch.countDown(); + } + } catch (Exception e) { + errorRef.set(e); latch.countDown(); } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + }, + // errorHandler + (jsonRpcError) -> { + errorRef.set(new RuntimeException("JSON-RPC Error: " + jsonRpcError.getMessage())); + latch.countDown(); + }, + // failureHandler + () -> { + if (errorRef.get() == null) { + errorRef.set(new RuntimeException("Stream processing failed")); + } + latch.countDown(); } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); - } - latch.countDown(); - return null; - }); - - + ); + boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); - + + Message messageResponse = messageResponseRef.get(); + Assertions.assertNotNull(messageResponse); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + } - + @Test public void testListPushNotificationConfigWithConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -879,7 +782,7 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); - + try { ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); assertEquals("111", listResponse.getId()); @@ -894,7 +797,7 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testListPushNotificationConfigWithoutConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -907,14 +810,14 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { .url("http://2.example.com") .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - + // will overwrite the previous one savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); assertEquals("111", listResponse.getId()); assertEquals(1, listResponse.getResult().size()); - + PushNotificationConfig expectedNotificationConfig = new PushNotificationConfig.Builder() .url("http://2.example.com") .id(MINIMAL_TASK.getId()) @@ -928,7 +831,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testListPushNotificationConfigTaskNotFound() { try { @@ -938,7 +841,7 @@ public void testListPushNotificationConfigTaskNotFound() { assertInstanceOf(TaskNotFoundError.class, e.getCause()); } } - + @Test public void testListPushNotificationConfigEmptyList() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -952,7 +855,7 @@ public void testListPushNotificationConfigEmptyList() throws Exception { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testDeletePushNotificationConfigWithValidConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -961,7 +864,7 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build()); - + PushNotificationConfig notificationConfig1 = new PushNotificationConfig.Builder() .url("http://example.com") @@ -975,18 +878,18 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); savePushNotificationConfigInStore("task-456", notificationConfig1); - + try { // specify the config ID to delete DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), "config1"); assertNull(deleteResponse.getError()); assertNull(deleteResponse.getResult()); - + // should now be 1 left ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); assertEquals(1, listResponse.getResult().size()); - + // should remain unchanged, this is a different task listResponse = client.listTaskPushNotificationConfig("task-456"); assertEquals(1, listResponse.getResult().size()); @@ -1000,7 +903,7 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception deleteTaskInTaskStore("task-456"); } } - + @Test public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -1016,13 +919,13 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); - + try { DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), "non-existent-config-id"); assertNull(deleteResponse.getError()); assertNull(deleteResponse.getResult()); - + // should remain unchanged ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); assertEquals(2, listResponse.getResult().size()); @@ -1034,7 +937,7 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testDeletePushNotificationConfigTaskNotFound() { try { @@ -1044,7 +947,7 @@ public void testDeletePushNotificationConfigTaskNotFound() { assertInstanceOf(TaskNotFoundError.class, e.getCause()); } } - + @Test public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -1057,16 +960,16 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio .url("http://2.example.com") .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - + // this one will overwrite the previous one savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); - + try { DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); assertNull(deleteResponse.getError()); assertNull(deleteResponse.getResult()); - + // should now be 0 ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); assertEquals(0, listResponse.getResult().size()); @@ -1077,59 +980,7 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - - private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { - line = extractSseData(line); - if (line != null) { - return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); - } - return null; - } - - private static String extractSseData(String line) { - if (line.startsWith("data:")) { - line = line.substring(5).trim(); - return line; - } - return null; - } - - private boolean isStreamClosedError(Throwable throwable) { - // Unwrap the CompletionException - Throwable cause = throwable; - - while (cause != null) { - if (cause instanceof EOFException) { - return true; - } - cause = cause.getCause(); - } - return false; - } - - private CompletableFuture>> initialiseStreamingRequest( - StreamingJSONRPCRequest request, String mediaType) throws Exception { - - // Create the client - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - - // Create the request - HttpRequest.Builder builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/")) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) - .header("Content-Type", APPLICATION_JSON); - if (mediaType != null) { - builder.header("Accept", mediaType); - } - HttpRequest httpRequest = builder.build(); - - - // Send request async and return the CompletableFuture - return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); - } - + protected void saveTaskInTaskStore(Task task) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1139,13 +990,13 @@ protected void saveTaskInTaskStore(Task task) throws Exception { .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(task))) .header("Content-Type", APPLICATION_JSON) .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { -throw new RuntimeException(String.format("Saving task failed! Status: %d, Body: %s", response.statusCode(), response.body())); + throw new RuntimeException(String.format("Saving task failed! Status: %d, Body: %s", response.statusCode(), response.body())); } } - + protected Task getTaskFromTaskStore(String taskId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1154,17 +1005,17 @@ protected Task getTaskFromTaskStore(String taskId) throws Exception { .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) .GET() .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() == 404) { return null; } if (response.statusCode() != 200) { -throw new RuntimeException(String.format("Getting task failed! Status: %d, Body: %s", response.statusCode(), response.body())); + throw new RuntimeException(String.format("Getting task failed! Status: %d, Body: %s", response.statusCode(), response.body())); } return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); } - + protected void deleteTaskInTaskStore(String taskId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1178,7 +1029,7 @@ protected void deleteTaskInTaskStore(String taskId) throws Exception { throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); } } - + protected void ensureQueueForTask(String taskId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1189,10 +1040,10 @@ protected void ensureQueueForTask(String taskId) throws Exception { .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { -throw new RuntimeException(String.format("Ensuring queue failed! Status: %d, Body: %s", response.statusCode(), response.body())); + throw new RuntimeException(String.format("Ensuring queue failed! Status: %d, Body: %s", response.statusCode(), response.body())); } } - + protected void enqueueEventOnServer(Event event) throws Exception { String path; if (event instanceof TaskArtifactUpdateEvent e) { @@ -1211,17 +1062,17 @@ protected void enqueueEventOnServer(Event event) throws Exception { .header("Content-Type", APPLICATION_JSON) .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(event))) .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { throw new RuntimeException(response.statusCode() + ": Queueing event failed!" + response.body()); } } - + private CompletableFuture awaitStreamingSubscription() { int cnt = getStreamingSubscribedCount(); AtomicInteger initialCount = new AtomicInteger(cnt); - + return CompletableFuture.runAsync(() -> { try { boolean done = false; @@ -1243,7 +1094,7 @@ private CompletableFuture awaitStreamingSubscription() { } }); } - + private int getStreamingSubscribedCount() { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1260,7 +1111,7 @@ private int getStreamingSubscribedCount() { throw new RuntimeException(e); } } - + protected void deletePushNotificationConfigInStore(String taskId, String configId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1274,7 +1125,7 @@ protected void deletePushNotificationConfigInStore(String taskId, String configI throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); } } - + protected void savePushNotificationConfigInStore(String taskId, PushNotificationConfig notificationConfig) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1284,14 +1135,10 @@ protected void savePushNotificationConfigInStore(String taskId, PushNotification .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(notificationConfig))) .header("Content-Type", APPLICATION_JSON) .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { throw new RuntimeException(response.statusCode() + ": Creating task push notification config failed! " + response.body()); } } - - private static class BreakException extends RuntimeException { - - } } From 2d7c137230f50fb0a0a2b16d4506a26b40909449 Mon Sep 17 00:00:00 2001 From: David Brassely Date: Thu, 14 Aug 2025 15:30:12 +0200 Subject: [PATCH 065/493] feat: Make A2AClient agnostic to the transport protocol --- client/pom.xml | 2 +- client/src/main/java/io/a2a/A2A.java | 4 +- .../java/io/a2a/client/A2ACardResolver.java | 4 +- .../main/java/io/a2a/client/A2AClient.java | 716 ------------------ .../java/io/a2a/client/A2AGrpcClient.java | 200 ----- .../io/a2a/client/A2ACardResolverTest.java | 4 +- pom.xml | 1 + .../grpc/quarkus/QuarkusGrpcHandler.java | 2 +- .../server/grpc/quarkus/A2ATestResource.java | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 2 +- .../tasks/BasePushNotificationSender.java | 4 +- .../AbstractA2ARequestHandlerTest.java | 4 +- .../server/apps/common/TestHttpClient.java | 4 +- transport/grpc/pom.xml | 5 + .../grpc/client/EventStreamObserver.java | 16 +- .../transport/grpc/client/GrpcTransport.java | 164 ++++ .../grpc/server}/handler/GrpcHandler.java | 2 +- .../io/a2a/grpc/handler/GrpcHandlerTest.java | 1 + transport/jsonrpc/pom.xml | 5 + .../jsonrpc/client}/A2AHttpClient.java | 2 +- .../jsonrpc/client}/A2AHttpResponse.java | 2 +- .../jsonrpc/client/JSONRPCTransport.java | 301 ++++++++ .../jsonrpc/client}/JdkA2AHttpClient.java | 2 +- .../jsonrpc}/client/sse/SSEEventListener.java | 14 +- .../server}/handler/JSONRPCHandler.java | 2 +- .../jsonrpc/client/JsonStreamingMessages.java | 148 ++++ .../client/sse/SSEEventListenerTest.java | 25 +- .../server}/handler/JSONRPCHandlerTest.java | 3 +- transport/spi/pom.xml | 29 + .../a2a/transport/spi/client/Transport.java | 109 +++ 30 files changed, 812 insertions(+), 967 deletions(-) delete mode 100644 client/src/main/java/io/a2a/client/A2AClient.java delete mode 100644 client/src/main/java/io/a2a/client/A2AGrpcClient.java rename client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java => transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java (83%) create mode 100644 transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java rename transport/grpc/src/main/java/io/a2a/{grpc => transport/grpc/server}/handler/GrpcHandler.java (99%) rename {client/src/main/java/io/a2a/http => transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client}/A2AHttpClient.java (96%) rename {client/src/main/java/io/a2a/http => transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client}/A2AHttpResponse.java (70%) create mode 100644 transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java rename {client/src/main/java/io/a2a/http => transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client}/JdkA2AHttpClient.java (99%) rename {client/src/main/java/io/a2a => transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc}/client/sse/SSEEventListener.java (98%) rename transport/jsonrpc/src/main/java/io/a2a/{jsonrpc => transport/jsonrpc/server}/handler/JSONRPCHandler.java (99%) create mode 100644 transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java rename {client/src/test/java/io/a2a => transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc}/client/sse/SSEEventListenerTest.java (97%) rename transport/jsonrpc/src/test/java/io/a2a/{jsonrpc => transport/jsonrpc/server}/handler/JSONRPCHandlerTest.java (99%) create mode 100644 transport/spi/pom.xml create mode 100644 transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java diff --git a/client/pom.xml b/client/pom.xml index fa381e65e..9f8b37779 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -29,7 +29,7 @@ ${project.groupId} - a2a-java-sdk-spec-grpc + a2a-java-sdk-transport-jsonrpc ${project.version} diff --git a/client/src/main/java/io/a2a/A2A.java b/client/src/main/java/io/a2a/A2A.java index 1d1f6260e..5fb536df8 100644 --- a/client/src/main/java/io/a2a/A2A.java +++ b/client/src/main/java/io/a2a/A2A.java @@ -4,8 +4,8 @@ import java.util.Map; import io.a2a.client.A2ACardResolver; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.JdkA2AHttpClient; +import io.a2a.transport.jsonrpc.client.A2AHttpClient; +import io.a2a.transport.jsonrpc.client.JdkA2AHttpClient; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/client/src/main/java/io/a2a/client/A2ACardResolver.java b/client/src/main/java/io/a2a/client/A2ACardResolver.java index 8ec181647..c5bb0b7c0 100644 --- a/client/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/client/src/main/java/io/a2a/client/A2ACardResolver.java @@ -9,8 +9,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.A2AHttpResponse; +import io.a2a.transport.jsonrpc.client.A2AHttpClient; +import io.a2a.transport.jsonrpc.client.A2AHttpResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/client/src/main/java/io/a2a/client/A2AClient.java b/client/src/main/java/io/a2a/client/A2AClient.java deleted file mode 100644 index 146e1bc58..000000000 --- a/client/src/main/java/io/a2a/client/A2AClient.java +++ /dev/null @@ -1,716 +0,0 @@ -package io.a2a.client; - -import static io.a2a.util.Assert.checkNotNullParam; - -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.client.sse.SSEEventListener; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.A2AHttpResponse; -import io.a2a.http.JdkA2AHttpClient; -import io.a2a.A2A; -import io.a2a.spec.A2AClientError; -import io.a2a.spec.A2AClientJSONError; -import io.a2a.spec.A2AServerException; -import io.a2a.spec.AgentCard; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; -import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; -import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.JSONRPCMessage; -import io.a2a.spec.JSONRPCResponse; -import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; -import io.a2a.spec.MessageSendParams; -import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; -import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.TaskIdParams; -import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.spec.TaskQueryParams; -import io.a2a.spec.TaskResubscriptionRequest; -import io.a2a.util.Utils; - -/** - * An A2A client. - */ -public class A2AClient { - - private static final TypeReference SEND_MESSAGE_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference GET_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference CANCEL_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; - private static final TypeReference GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE = new TypeReference<>() {}; - private final A2AHttpClient httpClient; - private final String agentUrl; - private AgentCard agentCard; - - - /** - * Create a new A2AClient. - * - * @param agentCard the agent card for the A2A server this client will be communicating with - */ - public A2AClient(AgentCard agentCard) { - checkNotNullParam("agentCard", agentCard); - this.agentCard = agentCard; - this.agentUrl = agentCard.url(); - this.httpClient = new JdkA2AHttpClient(); - } - - /** - * Create a new A2AClient. - * - * @param agentUrl the URL for the A2A server this client will be communicating with - */ - public A2AClient(String agentUrl) { - checkNotNullParam("agentUrl", agentUrl); - this.agentUrl = agentUrl; - this.httpClient = new JdkA2AHttpClient(); - } - - /** - * Fetches the agent card and initialises an A2A client. - * - * @param httpClient the {@link A2AHttpClient} to use - * @param baseUrl the base URL of the agent's host - * @param agentCardPath the path to the agent card endpoint, relative to the {@code baseUrl}. If {@code null}, the - * value {@link A2ACardResolver#DEFAULT_AGENT_CARD_PATH} will be used - * @return an initialised {@code A2AClient} instance - * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError if the agent card response is invalid - */ - public static A2AClient getClientFromAgentCardUrl(A2AHttpClient httpClient, String baseUrl, - String agentCardPath) throws A2AClientError, A2AClientJSONError { - A2ACardResolver resolver = new A2ACardResolver(httpClient, baseUrl, agentCardPath); - AgentCard card = resolver.getAgentCard(); - return new A2AClient(card); - } - - /** - * Get the agent card for the A2A server this client will be communicating with from - * the default public agent card endpoint. - * - * @return the agent card for the A2A server - * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema - */ - public AgentCard getAgentCard() throws A2AClientError, A2AClientJSONError { - if (this.agentCard == null) { - this.agentCard = A2A.getAgentCard(this.httpClient, this.agentUrl); - } - return this.agentCard; - } - - /** - * Get the agent card for the A2A server this client will be communicating with. - * - * @param relativeCardPath the path to the agent card endpoint relative to the base URL of the A2A server - * @param authHeaders the HTTP authentication headers to use - * @return the agent card for the A2A server - * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema - */ - public AgentCard getAgentCard(String relativeCardPath, Map authHeaders) throws A2AClientError, A2AClientJSONError { - if (this.agentCard == null) { - this.agentCard = A2A.getAgentCard(this.httpClient, this.agentUrl, relativeCardPath, authHeaders); - } - return this.agentCard; - } - - /** - * Send a message to the remote agent. - * - * @param messageSendParams the parameters for the message to be sent - * @return the response, may contain a message or a task - * @throws A2AServerException if sending the message fails for any reason - */ - public SendMessageResponse sendMessage(MessageSendParams messageSendParams) throws A2AServerException { - return sendMessage(null, messageSendParams); - } - - /** - * Send a message to the remote agent. - * - * @param requestId the request ID to use - * @param messageSendParams the parameters for the message to be sent - * @return the response, may contain a message or a task - * @throws A2AServerException if sending the message fails for any reason - */ - public SendMessageResponse sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException { - SendMessageRequest.Builder sendMessageRequestBuilder = new SendMessageRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(SendMessageRequest.METHOD) - .params(messageSendParams); - - if (requestId != null) { - sendMessageRequestBuilder.id(requestId); - } - - SendMessageRequest sendMessageRequest = sendMessageRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(sendMessageRequest); - return unmarshalResponse(httpResponseBody, SEND_MESSAGE_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to send message: " + e, e.getCause()); - } - } - - /** - * Retrieve a task from the A2A server. This method can be used to retrieve the generated - * artifacts for a task. - * - * @param id the task ID - * @return the response containing the task - * @throws A2AServerException if retrieving the task fails for any reason - */ - public GetTaskResponse getTask(String id) throws A2AServerException { - return getTask(null, new TaskQueryParams(id)); - } - - /** - * Retrieve a task from the A2A server. This method can be used to retrieve the generated - * artifacts for a task. - * - * @param taskQueryParams the params for the task to be queried - * @return the response containing the task - * @throws A2AServerException if retrieving the task fails for any reason - */ - public GetTaskResponse getTask(TaskQueryParams taskQueryParams) throws A2AServerException { - return getTask(null, taskQueryParams); - } - - /** - * Retrieve the generated artifacts for a task. - * - * @param requestId the request ID to use - * @param taskQueryParams the params for the task to be queried - * @return the response containing the task - * @throws A2AServerException if retrieving the task fails for any reason - */ - public GetTaskResponse getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException { - GetTaskRequest.Builder getTaskRequestBuilder = new GetTaskRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(GetTaskRequest.METHOD) - .params(taskQueryParams); - - if (requestId != null) { - getTaskRequestBuilder.id(requestId); - } - - GetTaskRequest getTaskRequest = getTaskRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(getTaskRequest); - return unmarshalResponse(httpResponseBody, GET_TASK_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get task: " + e, e.getCause()); - } - } - - /** - * Cancel a task that was previously submitted to the A2A server. - * - * @param id the task ID - * @return the response indicating if the task was cancelled - * @throws A2AServerException if cancelling the task fails for any reason - */ - public CancelTaskResponse cancelTask(String id) throws A2AServerException { - return cancelTask(null, new TaskIdParams(id)); - } - - /** - * Cancel a task that was previously submitted to the A2A server. - * - * @param taskIdParams the params for the task to be cancelled - * @return the response indicating if the task was cancelled - * @throws A2AServerException if cancelling the task fails for any reason - */ - public CancelTaskResponse cancelTask(TaskIdParams taskIdParams) throws A2AServerException { - return cancelTask(null, taskIdParams); - } - - /** - * Cancel a task that was previously submitted to the A2A server. - * - * @param requestId the request ID to use - * @param taskIdParams the params for the task to be cancelled - * @return the response indicating if the task was cancelled - * @throws A2AServerException if retrieving the task fails for any reason - */ - public CancelTaskResponse cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException { - CancelTaskRequest.Builder cancelTaskRequestBuilder = new CancelTaskRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(CancelTaskRequest.METHOD) - .params(taskIdParams); - - if (requestId != null) { - cancelTaskRequestBuilder.id(requestId); - } - - CancelTaskRequest cancelTaskRequest = cancelTaskRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(cancelTaskRequest); - return unmarshalResponse(httpResponseBody, CANCEL_TASK_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to cancel task: " + e, e.getCause()); - } - } - - /** - * Get the push notification configuration for a task. - * - * @param taskId the task ID - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String taskId) throws A2AServerException { - return getTaskPushNotificationConfig(null, new GetTaskPushNotificationConfigParams(taskId)); - } - - /** - * Get the push notification configuration for a task. - * - * @param taskId the task ID - * @param pushNotificationConfigId the push notification configuration ID - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String taskId, String pushNotificationConfigId) throws A2AServerException { - return getTaskPushNotificationConfig(null, new GetTaskPushNotificationConfigParams(taskId, pushNotificationConfigId)); - } - - /** - * Get the push notification configuration for a task. - * - * @param getTaskPushNotificationConfigParams the params for the task - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { - return getTaskPushNotificationConfig(null, getTaskPushNotificationConfigParams); - } - - /** - * Get the push notification configuration for a task. - * - * @param requestId the request ID to use - * @param getTaskPushNotificationConfigParams the params for the task - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public GetTaskPushNotificationConfigResponse getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { - GetTaskPushNotificationConfigRequest.Builder getTaskPushNotificationRequestBuilder = new GetTaskPushNotificationConfigRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(GetTaskPushNotificationConfigRequest.METHOD) - .params(getTaskPushNotificationConfigParams); - - if (requestId != null) { - getTaskPushNotificationRequestBuilder.id(requestId); - } - - GetTaskPushNotificationConfigRequest getTaskPushNotificationRequest = getTaskPushNotificationRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(getTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get task push notification config: " + e, e.getCause()); - } - } - - /** - * Set push notification configuration for a task. - * - * @param taskId the task ID - * @param pushNotificationConfig the push notification configuration - * @return the response indicating whether setting the task push notification configuration succeeded - * @throws A2AServerException if setting the push notification configuration fails for any reason - */ - public SetTaskPushNotificationConfigResponse setTaskPushNotificationConfig(String taskId, - PushNotificationConfig pushNotificationConfig) throws A2AServerException { - return setTaskPushNotificationConfig(null, taskId, pushNotificationConfig); - } - - /** - * Set push notification configuration for a task. - * - * @param requestId the request ID to use - * @param taskId the task ID - * @param pushNotificationConfig the push notification configuration - * @return the response indicating whether setting the task push notification configuration succeeded - * @throws A2AServerException if setting the push notification configuration fails for any reason - */ - public SetTaskPushNotificationConfigResponse setTaskPushNotificationConfig(String requestId, String taskId, - PushNotificationConfig pushNotificationConfig) throws A2AServerException { - SetTaskPushNotificationConfigRequest.Builder setTaskPushNotificationRequestBuilder = new SetTaskPushNotificationConfigRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(SetTaskPushNotificationConfigRequest.METHOD) - .params(new TaskPushNotificationConfig(taskId, pushNotificationConfig)); - - if (requestId != null) { - setTaskPushNotificationRequestBuilder.id(requestId); - } - - SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = setTaskPushNotificationRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(setTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to set task push notification config: " + e, e.getCause()); - } - } - - /** - * Retrieves the push notification configurations for a specified task. - * - * @param requestId the request ID to use - * @param taskId the task ID to use - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(String requestId, String taskId) throws A2AServerException { - return listTaskPushNotificationConfig(requestId, new ListTaskPushNotificationConfigParams(taskId)); - } - - /** - * Retrieves the push notification configurations for a specified task. - * - * @param taskId the task ID to use - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(String taskId) throws A2AServerException { - return listTaskPushNotificationConfig(null, new ListTaskPushNotificationConfigParams(taskId)); - } - - /** - * Retrieves the push notification configurations for a specified task. - * - * @param listTaskPushNotificationConfigParams the params for retrieving the push notification configuration - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { - return listTaskPushNotificationConfig(null, listTaskPushNotificationConfigParams); - } - - /** - * Retrieves the push notification configurations for a specified task. - * - * @param requestId the request ID to use - * @param listTaskPushNotificationConfigParams the params for retrieving the push notification configuration - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(String requestId, - ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { - ListTaskPushNotificationConfigRequest.Builder listTaskPushNotificationRequestBuilder = new ListTaskPushNotificationConfigRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(ListTaskPushNotificationConfigRequest.METHOD) - .params(listTaskPushNotificationConfigParams); - - if (requestId != null) { - listTaskPushNotificationRequestBuilder.id(requestId); - } - - ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = listTaskPushNotificationRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(listTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to list task push notification config: " + e, e.getCause()); - } - } - - /** - * Delete the push notification configuration for a specified task. - * - * @param requestId the request ID to use - * @param taskId the task ID - * @param pushNotificationConfigId the push notification config ID - * @return the response - * @throws A2AServerException if deleting the push notification configuration fails for any reason - */ - public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(String requestId, String taskId, - String pushNotificationConfigId) throws A2AServerException { - return deleteTaskPushNotificationConfig(requestId, new DeleteTaskPushNotificationConfigParams(taskId, pushNotificationConfigId)); - } - - /** - * Delete the push notification configuration for a specified task. - * - * @param taskId the task ID - * @param pushNotificationConfigId the push notification config ID - * @return the response - * @throws A2AServerException if deleting the push notification configuration fails for any reason - */ - public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(String taskId, - String pushNotificationConfigId) throws A2AServerException { - return deleteTaskPushNotificationConfig(null, new DeleteTaskPushNotificationConfigParams(taskId, pushNotificationConfigId)); - } - - /** - * Delete the push notification configuration for a specified task. - * - * @param deleteTaskPushNotificationConfigParams the params for deleting the push notification configuration - * @return the response - * @throws A2AServerException if deleting the push notification configuration fails for any reason - */ - public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { - return deleteTaskPushNotificationConfig(null, deleteTaskPushNotificationConfigParams); - } - - /** - * Delete the push notification configuration for a specified task. - * - * @param requestId the request ID to use - * @param deleteTaskPushNotificationConfigParams the params for deleting the push notification configuration - * @return the response - * @throws A2AServerException if deleting the push notification configuration fails for any reason - */ - public DeleteTaskPushNotificationConfigResponse deleteTaskPushNotificationConfig(String requestId, - DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { - DeleteTaskPushNotificationConfigRequest.Builder deleteTaskPushNotificationRequestBuilder = new DeleteTaskPushNotificationConfigRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(DeleteTaskPushNotificationConfigRequest.METHOD) - .params(deleteTaskPushNotificationConfigParams); - - if (requestId != null) { - deleteTaskPushNotificationRequestBuilder.id(requestId); - } - - DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = deleteTaskPushNotificationRequestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(deleteTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to delete task push notification config: " + e, e.getCause()); - } - } - - /** - * Send a streaming message to the remote agent. - * - * @param messageSendParams the parameters for the message to be sent - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if the remote agent returns an error - * @param failureHandler a consumer that will be invoked if a failure occurs when processing events - * @throws A2AServerException if sending the streaming message fails for any reason - */ - public void sendStreamingMessage(MessageSendParams messageSendParams, Consumer eventHandler, - Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - sendStreamingMessage(null, messageSendParams, eventHandler, errorHandler, failureHandler); - } - - /** - * Send a streaming message to the remote agent. - * - * @param requestId the request ID to use - * @param messageSendParams the parameters for the message to be sent - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if the remote agent returns an error - * @param failureHandler a consumer that will be invoked if a failure occurs when processing events - * @throws A2AServerException if sending the streaming message fails for any reason - */ - public void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, - Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - checkNotNullParam("messageSendParams", messageSendParams); - checkNotNullParam("eventHandler", eventHandler); - checkNotNullParam("errorHandler", errorHandler); - checkNotNullParam("failureHandler", failureHandler); - - SendStreamingMessageRequest.Builder sendStreamingMessageRequestBuilder = new SendStreamingMessageRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(SendStreamingMessageRequest.METHOD) - .params(messageSendParams); - - if (requestId != null) { - sendStreamingMessageRequestBuilder.id(requestId); - } - - AtomicReference> ref = new AtomicReference<>(); - SSEEventListener sseEventListener = new SSEEventListener(eventHandler, errorHandler, failureHandler); - SendStreamingMessageRequest sendStreamingMessageRequest = sendStreamingMessageRequestBuilder.build(); - try { - A2AHttpClient.PostBuilder builder = createPostBuilder(sendStreamingMessageRequest); - ref.set(builder.postAsyncSSE( - msg -> sseEventListener.onMessage(msg, ref.get()), - throwable -> sseEventListener.onError(throwable, ref.get()), - () -> { - // We don't need to do anything special on completion - })); - - } catch (IOException e) { - throw new A2AServerException("Failed to send streaming message request: " + e, e.getCause()); - } catch (InterruptedException e) { - throw new A2AServerException("Send streaming message request timed out: " + e, e.getCause()); - } - } - - /** - * Resubscribe to an ongoing task. - * - * @param taskIdParams the params for the task to resubscribe to - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if the remote agent returns an error - * @param failureHandler a consumer that will be invoked if a failure occurs when processing events - * @throws A2AServerException if resubscribing to the task fails for any reason - */ - public void resubscribeToTask(TaskIdParams taskIdParams, Consumer eventHandler, - Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - resubscribeToTask(null, taskIdParams, eventHandler, errorHandler, failureHandler); - } - - /** - * Resubscribe to an ongoing task. - * - * @param requestId the request ID to use - * @param taskIdParams the params for the task to resubscribe to - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if the remote agent returns an error - * @param failureHandler a consumer that will be invoked if a failure occurs when processing events - * @throws A2AServerException if resubscribing to the task fails for any reason - */ - public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, - Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - checkNotNullParam("taskIdParams", taskIdParams); - checkNotNullParam("eventHandler", eventHandler); - checkNotNullParam("errorHandler", errorHandler); - checkNotNullParam("failureHandler", failureHandler); - - TaskResubscriptionRequest.Builder taskResubscriptionRequestBuilder = new TaskResubscriptionRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(TaskResubscriptionRequest.METHOD) - .params(taskIdParams); - - if (requestId != null) { - taskResubscriptionRequestBuilder.id(requestId); - } - - AtomicReference> ref = new AtomicReference<>(); - SSEEventListener sseEventListener = new SSEEventListener(eventHandler, errorHandler, failureHandler); - TaskResubscriptionRequest taskResubscriptionRequest = taskResubscriptionRequestBuilder.build(); - try { - A2AHttpClient.PostBuilder builder = createPostBuilder(taskResubscriptionRequest); - ref.set(builder.postAsyncSSE( - msg -> sseEventListener.onMessage(msg, ref.get()), - throwable -> sseEventListener.onError(throwable, ref.get()), - () -> { - // We don't need to do anything special on completion - })); - - } catch (IOException e) { - throw new A2AServerException("Failed to send task resubscription request: " + e, e.getCause()); - } catch (InterruptedException e) { - throw new A2AServerException("Task resubscription request timed out: " + e, e.getCause()); - } - } - - /** - * Retrieve the authenticated extended agent card. - * - * @param authHeaders the HTTP authentication headers to use - * @return the response - * @throws A2AServerException if retrieving the authenticated extended agent card fails for any reason - */ - public GetAuthenticatedExtendedCardResponse getAuthenticatedExtendedCard(Map authHeaders) throws A2AServerException { - return getAuthenticatedExtendedCard(null, authHeaders); - } - - /** - * Retrieve the authenticated extended agent card. - * - * @param requestId the request ID to use - * @param authHeaders the HTTP authentication headers to use - * @return the response - * @throws A2AServerException if retrieving the authenticated extended agent card fails for any reason - */ - public GetAuthenticatedExtendedCardResponse getAuthenticatedExtendedCard(String requestId, - Map authHeaders) throws A2AServerException { - GetAuthenticatedExtendedCardRequest.Builder requestBuilder = new GetAuthenticatedExtendedCardRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(GetAuthenticatedExtendedCardRequest.METHOD); - - if (requestId != null) { - requestBuilder.id(requestId); - } - - GetAuthenticatedExtendedCardRequest request = requestBuilder.build(); - - try { - String httpResponseBody = sendPostRequest(request, authHeaders); - return unmarshalResponse(httpResponseBody, GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get authenticated extended agent card: " + e, e); - } - } - - private String sendPostRequest(Object value) throws IOException, InterruptedException { - return sendPostRequest(value, null); - } - - private String sendPostRequest(Object value, Map authHeaders) throws IOException, InterruptedException { - A2AHttpClient.PostBuilder builder = createPostBuilder(value, authHeaders); - A2AHttpResponse response = builder.post(); - if (!response.success()) { - throw new IOException("Request failed " + response.status()); - } - return response.body(); - } - - private A2AHttpClient.PostBuilder createPostBuilder(Object value) throws JsonProcessingException { - return createPostBuilder(value, null); - } - - private A2AHttpClient.PostBuilder createPostBuilder(Object value, Map authHeaders) throws JsonProcessingException { - A2AHttpClient.PostBuilder builder = httpClient.createPost() - .url(agentUrl) - .addHeader("Content-Type", "application/json") - .body(Utils.OBJECT_MAPPER.writeValueAsString(value)); - if (authHeaders != null) { - for (Map.Entry entry : authHeaders.entrySet()) { - builder.addHeader(entry.getKey(), entry.getValue()); - } - } - return builder; - } - - private T unmarshalResponse(String response, TypeReference typeReference) - throws A2AServerException, JsonProcessingException { - T value = Utils.unmarshalFrom(response, typeReference); - JSONRPCError error = value.getError(); - if (error != null) { - throw new A2AServerException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); - } - return value; - } -} diff --git a/client/src/main/java/io/a2a/client/A2AGrpcClient.java b/client/src/main/java/io/a2a/client/A2AGrpcClient.java deleted file mode 100644 index 661adc228..000000000 --- a/client/src/main/java/io/a2a/client/A2AGrpcClient.java +++ /dev/null @@ -1,200 +0,0 @@ -package io.a2a.client; - -import static io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub; -import static io.a2a.grpc.A2AServiceGrpc.A2AServiceStub; -import static io.a2a.grpc.utils.ProtoUtils.FromProto; -import static io.a2a.grpc.utils.ProtoUtils.ToProto; -import static io.a2a.util.Assert.checkNotNullParam; - -import java.util.function.Consumer; - -import io.a2a.client.sse.SSEStreamObserver; -import io.a2a.grpc.A2AServiceGrpc; -import io.a2a.grpc.CancelTaskRequest; -import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskRequest; -import io.a2a.grpc.SendMessageRequest; -import io.a2a.grpc.SendMessageResponse; -import io.a2a.grpc.StreamResponse; -import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.spec.A2AServerException; -import io.a2a.spec.AgentCard; -import io.a2a.spec.EventKind; -import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.MessageSendParams; -import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.Task; -import io.a2a.spec.TaskIdParams; -import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.spec.TaskQueryParams; -import io.grpc.Channel; -import io.grpc.StatusRuntimeException; -import io.grpc.stub.StreamObserver; - -/** - * A2A Client for interacting with an A2A agent via gRPC. - */ -public class A2AGrpcClient { - - private A2AServiceBlockingV2Stub blockingStub; - private A2AServiceStub asyncStub; - private AgentCard agentCard; - - /** - * Create an A2A client for interacting with an A2A agent via gRPC. - * - * @param channel the gRPC channel - * @param agentCard the agent card for the A2A server this client will be communicating with - */ - public A2AGrpcClient(Channel channel, AgentCard agentCard) { - checkNotNullParam("channel", channel); - checkNotNullParam("agentCard", agentCard); - this.asyncStub = A2AServiceGrpc.newStub(channel); - this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); - this.agentCard = agentCard; - } - - /** - * Send a message to the remote agent. - * - * @param messageSendParams the parameters for the message to be sent - * @return the response, may be a message or a task - * @throws A2AServerException if sending the message fails for any reason - */ - public EventKind sendMessage(MessageSendParams messageSendParams) throws A2AServerException { - SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); - try { - SendMessageResponse response = blockingStub.sendMessage(request); - if (response.hasMsg()) { - return FromProto.message(response.getMsg()); - } else if (response.hasTask()) { - return FromProto.task(response.getTask()); - } else { - throw new A2AServerException("Server response did not contain a message or task"); - } - } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to send message: " + e, e); - } - } - - /** - * Retrieves the current state and history of a specific task. - * - * @param taskQueryParams the params for the task to be queried - * @return the task - * @throws A2AServerException if retrieving the task fails for any reason - */ - public Task getTask(TaskQueryParams taskQueryParams) throws A2AServerException { - GetTaskRequest.Builder requestBuilder = GetTaskRequest.newBuilder(); - requestBuilder.setName("tasks/" + taskQueryParams.id()); - if (taskQueryParams.historyLength() != null) { - requestBuilder.setHistoryLength(taskQueryParams.historyLength()); - } - GetTaskRequest getTaskRequest = requestBuilder.build(); - try { - return FromProto.task(blockingStub.getTask(getTaskRequest)); - } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to get task: " + e, e); - } - } - - /** - * Cancel a task that was previously submitted to the A2A server. - * - * @param taskIdParams the params for the task to be cancelled - * @return the updated task - * @throws A2AServerException if cancelling the task fails for any reason - */ - public Task cancelTask(TaskIdParams taskIdParams) throws A2AServerException { - CancelTaskRequest cancelTaskRequest = CancelTaskRequest.newBuilder() - .setName("tasks/" + taskIdParams.id()) - .build(); - try { - return FromProto.task(blockingStub.cancelTask(cancelTaskRequest)); - } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to cancel task: " + e, e); - } - } - - /** - * Set push notification configuration for a task. - * - * @param taskPushNotificationConfig the task push notification configuration - * @return the task push notification config - * @throws A2AServerException if setting the push notification configuration fails for any reason - */ - public TaskPushNotificationConfig setTaskPushNotificationConfig(TaskPushNotificationConfig taskPushNotificationConfig) throws A2AServerException { - String configId = taskPushNotificationConfig.pushNotificationConfig().id(); - CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + taskPushNotificationConfig.taskId()) - .setConfig(ToProto.taskPushNotificationConfig(taskPushNotificationConfig)) - .setConfigId(configId == null ? "" : configId) - .build(); - try { - return FromProto.taskPushNotificationConfig(blockingStub.createTaskPushNotificationConfig(request)); - } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to set the task push notification config: " + e, e); - } - } - - /** - * Get the push notification configuration for a task. - * - * @param getTaskPushNotificationConfigParams the params for the task - * @return the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason - */ - public TaskPushNotificationConfig getTaskPushNotificationConfig(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { - GetTaskPushNotificationConfigRequest getTaskPushNotificationConfigRequest = GetTaskPushNotificationConfigRequest.newBuilder() - .setName(getTaskPushNotificationConfigName(getTaskPushNotificationConfigParams)) - .build(); - try { - return FromProto.taskPushNotificationConfig(blockingStub.getTaskPushNotificationConfig(getTaskPushNotificationConfigRequest)); - } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to get the task push notification config: " + e, e); - } - } - - /** - * Send a streaming message request to the remote agent. - * - * @param messageSendParams the parameters for the message to be sent - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if an error occurs - * @throws A2AServerException if sending the streaming message fails for any reason - */ - public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer eventHandler, - Consumer errorHandler) throws A2AServerException { - SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); - StreamObserver streamObserver = new SSEStreamObserver(eventHandler, errorHandler); - try { - asyncStub.sendStreamingMessage(request, streamObserver); - } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to send streaming message: " + e, e); - } - } - - private SendMessageRequest createGrpcSendMessageRequestFromMessageSendParams(MessageSendParams messageSendParams) { - SendMessageRequest.Builder builder = SendMessageRequest.newBuilder(); - builder.setRequest(ToProto.message(messageSendParams.message())); - if (messageSendParams.configuration() != null) { - builder.setConfiguration(ToProto.messageSendConfiguration(messageSendParams.configuration())); - } - if (messageSendParams.metadata() != null) { - builder.setMetadata(ToProto.struct(messageSendParams.metadata())); - } - return builder.build(); - } - - private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) { - StringBuilder name = new StringBuilder(); - name.append("tasks/"); - name.append(getTaskPushNotificationConfigParams.id()); - if (getTaskPushNotificationConfigParams.pushNotificationConfigId() != null) { - name.append("/pushNotificationConfigs/"); - name.append(getTaskPushNotificationConfigParams.pushNotificationConfigId()); - } - return name.toString(); - } -} diff --git a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java index c9ce509d3..d85e341f4 100644 --- a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java +++ b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java @@ -11,8 +11,8 @@ import java.util.function.Consumer; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.A2AHttpResponse; +import io.a2a.transport.jsonrpc.client.A2AHttpClient; +import io.a2a.transport.jsonrpc.client.A2AHttpResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/pom.xml b/pom.xml index 22e44da39..d454e284a 100644 --- a/pom.xml +++ b/pom.xml @@ -290,6 +290,7 @@ spec-grpc tck tests/server-common + transport/spi transport/jsonrpc transport/grpc diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 4a98ace48..816ed6f79 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -3,7 +3,7 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import io.a2a.grpc.handler.GrpcHandler; +import io.a2a.transport.grpc.server.handler.GrpcHandler; import io.a2a.server.PublicAgentCard; import io.a2a.server.requesthandlers.CallContextFactory; import io.a2a.server.requesthandlers.RequestHandler; diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java index 25758da9c..9fb0074ac 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -19,7 +19,7 @@ import jakarta.ws.rs.core.Response; import io.a2a.server.apps.common.TestUtilsBean; -import io.a2a.grpc.handler.GrpcHandler; +import io.a2a.transport.grpc.server.handler.GrpcHandler; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 2cd411409..4e8cbcbb9 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; -import io.a2a.jsonrpc.handler.JSONRPCHandler; +import io.a2a.transport.jsonrpc.server.handler.JSONRPCHandler; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index 33ac4445c..ac04216f3 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -10,8 +10,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.JdkA2AHttpClient; +import io.a2a.transport.jsonrpc.client.A2AHttpClient; +import io.a2a.transport.jsonrpc.client.JdkA2AHttpClient; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.util.Utils; diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 7232d1a0c..7c62562f8 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -12,8 +12,8 @@ import java.util.concurrent.Executors; import java.util.function.Consumer; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.A2AHttpResponse; +import io.a2a.transport.jsonrpc.client.A2AHttpClient; +import io.a2a.transport.jsonrpc.client.A2AHttpResponse; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java index c5deef68f..046e0f8bb 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java @@ -11,8 +11,8 @@ import jakarta.enterprise.context.Dependent; import jakarta.enterprise.inject.Alternative; -import io.a2a.http.A2AHttpClient; -import io.a2a.http.A2AHttpResponse; +import io.a2a.transport.jsonrpc.client.A2AHttpClient; +import io.a2a.transport.jsonrpc.client.A2AHttpResponse; import io.a2a.spec.Task; import io.a2a.util.Utils; diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 93ec2a095..7bcc7149d 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -18,6 +18,11 @@ Java SDK for the Agent2Agent Protocol (A2A) - gRPC + + io.github.a2asdk + a2a-java-sdk-transport-spi + ${project.version} + io.github.a2asdk a2a-java-sdk-server-common diff --git a/client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java similarity index 83% rename from client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java rename to transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java index adc721f42..ae6d705fc 100644 --- a/client/src/main/java/io/a2a/client/sse/SSEStreamObserver.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java @@ -1,22 +1,22 @@ -package io.a2a.client.sse; +package io.a2a.transport.grpc.client; -import static io.a2a.grpc.utils.ProtoUtils.FromProto; +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.StreamingEventKind; +import io.grpc.stub.StreamObserver; import java.util.function.Consumer; import java.util.logging.Logger; -import io.a2a.grpc.StreamResponse; -import io.a2a.spec.StreamingEventKind; -import io.grpc.stub.StreamObserver; +import static io.a2a.grpc.utils.ProtoUtils.FromProto; -public class SSEStreamObserver implements StreamObserver { +public class EventStreamObserver implements StreamObserver { - private static final Logger log = Logger.getLogger(SSEStreamObserver.class.getName()); + private static final Logger log = Logger.getLogger(EventStreamObserver.class.getName()); private final Consumer eventHandler; private final Consumer errorHandler; - public SSEStreamObserver(Consumer eventHandler, Consumer errorHandler) { + public EventStreamObserver(Consumer eventHandler, Consumer errorHandler) { this.eventHandler = eventHandler; this.errorHandler = errorHandler; } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java new file mode 100644 index 000000000..2975948c5 --- /dev/null +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java @@ -0,0 +1,164 @@ +package io.a2a.transport.grpc.client; + +import io.a2a.grpc.*; +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.spec.*; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Task; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.transport.spi.client.Transport; +import io.grpc.Channel; +import io.grpc.StatusRuntimeException; +import io.grpc.stub.StreamObserver; + +import java.util.List; +import java.util.function.Consumer; + +import static io.a2a.util.Assert.checkNotNullParam; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +public class GrpcTransport implements Transport { + + private A2AServiceGrpc.A2AServiceBlockingV2Stub blockingStub; + private A2AServiceGrpc.A2AServiceStub asyncStub; + private AgentCard agentCard; + + /** + * Create an A2A client for interacting with an A2A agent via gRPC. + * + * @param channel the gRPC channel + * @param agentCard the agent card for the A2A server this client will be communicating with + */ + public GrpcTransport(Channel channel, AgentCard agentCard) { + checkNotNullParam("channel", channel); + checkNotNullParam("agentCard", agentCard); + this.asyncStub = A2AServiceGrpc.newStub(channel); + this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); + this.agentCard = agentCard; + } + + @Override + public EventKind sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException { + SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); + try { + SendMessageResponse response = blockingStub.sendMessage(request); + if (response.hasMsg()) { + return ProtoUtils.FromProto.message(response.getMsg()); + } else if (response.hasTask()) { + return ProtoUtils.FromProto.task(response.getTask()); + } else { + throw new A2AServerException("Server response did not contain a message or task"); + } + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to send message: " + e, e); + } + } + + @Override + public Task getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException { + io.a2a.grpc.GetTaskRequest.Builder requestBuilder = io.a2a.grpc.GetTaskRequest.newBuilder(); + requestBuilder.setName("tasks/" + taskQueryParams.id()); + if (taskQueryParams.historyLength() != null) { + requestBuilder.setHistoryLength(taskQueryParams.historyLength()); + } + io.a2a.grpc.GetTaskRequest getTaskRequest = requestBuilder.build(); + try { + return ProtoUtils.FromProto.task(blockingStub.getTask(getTaskRequest)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to get task: " + e, e); + } + } + + @Override + public Task cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException { + io.a2a.grpc.CancelTaskRequest cancelTaskRequest = io.a2a.grpc.CancelTaskRequest.newBuilder() + .setName("tasks/" + taskIdParams.id()) + .build(); + try { + return ProtoUtils.FromProto.task(blockingStub.cancelTask(cancelTaskRequest)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to cancel task: " + e, e); + } + } + + @Override + public TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { + io.a2a.grpc.GetTaskPushNotificationConfigRequest getTaskPushNotificationConfigRequest = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder() + .setName(getTaskPushNotificationConfigName(getTaskPushNotificationConfigParams)) + .build(); + try { + return ProtoUtils.FromProto.taskPushNotificationConfig(blockingStub.getTaskPushNotificationConfig(getTaskPushNotificationConfigRequest)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to get the task push notification config: " + e, e); + } + } + + @Override + public TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, TaskPushNotificationConfig taskPushNotificationConfig) throws A2AServerException { + String configId = taskPushNotificationConfig.pushNotificationConfig().id(); + CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + taskPushNotificationConfig.taskId()) + .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushNotificationConfig)) + .setConfigId(configId == null ? "" : configId) + .build(); + try { + return ProtoUtils.FromProto.taskPushNotificationConfig(blockingStub.createTaskPushNotificationConfig(request)); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to set the task push notification config: " + e, e); + } + } + + @Override + public List listTaskPushNotificationConfig(String requestId, ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { + return List.of(); + } + + @Override + public void deleteTaskPushNotificationConfig(String requestId, DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { + + } + + @Override + public void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { + SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); + StreamObserver streamObserver = new EventStreamObserver(eventHandler, errorHandler); + try { + asyncStub.sendStreamingMessage(request, streamObserver); + } catch (StatusRuntimeException e) { + throw new A2AServerException("Failed to send streaming message: " + e, e); + } + } + + @Override + public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { + + } + + private SendMessageRequest createGrpcSendMessageRequestFromMessageSendParams(MessageSendParams messageSendParams) { + SendMessageRequest.Builder builder = SendMessageRequest.newBuilder(); + builder.setRequest(ProtoUtils.ToProto.message(messageSendParams.message())); + if (messageSendParams.configuration() != null) { + builder.setConfiguration(ProtoUtils.ToProto.messageSendConfiguration(messageSendParams.configuration())); + } + if (messageSendParams.metadata() != null) { + builder.setMetadata(ProtoUtils.ToProto.struct(messageSendParams.metadata())); + } + return builder.build(); + } + + private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) { + StringBuilder name = new StringBuilder(); + name.append("tasks/"); + name.append(getTaskPushNotificationConfigParams.id()); + if (getTaskPushNotificationConfigParams.pushNotificationConfigId() != null) { + name.append("/pushNotificationConfigs/"); + name.append(getTaskPushNotificationConfigParams.pushNotificationConfigId()); + } + return name.toString(); + } +} diff --git a/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/server/handler/GrpcHandler.java similarity index 99% rename from transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java rename to transport/grpc/src/main/java/io/a2a/transport/grpc/server/handler/GrpcHandler.java index 69f9d2c1d..4ecdbc9ab 100644 --- a/transport/grpc/src/main/java/io/a2a/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/server/handler/GrpcHandler.java @@ -1,4 +1,4 @@ -package io.a2a.grpc.handler; +package io.a2a.transport.grpc.server.handler; import static io.a2a.grpc.utils.ProtoUtils.FromProto; import static io.a2a.grpc.utils.ProtoUtils.ToProto; diff --git a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java index 1cfeb6626..76cdb6f6e 100644 --- a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java @@ -45,6 +45,7 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.transport.grpc.server.handler.GrpcHandler; import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.internal.testing.StreamRecorder; diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 15eecd06e..d0f3304fe 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -18,6 +18,11 @@ Java SDK for the Agent2Agent Protocol (A2A) - JSONRPC + + io.github.a2asdk + a2a-java-sdk-transport-spi + ${project.version} + io.github.a2asdk a2a-java-sdk-server-common diff --git a/client/src/main/java/io/a2a/http/A2AHttpClient.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpClient.java similarity index 96% rename from client/src/main/java/io/a2a/http/A2AHttpClient.java rename to transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpClient.java index 7a246843a..3ebd6e8b7 100644 --- a/client/src/main/java/io/a2a/http/A2AHttpClient.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpClient.java @@ -1,4 +1,4 @@ -package io.a2a.http; +package io.a2a.transport.jsonrpc.client; import java.io.IOException; import java.util.concurrent.CompletableFuture; diff --git a/client/src/main/java/io/a2a/http/A2AHttpResponse.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpResponse.java similarity index 70% rename from client/src/main/java/io/a2a/http/A2AHttpResponse.java rename to transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpResponse.java index d6973a5dc..0e7074b8b 100644 --- a/client/src/main/java/io/a2a/http/A2AHttpResponse.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpResponse.java @@ -1,4 +1,4 @@ -package io.a2a.http; +package io.a2a.transport.jsonrpc.client; public interface A2AHttpResponse { int status(); diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java new file mode 100644 index 000000000..dce6ab668 --- /dev/null +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java @@ -0,0 +1,301 @@ +package io.a2a.transport.jsonrpc.client; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import io.a2a.spec.A2AServerException; +import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskPushNotificationConfigResponse; +import io.a2a.spec.GetTaskRequest; +import io.a2a.spec.GetTaskResponse; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.JSONRPCMessage; +import io.a2a.spec.JSONRPCResponse; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendMessageResponse; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.SetTaskPushNotificationConfigResponse; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskResubscriptionRequest; +import io.a2a.transport.jsonrpc.client.sse.SSEEventListener; +import io.a2a.transport.spi.client.Transport; +import io.a2a.util.Utils; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +public class JSONRPCTransport implements Transport { + + private static final TypeReference SEND_MESSAGE_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference GET_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference CANCEL_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + + private final String agentUrl; + private final A2AHttpClient httpClient; + + public JSONRPCTransport(String agentUrl) { + this(agentUrl, new JdkA2AHttpClient()); + } + + public JSONRPCTransport(String agentUrl, A2AHttpClient httpClient) { + this.agentUrl = agentUrl; + this.httpClient = httpClient; + } + + @Override + public EventKind sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException { + SendMessageRequest.Builder sendMessageRequestBuilder = new SendMessageRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(SendMessageRequest.METHOD) + .params(messageSendParams); + + if (requestId != null) { + sendMessageRequestBuilder.id(requestId); + } + + SendMessageRequest sendMessageRequest = sendMessageRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(sendMessageRequest); + return unmarshalResponse(httpResponseBody, SEND_MESSAGE_RESPONSE_REFERENCE).getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to send message: " + e, e.getCause()); + } + } + + @Override + public Task getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException { + GetTaskRequest.Builder getTaskRequestBuilder = new GetTaskRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetTaskRequest.METHOD) + .params(taskQueryParams); + + if (requestId != null) { + getTaskRequestBuilder.id(requestId); + } + + GetTaskRequest getTaskRequest = getTaskRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(getTaskRequest); + return unmarshalResponse(httpResponseBody, GET_TASK_RESPONSE_REFERENCE).getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to get task: " + e, e.getCause()); + } + } + + @Override + public Task cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException { + CancelTaskRequest.Builder cancelTaskRequestBuilder = new CancelTaskRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(CancelTaskRequest.METHOD) + .params(taskIdParams); + + if (requestId != null) { + cancelTaskRequestBuilder.id(requestId); + } + + CancelTaskRequest cancelTaskRequest = cancelTaskRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(cancelTaskRequest); + return unmarshalResponse(httpResponseBody, CANCEL_TASK_RESPONSE_REFERENCE).getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to cancel task: " + e, e.getCause()); + } + } + + @Override + public TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { + GetTaskPushNotificationConfigRequest.Builder getTaskPushNotificationRequestBuilder = new GetTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetTaskPushNotificationConfigRequest.METHOD) + .params(getTaskPushNotificationConfigParams); + + if (requestId != null) { + getTaskPushNotificationRequestBuilder.id(requestId); + } + + GetTaskPushNotificationConfigRequest getTaskPushNotificationRequest = getTaskPushNotificationRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(getTaskPushNotificationRequest); + return unmarshalResponse(httpResponseBody, GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE).getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to get task push notification config: " + e, e.getCause()); + } + } + + @Override + public TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, PushNotificationConfig pushNotificationConfig) throws A2AServerException { + SetTaskPushNotificationConfigRequest.Builder setTaskPushNotificationRequestBuilder = new SetTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(SetTaskPushNotificationConfigRequest.METHOD) + .params(new TaskPushNotificationConfig(taskId, pushNotificationConfig)); + + if (requestId != null) { + setTaskPushNotificationRequestBuilder.id(requestId); + } + + SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = setTaskPushNotificationRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(setTaskPushNotificationRequest); + return unmarshalResponse(httpResponseBody, SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE).getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to set task push notification config: " + e, e.getCause()); + } + } + + @Override + public List listTaskPushNotificationConfig(String requestId, ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { + ListTaskPushNotificationConfigRequest.Builder listTaskPushNotificationRequestBuilder = new ListTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(ListTaskPushNotificationConfigRequest.METHOD) + .params(listTaskPushNotificationConfigParams); + + if (requestId != null) { + listTaskPushNotificationRequestBuilder.id(requestId); + } + + ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = listTaskPushNotificationRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(listTaskPushNotificationRequest); + return unmarshalResponse(httpResponseBody, LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE).getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to list task push notification config: " + e, e.getCause()); + } + } + + @Override + public void deleteTaskPushNotificationConfig(String requestId, DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { + DeleteTaskPushNotificationConfigRequest.Builder deleteTaskPushNotificationRequestBuilder = new DeleteTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(DeleteTaskPushNotificationConfigRequest.METHOD) + .params(deleteTaskPushNotificationConfigParams); + + if (requestId != null) { + deleteTaskPushNotificationRequestBuilder.id(requestId); + } + + DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = deleteTaskPushNotificationRequestBuilder.build(); + + try { + String httpResponseBody = sendPostRequest(deleteTaskPushNotificationRequest); + unmarshalResponse(httpResponseBody, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + } catch (IOException | InterruptedException e) { + throw new A2AServerException("Failed to delete task push notification config: " + e, e.getCause()); + } + } + + @Override + public void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { + SendStreamingMessageRequest.Builder sendStreamingMessageRequestBuilder = new SendStreamingMessageRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(SendStreamingMessageRequest.METHOD) + .params(messageSendParams); + + if (requestId != null) { + sendStreamingMessageRequestBuilder.id(requestId); + } + + AtomicReference> ref = new AtomicReference<>(); + SSEEventListener sseEventListener = new SSEEventListener(eventHandler, errorHandler, failureHandler); + SendStreamingMessageRequest sendStreamingMessageRequest = sendStreamingMessageRequestBuilder.build(); + try { + A2AHttpClient.PostBuilder builder = createPostBuilder(sendStreamingMessageRequest); + ref.set(builder.postAsyncSSE( + msg -> sseEventListener.onMessage(msg, ref.get()), + throwable -> sseEventListener.onError(throwable, ref.get()), + () -> { + // We don't need to do anything special on completion + })); + + } catch (IOException e) { + throw new A2AServerException("Failed to send streaming message request: " + e, e.getCause()); + } catch (InterruptedException e) { + throw new A2AServerException("Send streaming message request timed out: " + e, e.getCause()); + } + } + + @Override + public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { + TaskResubscriptionRequest.Builder taskResubscriptionRequestBuilder = new TaskResubscriptionRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(TaskResubscriptionRequest.METHOD) + .params(taskIdParams); + + if (requestId != null) { + taskResubscriptionRequestBuilder.id(requestId); + } + + AtomicReference> ref = new AtomicReference<>(); + SSEEventListener sseEventListener = new SSEEventListener(eventHandler, errorHandler, failureHandler); + TaskResubscriptionRequest taskResubscriptionRequest = taskResubscriptionRequestBuilder.build(); + try { + A2AHttpClient.PostBuilder builder = createPostBuilder(taskResubscriptionRequest); + ref.set(builder.postAsyncSSE( + msg -> sseEventListener.onMessage(msg, ref.get()), + throwable -> sseEventListener.onError(throwable, ref.get()), + () -> { + // We don't need to do anything special on completion + })); + + } catch (IOException e) { + throw new A2AServerException("Failed to send task resubscription request: " + e, e.getCause()); + } catch (InterruptedException e) { + throw new A2AServerException("Task resubscription request timed out: " + e, e.getCause()); + } + } + + private String sendPostRequest(Object value) throws IOException, InterruptedException { + A2AHttpClient.PostBuilder builder = createPostBuilder(value); + A2AHttpResponse response = builder.post(); + if (!response.success()) { + throw new IOException("Request failed " + response.status()); + } + return response.body(); + } + + private A2AHttpClient.PostBuilder createPostBuilder(Object value) throws JsonProcessingException { + return httpClient.createPost() + .url(agentUrl) + .addHeader("Content-Type", "application/json") + .body(Utils.OBJECT_MAPPER.writeValueAsString(value)); + + } + + private T unmarshalResponse(String response, TypeReference typeReference) + throws A2AServerException, JsonProcessingException { + T value = Utils.unmarshalFrom(response, typeReference); + JSONRPCError error = value.getError(); + if (error != null) { + throw new A2AServerException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); + } + return value; + } +} diff --git a/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JdkA2AHttpClient.java similarity index 99% rename from client/src/main/java/io/a2a/http/JdkA2AHttpClient.java rename to transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JdkA2AHttpClient.java index c3d5907a2..e61d7fab3 100644 --- a/client/src/main/java/io/a2a/http/JdkA2AHttpClient.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JdkA2AHttpClient.java @@ -1,4 +1,4 @@ -package io.a2a.http; +package io.a2a.transport.jsonrpc.client; import java.io.IOException; import java.net.URI; diff --git a/client/src/main/java/io/a2a/client/sse/SSEEventListener.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListener.java similarity index 98% rename from client/src/main/java/io/a2a/client/sse/SSEEventListener.java rename to transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListener.java index 8ed0e9aa3..7f4ff19fc 100644 --- a/client/src/main/java/io/a2a/client/sse/SSEEventListener.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListener.java @@ -1,10 +1,4 @@ -package io.a2a.client.sse; - -import static io.a2a.util.Utils.OBJECT_MAPPER; - -import java.util.concurrent.Future; -import java.util.function.Consumer; -import java.util.logging.Logger; +package io.a2a.transport.jsonrpc.client.sse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -12,6 +6,12 @@ import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskStatusUpdateEvent; +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import static io.a2a.util.Utils.OBJECT_MAPPER; + public class SSEEventListener { private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); private final Consumer eventHandler; diff --git a/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandler.java similarity index 99% rename from transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java rename to transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandler.java index e4e11f294..463c2aab3 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandler.java @@ -1,4 +1,4 @@ -package io.a2a.jsonrpc.handler; +package io.a2a.transport.jsonrpc.server.handler; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import jakarta.enterprise.context.ApplicationScoped; diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java new file mode 100644 index 000000000..7bf46054d --- /dev/null +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java @@ -0,0 +1,148 @@ +package io.a2a.transport.jsonrpc.client; + +/** + * Contains JSON strings for testing SSE streaming. + */ +public class JsonStreamingMessages { + + public static final String STREAMING_TASK_EVENT = """ + data: { + "jsonrpc": "2.0", + "id": "1234", + "result": { + "kind": "task", + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "working" + } + } + } + """; + + + public static final String STREAMING_MESSAGE_EVENT = """ + data: { + "jsonrpc": "2.0", + "id": "1234", + "result": { + "kind": "message", + "role": "agent", + "messageId": "msg-123", + "contextId": "context-456", + "parts": [ + { + "kind": "text", + "text": "Hello, world!" + } + ] + } + }"""; + + public static final String STREAMING_STATUS_UPDATE_EVENT = """ + data: { + "jsonrpc": "2.0", + "id": "1234", + "result": { + "taskId": "1", + "contextId": "2", + "status": { + "state": "submitted" + }, + "final": false, + "kind": "status-update" + } + }"""; + + public static final String STREAMING_STATUS_UPDATE_EVENT_FINAL = """ + data: { + "jsonrpc": "2.0", + "id": "1234", + "result": { + "taskId": "1", + "contextId": "2", + "status": { + "state": "completed" + }, + "final": true, + "kind": "status-update" + } + }"""; + + public static final String STREAMING_ARTIFACT_UPDATE_EVENT = """ + data: { + "jsonrpc": "2.0", + "id": "1234", + "result": { + "kind": "artifact-update", + "taskId": "1", + "contextId": "2", + "append": false, + "lastChunk": true, + "artifact": { + "artifactId": "artifact-1", + "parts": [ + { + "kind": "text", + "text": "Why did the chicken cross the road? To get to the other side!" + } + ] + } + } + } + }"""; + + public static final String STREAMING_ERROR_EVENT = """ + data: { + "jsonrpc": "2.0", + "id": "1234", + "error": { + "code": -32602, + "message": "Invalid parameters", + "data": "Missing required field" + } + }"""; + + public static final String SEND_MESSAGE_STREAMING_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "id": "request-1234", + "method": "message/stream", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me some jokes" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": false + }, + } + }"""; + + static final String SEND_MESSAGE_STREAMING_TEST_RESPONSE = + "event: message\n" + + "data: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"completed\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"kind\":\"text\",\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{},\"kind\":\"task\"}}\n\n"; + + static final String TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE = + "event: message\n" + + "data: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"completed\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"kind\":\"text\",\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{},\"kind\":\"task\"}}\n\n"; + + public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "id": "request-1234", + "method": "tasks/resubscribe", + "params": { + "id": "task-1234" + } + }"""; +} \ No newline at end of file diff --git a/client/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListenerTest.java similarity index 97% rename from client/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java rename to transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListenerTest.java index 1fca0ff9c..5e4fc357c 100644 --- a/client/src/test/java/io/a2a/client/sse/SSEEventListenerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListenerTest.java @@ -1,18 +1,5 @@ -package io.a2a.client.sse; +package io.a2a.transport.jsonrpc.client.sse; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import io.a2a.client.JsonStreamingMessages; import io.a2a.spec.Artifact; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; @@ -24,8 +11,18 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; +import io.a2a.transport.jsonrpc.client.JsonStreamingMessages; import org.junit.jupiter.api.Test; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.*; + public class SSEEventListenerTest { @Test diff --git a/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandlerTest.java similarity index 99% rename from transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java rename to transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandlerTest.java index a645c9b37..931f4fdc0 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandlerTest.java @@ -1,4 +1,4 @@ -package io.a2a.jsonrpc.handler; +package io.a2a.transport.jsonrpc.server.handler; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -65,6 +65,7 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.transport.jsonrpc.server.handler.JSONRPCHandler; import mutiny.zero.ZeroPublisher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; diff --git a/transport/spi/pom.xml b/transport/spi/pom.xml new file mode 100644 index 000000000..1e284687f --- /dev/null +++ b/transport/spi/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-transport-spi + + jar + + Java SDK A2A Transport: SPI + Java SDK for the Agent2Agent Protocol (A2A) - SPI + + + + io.github.a2asdk + a2a-java-sdk-spec + ${project.version} + + + + + diff --git a/transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java b/transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java new file mode 100644 index 000000000..9ab643b56 --- /dev/null +++ b/transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java @@ -0,0 +1,109 @@ +package io.a2a.transport.spi.client; + +import io.a2a.spec.*; + +import java.util.List; +import java.util.function.Consumer; + +public interface Transport { + + /** + * Send a message to the remote agent. + * + * @param requestId the request ID to use + * @param messageSendParams the parameters for the message to be sent + * @return the response, may contain a message or a task + * @throws A2AServerException if sending the message fails for any reason + */ + EventKind sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException; + + /** + * Retrieve the generated artifacts for a task. + * + * @param requestId the request ID to use + * @param taskQueryParams the params for the task to be queried + * @return the response containing the task + * @throws A2AServerException if retrieving the task fails for any reason + */ + Task getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException; + + /** + * Cancel a task that was previously submitted to the A2A server. + * + * @param requestId the request ID to use + * @param taskIdParams the params for the task to be cancelled + * @return the response indicating if the task was cancelled + * @throws A2AServerException if retrieving the task fails for any reason + */ + Task cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException; + + /** + * Get the push notification configuration for a task. + * + * @param requestId the request ID to use + * @param getTaskPushNotificationConfigParams the params for the task + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException; + + /** + * Set push notification configuration for a task. + * + * @param requestId the request ID to use + * @param taskId the task ID + * @param pushNotificationConfig the push notification configuration + * @return the response indicating whether setting the task push notification configuration succeeded + * @throws A2AServerException if setting the push notification configuration fails for any reason + */ + TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, + PushNotificationConfig pushNotificationConfig) throws A2AServerException; + + /** + * Retrieves the push notification configurations for a specified task. + * + * @param requestId the request ID to use + * @param listTaskPushNotificationConfigParams the params for retrieving the push notification configuration + * @return the response containing the push notification configuration + * @throws A2AServerException if getting the push notification configuration fails for any reason + */ + List listTaskPushNotificationConfig(String requestId, + ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException; + + /** + * Delete the push notification configuration for a specified task. + * + * @param requestId the request ID to use + * @param deleteTaskPushNotificationConfigParams the params for deleting the push notification configuration + * @throws A2AServerException if deleting the push notification configuration fails for any reason + */ + void deleteTaskPushNotificationConfig(String requestId, + DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException; + + /** + * Send a streaming message to the remote agent. + * + * @param requestId the request ID to use + * @param messageSendParams the parameters for the message to be sent + * @param eventHandler a consumer that will be invoked for each event received from the remote agent + * @param errorHandler a consumer that will be invoked if the remote agent returns an error + * @param failureHandler a consumer that will be invoked if a failure occurs when processing events + * @throws A2AServerException if sending the streaming message fails for any reason + */ + void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, + Consumer errorHandler, Runnable failureHandler) throws A2AServerException; + + /** + * Resubscribe to an ongoing task. + * + * @param requestId the request ID to use + * @param taskIdParams the params for the task to resubscribe to + * @param eventHandler a consumer that will be invoked for each event received from the remote agent + * @param errorHandler a consumer that will be invoked if the remote agent returns an error + * @param failureHandler a consumer that will be invoked if a failure occurs when processing events + * @throws A2AServerException if resubscribing to the task fails for any reason + */ + void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, + Consumer errorHandler, Runnable failureHandler) throws A2AServerException; + +} From db2a40812f03ab6adf56e9ed9fd2306efd6561b0 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 14 Aug 2025 13:15:38 -0400 Subject: [PATCH 066/493] fix: Split out client and server transport code into separate modules and also split out the http client code into a separate module --- client-http/pom.xml | 33 ++++++++++++ .../io/a2a/client/http}/A2AHttpClient.java | 2 +- .../io/a2a/client/http}/A2AHttpResponse.java | 2 +- .../io/a2a/client/http}/JdkA2AHttpClient.java | 2 +- client-transport/grpc/pom.xml | 53 +++++++++++++++++++ .../transport/grpc}/EventStreamObserver.java | 2 +- .../client/transport/grpc}/GrpcTransport.java | 6 +-- client-transport/jsonrpc/pom.xml | 53 +++++++++++++++++++ .../transport/jsonrpc}/JSONRPCTransport.java | 10 ++-- .../jsonrpc}/sse/SSEEventListener.java | 2 +- .../jsonrpc/sse}/JsonStreamingMessages.java | 2 +- .../jsonrpc}/sse/SSEEventListenerTest.java | 3 +- {transport => client-transport}/spi/pom.xml | 7 ++- .../a2a/client/transport/spi}/Transport.java | 2 +- client/pom.xml | 7 ++- client/src/main/java/io/a2a/A2A.java | 4 +- .../java/io/a2a/client/A2ACardResolver.java | 4 +- .../io/a2a/client/A2ACardResolverTest.java | 4 +- examples/helloworld/server/pom.xml | 4 ++ pom.xml | 5 +- .../grpc/quarkus/QuarkusGrpcHandler.java | 4 +- .../server/grpc/quarkus/A2ATestResource.java | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 2 +- server-common/pom.xml | 2 +- .../tasks/BasePushNotificationSender.java | 4 +- .../AbstractA2ARequestHandlerTest.java | 4 +- .../server/apps/common/TestHttpClient.java | 4 +- .../grpc/handler}/CallContextFactory.java | 4 +- .../{server => }/handler/GrpcHandler.java | 3 +- .../grpc/handler/GrpcHandlerTest.java | 4 +- .../{server => }/handler/JSONRPCHandler.java | 2 +- .../handler/JSONRPCHandlerTest.java | 4 +- 32 files changed, 198 insertions(+), 48 deletions(-) create mode 100644 client-http/pom.xml rename {transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client => client-http/src/main/java/io/a2a/client/http}/A2AHttpClient.java (96%) rename {transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client => client-http/src/main/java/io/a2a/client/http}/A2AHttpResponse.java (70%) rename {transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client => client-http/src/main/java/io/a2a/client/http}/JdkA2AHttpClient.java (99%) create mode 100644 client-transport/grpc/pom.xml rename {transport/grpc/src/main/java/io/a2a/transport/grpc/client => client-transport/grpc/src/main/java/io/a2a/client/transport/grpc}/EventStreamObserver.java (97%) rename {transport/grpc/src/main/java/io/a2a/transport/grpc/client => client-transport/grpc/src/main/java/io/a2a/client/transport/grpc}/GrpcTransport.java (98%) create mode 100644 client-transport/jsonrpc/pom.xml rename {transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client => client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc}/JSONRPCTransport.java (98%) rename {transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client => client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc}/sse/SSEEventListener.java (98%) rename {transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client => client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse}/JsonStreamingMessages.java (99%) rename {transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client => client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc}/sse/SSEEventListenerTest.java (98%) rename {transport => client-transport}/spi/pom.xml (78%) rename {transport/spi/src/main/java/io/a2a/transport/spi/client => client-transport/spi/src/main/java/io/a2a/client/transport/spi}/Transport.java (99%) rename {server-common/src/main/java/io/a2a/server/requesthandlers => transport/grpc/src/main/java/io/a2a/transport/grpc/handler}/CallContextFactory.java (81%) rename transport/grpc/src/main/java/io/a2a/transport/grpc/{server => }/handler/GrpcHandler.java (99%) rename transport/grpc/src/test/java/io/a2a/{ => transport}/grpc/handler/GrpcHandlerTest.java (99%) rename transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/{server => }/handler/JSONRPCHandler.java (99%) rename transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/{server => }/handler/JSONRPCHandlerTest.java (99%) diff --git a/client-http/pom.xml b/client-http/pom.xml new file mode 100644 index 000000000..ceff12762 --- /dev/null +++ b/client-http/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + + a2a-java-sdk-client-http + + jar + + Java SDK A2A HTTP Client + Java SDK for the Agent2Agent Protocol (A2A) - HTTP Client + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + + \ No newline at end of file diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpClient.java b/client-http/src/main/java/io/a2a/client/http/A2AHttpClient.java similarity index 96% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpClient.java rename to client-http/src/main/java/io/a2a/client/http/A2AHttpClient.java index 3ebd6e8b7..f59e079f2 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpClient.java +++ b/client-http/src/main/java/io/a2a/client/http/A2AHttpClient.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.client; +package io.a2a.client.http; import java.io.IOException; import java.util.concurrent.CompletableFuture; diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpResponse.java b/client-http/src/main/java/io/a2a/client/http/A2AHttpResponse.java similarity index 70% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpResponse.java rename to client-http/src/main/java/io/a2a/client/http/A2AHttpResponse.java index 0e7074b8b..171fceebd 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/A2AHttpResponse.java +++ b/client-http/src/main/java/io/a2a/client/http/A2AHttpResponse.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.client; +package io.a2a.client.http; public interface A2AHttpResponse { int status(); diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JdkA2AHttpClient.java b/client-http/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java similarity index 99% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JdkA2AHttpClient.java rename to client-http/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index e61d7fab3..2cdbb2d37 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JdkA2AHttpClient.java +++ b/client-http/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.client; +package io.a2a.client.http; import java.io.IOException; import java.net.URI; diff --git a/client-transport/grpc/pom.xml b/client-transport/grpc/pom.xml new file mode 100644 index 000000000..87e873a4d --- /dev/null +++ b/client-transport/grpc/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-client-transport-grpc + jar + + Java SDK A2A Client Transport: gRPC + Java SDK for the Agent2Agent Protocol (A2A) - gRPC Client Transport + + + + ${project.groupId} + a2a-java-sdk-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-spi + ${project.version} + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + + \ No newline at end of file diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java similarity index 97% rename from transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java rename to client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java index ae6d705fc..48bfa6e76 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/client/EventStreamObserver.java +++ b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java @@ -1,4 +1,4 @@ -package io.a2a.transport.grpc.client; +package io.a2a.client.transport.grpc; import io.a2a.grpc.StreamResponse; diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java similarity index 98% rename from transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java rename to client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 2975948c5..f008a8b10 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/client/GrpcTransport.java +++ b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -1,4 +1,4 @@ -package io.a2a.transport.grpc.client; +package io.a2a.client.transport.grpc; import io.a2a.grpc.*; import io.a2a.grpc.SendMessageRequest; @@ -6,9 +6,10 @@ import io.a2a.grpc.utils.ProtoUtils; import io.a2a.spec.*; import io.a2a.spec.AgentCard; +import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.transport.spi.client.Transport; +import io.a2a.client.transport.spi.Transport; import io.grpc.Channel; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; @@ -98,7 +99,6 @@ public TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId } } - @Override public TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, TaskPushNotificationConfig taskPushNotificationConfig) throws A2AServerException { String configId = taskPushNotificationConfig.pushNotificationConfig().id(); CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() diff --git a/client-transport/jsonrpc/pom.xml b/client-transport/jsonrpc/pom.xml new file mode 100644 index 000000000..7afc0c769 --- /dev/null +++ b/client-transport/jsonrpc/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-client-transport-jsonrpc + jar + + Java SDK A2A Client Transport: JSONRPC + Java SDK for the Agent2Agent Protocol (A2A) - JSONRPC Client Transport + + + + ${project.groupId} + a2a-java-sdk-client-http + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-spi + ${project.version} + + + ${project.groupId} + a2a-java-sdk-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec + ${project.version} + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + + \ No newline at end of file diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java similarity index 98% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java rename to client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index dce6ab668..20986993a 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/JSONRPCTransport.java +++ b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -1,7 +1,13 @@ -package io.a2a.transport.jsonrpc.client; +package io.a2a.client.transport.jsonrpc; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.jsonrpc.sse.SSEEventListener; +import io.a2a.client.transport.spi.Transport; import io.a2a.spec.A2AServerException; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; @@ -33,8 +39,6 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskResubscriptionRequest; -import io.a2a.transport.jsonrpc.client.sse.SSEEventListener; -import io.a2a.transport.spi.client.Transport; import io.a2a.util.Utils; import java.io.IOException; diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListener.java b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java similarity index 98% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListener.java rename to client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index 7f4ff19fc..0312e9873 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListener.java +++ b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.client.sse; +package io.a2a.client.transport.jsonrpc.sse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java similarity index 99% rename from transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java rename to client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java index 7bf46054d..4b79a57cb 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/JsonStreamingMessages.java +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.client; +package io.a2a.client.transport.jsonrpc.sse; /** * Contains JSON strings for testing SSE streaming. diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListenerTest.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java similarity index 98% rename from transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListenerTest.java rename to client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 5e4fc357c..021b810dd 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/client/sse/SSEEventListenerTest.java +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.client.sse; +package io.a2a.client.transport.jsonrpc.sse; import io.a2a.spec.Artifact; import io.a2a.spec.JSONRPCError; @@ -11,7 +11,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import io.a2a.transport.jsonrpc.client.JsonStreamingMessages; import org.junit.jupiter.api.Test; import java.util.concurrent.ExecutionException; diff --git a/transport/spi/pom.xml b/client-transport/spi/pom.xml similarity index 78% rename from transport/spi/pom.xml rename to client-transport/spi/pom.xml index 1e284687f..76a3002f8 100644 --- a/transport/spi/pom.xml +++ b/client-transport/spi/pom.xml @@ -10,12 +10,11 @@ 0.2.6.Beta1-SNAPSHOT ../../pom.xml - a2a-java-sdk-transport-spi - + a2a-java-sdk-client-transport-spi jar - Java SDK A2A Transport: SPI - Java SDK for the Agent2Agent Protocol (A2A) - SPI + Java SDK A2A Client Transport: SPI + Java SDK for the Agent2Agent Protocol (A2A) - Client Transport SPI diff --git a/transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/Transport.java similarity index 99% rename from transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java rename to client-transport/spi/src/main/java/io/a2a/client/transport/spi/Transport.java index 9ab643b56..ce473a321 100644 --- a/transport/spi/src/main/java/io/a2a/transport/spi/client/Transport.java +++ b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/Transport.java @@ -1,4 +1,4 @@ -package io.a2a.transport.spi.client; +package io.a2a.client.transport.spi; import io.a2a.spec.*; diff --git a/client/pom.xml b/client/pom.xml index 9f8b37779..b3a09f64f 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -17,6 +17,11 @@ Java SDK for the Agent2Agent Protocol (A2A) - Client + + ${project.groupId} + a2a-java-sdk-client-http + ${project.version} + ${project.groupId} a2a-java-sdk-common @@ -29,7 +34,7 @@ ${project.groupId} - a2a-java-sdk-transport-jsonrpc + a2a-java-sdk-client-transport-jsonrpc ${project.version} diff --git a/client/src/main/java/io/a2a/A2A.java b/client/src/main/java/io/a2a/A2A.java index 5fb536df8..46b9c11ad 100644 --- a/client/src/main/java/io/a2a/A2A.java +++ b/client/src/main/java/io/a2a/A2A.java @@ -4,8 +4,8 @@ import java.util.Map; import io.a2a.client.A2ACardResolver; -import io.a2a.transport.jsonrpc.client.A2AHttpClient; -import io.a2a.transport.jsonrpc.client.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/client/src/main/java/io/a2a/client/A2ACardResolver.java b/client/src/main/java/io/a2a/client/A2ACardResolver.java index c5bb0b7c0..bbbc6caf0 100644 --- a/client/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/client/src/main/java/io/a2a/client/A2ACardResolver.java @@ -9,8 +9,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.transport.jsonrpc.client.A2AHttpClient; -import io.a2a.transport.jsonrpc.client.A2AHttpResponse; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java index d85e341f4..aef8cca9c 100644 --- a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java +++ b/client/src/test/java/io/a2a/client/A2ACardResolverTest.java @@ -11,8 +11,8 @@ import java.util.function.Consumer; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.transport.jsonrpc.client.A2AHttpClient; -import io.a2a.transport.jsonrpc.client.A2AHttpResponse; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 2b0b15652..e23537cee 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -34,6 +34,10 @@ jakarta.ws.rs jakarta.ws.rs-api + + io.github.a2asdk + a2a-java-sdk-client + diff --git a/pom.xml b/pom.xml index d454e284a..0e1e4542d 100644 --- a/pom.xml +++ b/pom.xml @@ -280,6 +280,10 @@ client + client-http + client-transport/grpc + client-transport/jsonrpc + client-transport/spi common examples/helloworld reference/common @@ -290,7 +294,6 @@ spec-grpc tck tests/server-common - transport/spi transport/jsonrpc transport/grpc diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 816ed6f79..5b30d1b15 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -3,9 +3,9 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import io.a2a.transport.grpc.server.handler.GrpcHandler; +import io.a2a.transport.grpc.handler.CallContextFactory; +import io.a2a.transport.grpc.handler.GrpcHandler; import io.a2a.server.PublicAgentCard; -import io.a2a.server.requesthandlers.CallContextFactory; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; import io.quarkus.grpc.GrpcService; diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java index 9fb0074ac..bf9acfb1e 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -19,11 +19,11 @@ import jakarta.ws.rs.core.Response; import io.a2a.server.apps.common.TestUtilsBean; -import io.a2a.transport.grpc.server.handler.GrpcHandler; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.transport.grpc.handler.GrpcHandler; import io.a2a.util.Utils; @Path("/test") diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 4e8cbcbb9..900ce15ca 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; -import io.a2a.transport.jsonrpc.server.handler.JSONRPCHandler; +import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; diff --git a/server-common/pom.xml b/server-common/pom.xml index 5677d4d02..29e7f6b76 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -24,7 +24,7 @@ ${project.groupId} - a2a-java-sdk-client + a2a-java-sdk-client-http ${project.version} diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index ac04216f3..aec3f3cc3 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -10,8 +10,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.transport.jsonrpc.client.A2AHttpClient; -import io.a2a.transport.jsonrpc.client.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.util.Utils; diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 7c62562f8..1e3a0af5f 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -12,8 +12,8 @@ import java.util.concurrent.Executors; import java.util.function.Consumer; -import io.a2a.transport.jsonrpc.client.A2AHttpClient; -import io.a2a.transport.jsonrpc.client.A2AHttpResponse; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java index 046e0f8bb..87d2e536b 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java @@ -11,8 +11,8 @@ import jakarta.enterprise.context.Dependent; import jakarta.enterprise.inject.Alternative; -import io.a2a.transport.jsonrpc.client.A2AHttpClient; -import io.a2a.transport.jsonrpc.client.A2AHttpResponse; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; import io.a2a.spec.Task; import io.a2a.util.Utils; diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/CallContextFactory.java similarity index 81% rename from server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java rename to transport/grpc/src/main/java/io/a2a/transport/grpc/handler/CallContextFactory.java index ef173ba0d..f214a51e5 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/CallContextFactory.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/CallContextFactory.java @@ -1,8 +1,8 @@ -package io.a2a.server.requesthandlers; +package io.a2a.transport.grpc.handler; import io.a2a.server.ServerCallContext; import io.grpc.stub.StreamObserver; public interface CallContextFactory { ServerCallContext create(StreamObserver responseObserver); -} +} \ No newline at end of file diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/server/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java similarity index 99% rename from transport/grpc/src/main/java/io/a2a/transport/grpc/server/handler/GrpcHandler.java rename to transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 4ecdbc9ab..b32ccc23f 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/server/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -1,4 +1,4 @@ -package io.a2a.transport.grpc.server.handler; +package io.a2a.transport.grpc.handler; import static io.a2a.grpc.utils.ProtoUtils.FromProto; import static io.a2a.grpc.utils.ProtoUtils.ToProto; @@ -17,7 +17,6 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; -import io.a2a.server.requesthandlers.CallContextFactory; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; import io.a2a.spec.ContentTypeNotSupportedError; diff --git a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java similarity index 99% rename from transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java rename to transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 76cdb6f6e..c49f91d84 100644 --- a/transport/grpc/src/test/java/io/a2a/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -1,4 +1,4 @@ -package io.a2a.grpc.handler; +package io.a2a.transport.grpc.handler; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -32,7 +32,6 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.events.EventConsumer; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; -import io.a2a.server.requesthandlers.CallContextFactory; import io.a2a.server.requesthandlers.DefaultRequestHandler; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.server.tasks.TaskUpdater; @@ -45,7 +44,6 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.transport.grpc.server.handler.GrpcHandler; import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.internal.testing.StreamRecorder; diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java similarity index 99% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandler.java rename to transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 463c2aab3..e75f6f2d3 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.server.handler; +package io.a2a.transport.jsonrpc.handler; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import jakarta.enterprise.context.ApplicationScoped; diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java similarity index 99% rename from transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandlerTest.java rename to transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 931f4fdc0..728a26d9d 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/server/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1,4 +1,4 @@ -package io.a2a.transport.jsonrpc.server.handler; +package io.a2a.transport.jsonrpc.handler; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -65,7 +65,7 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.transport.jsonrpc.server.handler.JSONRPCHandler; +import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import mutiny.zero.ZeroPublisher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; From 73f25b1a39513710a00168d27aef328183195b2f Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 14 Aug 2025 13:40:57 -0400 Subject: [PATCH 067/493] fix: Temporarily comment out grpc transport code to get the build to succeed --- .../io/a2a/client/transport/grpc/GrpcTransport.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index f008a8b10..d272262e3 100644 --- a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -1,3 +1,4 @@ +/* package io.a2a.client.transport.grpc; import io.a2a.grpc.*; @@ -19,22 +20,26 @@ import static io.a2a.util.Assert.checkNotNullParam; +*/ /** * @author David BRASSELY (david.brassely at graviteesource.com) * @author GraviteeSource Team - */ + *//* + public class GrpcTransport implements Transport { private A2AServiceGrpc.A2AServiceBlockingV2Stub blockingStub; private A2AServiceGrpc.A2AServiceStub asyncStub; private AgentCard agentCard; - /** + */ +/** * Create an A2A client for interacting with an A2A agent via gRPC. * * @param channel the gRPC channel * @param agentCard the agent card for the A2A server this client will be communicating with - */ + *//* + public GrpcTransport(Channel channel, AgentCard agentCard) { checkNotNullParam("channel", channel); checkNotNullParam("agentCard", agentCard); @@ -162,3 +167,4 @@ private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigPa return name.toString(); } } +*/ From 95c111477d685bc7698476533f61bdc99c15991a Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 14 Aug 2025 13:54:52 -0400 Subject: [PATCH 068/493] fix: Rename Transport to ClientTransport --- .../io/a2a/client/transport/jsonrpc/JSONRPCTransport.java | 4 ++-- .../transport/spi/{Transport.java => ClientTransport.java} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename client-transport/spi/src/main/java/io/a2a/client/transport/spi/{Transport.java => ClientTransport.java} (99%) diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 20986993a..e9c270b02 100644 --- a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -7,7 +7,7 @@ import io.a2a.client.http.A2AHttpResponse; import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.client.transport.jsonrpc.sse.SSEEventListener; -import io.a2a.client.transport.spi.Transport; +import io.a2a.client.transport.spi.ClientTransport; import io.a2a.spec.A2AServerException; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; @@ -47,7 +47,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -public class JSONRPCTransport implements Transport { +public class JSONRPCTransport implements ClientTransport { private static final TypeReference SEND_MESSAGE_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference GET_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; diff --git a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/Transport.java b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java similarity index 99% rename from client-transport/spi/src/main/java/io/a2a/client/transport/spi/Transport.java rename to client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index ce473a321..39512fced 100644 --- a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/Transport.java +++ b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.function.Consumer; -public interface Transport { +public interface ClientTransport { /** * Send a message to the remote agent. From d4d1f95613d85f10dd823b423aee75ab603ca28f Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 8 Aug 2025 09:51:51 -0400 Subject: [PATCH 069/493] feat: Update the ClientTransport interface, introducing ClientCallContext, ClientConfig, and ClientCallInterceptor similar to the Python SDK. Introduce a ClientTransportProvider and update the JSONRPC and gRPC transport implementations. Introduce a new Client and ClientFactory implementations. --- client-config/pom.xml | 47 + .../a2a/client/config/ClientCallContext.java | 27 + .../client/config/ClientCallInterceptor.java | 26 + .../io/a2a/client/config/ClientConfig.java | 150 +++ .../a2a/client/config/PayloadAndHeaders.java | 22 + client-transport/grpc/pom.xml | 5 + .../transport/grpc/EventStreamObserver.java | 4 +- .../client/transport/grpc/GrpcTransport.java | 244 +++-- .../transport/grpc/GrpcTransportProvider.java | 29 + ...ient.transport.spi.ClientTransportProvider | 1 + client-transport/jsonrpc/pom.xml | 10 + .../transport/jsonrpc/JSONRPCTransport.java | 386 +++++--- .../jsonrpc/JSONRPCTransportProvider.java | 24 + .../jsonrpc/sse/SSEEventListener.java | 15 +- ...ient.transport.spi.ClientTransportProvider | 1 + .../JSONRPCTransportStreamingTest.java | 32 +- .../jsonrpc/JSONRPCTransportTest.java | 245 ++--- .../transport/jsonrpc/JsonMessages.java | 666 ++++++++++++++ .../jsonrpc}/JsonStreamingMessages.java | 4 +- .../jsonrpc/sse/JsonStreamingMessages.java | 148 --- .../jsonrpc/sse/SSEEventListenerTest.java | 65 +- client-transport/spi/pom.xml | 6 +- .../client/transport/spi/ClientTransport.java | 161 ++-- .../spi/ClientTransportProvider.java | 32 + client/pom.xml | 16 +- .../java/io/a2a/client/AbstractClient.java | 185 ++++ .../src/main/java/io/a2a/client/Client.java | 186 ++++ .../main/java/io/a2a/client/ClientEvent.java | 4 + .../java/io/a2a/client/ClientFactory.java | 127 +++ .../java/io/a2a/client/ClientTaskManager.java | 141 +++ .../main/java/io/a2a/client/MessageEvent.java | 26 + .../main/java/io/a2a/client/TaskEvent.java | 27 + .../java/io/a2a/client/TaskUpdateEvent.java | 37 + .../test/java/io/a2a/client/JsonMessages.java | 491 +--------- examples/helloworld/client/pom.xml | 2 +- .../examples/helloworld/HelloWorldClient.java | 9 +- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- pom.xml | 1 + server-common/pom.xml | 5 + .../java/io/a2a/server/tasks/TaskManager.java | 56 +- .../java/io/a2a/spec/A2AClientException.java | 23 + .../a2a/spec/A2AClientInvalidArgsError.java | 15 + .../a2a/spec/A2AClientInvalidStateError.java | 15 + .../io/a2a/spec/TaskArtifactUpdateEvent.java | 2 +- .../io/a2a/spec/TaskStatusUpdateEvent.java | 2 +- .../java/io/a2a/spec/TransportProtocol.java | 2 +- .../main/java/io/a2a/spec/UpdateEvent.java | 4 + spec/src/main/java/io/a2a/util/Utils.java | 66 ++ tests/server-common/pom.xml | 6 + .../apps/common/AbstractA2AServerTest.java | 852 +++++++++++------- 51 files changed, 3112 insertions(+), 1542 deletions(-) create mode 100644 client-config/pom.xml create mode 100644 client-config/src/main/java/io/a2a/client/config/ClientCallContext.java create mode 100644 client-config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java create mode 100644 client-config/src/main/java/io/a2a/client/config/ClientConfig.java create mode 100644 client-config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java create mode 100644 client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java create mode 100644 client-transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider create mode 100644 client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java create mode 100644 client-transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider rename client/src/test/java/io/a2a/client/A2AClientStreamingTest.java => client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java (84%) rename client/src/test/java/io/a2a/client/A2AClientTest.java => client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java (76%) create mode 100644 client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java rename {client/src/test/java/io/a2a/client => client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc}/JsonStreamingMessages.java (98%) delete mode 100644 client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java create mode 100644 client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java create mode 100644 client/src/main/java/io/a2a/client/AbstractClient.java create mode 100644 client/src/main/java/io/a2a/client/Client.java create mode 100644 client/src/main/java/io/a2a/client/ClientEvent.java create mode 100644 client/src/main/java/io/a2a/client/ClientFactory.java create mode 100644 client/src/main/java/io/a2a/client/ClientTaskManager.java create mode 100644 client/src/main/java/io/a2a/client/MessageEvent.java create mode 100644 client/src/main/java/io/a2a/client/TaskEvent.java create mode 100644 client/src/main/java/io/a2a/client/TaskUpdateEvent.java create mode 100644 spec/src/main/java/io/a2a/spec/A2AClientException.java create mode 100644 spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java create mode 100644 spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java create mode 100644 spec/src/main/java/io/a2a/spec/UpdateEvent.java diff --git a/client-config/pom.xml b/client-config/pom.xml new file mode 100644 index 000000000..3d85221fb --- /dev/null +++ b/client-config/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.2.6.Beta1-SNAPSHOT + + a2a-java-sdk-client-config + + jar + + Java SDK A2A Client Configuration + Java SDK for the Agent2Agent Protocol (A2A) - Client Configuration + + + + ${project.groupId} + a2a-java-sdk-client-http + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec + ${project.version} + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + io.grpc + grpc-api + + + + \ No newline at end of file diff --git a/client-config/src/main/java/io/a2a/client/config/ClientCallContext.java b/client-config/src/main/java/io/a2a/client/config/ClientCallContext.java new file mode 100644 index 000000000..0cfff4d65 --- /dev/null +++ b/client-config/src/main/java/io/a2a/client/config/ClientCallContext.java @@ -0,0 +1,27 @@ +package io.a2a.client.config; + +import java.util.Map; + +/** + * A context passed with each client call, allowing for call-specific. + * configuration and data passing. Such as authentication details or + * request deadlines. + */ +public class ClientCallContext { + + private final Map state; + private final Map headers; + + public ClientCallContext(Map state, Map headers) { + this.state = state; + this.headers = headers; + } + + public Map getState() { + return state; + } + + public Map getHeaders() { + return headers; + } +} diff --git a/client-config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java b/client-config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java new file mode 100644 index 000000000..631cd8353 --- /dev/null +++ b/client-config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java @@ -0,0 +1,26 @@ +package io.a2a.client.config; + +import java.util.Map; + +import io.a2a.spec.AgentCard; + +/** + * An abstract base class for client-side call interceptors. + * Interceptors can inspect and modify requests before they are sent, + * which is ideal for concerns like authentication, logging, or tracing. + */ +public abstract class ClientCallInterceptor { + + /** + * Intercept a client call before the request is sent. + * + * @param methodName the name of the protocol method (e.g., 'message/send') + * @param payload the request payload + * @param headers the headers to use + * @param agentCard the agent card (may be {@code null}) + * @param clientCallContext the {@code ClientCallContext} for this call (may be {@code null}) + * @return the potentially modified payload and headers + */ + public abstract PayloadAndHeaders intercept(String methodName, Object payload, Map headers, + AgentCard agentCard, ClientCallContext clientCallContext); +} diff --git a/client-config/src/main/java/io/a2a/client/config/ClientConfig.java b/client-config/src/main/java/io/a2a/client/config/ClientConfig.java new file mode 100644 index 000000000..ef8cc948d --- /dev/null +++ b/client-config/src/main/java/io/a2a/client/config/ClientConfig.java @@ -0,0 +1,150 @@ +package io.a2a.client.config; + +import java.util.List; +import java.util.Map; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.spec.PushNotificationConfig; +import io.grpc.Channel; + +/** + * Configuration for the A2A client factory. + */ +public class ClientConfig { + + private final Boolean streaming; + private final Boolean polling; + private final A2AHttpClient httpClient; + private final Channel channel; + private final List supportedTransports; + private final Boolean useClientPreference; + private final List acceptedOutputModes; + private final PushNotificationConfig pushNotificationConfig; + private final Integer historyLength; + private final Map metadata; + + public ClientConfig(Boolean streaming, Boolean polling, A2AHttpClient httpClient, Channel channel, + List supportedTransports, Boolean useClientPreference, + List acceptedOutputModes, PushNotificationConfig pushNotificationConfig, + Integer historyLength, Map metadata) { + this.streaming = streaming == null ? true : streaming; + this.polling = polling == null ? false : polling; + this.httpClient = httpClient; + this.channel = channel; + this.supportedTransports = supportedTransports; + this.useClientPreference = useClientPreference == null ? false : useClientPreference; + this.acceptedOutputModes = acceptedOutputModes; + this.pushNotificationConfig = pushNotificationConfig; + this.historyLength = historyLength; + this.metadata = metadata; + } + + public boolean isStreaming() { + return streaming; + } + + public boolean isPolling() { + return polling; + } + + public A2AHttpClient getHttpClient() { + return httpClient; + } + + public Channel getChannel() { + return channel; + } + + public List getSupportedTransports() { + return supportedTransports; + } + + public boolean isUseClientPreference() { + return useClientPreference; + } + + public List getAcceptedOutputModes() { + return acceptedOutputModes; + } + + public PushNotificationConfig getPushNotificationConfig() { + return pushNotificationConfig; + } + + public Integer getHistoryLength() { + return historyLength; + } + + public Map getMetadata() { + return metadata; + } + + public static class Builder { + private Boolean streaming; + private Boolean polling; + private A2AHttpClient httpClient; + private Channel channel; + private List supportedTransports; + private Boolean useClientPreference; + private List acceptedOutputModes; + private PushNotificationConfig pushNotificationConfig; + private Integer historyLength; + private Map metadata; + + public Builder setStreaming(Boolean streaming) { + this.streaming = streaming; + return this; + } + + public Builder setPolling(Boolean polling) { + this.polling = polling; + return this; + } + + public Builder setHttpClient(A2AHttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + + public Builder setChannel(Channel channel) { + this.channel = channel; + return this; + } + + public Builder setSupportedTransports(List supportedTransports) { + this.supportedTransports = supportedTransports; + return this; + } + + public Builder setUseClientPreference(Boolean useClientPreference) { + this.useClientPreference = useClientPreference; + return this; + } + + public Builder setAcceptedOutputModes(List acceptedOutputModes) { + this.acceptedOutputModes = acceptedOutputModes; + return this; + } + + public Builder setPushNotificationConfig(PushNotificationConfig pushNotificationConfig) { + this.pushNotificationConfig = pushNotificationConfig; + return this; + } + + public Builder setHistoryLength(Integer historyLength) { + this.historyLength = historyLength; + return this; + } + + public Builder setMetadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public ClientConfig build() { + return new ClientConfig(streaming, polling, httpClient, channel, + supportedTransports, useClientPreference, acceptedOutputModes, + pushNotificationConfig, historyLength, metadata); + } + } +} \ No newline at end of file diff --git a/client-config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java b/client-config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java new file mode 100644 index 000000000..2146a5547 --- /dev/null +++ b/client-config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java @@ -0,0 +1,22 @@ +package io.a2a.client.config; + +import java.util.Map; + +public class PayloadAndHeaders { + + private final Object payload; + private final Map headers; + + public PayloadAndHeaders(Object payload, Map headers) { + this.payload = payload; + this.headers = headers; + } + + public Object getPayload() { + return payload; + } + + public Map getHeaders() { + return headers; + } +} diff --git a/client-transport/grpc/pom.xml b/client-transport/grpc/pom.xml index 87e873a4d..aaf5f734b 100644 --- a/client-transport/grpc/pom.xml +++ b/client-transport/grpc/pom.xml @@ -17,6 +17,11 @@ Java SDK for the Agent2Agent Protocol (A2A) - gRPC Client Transport + + ${project.groupId} + a2a-java-sdk-client-config + ${project.version} + ${project.groupId} a2a-java-sdk-common diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java index 48bfa6e76..4edc4a3f5 100644 --- a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java +++ b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java @@ -47,7 +47,9 @@ public void onNext(StreamResponse response) { @Override public void onError(Throwable t) { - errorHandler.accept(t); + if (errorHandler != null) { + errorHandler.accept(t); + } } @Override diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index d272262e3..903eda965 100644 --- a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -1,170 +1,252 @@ -/* package io.a2a.client.transport.grpc; -import io.a2a.grpc.*; +import static io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub; +import static io.a2a.grpc.A2AServiceGrpc.A2AServiceStub; +import static io.a2a.grpc.utils.ProtoUtils.FromProto; +import static io.a2a.grpc.utils.ProtoUtils.ToProto; +import static io.a2a.util.Assert.checkNotNullParam; + +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import io.a2a.client.config.ClientCallContext; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.CancelTaskRequest; +import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; +import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskRequest; +import io.a2a.grpc.ListTaskPushNotificationConfigRequest; import io.a2a.grpc.SendMessageRequest; import io.a2a.grpc.SendMessageResponse; -import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.spec.*; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.TaskSubscriptionRequest; + +import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; -import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.client.transport.spi.Transport; +import io.a2a.spec.TaskQueryParams; import io.grpc.Channel; + import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; -import java.util.List; -import java.util.function.Consumer; - -import static io.a2a.util.Assert.checkNotNullParam; +public class GrpcTransport implements ClientTransport { -*/ -/** - * @author David BRASSELY (david.brassely at graviteesource.com) - * @author GraviteeSource Team - *//* - -public class GrpcTransport implements Transport { - - private A2AServiceGrpc.A2AServiceBlockingV2Stub blockingStub; - private A2AServiceGrpc.A2AServiceStub asyncStub; + private final A2AServiceBlockingV2Stub blockingStub; + private final A2AServiceStub asyncStub; private AgentCard agentCard; - */ -/** - * Create an A2A client for interacting with an A2A agent via gRPC. - * - * @param channel the gRPC channel - * @param agentCard the agent card for the A2A server this client will be communicating with - *//* - public GrpcTransport(Channel channel, AgentCard agentCard) { checkNotNullParam("channel", channel); - checkNotNullParam("agentCard", agentCard); this.asyncStub = A2AServiceGrpc.newStub(channel); this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); this.agentCard = agentCard; } @Override - public EventKind sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException { - SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); + public EventKind sendMessage(MessageSendParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest(request, context); + try { - SendMessageResponse response = blockingStub.sendMessage(request); + SendMessageResponse response = blockingStub.sendMessage(sendMessageRequest); if (response.hasMsg()) { - return ProtoUtils.FromProto.message(response.getMsg()); + return FromProto.message(response.getMsg()); } else if (response.hasTask()) { - return ProtoUtils.FromProto.task(response.getTask()); + return FromProto.task(response.getTask()); } else { - throw new A2AServerException("Server response did not contain a message or task"); + throw new A2AClientException("Server response did not contain a message or task"); } } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to send message: " + e, e); + throw new A2AClientException("Failed to send message: " + e.getMessage(), e); } } @Override - public Task getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException { - io.a2a.grpc.GetTaskRequest.Builder requestBuilder = io.a2a.grpc.GetTaskRequest.newBuilder(); - requestBuilder.setName("tasks/" + taskQueryParams.id()); - if (taskQueryParams.historyLength() != null) { - requestBuilder.setHistoryLength(taskQueryParams.historyLength()); + public void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + checkNotNullParam("eventConsumer", eventConsumer); + SendMessageRequest grpcRequest = createGrpcSendMessageRequest(request, context); + StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); + + try { + asyncStub.sendStreamingMessage(grpcRequest, streamObserver); + } catch (StatusRuntimeException e) { + throw new A2AClientException("Failed to send streaming message: " + e.getMessage(), e); } - io.a2a.grpc.GetTaskRequest getTaskRequest = requestBuilder.build(); + } + + @Override + public Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + GetTaskRequest.Builder requestBuilder = GetTaskRequest.newBuilder(); + requestBuilder.setName("tasks/" + request.id()); + if (request.historyLength() != null) { + requestBuilder.setHistoryLength(request.historyLength()); + } + GetTaskRequest getTaskRequest = requestBuilder.build(); + try { - return ProtoUtils.FromProto.task(blockingStub.getTask(getTaskRequest)); + return FromProto.task(blockingStub.getTask(getTaskRequest)); } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to get task: " + e, e); + throw new A2AClientException("Failed to get task: " + e.getMessage(), e); } } @Override - public Task cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException { - io.a2a.grpc.CancelTaskRequest cancelTaskRequest = io.a2a.grpc.CancelTaskRequest.newBuilder() - .setName("tasks/" + taskIdParams.id()) + public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + CancelTaskRequest cancelTaskRequest = CancelTaskRequest.newBuilder() + .setName("tasks/" + request.id()) .build(); + try { - return ProtoUtils.FromProto.task(blockingStub.cancelTask(cancelTaskRequest)); + return FromProto.task(blockingStub.cancelTask(cancelTaskRequest)); } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to cancel task: " + e, e); + throw new A2AClientException("Failed to cancel task: " + e.getMessage(), e); } } @Override - public TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { - io.a2a.grpc.GetTaskPushNotificationConfigRequest getTaskPushNotificationConfigRequest = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder() - .setName(getTaskPushNotificationConfigName(getTaskPushNotificationConfigParams)) + public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + String configId = request.pushNotificationConfig().id(); + CreateTaskPushNotificationConfigRequest grpcRequest = CreateTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + request.taskId()) + .setConfig(ToProto.taskPushNotificationConfig(request)) + .setConfigId(configId == null ? "" : configId) .build(); + try { - return ProtoUtils.FromProto.taskPushNotificationConfig(blockingStub.getTaskPushNotificationConfig(getTaskPushNotificationConfigRequest)); + return FromProto.taskPushNotificationConfig(blockingStub.createTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to get the task push notification config: " + e, e); + throw new A2AClientException("Failed to set task push notification config: " + e.getMessage(), e); } } - public TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, TaskPushNotificationConfig taskPushNotificationConfig) throws A2AServerException { - String configId = taskPushNotificationConfig.pushNotificationConfig().id(); - CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + taskPushNotificationConfig.taskId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushNotificationConfig)) - .setConfigId(configId == null ? "" : configId) + @Override + public TaskPushNotificationConfig getTaskPushNotificationConfiguration( + GetTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + GetTaskPushNotificationConfigRequest grpcRequest = GetTaskPushNotificationConfigRequest.newBuilder() + .setName(getTaskPushNotificationConfigName(request)) .build(); + try { - return ProtoUtils.FromProto.taskPushNotificationConfig(blockingStub.createTaskPushNotificationConfig(request)); + return FromProto.taskPushNotificationConfig(blockingStub.getTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to set the task push notification config: " + e, e); + throw new A2AClientException("Failed to get task push notification config: " + e.getMessage(), e); } } @Override - public List listTaskPushNotificationConfig(String requestId, ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { - return List.of(); + public List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + ListTaskPushNotificationConfigRequest grpcRequest = ListTaskPushNotificationConfigRequest.newBuilder() + .setParent("tasks/" + request.id()) + .build(); + + try { + return blockingStub.listTaskPushNotificationConfig(grpcRequest).getConfigsList().stream() + .map(FromProto::taskPushNotificationConfig) + .collect(Collectors.toList()); + } catch (StatusRuntimeException e) { + throw new A2AClientException("Failed to list task push notification configs: " + e.getMessage(), e); + } } @Override - public void deleteTaskPushNotificationConfig(String requestId, DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { + public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + DeleteTaskPushNotificationConfigRequest grpcRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() + .setName(getTaskPushNotificationConfigName(request.id(), request.pushNotificationConfigId())) + .build(); + try { + blockingStub.deleteTaskPushNotificationConfig(grpcRequest); + } catch (StatusRuntimeException e) { + throw new A2AClientException("Failed to delete task push notification configs: " + e.getMessage(), e); + } } @Override - public void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - SendMessageRequest request = createGrpcSendMessageRequestFromMessageSendParams(messageSendParams); - StreamObserver streamObserver = new EventStreamObserver(eventHandler, errorHandler); + public void resubscribe(TaskIdParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + checkNotNullParam("eventConsumer", eventConsumer); + + TaskSubscriptionRequest grpcRequest = TaskSubscriptionRequest.newBuilder() + .setName("tasks/" + request.id()) + .build(); + + StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); + try { - asyncStub.sendStreamingMessage(request, streamObserver); + asyncStub.taskSubscription(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { - throw new A2AServerException("Failed to send streaming message: " + e, e); + throw new A2AClientException("Failed to resubscribe to task: " + e.getMessage(), e); } } @Override - public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { + public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + // TODO: Determine how to handle retrieving the authenticated extended agent card + return agentCard; + } + @Override + public void close() { } - private SendMessageRequest createGrpcSendMessageRequestFromMessageSendParams(MessageSendParams messageSendParams) { + private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messageSendParams, ClientCallContext context) { SendMessageRequest.Builder builder = SendMessageRequest.newBuilder(); - builder.setRequest(ProtoUtils.ToProto.message(messageSendParams.message())); + builder.setRequest(ToProto.message(messageSendParams.message())); if (messageSendParams.configuration() != null) { - builder.setConfiguration(ProtoUtils.ToProto.messageSendConfiguration(messageSendParams.configuration())); + builder.setConfiguration(ToProto.messageSendConfiguration(messageSendParams.configuration())); } if (messageSendParams.metadata() != null) { - builder.setMetadata(ProtoUtils.ToProto.struct(messageSendParams.metadata())); + builder.setMetadata(ToProto.struct(messageSendParams.metadata())); } return builder.build(); } - private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) { + private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams params) { + return getTaskPushNotificationConfigName(params.id(), params.pushNotificationConfigId()); + } + + private String getTaskPushNotificationConfigName(String taskId, String pushNotificationConfigId) { StringBuilder name = new StringBuilder(); name.append("tasks/"); - name.append(getTaskPushNotificationConfigParams.id()); - if (getTaskPushNotificationConfigParams.pushNotificationConfigId() != null) { + name.append(taskId); + if (pushNotificationConfigId != null) { name.append("/pushNotificationConfigs/"); - name.append(getTaskPushNotificationConfigParams.pushNotificationConfigId()); + name.append(pushNotificationConfigId); } return name.toString(); } -} -*/ + +} \ No newline at end of file diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java new file mode 100644 index 000000000..cdaffed88 --- /dev/null +++ b/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -0,0 +1,29 @@ +package io.a2a.client.transport.grpc; + +import java.util.List; + +import io.a2a.client.config.ClientCallInterceptor; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.AgentCard; +import io.a2a.spec.TransportProtocol; + +/** + * Provider for gRPC transport implementation. + */ +public class GrpcTransportProvider implements ClientTransportProvider { + + @Override + public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, + String agentUrl, List interceptors) { + // not making use of the interceptors for gRPC for now + return new GrpcTransport(clientConfig.getChannel(), agentCard); + } + + @Override + public String getTransportProtocol() { + return TransportProtocol.GRPC.asString(); + } + +} diff --git a/client-transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider b/client-transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider new file mode 100644 index 000000000..86d4fa7e5 --- /dev/null +++ b/client-transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider @@ -0,0 +1 @@ +io.a2a.client.transport.grpc.GrpcTransportProvider \ No newline at end of file diff --git a/client-transport/jsonrpc/pom.xml b/client-transport/jsonrpc/pom.xml index 7afc0c769..dd9a74c22 100644 --- a/client-transport/jsonrpc/pom.xml +++ b/client-transport/jsonrpc/pom.xml @@ -17,6 +17,16 @@ Java SDK for the Agent2Agent Protocol (A2A) - JSONRPC Client Transport + + ${project.groupId} + a2a-java-sdk-client + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-config + ${project.version} + ${project.groupId} a2a-java-sdk-client-http diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index e9c270b02..034584cec 100644 --- a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -1,19 +1,30 @@ package io.a2a.client.transport.jsonrpc; +import static io.a2a.util.Assert.checkNotNullParam; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import io.a2a.client.A2ACardResolver; +import io.a2a.client.config.ClientCallContext; +import io.a2a.client.config.ClientCallInterceptor; +import io.a2a.client.config.PayloadAndHeaders; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.client.http.JdkA2AHttpClient; -import io.a2a.client.transport.jsonrpc.sse.SSEEventListener; import io.a2a.client.transport.spi.ClientTransport; -import io.a2a.spec.A2AServerException; +import io.a2a.spec.A2AClientError; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; + import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; @@ -23,11 +34,13 @@ import io.a2a.spec.JSONRPCError; import io.a2a.spec.JSONRPCMessage; import io.a2a.spec.JSONRPCResponse; + import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.MessageSendParams; -import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; import io.a2a.spec.SendStreamingMessageRequest; @@ -39,13 +52,11 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskResubscriptionRequest; -import io.a2a.util.Utils; - -import java.io.IOException; -import java.util.List; +import io.a2a.client.transport.jsonrpc.sse.SSEEventListener; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; + +import io.a2a.util.Utils; public class JSONRPCTransport implements ClientTransport { @@ -56,228 +67,320 @@ public class JSONRPCTransport implements ClientTransport { private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; + // TODO: Uncomment once support for v0.3.0 has been merged + //private static final TypeReference GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE = new TypeReference<>() {}; - private final String agentUrl; private final A2AHttpClient httpClient; + private final String agentUrl; + private final List interceptors; + private AgentCard agentCard; public JSONRPCTransport(String agentUrl) { - this(agentUrl, new JdkA2AHttpClient()); + this(null, null, agentUrl, null); } - public JSONRPCTransport(String agentUrl, A2AHttpClient httpClient) { + public JSONRPCTransport(AgentCard agentCard) { + this(null, agentCard, agentCard.url(), null); + } + + public JSONRPCTransport(A2AHttpClient httpClient, AgentCard agentCard, + String agentUrl, List interceptors) { + this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; + this.agentCard = agentCard; this.agentUrl = agentUrl; - this.httpClient = httpClient; + this.interceptors = interceptors; } @Override - public EventKind sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException { - SendMessageRequest.Builder sendMessageRequestBuilder = new SendMessageRequest.Builder() + public EventKind sendMessage(MessageSendParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + SendMessageRequest sendMessageRequest = new SendMessageRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .method(SendMessageRequest.METHOD) - .params(messageSendParams); - - if (requestId != null) { - sendMessageRequestBuilder.id(requestId); - } + .params(request) + .build(); // id will be randomly generated - SendMessageRequest sendMessageRequest = sendMessageRequestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, sendMessageRequest, + agentCard, context); try { - String httpResponseBody = sendPostRequest(sendMessageRequest); - return unmarshalResponse(httpResponseBody, SEND_MESSAGE_RESPONSE_REFERENCE).getResult(); + String httpResponseBody = sendPostRequest(payloadAndHeaders); + SendMessageResponse response = unmarshalResponse(httpResponseBody, SEND_MESSAGE_RESPONSE_REFERENCE); + return response.getResult(); + } catch (A2AClientException e) { + throw e; } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to send message: " + e, e.getCause()); + throw new A2AClientException("Failed to send message: " + e, e); } } @Override - public Task getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException { - GetTaskRequest.Builder getTaskRequestBuilder = new GetTaskRequest.Builder() + public void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + checkNotNullParam("eventConsumer", eventConsumer); + SendStreamingMessageRequest sendStreamingMessageRequest = new SendStreamingMessageRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(GetTaskRequest.METHOD) - .params(taskQueryParams); + .method(SendStreamingMessageRequest.METHOD) + .params(request) + .build(); // id will be randomly generated - if (requestId != null) { - getTaskRequestBuilder.id(requestId); - } + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD, + sendStreamingMessageRequest, agentCard, context); - GetTaskRequest getTaskRequest = getTaskRequestBuilder.build(); + AtomicReference> ref = new AtomicReference<>(); + SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { - String httpResponseBody = sendPostRequest(getTaskRequest); - return unmarshalResponse(httpResponseBody, GET_TASK_RESPONSE_REFERENCE).getResult(); - } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get task: " + e, e.getCause()); + A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders); + ref.set(builder.postAsyncSSE( + msg -> sseEventListener.onMessage(msg, ref.get()), + throwable -> sseEventListener.onError(throwable, ref.get()), + () -> { + // We don't need to do anything special on completion + })); + } catch (IOException e) { + throw new A2AClientException("Failed to send streaming message request: " + e, e); + } catch (InterruptedException e) { + throw new A2AClientException("Send streaming message request timed out: " + e, e); } } @Override - public Task cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException { - CancelTaskRequest.Builder cancelTaskRequestBuilder = new CancelTaskRequest.Builder() + public Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + GetTaskRequest getTaskRequest = new GetTaskRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(CancelTaskRequest.METHOD) - .params(taskIdParams); - - if (requestId != null) { - cancelTaskRequestBuilder.id(requestId); - } + .method(GetTaskRequest.METHOD) + .params(request) + .build(); // id will be randomly generated - CancelTaskRequest cancelTaskRequest = cancelTaskRequestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, getTaskRequest, + agentCard, context); try { - String httpResponseBody = sendPostRequest(cancelTaskRequest); - return unmarshalResponse(httpResponseBody, CANCEL_TASK_RESPONSE_REFERENCE).getResult(); + String httpResponseBody = sendPostRequest(payloadAndHeaders); + GetTaskResponse response = unmarshalResponse(httpResponseBody, GET_TASK_RESPONSE_REFERENCE); + return response.getResult(); + } catch (A2AClientException e) { + throw e; } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to cancel task: " + e, e.getCause()); + throw new A2AClientException("Failed to get task: " + e, e); } } @Override - public TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException { - GetTaskPushNotificationConfigRequest.Builder getTaskPushNotificationRequestBuilder = new GetTaskPushNotificationConfigRequest.Builder() + public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + CancelTaskRequest cancelTaskRequest = new CancelTaskRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(GetTaskPushNotificationConfigRequest.METHOD) - .params(getTaskPushNotificationConfigParams); - - if (requestId != null) { - getTaskPushNotificationRequestBuilder.id(requestId); - } + .method(CancelTaskRequest.METHOD) + .params(request) + .build(); // id will be randomly generated - GetTaskPushNotificationConfigRequest getTaskPushNotificationRequest = getTaskPushNotificationRequestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(CancelTaskRequest.METHOD, cancelTaskRequest, + agentCard, context); try { - String httpResponseBody = sendPostRequest(getTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE).getResult(); + String httpResponseBody = sendPostRequest(payloadAndHeaders); + CancelTaskResponse response = unmarshalResponse(httpResponseBody, CANCEL_TASK_RESPONSE_REFERENCE); + return response.getResult(); + } catch (A2AClientException e) { + throw e; } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to get task push notification config: " + e, e.getCause()); + throw new A2AClientException("Failed to cancel task: " + e, e); } } @Override - public TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, PushNotificationConfig pushNotificationConfig) throws A2AServerException { - SetTaskPushNotificationConfigRequest.Builder setTaskPushNotificationRequestBuilder = new SetTaskPushNotificationConfigRequest.Builder() + public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = new SetTaskPushNotificationConfigRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .method(SetTaskPushNotificationConfigRequest.METHOD) - .params(new TaskPushNotificationConfig(taskId, pushNotificationConfig)); + .params(request) + .build(); // id will be randomly generated + + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, + setTaskPushNotificationRequest, agentCard, context); - if (requestId != null) { - setTaskPushNotificationRequestBuilder.id(requestId); + try { + String httpResponseBody = sendPostRequest(payloadAndHeaders); + SetTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, + SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + return response.getResult(); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to set task push notification config: " + e, e); } + } - SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = setTaskPushNotificationRequestBuilder.build(); + @Override + public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + GetTaskPushNotificationConfigRequest getTaskPushNotificationRequest = new GetTaskPushNotificationConfigRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetTaskPushNotificationConfigRequest.METHOD) + .params(request) + .build(); // id will be randomly generated + + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskPushNotificationConfigRequest.METHOD, + getTaskPushNotificationRequest, agentCard, context); try { - String httpResponseBody = sendPostRequest(setTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE).getResult(); + String httpResponseBody = sendPostRequest(payloadAndHeaders); + GetTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, + GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + return response.getResult(); + } catch (A2AClientException e) { + throw e; } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to set task push notification config: " + e, e.getCause()); + throw new A2AClientException("Failed to get task push notification config: " + e, e); } } @Override - public List listTaskPushNotificationConfig(String requestId, ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException { - ListTaskPushNotificationConfigRequest.Builder listTaskPushNotificationRequestBuilder = new ListTaskPushNotificationConfigRequest.Builder() + public List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = new ListTaskPushNotificationConfigRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .method(ListTaskPushNotificationConfigRequest.METHOD) - .params(listTaskPushNotificationConfigParams); - - if (requestId != null) { - listTaskPushNotificationRequestBuilder.id(requestId); - } + .params(request) + .build(); // id will be randomly generated - ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = listTaskPushNotificationRequestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, + listTaskPushNotificationRequest, agentCard, context); try { - String httpResponseBody = sendPostRequest(listTaskPushNotificationRequest); - return unmarshalResponse(httpResponseBody, LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE).getResult(); + String httpResponseBody = sendPostRequest(payloadAndHeaders); + ListTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, + LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + return response.getResult(); + } catch (A2AClientException e) { + throw e; } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to list task push notification config: " + e, e.getCause()); + throw new A2AClientException("Failed to list task push notification configs: " + e, e); } } @Override - public void deleteTaskPushNotificationConfig(String requestId, DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException { - DeleteTaskPushNotificationConfigRequest.Builder deleteTaskPushNotificationRequestBuilder = new DeleteTaskPushNotificationConfigRequest.Builder() + public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = new DeleteTaskPushNotificationConfigRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .method(DeleteTaskPushNotificationConfigRequest.METHOD) - .params(deleteTaskPushNotificationConfigParams); - - if (requestId != null) { - deleteTaskPushNotificationRequestBuilder.id(requestId); - } + .params(request) + .build(); // id will be randomly generated - DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = deleteTaskPushNotificationRequestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, + deleteTaskPushNotificationRequest, agentCard, context); try { - String httpResponseBody = sendPostRequest(deleteTaskPushNotificationRequest); + String httpResponseBody = sendPostRequest(payloadAndHeaders); unmarshalResponse(httpResponseBody, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + } catch (A2AClientException e) { + throw e; } catch (IOException | InterruptedException e) { - throw new A2AServerException("Failed to delete task push notification config: " + e, e.getCause()); + throw new A2AClientException("Failed to delete task push notification configs: " + e, e); } } @Override - public void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - SendStreamingMessageRequest.Builder sendStreamingMessageRequestBuilder = new SendStreamingMessageRequest.Builder() + public void resubscribe(TaskIdParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + checkNotNullParam("eventConsumer", eventConsumer); + checkNotNullParam("errorConsumer", errorConsumer); + TaskResubscriptionRequest taskResubscriptionRequest = new TaskResubscriptionRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(SendStreamingMessageRequest.METHOD) - .params(messageSendParams); + .method(TaskResubscriptionRequest.METHOD) + .params(request) + .build(); // id will be randomly generated - if (requestId != null) { - sendStreamingMessageRequestBuilder.id(requestId); - } + PayloadAndHeaders payloadAndHeaders = applyInterceptors(TaskResubscriptionRequest.METHOD, + taskResubscriptionRequest, agentCard, context); AtomicReference> ref = new AtomicReference<>(); - SSEEventListener sseEventListener = new SSEEventListener(eventHandler, errorHandler, failureHandler); - SendStreamingMessageRequest sendStreamingMessageRequest = sendStreamingMessageRequestBuilder.build(); + SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); + try { - A2AHttpClient.PostBuilder builder = createPostBuilder(sendStreamingMessageRequest); + A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders); ref.set(builder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), () -> { // We don't need to do anything special on completion })); - } catch (IOException e) { - throw new A2AServerException("Failed to send streaming message request: " + e, e.getCause()); + throw new A2AClientException("Failed to send task resubscription request: " + e, e); } catch (InterruptedException e) { - throw new A2AServerException("Send streaming message request timed out: " + e, e.getCause()); + throw new A2AClientException("Task resubscription request timed out: " + e, e); } } @Override - public void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) throws A2AServerException { - TaskResubscriptionRequest.Builder taskResubscriptionRequestBuilder = new TaskResubscriptionRequest.Builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) - .method(TaskResubscriptionRequest.METHOD) - .params(taskIdParams); - - if (requestId != null) { - taskResubscriptionRequestBuilder.id(requestId); + public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + A2ACardResolver resolver; + try { + if (agentCard == null) { + resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context)); + agentCard = resolver.getAgentCard(); + } + if (!agentCard.supportsAuthenticatedExtendedCard()) { + return agentCard; + } + resolver = new A2ACardResolver(httpClient, agentUrl, "/agent/authenticatedExtendedCard", + getHttpHeaders(context)); + agentCard = resolver.getAgentCard(); + + // TODO: Uncomment this code once support for v0.3.0 has been merged and remove the above 3 lines + /*GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = new GetAuthenticatedExtendedCardRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(GetAuthenticatedExtendedCardRequest.METHOD) + .build(); // id will be randomly generated + + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetAuthenticatedExtendedCardRequest.METHOD, + getExtendedAgentCardRequest, agentCard, context); + + try { + String httpResponseBody = sendPostRequest(payloadAndHeaders); + GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody, + GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + return response.getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); + }*/ + return agentCard; + } catch(A2AClientError e){ + throw new A2AClientException("Failed to get agent card: " + e, e); } + } - AtomicReference> ref = new AtomicReference<>(); - SSEEventListener sseEventListener = new SSEEventListener(eventHandler, errorHandler, failureHandler); - TaskResubscriptionRequest taskResubscriptionRequest = taskResubscriptionRequestBuilder.build(); - try { - A2AHttpClient.PostBuilder builder = createPostBuilder(taskResubscriptionRequest); - ref.set(builder.postAsyncSSE( - msg -> sseEventListener.onMessage(msg, ref.get()), - throwable -> sseEventListener.onError(throwable, ref.get()), - () -> { - // We don't need to do anything special on completion - })); + @Override + public void close() { + // no-op + } - } catch (IOException e) { - throw new A2AServerException("Failed to send task resubscription request: " + e, e.getCause()); - } catch (InterruptedException e) { - throw new A2AServerException("Task resubscription request timed out: " + e, e.getCause()); + private PayloadAndHeaders applyInterceptors(String methodName, Object payload, + AgentCard agentCard, ClientCallContext clientCallContext) { + PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, getHttpHeaders(clientCallContext)); + if (interceptors != null && ! interceptors.isEmpty()) { + for (ClientCallInterceptor interceptor : interceptors) { + payloadAndHeaders = interceptor.intercept(methodName, payloadAndHeaders.getPayload(), + payloadAndHeaders.getHeaders(), agentCard, clientCallContext); + } } + return payloadAndHeaders; } - private String sendPostRequest(Object value) throws IOException, InterruptedException { - A2AHttpClient.PostBuilder builder = createPostBuilder(value); + private String sendPostRequest(PayloadAndHeaders payloadAndHeaders) throws IOException, InterruptedException { + A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders); A2AHttpResponse response = builder.post(); if (!response.success()) { throw new IOException("Request failed " + response.status()); @@ -285,21 +388,32 @@ private String sendPostRequest(Object value) throws IOException, InterruptedExce return response.body(); } - private A2AHttpClient.PostBuilder createPostBuilder(Object value) throws JsonProcessingException { - return httpClient.createPost() + private A2AHttpClient.PostBuilder createPostBuilder(PayloadAndHeaders payloadAndHeaders) throws JsonProcessingException { + A2AHttpClient.PostBuilder postBuilder = httpClient.createPost() .url(agentUrl) .addHeader("Content-Type", "application/json") - .body(Utils.OBJECT_MAPPER.writeValueAsString(value)); + .body(Utils.OBJECT_MAPPER.writeValueAsString(payloadAndHeaders.getPayload())); + + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + postBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + return postBuilder; } - private T unmarshalResponse(String response, TypeReference typeReference) - throws A2AServerException, JsonProcessingException { + private > T unmarshalResponse(String response, TypeReference typeReference) + throws A2AClientException, JsonProcessingException { T value = Utils.unmarshalFrom(response, typeReference); JSONRPCError error = value.getError(); if (error != null) { - throw new A2AServerException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); + throw new A2AClientException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); } return value; } -} + + private Map getHttpHeaders(ClientCallContext context) { + return context != null ? context.getHeaders() : null; + } +} \ No newline at end of file diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java new file mode 100644 index 000000000..3fe865515 --- /dev/null +++ b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -0,0 +1,24 @@ +package io.a2a.client.transport.jsonrpc; + +import java.util.List; + +import io.a2a.client.config.ClientCallInterceptor; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.AgentCard; +import io.a2a.spec.TransportProtocol; + +public class JSONRPCTransportProvider implements ClientTransportProvider { + + @Override + public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, + String agentUrl, List interceptors) { + return new JSONRPCTransport(clientConfig.getHttpClient(), agentCard, agentUrl, interceptors); + } + + @Override + public String getTransportProtocol() { + return TransportProtocol.JSONRPC.asString(); + } +} diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index 0312e9873..99ca546c4 100644 --- a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -15,13 +15,12 @@ public class SSEEventListener { private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); private final Consumer eventHandler; - private final Consumer errorHandler; - private final Runnable failureHandler; + private final Consumer errorHandler; - public SSEEventListener(Consumer eventHandler, Consumer errorHandler, Runnable failureHandler) { + public SSEEventListener(Consumer eventHandler, + Consumer errorHandler) { this.eventHandler = eventHandler; this.errorHandler = errorHandler; - this.failureHandler = failureHandler; } public void onMessage(String message, Future completableFuture) { @@ -33,7 +32,9 @@ public void onMessage(String message, Future completableFuture) { } public void onError(Throwable throwable, Future future) { - failureHandler.run(); + if (errorHandler != null) { + errorHandler.accept(throwable); + } future.cancel(true); // close SSE channel } @@ -41,7 +42,9 @@ private void handleMessage(JsonNode jsonNode, Future future) { try { if (jsonNode.has("error")) { JSONRPCError error = OBJECT_MAPPER.treeToValue(jsonNode.get("error"), JSONRPCError.class); - errorHandler.accept(error); + if (errorHandler != null) { + errorHandler.accept(error); + } } else if (jsonNode.has("result")) { // result can be a Task, Message, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent JsonNode result = jsonNode.path("result"); diff --git a/client-transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider b/client-transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider new file mode 100644 index 000000000..b2904cb45 --- /dev/null +++ b/client-transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider @@ -0,0 +1 @@ +io.a2a.client.transport.jsonrpc.JSONRPCTransportProvider \ No newline at end of file diff --git a/client/src/test/java/io/a2a/client/A2AClientStreamingTest.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java similarity index 84% rename from client/src/test/java/io/a2a/client/A2AClientStreamingTest.java rename to client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index 78b0c0945..0ba3063e0 100644 --- a/client/src/test/java/io/a2a/client/A2AClientStreamingTest.java +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -1,9 +1,9 @@ -package io.a2a.client; +package io.a2a.client.transport.jsonrpc; -import static io.a2a.client.JsonStreamingMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST; -import static io.a2a.client.JsonStreamingMessages.SEND_MESSAGE_STREAMING_TEST_RESPONSE; -import static io.a2a.client.JsonStreamingMessages.TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE; -import static io.a2a.client.JsonStreamingMessages.TASK_RESUBSCRIPTION_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.SEND_MESSAGE_STREAMING_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.TASK_RESUBSCRIPTION_TEST_REQUEST; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -18,8 +18,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.spec.Artifact; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -37,7 +37,7 @@ import org.mockserver.matchers.MatchType; import org.mockserver.model.JsonBody; -public class A2AClientStreamingTest { +public class JSONRPCTransportStreamingTest { private ClientAndServer server; @@ -85,7 +85,7 @@ public void testA2AClientSendStreamingMessage() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_STREAMING_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_STREAMING_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -95,7 +95,7 @@ public void testA2AClientSendStreamingMessage() throws Exception { .withBody(SEND_MESSAGE_STREAMING_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Message message = new Message.Builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me some jokes"))) @@ -117,9 +117,8 @@ public void testA2AClientSendStreamingMessage() throws Exception { receivedEvent.set(event); latch.countDown(); }; - Consumer errorHandler = error -> {}; - Runnable failureHandler = () -> {}; - client.sendStreamingMessage("request-1234", params, eventHandler, errorHandler, failureHandler); + Consumer errorHandler = error -> {}; + client.sendMessageStreaming(params, eventHandler, errorHandler, null); boolean eventReceived = latch.await(10, TimeUnit.SECONDS); assertTrue(eventReceived); @@ -132,7 +131,7 @@ public void testA2AClientResubscribeToTask() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(TASK_RESUBSCRIPTION_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(TASK_RESUBSCRIPTION_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -142,7 +141,7 @@ public void testA2AClientResubscribeToTask() throws Exception { .withBody(TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); TaskIdParams taskIdParams = new TaskIdParams("task-1234"); AtomicReference receivedEvent = new AtomicReference<>(); @@ -151,9 +150,8 @@ public void testA2AClientResubscribeToTask() throws Exception { receivedEvent.set(event); latch.countDown(); }; - Consumer errorHandler = error -> {}; - Runnable failureHandler = () -> {}; - client.resubscribeToTask("request-1234", taskIdParams, eventHandler, errorHandler, failureHandler); + Consumer errorHandler = error -> {}; + client.resubscribe(taskIdParams, eventHandler, errorHandler, null); boolean eventReceived = latch.await(10, TimeUnit.SECONDS); assertTrue(eventReceived); diff --git a/client/src/test/java/io/a2a/client/A2AClientTest.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java similarity index 76% rename from client/src/test/java/io/a2a/client/A2AClientTest.java rename to client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 373aa3d9f..5e2dcaa77 100644 --- a/client/src/test/java/io/a2a/client/A2AClientTest.java +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -1,29 +1,28 @@ -package io.a2a.client; - -import static io.a2a.client.JsonMessages.AGENT_CARD; -import static io.a2a.client.JsonMessages.AUTHENTICATION_EXTENDED_AGENT_CARD; -import static io.a2a.client.JsonMessages.CANCEL_TASK_TEST_REQUEST; -import static io.a2a.client.JsonMessages.CANCEL_TASK_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST; -import static io.a2a.client.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE; -import static io.a2a.client.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; -import static io.a2a.client.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.GET_TASK_TEST_REQUEST; -import static io.a2a.client.JsonMessages.GET_TASK_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_ERROR_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_TEST_REQUEST; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_ERROR_TEST_REQUEST; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST; -import static io.a2a.client.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE; -import static io.a2a.client.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; -import static io.a2a.client.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +package io.a2a.client.transport.jsonrpc; + +import static io.a2a.client.transport.jsonrpc.JsonMessages.AGENT_CARD; +import static io.a2a.client.transport.jsonrpc.JsonMessages.AGENT_CARD_SUPPORTS_EXTENDED; +import static io.a2a.client.transport.jsonrpc.JsonMessages.AUTHENTICATION_EXTENDED_AGENT_CARD; +import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_ERROR_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_ERROR_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -38,22 +37,17 @@ import java.util.List; import java.util.Map; -import io.a2a.spec.A2AServerException; +import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; -import io.a2a.spec.AgentCardSignature; -import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; -import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DataPart; +import io.a2a.spec.EventKind; import io.a2a.spec.FileContent; import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskResponse; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -62,15 +56,12 @@ import io.a2a.spec.PushNotificationAuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SecurityScheme; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; -import io.a2a.spec.TransportProtocol; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -79,7 +70,7 @@ import org.mockserver.matchers.MatchType; import org.mockserver.model.JsonBody; -public class A2AClientTest { +public class JSONRPCTransportTest { private ClientAndServer server; @@ -99,7 +90,7 @@ public void testA2AClientSendMessage() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -108,7 +99,7 @@ public void testA2AClientSendMessage() throws Exception { .withBody(SEND_MESSAGE_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Message message = new Message.Builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) @@ -124,11 +115,7 @@ public void testA2AClientSendMessage() throws Exception { .configuration(configuration) .build(); - SendMessageResponse response = client.sendMessage("request-1234", params); - - assertEquals("2.0", response.getJsonrpc()); - assertNotNull(response.getId()); - Object result = response.getResult(); + EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); @@ -151,7 +138,7 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -160,7 +147,7 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception { .withBody(SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Message message = new Message.Builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) @@ -176,11 +163,7 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception { .configuration(configuration) .build(); - SendMessageResponse response = client.sendMessage("request-1234-with-message-response", params); - - assertEquals("2.0", response.getJsonrpc()); - assertNotNull(response.getId()); - Object result = response.getResult(); + EventKind result = client.sendMessage(params, null); assertInstanceOf(Message.class, result); Message agentMessage = (Message) result; assertEquals(Message.Role.AGENT, agentMessage.getRole()); @@ -197,7 +180,7 @@ public void testA2AClientSendMessageWithError() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_WITH_ERROR_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_WITH_ERROR_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -206,7 +189,7 @@ public void testA2AClientSendMessageWithError() throws Exception { .withBody(SEND_MESSAGE_ERROR_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Message message = new Message.Builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) @@ -223,9 +206,9 @@ public void testA2AClientSendMessageWithError() throws Exception { .build(); try { - client.sendMessage("request-1234-with-error", params); + client.sendMessage(params, null); fail(); // should not reach here - } catch (A2AServerException e) { + } catch (A2AClientException e) { assertTrue(e.getMessage().contains("Invalid parameters: Hello world")); } } @@ -236,7 +219,7 @@ public void testA2AClientGetTask() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(GET_TASK_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(GET_TASK_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -245,15 +228,9 @@ public void testA2AClientGetTask() throws Exception { .withBody(GET_TASK_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); - GetTaskResponse response = client.getTask("request-1234", - new TaskQueryParams("de38c76d-d54c-436c-8b9f-4c2703648d64", 10)); - - assertEquals("2.0", response.getJsonrpc()); - assertEquals(1, response.getId()); - Object result = response.getResult(); - assertInstanceOf(Task.class, result); - Task task = (Task) result; + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + Task task = client.getTask(new TaskQueryParams("de38c76d-d54c-436c-8b9f-4c2703648d64", + 10), null); assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.getContextId()); assertEquals(TaskState.COMPLETED, task.getStatus().state()); @@ -295,7 +272,7 @@ public void testA2AClientCancelTask() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(CANCEL_TASK_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(CANCEL_TASK_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -304,15 +281,9 @@ public void testA2AClientCancelTask() throws Exception { .withBody(CANCEL_TASK_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); - CancelTaskResponse response = client.cancelTask("request-1234", - new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64", new HashMap<>())); - - assertEquals("2.0", response.getJsonrpc()); - assertEquals(1, response.getId()); - Object result = response.getResult(); - assertInstanceOf(Task.class, result); - Task task = (Task) result; + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + Task task = client.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64", + new HashMap<>()), null); assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.getContextId()); assertEquals(TaskState.CANCELED, task.getStatus().state()); @@ -325,7 +296,7 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -334,13 +305,10 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception { .withBody(GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); - GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("1", - new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", null, new HashMap<>())); - assertEquals("2.0", response.getJsonrpc()); - assertEquals(1, response.getId()); - assertInstanceOf(TaskPushNotificationConfig.class, response.getResult()); - TaskPushNotificationConfig taskPushNotificationConfig = (TaskPushNotificationConfig) response.getResult(); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + TaskPushNotificationConfig taskPushNotificationConfig = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", null, + new HashMap<>()), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); @@ -355,7 +323,7 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -364,22 +332,19 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { .withBody(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); - SetTaskPushNotificationConfigResponse response = client.setTaskPushNotificationConfig("1", - "de38c76d-d54c-436c-8b9f-4c2703648d64", - new PushNotificationConfig.Builder() - .url("https://example.com/callback") - .authenticationInfo(new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), null)) - .build()); - assertEquals("2.0", response.getJsonrpc()); - assertEquals(1, response.getId()); - assertInstanceOf(TaskPushNotificationConfig.class, response.getResult()); - TaskPushNotificationConfig taskPushNotificationConfig = (TaskPushNotificationConfig) response.getResult(); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration( + new TaskPushNotificationConfig("de38c76d-d54c-436c-8b9f-4c2703648d64", + new PushNotificationConfig.Builder() + .url("https://example.com/callback") + .authenticationInfo(new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), + null)) + .build()), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); - assertTrue(authenticationInfo.schemes().size() == 1); + assertEquals(1, authenticationInfo.schemes().size()); assertEquals("jwt", authenticationInfo.schemes().get(0)); } @@ -389,7 +354,7 @@ public void testA2AClientGetAgentCard() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/.well-known/agent-card.json") + .withPath("/.well-known/agent.json") ) .respond( response() @@ -397,8 +362,8 @@ public void testA2AClientGetAgentCard() throws Exception { .withBody(AGENT_CARD) ); - A2AClient client = new A2AClient("http://localhost:4001"); - AgentCard agentCard = client.getAgentCard(); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + AgentCard agentCard = client.getAgentCard(null); assertEquals("GeoSpatial Route Planner Agent", agentCard.name()); assertEquals("Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", agentCard.description()); assertEquals("https://georoute-agent.example.com/a2a/v1", agentCard.url()); @@ -448,38 +413,36 @@ public void testA2AClientGetAgentCard() throws Exception { assertEquals(inputModes, skills.get(1).inputModes()); outputModes = List.of("image/png", "image/jpeg", "application/json", "text/html"); assertEquals(outputModes, skills.get(1).outputModes()); - assertTrue(agentCard.supportsAuthenticatedExtendedCard()); + assertFalse(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.9", agentCard.protocolVersion()); - assertEquals("JSONRPC", agentCard.preferredTransport()); - List additionalInterfaces = agentCard.additionalInterfaces(); - assertEquals(3, additionalInterfaces.size()); - AgentInterface jsonrpc = new AgentInterface(TransportProtocol.JSONRPC.asString(), "https://georoute-agent.example.com/a2a/v1"); - AgentInterface grpc = new AgentInterface(TransportProtocol.GRPC.asString(), "https://georoute-agent.example.com/a2a/grpc"); - AgentInterface httpJson = new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "https://georoute-agent.example.com/a2a/json"); - assertEquals(jsonrpc, additionalInterfaces.get(0)); - assertEquals(grpc, additionalInterfaces.get(1)); - assertEquals(httpJson, additionalInterfaces.get(2)); + assertEquals("0.2.5", agentCard.protocolVersion()); } @Test public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { this.server.when( request() - .withMethod("POST") - .withPath("/") - .withBody(JsonBody.json(GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST, MatchType.STRICT)) - + .withMethod("GET") + .withPath("/.well-known/agent.json") ) .respond( response() .withStatusCode(200) - .withBody(GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE) + .withBody(AGENT_CARD_SUPPORTS_EXTENDED) + ); + this.server.when( + request() + .withMethod("GET") + .withPath("/agent/authenticatedExtendedCard") + ) + .respond( + response() + .withStatusCode(200) + .withBody(AUTHENTICATION_EXTENDED_AGENT_CARD) ); - A2AClient client = new A2AClient("http://localhost:4001"); - GetAuthenticatedExtendedCardResponse response = client.getAuthenticatedExtendedCard("1", null); - AgentCard agentCard = response.getResult(); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + AgentCard agentCard = client.getAgentCard(null); assertEquals("GeoSpatial Route Planner Agent Extended", agentCard.name()); assertEquals("Extended description", agentCard.description()); assertEquals("https://georoute-agent.example.com/a2a/v1", agentCard.url()); @@ -535,13 +498,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertEquals(List.of("extended"), skills.get(2).tags()); assertTrue(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.9", agentCard.protocolVersion()); - List signatures = agentCard.signatures(); - assertEquals(1, signatures.size()); - AgentCardSignature signature = new AgentCardSignature(null, - "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", - "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ"); - assertEquals(signature, signatures.get(0)); + assertEquals("0.2.5", agentCard.protocolVersion()); } @Test @@ -550,7 +507,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -559,7 +516,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { .withBody(SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Message message = new Message.Builder() .role(Message.Role.USER) .parts(List.of( @@ -578,11 +535,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { .configuration(configuration) .build(); - SendMessageResponse response = client.sendMessage("request-1234-with-file", params); - - assertEquals("2.0", response.getJsonrpc()); - assertNotNull(response.getId()); - Object result = response.getResult(); + EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); @@ -605,7 +558,7 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -614,14 +567,14 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { .withBody(SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); - + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + Map data = new HashMap<>(); data.put("temperature", 25.5); data.put("humidity", 60.2); data.put("location", "San Francisco"); data.put("timestamp", "2024-01-15T10:30:00Z"); - + Message message = new Message.Builder() .role(Message.Role.USER) .parts(List.of( @@ -640,11 +593,7 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { .configuration(configuration) .build(); - SendMessageResponse response = client.sendMessage("request-1234-with-data", params); - - assertEquals("2.0", response.getJsonrpc()); - assertNotNull(response.getId()); - Object result = response.getResult(); + EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); @@ -667,7 +616,7 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST, MatchType.STRICT)) + .withBody(JsonBody.json(SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -676,13 +625,13 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { .withBody(SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE) ); - A2AClient client = new A2AClient("http://localhost:4001"); - + JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); + Map data = new HashMap<>(); data.put("chartType", "bar"); data.put("dataPoints", List.of(10, 20, 30, 40)); data.put("labels", List.of("Q1", "Q2", "Q3", "Q4")); - + Message message = new Message.Builder() .role(Message.Role.USER) .parts(List.of( @@ -702,11 +651,7 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { .configuration(configuration) .build(); - SendMessageResponse response = client.sendMessage("request-1234-with-mixed", params); - - assertEquals("2.0", response.getJsonrpc()); - assertNotNull(response.getId()); - Object result = response.getResult(); + EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java new file mode 100644 index 000000000..b0a5fc111 --- /dev/null +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -0,0 +1,666 @@ +package io.a2a.client.transport.jsonrpc; + +/** + * Request and response messages used by the tests. These have been created following examples from + * the A2A sample messages. + */ +public class JsonMessages { + + static final String AGENT_CARD = """ + { + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": false, + "protocolVersion": "0.2.5" + }"""; + + static final String AGENT_CARD_SUPPORTS_EXTENDED = """ + { + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" + }"""; + + + static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """ + { + "name": "GeoSpatial Route Planner Agent Extended", + "description": "Extended description", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + }, + { + "id": "skill-extended", + "name": "Extended Skill", + "description": "This is an extended skill.", + "tags": ["extended"] + } + ], + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" + }"""; + + + static final String SEND_MESSAGE_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + }, + } + }"""; + + static final String SEND_MESSAGE_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "joke", + "parts": [ + { + "kind": "text", + "text": "Why did the chicken cross the road? To get to the other side!" + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + }, + } + }"""; + + + static final String SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "role": "agent", + "parts": [ + { + "kind": "text", + "text": "Why did the chicken cross the road? To get to the other side!" + } + ], + "messageId": "msg-456", + "kind": "message" + } + }"""; + + static final String SEND_MESSAGE_WITH_ERROR_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + }, + } + }"""; + + static final String SEND_MESSAGE_ERROR_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "error": { + "code": -32702, + "message": "Invalid parameters", + "data": "Hello world" + } + }"""; + + static final String GET_TASK_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "tasks/get", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength": 10 + } + } + """; + + static final String GET_TASK_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "parts": [ + { + "kind": "text", + "text": "Why did the chicken cross the road? To get to the other side!" + } + ] + } + ], + "history": [ + { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + }, + { + "kind": "file", + "file": { + "uri": "file:///path/to/file.txt", + "mimeType": "text/plain" + } + }, + { + "kind": "file", + "file": { + "bytes": "aGVsbG8=", + "name": "hello.txt" + } + } + ], + "messageId": "message-123", + "kind": "message" + } + ], + "metadata": {}, + "kind": "task" + } + } + """; + + static final String CANCEL_TASK_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "tasks/cancel", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "metadata": {} + } + } + """; + + static final String CANCEL_TASK_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "canceled" + }, + "metadata": {}, + "kind" : "task" + } + } + """; + + static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "tasks/pushNotificationConfig/get", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "metadata": {}, + } + } + """; + + static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + } + """; + + static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "tasks/pushNotificationConfig/set", + "params": { + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + }"""; + + static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + } + """; + + static final String SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "analyze this image" + }, + { + "kind": "file", + "file": { + "uri": "file:///path/to/image.jpg", + "mimeType": "image/jpeg" + } + } + ], + "messageId": "message-1234-with-file", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "image-analysis", + "parts": [ + { + "kind": "text", + "text": "This is an image of a cat sitting on a windowsill." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "process this data" + }, + { + "kind": "data", + "data": { + "temperature": 25.5, + "humidity": 60.2, + "location": "San Francisco", + "timestamp": "2024-01-15T10:30:00Z" + } + } + ], + "messageId": "message-1234-with-data", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "data-analysis", + "parts": [ + { + "kind": "text", + "text": "Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "analyze this data and image" + }, + { + "kind": "file", + "file": { + "bytes": "aGVsbG8=", + "name": "chart.png", + "mimeType": "image/png" + } + }, + { + "kind": "data", + "data": { + "chartType": "bar", + "dataPoints": [10, 20, 30, 40], + "labels": ["Q1", "Q2", "Q3", "Q4"] + } + } + ], + "messageId": "message-1234-with-mixed", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "mixed-analysis", + "parts": [ + { + "kind": "text", + "text": "Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + +} diff --git a/client/src/test/java/io/a2a/client/JsonStreamingMessages.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java similarity index 98% rename from client/src/test/java/io/a2a/client/JsonStreamingMessages.java rename to client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java index cf80de7b8..909955e81 100644 --- a/client/src/test/java/io/a2a/client/JsonStreamingMessages.java +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java @@ -1,4 +1,4 @@ -package io.a2a.client; +package io.a2a.client.transport.jsonrpc; /** * Contains JSON strings for testing SSE streaming. @@ -106,7 +106,6 @@ public class JsonStreamingMessages { public static final String SEND_MESSAGE_STREAMING_TEST_REQUEST = """ { "jsonrpc": "2.0", - "id": "request-1234", "method": "message/stream", "params": { "message": { @@ -139,7 +138,6 @@ public class JsonStreamingMessages { public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ { "jsonrpc": "2.0", - "id": "request-1234", "method": "tasks/resubscribe", "params": { "id": "task-1234" diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java deleted file mode 100644 index 4b79a57cb..000000000 --- a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/JsonStreamingMessages.java +++ /dev/null @@ -1,148 +0,0 @@ -package io.a2a.client.transport.jsonrpc.sse; - -/** - * Contains JSON strings for testing SSE streaming. - */ -public class JsonStreamingMessages { - - public static final String STREAMING_TASK_EVENT = """ - data: { - "jsonrpc": "2.0", - "id": "1234", - "result": { - "kind": "task", - "id": "task-123", - "contextId": "context-456", - "status": { - "state": "working" - } - } - } - """; - - - public static final String STREAMING_MESSAGE_EVENT = """ - data: { - "jsonrpc": "2.0", - "id": "1234", - "result": { - "kind": "message", - "role": "agent", - "messageId": "msg-123", - "contextId": "context-456", - "parts": [ - { - "kind": "text", - "text": "Hello, world!" - } - ] - } - }"""; - - public static final String STREAMING_STATUS_UPDATE_EVENT = """ - data: { - "jsonrpc": "2.0", - "id": "1234", - "result": { - "taskId": "1", - "contextId": "2", - "status": { - "state": "submitted" - }, - "final": false, - "kind": "status-update" - } - }"""; - - public static final String STREAMING_STATUS_UPDATE_EVENT_FINAL = """ - data: { - "jsonrpc": "2.0", - "id": "1234", - "result": { - "taskId": "1", - "contextId": "2", - "status": { - "state": "completed" - }, - "final": true, - "kind": "status-update" - } - }"""; - - public static final String STREAMING_ARTIFACT_UPDATE_EVENT = """ - data: { - "jsonrpc": "2.0", - "id": "1234", - "result": { - "kind": "artifact-update", - "taskId": "1", - "contextId": "2", - "append": false, - "lastChunk": true, - "artifact": { - "artifactId": "artifact-1", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ] - } - } - } - }"""; - - public static final String STREAMING_ERROR_EVENT = """ - data: { - "jsonrpc": "2.0", - "id": "1234", - "error": { - "code": -32602, - "message": "Invalid parameters", - "data": "Missing required field" - } - }"""; - - public static final String SEND_MESSAGE_STREAMING_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234", - "method": "message/stream", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me some jokes" - } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": false - }, - } - }"""; - - static final String SEND_MESSAGE_STREAMING_TEST_RESPONSE = - "event: message\n" + - "data: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"completed\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"kind\":\"text\",\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{},\"kind\":\"task\"}}\n\n"; - - static final String TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE = - "event: message\n" + - "data: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"completed\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"kind\":\"text\",\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{},\"kind\":\"task\"}}\n\n"; - - public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234", - "method": "tasks/resubscribe", - "params": { - "id": "task-1234" - } - }"""; -} \ No newline at end of file diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 021b810dd..8c4c1495e 100644 --- a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -1,5 +1,19 @@ package io.a2a.client.transport.jsonrpc.sse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import io.a2a.client.transport.jsonrpc.JsonStreamingMessages; import io.a2a.spec.Artifact; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; @@ -13,15 +27,6 @@ import io.a2a.spec.TextPart; import org.junit.jupiter.api.Test; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.jupiter.api.Assertions.*; - public class SSEEventListenerTest { @Test @@ -30,8 +35,8 @@ public void testOnEventWithTaskResult() throws Exception { AtomicReference receivedEvent = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> receivedEvent.set(event), - error -> {}, - () -> {}); + error -> {} + ); // Parse the task event JSON String eventData = JsonStreamingMessages.STREAMING_TASK_EVENT.substring( @@ -55,8 +60,8 @@ public void testOnEventWithMessageResult() throws Exception { AtomicReference receivedEvent = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> receivedEvent.set(event), - error -> {}, - () -> {}); + error -> {} + ); // Parse the message event JSON String eventData = JsonStreamingMessages.STREAMING_MESSAGE_EVENT.substring( @@ -83,8 +88,8 @@ public void testOnEventWithTaskStatusUpdateEventEvent() throws Exception { AtomicReference receivedEvent = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> receivedEvent.set(event), - error -> {}, - () -> {}); + error -> {} + ); // Parse the message event JSON String eventData = JsonStreamingMessages.STREAMING_STATUS_UPDATE_EVENT.substring( @@ -109,8 +114,8 @@ public void testOnEventWithTaskArtifactUpdateEventEvent() throws Exception { AtomicReference receivedEvent = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> receivedEvent.set(event), - error -> {}, - () -> {}); + error -> {} + ); // Parse the message event JSON String eventData = JsonStreamingMessages.STREAMING_ARTIFACT_UPDATE_EVENT.substring( @@ -138,11 +143,11 @@ public void testOnEventWithTaskArtifactUpdateEventEvent() throws Exception { @Test public void testOnEventWithError() throws Exception { // Set up event handler - AtomicReference receivedError = new AtomicReference<>(); + AtomicReference receivedError = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> {}, - error -> receivedError.set(error), - () -> {}); + error -> receivedError.set(error) + ); // Parse the error event JSON String eventData = JsonStreamingMessages.STREAMING_ERROR_EVENT.substring( @@ -153,9 +158,11 @@ public void testOnEventWithError() throws Exception { // Verify the error was processed correctly assertNotNull(receivedError.get()); - assertEquals(-32602, receivedError.get().getCode()); - assertEquals("Invalid parameters", receivedError.get().getMessage()); - assertEquals("Missing required field", receivedError.get().getData()); + assertInstanceOf(JSONRPCError.class, receivedError.get()); + JSONRPCError jsonrpcError = (JSONRPCError) receivedError.get(); + assertEquals(-32602, jsonrpcError.getCode()); + assertEquals("Invalid parameters", jsonrpcError.getMessage()); + assertEquals("Missing required field", jsonrpcError.getData()); } @Test @@ -163,8 +170,8 @@ public void testOnFailure() { AtomicBoolean failureHandlerCalled = new AtomicBoolean(false); SSEEventListener listener = new SSEEventListener( event -> {}, - error -> {}, - () -> failureHandlerCalled.set(true)); + error -> failureHandlerCalled.set(true) + ); // Simulate a failure CancelCapturingFuture future = new CancelCapturingFuture(); @@ -189,8 +196,8 @@ public void testFinalTaskStatusUpdateEventCancels() { AtomicReference receivedEvent = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> receivedEvent.set(event), - error -> {}, - () -> {}); + error -> {} + ); } @@ -201,8 +208,8 @@ public void testOnEventWithFinalTaskStatusUpdateEventEventCancels() throws Excep AtomicReference receivedEvent = new AtomicReference<>(); SSEEventListener listener = new SSEEventListener( event -> receivedEvent.set(event), - error -> {}, - () -> {}); + error -> {} + ); // Parse the message event JSON String eventData = JsonStreamingMessages.STREAMING_STATUS_UPDATE_EVENT_FINAL.substring( diff --git a/client-transport/spi/pom.xml b/client-transport/spi/pom.xml index 76a3002f8..7c4b112f0 100644 --- a/client-transport/spi/pom.xml +++ b/client-transport/spi/pom.xml @@ -17,12 +17,16 @@ Java SDK for the Agent2Agent Protocol (A2A) - Client Transport SPI + + io.github.a2asdk + a2a-java-sdk-client-config + ${project.version} + io.github.a2asdk a2a-java-sdk-spec ${project.version} - diff --git a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 39512fced..163dd6582 100644 --- a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -1,109 +1,140 @@ package io.a2a.client.transport.spi; -import io.a2a.spec.*; - import java.util.List; import java.util.function.Consumer; +import io.a2a.client.config.ClientCallContext; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; + +/** + * Interface for a client transport. + */ public interface ClientTransport { /** - * Send a message to the remote agent. + * Send a non-streaming message request to the agent. * - * @param requestId the request ID to use - * @param messageSendParams the parameters for the message to be sent - * @return the response, may contain a message or a task - * @throws A2AServerException if sending the message fails for any reason + * @param request the message send parameters + * @param context optional client call context for the request (may be {@code null}) + * @return the response, either a Task or Message + * @throws A2AClientException if sending the message fails for any reason */ - EventKind sendMessage(String requestId, MessageSendParams messageSendParams) throws A2AServerException; + EventKind sendMessage(MessageSendParams request, ClientCallContext context) + throws A2AClientException; /** - * Retrieve the generated artifacts for a task. + * Send a streaming message request to the agent and receive responses as they arrive. * - * @param requestId the request ID to use - * @param taskQueryParams the params for the task to be queried - * @return the response containing the task - * @throws A2AServerException if retrieving the task fails for any reason + * @param request the message send parameters + * @param eventConsumer consumer that will receive streaming events as they arrive + * @param errorConsumer consumer that will be called if an error occurs during streaming + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if setting up the streaming connection fails */ - Task getTask(String requestId, TaskQueryParams taskQueryParams) throws A2AServerException; + void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException; /** - * Cancel a task that was previously submitted to the A2A server. + * Retrieve the current state and history of a specific task. * - * @param requestId the request ID to use - * @param taskIdParams the params for the task to be cancelled - * @return the response indicating if the task was cancelled - * @throws A2AServerException if retrieving the task fails for any reason + * @param request the task query parameters specifying which task to retrieve + * @param context optional client call context for the request (may be {@code null}) + * @return the task + * @throws A2AClientException if retrieving the task fails for any reason */ - Task cancelTask(String requestId, TaskIdParams taskIdParams) throws A2AServerException; + Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException; /** - * Get the push notification configuration for a task. + * Request the agent to cancel a specific task. * - * @param requestId the request ID to use - * @param getTaskPushNotificationConfigParams the params for the task - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason + * @param request the task ID parameters specifying which task to cancel + * @param context optional client call context for the request (may be {@code null}) + * @return the cancelled task + * @throws A2AClientException if cancelling the task fails for any reason */ - TaskPushNotificationConfig getTaskPushNotificationConfig(String requestId, GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams) throws A2AServerException; + Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException; /** - * Set push notification configuration for a task. + * Set or update the push notification configuration for a specific task. * - * @param requestId the request ID to use - * @param taskId the task ID - * @param pushNotificationConfig the push notification configuration - * @return the response indicating whether setting the task push notification configuration succeeded - * @throws A2AServerException if setting the push notification configuration fails for any reason + * @param request the push notification configuration to set for the task + * @param context optional client call context for the request (may be {@code null}) + * @return the configured TaskPushNotificationConfig + * @throws A2AClientException if setting the task push notification configuration fails for any reason */ - TaskPushNotificationConfig setTaskPushNotificationConfig(String requestId, String taskId, - PushNotificationConfig pushNotificationConfig) throws A2AServerException; + TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, + ClientCallContext context) throws A2AClientException; /** - * Retrieves the push notification configurations for a specified task. + * Retrieve the push notification configuration for a specific task. * - * @param requestId the request ID to use - * @param listTaskPushNotificationConfigParams the params for retrieving the push notification configuration - * @return the response containing the push notification configuration - * @throws A2AServerException if getting the push notification configuration fails for any reason + * @param request the parameters specifying which task's notification config to retrieve + * @param context optional client call context for the request (may be {@code null}) + * @return the task push notification config + * @throws A2AClientException if getting the task push notification config fails for any reason */ - List listTaskPushNotificationConfig(String requestId, - ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams) throws A2AServerException; + TaskPushNotificationConfig getTaskPushNotificationConfiguration( + GetTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException; /** - * Delete the push notification configuration for a specified task. + * Retrieve the list of push notification configurations for a specific task. * - * @param requestId the request ID to use - * @param deleteTaskPushNotificationConfigParams the params for deleting the push notification configuration - * @throws A2AServerException if deleting the push notification configuration fails for any reason + * @param request the parameters specifying which task's notification configs to retrieve + * @param context optional client call context for the request (may be {@code null}) + * @return the list of task push notification configs + * @throws A2AClientException if getting the task push notification configs fails for any reason */ - void deleteTaskPushNotificationConfig(String requestId, - DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams) throws A2AServerException; + List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException; /** - * Send a streaming message to the remote agent. + * Delete the list of push notification configurations for a specific task. * - * @param requestId the request ID to use - * @param messageSendParams the parameters for the message to be sent - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if the remote agent returns an error - * @param failureHandler a consumer that will be invoked if a failure occurs when processing events - * @throws A2AServerException if sending the streaming message fails for any reason + * @param request the parameters specifying which task's notification configs to delete + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if deleting the task push notification configs fails for any reason */ - void sendStreamingMessage(String requestId, MessageSendParams messageSendParams, Consumer eventHandler, - Consumer errorHandler, Runnable failureHandler) throws A2AServerException; + void deleteTaskPushNotificationConfigurations( + DeleteTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException; /** - * Resubscribe to an ongoing task. + * Reconnect to get task updates for an existing task. * - * @param requestId the request ID to use - * @param taskIdParams the params for the task to resubscribe to - * @param eventHandler a consumer that will be invoked for each event received from the remote agent - * @param errorHandler a consumer that will be invoked if the remote agent returns an error - * @param failureHandler a consumer that will be invoked if a failure occurs when processing events - * @throws A2AServerException if resubscribing to the task fails for any reason + * @param request the task ID parameters specifying which task to resubscribe to + * @param eventConsumer consumer that will receive streaming events as they arrive + * @param errorConsumer consumer that will be called if an error occurs during streaming + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if resubscribing to the task fails for any reason + */ + void resubscribe(TaskIdParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException; + + /** + * Retrieve the 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 + */ + AgentCard getAgentCard(ClientCallContext context) throws A2AClientException; + + /** + * Close the transport and release any associated resources. */ - void resubscribeToTask(String requestId, TaskIdParams taskIdParams, Consumer eventHandler, - Consumer errorHandler, Runnable failureHandler) throws A2AServerException; + void close(); } diff --git a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java new file mode 100644 index 000000000..5ebed06a9 --- /dev/null +++ b/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java @@ -0,0 +1,32 @@ +package io.a2a.client.transport.spi; + +import java.util.List; + +import io.a2a.client.config.ClientCallInterceptor; +import io.a2a.client.config.ClientConfig; +import io.a2a.spec.AgentCard; + +/** + * Client transport provider interface. + */ +public interface ClientTransportProvider { + + /** + * Create a client transport. + * + * @param clientConfig the client config to use + * @param agentCard the agent card for the remote agent + * @param agentUrl the remote agent's URL + * @param interceptors the optional interceptors to use for a client call (may be {@code null}) + * @return the client transport + */ + ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, + String agentUrl, List interceptors); + + /** + * Get the name of the client transport. + */ + String getTransportProtocol(); + +} + diff --git a/client/pom.xml b/client/pom.xml index b3a09f64f..c35fbeef6 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -17,6 +17,11 @@ Java SDK for the Agent2Agent Protocol (A2A) - Client + + ${project.groupId} + a2a-java-sdk-client-config + ${project.version} + ${project.groupId} a2a-java-sdk-client-http @@ -24,20 +29,19 @@ ${project.groupId} - a2a-java-sdk-common + a2a-java-sdk-client-transport-spi ${project.version} ${project.groupId} - a2a-java-sdk-spec + a2a-java-sdk-common ${project.version} ${project.groupId} - a2a-java-sdk-client-transport-jsonrpc + a2a-java-sdk-spec ${project.version} - org.junit.jupiter junit-jupiter-api @@ -49,6 +53,10 @@ mockserver-netty test + + io.grpc + grpc-api + \ No newline at end of file diff --git a/client/src/main/java/io/a2a/client/AbstractClient.java b/client/src/main/java/io/a2a/client/AbstractClient.java new file mode 100644 index 000000000..f6794ab38 --- /dev/null +++ b/client/src/main/java/io/a2a/client/AbstractClient.java @@ -0,0 +1,185 @@ +package io.a2a.client; + +import static io.a2a.util.Assert.checkNotNullParam; + +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import io.a2a.client.config.ClientCallContext; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.Message; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; + +/** + * Abstract class representing an A2A client. Provides a standard set + * of methods for interacting with an A2A agent, regardless of the underlying + * transport protocol. It supports sending messages, managing tasks, and + * handling event streams. + */ +public abstract class AbstractClient { + + private final List> consumers; + private final Consumer streamingErrorHandler; + + public AbstractClient(List> consumers) { + this(consumers, null); + } + + public AbstractClient(List> consumers, Consumer streamingErrorHandler) { + checkNotNullParam("consumers", consumers); + this.consumers = consumers; + this.streamingErrorHandler = streamingErrorHandler; + } + + /** + * Send a message to the remote agent. This method will automatically use + * the streaming or non-streaming approach as determined by the server's + * agent card and the client configuration. The configured client consumers + * and will be used to handle messages, tasks, and update events received + * from the remote agent. The configured streaming error handler will be used + * if an error occurs during streaming. The configured client push notification + * configuration will get used for streaming. + * + * @param request the message + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if sending the message fails for any reason + */ + public abstract void sendMessage(Message request, ClientCallContext context) throws A2AClientException; + + /** + * Send a message to the remote agent. This method will automatically use + * the streaming or non-streaming approach as determined by the server's + * agent card and the client configuration. The configured client consumers + * will be used to handle messages, tasks, and update events received from + * the remote agent. The configured streaming error handler will be used + * if an error occurs during streaming. + * + * @param request the message + * @param pushNotificationConfiguration the push notification configuration that should be + * used if the streaming approach is used + * @param metadata the optional metadata to include when sending the message + * @throws A2AClientException if sending the message fails for any reason + */ + public abstract void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, + Map metadata, ClientCallContext context) throws A2AClientException; + + /** + * Retrieve the current state and history of a specific task. + * + * @param request the task query parameters specifying which task to retrieve + * @param context optional client call context for the request (may be {@code null}) + * @return the task + * @throws A2AClientException if retrieving the task fails for any reason + */ + public abstract Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException; + + /** + * Request the agent to cancel a specific task. + * + * @param request the task ID parameters specifying which task to cancel + * @param context optional client call context for the request (may be {@code null}) + * @return the cancelled task + * @throws A2AClientException if cancelling the task fails for any reason + */ + public abstract Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException; + + /** + * Set or update the push notification configuration for a specific task. + * + * @param request the push notification configuration to set for the task + * @param context optional client call context for the request (may be {@code null}) + * @return the configured TaskPushNotificationConfig + * @throws A2AClientException if setting the task push notification configuration fails for any reason + */ + public abstract TaskPushNotificationConfig setTaskPushNotificationConfiguration( + TaskPushNotificationConfig request, + ClientCallContext context) throws A2AClientException; + + /** + * Retrieve the push notification configuration for a specific task. + * + * @param request the parameters specifying which task's notification config to retrieve + * @param context optional client call context for the request (may be {@code null}) + * @return the task push notification config + * @throws A2AClientException if getting the task push notification config fails for any reason + */ + public abstract TaskPushNotificationConfig getTaskPushNotificationConfiguration( + GetTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException; + + /** + * Retrieve the list of push notification configurations for a specific task. + * + * @param request the parameters specifying which task's notification configs to retrieve + * @param context optional client call context for the request (may be {@code null}) + * @return the list of task push notification configs + * @throws A2AClientException if getting the task push notification configs fails for any reason + */ + public abstract List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException; + + /** + * Delete the list of push notification configurations for a specific task. + * + * @param request the parameters specifying which task's notification configs to delete + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if deleting the task push notification configs fails for any reason + */ + public abstract void deleteTaskPushNotificationConfigurations( + DeleteTaskPushNotificationConfigParams request, + ClientCallContext context) throws A2AClientException; + + /** + * Resubscribe to a task's event stream. + * This is only available if both the client and server support streaming. + * + * @param request the parameters specifying which task's notification configs to delete + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if resubscribing fails for any reason + */ + public abstract void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException; + + /** + * Retrieve the 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 + */ + public abstract AgentCard getAgentCard(ClientCallContext context) throws A2AClientException; + + /** + * Close the transport and release any associated resources. + */ + public abstract void close(); + + /** + * Process the event using all configured consumers. + */ + public void consume(ClientEvent clientEventOrMessage, AgentCard agentCard) { + for (BiConsumer consumer : consumers) { + consumer.accept(clientEventOrMessage, agentCard); + } + } + + /** + * Get the error handler that should be used during streaming. + * + * @return the streaming error handler + */ + public Consumer getStreamingErrorHandler() { + return streamingErrorHandler; + } + +} diff --git a/client/src/main/java/io/a2a/client/Client.java b/client/src/main/java/io/a2a/client/Client.java new file mode 100644 index 000000000..5dc57b56a --- /dev/null +++ b/client/src/main/java/io/a2a/client/Client.java @@ -0,0 +1,186 @@ +package io.a2a.client; + +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import io.a2a.client.config.ClientCallContext; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.spec.A2AClientError; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.A2AClientInvalidStateError; +import io.a2a.spec.AgentCard; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendConfiguration; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskStatusUpdateEvent; + +public class Client extends AbstractClient { + + private final ClientConfig clientConfig; + private final ClientTransport clientTransport; + private AgentCard agentCard; + + public Client(AgentCard agentCard, ClientConfig clientConfig, ClientTransport clientTransport, + List> consumers, Consumer streamingErrorHandler) { + super(consumers, streamingErrorHandler); + this.agentCard = agentCard; + this.clientConfig = clientConfig; + this.clientTransport = clientTransport; + } + + + @Override + public void sendMessage(Message request, ClientCallContext context) throws A2AClientException { + MessageSendConfiguration messageSendConfiguration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) + .blocking(clientConfig.isPolling()) + .historyLength(clientConfig.getHistoryLength()) + .pushNotification(clientConfig.getPushNotificationConfig()) + .build(); + + MessageSendParams messageSendParams = new MessageSendParams.Builder() + .message(request) + .configuration(messageSendConfiguration) + .metadata(clientConfig.getMetadata()) + .build(); + + sendMessage(messageSendParams, context); + } + + @Override + public void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, + Map metatadata, ClientCallContext context) throws A2AClientException { + MessageSendConfiguration messageSendConfiguration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) + .blocking(clientConfig.isPolling()) + .historyLength(clientConfig.getHistoryLength()) + .pushNotification(pushNotificationConfiguration) + .build(); + + MessageSendParams messageSendParams = new MessageSendParams.Builder() + .message(request) + .configuration(messageSendConfiguration) + .metadata(metatadata) + .build(); + + sendMessage(messageSendParams, context); + } + + @Override + public Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException { + return clientTransport.getTask(request, context); + } + + @Override + public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException { + return clientTransport.cancelTask(request, context); + } + + @Override + public TaskPushNotificationConfig setTaskPushNotificationConfiguration( + TaskPushNotificationConfig request, ClientCallContext context) throws A2AClientException { + return clientTransport.setTaskPushNotificationConfiguration(request, context); + } + + @Override + public TaskPushNotificationConfig getTaskPushNotificationConfiguration( + GetTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + return clientTransport.getTaskPushNotificationConfiguration(request, context); + } + + @Override + public List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + return clientTransport.listTaskPushNotificationConfigurations(request, context); + } + + @Override + public void deleteTaskPushNotificationConfigurations( + DeleteTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + clientTransport.deleteTaskPushNotificationConfigurations(request, context); + } + + @Override + public void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException { + if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { + throw new A2AClientException("Client and/or server does not support resubscription"); + } + ClientTaskManager tracker = new ClientTaskManager(); + Consumer eventHandler = event -> { + try { + ClientEvent clientEvent = getClientEvent(event, tracker); + consume(clientEvent, agentCard); + } catch (A2AClientError e) { + getStreamingErrorHandler().accept(e); + } + }; + clientTransport.resubscribe(request, eventHandler, getStreamingErrorHandler(), context); + } + + @Override + public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + agentCard = clientTransport.getAgentCard(context); + return agentCard; + } + + @Override + public void close() { + clientTransport.close(); + } + + private ClientEvent getClientEvent(StreamingEventKind event, ClientTaskManager taskManager) throws A2AClientError { + if (event instanceof Message message) { + return new MessageEvent(message); + } else if (event instanceof Task task) { + taskManager.saveTaskEvent(task); + return new TaskEvent(taskManager.getCurrentTask()); + } else if (event instanceof TaskStatusUpdateEvent updateEvent) { + taskManager.saveTaskEvent(updateEvent); + return new TaskUpdateEvent(taskManager.getCurrentTask(), updateEvent); + } else if (event instanceof TaskArtifactUpdateEvent updateEvent) { + taskManager.saveTaskEvent(updateEvent); + return new TaskUpdateEvent(taskManager.getCurrentTask(), updateEvent); + } else { + throw new A2AClientInvalidStateError("Invalid client event"); + } + } + + private void sendMessage(MessageSendParams messageSendParams, ClientCallContext context) throws A2AClientException { + if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { + EventKind eventKind = clientTransport.sendMessage(messageSendParams, context); + ClientEvent clientEvent; + if (eventKind instanceof Task task) { + clientEvent = new TaskEvent(task); + } else { + // must be a message + clientEvent = new MessageEvent((Message) eventKind); + } + consume(clientEvent, agentCard); + } else { + ClientTaskManager tracker = new ClientTaskManager(); + Consumer eventHandler = event -> { + try { + ClientEvent clientEvent = getClientEvent(event, tracker); + consume(clientEvent, agentCard); + } catch (A2AClientError e) { + getStreamingErrorHandler().accept(e); + } + }; + clientTransport.sendMessageStreaming(messageSendParams, eventHandler, getStreamingErrorHandler(), context); + } + } +} diff --git a/client/src/main/java/io/a2a/client/ClientEvent.java b/client/src/main/java/io/a2a/client/ClientEvent.java new file mode 100644 index 000000000..dcaae9495 --- /dev/null +++ b/client/src/main/java/io/a2a/client/ClientEvent.java @@ -0,0 +1,4 @@ +package io.a2a.client; + +public sealed interface ClientEvent permits MessageEvent, TaskEvent, TaskUpdateEvent { +} diff --git a/client/src/main/java/io/a2a/client/ClientFactory.java b/client/src/main/java/io/a2a/client/ClientFactory.java new file mode 100644 index 000000000..6789b76ac --- /dev/null +++ b/client/src/main/java/io/a2a/client/ClientFactory.java @@ -0,0 +1,127 @@ +package io.a2a.client; + +import static io.a2a.util.Assert.checkNotNullParam; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import io.a2a.client.config.ClientCallInterceptor; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; + +/** + * Used to generate the appropriate client for the agent. + */ +public class ClientFactory { + + private final ClientConfig clientConfig; + private final Map transportProviderRegistry = new HashMap<>(); + + public ClientFactory(ClientConfig clientConfig) { + this.clientConfig = clientConfig; + ServiceLoader loader = ServiceLoader.load(ClientTransportProvider.class); + for (ClientTransportProvider transport : loader) { + this.transportProviderRegistry.put(transport.getTransportProtocol(), transport); + } + } + + /** + * Create a new A2A client for the given agent card. + * + * @param agentCard the agent card for the remote agent + * @param consumers a list of consumers to pass responses from the remote agent to + * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @throws A2AClientException if the client cannot be created for any reason + */ + public AbstractClient create(AgentCard agentCard, List> consumers, + Consumer streamingErrorHandler) throws A2AClientException { + return create(agentCard, consumers, streamingErrorHandler, null); + } + + /** + * Create a new A2A client for the given agent card. + * + * @param agentCard the agent card for the remote agent + * @param consumers a list of consumers to pass responses from the remote agent to + * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @param interceptors the optional list of client call interceptors (may be {@code null}) + * @throws A2AClientException if the client cannot be created for any reason + */ + public AbstractClient create(AgentCard agentCard, List> consumers, + Consumer streamingErrorHandler, List interceptors) throws A2AClientException { + checkNotNullParam("agentCard", agentCard); + checkNotNullParam("consumers", consumers); + LinkedHashMap serverPreferredTransports = getServerPreferredTransports(agentCard); + List clientPreferredTransports = getClientPreferredTransports(); + ClientTransport clientTransport = getClientTransport(clientPreferredTransports, serverPreferredTransports, + agentCard, interceptors); + return new Client(agentCard, clientConfig, clientTransport, consumers, streamingErrorHandler); + } + + private static LinkedHashMap getServerPreferredTransports(AgentCard agentCard) { + LinkedHashMap serverPreferredTransports = new LinkedHashMap<>(); + serverPreferredTransports.put(agentCard.preferredTransport(), agentCard.url()); + for (AgentInterface agentInterface : agentCard.additionalInterfaces()) { + serverPreferredTransports.putIfAbsent(agentInterface.transport(), agentInterface.url()); + } + return serverPreferredTransports; + } + + private List getClientPreferredTransports() { + List preferredClientTransports = clientConfig.getSupportedTransports(); + if (preferredClientTransports == null) { + preferredClientTransports = new ArrayList<>(); + } + if (preferredClientTransports.isEmpty()) { + // default to JSONRPC if not specified + preferredClientTransports.add(TransportProtocol.JSONRPC.asString()); + } + return preferredClientTransports; + } + + private ClientTransport getClientTransport(List clientPreferredTransports, + LinkedHashMap serverPreferredTransports, + AgentCard agentCard, + List interceptors) throws A2AClientException { + String transportProtocol = null; + String transportUrl = null; + if (clientConfig.isUseClientPreference()) { + for (String clientPreferredTransport : clientPreferredTransports) { + if (serverPreferredTransports.containsKey(clientPreferredTransport)) { + transportProtocol = clientPreferredTransport; + transportUrl = serverPreferredTransports.get(transportProtocol); + break; + } + } + } else { + for (Map.Entry transport : serverPreferredTransports.entrySet()) { + if (clientPreferredTransports.contains(transport.getKey())) { + transportProtocol = transport.getKey(); + transportUrl = transport.getValue(); + break; + } + } + } + if (transportProtocol == null || transportUrl == null) { + throw new A2AClientException("No compatible transports found"); + } + if (! transportProviderRegistry.containsKey(transportProtocol)) { + throw new A2AClientException("No client available for " + transportProtocol); + } + + ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(transportProtocol); + return clientTransportProvider.create(clientConfig, agentCard, transportUrl, interceptors); + } + +} diff --git a/client/src/main/java/io/a2a/client/ClientTaskManager.java b/client/src/main/java/io/a2a/client/ClientTaskManager.java new file mode 100644 index 000000000..39975c184 --- /dev/null +++ b/client/src/main/java/io/a2a/client/ClientTaskManager.java @@ -0,0 +1,141 @@ +package io.a2a.client; + +import static io.a2a.util.Utils.appendArtifactToTask; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.a2a.spec.A2AClientError; +import io.a2a.spec.A2AClientInvalidArgsError; +import io.a2a.spec.A2AClientInvalidStateError; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; + +/** + * Helps manage a task's lifecycle during the execution of a request. + * Responsible for retrieving, saving, and updating the task based on + * events received from the agent. + */ +public class ClientTaskManager { + + private Task currentTask; + private String taskId; + private String contextId; + + public ClientTaskManager() { + this.currentTask = null; + this.taskId = null; + this.contextId = null; + } + + public Task getCurrentTask() throws A2AClientInvalidStateError { + if (currentTask == null) { + throw new A2AClientInvalidStateError("No current task"); + } + return currentTask; + } + + public Task saveTaskEvent(Task task) throws A2AClientInvalidArgsError { + if (currentTask != null) { + throw new A2AClientInvalidArgsError("Task is already set, create new manager for new tasks."); + } + saveTask(task); + return task; + } + + public Task saveTaskEvent(TaskStatusUpdateEvent taskStatusUpdateEvent) throws A2AClientError { + if (taskId == null) { + taskId = taskStatusUpdateEvent.getTaskId(); + } + if (contextId == null) { + contextId = taskStatusUpdateEvent.getContextId(); + } + Task task = currentTask; + if (task == null) { + task = new Task.Builder() + .status(new TaskStatus(TaskState.UNKNOWN)) + .id(taskId) + .contextId(contextId == null ? "" : contextId) + .build(); + } + + Task.Builder taskBuilder = new Task.Builder(task); + if (taskStatusUpdateEvent.getStatus().message() != null) { + if (task.getHistory() == null) { + taskBuilder.history(taskStatusUpdateEvent.getStatus().message()); + } else { + List history = task.getHistory(); + history.add(taskStatusUpdateEvent.getStatus().message()); + taskBuilder.history(history); + } + } + if (taskStatusUpdateEvent.getMetadata() != null) { + Map metadata = taskStatusUpdateEvent.getMetadata(); + if (metadata == null) { + metadata = new HashMap<>(); + } + metadata.putAll(taskStatusUpdateEvent.getMetadata()); + taskBuilder.metadata(metadata); + } + taskBuilder.status(taskStatusUpdateEvent.getStatus()); + currentTask = taskBuilder.build(); + return currentTask; + } + + public Task saveTaskEvent(TaskArtifactUpdateEvent taskArtifactUpdateEvent) { + if (taskId == null) { + taskId = taskArtifactUpdateEvent.getTaskId(); + } + if (contextId == null) { + contextId = taskArtifactUpdateEvent.getContextId(); + } + Task task = currentTask; + if (task == null) { + task = new Task.Builder() + .status(new TaskStatus(TaskState.UNKNOWN)) + .id(taskId) + .contextId(contextId == null ? "" : contextId) + .build(); + } + currentTask = appendArtifactToTask(task, taskArtifactUpdateEvent, taskId); + return currentTask; + } + + /** + * Update a task by adding a message to its history. If the task has a message in its current status, + * that message is moved to the history first. + * + * @param message the new message to add to the history + * @param task the task to update + * @return the updated task + */ + public Task updateWithMessage(Message message, Task task) { + Task.Builder taskBuilder = new Task.Builder(task); + List history = task.getHistory(); + if (history == null) { + history = new ArrayList<>(); + } + if (task.getStatus().message() != null) { + history.add(task.getStatus().message()); + taskBuilder.status(new TaskStatus(task.getStatus().state(), null, task.getStatus().timestamp())); + } + history.add(message); + taskBuilder.history(history); + currentTask = taskBuilder.build(); + return currentTask; + } + + private void saveTask(Task task) { + currentTask = task; + if (taskId == null) { + taskId = currentTask.getId(); + contextId = currentTask.getContextId(); + } + } +} diff --git a/client/src/main/java/io/a2a/client/MessageEvent.java b/client/src/main/java/io/a2a/client/MessageEvent.java new file mode 100644 index 000000000..b5970ab78 --- /dev/null +++ b/client/src/main/java/io/a2a/client/MessageEvent.java @@ -0,0 +1,26 @@ +package io.a2a.client; + +import io.a2a.spec.Message; + +/** + * A message event received by a client. + */ +public final class MessageEvent implements ClientEvent { + + private final Message message; + + /** + * A message event. + * + * @param message the message received + */ + public MessageEvent(Message message) { + this.message = message; + } + + public Message getMessage() { + return message; + } +} + + diff --git a/client/src/main/java/io/a2a/client/TaskEvent.java b/client/src/main/java/io/a2a/client/TaskEvent.java new file mode 100644 index 000000000..a18392841 --- /dev/null +++ b/client/src/main/java/io/a2a/client/TaskEvent.java @@ -0,0 +1,27 @@ +package io.a2a.client; + +import static io.a2a.util.Assert.checkNotNullParam; + +import io.a2a.spec.Task; + +/** + * A task event received by a client. + */ +public final class TaskEvent implements ClientEvent { + + private final Task task; + + /** + * A client task event. + * + * @param task the task received + */ + public TaskEvent(Task task) { + checkNotNullParam("task", task); + this.task = task; + } + + public Task getTask() { + return task; + } +} diff --git a/client/src/main/java/io/a2a/client/TaskUpdateEvent.java b/client/src/main/java/io/a2a/client/TaskUpdateEvent.java new file mode 100644 index 000000000..c45650822 --- /dev/null +++ b/client/src/main/java/io/a2a/client/TaskUpdateEvent.java @@ -0,0 +1,37 @@ +package io.a2a.client; + +import static io.a2a.util.Assert.checkNotNullParam; + +import io.a2a.spec.Task; +import io.a2a.spec.UpdateEvent; + +/** + * A task update event received by a client. + */ +public final class TaskUpdateEvent implements ClientEvent { + + private final Task task; + private final UpdateEvent updateEvent; + + /** + * A task update event. + * + * @param task the current task + * @param updateEvent the update event received for the current task + */ + public TaskUpdateEvent(Task task, UpdateEvent updateEvent) { + checkNotNullParam("task", task); + checkNotNullParam("updateEvent", updateEvent); + this.task = task; + this.updateEvent = updateEvent; + } + + public Task getTask() { + return task; + } + + public UpdateEvent getUpdateEvent() { + return updateEvent; + } + +} diff --git a/client/src/test/java/io/a2a/client/JsonMessages.java b/client/src/test/java/io/a2a/client/JsonMessages.java index d59ee0146..b99da3623 100644 --- a/client/src/test/java/io/a2a/client/JsonMessages.java +++ b/client/src/test/java/io/a2a/client/JsonMessages.java @@ -161,493 +161,4 @@ public class JsonMessages { }"""; - static final String SEND_MESSAGE_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } - }"""; - - static final String SEND_MESSAGE_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "joke", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; - - static final String SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": "request-1234-with-message-response", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } - }"""; - - - static final String SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "role": "agent", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ], - "messageId": "msg-456", - "kind": "message" - } - }"""; - - static final String SEND_MESSAGE_WITH_ERROR_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234-with-error", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } - }"""; - - static final String SEND_MESSAGE_ERROR_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "error": { - "code": -32702, - "message": "Invalid parameters", - "data": "Hello world" - } - }"""; - - static final String GET_TASK_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234", - "method": "tasks/get", - "params": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "historyLength": 10 - } - } - """; - - static final String GET_TASK_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ] - } - ], - "history": [ - { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - }, - { - "kind": "file", - "file": { - "uri": "file:///path/to/file.txt", - "mimeType": "text/plain" - } - }, - { - "kind": "file", - "file": { - "bytes": "aGVsbG8=", - "name": "hello.txt" - } - } - ], - "messageId": "message-123", - "kind": "message" - } - ], - "metadata": {}, - "kind": "task" - } - } - """; - - static final String CANCEL_TASK_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234", - "method": "tasks/cancel", - "params": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "metadata": {} - } - } - """; - - static final String CANCEL_TASK_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "canceled" - }, - "metadata": {}, - "kind" : "task" - } - } - """; - - static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "1", - "method": "tasks/pushNotificationConfig/get", - "params": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "metadata": {}, - } - } - """; - - static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "pushNotificationConfig": { - "url": "https://example.com/callback", - "authentication": { - "schemes": ["jwt"] - } - } - } - } - """; - - static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "1", - "method": "tasks/pushNotificationConfig/set", - "params": { - "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "pushNotificationConfig": { - "url": "https://example.com/callback", - "authentication": { - "schemes": ["jwt"] - } - } - } - }"""; - - static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "pushNotificationConfig": { - "url": "https://example.com/callback", - "authentication": { - "schemes": ["jwt"] - } - } - } - } - """; - - static final String SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234-with-file", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "analyze this image" - }, - { - "kind": "file", - "file": { - "uri": "file:///path/to/image.jpg", - "mimeType": "image/jpeg" - } - } - ], - "messageId": "message-1234-with-file", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; - - static final String SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "image-analysis", - "parts": [ - { - "kind": "text", - "text": "This is an image of a cat sitting on a windowsill." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; - - static final String SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234-with-data", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "process this data" - }, - { - "kind": "data", - "data": { - "temperature": 25.5, - "humidity": 60.2, - "location": "San Francisco", - "timestamp": "2024-01-15T10:30:00Z" - } - } - ], - "messageId": "message-1234-with-data", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; - - static final String SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "data-analysis", - "parts": [ - { - "kind": "text", - "text": "Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; - - static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "request-1234-with-mixed", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "analyze this data and image" - }, - { - "kind": "file", - "file": { - "bytes": "aGVsbG8=", - "name": "chart.png", - "mimeType": "image/png" - } - }, - { - "kind": "data", - "data": { - "chartType": "bar", - "dataPoints": [10, 20, 30, 40], - "labels": ["Q1", "Q2", "Q3", "Q4"] - } - } - ], - "messageId": "message-1234-with-mixed", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; - - static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "mixed-analysis", - "parts": [ - { - "kind": "text", - "text": "Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; - - static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST = """ - { - "jsonrpc": "2.0", - "id": "1", - "method": "agent/getAuthenticatedExtendedCard" - } - """; - - static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": "1", - "result": - """ + AUTHENTICATION_EXTENDED_AGENT_CARD + - """ - } - """; -} +} \ No newline at end of file diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 3aaa5c221..25970a7d4 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -18,7 +18,7 @@ io.github.a2asdk - a2a-java-sdk-client + a2a-java-sdk-client-transport-jsonrpc diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index 94e595228..f810244e1 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -4,8 +4,8 @@ import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; -import io.a2a.client.A2AClient; import io.a2a.A2A; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.spec.AgentCard; import io.a2a.spec.EventKind; import io.a2a.spec.Message; @@ -46,15 +46,12 @@ public static void main(String[] args) { System.out.println("Public card does not indicate support for an extended card. Using public card."); } - A2AClient client = new A2AClient(finalAgentCard); + JSONRPCTransport client = new JSONRPCTransport(finalAgentCard); Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you MessageSendParams params = new MessageSendParams.Builder() .message(message) .build(); - SendMessageResponse response = client.sendMessage(params); - System.out.println("Message sent with ID: " + response.getId()); - - EventKind result = response.getResult(); + EventKind result = client.sendMessage(params, null); if (result instanceof Message responseMessage) { StringBuilder textBuilder = new StringBuilder(); if (responseMessage.getParts() != null) { diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index a60b123d9..47ea4ce82 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -28,7 +28,7 @@ io.github.a2asdk - a2a-java-sdk-client + a2a-java-sdk-client-transport-jsonrpc ${project.version} diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index e23537cee..4ff3752bb 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -36,7 +36,7 @@ io.github.a2asdk - a2a-java-sdk-client + a2a-java-sdk-client-transport-jsonrpc diff --git a/pom.xml b/pom.xml index 0e1e4542d..55a1d177f 100644 --- a/pom.xml +++ b/pom.xml @@ -280,6 +280,7 @@ client + client-config client-http client-transport/grpc client-transport/jsonrpc diff --git a/server-common/pom.xml b/server-common/pom.xml index 29e7f6b76..3f3ef584a 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -27,6 +27,11 @@ a2a-java-sdk-client-http ${project.version} + + ${project.groupId} + a2a-java-sdk-client-transport-jsonrpc + ${project.version} + com.fasterxml.jackson.core jackson-databind diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index cebddf85a..73d3d2ed6 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -2,6 +2,7 @@ import static io.a2a.spec.TaskState.SUBMITTED; import static io.a2a.util.Assert.checkNotNullParam; +import static io.a2a.util.Utils.appendArtifactToTask; import java.util.ArrayList; import java.util.List; @@ -82,60 +83,7 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { checkIdsAndUpdateIfNecessary(event.getTaskId(), event.getContextId()); Task task = ensureTask(event.getTaskId(), event.getContextId()); - - // Append artifacts - List artifacts = task.getArtifacts() == null ? new ArrayList<>() : new ArrayList<>(task.getArtifacts()); - - Artifact newArtifact = event.getArtifact(); - String artifactId = newArtifact.artifactId(); - boolean appendParts = event.isAppend() != null && event.isAppend(); - - Artifact existingArtifact = null; - int existingArtifactIndex = -1; - - for (int i = 0; i < artifacts.size(); i++) { - Artifact curr = artifacts.get(i); - if (curr.artifactId() != null && curr.artifactId().equals(artifactId)) { - existingArtifact = curr; - existingArtifactIndex = i; - break; - } - } - - if (!appendParts) { - // This represents the first chunk for this artifact index - if (existingArtifactIndex >= 0) { - // Replace the existing artifact entirely with the new artifact - LOGGER.debug("Replacing artifact at id {} for task {}", artifactId, taskId); - artifacts.set(existingArtifactIndex, newArtifact); - } else { - // Append the new artifact since no artifact with this id/index exists yet - LOGGER.debug("Adding artifact at id {} for task {}", artifactId, taskId); - artifacts.add(newArtifact); - } - - } else if (existingArtifact != null) { - // Append new parts to the existing artifact's parts list - // Do this to a copy - LOGGER.debug("Appending parts to artifact id {} for task {}", artifactId, taskId); - List> parts = new ArrayList<>(existingArtifact.parts()); - parts.addAll(newArtifact.parts()); - Artifact updated = new Artifact.Builder(existingArtifact) - .parts(parts) - .build(); - artifacts.set(existingArtifactIndex, updated); - } else { - // We received a chunk to append, but we don't have an existing artifact. - // We will ignore this chunk - LOGGER.warn( - "Received append=true for nonexistent artifact index for artifact {} in task {}. Ignoring chunk.", - artifactId, taskId); - } - - task = new Task.Builder(task) - .artifacts(artifacts) - .build(); - + task = appendArtifactToTask(task, event, taskId); return saveTask(task); } diff --git a/spec/src/main/java/io/a2a/spec/A2AClientException.java b/spec/src/main/java/io/a2a/spec/A2AClientException.java new file mode 100644 index 000000000..17ff073d5 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/A2AClientException.java @@ -0,0 +1,23 @@ +package io.a2a.spec; + +/** + * Exception to indicate a general failure related to an A2A client. + */ +public class A2AClientException extends A2AException { + + public A2AClientException() { + super(); + } + + public A2AClientException(final String msg) { + super(msg); + } + + public A2AClientException(final Throwable cause) { + super(cause); + } + + public A2AClientException(final String msg, final Throwable cause) { + super(msg, cause); + } +} diff --git a/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java b/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java new file mode 100644 index 000000000..c39c53350 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java @@ -0,0 +1,15 @@ +package io.a2a.spec; + +public class A2AClientInvalidArgsError extends A2AClientError { + + public A2AClientInvalidArgsError() { + } + + public A2AClientInvalidArgsError(String message) { + super("Invalid arguments error: " + message); + } + + public A2AClientInvalidArgsError(String message, Throwable cause) { + super("Invalid arguments error: " + message, cause); + } +} diff --git a/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java b/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java new file mode 100644 index 000000000..e828fe95d --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java @@ -0,0 +1,15 @@ +package io.a2a.spec; + +public class A2AClientInvalidStateError extends A2AClientError { + + public A2AClientInvalidStateError() { + } + + public A2AClientInvalidStateError(String message) { + super("Invalid state error: " + message); + } + + public A2AClientInvalidStateError(String message, Throwable cause) { + super("Invalid state error: " + message, cause); + } +} diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 03269bf33..279916c68 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -14,7 +14,7 @@ */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventKind { +public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { public static final String ARTIFACT_UPDATE = "artifact-update"; private final String taskId; diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 21726d607..788655530 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -14,7 +14,7 @@ */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKind { +public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { public static final String STATUS_UPDATE = "status-update"; private final String taskId; diff --git a/spec/src/main/java/io/a2a/spec/TransportProtocol.java b/spec/src/main/java/io/a2a/spec/TransportProtocol.java index 289e9f5de..afd11c7a1 100644 --- a/spec/src/main/java/io/a2a/spec/TransportProtocol.java +++ b/spec/src/main/java/io/a2a/spec/TransportProtocol.java @@ -35,4 +35,4 @@ public static TransportProtocol fromString(String transport) { throw new IllegalArgumentException("Invalid transport: " + transport); } } -} \ No newline at end of file +} diff --git a/spec/src/main/java/io/a2a/spec/UpdateEvent.java b/spec/src/main/java/io/a2a/spec/UpdateEvent.java new file mode 100644 index 000000000..81060c8eb --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/UpdateEvent.java @@ -0,0 +1,4 @@ +package io.a2a.spec; + +public sealed interface UpdateEvent permits TaskStatusUpdateEvent, TaskArtifactUpdateEvent { +} diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index aac6af61c..c9e982910 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -1,13 +1,23 @@ package io.a2a.util; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.a2a.spec.Artifact; +import io.a2a.spec.Part; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; + public class Utils { public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final Logger log = Logger.getLogger(Utils.class.getName()); static { // needed for date/time types OBJECT_MAPPER.registerModule(new JavaTimeModule()); @@ -27,4 +37,60 @@ public static T defaultIfNull(T value, T defaultValue) { public static void rethrow(Throwable t) throws T { throw (T) t; } + + public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event, String taskId) { + // Append artifacts + List artifacts = task.getArtifacts() == null ? new ArrayList<>() : new ArrayList<>(task.getArtifacts()); + + Artifact newArtifact = event.getArtifact(); + String artifactId = newArtifact.artifactId(); + boolean appendParts = event.isAppend() != null && event.isAppend(); + + Artifact existingArtifact = null; + int existingArtifactIndex = -1; + + for (int i = 0; i < artifacts.size(); i++) { + Artifact curr = artifacts.get(i); + if (curr.artifactId() != null && curr.artifactId().equals(artifactId)) { + existingArtifact = curr; + existingArtifactIndex = i; + break; + } + } + + if (!appendParts) { + // This represents the first chunk for this artifact index + if (existingArtifactIndex >= 0) { + // Replace the existing artifact entirely with the new artifact + log.fine(String.format("Replacing artifact at id %s for task %s", artifactId, taskId)); + artifacts.set(existingArtifactIndex, newArtifact); + } else { + // Append the new artifact since no artifact with this id/index exists yet + log.fine(String.format("Adding artifact at id %s for task %s", artifactId, taskId)); + artifacts.add(newArtifact); + } + + } else if (existingArtifact != null) { + // Append new parts to the existing artifact's parts list + // Do this to a copy + log.fine(String.format("Appending parts to artifact id %s for task %s", artifactId, taskId)); + List> parts = new ArrayList<>(existingArtifact.parts()); + parts.addAll(newArtifact.parts()); + Artifact updated = new Artifact.Builder(existingArtifact) + .parts(parts) + .build(); + artifacts.set(existingArtifactIndex, updated); + } else { + // We received a chunk to append, but we don't have an existing artifact. + // We will ignore this chunk + log.warning( + String.format("Received append=true for nonexistent artifact index for artifact %s in task %s. Ignoring chunk.", + artifactId, taskId)); + } + + return new Task.Builder(task) + .artifacts(artifacts) + .build(); + + } } diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 5b1cc3d1e..6ebf239fb 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -53,6 +53,12 @@ quarkus-arc test + + io.github.a2asdk + a2a-java-sdk-client-transport-jsonrpc + 0.2.6.Beta1-SNAPSHOT + test + diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 783318c43..38693ee33 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -9,6 +9,7 @@ import static org.wildfly.common.Assert.assertNotNull; import static org.wildfly.common.Assert.assertTrue; +import java.io.EOFException; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -23,47 +24,65 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; import jakarta.ws.rs.core.MediaType; -import io.a2a.client.A2AClient; +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.spec.A2AClientException; import io.a2a.spec.A2AServerException; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; +import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; import io.a2a.spec.GetAuthenticatedExtendedCardRequest; import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; +import io.a2a.spec.GetTaskRequest; import io.a2a.spec.GetTaskResponse; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.JSONRPCErrorResponse; +import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SendStreamingMessageResponse; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.SetTaskPushNotificationConfigResponse; +import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; +import io.restassured.RestAssured; +import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -74,45 +93,46 @@ * which delegates to {@link TestUtilsBean}. */ public abstract class AbstractA2AServerTest { + private static final Task MINIMAL_TASK = new Task.Builder() .id("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Task CANCEL_TASK = new Task.Builder() .id("cancel-task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Task CANCEL_TASK_NOT_SUPPORTED = new Task.Builder() .id("cancel-task-not-supported-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Task SEND_MESSAGE_NOT_SUPPORTED = new Task.Builder() .id("task-not-supported-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - + private static final Message MESSAGE = new Message.Builder() .messageId("111") .role(Message.Role.AGENT) .parts(new TextPart("test message")) .build(); public static final String APPLICATION_JSON = "application/json"; - + private final int serverPort; - private A2AClient client; - + private ClientTransport client; + protected AbstractA2AServerTest(int serverPort) { this.serverPort = serverPort; - this.client = new A2AClient("http://localhost:" + serverPort); + this.client = new JSONRPCTransport("http://localhost:" + serverPort); } - + @Test public void testTaskStoreMethodsSanityTest() throws Exception { Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); @@ -121,89 +141,139 @@ public void testTaskStoreMethodsSanityTest() throws Exception { assertEquals(task.getId(), saved.getId()); assertEquals(task.getContextId(), saved.getContextId()); assertEquals(task.getStatus().state(), saved.getStatus().state()); - + deleteTaskInTaskStore(task.getId()); Task saved2 = getTaskFromTaskStore(task.getId()); assertNull(saved2); } - + @Test public void testGetTaskSuccess() throws Exception { testGetTask(); } - + private void testGetTask() throws Exception { testGetTask(null); } - + private void testGetTask(String mediaType) throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - GetTaskResponse response = client.getTask("1", new TaskQueryParams(MINIMAL_TASK.getId())); + GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); + RequestSpecification requestSpecification = RestAssured.given() + .contentType(MediaType.APPLICATION_JSON) + .body(request); + if (mediaType != null) { + requestSpecification = requestSpecification.accept(mediaType); + } + GetTaskResponse response = requestSpecification + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(GetTaskResponse.class); assertEquals("1", response.getId()); assertEquals("task-123", response.getResult().getId()); assertEquals("session-xyz", response.getResult().getContextId()); assertEquals(TaskState.SUBMITTED, response.getResult().getStatus().state()); assertNull(response.getError()); - } catch (A2AServerException e) { - fail("Unexpected exception during getTask: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testGetTaskNotFound() throws Exception { assertTrue(getTaskFromTaskStore("non-existent-task") == null); - try { - client.getTask("1", new TaskQueryParams("non-existent-task")); - fail("Expected A2AServerException to be thrown"); - } catch (A2AServerException e) { - assertInstanceOf(TaskNotFoundError.class, e.getCause()); - } + GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams("non-existent-task")); + GetTaskResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(GetTaskResponse.class); + assertEquals("1", response.getId()); + // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 + assertInstanceOf(JSONRPCError.class, response.getError()); + assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); + assertNull(response.getResult()); } - + @Test public void testCancelTaskSuccess() throws Exception { saveTaskInTaskStore(CANCEL_TASK); try { - CancelTaskResponse response = client.cancelTask("1", new TaskIdParams(CANCEL_TASK.getId())); - assertEquals("1", response.getId()); + CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK.getId())); + CancelTaskResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(CancelTaskResponse.class); assertNull(response.getError()); + assertEquals(request.getId(), response.getId()); Task task = response.getResult(); assertEquals(CANCEL_TASK.getId(), task.getId()); assertEquals(CANCEL_TASK.getContextId(), task.getContextId()); assertEquals(TaskState.CANCELED, task.getStatus().state()); - } catch (A2AServerException e) { - fail("Unexpected exception during cancel task success test: " + e.getMessage(), e); + } catch (Exception e) { } finally { deleteTaskInTaskStore(CANCEL_TASK.getId()); } } - + @Test public void testCancelTaskNotSupported() throws Exception { saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); try { - client.cancelTask("1", new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); - fail("Expected A2AServerException to be thrown"); - } catch (A2AServerException e) { - assertInstanceOf(UnsupportedOperationError.class, e.getCause()); + CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); + CancelTaskResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(CancelTaskResponse.class); + assertEquals(request.getId(), response.getId()); + assertNull(response.getResult()); + // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 + assertInstanceOf(JSONRPCError.class, response.getError()); + assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); + } catch (Exception e) { } finally { deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); } } - + @Test public void testCancelTaskNotFound() { - try { - client.cancelTask("1", new TaskIdParams("non-existent-task")); - fail("Expected A2AServerException to be thrown"); - } catch (A2AServerException e) { - assertInstanceOf(TaskNotFoundError.class, e.getCause()); - } + CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams("non-existent-task")); + CancelTaskResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(CancelTaskResponse.class) + ; + assertEquals(request.getId(), response.getId()); + assertNull(response.getResult()); + // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 + assertInstanceOf(JSONRPCError.class, response.getError()); + assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); } - + @Test public void testSendMessageNewMessageSuccess() throws Exception { assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); @@ -211,23 +281,25 @@ public void testSendMessageNewMessageSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - MessageSendParams messageSendParams = new MessageSendParams(message, null, null); - - try { - SendMessageResponse response = client.sendMessage("1", messageSendParams); - assertEquals("1", response.getId()); - assertNull(response.getError()); - Message messageResponse = (Message) response.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - } catch (A2AServerException e) { - fail("Unexpected exception during send new message test: " + e.getMessage(), e); - } + SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); + SendMessageResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(SendMessageResponse.class); + assertNull(response.getError()); + Message messageResponse = (Message) response.getResult(); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); } - + @Test public void testSendMessageExistingTaskSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -236,10 +308,16 @@ public void testSendMessageExistingTaskSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - MessageSendParams messageSendParams = new MessageSendParams(message, null, null); - - SendMessageResponse response = client.sendMessage("1", messageSendParams); - assertEquals("1", response.getId()); + SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); + SendMessageResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(SendMessageResponse.class); assertNull(response.getError()); Message messageResponse = (Message) response.getResult(); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); @@ -247,78 +325,108 @@ public void testSendMessageExistingTaskSuccess() throws Exception { Part part = messageResponse.getParts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("test message", ((TextPart) part).getText()); - } catch (A2AServerException e) { - fail("Unexpected exception during send message to existing task test: " + e.getMessage(), e); + } catch (Exception e) { } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testSetPushNotificationSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Builder() - .url("http://example.com") - .build(); - SetTaskPushNotificationConfigResponse response = client.setTaskPushNotificationConfig("1", - MINIMAL_TASK.getId(), pushNotificationConfig); - assertEquals("1", response.getId()); + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); + SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + SetTaskPushNotificationConfigResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(SetTaskPushNotificationConfigResponse.class); assertNull(response.getError()); + assertEquals(request.getId(), response.getId()); TaskPushNotificationConfig config = response.getResult(); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); - } catch (A2AServerException e) { - fail("Unexpected exception during set push notification test: " + e.getMessage(), e); + } catch (Exception e) { } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testGetPushNotificationSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Builder() - .url("http://example.com") - .build(); - - // First set the push notification config - SetTaskPushNotificationConfigResponse setResponse = client.setTaskPushNotificationConfig("1", - MINIMAL_TASK.getId(), pushNotificationConfig); - assertNotNull(setResponse); - - // Then get the push notification config - GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("2", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - assertEquals("2", response.getId()); + TaskPushNotificationConfig taskPushConfig = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); + + SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + SetTaskPushNotificationConfigResponse setTaskPushNotificationResponse = given() + .contentType(MediaType.APPLICATION_JSON) + .body(setTaskPushNotificationRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(SetTaskPushNotificationConfigResponse.class); + assertNotNull(setTaskPushNotificationResponse); + + GetTaskPushNotificationConfigRequest request = + new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + GetTaskPushNotificationConfigResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(GetTaskPushNotificationConfigResponse.class); assertNull(response.getError()); + assertEquals(request.getId(), response.getId()); TaskPushNotificationConfig config = response.getResult(); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); - } catch (A2AServerException e) { - fail("Unexpected exception during get push notification test: " + e.getMessage(), e); + } catch (Exception e) { } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testError() { Message message = new Message.Builder(MESSAGE) .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId()) .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId()) .build(); - MessageSendParams messageSendParams = new MessageSendParams(message, null, null); - - try { - client.sendMessage("1", messageSendParams); - fail("Expected A2AServerException to be thrown"); - } catch (A2AServerException e) { - assertInstanceOf(UnsupportedOperationError.class, e.getCause()); - } + SendMessageRequest request = new SendMessageRequest( + "1", new MessageSendParams(message, null, null)); + SendMessageResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(request) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(SendMessageResponse.class); + assertEquals(request.getId(), response.getId()); + assertNull(response.getResult()); + // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 + assertInstanceOf(JSONRPCError.class, response.getError()); + assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); } - + @Test public void testGetAgentCard() { AgentCard agentCard = given() @@ -340,7 +448,7 @@ public void testGetAgentCard() { assertTrue(agentCard.capabilities().stateTransitionHistory()); assertTrue(agentCard.skills().isEmpty()); } - + @Test public void testGetExtendAgentCardNotSupported() { GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); @@ -358,7 +466,7 @@ public void testGetExtendAgentCardNotSupported() { assertEquals(new AuthenticatedExtendedCardNotConfiguredError().getCode(), response.getError().getCode()); assertNull(response.getResult()); } - + @Test public void testMalformedJSONRPCRequest() { // missing closing bracket @@ -375,20 +483,20 @@ public void testMalformedJSONRPCRequest() { assertNotNull(response.getError()); assertEquals(new JSONParseError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidParamsJSONRPCRequest() { String invalidParamsRequest = """ {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} """; testInvalidParams(invalidParamsRequest); - + invalidParamsRequest = """ {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} """; testInvalidParams(invalidParamsRequest); } - + private void testInvalidParams(String invalidParamsRequest) { JSONRPCErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) @@ -403,7 +511,7 @@ private void testInvalidParams(String invalidParamsRequest) { assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); assertEquals("1", response.getId()); } - + @Test public void testInvalidJSONRPCRequestMissingJsonrpc() { String invalidRequest = """ @@ -424,7 +532,7 @@ public void testInvalidJSONRPCRequestMissingJsonrpc() { assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidJSONRPCRequestMissingMethod() { String invalidRequest = """ @@ -442,7 +550,7 @@ public void testInvalidJSONRPCRequestMissingMethod() { assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidJSONRPCRequestInvalidId() { String invalidRequest = """ @@ -460,7 +568,7 @@ public void testInvalidJSONRPCRequestInvalidId() { assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } - + @Test public void testInvalidJSONRPCRequestNonExistentMethod() { String invalidRequest = """ @@ -478,12 +586,13 @@ public void testInvalidJSONRPCRequestNonExistentMethod() { assertNotNull(response.getError()); assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); } - + @Test public void testNonStreamingMethodWithAcceptHeader() throws Exception { testGetTask(MediaType.APPLICATION_JSON); } - + + @Test public void testSendMessageStreamExistingTaskSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -492,121 +601,115 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - MessageSendParams messageSendParams = new MessageSendParams(message, null, null); - + SendStreamingMessageRequest request = new SendStreamingMessageRequest( + "1", new MessageSendParams(message, null, null)); + + CompletableFuture>> responseFuture = initialiseStreamingRequest(request, null); + CountDownLatch latch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); - AtomicReference messageResponseRef = new AtomicReference<>(); - - // Replace the native HttpClient with A2AClient's sendStreamingMessage method. - client.sendStreamingMessage( - "1", - messageSendParams, - // eventHandler - (streamingEvent) -> { - try { - if (streamingEvent instanceof Message) { - messageResponseRef.set((Message) streamingEvent); - latch.countDown(); - } - } catch (Exception e) { - errorRef.set(e); + + responseFuture.thenAccept(response -> { + if (response.statusCode() != 200) { + //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); + throw new IllegalStateException("Status code was " + response.statusCode()); + } + response.body().forEach(line -> { + try { + SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); + if (jsonResponse != null) { + assertNull(jsonResponse.getError()); + Message messageResponse = (Message) jsonResponse.getResult(); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); latch.countDown(); } - }, - // errorHandler - (jsonRpcError) -> { - errorRef.set(new RuntimeException("JSON-RPC Error: " + jsonRpcError.getMessage())); - latch.countDown(); - }, - // failureHandler - () -> { - if (errorRef.get() == null) { - errorRef.set(new RuntimeException("Stream processing failed")); - } - latch.countDown(); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } - ); - + }); + }).exceptionally(t -> { + if (!isStreamClosedError(t)) { + errorRef.set(t); + } + latch.countDown(); + return null; + }); + boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); - - Message messageResponse = messageResponseRef.get(); - Assertions.assertNotNull(messageResponse); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); } catch (Exception e) { - fail("Unexpected exception during send message stream to existing task test: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void testResubscribeExistingTaskSuccess() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); saveTaskInTaskStore(MINIMAL_TASK); - + try { // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap // does not work because after the message is sent, the queue becomes null but task resubscription // requires the queue to still be active ensureQueueForTask(MINIMAL_TASK.getId()); - + CountDownLatch taskResubscriptionRequestSent = new CountDownLatch(1); CountDownLatch taskResubscriptionResponseReceived = new CountDownLatch(2); - AtomicReference firstResponse = new AtomicReference<>(); - AtomicReference secondResponse = new AtomicReference<>(); - AtomicReference errorRef = new AtomicReference<>(); - + AtomicReference firstResponse = new AtomicReference<>(); + AtomicReference secondResponse = new AtomicReference<>(); + // resubscribe to the task, requires the task and its queue to still be active - TaskIdParams taskIdParams = new TaskIdParams(MINIMAL_TASK.getId()); - + TaskResubscriptionRequest taskResubscriptionRequest = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + // Count down the latch when the MultiSseSupport on the server has started subscribing awaitStreamingSubscription() .whenComplete((unused, throwable) -> taskResubscriptionRequestSent.countDown()); - - // Use A2AClient-like resubscribeToTask Method - client.resubscribeToTask( - "1", // requestId - taskIdParams, - // eventHandler - (streamingEvent) -> { + + CompletableFuture>> responseFuture = initialiseStreamingRequest(taskResubscriptionRequest, null); + + AtomicReference errorRef = new AtomicReference<>(); + + responseFuture.thenAccept(response -> { + + if (response.statusCode() != 200) { + throw new IllegalStateException("Status code was " + response.statusCode()); + } + try { + response.body().forEach(line -> { try { - if (streamingEvent instanceof TaskArtifactUpdateEvent artifactUpdateEvent) { - firstResponse.set(artifactUpdateEvent); - taskResubscriptionResponseReceived.countDown(); - } else if (streamingEvent instanceof TaskStatusUpdateEvent statusUpdateEvent) { - secondResponse.set(statusUpdateEvent); + SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); + if (jsonResponse != null) { + SendStreamingMessageResponse sendStreamingMessageResponse = Utils.OBJECT_MAPPER.readValue(line.substring("data: ".length()).trim(), SendStreamingMessageResponse.class); + if (taskResubscriptionResponseReceived.getCount() == 2) { + firstResponse.set(sendStreamingMessageResponse); + } else { + secondResponse.set(sendStreamingMessageResponse); + } taskResubscriptionResponseReceived.countDown(); + if (taskResubscriptionResponseReceived.getCount() == 0) { + throw new BreakException(); + } } - } catch (Exception e) { - errorRef.set(e); - taskResubscriptionResponseReceived.countDown(); - taskResubscriptionResponseReceived.countDown(); // Make sure the counter is zeroed + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } - }, - // errorHandler - (jsonRpcError) -> { - errorRef.set(new RuntimeException("JSON-RPC Error: " + jsonRpcError.getMessage())); - taskResubscriptionResponseReceived.countDown(); - taskResubscriptionResponseReceived.countDown(); // Make sure the counter is zeroed - }, - // failureHandler - () -> { - if (errorRef.get() == null) { - errorRef.set(new RuntimeException("Stream processing failed")); - } - taskResubscriptionResponseReceived.countDown(); - taskResubscriptionResponseReceived.countDown(); // Make sure the counter is zeroed - } - ); - + }); + } catch (BreakException e) { + } + }).exceptionally(t -> { + if (!isStreamClosedError(t)) { + errorRef.set(t); + } + return null; + }); + try { taskResubscriptionRequestSent.await(); List events = List.of( @@ -624,34 +727,37 @@ public void testResubscribeExistingTaskSuccess() throws Exception { .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build()); - + for (Event event : events) { enqueueEventOnServer(event); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - + // wait for the client to receive the responses taskResubscriptionResponseReceived.await(); - - Assertions.assertNull(errorRef.get()); - + assertNotNull(firstResponse.get()); - TaskArtifactUpdateEvent taskArtifactUpdateEvent = firstResponse.get(); + SendStreamingMessageResponse sendStreamingMessageResponse = firstResponse.get(); + assertNull(sendStreamingMessageResponse.getError()); + TaskArtifactUpdateEvent taskArtifactUpdateEvent = (TaskArtifactUpdateEvent) sendStreamingMessageResponse.getResult(); assertEquals(MINIMAL_TASK.getId(), taskArtifactUpdateEvent.getTaskId()); assertEquals(MINIMAL_TASK.getContextId(), taskArtifactUpdateEvent.getContextId()); Part part = taskArtifactUpdateEvent.getArtifact().parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("text", ((TextPart) part).getText()); - + assertNotNull(secondResponse.get()); - TaskStatusUpdateEvent taskStatusUpdateEvent = secondResponse.get(); + sendStreamingMessageResponse = secondResponse.get(); + assertNull(sendStreamingMessageResponse.getError()); + TaskStatusUpdateEvent taskStatusUpdateEvent = (TaskStatusUpdateEvent) sendStreamingMessageResponse.getResult(); assertEquals(MINIMAL_TASK.getId(), taskStatusUpdateEvent.getTaskId()); assertEquals(MINIMAL_TASK.getContextId(), taskStatusUpdateEvent.getContextId()); assertEquals(TaskState.COMPLETED, taskStatusUpdateEvent.getStatus().state()); assertNotNull(taskStatusUpdateEvent.getStatus().timestamp()); } finally { + //setStreamingSubscribedRunnable(null); deleteTaskInTaskStore(MINIMAL_TASK.getId()); executorService.shutdown(); if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { @@ -659,114 +765,109 @@ public void testResubscribeExistingTaskSuccess() throws Exception { } } } - + @Test public void testResubscribeNoExistingTaskError() throws Exception { - TaskIdParams taskIdParams = new TaskIdParams("non-existent-task"); - + TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams("non-existent-task")); + + CompletableFuture>> responseFuture = initialiseStreamingRequest(request, null); + CountDownLatch latch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); - AtomicReference jsonRpcErrorRef = new AtomicReference<>(); - - // Use A2AClient-like resubscribeToTask Method - client.resubscribeToTask( - "1", // requestId - taskIdParams, - // eventHandler - (streamingEvent) -> { - // Do not expect to receive any success events, as the task does not exist - errorRef.set(new RuntimeException("Unexpected event received for non-existent task")); - latch.countDown(); - }, - // errorHandler - (jsonRpcError) -> { - jsonRpcErrorRef.set(jsonRpcError); - latch.countDown(); - }, - // failureHandler - () -> { - if (errorRef.get() == null && jsonRpcErrorRef.get() == null) { - errorRef.set(new RuntimeException("Expected error for non-existent task")); + + responseFuture.thenAccept(response -> { + if (response.statusCode() != 200) { + //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); + throw new IllegalStateException("Status code was " + response.statusCode()); + } + response.body().forEach(line -> { + try { + SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); + if (jsonResponse != null) { + assertEquals(request.getId(), jsonResponse.getId()); + assertNull(jsonResponse.getResult()); + // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 + assertInstanceOf(JSONRPCError.class, jsonResponse.getError()); + assertEquals(new TaskNotFoundError().getCode(), jsonResponse.getError().getCode()); + latch.countDown(); } - latch.countDown(); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } - ); - + }); + }).exceptionally(t -> { + if (!isStreamClosedError(t)) { + errorRef.set(t); + } + latch.countDown(); + return null; + }); + boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); - - // Validation returns the expected TaskNotFoundError - JSONRPCError jsonRpcError = jsonRpcErrorRef.get(); - Assertions.assertNotNull(jsonRpcError); - assertEquals(new TaskNotFoundError().getCode(), jsonRpcError.getCode()); } - + @Test public void testStreamingMethodWithAcceptHeader() throws Exception { testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); } - + @Test public void testSendMessageStreamNewMessageSuccess() throws Exception { testSendStreamingMessage(null); } - + private void testSendStreamingMessage(String mediaType) throws Exception { Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - MessageSendParams messageSendParams = new MessageSendParams(message, null, null); - + SendStreamingMessageRequest request = new SendStreamingMessageRequest( + "1", new MessageSendParams(message, null, null)); + + CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); + CountDownLatch latch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); - AtomicReference messageResponseRef = new AtomicReference<>(); - - // Using A2AClient's sendStreamingMessage method - client.sendStreamingMessage( - "1", // requestId - messageSendParams, - // eventHandler - (streamingEvent) -> { - try { - if (streamingEvent instanceof Message) { - messageResponseRef.set((Message) streamingEvent); - latch.countDown(); - } - } catch (Exception e) { - errorRef.set(e); + + responseFuture.thenAccept(response -> { + if (response.statusCode() != 200) { + //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); + throw new IllegalStateException("Status code was " + response.statusCode()); + } + response.body().forEach(line -> { + try { + SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); + if (jsonResponse != null) { + assertNull(jsonResponse.getError()); + Message messageResponse = (Message) jsonResponse.getResult(); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); latch.countDown(); } - }, - // errorHandler - (jsonRpcError) -> { - errorRef.set(new RuntimeException("JSON-RPC Error: " + jsonRpcError.getMessage())); - latch.countDown(); - }, - // failureHandler - () -> { - if (errorRef.get() == null) { - errorRef.set(new RuntimeException("Stream processing failed")); - } - latch.countDown(); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); } - ); - + }); + }).exceptionally(t -> { + if (!isStreamClosedError(t)) { + errorRef.set(t); + } + latch.countDown(); + return null; + }); + + boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); - - Message messageResponse = messageResponseRef.get(); - Assertions.assertNotNull(messageResponse); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - + } - + @Test public void testListPushNotificationConfigWithConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -782,13 +883,13 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); - + try { - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); - assertEquals("111", listResponse.getId()); - assertEquals(2, listResponse.getResult().size()); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1), listResponse.getResult().get(0)); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2), listResponse.getResult().get(1)); + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + assertEquals(2, result.size()); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1), result.get(0)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2), result.get(1)); } catch (Exception e) { fail(); } finally { @@ -797,7 +898,7 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testListPushNotificationConfigWithoutConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -810,20 +911,20 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { .url("http://2.example.com") .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - + // will overwrite the previous one savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); - assertEquals("111", listResponse.getId()); - assertEquals(1, listResponse.getResult().size()); - + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + assertEquals(1, result.size()); + PushNotificationConfig expectedNotificationConfig = new PushNotificationConfig.Builder() .url("http://2.example.com") .id(MINIMAL_TASK.getId()) .build(); assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), expectedNotificationConfig), - listResponse.getResult().get(0)); + result.get(0)); } catch (Exception e) { fail(); } finally { @@ -831,31 +932,32 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testListPushNotificationConfigTaskNotFound() { try { - client.listTaskPushNotificationConfig("111", "non-existent-task"); + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams("non-existent-task"), null); fail(); - } catch (A2AServerException e) { + } catch (A2AClientException e) { assertInstanceOf(TaskNotFoundError.class, e.getCause()); } } - + @Test public void testListPushNotificationConfigEmptyList() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig("111", MINIMAL_TASK.getId()); - assertEquals("111", listResponse.getId()); - assertEquals(0, listResponse.getResult().size()); + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + assertEquals(0, result.size()); } catch (Exception e) { fail(); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testDeletePushNotificationConfigWithValidConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -864,7 +966,7 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build()); - + PushNotificationConfig notificationConfig1 = new PushNotificationConfig.Builder() .url("http://example.com") @@ -878,21 +980,22 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); savePushNotificationConfigInStore("task-456", notificationConfig1); - + try { // specify the config ID to delete - DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), - "config1"); - assertNull(deleteResponse.getError()); - assertNull(deleteResponse.getResult()); - + client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "config1"), + null); + // should now be 1 left - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); - assertEquals(1, listResponse.getResult().size()); - + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + assertEquals(1, result.size()); + // should remain unchanged, this is a different task - listResponse = client.listTaskPushNotificationConfig("task-456"); - assertEquals(1, listResponse.getResult().size()); + result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams("task-456"), null); + assertEquals(1, result.size()); } catch (Exception e) { fail(); } finally { @@ -903,7 +1006,7 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception deleteTaskInTaskStore("task-456"); } } - + @Test public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -919,16 +1022,16 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); - + try { - DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), - "non-existent-config-id"); - assertNull(deleteResponse.getError()); - assertNull(deleteResponse.getResult()); - + client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "non-existent-config-id"), + null); + // should remain unchanged - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); - assertEquals(2, listResponse.getResult().size()); + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + assertEquals(2, result.size()); } catch (Exception e) { fail(); } finally { @@ -937,17 +1040,20 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + @Test public void testDeletePushNotificationConfigTaskNotFound() { try { - client.deleteTaskPushNotificationConfig("non-existent-task", "non-existent-config-id"); + client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams("non-existent-task", + "non-existent-config-id"), + null); fail(); - } catch (A2AServerException e) { + } catch (A2AClientException e) { assertInstanceOf(TaskNotFoundError.class, e.getCause()); } } - + @Test public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -960,19 +1066,19 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio .url("http://2.example.com") .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - + // this one will overwrite the previous one savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); - + try { - DeleteTaskPushNotificationConfigResponse deleteResponse = client.deleteTaskPushNotificationConfig(MINIMAL_TASK.getId(), - MINIMAL_TASK.getId()); - assertNull(deleteResponse.getError()); - assertNull(deleteResponse.getResult()); - + client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()), + null); + // should now be 0 - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(MINIMAL_TASK.getId()); - assertEquals(0, listResponse.getResult().size()); + List result = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + assertEquals(0, result.size()); } catch (Exception e) { fail(); } finally { @@ -980,7 +1086,59 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } - + + private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { + line = extractSseData(line); + if (line != null) { + return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); + } + return null; + } + + private static String extractSseData(String line) { + if (line.startsWith("data:")) { + line = line.substring(5).trim(); + return line; + } + return null; + } + + private boolean isStreamClosedError(Throwable throwable) { + // Unwrap the CompletionException + Throwable cause = throwable; + + while (cause != null) { + if (cause instanceof EOFException) { + return true; + } + cause = cause.getCause(); + } + return false; + } + + private CompletableFuture>> initialiseStreamingRequest( + StreamingJSONRPCRequest request, String mediaType) throws Exception { + + // Create the client + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + + // Create the request + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/")) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) + .header("Content-Type", APPLICATION_JSON); + if (mediaType != null) { + builder.header("Accept", mediaType); + } + HttpRequest httpRequest = builder.build(); + + + // Send request async and return the CompletableFuture + return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); + } + protected void saveTaskInTaskStore(Task task) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -990,13 +1148,13 @@ protected void saveTaskInTaskStore(Task task) throws Exception { .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(task))) .header("Content-Type", APPLICATION_JSON) .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { throw new RuntimeException(String.format("Saving task failed! Status: %d, Body: %s", response.statusCode(), response.body())); } } - + protected Task getTaskFromTaskStore(String taskId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1005,7 +1163,7 @@ protected Task getTaskFromTaskStore(String taskId) throws Exception { .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) .GET() .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() == 404) { return null; @@ -1015,7 +1173,7 @@ protected Task getTaskFromTaskStore(String taskId) throws Exception { } return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); } - + protected void deleteTaskInTaskStore(String taskId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1029,7 +1187,7 @@ protected void deleteTaskInTaskStore(String taskId) throws Exception { throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); } } - + protected void ensureQueueForTask(String taskId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1043,7 +1201,7 @@ protected void ensureQueueForTask(String taskId) throws Exception { throw new RuntimeException(String.format("Ensuring queue failed! Status: %d, Body: %s", response.statusCode(), response.body())); } } - + protected void enqueueEventOnServer(Event event) throws Exception { String path; if (event instanceof TaskArtifactUpdateEvent e) { @@ -1062,17 +1220,17 @@ protected void enqueueEventOnServer(Event event) throws Exception { .header("Content-Type", APPLICATION_JSON) .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(event))) .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { throw new RuntimeException(response.statusCode() + ": Queueing event failed!" + response.body()); } } - + private CompletableFuture awaitStreamingSubscription() { int cnt = getStreamingSubscribedCount(); AtomicInteger initialCount = new AtomicInteger(cnt); - + return CompletableFuture.runAsync(() -> { try { boolean done = false; @@ -1094,7 +1252,7 @@ private CompletableFuture awaitStreamingSubscription() { } }); } - + private int getStreamingSubscribedCount() { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1111,7 +1269,7 @@ private int getStreamingSubscribedCount() { throw new RuntimeException(e); } } - + protected void deletePushNotificationConfigInStore(String taskId, String configId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1125,7 +1283,7 @@ protected void deletePushNotificationConfigInStore(String taskId, String configI throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); } } - + protected void savePushNotificationConfigInStore(String taskId, PushNotificationConfig notificationConfig) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1135,10 +1293,14 @@ protected void savePushNotificationConfigInStore(String taskId, PushNotification .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(notificationConfig))) .header("Content-Type", APPLICATION_JSON) .build(); - + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); if (response.statusCode() != 200) { throw new RuntimeException(response.statusCode() + ": Creating task push notification config failed! " + response.body()); } } -} + + private static class BreakException extends RuntimeException { + + } +} \ No newline at end of file From 3c214b69b953c7202a073160ff79d9da798575a1 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 18 Aug 2025 10:25:40 -0400 Subject: [PATCH 070/493] fix: Rename client-http to http-client since it's used by both client and server code --- client-config/pom.xml | 2 +- client-transport/jsonrpc/pom.xml | 2 +- client/pom.xml | 2 +- {client-http => http-client}/pom.xml | 2 +- .../src/main/java/io/a2a/client/http/A2AHttpClient.java | 0 .../src/main/java/io/a2a/client/http/A2AHttpResponse.java | 0 .../src/main/java/io/a2a/client/http/JdkA2AHttpClient.java | 0 pom.xml | 2 +- server-common/pom.xml | 2 +- 9 files changed, 6 insertions(+), 6 deletions(-) rename {client-http => http-client}/pom.xml (95%) rename {client-http => http-client}/src/main/java/io/a2a/client/http/A2AHttpClient.java (100%) rename {client-http => http-client}/src/main/java/io/a2a/client/http/A2AHttpResponse.java (100%) rename {client-http => http-client}/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java (100%) diff --git a/client-config/pom.xml b/client-config/pom.xml index 3d85221fb..df0f2d650 100644 --- a/client-config/pom.xml +++ b/client-config/pom.xml @@ -19,7 +19,7 @@ ${project.groupId} - a2a-java-sdk-client-http + a2a-java-sdk-http-client ${project.version} diff --git a/client-transport/jsonrpc/pom.xml b/client-transport/jsonrpc/pom.xml index dd9a74c22..5230fed98 100644 --- a/client-transport/jsonrpc/pom.xml +++ b/client-transport/jsonrpc/pom.xml @@ -29,7 +29,7 @@ ${project.groupId} - a2a-java-sdk-client-http + a2a-java-sdk-http-client ${project.version} diff --git a/client/pom.xml b/client/pom.xml index c35fbeef6..76cbb1ef4 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -24,7 +24,7 @@ ${project.groupId} - a2a-java-sdk-client-http + a2a-java-sdk-http-client ${project.version} diff --git a/client-http/pom.xml b/http-client/pom.xml similarity index 95% rename from client-http/pom.xml rename to http-client/pom.xml index ceff12762..8ec988aa2 100644 --- a/client-http/pom.xml +++ b/http-client/pom.xml @@ -9,7 +9,7 @@ a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT - a2a-java-sdk-client-http + a2a-java-sdk-http-client jar diff --git a/client-http/src/main/java/io/a2a/client/http/A2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java similarity index 100% rename from client-http/src/main/java/io/a2a/client/http/A2AHttpClient.java rename to http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java diff --git a/client-http/src/main/java/io/a2a/client/http/A2AHttpResponse.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpResponse.java similarity index 100% rename from client-http/src/main/java/io/a2a/client/http/A2AHttpResponse.java rename to http-client/src/main/java/io/a2a/client/http/A2AHttpResponse.java diff --git a/client-http/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java similarity index 100% rename from client-http/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java rename to http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java diff --git a/pom.xml b/pom.xml index 55a1d177f..36e39cd41 100644 --- a/pom.xml +++ b/pom.xml @@ -281,7 +281,7 @@ client client-config - client-http + http-client client-transport/grpc client-transport/jsonrpc client-transport/spi diff --git a/server-common/pom.xml b/server-common/pom.xml index 3f3ef584a..1bcc2b7b9 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -24,7 +24,7 @@ ${project.groupId} - a2a-java-sdk-client-http + a2a-java-sdk-http-client ${project.version} From a795da8871fcfe63c594514875f7586263cc7d17 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 18 Aug 2025 10:30:19 -0400 Subject: [PATCH 071/493] fix: Move existing client contents to client/base --- client/{ => base}/pom.xml | 1 + client/{ => base}/src/main/java/io/a2a/A2A.java | 0 .../{ => base}/src/main/java/io/a2a/client/A2ACardResolver.java | 0 .../{ => base}/src/main/java/io/a2a/client/AbstractClient.java | 0 client/{ => base}/src/main/java/io/a2a/client/Client.java | 0 client/{ => base}/src/main/java/io/a2a/client/ClientEvent.java | 0 .../{ => base}/src/main/java/io/a2a/client/ClientFactory.java | 0 .../src/main/java/io/a2a/client/ClientTaskManager.java | 0 client/{ => base}/src/main/java/io/a2a/client/MessageEvent.java | 0 client/{ => base}/src/main/java/io/a2a/client/TaskEvent.java | 0 .../{ => base}/src/main/java/io/a2a/client/TaskUpdateEvent.java | 0 client/{ => base}/src/main/resources/META-INF/beans.xml | 0 .../src/test/java/io/a2a/client/A2ACardResolverTest.java | 0 client/{ => base}/src/test/java/io/a2a/client/JsonMessages.java | 0 pom.xml | 2 +- 15 files changed, 2 insertions(+), 1 deletion(-) rename client/{ => base}/pom.xml (97%) rename client/{ => base}/src/main/java/io/a2a/A2A.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/A2ACardResolver.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/AbstractClient.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/Client.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/ClientEvent.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/ClientFactory.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/ClientTaskManager.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/MessageEvent.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/TaskEvent.java (100%) rename client/{ => base}/src/main/java/io/a2a/client/TaskUpdateEvent.java (100%) rename client/{ => base}/src/main/resources/META-INF/beans.xml (100%) rename client/{ => base}/src/test/java/io/a2a/client/A2ACardResolverTest.java (100%) rename client/{ => base}/src/test/java/io/a2a/client/JsonMessages.java (100%) diff --git a/client/pom.xml b/client/base/pom.xml similarity index 97% rename from client/pom.xml rename to client/base/pom.xml index 76cbb1ef4..79c5966c9 100644 --- a/client/pom.xml +++ b/client/base/pom.xml @@ -8,6 +8,7 @@ io.github.a2asdk a2a-java-sdk-parent 0.3.0.Beta1-SNAPSHOT + ../../pom.xml a2a-java-sdk-client diff --git a/client/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java similarity index 100% rename from client/src/main/java/io/a2a/A2A.java rename to client/base/src/main/java/io/a2a/A2A.java diff --git a/client/src/main/java/io/a2a/client/A2ACardResolver.java b/client/base/src/main/java/io/a2a/client/A2ACardResolver.java similarity index 100% rename from client/src/main/java/io/a2a/client/A2ACardResolver.java rename to client/base/src/main/java/io/a2a/client/A2ACardResolver.java diff --git a/client/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java similarity index 100% rename from client/src/main/java/io/a2a/client/AbstractClient.java rename to client/base/src/main/java/io/a2a/client/AbstractClient.java diff --git a/client/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java similarity index 100% rename from client/src/main/java/io/a2a/client/Client.java rename to client/base/src/main/java/io/a2a/client/Client.java diff --git a/client/src/main/java/io/a2a/client/ClientEvent.java b/client/base/src/main/java/io/a2a/client/ClientEvent.java similarity index 100% rename from client/src/main/java/io/a2a/client/ClientEvent.java rename to client/base/src/main/java/io/a2a/client/ClientEvent.java diff --git a/client/src/main/java/io/a2a/client/ClientFactory.java b/client/base/src/main/java/io/a2a/client/ClientFactory.java similarity index 100% rename from client/src/main/java/io/a2a/client/ClientFactory.java rename to client/base/src/main/java/io/a2a/client/ClientFactory.java diff --git a/client/src/main/java/io/a2a/client/ClientTaskManager.java b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java similarity index 100% rename from client/src/main/java/io/a2a/client/ClientTaskManager.java rename to client/base/src/main/java/io/a2a/client/ClientTaskManager.java diff --git a/client/src/main/java/io/a2a/client/MessageEvent.java b/client/base/src/main/java/io/a2a/client/MessageEvent.java similarity index 100% rename from client/src/main/java/io/a2a/client/MessageEvent.java rename to client/base/src/main/java/io/a2a/client/MessageEvent.java diff --git a/client/src/main/java/io/a2a/client/TaskEvent.java b/client/base/src/main/java/io/a2a/client/TaskEvent.java similarity index 100% rename from client/src/main/java/io/a2a/client/TaskEvent.java rename to client/base/src/main/java/io/a2a/client/TaskEvent.java diff --git a/client/src/main/java/io/a2a/client/TaskUpdateEvent.java b/client/base/src/main/java/io/a2a/client/TaskUpdateEvent.java similarity index 100% rename from client/src/main/java/io/a2a/client/TaskUpdateEvent.java rename to client/base/src/main/java/io/a2a/client/TaskUpdateEvent.java diff --git a/client/src/main/resources/META-INF/beans.xml b/client/base/src/main/resources/META-INF/beans.xml similarity index 100% rename from client/src/main/resources/META-INF/beans.xml rename to client/base/src/main/resources/META-INF/beans.xml diff --git a/client/src/test/java/io/a2a/client/A2ACardResolverTest.java b/client/base/src/test/java/io/a2a/client/A2ACardResolverTest.java similarity index 100% rename from client/src/test/java/io/a2a/client/A2ACardResolverTest.java rename to client/base/src/test/java/io/a2a/client/A2ACardResolverTest.java diff --git a/client/src/test/java/io/a2a/client/JsonMessages.java b/client/base/src/test/java/io/a2a/client/JsonMessages.java similarity index 100% rename from client/src/test/java/io/a2a/client/JsonMessages.java rename to client/base/src/test/java/io/a2a/client/JsonMessages.java diff --git a/pom.xml b/pom.xml index 36e39cd41..985a4492b 100644 --- a/pom.xml +++ b/pom.xml @@ -279,7 +279,7 @@ - client + client/base client-config http-client client-transport/grpc From d3f66ba6d06ab12f33e7e7ef505de1b8c6133cd3 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 18 Aug 2025 10:33:47 -0400 Subject: [PATCH 072/493] fix: Move client-config to client/config --- {client-config => client/config}/pom.xml | 1 + .../src/main/java/io/a2a/client/config/ClientCallContext.java | 0 .../main/java/io/a2a/client/config/ClientCallInterceptor.java | 0 .../src/main/java/io/a2a/client/config/ClientConfig.java | 0 .../src/main/java/io/a2a/client/config/PayloadAndHeaders.java | 0 pom.xml | 2 +- 6 files changed, 2 insertions(+), 1 deletion(-) rename {client-config => client/config}/pom.xml (96%) rename {client-config => client/config}/src/main/java/io/a2a/client/config/ClientCallContext.java (100%) rename {client-config => client/config}/src/main/java/io/a2a/client/config/ClientCallInterceptor.java (100%) rename {client-config => client/config}/src/main/java/io/a2a/client/config/ClientConfig.java (100%) rename {client-config => client/config}/src/main/java/io/a2a/client/config/PayloadAndHeaders.java (100%) diff --git a/client-config/pom.xml b/client/config/pom.xml similarity index 96% rename from client-config/pom.xml rename to client/config/pom.xml index df0f2d650..560671fac 100644 --- a/client-config/pom.xml +++ b/client/config/pom.xml @@ -8,6 +8,7 @@ io.github.a2asdk a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT + ../../pom.xml a2a-java-sdk-client-config diff --git a/client-config/src/main/java/io/a2a/client/config/ClientCallContext.java b/client/config/src/main/java/io/a2a/client/config/ClientCallContext.java similarity index 100% rename from client-config/src/main/java/io/a2a/client/config/ClientCallContext.java rename to client/config/src/main/java/io/a2a/client/config/ClientCallContext.java diff --git a/client-config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java b/client/config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java similarity index 100% rename from client-config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java rename to client/config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java diff --git a/client-config/src/main/java/io/a2a/client/config/ClientConfig.java b/client/config/src/main/java/io/a2a/client/config/ClientConfig.java similarity index 100% rename from client-config/src/main/java/io/a2a/client/config/ClientConfig.java rename to client/config/src/main/java/io/a2a/client/config/ClientConfig.java diff --git a/client-config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java b/client/config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java similarity index 100% rename from client-config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java rename to client/config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java diff --git a/pom.xml b/pom.xml index 985a4492b..e3a1d484c 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,7 @@ client/base - client-config + client/config http-client client-transport/grpc client-transport/jsonrpc From 4f4bc2d9a90ad39396bd71a5651aa54e4115328c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 18 Aug 2025 10:37:35 -0400 Subject: [PATCH 073/493] fix: Move the contents of client-transport to client/transport --- {client-transport => client/transport}/grpc/pom.xml | 2 +- .../io/a2a/client/transport/grpc/EventStreamObserver.java | 0 .../java/io/a2a/client/transport/grpc/GrpcTransport.java | 0 .../a2a/client/transport/grpc/GrpcTransportProvider.java | 0 .../io.a2a.client.transport.spi.ClientTransportProvider | 0 {client-transport => client/transport}/jsonrpc/pom.xml | 2 +- .../io/a2a/client/transport/jsonrpc/JSONRPCTransport.java | 0 .../transport/jsonrpc/JSONRPCTransportProvider.java | 0 .../client/transport/jsonrpc/sse/SSEEventListener.java | 0 .../io.a2a.client.transport.spi.ClientTransportProvider | 0 .../transport/jsonrpc/JSONRPCTransportStreamingTest.java | 0 .../client/transport/jsonrpc/JSONRPCTransportTest.java | 0 .../io/a2a/client/transport/jsonrpc/JsonMessages.java | 0 .../client/transport/jsonrpc/JsonStreamingMessages.java | 0 .../transport/jsonrpc/sse/SSEEventListenerTest.java | 0 {client-transport => client/transport}/spi/pom.xml | 2 +- .../java/io/a2a/client/transport/spi/ClientTransport.java | 0 .../a2a/client/transport/spi/ClientTransportProvider.java | 0 pom.xml | 8 ++++---- transport/grpc/pom.xml | 5 ----- transport/jsonrpc/pom.xml | 5 ----- 21 files changed, 7 insertions(+), 17 deletions(-) rename {client-transport => client/transport}/grpc/pom.xml (97%) rename {client-transport => client/transport}/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java (100%) rename {client-transport => client/transport}/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java (100%) rename {client-transport => client/transport}/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java (100%) rename {client-transport => client/transport}/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider (100%) rename {client-transport => client/transport}/jsonrpc/pom.xml (97%) rename {client-transport => client/transport}/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java (100%) rename {client-transport => client/transport}/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java (100%) rename {client-transport => client/transport}/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java (100%) rename {client-transport => client/transport}/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider (100%) rename {client-transport => client/transport}/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java (100%) rename {client-transport => client/transport}/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java (100%) rename {client-transport => client/transport}/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java (100%) rename {client-transport => client/transport}/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java (100%) rename {client-transport => client/transport}/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java (100%) rename {client-transport => client/transport}/spi/pom.xml (95%) rename {client-transport => client/transport}/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java (100%) rename {client-transport => client/transport}/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java (100%) diff --git a/client-transport/grpc/pom.xml b/client/transport/grpc/pom.xml similarity index 97% rename from client-transport/grpc/pom.xml rename to client/transport/grpc/pom.xml index aaf5f734b..58cb9e9a9 100644 --- a/client-transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -8,7 +8,7 @@ io.github.a2asdk a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT - ../../pom.xml + ../../../pom.xml a2a-java-sdk-client-transport-grpc jar diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java similarity index 100% rename from client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java rename to client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java similarity index 100% rename from client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java rename to client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java diff --git a/client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java similarity index 100% rename from client-transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java rename to client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java diff --git a/client-transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider b/client/transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider similarity index 100% rename from client-transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider rename to client/transport/grpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider diff --git a/client-transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml similarity index 97% rename from client-transport/jsonrpc/pom.xml rename to client/transport/jsonrpc/pom.xml index 5230fed98..8bcac1e01 100644 --- a/client-transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -8,7 +8,7 @@ io.github.a2asdk a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT - ../../pom.xml + ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc jar diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java similarity index 100% rename from client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java rename to client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java similarity index 100% rename from client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java rename to client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java diff --git a/client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java similarity index 100% rename from client-transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java rename to client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java diff --git a/client-transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider b/client/transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider similarity index 100% rename from client-transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider rename to client/transport/jsonrpc/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java similarity index 100% rename from client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java rename to client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java similarity index 100% rename from client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java rename to client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java similarity index 100% rename from client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java rename to client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java similarity index 100% rename from client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java rename to client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java diff --git a/client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java similarity index 100% rename from client-transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java rename to client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java diff --git a/client-transport/spi/pom.xml b/client/transport/spi/pom.xml similarity index 95% rename from client-transport/spi/pom.xml rename to client/transport/spi/pom.xml index 7c4b112f0..d05c088a6 100644 --- a/client-transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -8,7 +8,7 @@ io.github.a2asdk a2a-java-sdk-parent 0.2.6.Beta1-SNAPSHOT - ../../pom.xml + ../../../pom.xml a2a-java-sdk-client-transport-spi jar diff --git a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java similarity index 100% rename from client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java rename to client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java diff --git a/client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java similarity index 100% rename from client-transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java rename to client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java diff --git a/pom.xml b/pom.xml index e3a1d484c..b5fa46a08 100644 --- a/pom.xml +++ b/pom.xml @@ -281,12 +281,12 @@ client/base client/config - http-client - client-transport/grpc - client-transport/jsonrpc - client-transport/spi + client/transport/grpc + client/transport/jsonrpc + client/transport/spi common examples/helloworld + http-client reference/common reference/grpc reference/jsonrpc diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 7bcc7149d..93ec2a095 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -18,11 +18,6 @@ Java SDK for the Agent2Agent Protocol (A2A) - gRPC - - io.github.a2asdk - a2a-java-sdk-transport-spi - ${project.version} - io.github.a2asdk a2a-java-sdk-server-common diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index d0f3304fe..15eecd06e 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -18,11 +18,6 @@ Java SDK for the Agent2Agent Protocol (A2A) - JSONRPC - - io.github.a2asdk - a2a-java-sdk-transport-spi - ${project.version} - io.github.a2asdk a2a-java-sdk-server-common From e03ec0551b1c54ead763b38d225b6542b877543a Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 18 Aug 2025 12:28:20 -0400 Subject: [PATCH 074/493] fix: Introduce a ClientTransportConfig interface, update ClientConfig to make use of it, and remove the http-client and grpc dependencies from the client config module --- client/config/pom.xml | 9 ------ .../io/a2a/client/config/ClientConfig.java | 32 ++++++------------- .../client/config/ClientTransportConfig.java | 7 ++++ .../transport/grpc/GrpcTransportConfig.java | 17 ++++++++++ .../transport/grpc/GrpcTransportProvider.java | 17 +++++++++- .../jsonrpc/JSONRPCTransportConfig.java | 17 ++++++++++ .../jsonrpc/JSONRPCTransportProvider.java | 14 +++++++- 7 files changed, 79 insertions(+), 34 deletions(-) create mode 100644 client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java create mode 100644 client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java create mode 100644 client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java diff --git a/client/config/pom.xml b/client/config/pom.xml index 560671fac..b20757697 100644 --- a/client/config/pom.xml +++ b/client/config/pom.xml @@ -18,11 +18,6 @@ Java SDK for the Agent2Agent Protocol (A2A) - Client Configuration - - ${project.groupId} - a2a-java-sdk-http-client - ${project.version} - ${project.groupId} a2a-java-sdk-spec @@ -39,10 +34,6 @@ mockserver-netty test - - io.grpc - grpc-api - \ No newline at end of file diff --git a/client/config/src/main/java/io/a2a/client/config/ClientConfig.java b/client/config/src/main/java/io/a2a/client/config/ClientConfig.java index ef8cc948d..913495175 100644 --- a/client/config/src/main/java/io/a2a/client/config/ClientConfig.java +++ b/client/config/src/main/java/io/a2a/client/config/ClientConfig.java @@ -3,9 +3,7 @@ import java.util.List; import java.util.Map; -import io.a2a.client.http.A2AHttpClient; import io.a2a.spec.PushNotificationConfig; -import io.grpc.Channel; /** * Configuration for the A2A client factory. @@ -14,8 +12,7 @@ public class ClientConfig { private final Boolean streaming; private final Boolean polling; - private final A2AHttpClient httpClient; - private final Channel channel; + private final List clientTransportConfigs; private final List supportedTransports; private final Boolean useClientPreference; private final List acceptedOutputModes; @@ -23,14 +20,13 @@ public class ClientConfig { private final Integer historyLength; private final Map metadata; - public ClientConfig(Boolean streaming, Boolean polling, A2AHttpClient httpClient, Channel channel, + public ClientConfig(Boolean streaming, Boolean polling, List clientTransportConfigs, List supportedTransports, Boolean useClientPreference, List acceptedOutputModes, PushNotificationConfig pushNotificationConfig, Integer historyLength, Map metadata) { this.streaming = streaming == null ? true : streaming; this.polling = polling == null ? false : polling; - this.httpClient = httpClient; - this.channel = channel; + this.clientTransportConfigs = clientTransportConfigs; this.supportedTransports = supportedTransports; this.useClientPreference = useClientPreference == null ? false : useClientPreference; this.acceptedOutputModes = acceptedOutputModes; @@ -47,12 +43,8 @@ public boolean isPolling() { return polling; } - public A2AHttpClient getHttpClient() { - return httpClient; - } - - public Channel getChannel() { - return channel; + public List getClientTransportConfigs() { + return clientTransportConfigs; } public List getSupportedTransports() { @@ -82,8 +74,7 @@ public Map getMetadata() { public static class Builder { private Boolean streaming; private Boolean polling; - private A2AHttpClient httpClient; - private Channel channel; + private List clientTransportConfigs; private List supportedTransports; private Boolean useClientPreference; private List acceptedOutputModes; @@ -101,13 +92,8 @@ public Builder setPolling(Boolean polling) { return this; } - public Builder setHttpClient(A2AHttpClient httpClient) { - this.httpClient = httpClient; - return this; - } - - public Builder setChannel(Channel channel) { - this.channel = channel; + public Builder setClientTransportConfigs(List clientTransportConfigs) { + this.clientTransportConfigs = clientTransportConfigs; return this; } @@ -142,7 +128,7 @@ public Builder setMetadata(Map metadata) { } public ClientConfig build() { - return new ClientConfig(streaming, polling, httpClient, channel, + return new ClientConfig(streaming, polling, clientTransportConfigs, supportedTransports, useClientPreference, acceptedOutputModes, pushNotificationConfig, historyLength, metadata); } diff --git a/client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java b/client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java new file mode 100644 index 000000000..560b69afb --- /dev/null +++ b/client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java @@ -0,0 +1,7 @@ +package io.a2a.client.config; + +/** + * Configuration for an A2A client transport. + */ +public interface ClientTransportConfig { +} \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java new file mode 100644 index 000000000..4b0fd1930 --- /dev/null +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java @@ -0,0 +1,17 @@ +package io.a2a.client.transport.grpc; + +import io.a2a.client.config.ClientTransportConfig; +import io.grpc.ManagedChannelBuilder; + +public class GrpcTransportConfig implements ClientTransportConfig { + + private final ManagedChannelBuilder channelBuilder; + + public GrpcTransportConfig(ManagedChannelBuilder channelBuilder) { + this.channelBuilder = channelBuilder; + } + + public ManagedChannelBuilder getManagedChannelBuilder() { + return channelBuilder; + } +} \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java index cdaffed88..0ee3d1d1e 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -4,10 +4,13 @@ import io.a2a.client.config.ClientCallInterceptor; import io.a2a.client.config.ClientConfig; +import io.a2a.client.config.ClientTransportConfig; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.AgentCard; import io.a2a.spec.TransportProtocol; +import io.grpc.Channel; +import io.grpc.ManagedChannelBuilder; /** * Provider for gRPC transport implementation. @@ -18,7 +21,19 @@ public class GrpcTransportProvider implements ClientTransportProvider { public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, String agentUrl, List interceptors) { // not making use of the interceptors for gRPC for now - return new GrpcTransport(clientConfig.getChannel(), agentCard); + ManagedChannelBuilder managedChannelBuilder = null; + List clientTransportConfigs = clientConfig.getClientTransportConfigs(); + if (clientTransportConfigs != null) { + for (ClientTransportConfig clientTransportConfig : clientTransportConfigs) { + if (clientTransportConfig instanceof GrpcTransportConfig grpcTransportConfig) { + managedChannelBuilder = grpcTransportConfig.getManagedChannelBuilder(); + break; + } + } + } + Channel channel = managedChannelBuilder == null ? ManagedChannelBuilder.forTarget(agentUrl).build() + : managedChannelBuilder.forTarget(agentUrl).build(); + return new GrpcTransport(channel, agentCard); } @Override diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java new file mode 100644 index 000000000..1627b9090 --- /dev/null +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java @@ -0,0 +1,17 @@ +package io.a2a.client.transport.jsonrpc; + +import io.a2a.client.config.ClientTransportConfig; +import io.a2a.client.http.A2AHttpClient; + +public class JSONRPCTransportConfig implements ClientTransportConfig { + + private final A2AHttpClient httpClient; + + public JSONRPCTransportConfig(A2AHttpClient httpClient) { + this.httpClient = httpClient; + } + + public A2AHttpClient getHttpClient() { + return httpClient; + } +} \ No newline at end of file diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java index 3fe865515..a72a86294 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -4,6 +4,8 @@ import io.a2a.client.config.ClientCallInterceptor; import io.a2a.client.config.ClientConfig; +import io.a2a.client.config.ClientTransportConfig; +import io.a2a.client.http.A2AHttpClient; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.AgentCard; @@ -14,7 +16,17 @@ public class JSONRPCTransportProvider implements ClientTransportProvider { @Override public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, String agentUrl, List interceptors) { - return new JSONRPCTransport(clientConfig.getHttpClient(), agentCard, agentUrl, interceptors); + A2AHttpClient httpClient = null; + List clientTransportConfigs = clientConfig.getClientTransportConfigs(); + if (clientTransportConfigs != null) { + for (ClientTransportConfig clientTransportConfig : clientTransportConfigs) { + if (clientTransportConfig instanceof JSONRPCTransportConfig jsonrpcTransportConfig) { + httpClient = jsonrpcTransportConfig.getHttpClient(); + break; + } + } + } + return new JSONRPCTransport(httpClient, agentCard, agentUrl, interceptors); } @Override From daab8b09b3f561c533a6073abf1cc4950c9975af Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 18 Aug 2025 13:53:25 -0400 Subject: [PATCH 075/493] fix: Additional updates to the client and update AbstractA2AServerTest to be able to make use of the appropriate client based on the transport and update the JSONRPC and gRPC tests to extend this --- .../java/io/a2a/client/AbstractClient.java | 44 +- .../src/main/java/io/a2a/client/Client.java | 97 +- .../java/io/a2a/client/ClientFactory.java | 10 +- client/config/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- .../transport/grpc/EventStreamObserver.java | 7 +- .../transport/grpc/GrpcErrorMapper.java | 71 + .../client/transport/grpc/GrpcTransport.java | 23 +- .../transport/grpc/GrpcTransportConfig.java | 15 +- .../transport/grpc/GrpcTransportProvider.java | 10 +- client/transport/jsonrpc/pom.xml | 2 +- .../transport/jsonrpc/JSONRPCTransport.java | 16 +- .../jsonrpc/JSONRPCTransportTest.java | 27 +- .../transport/jsonrpc/JsonMessages.java | 363 +++-- client/transport/spi/pom.xml | 2 +- http-client/pom.xml | 2 +- reference/grpc/pom.xml | 11 + .../grpc/quarkus/QuarkusA2AGrpcTest.java | 1246 +---------------- .../src/test/resources/application.properties | 5 +- .../apps/quarkus/QuarkusA2AJSONRPCTest.java | 168 +++ .../java/io/a2a/grpc/utils/ProtoUtils.java | 25 +- tests/server-common/pom.xml | 8 +- .../apps/common/AbstractA2AServerTest.java | 898 ++++++------ 23 files changed, 1115 insertions(+), 1939 deletions(-) create mode 100644 client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index f6794ab38..ae25a0c8b 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -45,7 +45,7 @@ public AbstractClient(List> consumers, Consum * Send a message to the remote agent. This method will automatically use * the streaming or non-streaming approach as determined by the server's * agent card and the client configuration. The configured client consumers - * and will be used to handle messages, tasks, and update events received + * will be used to handle messages, tasks, and update events received * from the remote agent. The configured streaming error handler will be used * if an error occurs during streaming. The configured client push notification * configuration will get used for streaming. @@ -56,6 +56,26 @@ public AbstractClient(List> consumers, Consum */ public abstract void sendMessage(Message request, ClientCallContext context) throws A2AClientException; + /** + * Send a message to the remote agent. This method will automatically use + * the streaming or non-streaming approach as determined by the server's + * agent card and the client configuration. The specified client consumers + * will be used to handle messages, tasks, and update events received + * from the remote agent. The specified streaming error handler will be used + * if an error occurs during streaming. The configured client push notification + * configuration will get used for streaming. + * + * @param request the message + * @param consumers a list of consumers to pass responses from the remote agent to + * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if sending the message fails for any reason + */ + public abstract void sendMessage(Message request, + List> consumers, + Consumer streamingErrorHandler, + ClientCallContext context) throws A2AClientException; + /** * Send a message to the remote agent. This method will automatically use * the streaming or non-streaming approach as determined by the server's @@ -143,6 +163,9 @@ public abstract void deleteTaskPushNotificationConfigurations( /** * Resubscribe to a task's event stream. * This is only available if both the client and server support streaming. + * The configured client consumers will be used to handle messages, tasks, + * and update events received from the remote agent. The configured streaming + * error handler will be used if an error occurs during streaming. * * @param request the parameters specifying which task's notification configs to delete * @param context optional client call context for the request (may be {@code null}) @@ -150,6 +173,23 @@ public abstract void deleteTaskPushNotificationConfigurations( */ public abstract void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException; + /** + * Resubscribe to a task's event stream. + * This is only available if both the client and server support streaming. + * The specified client consumers will be used to handle messages, tasks, and + * update events received from the remote agent. The specified streaming error + * handler will be used if an error occurs during streaming. + * + * @param request the parameters specifying which task's notification configs to delete + * @param consumers a list of consumers to pass responses from the remote agent to + * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if resubscribing fails for any reason + */ + public abstract void resubscribe(TaskIdParams request, List> consumers, + Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException; + + /** * Retrieve the AgentCard. * @@ -167,7 +207,7 @@ public abstract void deleteTaskPushNotificationConfigurations( /** * Process the event using all configured consumers. */ - public void consume(ClientEvent clientEventOrMessage, AgentCard agentCard) { + void consume(ClientEvent clientEventOrMessage, AgentCard agentCard) { for (BiConsumer consumer : consumers) { consumer.accept(clientEventOrMessage, agentCard); } diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index 5dc57b56a..e3d0c556d 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -58,7 +58,26 @@ public void sendMessage(Message request, ClientCallContext context) throws A2ACl .metadata(clientConfig.getMetadata()) .build(); - sendMessage(messageSendParams, context); + sendMessage(messageSendParams, null, null, context); + } + + @Override + public void sendMessage(Message request, List> consumers, + Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException { + MessageSendConfiguration messageSendConfiguration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) + .blocking(clientConfig.isPolling()) + .historyLength(clientConfig.getHistoryLength()) + .pushNotification(clientConfig.getPushNotificationConfig()) + .build(); + + MessageSendParams messageSendParams = new MessageSendParams.Builder() + .message(request) + .configuration(messageSendConfiguration) + .metadata(clientConfig.getMetadata()) + .build(); + + sendMessage(messageSendParams, consumers, streamingErrorHandler, context); } @Override @@ -77,7 +96,7 @@ public void sendMessage(Message request, PushNotificationConfig pushNotification .metadata(metatadata) .build(); - sendMessage(messageSendParams, context); + sendMessage(messageSendParams, null, null, context); } @Override @@ -116,19 +135,13 @@ public void deleteTaskPushNotificationConfigurations( @Override public void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException { - if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { - throw new A2AClientException("Client and/or server does not support resubscription"); - } - ClientTaskManager tracker = new ClientTaskManager(); - Consumer eventHandler = event -> { - try { - ClientEvent clientEvent = getClientEvent(event, tracker); - consume(clientEvent, agentCard); - } catch (A2AClientError e) { - getStreamingErrorHandler().accept(e); - } - }; - clientTransport.resubscribe(request, eventHandler, getStreamingErrorHandler(), context); + resubscribeToTask(request, null, null, context); + } + + @Override + public void resubscribe(TaskIdParams request, List> consumers, + Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException { + resubscribeToTask(request, consumers, streamingErrorHandler, context); } @Override @@ -159,7 +172,8 @@ private ClientEvent getClientEvent(StreamingEventKind event, ClientTaskManager t } } - private void sendMessage(MessageSendParams messageSendParams, ClientCallContext context) throws A2AClientException { + private void sendMessage(MessageSendParams messageSendParams, List> consumers, + Consumer errorHandler, ClientCallContext context) throws A2AClientException { if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { EventKind eventKind = clientTransport.sendMessage(messageSendParams, context); ClientEvent clientEvent; @@ -169,18 +183,61 @@ private void sendMessage(MessageSendParams messageSendParams, ClientCallContext // must be a message clientEvent = new MessageEvent((Message) eventKind); } - consume(clientEvent, agentCard); + consume(clientEvent, agentCard, consumers); } else { ClientTaskManager tracker = new ClientTaskManager(); + Consumer overriddenErrorHandler = getOverriddenErrorHandler(errorHandler); Consumer eventHandler = event -> { try { ClientEvent clientEvent = getClientEvent(event, tracker); - consume(clientEvent, agentCard); + consume(clientEvent, agentCard, consumers); } catch (A2AClientError e) { - getStreamingErrorHandler().accept(e); + overriddenErrorHandler.accept(e); } }; - clientTransport.sendMessageStreaming(messageSendParams, eventHandler, getStreamingErrorHandler(), context); + clientTransport.sendMessageStreaming(messageSendParams, eventHandler, overriddenErrorHandler, context); + } + } + + private void resubscribeToTask(TaskIdParams request, List> consumers, + Consumer errorHandler, ClientCallContext context) throws A2AClientException { + if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { + throw new A2AClientException("Client and/or server does not support resubscription"); + } + ClientTaskManager tracker = new ClientTaskManager(); + Consumer overriddenErrorHandler = getOverriddenErrorHandler(errorHandler); + Consumer eventHandler = event -> { + try { + ClientEvent clientEvent = getClientEvent(event, tracker); + consume(clientEvent, agentCard, consumers); + } catch (A2AClientError e) { + overriddenErrorHandler.accept(e); + } + }; + clientTransport.resubscribe(request, eventHandler, overriddenErrorHandler, context); + } + + private Consumer getOverriddenErrorHandler(Consumer errorHandler) { + return e -> { + if (errorHandler != null) { + errorHandler.accept(e); + } else { + if (getStreamingErrorHandler() != null) { + getStreamingErrorHandler().accept(e); + } + } + }; + } + + private void consume(ClientEvent clientEvent, AgentCard agentCard, List> consumers) { + if (consumers != null) { + // use specified consumers + for (BiConsumer consumer : consumers) { + consumer.accept(clientEvent, agentCard); + } + } else { + // use configured consumers + consume(clientEvent, agentCard); } } } diff --git a/client/base/src/main/java/io/a2a/client/ClientFactory.java b/client/base/src/main/java/io/a2a/client/ClientFactory.java index 6789b76ac..71c1a6020 100644 --- a/client/base/src/main/java/io/a2a/client/ClientFactory.java +++ b/client/base/src/main/java/io/a2a/client/ClientFactory.java @@ -42,10 +42,11 @@ public ClientFactory(ClientConfig clientConfig) { * @param agentCard the agent card for the remote agent * @param consumers a list of consumers to pass responses from the remote agent to * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @return the client to use * @throws A2AClientException if the client cannot be created for any reason */ - public AbstractClient create(AgentCard agentCard, List> consumers, - Consumer streamingErrorHandler) throws A2AClientException { + public Client create(AgentCard agentCard, List> consumers, + Consumer streamingErrorHandler) throws A2AClientException { return create(agentCard, consumers, streamingErrorHandler, null); } @@ -56,10 +57,11 @@ public AbstractClient create(AgentCard agentCard, List> consumers, - Consumer streamingErrorHandler, List interceptors) throws A2AClientException { + public Client create(AgentCard agentCard, List> consumers, + Consumer streamingErrorHandler, List interceptors) throws A2AClientException { checkNotNullParam("agentCard", agentCard); checkNotNullParam("consumers", consumers); LinkedHashMap serverPreferredTransports = getServerPreferredTransports(agentCard); diff --git a/client/config/pom.xml b/client/config/pom.xml index b20757697..a44194654 100644 --- a/client/config/pom.xml +++ b/client/config/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-client-config diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 58cb9e9a9..ad4a5b12e 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java index 4edc4a3f5..627286607 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java @@ -48,7 +48,12 @@ public void onNext(StreamResponse response) { @Override public void onError(Throwable t) { if (errorHandler != null) { - errorHandler.accept(t); + // Map gRPC errors to proper A2A exceptions + if (t instanceof io.grpc.StatusRuntimeException) { + errorHandler.accept(GrpcErrorMapper.mapGrpcError((io.grpc.StatusRuntimeException) t)); + } else { + errorHandler.accept(t); + } } } diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java new file mode 100644 index 000000000..7340f7cef --- /dev/null +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java @@ -0,0 +1,71 @@ +package io.a2a.client.transport.grpc; + +import io.a2a.spec.A2AClientException; +import io.a2a.spec.ContentTypeNotSupportedError; +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.grpc.Status; +import io.grpc.StatusRuntimeException; + +/** + * Utility class to map gRPC StatusRuntimeException to appropriate A2A error types + */ +public class GrpcErrorMapper { + + public static A2AClientException mapGrpcError(StatusRuntimeException e) { + return mapGrpcError(e, "gRPC error: "); + } + + public static A2AClientException mapGrpcError(StatusRuntimeException e, String errorPrefix) { + Status.Code code = e.getStatus().getCode(); + String description = e.getStatus().getDescription(); + + // Extract the actual error type from the description if possible + // (using description because the same code can map to multiple errors - + // see GrpcHandler#handleError) + if (description != null) { + if (description.contains("TaskNotFoundError")) { + return new A2AClientException(errorPrefix + description, new TaskNotFoundError()); + } else if (description.contains("UnsupportedOperationError")) { + return new A2AClientException(errorPrefix + description, new UnsupportedOperationError()); + } else if (description.contains("InvalidParamsError")) { + return new A2AClientException(errorPrefix + description, new InvalidParamsError()); + } else if (description.contains("InvalidRequestError")) { + return new A2AClientException(errorPrefix + description, new InvalidRequestError()); + } else if (description.contains("MethodNotFoundError")) { + return new A2AClientException(errorPrefix + description, new MethodNotFoundError()); + } else if (description.contains("TaskNotCancelableError")) { + return new A2AClientException(errorPrefix + description, new TaskNotCancelableError()); + } else if (description.contains("PushNotificationNotSupportedError")) { + return new A2AClientException(errorPrefix + description, new PushNotificationNotSupportedError()); + } else if (description.contains("JSONParseError")) { + return new A2AClientException(errorPrefix + description, new JSONParseError()); + } else if (description.contains("ContentTypeNotSupportedError")) { + return new A2AClientException(errorPrefix + description, new ContentTypeNotSupportedError(null, description, null)); + } else if (description.contains("InvalidAgentResponseError")) { + return new A2AClientException(errorPrefix + description, new InvalidAgentResponseError(null, description, null)); + } + } + + // Fall back to mapping based on status code + switch (code) { + case NOT_FOUND: + return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new TaskNotFoundError()); + case UNIMPLEMENTED: + return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new UnsupportedOperationError()); + case INVALID_ARGUMENT: + return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new InvalidParamsError()); + case INTERNAL: + return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new io.a2a.spec.InternalError(null, e.getMessage(), null)); + default: + return new A2AClientException(errorPrefix + e.getMessage(), e); + } + } +} diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 903eda965..f709ed362 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -70,7 +70,7 @@ public EventKind sendMessage(MessageSendParams request, ClientCallContext contex throw new A2AClientException("Server response did not contain a message or task"); } } catch (StatusRuntimeException e) { - throw new A2AClientException("Failed to send message: " + e.getMessage(), e); + throw GrpcErrorMapper.mapGrpcError(e, "Failed to send message: "); } } @@ -85,7 +85,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations( .map(FromProto::taskPushNotificationConfig) .collect(Collectors.toList()); } catch (StatusRuntimeException e) { - throw new A2AClientException("Failed to list task push notification configs: " + e.getMessage(), e); + throw GrpcErrorMapper.mapGrpcError(e, "Failed to list task push notification config: "); } } @@ -189,7 +189,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC try { blockingStub.deleteTaskPushNotificationConfig(grpcRequest); } catch (StatusRuntimeException e) { - throw new A2AClientException("Failed to delete task push notification configs: " + e.getMessage(), e); + throw GrpcErrorMapper.mapGrpcError(e, "Failed to delete task push notification config: "); } } @@ -208,7 +208,7 @@ public void resubscribe(TaskIdParams request, Consumer event try { asyncStub.taskSubscription(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { - throw new A2AClientException("Failed to resubscribe to task: " + e.getMessage(), e); + throw GrpcErrorMapper.mapGrpcError(e, "Failed to resubscribe task push notification config: "); } } @@ -246,6 +246,9 @@ private String getTaskPushNotificationConfigName(String taskId, String pushNotif name.append("/pushNotificationConfigs/"); name.append(pushNotificationConfigId); } + //name.append("/pushNotificationConfigs/"); + // Use taskId as default config ID if none provided + //name.append(pushNotificationConfigId != null ? pushNotificationConfigId : taskId); return name.toString(); } diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java index 4b0fd1930..5717f59b7 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java @@ -1,17 +1,20 @@ package io.a2a.client.transport.grpc; +import java.util.function.Function; + import io.a2a.client.config.ClientTransportConfig; -import io.grpc.ManagedChannelBuilder; +import io.grpc.Channel; public class GrpcTransportConfig implements ClientTransportConfig { - private final ManagedChannelBuilder channelBuilder; + private final Function channelFactory; - public GrpcTransportConfig(ManagedChannelBuilder channelBuilder) { - this.channelBuilder = channelBuilder; + public GrpcTransportConfig(Function channelFactory) { + this.channelFactory = channelFactory; } - public ManagedChannelBuilder getManagedChannelBuilder() { - return channelBuilder; + public Function getChannelFactory() { + return channelFactory; } + } \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java index 0ee3d1d1e..18e66fdb6 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -21,18 +21,18 @@ public class GrpcTransportProvider implements ClientTransportProvider { public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, String agentUrl, List interceptors) { // not making use of the interceptors for gRPC for now - ManagedChannelBuilder managedChannelBuilder = null; + Channel channel; List clientTransportConfigs = clientConfig.getClientTransportConfigs(); if (clientTransportConfigs != null) { for (ClientTransportConfig clientTransportConfig : clientTransportConfigs) { if (clientTransportConfig instanceof GrpcTransportConfig grpcTransportConfig) { - managedChannelBuilder = grpcTransportConfig.getManagedChannelBuilder(); - break; + channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); + return new GrpcTransport(channel, agentCard); } } } - Channel channel = managedChannelBuilder == null ? ManagedChannelBuilder.forTarget(agentUrl).build() - : managedChannelBuilder.forTarget(agentUrl).build(); + // no channel factory configured + channel = ManagedChannelBuilder.forTarget(agentUrl).build(); return new GrpcTransport(channel, agentCard); } diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 8bcac1e01..3a1d5be8b 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 034584cec..c22690274 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -26,6 +26,8 @@ import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; @@ -67,8 +69,7 @@ public class JSONRPCTransport implements ClientTransport { private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; - // TODO: Uncomment once support for v0.3.0 has been merged - //private static final TypeReference GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE = new TypeReference<>() {}; private final A2AHttpClient httpClient; private final String agentUrl; @@ -335,12 +336,8 @@ public AgentCard getAgentCard(ClientCallContext context) throws A2AClientExcepti if (!agentCard.supportsAuthenticatedExtendedCard()) { return agentCard; } - resolver = new A2ACardResolver(httpClient, agentUrl, "/agent/authenticatedExtendedCard", - getHttpHeaders(context)); - agentCard = resolver.getAgentCard(); - // TODO: Uncomment this code once support for v0.3.0 has been merged and remove the above 3 lines - /*GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = new GetAuthenticatedExtendedCardRequest.Builder() + GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = new GetAuthenticatedExtendedCardRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .method(GetAuthenticatedExtendedCardRequest.METHOD) .build(); // id will be randomly generated @@ -351,12 +348,11 @@ public AgentCard getAgentCard(ClientCallContext context) throws A2AClientExcepti try { String httpResponseBody = sendPostRequest(payloadAndHeaders); GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody, - GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE); + GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE); return response.getResult(); } catch (IOException | InterruptedException e) { throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); - }*/ - return agentCard; + } } catch(A2AClientError e){ throw new A2AClientException("Failed to get agent card: " + e, e); } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 5e2dcaa77..99e5ef151 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -5,6 +5,8 @@ import static io.a2a.client.transport.jsonrpc.JsonMessages.AUTHENTICATION_EXTENDED_AGENT_CARD; import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_RESPONSE; +import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE; import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_TASK_TEST_REQUEST; @@ -39,6 +41,7 @@ import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; import io.a2a.spec.DataPart; @@ -47,6 +50,7 @@ import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; @@ -62,6 +66,7 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; +import io.a2a.spec.TransportProtocol; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -354,7 +359,7 @@ public void testA2AClientGetAgentCard() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/.well-known/agent.json") + .withPath("/.well-known/agent-card.json") ) .respond( response() @@ -415,7 +420,16 @@ public void testA2AClientGetAgentCard() throws Exception { assertEquals(outputModes, skills.get(1).outputModes()); assertFalse(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.5", agentCard.protocolVersion()); + assertEquals("0.2.9", agentCard.protocolVersion()); + assertEquals("JSONRPC", agentCard.preferredTransport()); + List additionalInterfaces = agentCard.additionalInterfaces(); + assertEquals(3, additionalInterfaces.size()); + AgentInterface jsonrpc = new AgentInterface(TransportProtocol.JSONRPC.asString(), "https://georoute-agent.example.com/a2a/v1"); + AgentInterface grpc = new AgentInterface(TransportProtocol.GRPC.asString(), "https://georoute-agent.example.com/a2a/grpc"); + AgentInterface httpJson = new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "https://georoute-agent.example.com/a2a/json"); + assertEquals(jsonrpc, additionalInterfaces.get(0)); + assertEquals(grpc, additionalInterfaces.get(1)); + assertEquals(httpJson, additionalInterfaces.get(2)); } @Test @@ -423,7 +437,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/.well-known/agent.json") + .withPath("/.well-known/agent-card.json") ) .respond( response() @@ -432,13 +446,14 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { ); this.server.when( request() - .withMethod("GET") - .withPath("/agent/authenticatedExtendedCard") + .withMethod("POST") + .withPath("/") + .withBody(JsonBody.json(GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( response() .withStatusCode(200) - .withBody(AUTHENTICATION_EXTENDED_AGENT_CARD) + .withBody(GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE) ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index b0a5fc111..59838012c 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -8,134 +8,80 @@ public class JsonMessages { static final String AGENT_CARD = """ { - "name": "GeoSpatial Route Planner Agent", - "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", - "provider": { - "organization": "Example Geo Services Inc.", - "url": "https://www.examplegeoservices.com" - }, - "iconUrl": "https://georoute-agent.example.com/icon.png", - "version": "1.2.0", - "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", - "capabilities": { - "streaming": true, - "pushNotifications": true, - "stateTransitionHistory": false - }, - "securitySchemes": { - "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" - } - }, - "security": [{ "google": ["openid", "profile", "email"] }], - "defaultInputModes": ["application/json", "text/plain"], - "defaultOutputModes": ["application/json", "image/png"], - "skills": [ - { - "id": "route-optimizer-traffic", - "name": "Traffic-Aware Route Optimizer", - "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", - "tags": ["maps", "routing", "navigation", "directions", "traffic"], - "examples": [ - "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", - "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" - ], - "inputModes": ["application/json", "text/plain"], - "outputModes": [ - "application/json", - "application/vnd.geo+json", - "text/html" - ] - }, - { - "id": "custom-map-generator", - "name": "Personalized Map Generator", - "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", - "tags": ["maps", "customization", "visualization", "cartography"], - "examples": [ - "Generate a map of my upcoming road trip with all planned stops highlighted.", - "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." - ], - "inputModes": ["application/json"], - "outputModes": [ - "image/png", - "image/jpeg", - "application/json", - "text/html" - ] - } - ], - "supportsAuthenticatedExtendedCard": false, - "protocolVersion": "0.2.5" - }"""; - - static final String AGENT_CARD_SUPPORTS_EXTENDED = """ - { - "name": "GeoSpatial Route Planner Agent", - "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", - "provider": { - "organization": "Example Geo Services Inc.", - "url": "https://www.examplegeoservices.com" - }, - "iconUrl": "https://georoute-agent.example.com/icon.png", - "version": "1.2.0", - "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", - "capabilities": { - "streaming": true, - "pushNotifications": true, - "stateTransitionHistory": false - }, - "securitySchemes": { - "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" - } - }, - "security": [{ "google": ["openid", "profile", "email"] }], - "defaultInputModes": ["application/json", "text/plain"], - "defaultOutputModes": ["application/json", "image/png"], - "skills": [ - { - "id": "route-optimizer-traffic", - "name": "Traffic-Aware Route Optimizer", - "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", - "tags": ["maps", "routing", "navigation", "directions", "traffic"], - "examples": [ - "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", - "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" - ], - "inputModes": ["application/json", "text/plain"], - "outputModes": [ - "application/json", - "application/vnd.geo+json", - "text/html" - ] - }, - { - "id": "custom-map-generator", - "name": "Personalized Map Generator", - "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", - "tags": ["maps", "customization", "visualization", "cartography"], - "examples": [ - "Generate a map of my upcoming road trip with all planned stops highlighted.", - "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." - ], - "inputModes": ["application/json"], - "outputModes": [ - "image/png", - "image/jpeg", - "application/json", - "text/html" - ] - } - ], - "supportsAuthenticatedExtendedCard": true, - "protocolVersion": "0.2.5" - }"""; - + "protocolVersion": "0.2.9", + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "preferredTransport": "JSONRPC", + "additionalInterfaces" : [ + {"url": "https://georoute-agent.example.com/a2a/v1", "transport": "JSONRPC"}, + {"url": "https://georoute-agent.example.com/a2a/grpc", "transport": "GRPC"}, + {"url": "https://georoute-agent.example.com/a2a/json", "transport": "HTTP+JSON"} + ], + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": false, + "signatures": [ + { + "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", + "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" + } + ] + }"""; static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """ { @@ -205,10 +151,15 @@ public class JsonMessages { } ], "supportsAuthenticatedExtendedCard": true, - "protocolVersion": "0.2.5" + "protocolVersion": "0.2.9", + "signatures": [ + { + "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", + "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" + } + ] }"""; - static final String SEND_MESSAGE_TEST_REQUEST = """ { "jsonrpc": "2.0", @@ -663,4 +614,156 @@ public class JsonMessages { } }"""; + static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "agent/getAuthenticatedExtendedCard" + } + """; + + static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": "1", + "result": { + "name": "GeoSpatial Route Planner Agent Extended", + "description": "Extended description", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + }, + { + "id": "skill-extended", + "name": "Extended Skill", + "description": "This is an extended skill.", + "tags": ["extended"] + } + ], + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5", + "signatures": [ + { + "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdUI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", + "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" + } + ] + } + }"""; + + static final String AGENT_CARD_SUPPORTS_EXTENDED = """ + { + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" + }"""; } diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index d05c088a6..a809c6dea 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/http-client/pom.xml b/http-client/pom.xml index 8ec988aa2..f331fba11 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.2.6.Beta1-SNAPSHOT + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-http-client diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 61d44ca8a..6a4ec4618 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -43,6 +43,12 @@ test ${project.version} + + ${project.groupId} + a2a-java-sdk-client-transport-grpc + ${project.version} + test + io.quarkus @@ -79,6 +85,11 @@ io.grpc grpc-stub + + io.rest-assured + rest-assured + test + \ No newline at end of file diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java index 30fb9c71c..1a91eebde 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -1,1245 +1,23 @@ package io.a2a.server.grpc.quarkus; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import io.a2a.grpc.A2AServiceGrpc; -import io.a2a.grpc.CancelTaskRequest; -import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; -import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskRequest; -import io.a2a.grpc.ListTaskPushNotificationConfigRequest; -import io.a2a.grpc.ListTaskPushNotificationConfigResponse; -import io.a2a.grpc.SendMessageRequest; -import io.a2a.grpc.SendMessageResponse; -import io.a2a.grpc.StreamResponse; -import io.a2a.grpc.TaskSubscriptionRequest; -import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.grpc.GetAgentCardRequest; -import io.a2a.spec.AgentCard; -import io.a2a.spec.Artifact; -import io.a2a.spec.Event; -import io.a2a.spec.Message; -import io.a2a.spec.Part; -import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.Task; -import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.spec.TaskArtifactUpdateEvent; -import io.a2a.spec.TaskState; -import io.a2a.spec.TaskStatus; -import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.spec.TextPart; -import io.a2a.util.Utils; -import io.grpc.Status; -import io.grpc.StatusRuntimeException; -import io.quarkus.grpc.GrpcClient; +import io.a2a.server.apps.common.AbstractA2AServerTest; +import io.a2a.spec.TransportProtocol; import io.quarkus.test.junit.QuarkusTest; -import org.junit.jupiter.api.Test; @QuarkusTest -public class QuarkusA2AGrpcTest { - - - private static final Task MINIMAL_TASK = new Task.Builder() - .id("task-123") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - private static final Task CANCEL_TASK = new Task.Builder() - .id("cancel-task-123") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - private static final Task CANCEL_TASK_NOT_SUPPORTED = new Task.Builder() - .id("cancel-task-not-supported-123") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - private static final Task SEND_MESSAGE_NOT_SUPPORTED = new Task.Builder() - .id("task-not-supported-123") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - private static final Message MESSAGE = new Message.Builder() - .messageId("111") - .role(Message.Role.AGENT) - .parts(new TextPart("test message")) - .build(); - public static final String APPLICATION_JSON = "application/json"; - - @GrpcClient("a2a-service") - A2AServiceGrpc.A2AServiceBlockingStub client; - - private final int serverPort = 8081; - @Test - public void testTaskStoreMethodsSanityTest() throws Exception { - Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); - saveTaskInTaskStore(task); - Task saved = getTaskFromTaskStore(task.getId()); - assertEquals(task.getId(), saved.getId()); - assertEquals(task.getContextId(), saved.getContextId()); - assertEquals(task.getStatus().state(), saved.getStatus().state()); - - deleteTaskInTaskStore(task.getId()); - Task saved2 = getTaskFromTaskStore(task.getId()); - assertNull(saved2); - } - - @Test - public void testGetTaskSuccess() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - GetTaskRequest request = GetTaskRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) - .build(); - io.a2a.grpc.Task response = client.getTask(request); - assertEquals("task-123", response.getId()); - assertEquals("session-xyz", response.getContextId()); - assertEquals(io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED, response.getStatus().getState()); - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testGetTaskNotFound() throws Exception { - assertTrue(getTaskFromTaskStore("non-existent-task") == null); - GetTaskRequest request = GetTaskRequest.newBuilder() - .setName("tasks/non-existent-task") - .build(); - try { - client.getTask(request); - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("TaskNotFoundError")); - } - } - - @Test - public void testCancelTaskSuccess() throws Exception { - saveTaskInTaskStore(CANCEL_TASK); - try { - CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + CANCEL_TASK.getId()) - .build(); - io.a2a.grpc.Task response = client.cancelTask(request); - assertEquals(CANCEL_TASK.getId(), response.getId()); - assertEquals(CANCEL_TASK.getContextId(), response.getContextId()); - assertEquals(io.a2a.grpc.TaskState.TASK_STATE_CANCELLED, response.getStatus().getState()); - } finally { - deleteTaskInTaskStore(CANCEL_TASK.getId()); - } - } - - @Test - public void testCancelTaskNotFound() throws Exception { - CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/non-existent-task") - .build(); - try { - client.cancelTask(request); - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("TaskNotFoundError")); - } - } - - @Test - public void testCancelTaskNotSupported() throws Exception { - saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); - try { - CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + CANCEL_TASK_NOT_SUPPORTED.getId()) - .build(); - try { - client.cancelTask(request); - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - assertEquals(Status.UNIMPLEMENTED.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("UnsupportedOperationError")); - } - } finally { - deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); - } - } - - @Test - public void testSendMessageNewMessageSuccess() throws Exception { - assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(ProtoUtils.ToProto.message(message)) - .build(); - SendMessageResponse response = client.sendMessage(request); - assertTrue(response.hasMsg()); - io.a2a.grpc.Message grpcMessage = response.getMsg(); - // Convert back to spec Message for easier assertions - Message messageResponse = ProtoUtils.FromProto.message(grpcMessage); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - } - - @Test - public void testSendMessageExistingTaskSuccess() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - - SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(ProtoUtils.ToProto.message(message)) - .build(); - SendMessageResponse response = client.sendMessage(request); - - assertTrue(response.hasMsg()); - io.a2a.grpc.Message grpcMessage = response.getMsg(); - // Convert back to spec Message for easier assertions - Message messageResponse = ProtoUtils.FromProto.message(grpcMessage); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testError() throws Exception { - Message message = new Message.Builder(MESSAGE) - .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId()) - .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId()) - .build(); - - SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(ProtoUtils.ToProto.message(message)) - .build(); - - try { - client.sendMessage(request); - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - assertEquals(Status.UNIMPLEMENTED.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("UnsupportedOperationError")); - } - } - - @Test - public void testGetAgentCard() throws Exception { - // Test gRPC getAgentCard method - GetAgentCardRequest request = GetAgentCardRequest.newBuilder().build(); - - io.a2a.grpc.AgentCard grpcAgentCard = client.getAgentCard(request); - - // Verify the expected agent card fields directly on the gRPC response - assertNotNull(grpcAgentCard); - assertEquals("test-card", grpcAgentCard.getName()); - assertEquals("A test agent card", grpcAgentCard.getDescription()); - assertEquals("http://localhost:" + serverPort, grpcAgentCard.getUrl()); // Use dynamic port - assertEquals("1.0", grpcAgentCard.getVersion()); - assertEquals("http://example.com/docs", grpcAgentCard.getDocumentationUrl()); - assertTrue(grpcAgentCard.getCapabilities().getPushNotifications()); - assertTrue(grpcAgentCard.getCapabilities().getStreaming()); - // Note: stateTransitionHistory is not present in gRPC AgentCapabilities - assertTrue(grpcAgentCard.getSkillsList().isEmpty()); - } - - @Test - public void testGetExtendAgentCardNotSupported() { - // NOTE: This test is not applicable to gRPC since extended agent card retrieval - // is an HTTP/REST-specific feature that tests the /agent/authenticatedExtendedCard endpoint. - // gRPC handles agent capabilities differently through service definitions. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testMalformedJSONRPCRequest() { - // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific - // JSON parsing errors. gRPC uses Protocol Buffers for serialization and has its own - // parsing and validation mechanisms. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testInvalidParamsJSONRPCRequest() { - // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific - // parameter validation errors. gRPC uses strongly-typed Protocol Buffer messages - // which provide built-in type safety and validation. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testInvalidJSONRPCRequestMissingJsonrpc() { - // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific - // validation of the "jsonrpc" field. gRPC does not use JSON-RPC protocol elements. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testInvalidJSONRPCRequestMissingMethod() { - // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific - // validation of the "method" field. gRPC methods are defined in the service definition - // and invoked directly, not through JSON-RPC method names. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testInvalidJSONRPCRequestInvalidId() { - // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific - // validation of the "id" field. gRPC handles request/response correlation differently - // through its streaming mechanisms. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testInvalidJSONRPCRequestNonExistentMethod() { - // NOTE: This test is not applicable to gRPC since it tests JSON-RPC protocol-specific - // method not found errors. gRPC method resolution is handled at the service definition - // level and unknown methods result in different error types. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testNonStreamingMethodWithAcceptHeader() throws Exception { - // NOTE: This test is not applicable to gRPC since HTTP Accept headers - // are an HTTP/REST-specific concept and do not apply to gRPC protocol. - // gRPC uses Protocol Buffers for message encoding and doesn't use HTTP content negotiation. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. - } - - @Test - public void testSetPushNotificationSuccess() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // Create a PushNotificationConfig with an ID (needed for gRPC conversion) - PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() - .url("http://example.com") - .id(MINIMAL_TASK.getId()) // Using task ID as config ID for simplicity - .build(); - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig); - - CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId(MINIMAL_TASK.getId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig)) - .build(); - - io.a2a.grpc.TaskPushNotificationConfig response = client.createTaskPushNotificationConfig(request); - - // Convert back to spec for easier assertions - TaskPushNotificationConfig responseConfig = ProtoUtils.FromProto.taskPushNotificationConfig(response); - assertEquals(MINIMAL_TASK.getId(), responseConfig.taskId()); - assertEquals("http://example.com", responseConfig.pushNotificationConfig().url()); - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testGetPushNotificationSuccess() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // First, create a push notification config (same as previous test) - PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() - .url("http://example.com") - .id(MINIMAL_TASK.getId()) - .build(); - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig); - - CreateTaskPushNotificationConfigRequest createRequest = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId(MINIMAL_TASK.getId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig)) - .build(); - - io.a2a.grpc.TaskPushNotificationConfig createResponse = client.createTaskPushNotificationConfig(createRequest); - assertNotNull(createResponse); - - // Now, get the push notification config - GetTaskPushNotificationConfigRequest getRequest = GetTaskPushNotificationConfigRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId()) - .build(); - - io.a2a.grpc.TaskPushNotificationConfig getResponse = client.getTaskPushNotificationConfig(getRequest); - - // Convert back to spec for easier assertions - TaskPushNotificationConfig responseConfig = ProtoUtils.FromProto.taskPushNotificationConfig(getResponse); - assertEquals(MINIMAL_TASK.getId(), responseConfig.taskId()); - assertEquals("http://example.com", responseConfig.pushNotificationConfig().url()); - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testListPushNotificationConfigWithConfigId() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // Create first push notification config - PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() - .url("http://example.com") - .id("config1") - .build(); - TaskPushNotificationConfig taskPushConfig1 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); - - CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId("config1") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) - .build(); - client.createTaskPushNotificationConfig(createRequest1); - - // Create second push notification config - PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() - .url("http://example.com") - .id("config2") - .build(); - TaskPushNotificationConfig taskPushConfig2 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); - - CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId("config2") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) - .build(); - client.createTaskPushNotificationConfig(createRequest2); - - // Now, list all push notification configs for the task - ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .build(); - - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); - - // Verify the response - assertEquals(2, listResponse.getConfigsCount()); - - // Convert back to spec for easier assertions - TaskPushNotificationConfig config1 = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse.getConfigs(0)); - TaskPushNotificationConfig config2 = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse.getConfigs(1)); - - assertEquals(MINIMAL_TASK.getId(), config1.taskId()); - assertEquals("http://example.com", config1.pushNotificationConfig().url()); - assertEquals("config1", config1.pushNotificationConfig().id()); - - assertEquals(MINIMAL_TASK.getId(), config2.taskId()); - assertEquals("http://example.com", config2.pushNotificationConfig().url()); - assertEquals("config2", config2.pushNotificationConfig().id()); - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testListPushNotificationConfigWithoutConfigId() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // Create first push notification config without explicit ID (will use task ID as default) - PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() - .url("http://1.example.com") - .id(MINIMAL_TASK.getId()) // Use task ID as config ID - .build(); - TaskPushNotificationConfig taskPushConfig1 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); - - CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId(MINIMAL_TASK.getId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) - .build(); - client.createTaskPushNotificationConfig(createRequest1); - - // Create second push notification config with same ID (will overwrite the previous one) - PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() - .url("http://2.example.com") - .id(MINIMAL_TASK.getId()) // Same ID, will overwrite - .build(); - TaskPushNotificationConfig taskPushConfig2 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); - - CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId(MINIMAL_TASK.getId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) - .build(); - client.createTaskPushNotificationConfig(createRequest2); - - // Now, list all push notification configs for the task - ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .build(); - - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); - - // Verify only 1 config exists (second one overwrote the first) - assertEquals(1, listResponse.getConfigsCount()); - - // Convert back to spec for easier assertions - TaskPushNotificationConfig config = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse.getConfigs(0)); - - assertEquals(MINIMAL_TASK.getId(), config.taskId()); - assertEquals("http://2.example.com", config.pushNotificationConfig().url()); - assertEquals(MINIMAL_TASK.getId(), config.pushNotificationConfig().id()); - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testListPushNotificationConfigTaskNotFound() throws Exception { - ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/non-existent-task") - .build(); - - try { - client.listTaskPushNotificationConfig(listRequest); - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("TaskNotFoundError")); - } - } - - @Test - public void testListPushNotificationConfigEmptyList() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // List configs for a task that has no configs - ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .build(); - - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); - - // Verify empty list - assertEquals(0, listResponse.getConfigsCount()); - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } +public class QuarkusA2AGrpcTest extends AbstractA2AServerTest { - @Test - public void testDeletePushNotificationConfigWithValidConfigId() throws Exception { - // Create a second task for testing cross-task isolation - Task secondTask = new Task.Builder() - .id("task-456") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - saveTaskInTaskStore(MINIMAL_TASK); - saveTaskInTaskStore(secondTask); - try { - // Create config1 and config2 for MINIMAL_TASK - PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() - .url("http://example.com") - .id("config1") - .build(); - TaskPushNotificationConfig taskPushConfig1 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); - - CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId("config1") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) - .build(); - client.createTaskPushNotificationConfig(createRequest1); - - PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() - .url("http://example.com") - .id("config2") - .build(); - TaskPushNotificationConfig taskPushConfig2 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); - - CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId("config2") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) - .build(); - client.createTaskPushNotificationConfig(createRequest2); - - // Create config1 for secondTask - TaskPushNotificationConfig taskPushConfig3 = - new TaskPushNotificationConfig(secondTask.getId(), pushConfig1); - - CreateTaskPushNotificationConfigRequest createRequest3 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + secondTask.getId()) - .setConfigId("config1") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig3)) - .build(); - client.createTaskPushNotificationConfig(createRequest3); - - // Delete config1 from MINIMAL_TASK - DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/config1") - .build(); - - com.google.protobuf.Empty deleteResponse = client.deleteTaskPushNotificationConfig(deleteRequest); - assertNotNull(deleteResponse); // Should return Empty, not null - - // Verify MINIMAL_TASK now has only 1 config (config2) - ListTaskPushNotificationConfigRequest listRequest1 = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .build(); - ListTaskPushNotificationConfigResponse listResponse1 = client.listTaskPushNotificationConfig(listRequest1); - assertEquals(1, listResponse1.getConfigsCount()); - - TaskPushNotificationConfig remainingConfig = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse1.getConfigs(0)); - assertEquals("config2", remainingConfig.pushNotificationConfig().id()); - - // Verify secondTask remains unchanged (still has config1) - ListTaskPushNotificationConfigRequest listRequest2 = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + secondTask.getId()) - .build(); - ListTaskPushNotificationConfigResponse listResponse2 = client.listTaskPushNotificationConfig(listRequest2); - assertEquals(1, listResponse2.getConfigsCount()); - - TaskPushNotificationConfig secondTaskConfig = ProtoUtils.FromProto.taskPushNotificationConfig(listResponse2.getConfigs(0)); - assertEquals("config1", secondTaskConfig.pushNotificationConfig().id()); - - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); - deletePushNotificationConfigInStore(secondTask.getId(), "config1"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - deleteTaskInTaskStore(secondTask.getId()); - } - } - - @Test - public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // Create config1 and config2 - PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() - .url("http://example.com") - .id("config1") - .build(); - TaskPushNotificationConfig taskPushConfig1 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); - - CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId("config1") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) - .build(); - client.createTaskPushNotificationConfig(createRequest1); - - PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() - .url("http://example.com") - .id("config2") - .build(); - TaskPushNotificationConfig taskPushConfig2 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); - - CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId("config2") - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) - .build(); - client.createTaskPushNotificationConfig(createRequest2); - - // Try to delete non-existent config (should succeed silently) - DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/non-existent-config-id") - .build(); - - com.google.protobuf.Empty deleteResponse = client.deleteTaskPushNotificationConfig(deleteRequest); - assertNotNull(deleteResponse); // Should return Empty, not throw error - - // Verify both configs remain unchanged - ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .build(); - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); - assertEquals(2, listResponse.getConfigsCount()); - - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } + public QuarkusA2AGrpcTest() { + super(8081); // HTTP server port for utility endpoints } - @Test - public void testDeletePushNotificationConfigTaskNotFound() throws Exception { - DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() - .setName("tasks/non-existent-task/pushNotificationConfigs/non-existent-config-id") - .build(); - - try { - client.deleteTaskPushNotificationConfig(deleteRequest); - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("TaskNotFoundError")); - } - } - - @Test - public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // Create first config without explicit ID (will use task ID as default) - PushNotificationConfig pushConfig1 = new PushNotificationConfig.Builder() - .url("http://1.example.com") - .id(MINIMAL_TASK.getId()) - .build(); - TaskPushNotificationConfig taskPushConfig1 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig1); - - CreateTaskPushNotificationConfigRequest createRequest1 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId(MINIMAL_TASK.getId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig1)) - .build(); - client.createTaskPushNotificationConfig(createRequest1); - - // Create second config with same ID (will overwrite the previous one) - PushNotificationConfig pushConfig2 = new PushNotificationConfig.Builder() - .url("http://2.example.com") - .id(MINIMAL_TASK.getId()) - .build(); - TaskPushNotificationConfig taskPushConfig2 = - new TaskPushNotificationConfig(MINIMAL_TASK.getId(), pushConfig2); - - CreateTaskPushNotificationConfigRequest createRequest2 = CreateTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .setConfigId(MINIMAL_TASK.getId()) - .setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig2)) - .build(); - client.createTaskPushNotificationConfig(createRequest2); - - // Delete the config using task ID - DeleteTaskPushNotificationConfigRequest deleteRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + MINIMAL_TASK.getId()) - .build(); - - com.google.protobuf.Empty deleteResponse = client.deleteTaskPushNotificationConfig(deleteRequest); - assertNotNull(deleteResponse); // Should return Empty - - // Verify no configs remain - ListTaskPushNotificationConfigRequest listRequest = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + MINIMAL_TASK.getId()) - .build(); - ListTaskPushNotificationConfigResponse listResponse = client.listTaskPushNotificationConfig(listRequest); - assertEquals(0, listResponse.getConfigsCount()); - - } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testSendMessageStreamExistingTaskSuccess() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - // Build message for existing task - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - - // Create gRPC streaming request - SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(ProtoUtils.ToProto.message(message)) - .build(); - - // Use blocking iterator to consume stream responses - java.util.Iterator responseIterator = client.sendStreamingMessage(request); - - // Collect responses - expect at least one - java.util.List responses = new java.util.ArrayList<>(); - while (responseIterator.hasNext()) { - StreamResponse response = responseIterator.next(); - responses.add(response); - - // For this test, we expect to get the message back - stop after first response - if (response.hasMsg()) { - break; - } - } - - // Verify we got at least one response - assertTrue(responses.size() >= 1, "Expected at least one response from streaming call"); - - // Find the message response - StreamResponse messageResponse = null; - for (StreamResponse response : responses) { - if (response.hasMsg()) { - messageResponse = response; - break; - } - } - - assertNotNull(messageResponse, "Expected to receive a message response"); - - // Verify the message content - io.a2a.grpc.Message grpcMessage = messageResponse.getMsg(); - Message responseMessage = ProtoUtils.FromProto.message(grpcMessage); - assertEquals(MESSAGE.getMessageId(), responseMessage.getMessageId()); - assertEquals(MESSAGE.getRole(), responseMessage.getRole()); - Part part = responseMessage.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } + @Override + protected String getTransportProtocol() { + return TransportProtocol.GRPC.asString(); } - @Test - public void testStreamingMethodWithAcceptHeader() throws Exception { - // NOTE: This test is not applicable to gRPC since HTTP Accept headers - // are an HTTP/REST-specific concept and do not apply to gRPC protocol. - // gRPC uses Protocol Buffers for message encoding and doesn't use HTTP content negotiation. - - // This stub is maintained to preserve method order compatibility with AbstractA2AServerTest - // for future migration when extending that base class. + @Override + protected String getTransportUrl() { + return "localhost:9001"; // gRPC server runs on port 9001 } - - @Test - public void testSendMessageStreamNewMessageSuccess() throws Exception { - // Ensure no task exists initially (test creates new task via streaming) - assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null, "Task should not exist initially"); - - try { - // Build message for new task (no pre-existing task) - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - - // Create gRPC streaming request - SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(ProtoUtils.ToProto.message(message)) - .build(); - - // Use blocking iterator to consume stream responses - java.util.Iterator responseIterator = client.sendStreamingMessage(request); - - // Collect responses - expect at least one - java.util.List responses = new java.util.ArrayList<>(); - while (responseIterator.hasNext()) { - StreamResponse response = responseIterator.next(); - responses.add(response); - - // For this test, we expect to get the message back - stop after first response - if (response.hasMsg()) { - break; - } - } - - // Verify we got at least one response - assertTrue(responses.size() >= 1, "Expected at least one response from streaming call"); - - // Find the message response - StreamResponse messageResponse = null; - for (StreamResponse response : responses) { - if (response.hasMsg()) { - messageResponse = response; - break; - } - } - - assertNotNull(messageResponse, "Expected to receive a message response"); - - // Verify the message content - io.a2a.grpc.Message grpcMessage = messageResponse.getMsg(); - Message responseMessage = ProtoUtils.FromProto.message(grpcMessage); - assertEquals(MESSAGE.getMessageId(), responseMessage.getMessageId()); - assertEquals(MESSAGE.getRole(), responseMessage.getRole()); - Part part = responseMessage.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - - } finally { - // Clean up any task that may have been created (ignore if task doesn't exist) - try { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } catch (RuntimeException e) { - // Ignore if task doesn't exist (404 error) - if (!e.getMessage().contains("404")) { - throw e; - } - } - } - } - - @Test - public void testResubscribeExistingTaskSuccess() throws Exception { - ExecutorService executorService = Executors.newSingleThreadExecutor(); - saveTaskInTaskStore(MINIMAL_TASK); - - try { - // Ensure queue for task exists (required for resubscription) - ensureQueueForTask(MINIMAL_TASK.getId()); - - CountDownLatch taskResubscriptionRequestSent = new CountDownLatch(1); - CountDownLatch taskResubscriptionResponseReceived = new CountDownLatch(2); - AtomicReference firstResponse = new AtomicReference<>(); - AtomicReference secondResponse = new AtomicReference<>(); - - // Create gRPC task subscription request - TaskSubscriptionRequest subscriptionRequest = TaskSubscriptionRequest.newBuilder() - .setName("tasks/" + MINIMAL_TASK.getId()) - .build(); - - // Count down the latch when the gRPC streaming subscription is established - awaitStreamingSubscription() - .whenComplete((unused, throwable) -> taskResubscriptionRequestSent.countDown()); - - AtomicReference errorRef = new AtomicReference<>(); - - // Start the subscription in a separate thread - executorService.submit(() -> { - try { - java.util.Iterator responseIterator = client.taskSubscription(subscriptionRequest); - - while (responseIterator.hasNext()) { - StreamResponse response = responseIterator.next(); - - if (taskResubscriptionResponseReceived.getCount() == 2) { - firstResponse.set(response); - } else { - secondResponse.set(response); - } - taskResubscriptionResponseReceived.countDown(); - - if (taskResubscriptionResponseReceived.getCount() == 0) { - break; - } - } - } catch (Exception e) { - errorRef.set(e); - // Count down both latches to unblock the test - taskResubscriptionRequestSent.countDown(); - while (taskResubscriptionResponseReceived.getCount() > 0) { - taskResubscriptionResponseReceived.countDown(); - } - } - }); - - // Wait for subscription to be established - assertTrue(taskResubscriptionRequestSent.await(10, TimeUnit.SECONDS), "Subscription should be established"); - - // Inject events into the server's event queue - java.util.List events = java.util.List.of( - new TaskArtifactUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .artifact(new Artifact.Builder() - .artifactId("11") - .parts(new TextPart("text")) - .build()) - .build(), - new TaskStatusUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) - .build()); - - for (Event event : events) { - enqueueEventOnServer(event); - } - - // Wait for the client to receive the responses - assertTrue(taskResubscriptionResponseReceived.await(20, TimeUnit.SECONDS), "Should receive both responses"); - - // Check for errors - if (errorRef.get() != null) { - throw new RuntimeException("Error in subscription thread", errorRef.get()); - } - - // Verify first response (TaskArtifactUpdateEvent) - assertNotNull(firstResponse.get(), "Should receive first response"); - StreamResponse firstStreamResponse = firstResponse.get(); - assertTrue(firstStreamResponse.hasArtifactUpdate(), "First response should be artifact update"); - - io.a2a.grpc.TaskArtifactUpdateEvent artifactUpdate = firstStreamResponse.getArtifactUpdate(); - assertEquals(MINIMAL_TASK.getId(), artifactUpdate.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), artifactUpdate.getContextId()); - assertEquals("11", artifactUpdate.getArtifact().getArtifactId()); - assertEquals("text", artifactUpdate.getArtifact().getParts(0).getText()); - - // Verify second response (TaskStatusUpdateEvent) - assertNotNull(secondResponse.get(), "Should receive second response"); - StreamResponse secondStreamResponse = secondResponse.get(); - assertTrue(secondStreamResponse.hasStatusUpdate(), "Second response should be status update"); - - io.a2a.grpc.TaskStatusUpdateEvent statusUpdate = secondStreamResponse.getStatusUpdate(); - assertEquals(MINIMAL_TASK.getId(), statusUpdate.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), statusUpdate.getContextId()); - assertEquals(io.a2a.grpc.TaskState.TASK_STATE_COMPLETED, statusUpdate.getStatus().getState()); - assertTrue(statusUpdate.getFinal(), "Final status update should be marked as final"); - - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - executorService.shutdown(); - if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { - executorService.shutdownNow(); - } - } - } - - @Test - public void testResubscribeNoExistingTaskError() throws Exception { - // Try to resubscribe to a non-existent task - should get TaskNotFoundError - TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() - .setName("tasks/non-existent-task") - .build(); - - try { - // Use blocking iterator to consume stream responses - java.util.Iterator responseIterator = client.taskSubscription(request); - - // Try to get first response - should throw StatusRuntimeException - if (responseIterator.hasNext()) { - responseIterator.next(); - } - - // Should not reach here - assertTrue(false, "Expected StatusRuntimeException but method returned normally"); - } catch (StatusRuntimeException e) { - // Verify this is a TaskNotFoundError mapped to NOT_FOUND status - assertEquals(Status.NOT_FOUND.getCode(), e.getStatus().getCode()); - String description = e.getStatus().getDescription(); - assertTrue(description != null && description.contains("TaskNotFoundError")); - } - } - - - protected void saveTaskInTaskStore(Task task) throws Exception { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/test/task")) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(task))) - .header("Content-Type", APPLICATION_JSON) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() != 200) { - throw new RuntimeException( - String.format("Saving task failed! Status: %d, Body: %s", response.statusCode(), response.body())); - } - } - - protected Task getTaskFromTaskStore(String taskId) throws Exception { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) - .GET() - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() == 404) { - return null; - } - if (response.statusCode() != 200) { - throw new RuntimeException(String.format("Getting task failed! Status: %d, Body: %s", response.statusCode(), response.body())); - } - return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); - } - - protected void deleteTaskInTaskStore(String taskId) throws Exception { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(("http://localhost:" + serverPort + "/test/task/" + taskId))) - .DELETE() - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); - } - } - - protected void ensureQueueForTask(String taskId) throws Exception { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/test/queue/ensure/" + taskId)) - .POST(HttpRequest.BodyPublishers.noBody()) - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() != 200) { - throw new RuntimeException(String.format("Ensuring queue failed! Status: %d, Body: %s", response.statusCode(), response.body())); - } - } - - protected void enqueueEventOnServer(Event event) throws Exception { - String path; - if (event instanceof TaskArtifactUpdateEvent e) { - path = "test/queue/enqueueTaskArtifactUpdateEvent/" + e.getTaskId(); - } else if (event instanceof TaskStatusUpdateEvent e) { - path = "test/queue/enqueueTaskStatusUpdateEvent/" + e.getTaskId(); - } else { - throw new RuntimeException("Unknown event type " + event.getClass() + ". If you need the ability to" + - " handle more types, please add the REST endpoints."); - } - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/" + path)) - .header("Content-Type", APPLICATION_JSON) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(event))) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Queueing event failed!" + response.body()); - } - } - - private CompletableFuture awaitStreamingSubscription() { - int cnt = getStreamingSubscribedCount(); - AtomicInteger initialCount = new AtomicInteger(cnt); - - return CompletableFuture.runAsync(() -> { - try { - boolean done = false; - long end = System.currentTimeMillis() + 15000; - while (System.currentTimeMillis() < end) { - int count = getStreamingSubscribedCount(); - if (count > initialCount.get()) { - done = true; - break; - } - Thread.sleep(500); - } - if (!done) { - throw new RuntimeException("Timed out waiting for subscription"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted"); - } - }); - } - - private int getStreamingSubscribedCount() { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/test/streamingSubscribedCount")) - .GET() - .build(); - try { - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - String body = response.body().trim(); - return Integer.parseInt(body); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } - - protected void deletePushNotificationConfigInStore(String taskId, String configId) throws Exception { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(("http://localhost:" + serverPort + "/test/task/" + taskId + "/config/" + configId))) - .DELETE() - .build(); - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Deleting task failed!" + response.body()); - } - } - - protected void savePushNotificationConfigInStore(String taskId, PushNotificationConfig notificationConfig) throws Exception { - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(notificationConfig))) - .header("Content-Type", APPLICATION_JSON) - .build(); - - HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); - if (response.statusCode() != 200) { - throw new RuntimeException(response.statusCode() + ": Creating task push notification config failed! " + response.body()); - } - } - -} +} \ No newline at end of file diff --git a/reference/grpc/src/test/resources/application.properties b/reference/grpc/src/test/resources/application.properties index 2ddb0ae2a..9ddcb3e07 100644 --- a/reference/grpc/src/test/resources/application.properties +++ b/reference/grpc/src/test/resources/application.properties @@ -1,2 +1,3 @@ -quarkus.grpc.clients.a2a-service.host=localhost -quarkus.grpc.clients.a2a-service.port=9001 \ No newline at end of file +# Configure the gRPC server to listen on port 9001 +quarkus.grpc.server.port=9001 +quarkus.http.port=8081 \ No newline at end of file diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java index f1442da33..ff61a433b 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java @@ -1,12 +1,180 @@ package io.a2a.server.apps.quarkus; +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.wildfly.common.Assert.assertNotNull; +import jakarta.ws.rs.core.MediaType; + import io.a2a.server.apps.common.AbstractA2AServerTest; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCErrorResponse; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.Task; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; +import io.a2a.spec.TransportProtocol; import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + @QuarkusTest public class QuarkusA2AJSONRPCTest extends AbstractA2AServerTest { public QuarkusA2AJSONRPCTest() { super(8081); } + + @Override + protected String getTransportProtocol() { + return TransportProtocol.JSONRPC.asString(); + } + + @Override + protected String getTransportUrl() { + return "http://localhost:8081"; + } + + @Test + public void testMalformedJSONRPCRequest() { + // missing closing bracket + String malformedRequest = "{\"jsonrpc\": \"2.0\", \"method\": \"message/send\", \"params\": {\"foo\": \"bar\"}"; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(malformedRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new JSONParseError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidParamsJSONRPCRequest() { + String invalidParamsRequest = """ + {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} + """; + testInvalidParams(invalidParamsRequest); + + invalidParamsRequest = """ + {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} + """; + testInvalidParams(invalidParamsRequest); + } + + private void testInvalidParams(String invalidParamsRequest) { + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidParamsRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); + assertEquals("1", response.getId()); + } + + @Test + public void testInvalidJSONRPCRequestMissingJsonrpc() { + String invalidRequest = """ + { + "method": "message/send", + "params": {} + } + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidJSONRPCRequestMissingMethod() { + String invalidRequest = """ + {"jsonrpc": "2.0", "params": {}} + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidJSONRPCRequestInvalidId() { + String invalidRequest = """ + {"jsonrpc": "2.0", "method": "message/send", "params": {}, "id": {"bad": "type"}} + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidJSONRPCRequestNonExistentMethod() { + String invalidRequest = """ + {"jsonrpc": "2.0", "method" : "nonexistent/method", "params": {}} + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); + } + + @Test + public void testNonStreamingMethodWithAcceptHeader() throws Exception { + testGetTask(MediaType.APPLICATION_JSON); + } + + private void testGetTask(String mediaType) throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.getId()), null); + assertEquals("task-123", response.getId()); + assertEquals("session-xyz", response.getContextId()); + assertEquals(TaskState.SUBMITTED, response.getStatus().state()); + } catch (A2AClientException e) { + fail("Unexpected exception during getTask: " + e.getMessage(), e); + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 52a6b6719..ddeaa11c7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -168,7 +168,8 @@ public static io.a2a.grpc.Message message(Message message) { public static io.a2a.grpc.TaskPushNotificationConfig taskPushNotificationConfig(TaskPushNotificationConfig config) { io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); - builder.setName("tasks/" + config.taskId() + "/pushNotificationConfigs/" + config.pushNotificationConfig().id()); + String configId = config.pushNotificationConfig().id(); + builder.setName("tasks/" + config.taskId() + "/pushNotificationConfigs/" + (configId != null ? configId : config.taskId())); builder.setPushNotificationConfig(pushNotificationConfig(config.pushNotificationConfig())); return builder.build(); } @@ -703,13 +704,17 @@ public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc. } public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { - String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" + String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" or /tasks/{id} String[] parts = name.split("/"); - if (parts.length < 4) { + String taskId = parts[1]; + String configId; + if (parts.length == 2) { + configId = taskId; + } else if (parts.length < 4) { throw new IllegalArgumentException("Invalid name format for GetTaskPushNotificationConfigRequest: " + name); + } else { + configId = parts[3]; } - String taskId = parts[1]; - String configId = parts[3]; return new GetTaskPushNotificationConfigParams(taskId, configId); } @@ -763,13 +768,16 @@ private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.Sen private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification, String configId) { return new PushNotificationConfig( pushNotification.getUrl(), - pushNotification.getToken(), - authenticationInfo(pushNotification.getAuthentication()), + pushNotification.getToken().isEmpty() ? null : pushNotification.getToken(), + pushNotification.hasAuthentication() ? authenticationInfo(pushNotification.getAuthentication()) : null, pushNotification.getId().isEmpty() ? configId : pushNotification.getId() ); } private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification) { + /*if (pushNotification == null) { + return null; + }*/ return pushNotification(pushNotification, pushNotification.getId()); } @@ -795,6 +803,7 @@ public static Message message(io.a2a.grpc.Message message) { if (message.getMessageId().isEmpty()) { throw new InvalidParamsError(); } + return new Message( role(message.getRole()), message.getContentList().stream().map(item -> part(item)).collect(Collectors.toList()), @@ -868,7 +877,7 @@ private static DataPart dataPart(io.a2a.grpc.DataPart dataPart) { private static TaskStatus taskStatus(io.a2a.grpc.TaskStatus taskStatus) { return new TaskStatus( taskState(taskStatus.getState()), - message(taskStatus.getUpdate()), + taskStatus.hasUpdate() ? message(taskStatus.getUpdate()) : null, LocalDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) ); } diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 6ebf239fb..fb6cbc5db 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -56,7 +56,13 @@ io.github.a2asdk a2a-java-sdk-client-transport-jsonrpc - 0.2.6.Beta1-SNAPSHOT + ${project.version} + test + + + io.github.a2asdk + a2a-java-sdk-client-transport-grpc + ${project.version} test diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 38693ee33..d9464033c 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -3,6 +3,7 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; @@ -16,57 +17,55 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import jakarta.ws.rs.core.MediaType; import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.client.transport.spi.ClientTransport; -import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.Client; +import io.a2a.client.ClientEvent; +import io.a2a.client.ClientFactory; +import io.a2a.client.MessageEvent; +import io.a2a.client.TaskUpdateEvent; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.config.ClientTransportConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; +import io.a2a.client.transport.grpc.GrpcTransportConfig; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; +import io.a2a.spec.JSONRPCError; +import io.grpc.ManagedChannelBuilder; import io.a2a.spec.A2AClientException; -import io.a2a.spec.A2AServerException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; import io.a2a.spec.Artifact; -import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; import io.a2a.spec.GetAuthenticatedExtendedCardRequest; import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.JSONRPCErrorResponse; import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; -import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; import io.a2a.spec.SendStreamingMessageRequest; import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -74,17 +73,15 @@ import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; -import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; -import io.restassured.RestAssured; -import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -94,7 +91,7 @@ */ public abstract class AbstractA2AServerTest { - private static final Task MINIMAL_TASK = new Task.Builder() + protected static final Task MINIMAL_TASK = new Task.Builder() .id("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -126,13 +123,23 @@ public abstract class AbstractA2AServerTest { public static final String APPLICATION_JSON = "application/json"; private final int serverPort; - private ClientTransport client; + private Client client; + private Client nonStreamingClient; protected AbstractA2AServerTest(int serverPort) { this.serverPort = serverPort; - this.client = new JSONRPCTransport("http://localhost:" + serverPort); } + /** + * Get the transport protocol to use for this test (e.g., "JSONRPC", "GRPC"). + */ + protected abstract String getTransportProtocol(); + + /** + * Get the transport URL for this test. + */ + protected abstract String getTransportUrl(); + @Test public void testTaskStoreMethodsSanityTest() throws Exception { Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); @@ -159,25 +166,12 @@ private void testGetTask() throws Exception { private void testGetTask(String mediaType) throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); - RequestSpecification requestSpecification = RestAssured.given() - .contentType(MediaType.APPLICATION_JSON) - .body(request); - if (mediaType != null) { - requestSpecification = requestSpecification.accept(mediaType); - } - GetTaskResponse response = requestSpecification - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetTaskResponse.class); - assertEquals("1", response.getId()); - assertEquals("task-123", response.getResult().getId()); - assertEquals("session-xyz", response.getResult().getContextId()); - assertEquals(TaskState.SUBMITTED, response.getResult().getStatus().state()); - assertNull(response.getError()); + Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.getId()), null); + assertEquals("task-123", response.getId()); + assertEquals("session-xyz", response.getContextId()); + assertEquals(TaskState.SUBMITTED, response.getStatus().state()); + } catch (A2AClientException e) { + fail("Unexpected exception during getTask: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } @@ -186,44 +180,25 @@ private void testGetTask(String mediaType) throws Exception { @Test public void testGetTaskNotFound() throws Exception { assertTrue(getTaskFromTaskStore("non-existent-task") == null); - GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams("non-existent-task")); - GetTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetTaskResponse.class); - assertEquals("1", response.getId()); - // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); - assertNull(response.getResult()); + try { + getClient().getTask(new TaskQueryParams("non-existent-task"), null); + fail("Expected A2AClientException for non-existent task"); + } catch (A2AClientException e) { + // Expected - the client should throw an exception for non-existent tasks + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } } @Test public void testCancelTaskSuccess() throws Exception { saveTaskInTaskStore(CANCEL_TASK); try { - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK.getId())); - CancelTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(CancelTaskResponse.class); - assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); - Task task = response.getResult(); + Task task = getClient().cancelTask(new TaskIdParams(CANCEL_TASK.getId()), null); assertEquals(CANCEL_TASK.getId(), task.getId()); assertEquals(CANCEL_TASK.getContextId(), task.getContextId()); assertEquals(TaskState.CANCELED, task.getStatus().state()); - } catch (Exception e) { + } catch (A2AClientException e) { + fail("Unexpected exception during cancel task: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(CANCEL_TASK.getId()); } @@ -233,22 +208,11 @@ public void testCancelTaskSuccess() throws Exception { public void testCancelTaskNotSupported() throws Exception { saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); try { - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); - CancelTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(CancelTaskResponse.class); - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); - } catch (Exception e) { + getClient().cancelTask(new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId()), null); + fail("Expected A2AClientException for unsupported cancel operation"); + } catch (A2AClientException e) { + // Expected - the client should throw an exception for unsupported operations + assertInstanceOf(UnsupportedOperationError.class, e.getCause()); } finally { deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); } @@ -256,22 +220,13 @@ public void testCancelTaskNotSupported() throws Exception { @Test public void testCancelTaskNotFound() { - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams("non-existent-task")); - CancelTaskResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(CancelTaskResponse.class) - ; - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new TaskNotFoundError().getCode(), response.getError().getCode()); + try { + getClient().cancelTask(new TaskIdParams("non-existent-task"), null); + fail("Expected A2AClientException for non-existent task"); + } catch (A2AClientException e) { + // Expected - the client should throw an exception for non-existent tasks + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } } @Test @@ -281,18 +236,31 @@ public void testSendMessageNewMessageSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SendMessageResponse.class); - assertNull(response.getError()); - Message messageResponse = (Message) response.getResult(); + + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference receivedMessage = new AtomicReference<>(); + AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof MessageEvent messageEvent) { + if (latch.getCount() > 0) { + receivedMessage.set(messageEvent.getMessage()); + latch.countDown(); + } else { + wasUnexpectedEvent.set(true); + } + } else { + wasUnexpectedEvent.set(true); + } + }; + + // testing the non-streaming send message + getNonStreamingClient().sendMessage(message, List.of(consumer), null, null); + + assertTrue(latch.await(10, TimeUnit.SECONDS)); + assertFalse(wasUnexpectedEvent.get()); + Message messageResponse = receivedMessage.get(); + assertNotNull(messageResponse); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); assertEquals(MESSAGE.getRole(), messageResponse.getRole()); Part part = messageResponse.getParts().get(0); @@ -308,24 +276,36 @@ public void testSendMessageExistingTaskSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SendMessageResponse.class); - assertNull(response.getError()); - Message messageResponse = (Message) response.getResult(); + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference receivedMessage = new AtomicReference<>(); + AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof MessageEvent messageEvent) { + if (latch.getCount() > 0) { + receivedMessage.set(messageEvent.getMessage()); + latch.countDown(); + } else { + wasUnexpectedEvent.set(true); + } + } else { + wasUnexpectedEvent.set(true); + } + }; + + // testing the non-streaming send message + getNonStreamingClient().sendMessage(message, List.of(consumer), null, null); + assertFalse(wasUnexpectedEvent.get()); + assertTrue(latch.await(10, TimeUnit.SECONDS)); + Message messageResponse = receivedMessage.get(); + assertNotNull(messageResponse); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); assertEquals(MESSAGE.getRole(), messageResponse.getRole()); Part part = messageResponse.getParts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("test message", ((TextPart) part).getText()); - } catch (Exception e) { + } catch (A2AClientException e) { + fail("Unexpected exception during sendMessage: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } @@ -338,22 +318,11 @@ public void testSetPushNotificationSuccess() throws Exception { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SetTaskPushNotificationConfigResponse.class); - assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); - TaskPushNotificationConfig config = response.getResult(); + TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig, null); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); - } catch (Exception e) { + } catch (A2AClientException e) { + fail("Unexpected exception during set push notification test: " + e.getMessage(), e); } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); deleteTaskInTaskStore(MINIMAL_TASK.getId()); @@ -368,35 +337,15 @@ public void testGetPushNotificationSuccess() throws Exception { new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); - SetTaskPushNotificationConfigRequest setTaskPushNotificationRequest = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse setTaskPushNotificationResponse = given() - .contentType(MediaType.APPLICATION_JSON) - .body(setTaskPushNotificationRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SetTaskPushNotificationConfigResponse.class); - assertNotNull(setTaskPushNotificationResponse); - - GetTaskPushNotificationConfigRequest request = - new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - GetTaskPushNotificationConfigResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetTaskPushNotificationConfigResponse.class); - assertNull(response.getError()); - assertEquals(request.getId(), response.getId()); - TaskPushNotificationConfig config = response.getResult(); + TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig, null); + assertNotNull(setResult); + + TaskPushNotificationConfig config = getClient().getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); - } catch (Exception e) { + } catch (A2AClientException e) { + fail("Unexpected exception during get push notification test: " + e.getMessage(), e); } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); deleteTaskInTaskStore(MINIMAL_TASK.getId()); @@ -404,43 +353,30 @@ public void testGetPushNotificationSuccess() throws Exception { } @Test - public void testError() { + public void testError() throws A2AClientException { Message message = new Message.Builder(MESSAGE) .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId()) .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId()) .build(); - SendMessageRequest request = new SendMessageRequest( - "1", new MessageSendParams(message, null, null)); - SendMessageResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(SendMessageResponse.class); - assertEquals(request.getId(), response.getId()); - assertNull(response.getResult()); - // this should be an instance of UnsupportedOperationError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new UnsupportedOperationError().getCode(), response.getError().getCode()); + + try { + getNonStreamingClient().sendMessage(message, null); + + // For non-streaming clients, the error should still be thrown as an exception + fail("Expected A2AClientException for unsupported send message operation"); + } catch (A2AClientException e) { + // Expected - the client should throw an exception for unsupported operations + assertInstanceOf(UnsupportedOperationError.class, e.getCause()); + } } @Test - public void testGetAgentCard() { - AgentCard agentCard = given() - .contentType(MediaType.APPLICATION_JSON) - .when() - .get("/.well-known/agent-card.json") - .then() - .statusCode(200) - .extract() - .as(AgentCard.class); + public void testGetAgentCard() throws A2AClientException { + AgentCard agentCard = getClient().getAgentCard(null); assertNotNull(agentCard); assertEquals("test-card", agentCard.name()); assertEquals("A test agent card", agentCard.description()); - assertEquals("http://localhost:8081", agentCard.url()); + assertEquals(getTransportUrl(), agentCard.url()); assertEquals("1.0", agentCard.version()); assertEquals("http://example.com/docs", agentCard.documentationUrl()); assertTrue(agentCard.capabilities().pushNotifications()); @@ -467,132 +403,6 @@ public void testGetExtendAgentCardNotSupported() { assertNull(response.getResult()); } - @Test - public void testMalformedJSONRPCRequest() { - // missing closing bracket - String malformedRequest = "{\"jsonrpc\": \"2.0\", \"method\": \"message/send\", \"params\": {\"foo\": \"bar\"}"; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(malformedRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new JSONParseError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidParamsJSONRPCRequest() { - String invalidParamsRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} - """; - testInvalidParams(invalidParamsRequest); - - invalidParamsRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} - """; - testInvalidParams(invalidParamsRequest); - } - - private void testInvalidParams(String invalidParamsRequest) { - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidParamsRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); - assertEquals("1", response.getId()); - } - - @Test - public void testInvalidJSONRPCRequestMissingJsonrpc() { - String invalidRequest = """ - { - "method": "message/send", - "params": {} - } - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidJSONRPCRequestMissingMethod() { - String invalidRequest = """ - {"jsonrpc": "2.0", "params": {}} - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidJSONRPCRequestInvalidId() { - String invalidRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": {}, "id": {"bad": "type"}} - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidJSONRPCRequestNonExistentMethod() { - String invalidRequest = """ - {"jsonrpc": "2.0", "method" : "nonexistent/method", "params": {}} - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); - } - - @Test - public void testNonStreamingMethodWithAcceptHeader() throws Exception { - testGetTask(MediaType.APPLICATION_JSON); - } - - @Test public void testSendMessageStreamExistingTaskSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -601,48 +411,46 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest( - "1", new MessageSendParams(message, null, null)); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, null); CountDownLatch latch = new CountDownLatch(1); + AtomicReference receivedMessage = new AtomicReference<>(); + AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertNull(jsonResponse.getError()); - Message messageResponse = (Message) jsonResponse.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - latch.countDown(); - } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof MessageEvent messageEvent) { + if (latch.getCount() > 0) { + receivedMessage.set(messageEvent.getMessage()); + latch.countDown(); + } else { + wasUnexpectedEvent.set(true); } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); + } else { + wasUnexpectedEvent.set(true); } + }; + + Consumer errorHandler = error -> { + errorRef.set(error); latch.countDown(); - return null; - }); + }; - boolean dataRead = latch.await(20, TimeUnit.SECONDS); - Assertions.assertTrue(dataRead); - Assertions.assertNull(errorRef.get()); - } catch (Exception e) { + // testing the streaming send message + getClient().sendMessage(message, List.of(consumer), errorHandler, null); + + assertTrue(latch.await(10, TimeUnit.SECONDS)); + assertFalse(wasUnexpectedEvent.get()); + assertNull(errorRef.get()); + + Message messageResponse = receivedMessage.get(); + assertNotNull(messageResponse); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + } catch (A2AClientException e) { + fail("Unexpected exception during sendMessage: " + e.getMessage(), e); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } @@ -653,6 +461,7 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { public void testResubscribeExistingTaskSuccess() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); saveTaskInTaskStore(MINIMAL_TASK); + Client client = null; try { // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap @@ -660,104 +469,100 @@ public void testResubscribeExistingTaskSuccess() throws Exception { // requires the queue to still be active ensureQueueForTask(MINIMAL_TASK.getId()); - CountDownLatch taskResubscriptionRequestSent = new CountDownLatch(1); - CountDownLatch taskResubscriptionResponseReceived = new CountDownLatch(2); - AtomicReference firstResponse = new AtomicReference<>(); - AtomicReference secondResponse = new AtomicReference<>(); - - // resubscribe to the task, requires the task and its queue to still be active - TaskResubscriptionRequest taskResubscriptionRequest = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - - // Count down the latch when the MultiSseSupport on the server has started subscribing - awaitStreamingSubscription() - .whenComplete((unused, throwable) -> taskResubscriptionRequestSent.countDown()); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(taskResubscriptionRequest, null); - + CountDownLatch eventLatch = new CountDownLatch(2); + AtomicReference artifactUpdateEvent = new AtomicReference<>(); + AtomicReference statusUpdateEvent = new AtomicReference<>(); + AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); - responseFuture.thenAccept(response -> { - - if (response.statusCode() != 200) { - throw new IllegalStateException("Status code was " + response.statusCode()); - } - try { - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - SendStreamingMessageResponse sendStreamingMessageResponse = Utils.OBJECT_MAPPER.readValue(line.substring("data: ".length()).trim(), SendStreamingMessageResponse.class); - if (taskResubscriptionResponseReceived.getCount() == 2) { - firstResponse.set(sendStreamingMessageResponse); - } else { - secondResponse.set(sendStreamingMessageResponse); - } - taskResubscriptionResponseReceived.countDown(); - if (taskResubscriptionResponseReceived.getCount() == 0) { - throw new BreakException(); - } - } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); - } catch (BreakException e) { - } - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); + // Create consumer to handle resubscribed events + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof TaskUpdateEvent taskUpdateEvent) { + if (taskUpdateEvent.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifactEvent) { + artifactUpdateEvent.set(artifactEvent); + eventLatch.countDown(); + } else if (taskUpdateEvent.getUpdateEvent() instanceof TaskStatusUpdateEvent statusEvent) { + statusUpdateEvent.set(statusEvent); + eventLatch.countDown(); + } else { + wasUnexpectedEvent.set(true); + } + } else { + wasUnexpectedEvent.set(true); } - return null; - }); + }; - try { - taskResubscriptionRequestSent.await(); - List events = List.of( - new TaskArtifactUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .artifact(new Artifact.Builder() - .artifactId("11") - .parts(new TextPart("text")) - .build()) - .build(), - new TaskStatusUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) - .build()); - - for (Event event : events) { - enqueueEventOnServer(event); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + // Create error handler + Consumer errorHandler = error -> { + errorRef.set(error); + eventLatch.countDown(); + }; - // wait for the client to receive the responses - taskResubscriptionResponseReceived.await(); + // Get client for resubscription + client = getClient(); + + // Count down when the streaming subscription is established + CountDownLatch subscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); + + // Resubscribe to the task with specific consumer and error handler + List> consumers = consumer != null ? List.of(consumer) : List.of(); + client.resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), consumers, errorHandler, null); + + // Wait for subscription to be established + assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); + + // Enqueue events on the server + List events = List.of( + new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + new TaskStatusUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build()); + + for (Event event : events) { + enqueueEventOnServer(event); + } - assertNotNull(firstResponse.get()); - SendStreamingMessageResponse sendStreamingMessageResponse = firstResponse.get(); - assertNull(sendStreamingMessageResponse.getError()); - TaskArtifactUpdateEvent taskArtifactUpdateEvent = (TaskArtifactUpdateEvent) sendStreamingMessageResponse.getResult(); - assertEquals(MINIMAL_TASK.getId(), taskArtifactUpdateEvent.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), taskArtifactUpdateEvent.getContextId()); - Part part = taskArtifactUpdateEvent.getArtifact().parts().get(0); + // Wait for events to be received + assertTrue(eventLatch.await(30, TimeUnit.SECONDS)); + assertFalse(wasUnexpectedEvent.get()); + assertNull(errorRef.get()); + + // Verify artifact update event + TaskArtifactUpdateEvent receivedArtifactEvent = artifactUpdateEvent.get(); + assertNotNull(receivedArtifactEvent); + assertEquals(MINIMAL_TASK.getId(), receivedArtifactEvent.getTaskId()); + assertEquals(MINIMAL_TASK.getContextId(), receivedArtifactEvent.getContextId()); + Part part = receivedArtifactEvent.getArtifact().parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("text", ((TextPart) part).getText()); - assertNotNull(secondResponse.get()); - sendStreamingMessageResponse = secondResponse.get(); - assertNull(sendStreamingMessageResponse.getError()); - TaskStatusUpdateEvent taskStatusUpdateEvent = (TaskStatusUpdateEvent) sendStreamingMessageResponse.getResult(); - assertEquals(MINIMAL_TASK.getId(), taskStatusUpdateEvent.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), taskStatusUpdateEvent.getContextId()); - assertEquals(TaskState.COMPLETED, taskStatusUpdateEvent.getStatus().state()); - assertNotNull(taskStatusUpdateEvent.getStatus().timestamp()); + // Verify status update event + TaskStatusUpdateEvent receivedStatusEvent = statusUpdateEvent.get(); + assertNotNull(receivedStatusEvent); + assertEquals(MINIMAL_TASK.getId(), receivedStatusEvent.getTaskId()); + assertEquals(MINIMAL_TASK.getContextId(), receivedStatusEvent.getContextId()); + assertEquals(TaskState.COMPLETED, receivedStatusEvent.getStatus().state()); + assertNotNull(receivedStatusEvent.getStatus().timestamp()); } finally { - //setStreamingSubscribedRunnable(null); + try { + if (client != null) { + client.close(); + } + } catch (Exception e) { + // Ignore cleanup errors + } deleteTaskInTaskStore(MINIMAL_TASK.getId()); executorService.shutdown(); if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { @@ -768,53 +573,74 @@ public void testResubscribeExistingTaskSuccess() throws Exception { @Test public void testResubscribeNoExistingTaskError() throws Exception { - TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams("non-existent-task")); + CountDownLatch errorLatch = new CountDownLatch(1); + AtomicReference errorRef = new AtomicReference<>(); - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, null); + // Create error handler to capture the TaskNotFoundError + Consumer errorHandler = error -> { + errorRef.set(error); + errorLatch.countDown(); + }; - CountDownLatch latch = new CountDownLatch(1); - AtomicReference errorRef = new AtomicReference<>(); + // Get client for resubscription + Client client = getClient(); - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertEquals(request.getId(), jsonResponse.getId()); - assertNull(jsonResponse.getResult()); - // this should be an instance of TaskNotFoundError, see https://github.com/a2aproject/a2a-java/issues/23 - assertInstanceOf(JSONRPCError.class, jsonResponse.getError()); - assertEquals(new TaskNotFoundError().getCode(), jsonResponse.getError().getCode()); - latch.countDown(); + try { + client.resubscribe(new TaskIdParams("non-existent-task"), List.of(), errorHandler, null); + + // Wait for error to be captured (may come via error handler for streaming) + boolean errorReceived = errorLatch.await(10, TimeUnit.SECONDS); + + if (errorReceived) { + // Error came via error handler + Throwable error = errorRef.get(); + assertNotNull(error); + if (error instanceof A2AClientException) { + assertInstanceOf(TaskNotFoundError.class, ((A2AClientException) error).getCause()); + } else { + // Check if it's directly a TaskNotFoundError or walk the cause chain + Throwable cause = error; + boolean foundTaskNotFound = false; + while (cause != null && !foundTaskNotFound) { + if (cause instanceof TaskNotFoundError) { + foundTaskNotFound = true; + } + cause = cause.getCause(); + } + if (!foundTaskNotFound) { + fail("Expected TaskNotFoundError in error chain"); } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); + } else { + fail("Expected error for non-existent task resubscription"); } - latch.countDown(); - return null; - }); - - boolean dataRead = latch.await(20, TimeUnit.SECONDS); - Assertions.assertTrue(dataRead); - Assertions.assertNull(errorRef.get()); + } catch (A2AClientException e) { + // Also acceptable - the client might throw an exception immediately + assertInstanceOf(TaskNotFoundError.class, e.getCause()); + } finally { + try { + if (client != null) { + client.close(); + } + } catch (Exception e) { + // Ignore cleanup errors + } + } } @Test public void testStreamingMethodWithAcceptHeader() throws Exception { + // Skip this test for gRPC transport as it uses HTTP SSE which is JSONRPC-specific + Assumptions.assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "HTTP streaming tests are only supported for JSONRPC transport"); testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); } @Test public void testSendMessageStreamNewMessageSuccess() throws Exception { + // Skip this test for gRPC transport as it uses HTTP SSE which is JSONRPC-specific + Assumptions.assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "HTTP streaming tests are only supported for JSONRPC transport"); testSendStreamingMessage(null); } @@ -885,7 +711,7 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(2, result.size()); assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1), result.get(0)); @@ -915,7 +741,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { // will overwrite the previous one savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(1, result.size()); @@ -936,7 +762,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { @Test public void testListPushNotificationConfigTaskNotFound() { try { - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams("non-existent-task"), null); fail(); } catch (A2AClientException e) { @@ -948,11 +774,11 @@ public void testListPushNotificationConfigTaskNotFound() { public void testListPushNotificationConfigEmptyList() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(0, result.size()); } catch (Exception e) { - fail(); + fail(e.getMessage()); } finally { deleteTaskInTaskStore(MINIMAL_TASK.getId()); } @@ -983,21 +809,21 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception try { // specify the config ID to delete - client.deleteTaskPushNotificationConfigurations( + getClient().deleteTaskPushNotificationConfigurations( new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "config1"), null); // should now be 1 left - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(1, result.size()); // should remain unchanged, this is a different task - result = client.listTaskPushNotificationConfigurations( + result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams("task-456"), null); assertEquals(1, result.size()); } catch (Exception e) { - fail(); + fail(e.getMessage()); } finally { deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); @@ -1024,12 +850,12 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { - client.deleteTaskPushNotificationConfigurations( + getClient().deleteTaskPushNotificationConfigurations( new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "non-existent-config-id"), null); // should remain unchanged - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(2, result.size()); } catch (Exception e) { @@ -1044,7 +870,7 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc @Test public void testDeletePushNotificationConfigTaskNotFound() { try { - client.deleteTaskPushNotificationConfigurations( + getClient().deleteTaskPushNotificationConfigurations( new DeleteTaskPushNotificationConfigParams("non-existent-task", "non-existent-config-id"), null); @@ -1071,12 +897,12 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { - client.deleteTaskPushNotificationConfigurations( + getClient().deleteTaskPushNotificationConfigurations( new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()), null); // should now be 0 - List result = client.listTaskPushNotificationConfigurations( + List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); assertEquals(0, result.size()); } catch (Exception e) { @@ -1300,6 +1126,88 @@ protected void savePushNotificationConfigInStore(String taskId, PushNotification } } + /** + * Get a client instance. + */ + protected Client getClient() throws A2AClientException { + if (client == null) { + client = createClient(true); + } + return client; + } + + /** + * Get a client configured for non-streaming operations. + */ + protected Client getNonStreamingClient() throws A2AClientException { + if (nonStreamingClient == null) { + nonStreamingClient = createClient(false); + } + return nonStreamingClient; + } + + /** + * Create a client with the specified streaming configuration. + */ + private Client createClient(boolean streaming) throws A2AClientException { + AgentCard agentCard = createTestAgentCard(); + ClientConfig clientConfig = createClientConfig(streaming); + ClientFactory clientFactory = new ClientFactory(clientConfig); + return clientFactory.create(agentCard, List.of(), null, null); + } + + + + /** + * Create a test agent card with the appropriate transport configuration. + */ + private AgentCard createTestAgentCard() { + return new AgentCard.Builder() + .name("test-card") + .description("A test agent card") + .url(getTransportUrl()) + .version("1.0") + .documentationUrl("http://example.com/docs") + .preferredTransport(getTransportProtocol()) + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(true) + .stateTransitionHistory(true) + .build()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .skills(List.of()) + .additionalInterfaces(List.of(new AgentInterface(getTransportProtocol(), getTransportUrl()))) + .protocolVersion("0.2.5") + .build(); + } + + /** + * Create client configuration with transport-specific settings. + */ + private ClientConfig createClientConfig(boolean streaming) { + ClientConfig.Builder builder = new ClientConfig.Builder() + .setStreaming(streaming) + .setSupportedTransports(List.of(getTransportProtocol())) + .setAcceptedOutputModes(List.of("text")); + + // Set transport-specific configuration + List transportConfigs = new ArrayList<>(); + if (TransportProtocol.JSONRPC.asString().equals(getTransportProtocol())) { + transportConfigs.add(new JSONRPCTransportConfig(new JdkA2AHttpClient())); + } else if (TransportProtocol.GRPC.asString().equals(getTransportProtocol())) { + // For gRPC, use a function that creates a channel with plaintext communication + transportConfigs.add(new GrpcTransportConfig( + target -> ManagedChannelBuilder.forTarget(target).usePlaintext().build())); + } + + if (!transportConfigs.isEmpty()) { + builder.setClientTransportConfigs(transportConfigs); + } + + return builder.build(); + } + private static class BreakException extends RuntimeException { } From a56e270333247a3857947acc4db5b59297c7ff3c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 09:23:24 -0400 Subject: [PATCH 076/493] fix: Ensure onSetTaskPushNotificationConfig returns the updated pushNotificationConfig --- .../DefaultRequestHandler.java | 5 ++- .../InMemoryPushNotificationConfigStore.java | 3 +- .../tasks/PushNotificationConfigStore.java | 3 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 31 +++++++++++++------ .../jsonrpc/handler/JSONRPCHandlerTest.java | 5 ++- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index e79a97201..7cb223c98 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -273,9 +273,8 @@ public TaskPushNotificationConfig onSetTaskPushNotificationConfig( throw new TaskNotFoundError(); } - pushConfigStore.setInfo(params.taskId(), params.pushNotificationConfig()); - - return params; + PushNotificationConfig pushNotificationConfig = pushConfigStore.setInfo(params.taskId(), params.pushNotificationConfig()); + return new TaskPushNotificationConfig(params.taskId(), pushNotificationConfig); } @Override diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index e66fc1669..eac60fbc6 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -27,7 +27,7 @@ public InMemoryPushNotificationConfigStore() { } @Override - public void setInfo(String taskId, PushNotificationConfig notificationConfig) { + public PushNotificationConfig setInfo(String taskId, PushNotificationConfig notificationConfig) { List notificationConfigList = pushNotificationInfos.getOrDefault(taskId, new ArrayList<>()); PushNotificationConfig.Builder builder = new PushNotificationConfig.Builder(notificationConfig); if (notificationConfig.id() == null) { @@ -45,6 +45,7 @@ public void setInfo(String taskId, PushNotificationConfig notificationConfig) { } notificationConfigList.add(notificationConfig); pushNotificationInfos.put(taskId, notificationConfigList); + return notificationConfig; } @Override diff --git a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java index 68f132620..de7a27deb 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java @@ -13,8 +13,9 @@ public interface PushNotificationConfigStore { * Sets or updates the push notification configuration for a task. * @param taskId the task ID * @param notificationConfig the push notification configuration + * @return the potentially updated push notification configuration */ - void setInfo(String taskId, PushNotificationConfig notificationConfig); + PushNotificationConfig setInfo(String taskId, PushNotificationConfig notificationConfig); /** * Retrieves the push notification configuration for a task. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index ddeaa11c7..8aaf9224a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -687,24 +687,38 @@ public static MessageSendParams messageSendParams(io.a2a.grpc.SendMessageRequest return builder.build(); } - public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { - return taskPushNotificationConfig(request.getConfig()); + public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequestOrBuilder request) { + return taskPushNotificationConfig(request.getConfig(), true); } - public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfig config) { + public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfigOrBuilder config) { + return taskPushNotificationConfig(config, false); + } + + private static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfigOrBuilder config, boolean create) { String name = config.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" String[] parts = name.split("/"); - if (parts.length < 4) { - throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + name); + String configId = ""; + if (create) { + if (parts.length < 3) { + throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + name); + } + if (parts.length == 4) { + configId = parts[3]; + } + } else { + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + name); + } + configId = parts[3]; } String taskId = parts[1]; - String configId = parts[3]; PushNotificationConfig pnc = pushNotification(config.getPushNotificationConfig(), configId); return new TaskPushNotificationConfig(taskId, pnc); } public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { - String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" or /tasks/{id} + String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" String[] parts = name.split("/"); String taskId = parts[1]; String configId; @@ -775,9 +789,6 @@ private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificat } private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification) { - /*if (pushNotification == null) { - return null; - }*/ return pushNotification(pushNotification, pushNotification.getId()); } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 728a26d9d..0754cd055 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -557,7 +557,10 @@ public void testSetPushNotificationConfigSuccess() { MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - Assertions.assertSame(taskPushConfig, response.getResult()); + TaskPushNotificationConfig taskPushConfigResult = + new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").id(MINIMAL_TASK.getId()).build()); + Assertions.assertEquals(taskPushConfigResult, response.getResult()); } @Test From 8bfa988f73fe78732a0c6a760da00acdc5481782 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 10:31:24 -0400 Subject: [PATCH 077/493] fix: Test cleanup --- .../apps/common/AbstractA2AServerTest.java | 51 ++----------------- .../grpc/handler/GrpcHandlerTest.java | 6 +++ 2 files changed, 9 insertions(+), 48 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index d9464033c..26b572b9c 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -383,24 +383,7 @@ public void testGetAgentCard() throws A2AClientException { assertTrue(agentCard.capabilities().streaming()); assertTrue(agentCard.capabilities().stateTransitionHistory()); assertTrue(agentCard.skills().isEmpty()); - } - - @Test - public void testGetExtendAgentCardNotSupported() { - GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); - GetAuthenticatedExtendedCardResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(request) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(GetAuthenticatedExtendedCardResponse.class); - assertEquals("1", response.getId()); - assertInstanceOf(JSONRPCError.class, response.getError()); - assertEquals(new AuthenticatedExtendedCardNotConfiguredError().getCode(), response.getError().getCode()); - assertNull(response.getResult()); + assertFalse(agentCard.supportsAuthenticatedExtendedCard()); } @Test @@ -459,10 +442,7 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { @Test @Timeout(value = 3, unit = TimeUnit.MINUTES) public void testResubscribeExistingTaskSuccess() throws Exception { - ExecutorService executorService = Executors.newSingleThreadExecutor(); saveTaskInTaskStore(MINIMAL_TASK); - Client client = null; - try { // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap // does not work because after the message is sent, the queue becomes null but task resubscription @@ -498,9 +478,6 @@ public void testResubscribeExistingTaskSuccess() throws Exception { eventLatch.countDown(); }; - // Get client for resubscription - client = getClient(); - // Count down when the streaming subscription is established CountDownLatch subscriptionLatch = new CountDownLatch(1); awaitStreamingSubscription() @@ -508,7 +485,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { // Resubscribe to the task with specific consumer and error handler List> consumers = consumer != null ? List.of(consumer) : List.of(); - client.resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), consumers, errorHandler, null); + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), consumers, errorHandler, null); // Wait for subscription to be established assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); @@ -556,18 +533,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { assertEquals(TaskState.COMPLETED, receivedStatusEvent.getStatus().state()); assertNotNull(receivedStatusEvent.getStatus().timestamp()); } finally { - try { - if (client != null) { - client.close(); - } - } catch (Exception e) { - // Ignore cleanup errors - } deleteTaskInTaskStore(MINIMAL_TASK.getId()); - executorService.shutdown(); - if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { - executorService.shutdownNow(); - } } } @@ -582,11 +548,8 @@ public void testResubscribeNoExistingTaskError() throws Exception { errorLatch.countDown(); }; - // Get client for resubscription - Client client = getClient(); - try { - client.resubscribe(new TaskIdParams("non-existent-task"), List.of(), errorHandler, null); + getClient().resubscribe(new TaskIdParams("non-existent-task"), List.of(), errorHandler, null); // Wait for error to be captured (may come via error handler for streaming) boolean errorReceived = errorLatch.await(10, TimeUnit.SECONDS); @@ -617,14 +580,6 @@ public void testResubscribeNoExistingTaskError() throws Exception { } catch (A2AClientException e) { // Also acceptable - the client might throw an exception immediately assertInstanceOf(TaskNotFoundError.class, e.getCause()); - } finally { - try { - if (client != null) { - client.close(); - } - } catch (Exception e) { - // Ignore cleanup errors - } } } diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index c49f91d84..e5aba9097 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -50,6 +50,7 @@ import io.grpc.stub.StreamObserver; import mutiny.zero.ZeroPublisher; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; @@ -738,6 +739,11 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } + @Disabled + public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { + // TODO - getting the authenticated extended agent card isn't support for gRPC right now + } + private StreamRecorder sendMessageRequest(GrpcHandler handler) throws Exception { SendMessageRequest request = SendMessageRequest.newBuilder() .setRequest(GRPC_MESSAGE) From 779bc45170a2d7697cfed384a53ca727cf75e6cd Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 12:38:20 -0400 Subject: [PATCH 078/493] fix: Check for empty notification ID in InMemoryPushNotificationConfigStore#setInfo --- .../a2a/server/tasks/InMemoryPushNotificationConfigStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index eac60fbc6..451b60451 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -30,7 +30,7 @@ public InMemoryPushNotificationConfigStore() { public PushNotificationConfig setInfo(String taskId, PushNotificationConfig notificationConfig) { List notificationConfigList = pushNotificationInfos.getOrDefault(taskId, new ArrayList<>()); PushNotificationConfig.Builder builder = new PushNotificationConfig.Builder(notificationConfig); - if (notificationConfig.id() == null) { + if (notificationConfig.id() == null || notificationConfig.id().isEmpty()) { builder.id(taskId); } notificationConfig = builder.build(); From 78c9b7537d155d2e1b527d734c23939d8528f8ca Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 12:58:13 -0400 Subject: [PATCH 079/493] fix: Check if pushNotification is the default value for gRPC in FromProto#pushNotificationConfig --- spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 8aaf9224a..e53b0dc9b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -780,6 +780,9 @@ private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.Sen } private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification, String configId) { + if (pushNotification == null || pushNotification.getDefaultInstanceForType().equals(pushNotification)) { + return null; + } return new PushNotificationConfig( pushNotification.getUrl(), pushNotification.getToken().isEmpty() ? null : pushNotification.getToken(), From e497f15dbb02c2003faf708fbca8d06d0b3e6dcd Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 14:11:36 -0400 Subject: [PATCH 080/493] fix: Update JSONRPCTransport#getAgentCard so the extended agent card gets cached once retrieved --- .../a2a/client/transport/jsonrpc/JSONRPCTransport.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index c22690274..d3d93e463 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -75,6 +75,7 @@ public class JSONRPCTransport implements ClientTransport { private final String agentUrl; private final List interceptors; private AgentCard agentCard; + private boolean needsExtendedCard = false; public JSONRPCTransport(String agentUrl) { this(null, null, agentUrl, null); @@ -90,6 +91,7 @@ public JSONRPCTransport(A2AHttpClient httpClient, AgentCard agentCard, this.agentCard = agentCard; this.agentUrl = agentUrl; this.interceptors = interceptors; + this.needsExtendedCard = agentCard == null || agentCard.supportsAuthenticatedExtendedCard(); } @Override @@ -332,8 +334,9 @@ public AgentCard getAgentCard(ClientCallContext context) throws A2AClientExcepti if (agentCard == null) { resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context)); agentCard = resolver.getAgentCard(); + needsExtendedCard = agentCard.supportsAuthenticatedExtendedCard(); } - if (!agentCard.supportsAuthenticatedExtendedCard()) { + if (!needsExtendedCard) { return agentCard; } @@ -349,7 +352,9 @@ public AgentCard getAgentCard(ClientCallContext context) throws A2AClientExcepti String httpResponseBody = sendPostRequest(payloadAndHeaders); GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody, GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE); - return response.getResult(); + agentCard = response.getResult(); + needsExtendedCard = false; + return agentCard; } catch (IOException | InterruptedException e) { throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); } From ac7290afccf1cb73b496144cb0aa76fa6177bcb7 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 14:32:08 -0400 Subject: [PATCH 081/493] fix: Test cleanup --- .../apps/common/AbstractA2AServerTest.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 26b572b9c..4758aab02 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1,7 +1,5 @@ package io.a2a.server.apps.common; -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -21,8 +19,6 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -45,8 +41,6 @@ import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; import io.a2a.client.transport.grpc.GrpcTransportConfig; import io.a2a.client.http.JdkA2AHttpClient; -import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; -import io.a2a.spec.JSONRPCError; import io.grpc.ManagedChannelBuilder; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; @@ -474,7 +468,9 @@ public void testResubscribeExistingTaskSuccess() throws Exception { // Create error handler Consumer errorHandler = error -> { - errorRef.set(error); + if (!isStreamClosedError(error)) { + errorRef.set(error); + } eventLatch.countDown(); }; @@ -484,8 +480,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); // Resubscribe to the task with specific consumer and error handler - List> consumers = consumer != null ? List.of(consumer) : List.of(); - getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), consumers, errorHandler, null); + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), List.of(consumer), errorHandler, null); // Wait for subscription to be established assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); @@ -544,7 +539,9 @@ public void testResubscribeNoExistingTaskError() throws Exception { // Create error handler to capture the TaskNotFoundError Consumer errorHandler = error -> { - errorRef.set(error); + if (!isStreamClosedError(error)) { + errorRef.set(error); + } errorLatch.countDown(); }; @@ -578,8 +575,7 @@ public void testResubscribeNoExistingTaskError() throws Exception { fail("Expected error for non-existent task resubscription"); } } catch (A2AClientException e) { - // Also acceptable - the client might throw an exception immediately - assertInstanceOf(TaskNotFoundError.class, e.getCause()); + fail("Expected error for non-existent task resubscription"); } } From 3c5dfff0800e4262870504aca4c9e1738e8c390a Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 15:25:41 -0400 Subject: [PATCH 082/493] fix: Require channel configuration for gRPC --- .../transport/grpc/GrpcTransportProvider.java | 11 +++---- .../jsonrpc/JSONRPCTransportProvider.java | 3 +- .../spi/ClientTransportProvider.java | 4 ++- .../grpc/quarkus/QuarkusA2AGrpcTest.java | 33 +++++++++++++++++++ .../apps/common/AbstractA2AServerTest.java | 16 ++++----- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java index 18e66fdb6..bb28ef9c9 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -7,10 +7,10 @@ import io.a2a.client.config.ClientTransportConfig; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.TransportProtocol; import io.grpc.Channel; -import io.grpc.ManagedChannelBuilder; /** * Provider for gRPC transport implementation. @@ -19,21 +19,18 @@ public class GrpcTransportProvider implements ClientTransportProvider { @Override public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, - String agentUrl, List interceptors) { + String agentUrl, List interceptors) throws A2AClientException { // not making use of the interceptors for gRPC for now - Channel channel; List clientTransportConfigs = clientConfig.getClientTransportConfigs(); if (clientTransportConfigs != null) { for (ClientTransportConfig clientTransportConfig : clientTransportConfigs) { if (clientTransportConfig instanceof GrpcTransportConfig grpcTransportConfig) { - channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); + Channel channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); return new GrpcTransport(channel, agentCard); } } } - // no channel factory configured - channel = ManagedChannelBuilder.forTarget(agentUrl).build(); - return new GrpcTransport(channel, agentCard); + throw new A2AClientException("Missing required GrpcTransportConfig"); } @Override diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java index a72a86294..b9f8ce42b 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -8,6 +8,7 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.TransportProtocol; @@ -15,7 +16,7 @@ public class JSONRPCTransportProvider implements ClientTransportProvider { @Override public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, - String agentUrl, List interceptors) { + String agentUrl, List interceptors) throws A2AClientException { A2AHttpClient httpClient = null; List clientTransportConfigs = clientConfig.getClientTransportConfigs(); if (clientTransportConfigs != null) { diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java index 5ebed06a9..e4127ff82 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java @@ -4,6 +4,7 @@ import io.a2a.client.config.ClientCallInterceptor; import io.a2a.client.config.ClientConfig; +import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; /** @@ -19,9 +20,10 @@ public interface ClientTransportProvider { * @param agentUrl the remote agent's URL * @param interceptors the optional interceptors to use for a client call (may be {@code null}) * @return the client transport + * @throws io.a2a.spec.A2AClientException if an error occurs trying to create the client */ ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, - String agentUrl, List interceptors); + String agentUrl, List interceptors) throws A2AClientException; /** * Get the name of the client transport. diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java index 1a91eebde..959188408 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -1,12 +1,24 @@ package io.a2a.server.grpc.quarkus; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import io.a2a.client.config.ClientTransportConfig; +import io.a2a.client.transport.grpc.GrpcTransportConfig; import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.TransportProtocol; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.AfterAll; + @QuarkusTest public class QuarkusA2AGrpcTest extends AbstractA2AServerTest { + private static ManagedChannel channel; + public QuarkusA2AGrpcTest() { super(8081); // HTTP server port for utility endpoints } @@ -20,4 +32,25 @@ protected String getTransportProtocol() { protected String getTransportUrl() { return "localhost:9001"; // gRPC server runs on port 9001 } + + @Override + protected List getClientTransportConfigs() { + List transportConfigs = new ArrayList<>(); + transportConfigs.add(new GrpcTransportConfig( + target -> { + channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); + return channel; + })); + return transportConfigs; + } + + @AfterAll + public static void closeChannel() { + channel.shutdownNow(); + try { + channel.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } } \ No newline at end of file diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 4758aab02..bec07deff 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -134,6 +134,13 @@ protected AbstractA2AServerTest(int serverPort) { */ protected abstract String getTransportUrl(); + /** + * Get the transport configs to use for this test. + */ + protected List getClientTransportConfigs() { + return new ArrayList<>(); + } + @Test public void testTaskStoreMethodsSanityTest() throws Exception { Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build(); @@ -1143,14 +1150,7 @@ private ClientConfig createClientConfig(boolean streaming) { .setAcceptedOutputModes(List.of("text")); // Set transport-specific configuration - List transportConfigs = new ArrayList<>(); - if (TransportProtocol.JSONRPC.asString().equals(getTransportProtocol())) { - transportConfigs.add(new JSONRPCTransportConfig(new JdkA2AHttpClient())); - } else if (TransportProtocol.GRPC.asString().equals(getTransportProtocol())) { - // For gRPC, use a function that creates a channel with plaintext communication - transportConfigs.add(new GrpcTransportConfig( - target -> ManagedChannelBuilder.forTarget(target).usePlaintext().build())); - } + List transportConfigs = getClientTransportConfigs(); if (!transportConfigs.isEmpty()) { builder.setClientTransportConfigs(transportConfigs); From be35a32d99d0286702bf9ade135e7c86d613fe94 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 15:32:21 -0400 Subject: [PATCH 083/493] fix: Move JSONRPC specific test cases to QuarkusA2AJSONRPCTest --- .../apps/quarkus/QuarkusA2AJSONRPCTest.java | 120 ++++++++++++++++ .../apps/common/AbstractA2AServerTest.java | 129 +----------------- 2 files changed, 123 insertions(+), 126 deletions(-) diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java index ff61a433b..7b6257d58 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java @@ -2,23 +2,45 @@ import static io.restassured.RestAssured.given; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import static org.wildfly.common.Assert.assertNotNull; import jakarta.ws.rs.core.MediaType; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import com.fasterxml.jackson.core.JsonProcessingException; + import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.A2AClientException; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCErrorResponse; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendParams; import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.Part; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SendStreamingMessageResponse; +import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; +import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; +import io.a2a.util.Utils; import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @QuarkusTest @@ -177,4 +199,102 @@ private void testGetTask(String mediaType) throws Exception { } } + @Test + public void testStreamingMethodWithAcceptHeader() throws Exception { + testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); + } + + @Test + public void testSendMessageStreamNewMessageSuccess() throws Exception { + testSendStreamingMessage(null); + } + + private void testSendStreamingMessage(String mediaType) throws Exception { + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + SendStreamingMessageRequest request = new SendStreamingMessageRequest( + "1", new MessageSendParams(message, null, null)); + + CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference errorRef = new AtomicReference<>(); + + responseFuture.thenAccept(response -> { + if (response.statusCode() != 200) { + //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); + throw new IllegalStateException("Status code was " + response.statusCode()); + } + response.body().forEach(line -> { + try { + SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); + if (jsonResponse != null) { + assertNull(jsonResponse.getError()); + Message messageResponse = (Message) jsonResponse.getResult(); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + latch.countDown(); + } + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + }).exceptionally(t -> { + if (!isStreamClosedError(t)) { + errorRef.set(t); + } + latch.countDown(); + return null; + }); + + + boolean dataRead = latch.await(20, TimeUnit.SECONDS); + Assertions.assertTrue(dataRead); + Assertions.assertNull(errorRef.get()); + + } + + private CompletableFuture>> initialiseStreamingRequest( + StreamingJSONRPCRequest request, String mediaType) throws Exception { + + // Create the client + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + + // Create the request + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/")) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) + .header("Content-Type", APPLICATION_JSON); + if (mediaType != null) { + builder.header("Accept", mediaType); + } + HttpRequest httpRequest = builder.build(); + + + // Send request async and return the CompletableFuture + return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); + } + + private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { + line = extractSseData(line); + if (line != null) { + return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); + } + return null; + } + + private static String extractSseData(String line) { + if (line.startsWith("data:")) { + line = line.substring(5).trim(); + return line; + } + return null; + } } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index bec07deff..63a07e395 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -23,14 +23,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; import java.util.function.BiConsumer; import java.util.function.Consumer; -import jakarta.ws.rs.core.MediaType; - -import com.fasterxml.jackson.core.JsonProcessingException; - import io.a2a.client.Client; import io.a2a.client.ClientEvent; import io.a2a.client.ClientFactory; @@ -38,29 +33,18 @@ import io.a2a.client.TaskUpdateEvent; import io.a2a.client.config.ClientConfig; import io.a2a.client.config.ClientTransportConfig; -import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; -import io.a2a.client.transport.grpc.GrpcTransportConfig; -import io.a2a.client.http.JdkA2AHttpClient; -import io.grpc.ManagedChannelBuilder; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentInterface; -import io.a2a.spec.TransportProtocol; import io.a2a.spec.Artifact; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.Event; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; -import io.a2a.spec.MessageSendParams; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; @@ -74,8 +58,6 @@ import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -109,14 +91,14 @@ public abstract class AbstractA2AServerTest { .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - private static final Message MESSAGE = new Message.Builder() + protected static final Message MESSAGE = new Message.Builder() .messageId("111") .role(Message.Role.AGENT) .parts(new TextPart("test message")) .build(); public static final String APPLICATION_JSON = "application/json"; - private final int serverPort; + protected final int serverPort; private Client client; private Client nonStreamingClient; @@ -586,72 +568,6 @@ public void testResubscribeNoExistingTaskError() throws Exception { } } - @Test - public void testStreamingMethodWithAcceptHeader() throws Exception { - // Skip this test for gRPC transport as it uses HTTP SSE which is JSONRPC-specific - Assumptions.assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), - "HTTP streaming tests are only supported for JSONRPC transport"); - testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); - } - - @Test - public void testSendMessageStreamNewMessageSuccess() throws Exception { - // Skip this test for gRPC transport as it uses HTTP SSE which is JSONRPC-specific - Assumptions.assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), - "HTTP streaming tests are only supported for JSONRPC transport"); - testSendStreamingMessage(null); - } - - private void testSendStreamingMessage(String mediaType) throws Exception { - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest( - "1", new MessageSendParams(message, null, null)); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference errorRef = new AtomicReference<>(); - - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertNull(jsonResponse.getError()); - Message messageResponse = (Message) jsonResponse.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - latch.countDown(); - } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); - } - latch.countDown(); - return null; - }); - - - boolean dataRead = latch.await(20, TimeUnit.SECONDS); - Assertions.assertTrue(dataRead); - Assertions.assertNull(errorRef.get()); - - } - @Test public void testListPushNotificationConfigWithConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -871,23 +787,7 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio } } - private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { - line = extractSseData(line); - if (line != null) { - return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); - } - return null; - } - - private static String extractSseData(String line) { - if (line.startsWith("data:")) { - line = line.substring(5).trim(); - return line; - } - return null; - } - - private boolean isStreamClosedError(Throwable throwable) { + protected boolean isStreamClosedError(Throwable throwable) { // Unwrap the CompletionException Throwable cause = throwable; @@ -900,29 +800,6 @@ private boolean isStreamClosedError(Throwable throwable) { return false; } - private CompletableFuture>> initialiseStreamingRequest( - StreamingJSONRPCRequest request, String mediaType) throws Exception { - - // Create the client - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - - // Create the request - HttpRequest.Builder builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/")) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) - .header("Content-Type", APPLICATION_JSON); - if (mediaType != null) { - builder.header("Accept", mediaType); - } - HttpRequest httpRequest = builder.build(); - - - // Send request async and return the CompletableFuture - return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); - } - protected void saveTaskInTaskStore(Task task) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) From 2f4d3f8aeaf3cc8af5ba5f6dedf07be0f0572659 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 15:42:44 -0400 Subject: [PATCH 084/493] fix: Apply Gemini suggestions --- .../src/main/java/io/a2a/client/Client.java | 43 ++++++++----------- .../java/io/a2a/client/ClientTaskManager.java | 11 ++--- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index e3d0c556d..103b78eef 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -45,38 +45,14 @@ public Client(AgentCard agentCard, ClientConfig clientConfig, ClientTransport cl @Override public void sendMessage(Message request, ClientCallContext context) throws A2AClientException { - MessageSendConfiguration messageSendConfiguration = new MessageSendConfiguration.Builder() - .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) - .blocking(clientConfig.isPolling()) - .historyLength(clientConfig.getHistoryLength()) - .pushNotification(clientConfig.getPushNotificationConfig()) - .build(); - - MessageSendParams messageSendParams = new MessageSendParams.Builder() - .message(request) - .configuration(messageSendConfiguration) - .metadata(clientConfig.getMetadata()) - .build(); - + MessageSendParams messageSendParams = getMessageSendParams(request, clientConfig); sendMessage(messageSendParams, null, null, context); } @Override public void sendMessage(Message request, List> consumers, Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException { - MessageSendConfiguration messageSendConfiguration = new MessageSendConfiguration.Builder() - .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) - .blocking(clientConfig.isPolling()) - .historyLength(clientConfig.getHistoryLength()) - .pushNotification(clientConfig.getPushNotificationConfig()) - .build(); - - MessageSendParams messageSendParams = new MessageSendParams.Builder() - .message(request) - .configuration(messageSendConfiguration) - .metadata(clientConfig.getMetadata()) - .build(); - + MessageSendParams messageSendParams = getMessageSendParams(request, clientConfig); sendMessage(messageSendParams, consumers, streamingErrorHandler, context); } @@ -240,4 +216,19 @@ private void consume(ClientEvent clientEvent, AgentCard agentCard, List history = task.getHistory(); + List history = new ArrayList<>(task.getHistory()); history.add(taskStatusUpdateEvent.getStatus().message()); taskBuilder.history(history); } } if (taskStatusUpdateEvent.getMetadata() != null) { - Map metadata = taskStatusUpdateEvent.getMetadata(); - if (metadata == null) { - metadata = new HashMap<>(); - } - metadata.putAll(taskStatusUpdateEvent.getMetadata()); - taskBuilder.metadata(metadata); + Map newMetadata = task.getMetadata() != null ? new HashMap<>(task.getMetadata()) : new HashMap<>(); + newMetadata.putAll(taskStatusUpdateEvent.getMetadata()); + taskBuilder.metadata(newMetadata); } taskBuilder.status(taskStatusUpdateEvent.getStatus()); currentTask = taskBuilder.build(); From 313712f070e2d1d99a793b7755802700457b3655 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 16:08:01 -0400 Subject: [PATCH 085/493] fix: Remove unneeded dependency from client/base/pom.xml --- client/base/pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 79c5966c9..d39f6b91f 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -54,10 +54,6 @@ mockserver-netty test - - io.grpc - grpc-api - \ No newline at end of file From 7c955fc557cf31b817f01d2d29f6a2bcae0a78d1 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 16:42:43 -0400 Subject: [PATCH 086/493] fix: Test clean up --- .../server/apps/common/AbstractA2AServerTest.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 63a07e395..dd306b0d9 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -239,7 +239,7 @@ public void testSendMessageNewMessageSuccess() throws Exception { // testing the non-streaming send message getNonStreamingClient().sendMessage(message, List.of(consumer), null, null); - + assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(wasUnexpectedEvent.get()); Message messageResponse = receivedMessage.get(); @@ -344,7 +344,7 @@ public void testError() throws A2AClientException { try { getNonStreamingClient().sendMessage(message, null); - + // For non-streaming clients, the error should still be thrown as an exception fail("Expected A2AClientException for unsupported send message operation"); } catch (A2AClientException e) { @@ -407,7 +407,7 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(wasUnexpectedEvent.get()); assertNull(errorRef.get()); - + Message messageResponse = receivedMessage.get(); assertNotNull(messageResponse); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); @@ -536,10 +536,10 @@ public void testResubscribeNoExistingTaskError() throws Exception { try { getClient().resubscribe(new TaskIdParams("non-existent-task"), List.of(), errorHandler, null); - + // Wait for error to be captured (may come via error handler for streaming) boolean errorReceived = errorLatch.await(10, TimeUnit.SECONDS); - + if (errorReceived) { // Error came via error handler Throwable error = errorRef.get(); @@ -1028,7 +1028,7 @@ private ClientConfig createClientConfig(boolean streaming) { // Set transport-specific configuration List transportConfigs = getClientTransportConfigs(); - + if (!transportConfigs.isEmpty()) { builder.setClientTransportConfigs(transportConfigs); } @@ -1036,7 +1036,4 @@ private ClientConfig createClientConfig(boolean streaming) { return builder.build(); } - private static class BreakException extends RuntimeException { - - } } \ No newline at end of file From e077233d724db07a5f8b38d24d2a187a2a969d7f Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 20 Aug 2025 16:59:23 -0400 Subject: [PATCH 087/493] fix: Add details about how to use the new Client to the README and add some convenience methods to AbstractClient --- README.md | 289 ++++++++++++++---- .../java/io/a2a/client/A2ACardResolver.java | 14 + .../java/io/a2a/client/AbstractClient.java | 165 ++++++++++ .../java/io/a2a/client/ClientFactory.java | 6 + examples/helloworld/client/README.md | 2 +- .../examples/helloworld/HelloWorldClient.java | 2 + .../apps/common/AbstractA2AServerTest.java | 56 ++-- 7 files changed, 437 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 0b528adf0..aa41fd0ae 100644 --- a/README.md +++ b/README.md @@ -211,8 +211,14 @@ public class WeatherAgentExecutorProducer { ## A2A Client -The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. -To make use of the Java `A2AClient`, simply add the following dependency: +The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. The Java client implementation currently supports two transport protocols: JSON-RPC 2.0 and gRPC. + +To make use of the Java `Client`: + +### 1. Add the A2A Java SDK Client dependency to your project + +Adding a dependency on `a2a-java-sdk-client` will provide access to a `ClientFactory` +that you can use to create your A2A `Client`. ---- > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* @@ -227,67 +233,222 @@ To make use of the Java `A2AClient`, simply add the following dependency: ``` +### 2. Add dependencies on the A2A Java SDK Client Transport(s) you'd like to use + +You need to add a dependency on at least one of the following client transport modules: + +---- +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* +---- + +```xml + + io.github.a2asdk + a2a-java-sdk-client-transport-jsonrpc + + ${io.a2a.sdk.version} + +``` + +```xml + + io.github.a2asdk + a2a-java-sdk-client-transport-grpc + + ${io.a2a.sdk.version} + +``` + +Support for the HTTP+JSON/REST transport will be coming soon. + ### Sample Usage -#### Create an A2A client +#### Create a Client using the ClientFactory + +```java +// First, get the agent card for the A2A server agent you want to connect to +AgentCard agentCard = new A2ACardResolver("http://localhost:1234").getAgentCard(); + +// Specify configuration for the ClientFactory +ClientConfig clientConfig = new ClientConfig.Builder() + .setAcceptedOutputModes(List.of("text")) + .build(); + +// Create event consumers to handle responses that will be received from the A2A server +// (these consumers will be used for both streaming and non-streaming responses) +List> consumers = List.of( + (event, card) -> { + if (event instanceof MessageEvent messageEvent) { + // handle the messageEvent.getMessage() + ... + } else if (event instanceof TaskEvent taskEvent) { + // handle the taskEvent.getTask() + ... + } else if (event instanceof TaskUpdateEvent updateEvent) { + // handle the updateEvent.getTask() + ... + } + } +); + +// Create a handler that will be used for any errors that occur during streaming +Consumer errorHandler = error -> { + // handle the error.getMessage() + ... +}; + +// Create the client using ClientFactory +ClientFactory clientFactory = new ClientFactory(clientConfig); +Client client = clientFactory.create(agentCard, consumers, errorHandler); +``` + +#### Configuring Transport-Specific Settings + +Different transport protocols can be configured with specific settings using `ClientTransportConfig` implementations. The A2A Java SDK provides `JSONRPCTransportConfig` for the JSON-RPC transport and `GrpcTransportConfig` for the gRPC transport. + +##### JSON-RPC Transport Configuration + +For the JSON-RPC transport, if you'd like to use the default `JdkA2AHttpClient`, no additional +configuration is needed. To use a custom HTTP client instead, simply create a `JSONRPCTransportConfig` +as follows: + +```java +// Create a custom HTTP client +A2AHttpClient customHttpClient = ... + +// Create JSON-RPC transport configuration +JSONRPCTransportConfig jsonrpcConfig = new JSONRPCTransportConfig(customHttpClient); + +// Configure the client with transport-specific settings +ClientConfig clientConfig = new ClientConfig.Builder() + .setAcceptedOutputModes(List.of("text")) + .setClientTransportConfigs(List.of(jsonrpcConfig)) + .build(); +``` + +##### gRPC Transport Configuration + +For the gRPC transport, you must configure a channel factory: ```java -// Create an A2AClient (the URL specified is the server agent's URL, be sure to replace it with the actual URL of the A2A server you want to connect to) -A2AClient client = new A2AClient("http://localhost:1234"); +// Create a channel factory function that takes the agent URL and returns a Channel +Function channelFactory = agentUrl -> { + return ManagedChannelBuilder.forTarget(agentUrl) + ... + .build(); +}; + +// Create gRPC transport configuration +GrpcTransportConfig grpcConfig = new GrpcTransportConfig(channelFactory); + +// Configure the client with transport-specific settings +ClientConfig clientConfig = new ClientConfig.Builder() + .setAcceptedOutputModes(List.of("text")) + .setClientTransportConfigs(List.of(grpcConfig)) + .build(); +``` + +##### Multiple Transport Configurations + +You can specify configuration for multiple transports, the appropriate configuration +will be used based on the selected transport: + +```java +// Configure both JSON-RPC and gRPC transports +List transportConfigs = List.of( + new JSONRPCTransportConfig(...), + new GrpcTransportConfig(...) +); + +ClientConfig clientConfig = new ClientConfig.Builder() + .setAcceptedOutputModes(List.of("text")) + .setClientTransportConfigs(transportConfigs) + .build(); ``` #### Send a message to the A2A server agent ```java // Send a text message to the A2A server agent -Message message = A2A.toUserMessage("tell me a joke"); // the message ID will be automatically generated for you -MessageSendParams params = new MessageSendParams.Builder() - .message(message) - .build(); -SendMessageResponse response = client.sendMessage(params); +Message message = A2A.toUserMessage("tell me a joke"); + +// Send the message (uses configured consumers to handle responses) +// Streaming will automatically be used if supported by both client and server, +// otherwise the non-streaming send message method will be used automatically +client.sendMessage(message); + +// You can also optionally specify a ClientCallContext with call-specific config to use +client.sendMessage(message, clientCallContext); ``` -Note that `A2A#toUserMessage` will automatically generate a message ID for you when creating the `Message` -if you don't specify it. You can also explicitly specify a message ID like this: +#### Send a message with custom event handling ```java -Message message = A2A.toUserMessage("tell me a joke", "message-1234"); // messageId is message-1234 +// Create custom consumers for this specific message +List> customConsumers = List.of( + (event, card) -> { + // handle this specific message's responses + ... + } +); + +// Create custom error handler +Consumer customErrorHandler = error -> { + // handle the error + ... +}; + +Message message = A2A.toUserMessage("tell me a joke"); +client.sendMessage(message, customConsumers, customErrorHandler); ``` #### Get the current state of a task ```java // Retrieve the task with id "task-1234" -GetTaskResponse response = client.getTask("task-1234"); +Task task = client.getTask(new TaskQueryParams("task-1234")); // You can also specify the maximum number of items of history for the task -// to include in the response -GetTaskResponse response = client.getTask(new TaskQueryParams("task-1234", 10)); +// to include in the response and +Task task = client.getTask(new TaskQueryParams("task-1234", 10)); + +// You can also optionally specify a ClientCallContext with call-specific config to use +Task task = client.getTask(new TaskQueryParams("task-1234"), clientCallContext); ``` #### Cancel an ongoing task ```java // Cancel the task we previously submitted with id "task-1234" -CancelTaskResponse response = client.cancelTask("task-1234"); +Task cancelledTask = client.cancelTask(new TaskIdParams("task-1234")); // You can also specify additional properties using a map -Map metadata = ... -CancelTaskResponse response = client.cancelTask(new TaskIdParams("task-1234", metadata)); +Map metadata = Map.of("reason", "user_requested"); +Task cancelledTask = client.cancelTask(new TaskIdParams("task-1234", metadata)); + +// You can also optionally specify a ClientCallContext with call-specific config to use +Task cancelledTask = client.cancelTask(new TaskIdParams("task-1234"), clientCallContext); ``` #### Get a push notification configuration for a task ```java // Get task push notification configuration -GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("task-1234"); +TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams("task-1234")); // The push notification configuration ID can also be optionally specified -GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig("task-1234", "config-4567"); +TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams("task-1234", "config-4567")); // Additional properties can be specified using a map -Map metadata = ... -GetTaskPushNotificationConfigResponse response = client.getTaskPushNotificationConfig(new GetTaskPushNotificationConfigParams("task-1234", "config-1234", metadata)); +Map metadata = Map.of("source", "client"); +TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams("task-1234", "config-1234", metadata)); + +// You can also optionally specify a ClientCallContext with call-specific config to use +TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams("task-1234"), clientCallContext); ``` #### Set a push notification configuration for a task @@ -298,66 +459,68 @@ PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Build .url("https://example.com/callback") .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build(); -SetTaskPushNotificationResponse response = client.setTaskPushNotificationConfig("task-1234", pushNotificationConfig); -``` -#### List the push notification configurations for a task +TaskPushNotificationConfig taskConfig = new TaskPushNotificationConfig.Builder() + .taskId("task-1234") + .pushNotificationConfig(pushNotificationConfig) + .build(); -```java -ListTaskPushNotificationConfigResponse response = client.listTaskPushNotificationConfig("task-1234"); +TaskPushNotificationConfig result = client.setTaskPushNotificationConfiguration(taskConfig); -// Additional properties can be specified using a map -Map metadata = ... -ListTaskPushNotificationConfigResponse response = client.listTaskPushNotificationConfig(new ListTaskPushNotificationConfigParams("task-123", metadata)); +// You can also optionally specify a ClientCallContext with call-specific config to use +TaskPushNotificationConfig result = client.setTaskPushNotificationConfiguration(taskConfig, clientCallContext); ``` -#### Delete a push notification configuration for a task +#### List the push notification configurations for a task ```java -DeleteTaskPushNotificationConfigResponse response = client.deleteTaskPushNotificationConfig("task-1234", "config-4567"); +List configs = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams("task-1234")); // Additional properties can be specified using a map -Map metadata = ... -DeleteTaskPushNotificationConfigResponse response = client.deleteTaskPushNotificationConfig(new DeleteTaskPushNotificationConfigParams("task-1234", "config-4567", metadata)); +Map metadata = Map.of("filter", "active"); +List configs = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams("task-1234", metadata)); + +// You can also optionally specify a ClientCallContext with call-specific config to use +List configs = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams("task-1234"), clientCallContext); ``` -#### Send a streaming message +#### Delete a push notification configuration for a task ```java -// Send a text message to the remote agent -Message message = A2A.toUserMessage("tell me some jokes"); // the message ID will be automatically generated for you -MessageSendParams params = new MessageSendParams.Builder() - .message(message) - .build(); - -// Create a handler that will be invoked for Task, Message, TaskStatusUpdateEvent, and TaskArtifactUpdateEvent -Consumer eventHandler = event -> {...}; +client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams("task-1234", "config-4567")); -// Create a handler that will be invoked if an error is received -Consumer errorHandler = error -> {...}; - -// Create a handler that will be invoked in the event of a failure -Runnable failureHandler = () -> {...}; +// Additional properties can be specified using a map +Map metadata = Map.of("reason", "cleanup"); +client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams("task-1234", "config-4567", metadata)); -// Send the streaming message to the remote agent -client.sendStreamingMessage(params, eventHandler, errorHandler, failureHandler); +// You can also optionally specify a ClientCallContext with call-specific config to use +client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams("task-1234", "config-4567", clientCallContext); ``` #### Resubscribe to a task ```java -// Create a handler that will be invoked for Task, Message, TaskStatusUpdateEvent, and TaskArtifactUpdateEvent -Consumer eventHandler = event -> {...}; +// Resubscribe to an ongoing task with id "task-1234" using configured consumers +TaskIdParams taskIdParams = new TaskIdParams("task-1234"); +client.resubscribe(taskIdParams); -// Create a handler that will be invoked if an error is received -Consumer errorHandler = error -> {...}; +// Or resubscribe with custom consumers and error handler +List> customConsumers = List.of( + (event, card) -> System.out.println("Resubscribe event: " + event) +); +Consumer customErrorHandler = error -> + System.err.println("Resubscribe error: " + error.getMessage()); -// Create a handler that will be invoked in the event of a failure -Runnable failureHandler = () -> {...}; +client.resubscribe(taskIdParams, customConsumers, customErrorHandler); -// Resubscribe to an ongoing task with id "task-1234" -TaskIdParams taskIdParams = new TaskIdParams("task-1234"); -client.resubscribeToTask("request-1234", taskIdParams, eventHandler, errorHandler, failureHandler); +// You can also optionally specify a ClientCallContext with call-specific config to use +client.resubscribe(taskIdParams, clientCallContext); ``` #### Retrieve details about the server agent that this client agent is communicating with @@ -365,12 +528,6 @@ client.resubscribeToTask("request-1234", taskIdParams, eventHandler, errorHandle AgentCard serverAgentCard = client.getAgentCard(); ``` -An agent card can also be retrieved using the `A2A#getAgentCard` method: -```java -// http://localhost:1234 is the base URL for the agent whose card we want to retrieve -AgentCard agentCard = A2A.getAgentCard("http://localhost:1234"); -``` - ## Additional Examples ### Hello World Client Example diff --git a/client/base/src/main/java/io/a2a/client/A2ACardResolver.java b/client/base/src/main/java/io/a2a/client/A2ACardResolver.java index bbbc6caf0..f5de4746e 100644 --- a/client/base/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/client/base/src/main/java/io/a2a/client/A2ACardResolver.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; +import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; @@ -25,6 +26,19 @@ public class A2ACardResolver { private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; /** + * Get the agent card for an A2A agent. + * The {@code JdkA2AHttpClient} will be used to fetch the agent card. + * + * @param baseUrl the base URL for the agent whose agent card we want to retrieve + * @throws A2AClientError if the URL for the agent is invalid + */ + public A2ACardResolver(String baseUrl) throws A2AClientError { + this(new JdkA2AHttpClient(), baseUrl, null, null); + } + + /** + /**Get the agent card for an A2A agent. + * * @param httpClient the http client to use * @param baseUrl the base URL for the agent whose agent card we want to retrieve * @throws A2AClientError if the URL for the agent is invalid diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index ae25a0c8b..1667ed56f 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -41,6 +41,22 @@ public AbstractClient(List> consumers, Consum this.streamingErrorHandler = streamingErrorHandler; } + /** + * Send a message to the remote agent. This method will automatically use + * the streaming or non-streaming approach as determined by the server's + * agent card and the client configuration. The configured client consumers + * will be used to handle messages, tasks, and update events received + * from the remote agent. The configured streaming error handler will be used + * if an error occurs during streaming. The configured client push notification + * configuration will get used for streaming. + * + * @param request the message + * @throws A2AClientException if sending the message fails for any reason + */ + public void sendMessage(Message request) throws A2AClientException { + sendMessage(request, null); + } + /** * Send a message to the remote agent. This method will automatically use * the streaming or non-streaming approach as determined by the server's @@ -56,6 +72,26 @@ public AbstractClient(List> consumers, Consum */ public abstract void sendMessage(Message request, ClientCallContext context) throws A2AClientException; + /** + * Send a message to the remote agent. This method will automatically use + * the streaming or non-streaming approach as determined by the server's + * agent card and the client configuration. The specified client consumers + * will be used to handle messages, tasks, and update events received + * from the remote agent. The specified streaming error handler will be used + * if an error occurs during streaming. The configured client push notification + * configuration will get used for streaming. + * + * @param request the message + * @param consumers a list of consumers to pass responses from the remote agent to + * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @throws A2AClientException if sending the message fails for any reason + */ + public void sendMessage(Message request, + List> consumers, + Consumer streamingErrorHandler) throws A2AClientException { + sendMessage(request, consumers, streamingErrorHandler, null); + } + /** * Send a message to the remote agent. This method will automatically use * the streaming or non-streaming approach as determined by the server's @@ -90,9 +126,40 @@ public abstract void sendMessage(Message request, * @param metadata the optional metadata to include when sending the message * @throws A2AClientException if sending the message fails for any reason */ + public void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, + Map metadata) throws A2AClientException { + sendMessage(request, pushNotificationConfiguration, metadata, null); + } + + /** + * Send a message to the remote agent. This method will automatically use + * the streaming or non-streaming approach as determined by the server's + * agent card and the client configuration. The configured client consumers + * will be used to handle messages, tasks, and update events received from + * the remote agent. The configured streaming error handler will be used + * if an error occurs during streaming. + * + * @param request the message + * @param pushNotificationConfiguration the push notification configuration that should be + * used if the streaming approach is used + * @param metadata the optional metadata to include when sending the message + * @param context optional client call context for the request (may be {@code null}) + * @throws A2AClientException if sending the message fails for any reason + */ public abstract void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, Map metadata, ClientCallContext context) throws A2AClientException; + /** + * Retrieve the current state and history of a specific task. + * + * @param request the task query parameters specifying which task to retrieve + * @return the task + * @throws A2AClientException if retrieving the task fails for any reason + */ + public Task getTask(TaskQueryParams request) throws A2AClientException { + return getTask(request, null); + } + /** * Retrieve the current state and history of a specific task. * @@ -103,6 +170,17 @@ public abstract void sendMessage(Message request, PushNotificationConfig pushNot */ public abstract Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException; + /** + * Request the agent to cancel a specific task. + * + * @param request the task ID parameters specifying which task to cancel + * @return the cancelled task + * @throws A2AClientException if cancelling the task fails for any reason + */ + public Task cancelTask(TaskIdParams request) throws A2AClientException { + return cancelTask(request, null); + } + /** * Request the agent to cancel a specific task. * @@ -113,6 +191,18 @@ public abstract void sendMessage(Message request, PushNotificationConfig pushNot */ public abstract Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException; + /** + * Set or update the push notification configuration for a specific task. + * + * @param request the push notification configuration to set for the task + * @return the configured TaskPushNotificationConfig + * @throws A2AClientException if setting the task push notification configuration fails for any reason + */ + public TaskPushNotificationConfig setTaskPushNotificationConfiguration( + TaskPushNotificationConfig request) throws A2AClientException { + return setTaskPushNotificationConfiguration(request, null); + } + /** * Set or update the push notification configuration for a specific task. * @@ -125,6 +215,18 @@ public abstract TaskPushNotificationConfig setTaskPushNotificationConfiguration( TaskPushNotificationConfig request, ClientCallContext context) throws A2AClientException; + /** + * Retrieve the push notification configuration for a specific task. + * + * @param request the parameters specifying which task's notification config to retrieve + * @return the task push notification config + * @throws A2AClientException if getting the task push notification config fails for any reason + */ + public TaskPushNotificationConfig getTaskPushNotificationConfiguration( + GetTaskPushNotificationConfigParams request) throws A2AClientException { + return getTaskPushNotificationConfiguration(request, null); + } + /** * Retrieve the push notification configuration for a specific task. * @@ -137,6 +239,18 @@ public abstract TaskPushNotificationConfig getTaskPushNotificationConfiguration( GetTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException; + /** + * Retrieve the list of push notification configurations for a specific task. + * + * @param request the parameters specifying which task's notification configs to retrieve + * @return the list of task push notification configs + * @throws A2AClientException if getting the task push notification configs fails for any reason + */ + public List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigParams request) throws A2AClientException { + return listTaskPushNotificationConfigurations(request, null); + } + /** * Retrieve the list of push notification configurations for a specific task. * @@ -149,6 +263,17 @@ public abstract List listTaskPushNotificationConfigu ListTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException; + /** + * Delete the list of push notification configurations for a specific task. + * + * @param request the parameters specifying which task's notification configs to delete + * @throws A2AClientException if deleting the task push notification configs fails for any reason + */ + public void deleteTaskPushNotificationConfigurations( + DeleteTaskPushNotificationConfigParams request) throws A2AClientException { + deleteTaskPushNotificationConfigurations(request, null); + } + /** * Delete the list of push notification configurations for a specific task. * @@ -160,6 +285,20 @@ public abstract void deleteTaskPushNotificationConfigurations( DeleteTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException; + /** + * Resubscribe to a task's event stream. + * This is only available if both the client and server support streaming. + * The configured client consumers will be used to handle messages, tasks, + * and update events received from the remote agent. The configured streaming + * error handler will be used if an error occurs during streaming. + * + * @param request the parameters specifying which task's notification configs to delete + * @throws A2AClientException if resubscribing fails for any reason + */ + public void resubscribe(TaskIdParams request) throws A2AClientException { + resubscribe(request, null); + } + /** * Resubscribe to a task's event stream. * This is only available if both the client and server support streaming. @@ -173,6 +312,23 @@ public abstract void deleteTaskPushNotificationConfigurations( */ public abstract void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException; + /** + * Resubscribe to a task's event stream. + * This is only available if both the client and server support streaming. + * The specified client consumers will be used to handle messages, tasks, and + * update events received from the remote agent. The specified streaming error + * handler will be used if an error occurs during streaming. + * + * @param request the parameters specifying which task's notification configs to delete + * @param consumers a list of consumers to pass responses from the remote agent to + * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs + * @throws A2AClientException if resubscribing fails for any reason + */ + public void resubscribe(TaskIdParams request, List> consumers, + Consumer streamingErrorHandler) throws A2AClientException { + resubscribe(request, consumers, streamingErrorHandler, null); + } + /** * Resubscribe to a task's event stream. * This is only available if both the client and server support streaming. @@ -189,6 +345,15 @@ public abstract void deleteTaskPushNotificationConfigurations( public abstract void resubscribe(TaskIdParams request, List> consumers, Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException; + /** + * Retrieve the AgentCard. + * + * @return the AgentCard + * @throws A2AClientException if retrieving the agent card fails for any reason + */ + public AgentCard getAgentCard() throws A2AClientException { + return getAgentCard(null); + } /** * Retrieve the AgentCard. diff --git a/client/base/src/main/java/io/a2a/client/ClientFactory.java b/client/base/src/main/java/io/a2a/client/ClientFactory.java index 71c1a6020..ff38201f3 100644 --- a/client/base/src/main/java/io/a2a/client/ClientFactory.java +++ b/client/base/src/main/java/io/a2a/client/ClientFactory.java @@ -28,7 +28,13 @@ public class ClientFactory { private final ClientConfig clientConfig; private final Map transportProviderRegistry = new HashMap<>(); + /** + * Create a client factory used to generate the appropriate client for the agent. + * + * @param clientConfig the client config to use + */ public ClientFactory(ClientConfig clientConfig) { + checkNotNullParam("clientConfig", clientConfig); this.clientConfig = clientConfig; ServiceLoader loader = ServiceLoader.load(ClientTransportProvider.class); for (ClientTransportProvider transport : loader) { diff --git a/examples/helloworld/client/README.md b/examples/helloworld/client/README.md index 9bea69eb5..ac01c890f 100644 --- a/examples/helloworld/client/README.md +++ b/examples/helloworld/client/README.md @@ -78,7 +78,7 @@ The Java client (`HelloWorldClient.java`) performs the following actions: 1. Fetches the server's public agent card 2. Fetches the server's extended agent card -3. Creates an A2A client using the extended agent card that connects to the Python server at `http://localhost:9999`. +3. Creates a client using the extended agent card that connects to the Python server at `http://localhost:9999`. 4. Sends a regular message asking "how much is 10 USD in INR?". 5. Prints the server's response. 6. Sends the same message as a streaming request. diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index f810244e1..b5917dc33 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.a2a.A2A; +import io.a2a.client.ClientFactory; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.spec.AgentCard; import io.a2a.spec.EventKind; @@ -46,6 +47,7 @@ public static void main(String[] args) { System.out.println("Public card does not indicate support for an extended card. Using public card."); } + ClientFactory clientFactory = new ClientFactory(); JSONRPCTransport client = new JSONRPCTransport(finalAgentCard); Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you MessageSendParams params = new MessageSendParams.Builder() diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index dd306b0d9..e9967c7db 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -149,7 +149,7 @@ private void testGetTask() throws Exception { private void testGetTask(String mediaType) throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.getId()), null); + Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.getId())); assertEquals("task-123", response.getId()); assertEquals("session-xyz", response.getContextId()); assertEquals(TaskState.SUBMITTED, response.getStatus().state()); @@ -164,7 +164,7 @@ private void testGetTask(String mediaType) throws Exception { public void testGetTaskNotFound() throws Exception { assertTrue(getTaskFromTaskStore("non-existent-task") == null); try { - getClient().getTask(new TaskQueryParams("non-existent-task"), null); + getClient().getTask(new TaskQueryParams("non-existent-task")); fail("Expected A2AClientException for non-existent task"); } catch (A2AClientException e) { // Expected - the client should throw an exception for non-existent tasks @@ -176,7 +176,7 @@ public void testGetTaskNotFound() throws Exception { public void testCancelTaskSuccess() throws Exception { saveTaskInTaskStore(CANCEL_TASK); try { - Task task = getClient().cancelTask(new TaskIdParams(CANCEL_TASK.getId()), null); + Task task = getClient().cancelTask(new TaskIdParams(CANCEL_TASK.getId())); assertEquals(CANCEL_TASK.getId(), task.getId()); assertEquals(CANCEL_TASK.getContextId(), task.getContextId()); assertEquals(TaskState.CANCELED, task.getStatus().state()); @@ -191,7 +191,7 @@ public void testCancelTaskSuccess() throws Exception { public void testCancelTaskNotSupported() throws Exception { saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); try { - getClient().cancelTask(new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId()), null); + getClient().cancelTask(new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); fail("Expected A2AClientException for unsupported cancel operation"); } catch (A2AClientException e) { // Expected - the client should throw an exception for unsupported operations @@ -204,7 +204,7 @@ public void testCancelTaskNotSupported() throws Exception { @Test public void testCancelTaskNotFound() { try { - getClient().cancelTask(new TaskIdParams("non-existent-task"), null); + getClient().cancelTask(new TaskIdParams("non-existent-task")); fail("Expected A2AClientException for non-existent task"); } catch (A2AClientException e) { // Expected - the client should throw an exception for non-existent tasks @@ -238,7 +238,7 @@ public void testSendMessageNewMessageSuccess() throws Exception { }; // testing the non-streaming send message - getNonStreamingClient().sendMessage(message, List.of(consumer), null, null); + getNonStreamingClient().sendMessage(message, List.of(consumer), null); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(wasUnexpectedEvent.get()); @@ -277,7 +277,7 @@ public void testSendMessageExistingTaskSuccess() throws Exception { }; // testing the non-streaming send message - getNonStreamingClient().sendMessage(message, List.of(consumer), null, null); + getNonStreamingClient().sendMessage(message, List.of(consumer), null); assertFalse(wasUnexpectedEvent.get()); assertTrue(latch.await(10, TimeUnit.SECONDS)); Message messageResponse = receivedMessage.get(); @@ -301,7 +301,7 @@ public void testSetPushNotificationSuccess() throws Exception { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); - TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig, null); + TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (A2AClientException e) { @@ -320,11 +320,11 @@ public void testGetPushNotificationSuccess() throws Exception { new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); - TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig, null); + TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertNotNull(setResult); TaskPushNotificationConfig config = getClient().getTaskPushNotificationConfiguration( - new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (A2AClientException e) { @@ -343,7 +343,7 @@ public void testError() throws A2AClientException { .build(); try { - getNonStreamingClient().sendMessage(message, null); + getNonStreamingClient().sendMessage(message); // For non-streaming clients, the error should still be thrown as an exception fail("Expected A2AClientException for unsupported send message operation"); @@ -355,7 +355,7 @@ public void testError() throws A2AClientException { @Test public void testGetAgentCard() throws A2AClientException { - AgentCard agentCard = getClient().getAgentCard(null); + AgentCard agentCard = getClient().getAgentCard(); assertNotNull(agentCard); assertEquals("test-card", agentCard.name()); assertEquals("A test agent card", agentCard.description()); @@ -402,7 +402,7 @@ public void testSendMessageStreamExistingTaskSuccess() throws Exception { }; // testing the streaming send message - getClient().sendMessage(message, List.of(consumer), errorHandler, null); + getClient().sendMessage(message, List.of(consumer), errorHandler); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(wasUnexpectedEvent.get()); @@ -469,7 +469,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); // Resubscribe to the task with specific consumer and error handler - getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), List.of(consumer), errorHandler, null); + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), List.of(consumer), errorHandler); // Wait for subscription to be established assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); @@ -535,7 +535,7 @@ public void testResubscribeNoExistingTaskError() throws Exception { }; try { - getClient().resubscribe(new TaskIdParams("non-existent-task"), List.of(), errorHandler, null); + getClient().resubscribe(new TaskIdParams("non-existent-task"), List.of(), errorHandler); // Wait for error to be captured (may come via error handler for streaming) boolean errorReceived = errorLatch.await(10, TimeUnit.SECONDS); @@ -586,7 +586,7 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(2, result.size()); assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1), result.get(0)); assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2), result.get(1)); @@ -616,7 +616,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(1, result.size()); PushNotificationConfig expectedNotificationConfig = new PushNotificationConfig.Builder() @@ -637,7 +637,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { public void testListPushNotificationConfigTaskNotFound() { try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams("non-existent-task"), null); + new ListTaskPushNotificationConfigParams("non-existent-task")); fail(); } catch (A2AClientException e) { assertInstanceOf(TaskNotFoundError.class, e.getCause()); @@ -649,7 +649,7 @@ public void testListPushNotificationConfigEmptyList() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(0, result.size()); } catch (Exception e) { fail(e.getMessage()); @@ -684,17 +684,16 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception try { // specify the config ID to delete getClient().deleteTaskPushNotificationConfigurations( - new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "config1"), - null); + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "config1")); // should now be 1 left List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(1, result.size()); // should remain unchanged, this is a different task result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams("task-456"), null); + new ListTaskPushNotificationConfigParams("task-456")); assertEquals(1, result.size()); } catch (Exception e) { fail(e.getMessage()); @@ -725,12 +724,11 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc try { getClient().deleteTaskPushNotificationConfigurations( - new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "non-existent-config-id"), - null); + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "non-existent-config-id")); // should remain unchanged List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(2, result.size()); } catch (Exception e) { fail(); @@ -746,8 +744,7 @@ public void testDeletePushNotificationConfigTaskNotFound() { try { getClient().deleteTaskPushNotificationConfigurations( new DeleteTaskPushNotificationConfigParams("non-existent-task", - "non-existent-config-id"), - null); + "non-existent-config-id")); fail(); } catch (A2AClientException e) { assertInstanceOf(TaskNotFoundError.class, e.getCause()); @@ -772,8 +769,7 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio try { getClient().deleteTaskPushNotificationConfigurations( - new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()), - null); + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); // should now be 0 List result = getClient().listTaskPushNotificationConfigurations( From 3e7ebc90241721c583c3791d85dcaef531baf8fc Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 21 Aug 2025 11:00:04 -0400 Subject: [PATCH 088/493] fix: Update the helloworld client example to use the ClientFactory --- .../java/io/a2a/client/ClientFactory.java | 6 +- examples/helloworld/client/pom.xml | 4 ++ .../examples/helloworld/HelloWorldClient.java | 68 +++++++++++++------ .../examples/helloworld/HelloWorldRunner.java | 1 + examples/helloworld/pom.xml | 5 ++ 5 files changed, 63 insertions(+), 21 deletions(-) diff --git a/client/base/src/main/java/io/a2a/client/ClientFactory.java b/client/base/src/main/java/io/a2a/client/ClientFactory.java index ff38201f3..988df4ea4 100644 --- a/client/base/src/main/java/io/a2a/client/ClientFactory.java +++ b/client/base/src/main/java/io/a2a/client/ClientFactory.java @@ -80,8 +80,10 @@ public Client create(AgentCard agentCard, List getServerPreferredTransports(AgentCard agentCard) { LinkedHashMap serverPreferredTransports = new LinkedHashMap<>(); serverPreferredTransports.put(agentCard.preferredTransport(), agentCard.url()); - for (AgentInterface agentInterface : agentCard.additionalInterfaces()) { - serverPreferredTransports.putIfAbsent(agentInterface.transport(), agentInterface.url()); + if (agentCard.additionalInterfaces() != null) { + for (AgentInterface agentInterface : agentCard.additionalInterfaces()) { + serverPreferredTransports.putIfAbsent(agentInterface.transport(), agentInterface.url()); + } } return serverPreferredTransports; } diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 25970a7d4..cdfeef28d 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -16,6 +16,10 @@ Examples for the Java SDK for the Agent2Agent Protocol (A2A) + + io.github.a2asdk + a2a-java-sdk-client + io.github.a2asdk a2a-java-sdk-client-transport-jsonrpc diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index b5917dc33..e2303ac30 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -1,17 +1,23 @@ package io.a2a.examples.helloworld; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import com.fasterxml.jackson.databind.ObjectMapper; import io.a2a.A2A; +import io.a2a.client.A2ACardResolver; +import io.a2a.client.Client; +import io.a2a.client.ClientEvent; import io.a2a.client.ClientFactory; -import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.MessageEvent; +import io.a2a.client.config.ClientConfig; import io.a2a.spec.AgentCard; -import io.a2a.spec.EventKind; import io.a2a.spec.Message; -import io.a2a.spec.MessageSendParams; -import io.a2a.spec.SendMessageResponse; import io.a2a.spec.Part; import io.a2a.spec.TextPart; @@ -28,7 +34,7 @@ public class HelloWorldClient { public static void main(String[] args) { try { AgentCard finalAgentCard = null; - AgentCard publicAgentCard = A2A.getAgentCard("http://localhost:9999"); + AgentCard publicAgentCard = new A2ACardResolver("http://localhost:9999").getAgentCard(); System.out.println("Successfully fetched public agent card:"); System.out.println(OBJECT_MAPPER.writeValueAsString(publicAgentCard)); System.out.println("Using public agent card for client initialization (default)."); @@ -47,23 +53,47 @@ public static void main(String[] args) { System.out.println("Public card does not indicate support for an extended card. Using public card."); } - ClientFactory clientFactory = new ClientFactory(); - JSONRPCTransport client = new JSONRPCTransport(finalAgentCard); - Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you - MessageSendParams params = new MessageSendParams.Builder() - .message(message) - .build(); - EventKind result = client.sendMessage(params, null); - if (result instanceof Message responseMessage) { - StringBuilder textBuilder = new StringBuilder(); - if (responseMessage.getParts() != null) { - for (Part part : responseMessage.getParts()) { - if (part instanceof TextPart textPart) { - textBuilder.append(textPart.getText()); + final CompletableFuture messageResponse = new CompletableFuture<>(); + + // Create consumers list for handling client events + List> consumers = new ArrayList<>(); + consumers.add((event, agentCard) -> { + if (event instanceof MessageEvent messageEvent) { + Message responseMessage = messageEvent.getMessage(); + StringBuilder textBuilder = new StringBuilder(); + if (responseMessage.getParts() != null) { + for (Part part : responseMessage.getParts()) { + if (part instanceof TextPart textPart) { + textBuilder.append(textPart.getText()); + } } } + messageResponse.complete(textBuilder.toString()); + } else { + System.out.println("Received client event: " + event.getClass().getSimpleName()); } - System.out.println("Response: " + textBuilder.toString()); + }); + + // Create error handler for streaming errors + Consumer streamingErrorHandler = (error) -> { + System.err.println("Streaming error occurred: " + error.getMessage()); + error.printStackTrace(); + messageResponse.completeExceptionally(error); + }; + + ClientFactory clientFactory = new ClientFactory(new ClientConfig.Builder().build()); + Client client = clientFactory.create(finalAgentCard, consumers, streamingErrorHandler); + Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you + + System.out.println("Sending message: " + MESSAGE_TEXT); + client.sendMessage(message); + System.out.println("Message sent successfully. Responses will be handled by the configured consumers."); + + try { + String responseText = messageResponse.get(); + System.out.println("Response: " + responseText); + } catch (Exception e) { + System.err.println("Failed to get response: " + e.getMessage()); } } catch (Exception e) { System.err.println("An error occurred: " + e.getMessage()); diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 84a489367..f7831133f 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,5 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? //DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 47ea4ce82..a9579b75c 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -26,6 +26,11 @@ pom import + + io.github.a2asdk + a2a-java-sdk-client + ${project.version} + io.github.a2asdk a2a-java-sdk-client-transport-jsonrpc From a56d4f79392d7468bf019781228262b61645cec9 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 21 Aug 2025 11:55:04 -0400 Subject: [PATCH 089/493] fix: Client pom cleanup --- client/base/pom.xml | 5 ----- client/config/pom.xml | 1 - client/transport/grpc/pom.xml | 5 ----- client/transport/jsonrpc/pom.xml | 6 ------ client/transport/spi/pom.xml | 2 -- pom.xml | 35 ++++++++++++++++++++++++++++++++ 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index d39f6b91f..30750e13d 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -21,27 +21,22 @@ ${project.groupId} a2a-java-sdk-client-config - ${project.version} ${project.groupId} a2a-java-sdk-http-client - ${project.version} ${project.groupId} a2a-java-sdk-client-transport-spi - ${project.version} ${project.groupId} a2a-java-sdk-common - ${project.version} ${project.groupId} a2a-java-sdk-spec - ${project.version} org.junit.jupiter diff --git a/client/config/pom.xml b/client/config/pom.xml index a44194654..db7afdc30 100644 --- a/client/config/pom.xml +++ b/client/config/pom.xml @@ -21,7 +21,6 @@ ${project.groupId} a2a-java-sdk-spec - ${project.version} org.junit.jupiter diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index ad4a5b12e..758130291 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -20,27 +20,22 @@ ${project.groupId} a2a-java-sdk-client-config - ${project.version} ${project.groupId} a2a-java-sdk-common - ${project.version} ${project.groupId} a2a-java-sdk-spec - ${project.version} ${project.groupId} a2a-java-sdk-spec-grpc - ${project.version} ${project.groupId} a2a-java-sdk-client-transport-spi - ${project.version} org.junit.jupiter diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 3a1d5be8b..66010fec3 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -20,32 +20,26 @@ ${project.groupId} a2a-java-sdk-client - ${project.version} ${project.groupId} a2a-java-sdk-client-config - ${project.version} ${project.groupId} a2a-java-sdk-http-client - ${project.version} ${project.groupId} a2a-java-sdk-client-transport-spi - ${project.version} ${project.groupId} a2a-java-sdk-common - ${project.version} ${project.groupId} a2a-java-sdk-spec - ${project.version} org.junit.jupiter diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index a809c6dea..b83d5adaf 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -20,12 +20,10 @@ io.github.a2asdk a2a-java-sdk-client-config - ${project.version} io.github.a2asdk a2a-java-sdk-spec - ${project.version} diff --git a/pom.xml b/pom.xml index b5fa46a08..a242b91c2 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,41 @@ + + ${project.groupId} + a2a-java-sdk-client + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-config + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-spi + ${project.version} + + + ${project.groupId} + a2a-java-sdk-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-http-client + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec-grpc + ${project.version} + io.grpc grpc-bom From cf3e3f0cfeae766fc6439de4f86c6e318462348d Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 21 Aug 2025 13:02:27 -0400 Subject: [PATCH 090/493] fix: Avoid intermittent failures in resubscribe test case --- .../io/a2a/server/apps/common/AbstractA2AServerTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index e9967c7db..03cd53cfb 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -791,6 +791,11 @@ protected boolean isStreamClosedError(Throwable throwable) { if (cause instanceof EOFException) { return true; } + if (cause instanceof IOException && cause.getMessage() != null + && cause.getMessage().contains("cancelled")) { + // stream is closed upon cancellation + return true; + } cause = cause.getCause(); } return false; From f7cf1bf9a6f3705b79f2dab3231d6505cf094d1c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 21 Aug 2025 14:52:34 -0400 Subject: [PATCH 091/493] fix: Move JSONRPC-specific tests back to AbstractA2AServerTest so that other server implementations that also support JSONRPC can make use of these tests. These tests will be skipped when testing other transports --- .../apps/quarkus/QuarkusA2AJSONRPCTest.java | 277 ------------------ .../apps/common/AbstractA2AServerTest.java | 277 ++++++++++++++++++ 2 files changed, 277 insertions(+), 277 deletions(-) diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java index 7b6257d58..acebe4cb2 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java @@ -1,48 +1,9 @@ package io.a2a.server.apps.quarkus; -import static io.restassured.RestAssured.given; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; -import static org.wildfly.common.Assert.assertNotNull; -import jakarta.ws.rs.core.MediaType; - -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; - -import com.fasterxml.jackson.core.JsonProcessingException; - import io.a2a.server.apps.common.AbstractA2AServerTest; -import io.a2a.spec.A2AClientException; -import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCErrorResponse; -import io.a2a.spec.Message; -import io.a2a.spec.MessageSendParams; -import io.a2a.spec.MethodNotFoundError; -import io.a2a.spec.Part; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.StreamingJSONRPCRequest; -import io.a2a.spec.Task; -import io.a2a.spec.TaskQueryParams; -import io.a2a.spec.TaskState; -import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; -import io.a2a.util.Utils; import io.quarkus.test.junit.QuarkusTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - @QuarkusTest public class QuarkusA2AJSONRPCTest extends AbstractA2AServerTest { @@ -59,242 +20,4 @@ protected String getTransportProtocol() { protected String getTransportUrl() { return "http://localhost:8081"; } - - @Test - public void testMalformedJSONRPCRequest() { - // missing closing bracket - String malformedRequest = "{\"jsonrpc\": \"2.0\", \"method\": \"message/send\", \"params\": {\"foo\": \"bar\"}"; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(malformedRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new JSONParseError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidParamsJSONRPCRequest() { - String invalidParamsRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} - """; - testInvalidParams(invalidParamsRequest); - - invalidParamsRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} - """; - testInvalidParams(invalidParamsRequest); - } - - private void testInvalidParams(String invalidParamsRequest) { - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidParamsRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); - assertEquals("1", response.getId()); - } - - @Test - public void testInvalidJSONRPCRequestMissingJsonrpc() { - String invalidRequest = """ - { - "method": "message/send", - "params": {} - } - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidJSONRPCRequestMissingMethod() { - String invalidRequest = """ - {"jsonrpc": "2.0", "params": {}} - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidJSONRPCRequestInvalidId() { - String invalidRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": {}, "id": {"bad": "type"}} - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); - } - - @Test - public void testInvalidJSONRPCRequestNonExistentMethod() { - String invalidRequest = """ - {"jsonrpc": "2.0", "method" : "nonexistent/method", "params": {}} - """; - JSONRPCErrorResponse response = given() - .contentType(MediaType.APPLICATION_JSON) - .body(invalidRequest) - .when() - .post("/") - .then() - .statusCode(200) - .extract() - .as(JSONRPCErrorResponse.class); - assertNotNull(response.getError()); - assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); - } - - @Test - public void testNonStreamingMethodWithAcceptHeader() throws Exception { - testGetTask(MediaType.APPLICATION_JSON); - } - - private void testGetTask(String mediaType) throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.getId()), null); - assertEquals("task-123", response.getId()); - assertEquals("session-xyz", response.getContextId()); - assertEquals(TaskState.SUBMITTED, response.getStatus().state()); - } catch (A2AClientException e) { - fail("Unexpected exception during getTask: " + e.getMessage(), e); - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } - } - - @Test - public void testStreamingMethodWithAcceptHeader() throws Exception { - testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); - } - - @Test - public void testSendMessageStreamNewMessageSuccess() throws Exception { - testSendStreamingMessage(null); - } - - private void testSendStreamingMessage(String mediaType) throws Exception { - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest( - "1", new MessageSendParams(message, null, null)); - - CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference errorRef = new AtomicReference<>(); - - responseFuture.thenAccept(response -> { - if (response.statusCode() != 200) { - //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); - throw new IllegalStateException("Status code was " + response.statusCode()); - } - response.body().forEach(line -> { - try { - SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); - if (jsonResponse != null) { - assertNull(jsonResponse.getError()); - Message messageResponse = (Message) jsonResponse.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - latch.countDown(); - } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); - }).exceptionally(t -> { - if (!isStreamClosedError(t)) { - errorRef.set(t); - } - latch.countDown(); - return null; - }); - - - boolean dataRead = latch.await(20, TimeUnit.SECONDS); - Assertions.assertTrue(dataRead); - Assertions.assertNull(errorRef.get()); - - } - - private CompletableFuture>> initialiseStreamingRequest( - StreamingJSONRPCRequest request, String mediaType) throws Exception { - - // Create the client - HttpClient client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_2) - .build(); - - // Create the request - HttpRequest.Builder builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/")) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) - .header("Content-Type", APPLICATION_JSON); - if (mediaType != null) { - builder.header("Accept", mediaType); - } - HttpRequest httpRequest = builder.build(); - - - // Send request async and return the CompletableFuture - return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); - } - - private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { - line = extractSseData(line); - if (line != null) { - return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); - } - return null; - } - - private static String extractSseData(String line) { - if (line.startsWith("data:")) { - line = line.substring(5).trim(); - return line; - } - return null; - } } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 03cd53cfb..0cbee983f 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1,13 +1,17 @@ package io.a2a.server.apps.common; +import static io.restassured.RestAssured.given; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.wildfly.common.Assert.assertNotNull; import static org.wildfly.common.Assert.assertTrue; +import jakarta.ws.rs.core.MediaType; + import java.io.EOFException; import java.io.IOException; import java.net.URI; @@ -25,6 +29,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Stream; + +import com.fasterxml.jackson.core.JsonProcessingException; import io.a2a.client.Client; import io.a2a.client.ClientEvent; @@ -41,10 +48,19 @@ import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.Event; import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCErrorResponse; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SendStreamingMessageResponse; +import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; @@ -55,9 +71,11 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; +import io.a2a.spec.TransportProtocol; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -783,6 +801,265 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio } } + @Test + public void testMalformedJSONRPCRequest() { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + // missing closing bracket + String malformedRequest = "{\"jsonrpc\": \"2.0\", \"method\": \"message/send\", \"params\": {\"foo\": \"bar\"}"; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(malformedRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new JSONParseError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidParamsJSONRPCRequest() { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + String invalidParamsRequest = """ + {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} + """; + testInvalidParams(invalidParamsRequest); + + invalidParamsRequest = """ + {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} + """; + testInvalidParams(invalidParamsRequest); + } + + private void testInvalidParams(String invalidParamsRequest) { + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidParamsRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); + assertEquals("1", response.getId()); + } + + @Test + public void testInvalidJSONRPCRequestMissingJsonrpc() { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + String invalidRequest = """ + { + "method": "message/send", + "params": {} + } + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidJSONRPCRequestMissingMethod() { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + String invalidRequest = """ + {"jsonrpc": "2.0", "params": {}} + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidJSONRPCRequestInvalidId() { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + String invalidRequest = """ + {"jsonrpc": "2.0", "method": "message/send", "params": {}, "id": {"bad": "type"}} + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); + } + + @Test + public void testInvalidJSONRPCRequestNonExistentMethod() { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + String invalidRequest = """ + {"jsonrpc": "2.0", "method" : "nonexistent/method", "params": {}} + """; + JSONRPCErrorResponse response = given() + .contentType(MediaType.APPLICATION_JSON) + .body(invalidRequest) + .when() + .post("/") + .then() + .statusCode(200) + .extract() + .as(JSONRPCErrorResponse.class); + assertNotNull(response.getError()); + assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); + } + + @Test + public void testNonStreamingMethodWithAcceptHeader() throws Exception { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + testGetTask(MediaType.APPLICATION_JSON); + } + + @Test + public void testStreamingMethodWithAcceptHeader() throws Exception { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); + } + + @Test + public void testSendMessageStreamNewMessageSuccess() throws Exception { + // skip this test for non-JSONRPC transports + assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), + "JSONRPC-specific test"); + + testSendStreamingMessage(null); + } + + private void testSendStreamingMessage(String mediaType) throws Exception { + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + SendStreamingMessageRequest request = new SendStreamingMessageRequest( + "1", new MessageSendParams(message, null, null)); + + CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference errorRef = new AtomicReference<>(); + + responseFuture.thenAccept(response -> { + if (response.statusCode() != 200) { + //errorRef.set(new IllegalStateException("Status code was " + response.statusCode())); + throw new IllegalStateException("Status code was " + response.statusCode()); + } + response.body().forEach(line -> { + try { + SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); + if (jsonResponse != null) { + assertNull(jsonResponse.getError()); + Message messageResponse = (Message) jsonResponse.getResult(); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + latch.countDown(); + } + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + }).exceptionally(t -> { + if (!isStreamClosedError(t)) { + errorRef.set(t); + } + latch.countDown(); + return null; + }); + + + boolean dataRead = latch.await(20, TimeUnit.SECONDS); + Assertions.assertTrue(dataRead); + Assertions.assertNull(errorRef.get()); + + } + + private CompletableFuture>> initialiseStreamingRequest( + StreamingJSONRPCRequest request, String mediaType) throws Exception { + + // Create the client + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + + // Create the request + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/")) + .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) + .header("Content-Type", APPLICATION_JSON); + if (mediaType != null) { + builder.header("Accept", mediaType); + } + HttpRequest httpRequest = builder.build(); + + + // Send request async and return the CompletableFuture + return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); + } + + private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { + line = extractSseData(line); + if (line != null) { + return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); + } + return null; + } + + private static String extractSseData(String line) { + if (line.startsWith("data:")) { + line = line.substring(5).trim(); + return line; + } + return null; + } + protected boolean isStreamClosedError(Throwable throwable) { // Unwrap the CompletionException Throwable cause = throwable; From 26ab76d3ec0343a5f22cdf62b7d3e7999caf4325 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 21 Aug 2025 15:30:34 -0400 Subject: [PATCH 092/493] fix: Update server and client transport descriptions in the README --- README.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index aa41fd0ae..54b66a8f0 100644 --- a/README.md +++ b/README.md @@ -40,16 +40,19 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) - [Add a class that creates an A2A Agent Card](#2-add-a-class-that-creates-an-a2a-agent-card) - [Add a class that creates an A2A Agent Executor](#3-add-a-class-that-creates-an-a2a-agent-executor) -### 1. Add the A2A Java SDK Server Maven dependency to your project +### 1. Add an A2A Java SDK Reference Server dependency to your project -Adding a dependency on an A2A Java SDK Server will provide access to the core classes that make up the A2A specification -and allow you to run your agentic Java application as an A2A server agent. +Adding a dependency on an A2A Java SDK Reference Server will provide access to the core classes +that make up the A2A specification and allow you to run your agentic Java application as an A2A server agent. -The A2A Java SDK provides a [reference A2A server implementation](reference-impl/README.md) based on [Quarkus](https://quarkus.io) for use with our tests and examples. However, the project is designed in such a way that it is trivial to integrate with various Java runtimes. +The A2A Java SDK provides [reference A2A server implementations](reference) based on [Quarkus](https://quarkus.io) for use with our tests and examples. However, the project is designed in such a way that it is trivial to integrate with various Java runtimes. [Server Integrations](#server-integrations) contains a list of community contributed integrations of the server with various runtimes. You might be able to use one of these for your target runtime, or you can use them as inspiration to create your own. -To use the reference implementation with the JSONRPC protocol add the following dependency to your project: +#### Server Transports +The A2A Java SDK Reference Server implementations support a couple transports: JSON-RPC 2.0 and gRPC. + +To use the reference implementation with the JSON-RPC protocol, add the following dependency to your project: > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* @@ -62,7 +65,7 @@ To use the reference implementation with the JSONRPC protocol add the following ``` -To use the reference implementation with the gRPC protocol add the following dependency to your project: +To use the reference implementation with the gRPC protocol, add the following dependency to your project: > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* @@ -75,6 +78,11 @@ To use the reference implementation with the gRPC protocol add the following dep ``` +Note that you can add more than one of the above dependencies to your project depending on the transports +you'd like to support. + +Support for the HTTP+JSON/REST transport will be coming soon. + ### 2. Add a class that creates an A2A Agent Card ```java @@ -233,7 +241,7 @@ that you can use to create your A2A `Client`. ``` -### 2. Add dependencies on the A2A Java SDK Client Transport(s) you'd like to use +### 2. Add one or more dependencies on the A2A Java SDK Client Transport(s) you'd like to use You need to add a dependency on at least one of the following client transport modules: @@ -569,7 +577,8 @@ The following list contains community contributed integrations with various Java To contribute an integration, please see [CONTRIBUTING_INTEGRATIONS.md](CONTRIBUTING_INTEGRATIONS.md). -* [reference-impl/README.md](reference-impl/README.md) - Reference implementation, based on Quarkus. +* [reference/jsonrpc/README.md](reference/jsonrpc/README.md) - JSON-RPC 2.0 Reference implementation, based on Quarkus. +* [reference/grpc/README.md](reference/grpc/README.md) - gRPC Reference implementation, based on Quarkus. * https://github.com/wildfly-extras/a2a-java-sdk-server-jakarta - This integration is based on Jakarta EE, and should work in all runtimes supporting the [Jakarta EE Web Profile](https://jakarta.ee/specifications/webprofile/). From eef089b398d60052584f5f48935511adce1de68e Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 25 Aug 2025 15:27:52 -0400 Subject: [PATCH 093/493] fix: Make the JSONRPC and gRPC URLs used for TCK testing configurable (#239) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- .github/workflows/run-tck.yml | 8 ++++++-- .../java/io/a2a/tck/server/AgentCardProducer.java | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index ed5829956..6b9ca8bab 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -15,6 +15,10 @@ env: TCK_VERSION: 0.3.0.alpha # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 + # SUT_JSONRPC_URL to use for the TCK + SUT_JSONRPC_URL: http://localhost:9999 + # SUT_GRPC_URL to use for the TCK + SUT_GRPC_URL: http://localhost:9000 # Only run the latest job concurrency: @@ -57,7 +61,7 @@ jobs: working-directory: tck - name: Wait for SUT to start run: | - URL="http://localhost:9999/.well-known/agent-card.json" + URL="${{ env.SUT_JSONRPC_URL }}/.well-known/agent-card.json" EXPECTED_STATUS=200 TIMEOUT=120 RETRY_INTERVAL=2 @@ -91,5 +95,5 @@ jobs: - name: Run TCK run: | - ./run_tck.py --sut-url http://localhost:9999 --category all --compliance-report report.json + ./run_tck.py --sut-url ${{ env.SUT_JSONRPC_URL }} --category all --transports jsonrpc,grpc --compliance-report report.json working-directory: tck/a2a-tck diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 54c9654da..59c055fc8 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -20,10 +20,12 @@ public class AgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { + String sutJsonRpcUrl = getEnvOrDefault("SUT_JSONRPC_URL", "http://localhost:9999"); + String sutGrpcUrl = getEnvOrDefault("SUT_GRPC_URL", "http://localhost:9000"); return new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") - .url("http://localhost:9999") + .url(sutJsonRpcUrl) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() @@ -42,9 +44,14 @@ public AgentCard agentCard() { .build())) .protocolVersion("0.3.0") .additionalInterfaces(List.of( - new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"), - new AgentInterface(TransportProtocol.GRPC.asString(), "http://localhost:9000"))) + new AgentInterface(TransportProtocol.JSONRPC.asString(), sutJsonRpcUrl), + new AgentInterface(TransportProtocol.GRPC.asString(), sutGrpcUrl))) .build(); } + + private static String getEnvOrDefault(String envVar, String defaultValue) { + String value = System.getenv(envVar); + return value == null || value.isBlank() ? defaultValue : value; + } } From aa75bf9a22536e66db92c030f34ade423281d284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diana=20K=C5=99epinsk=C3=A1?= Date: Wed, 27 Aug 2025 14:07:32 +0200 Subject: [PATCH 094/493] fix: Link to examples in README.md (#240) Fixes outdated link to samples README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54b66a8f0..10ee67c73 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Then build the `spec-grpc` module with `mvn clean install -Pproto-compile` to re ## Examples -You can find an example of how to use the A2A Java SDK in the [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/multi_language/python_and_java_multiagent/weather_agent). +You can find examples of how to use the A2A Java SDK in the [a2a-samples repository](https://github.com/a2aproject/a2a-samples/tree/main/samples/java/agents). More examples will be added soon. From 5686c8eaafa94e60e9c39c6ca3a1560cc33913a8 Mon Sep 17 00:00:00 2001 From: Brassely David Date: Wed, 27 Aug 2025 20:01:29 +0200 Subject: [PATCH 095/493] =?UTF-8?q?feat:=20Provide=20a=20more=20friendly?= =?UTF-8?q?=20builder-based=20way=20to=20configure=20transpor=E2=80=A6=20(?= =?UTF-8?q?#238)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …t configuration This PR is a proposal to make the transport configuration specific to a given transport implementation. This one will ease developer experience by providing a builder for configuring a Client in a more stricter way compare to what exists today. Also, this will help at getting rid of strong coupling with different transport implementations. Two benefits: Get rid of dependencies which may not be required (grpc dependencies if you only go for jsonrpc) Easier to provide new transport implementation (such as HTTP+JSON). # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- README.md | 72 ++++---- client/base/pom.xml | 11 +- client/base/src/main/java/io/a2a/A2A.java | 2 +- .../java/io/a2a/client/AbstractClient.java | 4 +- .../src/main/java/io/a2a/client/Client.java | 11 +- .../java/io/a2a/client/ClientBuilder.java | 164 ++++++++++++++++++ .../java/io/a2a/client/ClientFactory.java | 137 --------------- .../java/io/a2a/client/ClientTaskManager.java | 2 +- .../io/a2a/client/config/ClientConfig.java | 31 +--- .../java/io/a2a/client/ClientBuilderTest.java | 96 ++++++++++ client/config/pom.xml | 38 ---- .../client/config/ClientTransportConfig.java | 7 - client/transport/grpc/pom.xml | 4 - .../client/transport/grpc/GrpcTransport.java | 2 +- .../transport/grpc/GrpcTransportConfig.java | 13 +- .../grpc/GrpcTransportConfigBuilder.java | 25 +++ .../transport/grpc/GrpcTransportProvider.java | 28 ++- client/transport/jsonrpc/pom.xml | 8 - .../transport/jsonrpc/JSONRPCTransport.java | 8 +- .../jsonrpc/JSONRPCTransportConfig.java | 8 +- .../JSONRPCTransportConfigBuilder.java | 28 +++ .../jsonrpc/JSONRPCTransportProvider.java | 32 ++-- .../JSONRPCTransportStreamingTest.java | 7 +- client/transport/spi/pom.xml | 4 - .../client/transport/spi/ClientTransport.java | 2 +- .../transport/spi/ClientTransportConfig.java | 21 +++ .../spi/ClientTransportConfigBuilder.java | 22 +++ .../spi/ClientTransportProvider.java | 15 +- .../spi/interceptors}/ClientCallContext.java | 2 +- .../interceptors}/ClientCallInterceptor.java | 2 +- .../spi/interceptors}/PayloadAndHeaders.java | 2 +- examples/helloworld/client/pom.xml | 4 - .../examples/helloworld/HelloWorldClient.java | 13 +- examples/helloworld/pom.xml | 10 -- examples/helloworld/server/pom.xml | 11 +- http-client/pom.xml | 5 + .../io/a2a/client/http}/A2ACardResolver.java | 5 +- .../a2a/client/http}/A2ACardResolverTest.java | 4 +- .../io/a2a/client/http}/JsonMessages.java | 2 +- pom.xml | 11 +- .../common/quarkus/DefaultProducers.java | 1 - .../grpc/quarkus/QuarkusA2AGrpcTest.java | 20 +-- .../apps/quarkus/QuarkusA2AJSONRPCTest.java | 8 + .../apps/common/AbstractA2AServerTest.java | 43 ++--- 44 files changed, 534 insertions(+), 411 deletions(-) create mode 100644 client/base/src/main/java/io/a2a/client/ClientBuilder.java delete mode 100644 client/base/src/main/java/io/a2a/client/ClientFactory.java rename client/{config => base}/src/main/java/io/a2a/client/config/ClientConfig.java (71%) create mode 100644 client/base/src/test/java/io/a2a/client/ClientBuilderTest.java delete mode 100644 client/config/pom.xml delete mode 100644 client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java create mode 100644 client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java create mode 100644 client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java rename client/{config/src/main/java/io/a2a/client/config => transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors}/ClientCallContext.java (92%) rename client/{config/src/main/java/io/a2a/client/config => transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors}/ClientCallInterceptor.java (95%) rename client/{config/src/main/java/io/a2a/client/config => transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors}/PayloadAndHeaders.java (89%) rename {client/base/src/main/java/io/a2a/client => http-client/src/main/java/io/a2a/client/http}/A2ACardResolver.java (96%) rename {client/base/src/test/java/io/a2a/client => http-client/src/test/java/io/a2a/client/http}/A2ACardResolverTest.java (98%) rename {client/base/src/test/java/io/a2a/client => http-client/src/test/java/io/a2a/client/http}/JsonMessages.java (99%) diff --git a/README.md b/README.md index 10ee67c73..e3a667ac8 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,7 @@ To make use of the Java `Client`: ### 1. Add the A2A Java SDK Client dependency to your project -Adding a dependency on `a2a-java-sdk-client` will provide access to a `ClientFactory` +Adding a dependency on `a2a-java-sdk-client` will provide access to a `ClientBuilder` that you can use to create your A2A `Client`. ---- @@ -243,21 +243,16 @@ that you can use to create your A2A `Client`. ### 2. Add one or more dependencies on the A2A Java SDK Client Transport(s) you'd like to use -You need to add a dependency on at least one of the following client transport modules: +By default, the sdk-client is coming with the JSONRPC transport dependency. Despite the fact that the JSONRPC transport +dependency is included by default, you still need to add the transport to the Client as described in [JSON-RPC Transport section](#json-rpc-transport-configuration). + + +If you want to use another transport (such as GRPC or HTTP+JSON), you'll need to add a relevant dependency: ---- > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* ---- -```xml - - io.github.a2asdk - a2a-java-sdk-client-transport-jsonrpc - - ${io.a2a.sdk.version} - -``` - ```xml io.github.a2asdk @@ -271,13 +266,13 @@ Support for the HTTP+JSON/REST transport will be coming soon. ### Sample Usage -#### Create a Client using the ClientFactory +#### Create a Client using the ClientBuilder ```java // First, get the agent card for the A2A server agent you want to connect to AgentCard agentCard = new A2ACardResolver("http://localhost:1234").getAgentCard(); -// Specify configuration for the ClientFactory +// Specify configuration for the ClientBuilder ClientConfig clientConfig = new ClientConfig.Builder() .setAcceptedOutputModes(List.of("text")) .build(); @@ -305,32 +300,39 @@ Consumer errorHandler = error -> { ... }; -// Create the client using ClientFactory -ClientFactory clientFactory = new ClientFactory(clientConfig); -Client client = clientFactory.create(agentCard, consumers, errorHandler); +// Create the client using the builder +Client client = Client + .builder(agentCard) + .clientConfig(clientConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .addConsumers(consumers) + .streamingErrorHandler(errorHandler) + .build(); ``` #### Configuring Transport-Specific Settings -Different transport protocols can be configured with specific settings using `ClientTransportConfig` implementations. The A2A Java SDK provides `JSONRPCTransportConfig` for the JSON-RPC transport and `GrpcTransportConfig` for the gRPC transport. +Different transport protocols can be configured with specific settings using specific `ClientTransportConfig` implementations. The A2A Java SDK provides `JSONRPCTransportConfig` for the JSON-RPC transport and `GrpcTransportConfig` for the gRPC transport. ##### JSON-RPC Transport Configuration For the JSON-RPC transport, if you'd like to use the default `JdkA2AHttpClient`, no additional -configuration is needed. To use a custom HTTP client instead, simply create a `JSONRPCTransportConfig` +configuration is needed. To use a custom HTTP client implementation, simply create a `JSONRPCTransportConfig` as follows: ```java // Create a custom HTTP client A2AHttpClient customHttpClient = ... -// Create JSON-RPC transport configuration -JSONRPCTransportConfig jsonrpcConfig = new JSONRPCTransportConfig(customHttpClient); - -// Configure the client with transport-specific settings +// Configure the client settings ClientConfig clientConfig = new ClientConfig.Builder() .setAcceptedOutputModes(List.of("text")) - .setClientTransportConfigs(List.of(jsonrpcConfig)) + .build(); + +Client client = Client + .builder(agentCard) + .clientConfig(clientConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig(customHttpClient)) .build(); ``` @@ -346,13 +348,15 @@ Function channelFactory = agentUrl -> { .build(); }; -// Create gRPC transport configuration -GrpcTransportConfig grpcConfig = new GrpcTransportConfig(channelFactory); - // Configure the client with transport-specific settings ClientConfig clientConfig = new ClientConfig.Builder() .setAcceptedOutputModes(List.of("text")) - .setClientTransportConfigs(List.of(grpcConfig)) + .build(); + +Client client = Client + .builder(agentCard) + .clientConfig(clientConfig) + .withTransport(GrpcTransport.class, new GrpcTransportConfig(channelFactory)) .build(); ``` @@ -363,15 +367,11 @@ will be used based on the selected transport: ```java // Configure both JSON-RPC and gRPC transports -List transportConfigs = List.of( - new JSONRPCTransportConfig(...), - new GrpcTransportConfig(...) -); - -ClientConfig clientConfig = new ClientConfig.Builder() - .setAcceptedOutputModes(List.of("text")) - .setClientTransportConfigs(transportConfigs) - .build(); +Client client = Client + .builder(agentCard) + .withTransport(GrpcTransport.class, new GrpcTransportConfig(channelFactory)) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .build(); ``` #### Send a message to the A2A server agent diff --git a/client/base/pom.xml b/client/base/pom.xml index 30750e13d..6df47c1b7 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -20,15 +20,20 @@ ${project.groupId} - a2a-java-sdk-client-config + a2a-java-sdk-http-client ${project.groupId} - a2a-java-sdk-http-client + a2a-java-sdk-client-transport-spi ${project.groupId} - a2a-java-sdk-client-transport-spi + a2a-java-sdk-client-transport-jsonrpc + + + ${project.groupId} + a2a-java-sdk-client-transport-grpc + test ${project.groupId} diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index 46b9c11ad..f72945677 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -3,7 +3,7 @@ import java.util.Collections; import java.util.Map; -import io.a2a.client.A2ACardResolver; +import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.spec.A2AClientError; diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index 1667ed56f..526b83e01 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -7,7 +7,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; -import io.a2a.client.config.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -387,4 +387,4 @@ public Consumer getStreamingErrorHandler() { return streamingErrorHandler; } -} +} \ No newline at end of file diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index 103b78eef..c7152a584 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -5,8 +5,8 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; -import io.a2a.client.config.ClientCallContext; import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; @@ -28,20 +28,27 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskStatusUpdateEvent; +import static io.a2a.util.Assert.checkNotNullParam; + public class Client extends AbstractClient { private final ClientConfig clientConfig; private final ClientTransport clientTransport; private AgentCard agentCard; - public Client(AgentCard agentCard, ClientConfig clientConfig, ClientTransport clientTransport, + Client(AgentCard agentCard, ClientConfig clientConfig, ClientTransport clientTransport, List> consumers, Consumer streamingErrorHandler) { super(consumers, streamingErrorHandler); + checkNotNullParam("agentCard", agentCard); + this.agentCard = agentCard; this.clientConfig = clientConfig; this.clientTransport = clientTransport; } + public static ClientBuilder builder(AgentCard agentCard) { + return new ClientBuilder(agentCard); + } @Override public void sendMessage(Message request, ClientCallContext context) throws A2AClientException { diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java new file mode 100644 index 000000000..a093dfae5 --- /dev/null +++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java @@ -0,0 +1,164 @@ +package io.a2a.client; + +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.ClientTransportConfig; +import io.a2a.client.transport.spi.ClientTransportConfigBuilder; +import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class ClientBuilder { + + private static final Map>> transportProviderRegistry = new HashMap<>(); + private static final Map, String> transportProtocolMapping = new HashMap<>(); + + static { + ServiceLoader loader = ServiceLoader.load(ClientTransportProvider.class); + for (ClientTransportProvider transport : loader) { + transportProviderRegistry.put(transport.getTransportProtocol(), transport); + transportProtocolMapping.put(transport.getTransportProtocolClass(), transport.getTransportProtocol()); + } + } + + private final AgentCard agentCard; + + private final List> consumers = new ArrayList<>(); + private Consumer streamErrorHandler; + private ClientConfig clientConfig; + + private final Map, ClientTransportConfig> clientTransports = new LinkedHashMap<>(); + + ClientBuilder(AgentCard agentCard) { + this.agentCard = agentCard; + } + + public ClientBuilder withTransport(Class clazz, ClientTransportConfigBuilder, ?> configBuilder) { + return withTransport(clazz, configBuilder.build()); + } + + public ClientBuilder withTransport(Class clazz, ClientTransportConfig config) { + clientTransports.put(clazz, config); + + return this; + } + + public ClientBuilder addConsumer(BiConsumer consumer) { + this.consumers.add(consumer); + return this; + } + + public ClientBuilder addConsumers(List> consumers) { + this.consumers.addAll(consumers); + return this; + } + + public ClientBuilder streamingErrorHandler(Consumer streamErrorHandler) { + this.streamErrorHandler = streamErrorHandler; + return this; + } + + public ClientBuilder clientConfig(ClientConfig clientConfig) { + this.clientConfig = clientConfig; + return this; + } + + public Client build() throws A2AClientException { + if (this.clientConfig == null) { + this.clientConfig = new ClientConfig.Builder().build(); + } + + ClientTransport clientTransport = buildClientTransport(); + + return new Client(agentCard, clientConfig, clientTransport, consumers, streamErrorHandler); + } + + @SuppressWarnings("unchecked") + private ClientTransport buildClientTransport() throws A2AClientException { + // Get the preferred transport + AgentInterface agentInterface = findBestClientTransport(); + Class transportProtocolClass = transportProviderRegistry.get(agentInterface.transport()).getTransportProtocolClass(); + + // Get the transport provider associated to the protocol + ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.transport()); + + // Retrieve the configuration associated to the preferred transport + ClientTransportConfig clientTransportConfig = clientTransports.get(transportProtocolClass); + + if (clientTransportConfig == null) { + throw new A2AClientException("Missing required TransportConfig for " + agentInterface.transport()); + } + + return clientTransportProvider.create(clientTransportConfig, agentCard, agentInterface.url()); + } + + private Map getServerPreferredTransports() { + Map serverPreferredTransports = new LinkedHashMap<>(); + serverPreferredTransports.put(agentCard.preferredTransport(), agentCard.url()); + if (agentCard.additionalInterfaces() != null) { + for (AgentInterface agentInterface : agentCard.additionalInterfaces()) { + serverPreferredTransports.putIfAbsent(agentInterface.transport(), agentInterface.url()); + } + } + return serverPreferredTransports; + } + + private List getClientPreferredTransports() { + List supportedClientTransports = new ArrayList<>(); + + if (clientTransports.isEmpty()) { + // default to JSONRPC if not specified + supportedClientTransports.add(TransportProtocol.JSONRPC.asString()); + } else { + clientTransports.forEach((aClass, clientTransportConfig) -> supportedClientTransports.add(transportProtocolMapping.get(aClass))); + } + return supportedClientTransports; + } + + private AgentInterface findBestClientTransport() throws A2AClientException { + // Retrieve transport supported by the A2A server + Map serverPreferredTransports = getServerPreferredTransports(); + + // Retrieve transport configured for this client (using withTransport methods) + List clientPreferredTransports = getClientPreferredTransports(); + + String transportProtocol = null; + String transportUrl = null; + if (clientConfig.isUseClientPreference()) { + for (String clientPreferredTransport : clientPreferredTransports) { + if (serverPreferredTransports.containsKey(clientPreferredTransport)) { + transportProtocol = clientPreferredTransport; + transportUrl = serverPreferredTransports.get(transportProtocol); + break; + } + } + } else { + for (Map.Entry transport : serverPreferredTransports.entrySet()) { + if (clientPreferredTransports.contains(transport.getKey())) { + transportProtocol = transport.getKey(); + transportUrl = transport.getValue(); + break; + } + } + } + if (transportProtocol == null || transportUrl == null) { + throw new A2AClientException("No compatible transport found"); + } + if (! transportProviderRegistry.containsKey(transportProtocol)) { + throw new A2AClientException("No client available for " + transportProtocol); + } + + return new AgentInterface(transportProtocol, transportUrl); + } +} diff --git a/client/base/src/main/java/io/a2a/client/ClientFactory.java b/client/base/src/main/java/io/a2a/client/ClientFactory.java deleted file mode 100644 index 988df4ea4..000000000 --- a/client/base/src/main/java/io/a2a/client/ClientFactory.java +++ /dev/null @@ -1,137 +0,0 @@ -package io.a2a.client; - -import static io.a2a.util.Assert.checkNotNullParam; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import io.a2a.client.config.ClientCallInterceptor; -import io.a2a.client.config.ClientConfig; -import io.a2a.client.transport.spi.ClientTransport; -import io.a2a.client.transport.spi.ClientTransportProvider; -import io.a2a.spec.A2AClientException; -import io.a2a.spec.AgentCard; -import io.a2a.spec.AgentInterface; -import io.a2a.spec.TransportProtocol; - -/** - * Used to generate the appropriate client for the agent. - */ -public class ClientFactory { - - private final ClientConfig clientConfig; - private final Map transportProviderRegistry = new HashMap<>(); - - /** - * Create a client factory used to generate the appropriate client for the agent. - * - * @param clientConfig the client config to use - */ - public ClientFactory(ClientConfig clientConfig) { - checkNotNullParam("clientConfig", clientConfig); - this.clientConfig = clientConfig; - ServiceLoader loader = ServiceLoader.load(ClientTransportProvider.class); - for (ClientTransportProvider transport : loader) { - this.transportProviderRegistry.put(transport.getTransportProtocol(), transport); - } - } - - /** - * Create a new A2A client for the given agent card. - * - * @param agentCard the agent card for the remote agent - * @param consumers a list of consumers to pass responses from the remote agent to - * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs - * @return the client to use - * @throws A2AClientException if the client cannot be created for any reason - */ - public Client create(AgentCard agentCard, List> consumers, - Consumer streamingErrorHandler) throws A2AClientException { - return create(agentCard, consumers, streamingErrorHandler, null); - } - - /** - * Create a new A2A client for the given agent card. - * - * @param agentCard the agent card for the remote agent - * @param consumers a list of consumers to pass responses from the remote agent to - * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs - * @param interceptors the optional list of client call interceptors (may be {@code null}) - * @return the client to use - * @throws A2AClientException if the client cannot be created for any reason - */ - public Client create(AgentCard agentCard, List> consumers, - Consumer streamingErrorHandler, List interceptors) throws A2AClientException { - checkNotNullParam("agentCard", agentCard); - checkNotNullParam("consumers", consumers); - LinkedHashMap serverPreferredTransports = getServerPreferredTransports(agentCard); - List clientPreferredTransports = getClientPreferredTransports(); - ClientTransport clientTransport = getClientTransport(clientPreferredTransports, serverPreferredTransports, - agentCard, interceptors); - return new Client(agentCard, clientConfig, clientTransport, consumers, streamingErrorHandler); - } - - private static LinkedHashMap getServerPreferredTransports(AgentCard agentCard) { - LinkedHashMap serverPreferredTransports = new LinkedHashMap<>(); - serverPreferredTransports.put(agentCard.preferredTransport(), agentCard.url()); - if (agentCard.additionalInterfaces() != null) { - for (AgentInterface agentInterface : agentCard.additionalInterfaces()) { - serverPreferredTransports.putIfAbsent(agentInterface.transport(), agentInterface.url()); - } - } - return serverPreferredTransports; - } - - private List getClientPreferredTransports() { - List preferredClientTransports = clientConfig.getSupportedTransports(); - if (preferredClientTransports == null) { - preferredClientTransports = new ArrayList<>(); - } - if (preferredClientTransports.isEmpty()) { - // default to JSONRPC if not specified - preferredClientTransports.add(TransportProtocol.JSONRPC.asString()); - } - return preferredClientTransports; - } - - private ClientTransport getClientTransport(List clientPreferredTransports, - LinkedHashMap serverPreferredTransports, - AgentCard agentCard, - List interceptors) throws A2AClientException { - String transportProtocol = null; - String transportUrl = null; - if (clientConfig.isUseClientPreference()) { - for (String clientPreferredTransport : clientPreferredTransports) { - if (serverPreferredTransports.containsKey(clientPreferredTransport)) { - transportProtocol = clientPreferredTransport; - transportUrl = serverPreferredTransports.get(transportProtocol); - break; - } - } - } else { - for (Map.Entry transport : serverPreferredTransports.entrySet()) { - if (clientPreferredTransports.contains(transport.getKey())) { - transportProtocol = transport.getKey(); - transportUrl = transport.getValue(); - break; - } - } - } - if (transportProtocol == null || transportUrl == null) { - throw new A2AClientException("No compatible transports found"); - } - if (! transportProviderRegistry.containsKey(transportProtocol)) { - throw new A2AClientException("No client available for " + transportProtocol); - } - - ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(transportProtocol); - return clientTransportProvider.create(clientConfig, agentCard, transportUrl, interceptors); - } - -} diff --git a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java index 602102f5f..591e70ae4 100644 --- a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java +++ b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java @@ -135,4 +135,4 @@ private void saveTask(Task task) { contextId = currentTask.getContextId(); } } -} +} \ No newline at end of file diff --git a/client/config/src/main/java/io/a2a/client/config/ClientConfig.java b/client/base/src/main/java/io/a2a/client/config/ClientConfig.java similarity index 71% rename from client/config/src/main/java/io/a2a/client/config/ClientConfig.java rename to client/base/src/main/java/io/a2a/client/config/ClientConfig.java index 913495175..fd8a016f4 100644 --- a/client/config/src/main/java/io/a2a/client/config/ClientConfig.java +++ b/client/base/src/main/java/io/a2a/client/config/ClientConfig.java @@ -12,22 +12,17 @@ public class ClientConfig { private final Boolean streaming; private final Boolean polling; - private final List clientTransportConfigs; - private final List supportedTransports; private final Boolean useClientPreference; private final List acceptedOutputModes; private final PushNotificationConfig pushNotificationConfig; private final Integer historyLength; private final Map metadata; - public ClientConfig(Boolean streaming, Boolean polling, List clientTransportConfigs, - List supportedTransports, Boolean useClientPreference, + public ClientConfig(Boolean streaming, Boolean polling, Boolean useClientPreference, List acceptedOutputModes, PushNotificationConfig pushNotificationConfig, Integer historyLength, Map metadata) { this.streaming = streaming == null ? true : streaming; this.polling = polling == null ? false : polling; - this.clientTransportConfigs = clientTransportConfigs; - this.supportedTransports = supportedTransports; this.useClientPreference = useClientPreference == null ? false : useClientPreference; this.acceptedOutputModes = acceptedOutputModes; this.pushNotificationConfig = pushNotificationConfig; @@ -43,14 +38,6 @@ public boolean isPolling() { return polling; } - public List getClientTransportConfigs() { - return clientTransportConfigs; - } - - public List getSupportedTransports() { - return supportedTransports; - } - public boolean isUseClientPreference() { return useClientPreference; } @@ -74,8 +61,6 @@ public Map getMetadata() { public static class Builder { private Boolean streaming; private Boolean polling; - private List clientTransportConfigs; - private List supportedTransports; private Boolean useClientPreference; private List acceptedOutputModes; private PushNotificationConfig pushNotificationConfig; @@ -92,16 +77,6 @@ public Builder setPolling(Boolean polling) { return this; } - public Builder setClientTransportConfigs(List clientTransportConfigs) { - this.clientTransportConfigs = clientTransportConfigs; - return this; - } - - public Builder setSupportedTransports(List supportedTransports) { - this.supportedTransports = supportedTransports; - return this; - } - public Builder setUseClientPreference(Boolean useClientPreference) { this.useClientPreference = useClientPreference; return this; @@ -128,8 +103,8 @@ public Builder setMetadata(Map metadata) { } public ClientConfig build() { - return new ClientConfig(streaming, polling, clientTransportConfigs, - supportedTransports, useClientPreference, acceptedOutputModes, + return new ClientConfig(streaming, polling, + useClientPreference, acceptedOutputModes, pushNotificationConfig, historyLength, metadata); } } diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java new file mode 100644 index 000000000..b57ca52ed --- /dev/null +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -0,0 +1,96 @@ +package io.a2a.client; + +import io.a2a.client.config.ClientConfig; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.grpc.GrpcTransport; +import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.AgentSkill; +import io.a2a.spec.TransportProtocol; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; + +public class ClientBuilderTest { + + private AgentCard card = new AgentCard.Builder() + .name("Hello World Agent") + .description("Just a hello world agent") + .url("http://localhost:9999") + .version("1.0.0") + .documentationUrl("http://example.com/docs") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(true) + .stateTransitionHistory(true) + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("hello_world") + .name("Returns hello world") + .description("just returns hello world") + .tags(Collections.singletonList("hello world")) + .examples(List.of("hi", "hello world")) + .build())) + .protocolVersion("0.3.0") + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) + .build(); + + @Test() + public void shouldNotFindCompatibleTransport() throws A2AClientException { + A2AClientException exception = Assertions.assertThrows(A2AClientException.class, + () -> Client + .builder(card) + .clientConfig(new ClientConfig.Builder().setUseClientPreference(true).build()) + .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() + .channelFactory(s -> null)) + .build()); + + Assertions.assertTrue(exception.getMessage().contains("No compatible transport found")); + } + + @Test() + public void shouldNotFindConfigurationTransport() throws A2AClientException { + A2AClientException exception = Assertions.assertThrows(A2AClientException.class, + () -> Client + .builder(card) + .clientConfig(new ClientConfig.Builder().setUseClientPreference(true).build()) + .build()); + + Assertions.assertTrue(exception.getMessage().startsWith("Missing required TransportConfig for")); + } + + @Test + public void shouldCreateJSONRPCClient() throws A2AClientException { + Client client = Client + .builder(card) + .clientConfig(new ClientConfig.Builder().setUseClientPreference(true).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder() + .addInterceptor(null) + .httpClient(null)) + .build(); + + Assertions.assertNotNull(client); + } + + @Test + public void shouldCreateClient_differentConfigurations() throws A2AClientException { + Client client = Client + .builder(card) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig(new JdkA2AHttpClient())) + .build(); + + Assertions.assertNotNull(client); + } +} diff --git a/client/config/pom.xml b/client/config/pom.xml deleted file mode 100644 index db7afdc30..000000000 --- a/client/config/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - 4.0.0 - - - io.github.a2asdk - a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT - ../../pom.xml - - a2a-java-sdk-client-config - - jar - - Java SDK A2A Client Configuration - Java SDK for the Agent2Agent Protocol (A2A) - Client Configuration - - - - ${project.groupId} - a2a-java-sdk-spec - - - org.junit.jupiter - junit-jupiter-api - test - - - - org.mock-server - mockserver-netty - test - - - - \ No newline at end of file diff --git a/client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java b/client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java deleted file mode 100644 index 560b69afb..000000000 --- a/client/config/src/main/java/io/a2a/client/config/ClientTransportConfig.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.a2a.client.config; - -/** - * Configuration for an A2A client transport. - */ -public interface ClientTransportConfig { -} \ No newline at end of file diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 758130291..b910d6ac7 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -17,10 +17,6 @@ Java SDK for the Agent2Agent Protocol (A2A) - gRPC Client Transport - - ${project.groupId} - a2a-java-sdk-client-config - ${project.groupId} a2a-java-sdk-common diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index f709ed362..b313fb43a 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -10,7 +10,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import io.a2a.client.config.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.CancelTaskRequest; diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java index 5717f59b7..c6fe443d2 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java @@ -1,20 +1,21 @@ package io.a2a.client.transport.grpc; -import java.util.function.Function; - -import io.a2a.client.config.ClientTransportConfig; +import io.a2a.client.transport.spi.ClientTransportConfig; +import io.a2a.util.Assert; import io.grpc.Channel; -public class GrpcTransportConfig implements ClientTransportConfig { +import java.util.function.Function; + +public class GrpcTransportConfig extends ClientTransportConfig { private final Function channelFactory; public GrpcTransportConfig(Function channelFactory) { + Assert.checkNotNullParam("channelFactory", channelFactory); this.channelFactory = channelFactory; } public Function getChannelFactory() { - return channelFactory; + return this.channelFactory; } - } \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java new file mode 100644 index 000000000..68909e065 --- /dev/null +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java @@ -0,0 +1,25 @@ +package io.a2a.client.transport.grpc; + +import io.a2a.client.transport.spi.ClientTransportConfigBuilder; +import io.a2a.util.Assert; +import io.grpc.Channel; + +import java.util.function.Function; + +public class GrpcTransportConfigBuilder extends ClientTransportConfigBuilder { + + private Function channelFactory; + + public GrpcTransportConfigBuilder channelFactory(Function channelFactory) { + Assert.checkNotNullParam("channelFactory", channelFactory); + + this.channelFactory = channelFactory; + + return this; + } + + @Override + public GrpcTransportConfig build() { + return new GrpcTransportConfig(channelFactory); + } +} \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java index bb28ef9c9..229ac4149 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -1,11 +1,5 @@ package io.a2a.client.transport.grpc; -import java.util.List; - -import io.a2a.client.config.ClientCallInterceptor; -import io.a2a.client.config.ClientConfig; -import io.a2a.client.config.ClientTransportConfig; -import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; @@ -15,21 +9,17 @@ /** * Provider for gRPC transport implementation. */ -public class GrpcTransportProvider implements ClientTransportProvider { +public class GrpcTransportProvider implements ClientTransportProvider { @Override - public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, - String agentUrl, List interceptors) throws A2AClientException { + public GrpcTransport create(GrpcTransportConfig grpcTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { // not making use of the interceptors for gRPC for now - List clientTransportConfigs = clientConfig.getClientTransportConfigs(); - if (clientTransportConfigs != null) { - for (ClientTransportConfig clientTransportConfig : clientTransportConfigs) { - if (clientTransportConfig instanceof GrpcTransportConfig grpcTransportConfig) { - Channel channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); - return new GrpcTransport(channel, agentCard); - } - } + + Channel channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); + if (channel != null) { + return new GrpcTransport(channel, agentCard); } + throw new A2AClientException("Missing required GrpcTransportConfig"); } @@ -38,4 +28,8 @@ public String getTransportProtocol() { return TransportProtocol.GRPC.asString(); } + @Override + public Class getTransportProtocolClass() { + return GrpcTransport.class; + } } diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 66010fec3..172c243a3 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -17,14 +17,6 @@ Java SDK for the Agent2Agent Protocol (A2A) - JSONRPC Client Transport - - ${project.groupId} - a2a-java-sdk-client - - - ${project.groupId} - a2a-java-sdk-client-config - ${project.groupId} a2a-java-sdk-http-client diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index d3d93e463..8464911f5 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -10,10 +10,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.client.A2ACardResolver; -import io.a2a.client.config.ClientCallContext; -import io.a2a.client.config.ClientCallInterceptor; -import io.a2a.client.config.PayloadAndHeaders; +import io.a2a.client.http.A2ACardResolver; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; +import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.client.http.JdkA2AHttpClient; diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java index 1627b9090..efd3bbdf9 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java @@ -1,12 +1,16 @@ package io.a2a.client.transport.jsonrpc; -import io.a2a.client.config.ClientTransportConfig; +import io.a2a.client.transport.spi.ClientTransportConfig; import io.a2a.client.http.A2AHttpClient; -public class JSONRPCTransportConfig implements ClientTransportConfig { +public class JSONRPCTransportConfig extends ClientTransportConfig { private final A2AHttpClient httpClient; + public JSONRPCTransportConfig() { + this.httpClient = null; + } + public JSONRPCTransportConfig(A2AHttpClient httpClient) { this.httpClient = httpClient; } diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java new file mode 100644 index 000000000..64153620f --- /dev/null +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java @@ -0,0 +1,28 @@ +package io.a2a.client.transport.jsonrpc; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.spi.ClientTransportConfigBuilder; + +public class JSONRPCTransportConfigBuilder extends ClientTransportConfigBuilder { + + private A2AHttpClient httpClient; + + public JSONRPCTransportConfigBuilder httpClient(A2AHttpClient httpClient) { + this.httpClient = httpClient; + + return this; + } + + @Override + public JSONRPCTransportConfig build() { + // No HTTP client provided, fallback to the default one (JDK-based implementation) + if (httpClient == null) { + httpClient = new JdkA2AHttpClient(); + } + + JSONRPCTransportConfig config = new JSONRPCTransportConfig(httpClient); + config.setInterceptors(this.interceptors); + return config; + } +} \ No newline at end of file diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java index b9f8ce42b..97c22866a 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -1,37 +1,29 @@ package io.a2a.client.transport.jsonrpc; -import java.util.List; - -import io.a2a.client.config.ClientCallInterceptor; -import io.a2a.client.config.ClientConfig; -import io.a2a.client.config.ClientTransportConfig; -import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.TransportProtocol; -public class JSONRPCTransportProvider implements ClientTransportProvider { +public class JSONRPCTransportProvider implements ClientTransportProvider { @Override - public ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, - String agentUrl, List interceptors) throws A2AClientException { - A2AHttpClient httpClient = null; - List clientTransportConfigs = clientConfig.getClientTransportConfigs(); - if (clientTransportConfigs != null) { - for (ClientTransportConfig clientTransportConfig : clientTransportConfigs) { - if (clientTransportConfig instanceof JSONRPCTransportConfig jsonrpcTransportConfig) { - httpClient = jsonrpcTransportConfig.getHttpClient(); - break; - } - } + public JSONRPCTransport create(JSONRPCTransportConfig clientTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { + if (clientTransportConfig == null) { + clientTransportConfig = new JSONRPCTransportConfig(new JdkA2AHttpClient()); } - return new JSONRPCTransport(httpClient, agentCard, agentUrl, interceptors); + + return new JSONRPCTransport(clientTransportConfig.getHttpClient(), agentCard, agentUrl, clientTransportConfig.getInterceptors()); } @Override public String getTransportProtocol() { return TransportProtocol.JSONRPC.asString(); } + + @Override + public Class getTransportProtocolClass() { + return JSONRPCTransport.class; + } } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index 0ba3063e0..66091532e 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -18,7 +18,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.spec.Artifact; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; @@ -61,17 +60,17 @@ public void testSendStreamingMessageParams() { .contextId("context-test") .messageId("message-test") .build(); - + MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() .acceptedOutputModes(List.of("text")) .blocking(false) .build(); - + MessageSendParams params = new MessageSendParams.Builder() .message(message) .configuration(configuration) .build(); - + assertNotNull(params); assertEquals(message, params.message()); assertEquals(configuration, params.configuration()); diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index b83d5adaf..99b183289 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -17,10 +17,6 @@ Java SDK for the Agent2Agent Protocol (A2A) - Client Transport SPI - - io.github.a2asdk - a2a-java-sdk-client-config - io.github.a2asdk a2a-java-sdk-spec diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 163dd6582..8084d8195 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.function.Consumer; -import io.a2a.client.config.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java new file mode 100644 index 000000000..657971383 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java @@ -0,0 +1,21 @@ +package io.a2a.client.transport.spi; + +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; + +import java.util.List; + +/** + * Configuration for an A2A client transport. + */ +public abstract class ClientTransportConfig { + + protected List interceptors; + + public void setInterceptors(List interceptors) { + this.interceptors = interceptors; + } + + public List getInterceptors() { + return interceptors; + } +} \ No newline at end of file diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java new file mode 100644 index 000000000..0858f910b --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java @@ -0,0 +1,22 @@ +package io.a2a.client.transport.spi; + +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; + +import java.util.ArrayList; +import java.util.List; + +public abstract class ClientTransportConfigBuilder, + B extends ClientTransportConfigBuilder> { + + protected List interceptors = new ArrayList<>(); + + public B addInterceptor(ClientCallInterceptor interceptor) { + if (interceptor != null) { + this.interceptors.add(interceptor); + } + + return (B) this; + } + + public abstract T build(); +} diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java index e4127ff82..4026bcc8e 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java @@ -1,34 +1,29 @@ package io.a2a.client.transport.spi; -import java.util.List; - -import io.a2a.client.config.ClientCallInterceptor; -import io.a2a.client.config.ClientConfig; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; /** * Client transport provider interface. */ -public interface ClientTransportProvider { +public interface ClientTransportProvider> { /** * Create a client transport. * - * @param clientConfig the client config to use - * @param agentCard the agent card for the remote agent + * @param clientTransportConfig the client transport config to use * @param agentUrl the remote agent's URL - * @param interceptors the optional interceptors to use for a client call (may be {@code null}) * @return the client transport * @throws io.a2a.spec.A2AClientException if an error occurs trying to create the client */ - ClientTransport create(ClientConfig clientConfig, AgentCard agentCard, - String agentUrl, List interceptors) throws A2AClientException; + T create(C clientTransportConfig, AgentCard agentCard, + String agentUrl) throws A2AClientException; /** * Get the name of the client transport. */ String getTransportProtocol(); + Class getTransportProtocolClass(); } diff --git a/client/config/src/main/java/io/a2a/client/config/ClientCallContext.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallContext.java similarity index 92% rename from client/config/src/main/java/io/a2a/client/config/ClientCallContext.java rename to client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallContext.java index 0cfff4d65..288d7b54a 100644 --- a/client/config/src/main/java/io/a2a/client/config/ClientCallContext.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallContext.java @@ -1,4 +1,4 @@ -package io.a2a.client.config; +package io.a2a.client.transport.spi.interceptors; import java.util.Map; diff --git a/client/config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java similarity index 95% rename from client/config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java rename to client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java index 631cd8353..3a44ff70b 100644 --- a/client/config/src/main/java/io/a2a/client/config/ClientCallInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java @@ -1,4 +1,4 @@ -package io.a2a.client.config; +package io.a2a.client.transport.spi.interceptors; import java.util.Map; diff --git a/client/config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java similarity index 89% rename from client/config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java rename to client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java index 2146a5547..73983a096 100644 --- a/client/config/src/main/java/io/a2a/client/config/PayloadAndHeaders.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java @@ -1,4 +1,4 @@ -package io.a2a.client.config; +package io.a2a.client.transport.spi.interceptors; import java.util.Map; diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index cdfeef28d..3aaa5c221 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -20,10 +20,6 @@ io.github.a2asdk a2a-java-sdk-client - - io.github.a2asdk - a2a-java-sdk-client-transport-jsonrpc - diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index e2303ac30..b68fef434 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -10,12 +10,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.a2a.A2A; -import io.a2a.client.A2ACardResolver; + import io.a2a.client.Client; +import io.a2a.client.ClientBuilder; import io.a2a.client.ClientEvent; -import io.a2a.client.ClientFactory; import io.a2a.client.MessageEvent; -import io.a2a.client.config.ClientConfig; +import io.a2a.client.http.A2ACardResolver; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.Part; @@ -81,8 +81,11 @@ public static void main(String[] args) { messageResponse.completeExceptionally(error); }; - ClientFactory clientFactory = new ClientFactory(new ClientConfig.Builder().build()); - Client client = clientFactory.create(finalAgentCard, consumers, streamingErrorHandler); + Client client = Client + .builder(finalAgentCard) + .addConsumers(consumers) + .streamingErrorHandler(streamingErrorHandler).build(); + Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you System.out.println("Sending message: " + MESSAGE_TEXT); diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index a9579b75c..4cb7c0c3e 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -31,16 +31,6 @@ a2a-java-sdk-client ${project.version} - - io.github.a2asdk - a2a-java-sdk-client-transport-jsonrpc - ${project.version} - - - io.github.a2asdk - a2a-java-sdk-reference-jsonrpc - ${project.version} - diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 4ff3752bb..cbc4ee401 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -18,7 +18,12 @@ io.github.a2asdk - a2a-java-sdk-reference-jsonrpc + a2a-java-sdk-client + + + io.github.a2asdk + a2a-java-sdk-server-common + ${project.version} io.quarkus @@ -34,10 +39,6 @@ jakarta.ws.rs jakarta.ws.rs-api - - io.github.a2asdk - a2a-java-sdk-client-transport-jsonrpc - diff --git a/http-client/pom.xml b/http-client/pom.xml index f331fba11..fca8bc4fe 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -17,6 +17,11 @@ Java SDK for the Agent2Agent Protocol (A2A) - HTTP Client + + ${project.groupId} + a2a-java-sdk-spec + + org.junit.jupiter junit-jupiter-api diff --git a/client/base/src/main/java/io/a2a/client/A2ACardResolver.java b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java similarity index 96% rename from client/base/src/main/java/io/a2a/client/A2ACardResolver.java rename to http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java index f5de4746e..f510cd2ac 100644 --- a/client/base/src/main/java/io/a2a/client/A2ACardResolver.java +++ b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java @@ -1,4 +1,4 @@ -package io.a2a.client; +package io.a2a.client.http; import static io.a2a.util.Utils.unmarshalFrom; @@ -9,9 +9,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.http.A2AHttpResponse; -import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/client/base/src/test/java/io/a2a/client/A2ACardResolverTest.java b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java similarity index 98% rename from client/base/src/test/java/io/a2a/client/A2ACardResolverTest.java rename to http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java index aef8cca9c..0b855007b 100644 --- a/client/base/src/test/java/io/a2a/client/A2ACardResolverTest.java +++ b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java @@ -1,4 +1,4 @@ -package io.a2a.client; +package io.a2a.client.http; import static io.a2a.util.Utils.OBJECT_MAPPER; import static io.a2a.util.Utils.unmarshalFrom; @@ -11,8 +11,6 @@ import java.util.function.Consumer; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.http.A2AHttpResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; diff --git a/client/base/src/test/java/io/a2a/client/JsonMessages.java b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java similarity index 99% rename from client/base/src/test/java/io/a2a/client/JsonMessages.java rename to http-client/src/test/java/io/a2a/client/http/JsonMessages.java index b99da3623..f5fd0426a 100644 --- a/client/base/src/test/java/io/a2a/client/JsonMessages.java +++ b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java @@ -1,4 +1,4 @@ -package io.a2a.client; +package io.a2a.client.http; /** * Request and response messages used by the tests. These have been created following examples from diff --git a/pom.xml b/pom.xml index a242b91c2..d1677324e 100644 --- a/pom.xml +++ b/pom.xml @@ -89,6 +89,16 @@ a2a-java-sdk-client-transport-spi ${project.version} + + ${project.groupId} + a2a-java-sdk-client-transport-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-grpc + ${project.version} + ${project.groupId} a2a-java-sdk-common @@ -315,7 +325,6 @@ client/base - client/config client/transport/grpc client/transport/jsonrpc client/transport/spi diff --git a/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java b/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java index 95227c008..4afeae0e9 100644 --- a/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java +++ b/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java @@ -27,7 +27,6 @@ public AgentCard createDefaultAgentCard() { } @Produces - @Default @DefaultBean public AgentExecutor createDefaultAgentExecutor() { throw new IllegalStateException(wrap("Please provide your own AgentExecutor implementation")); diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java index 959188408..885dad731 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -1,11 +1,10 @@ package io.a2a.server.grpc.quarkus; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.TimeUnit; -import io.a2a.client.config.ClientTransportConfig; -import io.a2a.client.transport.grpc.GrpcTransportConfig; +import io.a2a.client.ClientBuilder; +import io.a2a.client.transport.grpc.GrpcTransport; +import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.TransportProtocol; import io.grpc.ManagedChannel; @@ -34,14 +33,11 @@ protected String getTransportUrl() { } @Override - protected List getClientTransportConfigs() { - List transportConfigs = new ArrayList<>(); - transportConfigs.add(new GrpcTransportConfig( - target -> { - channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); - return channel; - })); - return transportConfigs; + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder().channelFactory(target -> { + channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); + return channel; + })); } @AfterAll diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java index acebe4cb2..7426cb5bc 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java @@ -1,5 +1,8 @@ package io.a2a.server.apps.quarkus; +import io.a2a.client.ClientBuilder; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.TransportProtocol; import io.quarkus.test.junit.QuarkusTest; @@ -20,4 +23,9 @@ protected String getTransportProtocol() { protected String getTransportUrl() { return "http://localhost:8081"; } + + @Override + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()); + } } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 0cbee983f..c69fe1ac1 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -10,6 +10,12 @@ import static org.wildfly.common.Assert.assertNotNull; import static org.wildfly.common.Assert.assertTrue; +import io.a2a.client.Client; +import io.a2a.client.ClientBuilder; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.ClientEvent; +import io.a2a.client.MessageEvent; +import io.a2a.client.TaskUpdateEvent; import jakarta.ws.rs.core.MediaType; import java.io.EOFException; @@ -19,7 +25,6 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -33,13 +38,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; -import io.a2a.client.Client; -import io.a2a.client.ClientEvent; -import io.a2a.client.ClientFactory; -import io.a2a.client.MessageEvent; -import io.a2a.client.TaskUpdateEvent; -import io.a2a.client.config.ClientConfig; -import io.a2a.client.config.ClientTransportConfig; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentCapabilities; @@ -137,9 +135,7 @@ protected AbstractA2AServerTest(int serverPort) { /** * Get the transport configs to use for this test. */ - protected List getClientTransportConfigs() { - return new ArrayList<>(); - } + protected abstract void configureTransport(ClientBuilder builder); @Test public void testTaskStoreMethodsSanityTest() throws Exception { @@ -1265,11 +1261,15 @@ protected Client getNonStreamingClient() throws A2AClientException { private Client createClient(boolean streaming) throws A2AClientException { AgentCard agentCard = createTestAgentCard(); ClientConfig clientConfig = createClientConfig(streaming); - ClientFactory clientFactory = new ClientFactory(clientConfig); - return clientFactory.create(agentCard, List.of(), null, null); - } + ClientBuilder clientBuilder = Client + .builder(agentCard) + .clientConfig(clientConfig); + configureTransport(clientBuilder); + + return clientBuilder.build(); + } /** * Create a test agent card with the appropriate transport configuration. @@ -1299,19 +1299,10 @@ private AgentCard createTestAgentCard() { * Create client configuration with transport-specific settings. */ private ClientConfig createClientConfig(boolean streaming) { - ClientConfig.Builder builder = new ClientConfig.Builder() + return new ClientConfig.Builder() .setStreaming(streaming) - .setSupportedTransports(List.of(getTransportProtocol())) - .setAcceptedOutputModes(List.of("text")); - - // Set transport-specific configuration - List transportConfigs = getClientTransportConfigs(); - - if (!transportConfigs.isEmpty()) { - builder.setClientTransportConfigs(transportConfigs); - } - - return builder.build(); + .setAcceptedOutputModes(List.of("text")) + .build(); } } \ No newline at end of file From 0cb88e31f1e0d9f7f80d9a5572a951336dd127e1 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 28 Aug 2025 08:12:49 -0400 Subject: [PATCH 096/493] fix: Update the examples after the latest client changes (#244) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- .../java/io/a2a/examples/helloworld/HelloWorldClient.java | 6 +++++- examples/helloworld/server/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/AgentCardProducer.java | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index b68fef434..a82438a35 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -16,6 +16,8 @@ import io.a2a.client.ClientEvent; import io.a2a.client.MessageEvent; import io.a2a.client.http.A2ACardResolver; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.Part; @@ -84,7 +86,9 @@ public static void main(String[] args) { Client client = Client .builder(finalAgentCard) .addConsumers(consumers) - .streamingErrorHandler(streamingErrorHandler).build(); + .streamingErrorHandler(streamingErrorHandler) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .build(); Message message = A2A.toUserMessage(MESSAGE_TEXT); // the message ID will be automatically generated for you diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index cbc4ee401..b35ffa8a9 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -22,7 +22,7 @@ io.github.a2asdk - a2a-java-sdk-server-common + a2a-java-sdk-reference-jsonrpc ${project.version} diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 06450c935..5d7b0c999 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -37,7 +37,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.2.5") + .protocolVersion("0.3.0") .build(); } } From e2bb967516c785a74a9608fe13567229aa34730a Mon Sep 17 00:00:00 2001 From: Aleksey Ivashin Date: Thu, 28 Aug 2025 18:48:23 +0300 Subject: [PATCH 097/493] fix: duplication of parameters in json for defining subtypes. (#241) fixed #234 fix kind dublicate because classes have gatKind method and add correctly JsonTypeName for type --- spec/pom.xml | 5 + .../io/a2a/spec/APIKeySecurityScheme.java | 5 + spec/src/main/java/io/a2a/spec/DataPart.java | 5 + spec/src/main/java/io/a2a/spec/EventKind.java | 2 +- spec/src/main/java/io/a2a/spec/FilePart.java | 5 + .../io/a2a/spec/HTTPAuthSecurityScheme.java | 5 + spec/src/main/java/io/a2a/spec/Message.java | 4 + .../io/a2a/spec/MutualTLSSecurityScheme.java | 6 + .../io/a2a/spec/OAuth2SecurityScheme.java | 5 + .../a2a/spec/OpenIdConnectSecurityScheme.java | 7 + spec/src/main/java/io/a2a/spec/Part.java | 8 +- .../main/java/io/a2a/spec/SecurityScheme.java | 2 +- .../java/io/a2a/spec/StreamingEventKind.java | 2 +- spec/src/main/java/io/a2a/spec/Task.java | 4 + .../io/a2a/spec/TaskArtifactUpdateEvent.java | 4 + .../io/a2a/spec/TaskStatusUpdateEvent.java | 4 + spec/src/main/java/io/a2a/spec/TextPart.java | 6 + .../io/a2a/spec/SubTypeSerializationTest.java | 129 ++++++++++++++++++ 18 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java diff --git a/spec/pom.xml b/spec/pom.xml index ee377b421..13a880aa0 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -37,6 +37,11 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-params + test + \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index 73ecd134c..fd1956b9b 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -4,13 +4,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonValue; import io.a2a.util.Assert; +import static io.a2a.spec.APIKeySecurityScheme.API_KEY; + /** * Defines a security scheme using an API key. */ +@JsonTypeName(API_KEY) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class APIKeySecurityScheme implements SecurityScheme { @@ -64,6 +68,7 @@ public APIKeySecurityScheme(@JsonProperty("in") String in, @JsonProperty("name") @JsonProperty("description") String description, @JsonProperty("type") String type) { Assert.checkNotNullParam("in", in); Assert.checkNotNullParam("name", name); + Assert.checkNotNullParam("type", type); if (! type.equals(API_KEY)) { throw new IllegalArgumentException("Invalid type for APIKeySecurityScheme"); } diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index b40cbb839..7ac244263 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -6,15 +6,20 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.DataPart.DATA; + /** * Represents a structured data segment (e.g., JSON) within a message or artifact. */ +@JsonTypeName(DATA) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class DataPart extends Part> { + public static final String DATA = "data"; private final Map data; private final Map metadata; private final Kind kind; diff --git a/spec/src/main/java/io/a2a/spec/EventKind.java b/spec/src/main/java/io/a2a/spec/EventKind.java index 07ab3fe9f..a1ed7ef31 100644 --- a/spec/src/main/java/io/a2a/spec/EventKind.java +++ b/spec/src/main/java/io/a2a/spec/EventKind.java @@ -8,7 +8,7 @@ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.PROPERTY, + include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "kind", visible = true ) diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 82c2a4f5f..79bd2e5e8 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -6,16 +6,21 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.FilePart.FILE; + /** * Represents a file segment within a message or artifact. The file content can be * provided either directly as bytes or as a URI. */ +@JsonTypeName(FILE) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class FilePart extends Part { + public static final String FILE = "file"; private final FileContent file; private final Map metadata; private final Kind kind; diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 0d6ed417f..408fd2605 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -5,11 +5,15 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.HTTPAuthSecurityScheme.HTTP; + /** * Defines a security scheme using HTTP authentication. */ +@JsonTypeName(HTTP) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class HTTPAuthSecurityScheme implements SecurityScheme { @@ -28,6 +32,7 @@ public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String descrip public HTTPAuthSecurityScheme(@JsonProperty("bearerFormat") String bearerFormat, @JsonProperty("scheme") String scheme, @JsonProperty("description") String description, @JsonProperty("type") String type) { Assert.checkNotNullParam("scheme", scheme); + Assert.checkNotNullParam("type", type); if (! type.equals(HTTP)) { throw new IllegalArgumentException("Invalid type for HTTPAuthSecurityScheme"); } diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index ec21cf06b..be3f6c847 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -8,13 +8,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.util.Assert; +import static io.a2a.spec.Message.MESSAGE; + /** * Represents a single message in the conversation between a user and an agent. */ +@JsonTypeName(MESSAGE) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class Message implements EventKind, StreamingEventKind { diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index 78f389f38..37f5bd755 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -4,10 +4,15 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.a2a.util.Assert; + +import static io.a2a.spec.MutualTLSSecurityScheme.MUTUAL_TLS; /** * Defines a security scheme using mTLS authentication. */ +@JsonTypeName(MUTUAL_TLS) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class MutualTLSSecurityScheme implements SecurityScheme { @@ -27,6 +32,7 @@ public MutualTLSSecurityScheme() { @JsonCreator public MutualTLSSecurityScheme(@JsonProperty("description") String description, @JsonProperty("type") String type) { + Assert.checkNotNullParam("type", type); if (!type.equals(MUTUAL_TLS)) { throw new IllegalArgumentException("Invalid type for MutualTLSSecurityScheme"); } diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index fd5c746a1..9094eee7c 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -5,11 +5,15 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.OAuth2SecurityScheme.OAUTH2; + /** * Defines a security scheme using OAuth 2.0. */ +@JsonTypeName(OAUTH2) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class OAuth2SecurityScheme implements SecurityScheme { @@ -28,6 +32,7 @@ public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2M public OAuth2SecurityScheme(@JsonProperty("flows") OAuthFlows flows, @JsonProperty("description") String description, @JsonProperty("oauth2MetadataUrl") String oauth2MetadataUrl, @JsonProperty("type") String type) { Assert.checkNotNullParam("flows", flows); + Assert.checkNotNullParam("type", type); if (!type.equals(OAUTH2)) { throw new IllegalArgumentException("Invalid type for OAuth2SecurityScheme"); } diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 26a07d67a..01726d2ea 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -4,10 +4,15 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.a2a.util.Assert; + +import static io.a2a.spec.OpenIdConnectSecurityScheme.OPENID_CONNECT; /** * Defines a security scheme using OpenID Connect. */ +@JsonTypeName(OPENID_CONNECT) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class OpenIdConnectSecurityScheme implements SecurityScheme { @@ -24,6 +29,8 @@ public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description) @JsonCreator public OpenIdConnectSecurityScheme(@JsonProperty("openIdConnectUrl") String openIdConnectUrl, @JsonProperty("description") String description, @JsonProperty("type") String type) { + Assert.checkNotNullParam("type", type); + Assert.checkNotNullParam("openIdConnectUrl", openIdConnectUrl); if (!type.equals(OPENID_CONNECT)) { throw new IllegalArgumentException("Invalid type for OpenIdConnectSecurityScheme"); } diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index cf7f04a74..993665c7b 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -12,14 +12,14 @@ */ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.PROPERTY, + include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "kind", visible = true ) @JsonSubTypes({ - @JsonSubTypes.Type(value = TextPart.class, name = "text"), - @JsonSubTypes.Type(value = FilePart.class, name = "file"), - @JsonSubTypes.Type(value = DataPart.class, name = "data") + @JsonSubTypes.Type(value = TextPart.class, name = TextPart.TEXT), + @JsonSubTypes.Type(value = FilePart.class, name = FilePart.FILE), + @JsonSubTypes.Type(value = DataPart.class, name = DataPart.DATA) }) public abstract class Part { public enum Kind { diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index 37895d146..b3cc104e3 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -7,7 +7,7 @@ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.PROPERTY, + include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true ) diff --git a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java index a7a6b6232..b8c16b00a 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java +++ b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java @@ -10,7 +10,7 @@ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.PROPERTY, + include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "kind", visible = true ) diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index 00f757f31..9d948f383 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -7,12 +7,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.util.Assert; +import static io.a2a.spec.Task.TASK; + /** * Represents a single, stateful operation or conversation between a client and an agent. */ +@JsonTypeName(TASK) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class Task implements EventKind, StreamingEventKind { diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 279916c68..fec66caa8 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -6,12 +6,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.TaskArtifactUpdateEvent.ARTIFACT_UPDATE; + /** * An event sent by the agent to notify the client that an artifact has been * generated or updated. This is typically used in streaming models. */ +@JsonTypeName(ARTIFACT_UPDATE) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 788655530..478ec1946 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -6,12 +6,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.TaskStatusUpdateEvent.STATUS_UPDATE; + /** * An event sent by the agent to notify the client of a change in a task's status. * This is typically used in streaming or subscription models. */ +@JsonTypeName(STATUS_UPDATE) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index be0a4b927..ade2047ee 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -6,14 +6,20 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; +import static io.a2a.spec.TextPart.TEXT; + /** * Represents a text segment within a message or artifact. */ +@JsonTypeName(TEXT) @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public class TextPart extends Part { + + public static final String TEXT = "text"; private final String text; private final Map metadata; private final Kind kind; diff --git a/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java b/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java new file mode 100644 index 000000000..a4571f923 --- /dev/null +++ b/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java @@ -0,0 +1,129 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SubTypeSerializationTest { + + private static final Task MINIMAL_TASK = new Task.Builder() + .id("task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + private static final TypeReference> MAP_TYPE_REFERENCE = new TypeReference<>() { + }; + + private static final ObjectMapper OBJECT_MAPPER; + + static { + OBJECT_MAPPER = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addAbstractTypeMapping(Map.class, SingleKeyHashMap.class); + OBJECT_MAPPER.registerModule(module); + OBJECT_MAPPER.registerModule(new JavaTimeModule()); + } + + @ParameterizedTest + @MethodSource("serializationTestCases") + void testSubtypeSerialization(Object objectToSerialize, String typePropertyName, String expectedTypeValue) throws JsonProcessingException { + Map map = OBJECT_MAPPER.readValue(OBJECT_MAPPER.writeValueAsString(objectToSerialize), + MAP_TYPE_REFERENCE); + assertEquals(expectedTypeValue, map.get(typePropertyName)); + } + + private static Stream serializationTestCases() { + return Stream.of( + Arguments.of( + new TaskStatusUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(), "kind", TaskStatusUpdateEvent.STATUS_UPDATE + ), + Arguments.of( + new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), "kind", TaskArtifactUpdateEvent.ARTIFACT_UPDATE + ), + Arguments.of( + MINIMAL_TASK, "kind", Task.TASK + ), + Arguments.of( + new Message.Builder() + .role(Message.Role.USER) + .parts(new TextPart("tell me some jokes")) + .contextId("context-1234") + .messageId("message-1234") + .build(), "kind", Message.MESSAGE + ), + Arguments.of( + new TextPart("text"), "kind", TextPart.TEXT + ), + Arguments.of( + new FilePart(new FileWithUri( + "image/jpeg", null, "file:///path/to/image.jpg")), + "kind", FilePart.FILE + ), + Arguments.of( + new DataPart(Map.of("chartType", "bar")), "kind", DataPart.DATA + ), + Arguments.of( + new APIKeySecurityScheme.Builder() + .in("test").name("name").description("description").build(), + "type", APIKeySecurityScheme.API_KEY + ), + Arguments.of( + new HTTPAuthSecurityScheme.Builder() + .scheme("basic").description("Basic Auth").build(), + "type", HTTPAuthSecurityScheme.HTTP + ), + Arguments.of( + new OAuth2SecurityScheme.Builder() + .flows(new OAuthFlows.Builder().build()) + .description("oAuth2SecurityScheme").build(), + "type", OAuth2SecurityScheme.OAUTH2 + ), + Arguments.of( + new OpenIdConnectSecurityScheme.Builder() + .openIdConnectUrl("https://accounts.google.com/.well-known/openid-configuration") + .description("OpenId").build(), + "type", OpenIdConnectSecurityScheme.OPENID_CONNECT + ), + Arguments.of( + new MutualTLSSecurityScheme("mutual tls test"), + "type", MutualTLSSecurityScheme.MUTUAL_TLS + ) + ); + } + + private static class SingleKeyHashMap extends HashMap { + @Override + public V put(K key, V value) { + if (containsKey(key)) { + throw new IllegalArgumentException("duplicate key " + key + + " with value " + get(key) + " and new value " + value); + } + return super.put(key, value); + } + } + +} From 96ea0042540e0af0a213e8c42e3ebb9da1b6731b Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 29 Aug 2025 04:00:50 -0400 Subject: [PATCH 098/493] fix: Update AbstractA2AServerTestCase to add a test for sending a new streaming messgage using the Client instead of HttpClient (#245) --- .../apps/common/AbstractA2AServerTest.java | 121 ++++++++++-------- 1 file changed, 67 insertions(+), 54 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index c69fe1ac1..9811c401c 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -384,56 +384,13 @@ public void testGetAgentCard() throws A2AClientException { } @Test - public void testSendMessageStreamExistingTaskSuccess() throws Exception { - saveTaskInTaskStore(MINIMAL_TASK); - try { - Message message = new Message.Builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .build(); - - CountDownLatch latch = new CountDownLatch(1); - AtomicReference receivedMessage = new AtomicReference<>(); - AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); - AtomicReference errorRef = new AtomicReference<>(); - - BiConsumer consumer = (event, agentCard) -> { - if (event instanceof MessageEvent messageEvent) { - if (latch.getCount() > 0) { - receivedMessage.set(messageEvent.getMessage()); - latch.countDown(); - } else { - wasUnexpectedEvent.set(true); - } - } else { - wasUnexpectedEvent.set(true); - } - }; - - Consumer errorHandler = error -> { - errorRef.set(error); - latch.countDown(); - }; - - // testing the streaming send message - getClient().sendMessage(message, List.of(consumer), errorHandler); - - assertTrue(latch.await(10, TimeUnit.SECONDS)); - assertFalse(wasUnexpectedEvent.get()); - assertNull(errorRef.get()); + public void testSendMessageStreamNewMessageSuccess() throws Exception { + testSendStreamingMessage(false); + } - Message messageResponse = receivedMessage.get(); - assertNotNull(messageResponse); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); - } catch (A2AClientException e) { - fail("Unexpected exception during sendMessage: " + e.getMessage(), e); - } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); - } + @Test + public void testSendMessageStreamExistingTaskSuccess() throws Exception { + testSendStreamingMessage(true); } @Test @@ -955,19 +912,19 @@ public void testStreamingMethodWithAcceptHeader() throws Exception { assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - testSendStreamingMessage(MediaType.SERVER_SENT_EVENTS); + testSendStreamingMessageWithHttpClient(MediaType.SERVER_SENT_EVENTS); } @Test - public void testSendMessageStreamNewMessageSuccess() throws Exception { + public void testStreamingMethodWithoutAcceptHeader() throws Exception { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - - testSendStreamingMessage(null); + + testSendStreamingMessageWithHttpClient(null); } - private void testSendStreamingMessage(String mediaType) throws Exception { + private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exception { Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) @@ -1017,6 +974,62 @@ private void testSendStreamingMessage(String mediaType) throws Exception { } + public void testSendStreamingMessage(boolean createTask) throws Exception { + if (createTask) { + saveTaskInTaskStore(MINIMAL_TASK); + } + try { + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference receivedMessage = new AtomicReference<>(); + AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); + AtomicReference errorRef = new AtomicReference<>(); + + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof MessageEvent messageEvent) { + if (latch.getCount() > 0) { + receivedMessage.set(messageEvent.getMessage()); + latch.countDown(); + } else { + wasUnexpectedEvent.set(true); + } + } else { + wasUnexpectedEvent.set(true); + } + }; + + Consumer errorHandler = error -> { + errorRef.set(error); + latch.countDown(); + }; + + // testing the streaming send message + getClient().sendMessage(message, List.of(consumer), errorHandler); + + assertTrue(latch.await(10, TimeUnit.SECONDS)); + assertFalse(wasUnexpectedEvent.get()); + assertNull(errorRef.get()); + + Message messageResponse = receivedMessage.get(); + assertNotNull(messageResponse); + assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); + assertEquals(MESSAGE.getRole(), messageResponse.getRole()); + Part part = messageResponse.getParts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("test message", ((TextPart) part).getText()); + } catch (A2AClientException e) { + fail("Unexpected exception during sendMessage: " + e.getMessage(), e); + } finally { + if (createTask) { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + } + private CompletableFuture>> initialiseStreamingRequest( StreamingJSONRPCRequest request, String mediaType) throws Exception { From ef190abef88686cfcc4717b655d1b01e591c1c8b Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 29 Aug 2025 04:01:20 -0400 Subject: [PATCH 099/493] fix: Set the server and TCK related environment variables with the commands that use them (#243) --- .github/workflows/run-tck.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 6b9ca8bab..9d1c0d3fa 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -15,10 +15,8 @@ env: TCK_VERSION: 0.3.0.alpha # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 - # SUT_JSONRPC_URL to use for the TCK + # SUT_JSONRPC_URL to use for the TCK and the server agent SUT_JSONRPC_URL: http://localhost:9999 - # SUT_GRPC_URL to use for the TCK - SUT_GRPC_URL: http://localhost:9000 # Only run the latest job concurrency: @@ -57,7 +55,7 @@ jobs: - name: Build with Maven, skipping tests run: mvn -B install -DskipTests - name: Start SUT - run: mvn -B quarkus:dev & + run: SUT_GRPC_URL=http://localhost:9000 mvn -B quarkus:dev & #SUT_JSONRPC_URL already set working-directory: tck - name: Wait for SUT to start run: | From 0238c29b087b4b46940cec96b8d4471c374008a2 Mon Sep 17 00:00:00 2001 From: Aleksey Ivashin Date: Mon, 1 Sep 2025 15:51:59 +0300 Subject: [PATCH 100/493] fix: Change to use OffsetDateTime for compatibility with other languages (#242) fixed #232 replace LocalDateTime to OffsetDateTime and deleted pattern because JavaTimeModule correctly serializes/deserializes --- .../java/io/a2a/grpc/utils/ProtoUtils.java | 6 +- .../java/io/a2a/grpc/utils/ToProtoTest.java | 20 ++++ .../src/main/java/io/a2a/spec/TaskStatus.java | 16 +++- .../test/java/io/a2a/spec/TaskStatusTest.java | 94 +++++++++++++++++++ 4 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 spec/src/test/java/io/a2a/spec/TaskStatusTest.java diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index e53b0dc9b..76a159da3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -3,8 +3,8 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -292,7 +292,7 @@ private static io.a2a.grpc.TaskStatus taskStatus(TaskStatus taskStatus) { builder.setUpdate(message(taskStatus.message())); } if (taskStatus.timestamp() != null) { - Instant instant = taskStatus.timestamp().toInstant(ZoneOffset.UTC); + Instant instant = taskStatus.timestamp().toInstant(); builder.setTimestamp(com.google.protobuf.Timestamp.newBuilder().setSeconds(instant.getEpochSecond()).setNanos(instant.getNano()).build()); } return builder.build(); @@ -892,7 +892,7 @@ private static TaskStatus taskStatus(io.a2a.grpc.TaskStatus taskStatus) { return new TaskStatus( taskState(taskStatus.getState()), taskStatus.hasUpdate() ? message(taskStatus.getUpdate()) : null, - LocalDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) + OffsetDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) ); } diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index aca9caaad..67e8463ed 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -22,6 +22,8 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; + +import java.time.OffsetDateTime; import java.util.Collections; import java.util.List; import java.util.Map; @@ -269,4 +271,22 @@ public void convertSendMessageConfiguration() { assertEquals(1, result.getAcceptedOutputModesCount()); assertEquals("text", result.getAcceptedOutputModesBytes(0).toStringUtf8()); } + + @Test + public void convertTaskTimestampStatus() { + OffsetDateTime expectedTimestamp = OffsetDateTime.parse("2024-10-05T12:34:56Z"); + TaskStatus testStatus = new TaskStatus(TaskState.COMPLETED, null, expectedTimestamp); + Task task = new Task.Builder() + .id("task-123") + .contextId("context-456") + .status(testStatus) + .build(); + + io.a2a.grpc.Task grpcTask = ProtoUtils.ToProto.task(task); + task = ProtoUtils.FromProto.task(grpcTask); + TaskStatus status = task.getStatus(); + assertEquals(TaskState.COMPLETED, status.state()); + assertNotNull(status.timestamp()); + assertEquals(expectedTimestamp, status.timestamp()); + } } diff --git a/spec/src/main/java/io/a2a/spec/TaskStatus.java b/spec/src/main/java/io/a2a/spec/TaskStatus.java index d41455d04..0b9bed654 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatus.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatus.java @@ -1,6 +1,7 @@ package io.a2a.spec; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -14,14 +15,23 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record TaskStatus(TaskState state, Message message, - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS") LocalDateTime timestamp) { + @JsonFormat(shape = JsonFormat.Shape.STRING) OffsetDateTime timestamp) { public TaskStatus { Assert.checkNotNullParam("state", state); - timestamp = timestamp == null ? LocalDateTime.now() : timestamp; + timestamp = timestamp == null ? OffsetDateTime.now(ZoneOffset.UTC) : timestamp; } public TaskStatus(TaskState state) { this(state, null, null); } + + /** + * Constructor for testing purposes. + * @param state the task state + * @param timestamp timestamp generation + */ + TaskStatus(TaskState state, OffsetDateTime timestamp) { + this(state, null, timestamp); + } } diff --git a/spec/src/test/java/io/a2a/spec/TaskStatusTest.java b/spec/src/test/java/io/a2a/spec/TaskStatusTest.java new file mode 100644 index 000000000..7c4a9db8a --- /dev/null +++ b/spec/src/test/java/io/a2a/spec/TaskStatusTest.java @@ -0,0 +1,94 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class TaskStatusTest { + + private static final ObjectMapper OBJECT_MAPPER; + + private static final String REPLACE_TIMESTAMP_PATTERN = ".*\"timestamp\":\"([^\"]+)\",?.*"; + + static { + OBJECT_MAPPER = new ObjectMapper(); + OBJECT_MAPPER.registerModule(new JavaTimeModule()); + OBJECT_MAPPER.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false); + } + + @Test + public void testTaskStatusWithSetTimestamp() { + TaskState state = TaskState.WORKING; + OffsetDateTime offsetDateTime = OffsetDateTime.parse("2023-10-01T12:00:00Z"); + TaskStatus status = new TaskStatus(state, offsetDateTime); + + assertNotNull(status.timestamp()); + assertEquals(offsetDateTime, status.timestamp()); + } + + @Test + public void testTaskStatusWithProvidedTimestamp() { + OffsetDateTime providedTimestamp = OffsetDateTime.parse("2024-01-01T00:00:00Z"); + TaskState state = TaskState.COMPLETED; + TaskStatus status = new TaskStatus(state, providedTimestamp); + + assertEquals(providedTimestamp, status.timestamp()); + } + + @Test + public void testTaskStatusSerializationUsesISO8601Format() throws Exception { + OffsetDateTime expectedTimestamp = OffsetDateTime.parse("2023-10-01T12:00:00.234-05:00"); + TaskState state = TaskState.WORKING; + TaskStatus status = new TaskStatus(state, expectedTimestamp); + + String json = OBJECT_MAPPER.writeValueAsString(status); + + String expectedJson = "{\"state\":\"working\",\"timestamp\":\"2023-10-01T12:00:00.234-05:00\"}"; + assertEquals(expectedJson, json); + } + + @Test + public void testTaskStatusDeserializationWithValidISO8601Format() throws Exception { + String validJson = "{" + + "\"state\": \"auth-required\"," + + "\"timestamp\": \"2023-10-01T12:00:00.10+03:00\"" + + "}"; + + TaskStatus result = OBJECT_MAPPER.readValue(validJson, TaskStatus.class); + assertEquals(TaskState.AUTH_REQUIRED, result.state()); + assertNotNull(result.timestamp()); + assertEquals(OffsetDateTime.parse("2023-10-01T12:00:00.100+03:00"), result.timestamp()); + } + + @Test + public void testTaskStatusDeserializationWithInvalidISO8601FormatFails() { + String invalidJson = "{" + + "\"state\": \"completed\"," + + "\"timestamp\": \"2023/10/01 12:00:00\"" + + "}"; + + assertThrows( + com.fasterxml.jackson.databind.exc.InvalidFormatException.class, + () -> OBJECT_MAPPER.readValue(invalidJson, TaskStatus.class) + ); + } + + @Test + public void testTaskStatusJsonTimestampMatchesISO8601Regex() throws Exception { + TaskState state = TaskState.WORKING; + OffsetDateTime expectedTimestamp = OffsetDateTime.parse("2023-10-01T12:00:00.234Z"); + TaskStatus status = new TaskStatus(state, expectedTimestamp); + + String json = OBJECT_MAPPER.writeValueAsString(status); + + String timestampValue = json.replaceAll(REPLACE_TIMESTAMP_PATTERN, "$1"); + assertEquals(expectedTimestamp, OffsetDateTime.parse(timestampValue)); + } +} From bbfa07d9752554d589f90b1e2b880e946e70c0b8 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Mon, 1 Sep 2025 16:09:14 +0200 Subject: [PATCH 101/493] fix: issue 226 (#227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking if the TaskUpdate has an update message before trying to convert it. Fixes #226 🦕 Signed-off-by: Emmanuel Hugonnet Co-authored-by: Aleksey Ivashin --- spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 76a159da3..8a889cbba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -889,8 +889,11 @@ private static DataPart dataPart(io.a2a.grpc.DataPart dataPart) { } private static TaskStatus taskStatus(io.a2a.grpc.TaskStatus taskStatus) { - return new TaskStatus( - taskState(taskStatus.getState()), + TaskState state = taskState(taskStatus.getState()); + if (state == null) { + return null; + } + return new TaskStatus(state, taskStatus.hasUpdate() ? message(taskStatus.getUpdate()) : null, OffsetDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) ); From 0fb41154475605f9a23cbc2900e67b7df093bda9 Mon Sep 17 00:00:00 2001 From: "LogiBricks.AI" Date: Mon, 1 Sep 2025 22:15:45 +0800 Subject: [PATCH 102/493] test: improve TaskManager test coverage (#181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #46 🦕 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../io/a2a/server/tasks/TaskManagerTest.java | 364 +++++++++++++++++- 1 file changed, 359 insertions(+), 5 deletions(-) diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 768a52bb2..525ed1086 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -1,12 +1,18 @@ package io.a2a.server.tasks; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; - -import java.util.Collections; -import java.util.HashMap; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; @@ -18,8 +24,6 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.util.Utils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; public class TaskManagerTest { private static final String TASK_JSON = """ @@ -175,4 +179,354 @@ public void testGetTaskNoTaskId() { Task retrieved = taskManagerWithoutId.getTask(); assertNull(retrieved); } + + // Additional tests for missing coverage scenarios + + @Test + public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A2AServerException { + // Setup: Create a task with an existing artifact + Task initialTask = minimalTask; + Artifact existingArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("existing content"))) + .build(); + Task taskWithArtifact = new Task.Builder(initialTask) + .artifacts(Collections.singletonList(existingArtifact)) + .build(); + taskStore.save(taskWithArtifact); + + // Test: Append new parts to existing artifact + Artifact newArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("new content"))) + .build(); + TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(newArtifact) + .append(true) + .build(); + + Task updatedTask = taskManager.saveTaskEvent(event); + + assertEquals(1, updatedTask.getArtifacts().size()); + Artifact updatedArtifact = updatedTask.getArtifacts().get(0); + assertEquals("artifact-id", updatedArtifact.artifactId()); + assertEquals(2, updatedArtifact.parts().size()); + assertEquals("existing content", ((TextPart) updatedArtifact.parts().get(0)).getText()); + assertEquals("new content", ((TextPart) updatedArtifact.parts().get(1)).getText()); + } + + @Test + public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throws A2AServerException { + // Setup: Create a task without artifacts + Task initialTask = minimalTask; + taskStore.save(initialTask); + + // Test: Try to append to non-existent artifact (should be ignored) + Artifact newArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("new content"))) + .build(); + TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(newArtifact) + .append(true) + .build(); + + Task saved = taskManager.saveTaskEvent(event); + Task updatedTask = taskManager.getTask(); + + // Should have no artifacts since append was ignored + assertEquals(0, updatedTask.getArtifacts().size()); + } + + @Test + public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws A2AServerException { + // Setup: Create a task with an existing artifact + Task initialTask = minimalTask; + Artifact existingArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("existing content"))) + .build(); + Task taskWithArtifact = new Task.Builder(initialTask) + .artifacts(Collections.singletonList(existingArtifact)) + .build(); + taskStore.save(taskWithArtifact); + + // Test: Replace existing artifact (append=false) + Artifact newArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("replacement content"))) + .build(); + TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(newArtifact) + .append(false) + .build(); + + Task saved = taskManager.saveTaskEvent(event); + Task updatedTask = taskManager.getTask(); + + assertEquals(1, updatedTask.getArtifacts().size()); + Artifact updatedArtifact = updatedTask.getArtifacts().get(0); + assertEquals("artifact-id", updatedArtifact.artifactId()); + assertEquals(1, updatedArtifact.parts().size()); + assertEquals("replacement content", ((TextPart) updatedArtifact.parts().get(0)).getText()); + } + + @Test + public void testTaskArtifactUpdateEventAppendNullWithExistingArtifact() throws A2AServerException { + // Setup: Create a task with an existing artifact + Task initialTask = minimalTask; + Artifact existingArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("existing content"))) + .build(); + Task taskWithArtifact = new Task.Builder(initialTask) + .artifacts(Collections.singletonList(existingArtifact)) + .build(); + taskStore.save(taskWithArtifact); + + // Test: Replace existing artifact (append=null, defaults to false) + Artifact newArtifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("replacement content"))) + .build(); + TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(newArtifact) + .build(); // append is null + + Task saved = taskManager.saveTaskEvent(event); + Task updatedTask = taskManager.getTask(); + + assertEquals(1, updatedTask.getArtifacts().size()); + Artifact updatedArtifact = updatedTask.getArtifacts().get(0); + assertEquals("artifact-id", updatedArtifact.artifactId()); + assertEquals(1, updatedArtifact.parts().size()); + assertEquals("replacement content", ((TextPart) updatedArtifact.parts().get(0)).getText()); + } + + @Test + public void testAddingTaskWithDifferentIdFails() { + // Test that adding a task with a different id from the taskmanager's taskId fails + TaskManager taskManagerWithId = new TaskManager("task-abc", "session-xyz", taskStore, null); + + Task differentTask = new Task.Builder() + .id("different-task-id") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + assertThrows(A2AServerException.class, () -> { + taskManagerWithId.saveTaskEvent(differentTask); + }); + } + + @Test + public void testAddingTaskWithDifferentIdViaStatusUpdateFails() { + // Test that adding a status update with different taskId fails + TaskManager taskManagerWithId = new TaskManager("task-abc", "session-xyz", taskStore, null); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("different-task-id") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .build(); + + assertThrows(A2AServerException.class, () -> { + taskManagerWithId.saveTaskEvent(event); + }); + } + + @Test + public void testAddingTaskWithDifferentIdViaArtifactUpdateFails() { + // Test that adding an artifact update with different taskId fails + TaskManager taskManagerWithId = new TaskManager("task-abc", "session-xyz", taskStore, null); + + Artifact artifact = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("content"))) + .build(); + TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + .taskId("different-task-id") + .contextId("session-xyz") + .artifact(artifact) + .build(); + + assertThrows(A2AServerException.class, () -> { + taskManagerWithId.saveTaskEvent(event); + }); + } + + @Test + public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException { + // Test that adding a task with no message, and there is a TaskManager.initialMessage, + // the initialMessage gets used + Message initialMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("initial message"))) + .messageId("initial-msg-id") + .build(); + + TaskManager taskManagerWithInitialMessage = new TaskManager(null, null, taskStore, initialMessage); + + // Use a status update event instead of a Task to trigger createTask + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("new-task-id") + .contextId("some-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .isFinal(false) + .build(); + + Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); + Task retrieved = taskManagerWithInitialMessage.getTask(); + + // Check that the task has the initial message in its history + assertNotNull(retrieved.getHistory()); + assertEquals(1, retrieved.getHistory().size()); + Message historyMessage = retrieved.getHistory().get(0); + assertEquals(initialMessage.getMessageId(), historyMessage.getMessageId()); + assertEquals(initialMessage.getRole(), historyMessage.getRole()); + assertEquals("initial message", ((TextPart) historyMessage.getParts().get(0)).getText()); + } + + @Test + public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerException { + // Test that adding a task with a message does not use the initial message + Message initialMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("initial message"))) + .messageId("initial-msg-id") + .build(); + + TaskManager taskManagerWithInitialMessage = new TaskManager(null, null, taskStore, initialMessage); + + Message taskMessage = new Message.Builder() + .role(Message.Role.AGENT) + .parts(Collections.singletonList(new TextPart("task message"))) + .messageId("task-msg-id") + .build(); + + // Use TaskStatusUpdateEvent to trigger the creation of a task, which will check if the initialMessage is used. + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("new-task-id") + .contextId("some-context") + .status(new TaskStatus(TaskState.SUBMITTED, taskMessage, null)) + .isFinal(false) + .build(); + + Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); + Task retrieved = taskManagerWithInitialMessage.getTask(); + + // There should now be a history containing the initialMessage + // But the current message (taskMessage) should be in the state, not in the history + assertNotNull(retrieved.getHistory()); + assertEquals(1, retrieved.getHistory().size()); + assertEquals("initial message", ((TextPart) retrieved.getHistory().get(0).getParts().get(0)).getText()); + + // The message in the current state should be taskMessage + assertNotNull(retrieved.getStatus().message()); + assertEquals("task message", ((TextPart) retrieved.getStatus().message().getParts().get(0)).getText()); + } + + @Test + public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException { + // Test handling of multiple artifacts with the same artifactId + Task initialTask = minimalTask; + taskStore.save(initialTask); + + // Add first artifact + Artifact artifact1 = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("content 1"))) + .build(); + TaskArtifactUpdateEvent event1 = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(artifact1) + .build(); + taskManager.saveTaskEvent(event1); + + // Add second artifact with same artifactId (should replace the first) + Artifact artifact2 = new Artifact.Builder() + .artifactId("artifact-id") + .name("artifact-2") + .parts(Collections.singletonList(new TextPart("content 2"))) + .build(); + TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(artifact2) + .build(); + taskManager.saveTaskEvent(event2); + + Task updatedTask = taskManager.getTask(); + assertEquals(1, updatedTask.getArtifacts().size()); + Artifact finalArtifact = updatedTask.getArtifacts().get(0); + assertEquals("artifact-id", finalArtifact.artifactId()); + assertEquals("artifact-2", finalArtifact.name()); + assertEquals("content 2", ((TextPart) finalArtifact.parts().get(0)).getText()); + } + + @Test + public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerException { + // Test handling of multiple artifacts with different artifactIds + Task initialTask = minimalTask; + taskStore.save(initialTask); + + // Add first artifact + Artifact artifact1 = new Artifact.Builder() + .artifactId("artifact-id-1") + .name("artifact-1") + .parts(Collections.singletonList(new TextPart("content 1"))) + .build(); + TaskArtifactUpdateEvent event1 = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(artifact1) + .build(); + taskManager.saveTaskEvent(event1); + + // Add second artifact with different artifactId (should be added) + Artifact artifact2 = new Artifact.Builder() + .artifactId("artifact-id-2") + .name("artifact-2") + .parts(Collections.singletonList(new TextPart("content 2"))) + .build(); + TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .artifact(artifact2) + .build(); + taskManager.saveTaskEvent(event2); + + Task updatedTask = taskManager.getTask(); + assertEquals(2, updatedTask.getArtifacts().size()); + + // Verify both artifacts are present + List artifacts = updatedTask.getArtifacts(); + assertTrue(artifacts.stream() + .anyMatch(a -> "artifact-id-1".equals(a.artifactId()) + && "content 1".equals(((TextPart) a.parts().get(0)).getText())) + , "Artifact 1 should be present"); + assertTrue(artifacts.stream() + .anyMatch(a -> "artifact-id-2".equals(a.artifactId()) + && "content 2".equals(((TextPart) a.parts().get(0)).getText())) + , "Artifact 2 should be present"); + } } From 09cca842c758575cd9e3ef79d6f071f3e8451f94 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 2 Sep 2025 16:26:30 +0200 Subject: [PATCH 103/493] fix: JSONRPCHandler's republisher should happen in a separate thread. (#247) FIxes https://github.com/a2aproject/a2a-java/issues/224 Also, add a test for JSONRPCHandler. GrpcHandler already uses a separate thread, but add a similar test there. --------- Co-authored-by: chenkangqiang --- .../grpc/handler/GrpcHandlerTest.java | 64 +++++++++ .../jsonrpc/handler/JSONRPCHandler.java | 57 ++++---- .../jsonrpc/handler/JSONRPCHandlerTest.java | 124 ++++++++++++++++-- 3 files changed, 206 insertions(+), 39 deletions(-) diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index e5aba9097..8f7edea23 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import com.google.protobuf.Empty; import com.google.protobuf.Struct; @@ -744,6 +745,69 @@ public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { // TODO - getting the authenticated extended agent card isn't support for gRPC right now } + @Test + public void testStreamingDoesNotBlockMainThread() throws Exception { + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + + // Track if the main thread gets blocked during streaming + AtomicBoolean eventReceived = new AtomicBoolean(false); + CountDownLatch streamStarted = new CountDownLatch(1); + GrpcHandler.setStreamingSubscribedRunnable(streamStarted::countDown); + CountDownLatch eventProcessed = new CountDownLatch(1); + + agentExecutorExecute = (context, eventQueue) -> { + // Wait a bit to ensure the main thread continues + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + eventQueue.enqueueEvent(context.getMessage()); + }; + + // Start streaming with a custom StreamObserver + List results = new ArrayList<>(); + List errors = new ArrayList<>(); + StreamObserver streamObserver = new StreamObserver<>() { + @Override + public void onNext(StreamResponse streamResponse) { + results.add(streamResponse); + eventReceived.set(true); + eventProcessed.countDown(); + } + + @Override + public void onError(Throwable throwable) { + errors.add(throwable); + eventProcessed.countDown(); + } + + @Override + public void onCompleted() { + eventProcessed.countDown(); + } + }; + + sendStreamingMessageRequest(handler, streamObserver); + + // The main thread should not be blocked - we should be able to continue immediately + Assertions.assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), + "Streaming subscription should start quickly without blocking main thread"); + + // This proves the main thread is not blocked - we can do other work + // Simulate main thread doing other work + Thread.sleep(50); + + // Wait for the actual event processing to complete + Assertions.assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), + "Event should be processed within reasonable time"); + + // Verify we received the event and no errors occurred + Assertions.assertTrue(eventReceived.get(), "Should have received streaming event"); + Assertions.assertTrue(errors.isEmpty(), "Should not have any errors"); + Assertions.assertEquals(1, results.size(), "Should have received exactly one event"); + } + private StreamRecorder sendMessageRequest(GrpcHandler handler) throws Exception { SendMessageRequest request = SendMessageRequest.newBuilder() .setRequest(GRPC_MESSAGE) diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index e75f6f2d3..4f06348d9 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -6,6 +6,7 @@ import jakarta.inject.Inject; import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import io.a2a.server.ExtendedAgentCard; @@ -242,37 +243,39 @@ private Flow.Publisher convertToSendStreamingMessa // We can't use the normal convertingProcessor since that propagates any errors as an error handled // via Subscriber.onError() rather than as part of the SendStreamingResponse payload return ZeroPublisher.create(createTubeConfig(), tube -> { - publisher.subscribe(new Flow.Subscriber() { - Flow.Subscription subscription; - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - subscription.request(1); - } + CompletableFuture.runAsync(() -> { + publisher.subscribe(new Flow.Subscriber() { + Flow.Subscription subscription; + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } - @Override - public void onNext(StreamingEventKind item) { - tube.send(new SendStreamingMessageResponse(requestId, item)); - subscription.request(1); - } + @Override + public void onNext(StreamingEventKind item) { + tube.send(new SendStreamingMessageResponse(requestId, item)); + subscription.request(1); + } - @Override - public void onError(Throwable throwable) { - if (throwable instanceof JSONRPCError jsonrpcError) { - tube.send(new SendStreamingMessageResponse(requestId, jsonrpcError)); - } else { - tube.send( - new SendStreamingMessageResponse( - requestId, new - InternalError(throwable.getMessage()))); + @Override + public void onError(Throwable throwable) { + if (throwable instanceof JSONRPCError jsonrpcError) { + tube.send(new SendStreamingMessageResponse(requestId, jsonrpcError)); + } else { + tube.send( + new SendStreamingMessageResponse( + requestId, new + InternalError(throwable.getMessage()))); + } + onComplete(); } - onComplete(); - } - @Override - public void onComplete() { - tube.complete(); - } + @Override + public void onComplete() { + tube.complete(); + } + }); }); }); } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 0754cd055..19137ab01 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -8,10 +8,12 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.Flow; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import io.a2a.server.ServerCallContext; @@ -65,7 +67,6 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import mutiny.zero.ZeroPublisher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; @@ -277,7 +278,7 @@ public void testOnMessageErrorMocks() { } @Test - public void testOnMessageStreamNewMessageSuccess() { + public void testOnMessageStreamNewMessageSuccess() throws InterruptedException { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -322,6 +323,8 @@ public void onComplete() { } }); + latch.await(); + // The Python implementation has several events emitted since it uses mocks. Also, in the // implementation, a Message is considered a 'final' Event in EventConsumer.consumeAll() // so there would be no more Events. @@ -367,6 +370,7 @@ public void testOnMessageStreamNewMessageSuccessMocks() { response = handler.onMessageSendStream(request, callContext); } + CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); response.subscribe(new Flow.Subscriber() { @@ -386,16 +390,17 @@ public void onNext(SendStreamingMessageResponse item) { @Override public void onError(Throwable throwable) { - + future.completeExceptionally(throwable); } @Override public void onComplete() { - + future.complete(null); } }); - assertEquals(events, results); + future.join(); + Assertions.assertEquals(events, results); } @Test @@ -512,6 +517,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { response = handler.onMessageSendStream(request, callContext); } + CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); // Unlike testOnMessageStreamNewMessageExistingTaskSuccess() the ZeroPublisher.fromIterable() @@ -534,16 +540,18 @@ public void onNext(SendStreamingMessageResponse item) { @Override public void onError(Throwable throwable) { - + future.completeExceptionally(throwable); } @Override public void onComplete() { - + future.complete(null); } }); - assertEquals(events, results); + future.join(); + + Assertions.assertEquals(events, results); } @@ -721,7 +729,7 @@ public void testOnResubscribeExistingTaskSuccess() { callContext); assertNull(smr.getError()); - + CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); response.subscribe(new Flow.Subscriber<>() { @@ -742,14 +750,18 @@ public void onNext(SendStreamingMessageResponse item) { @Override public void onError(Throwable throwable) { subscription.cancel(); + future.completeExceptionally(throwable); } @Override public void onComplete() { subscription.cancel(); + future.complete(null); } }); + future.join(); + // The Python implementation has several events emitted since it uses mocks. // // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation @@ -787,6 +799,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { response = handler.onResubscribeToTask(request, callContext); } + CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); // Unlike testOnResubscribeExistingTaskSuccess() the ZeroPublisher.fromIterable() @@ -810,14 +823,18 @@ public void onNext(SendStreamingMessageResponse item) { @Override public void onError(Throwable throwable) { subscription.cancel(); + future.completeExceptionally(throwable); } @Override public void onComplete() { subscription.cancel(); + future.complete(null); } }); + future.join(); + // The Python implementation has several events emitted since it uses mocks. // // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation @@ -1151,6 +1168,7 @@ public void testOnMessageStreamTaskIdMismatch() { SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); Flow.Publisher response = handler.onMessageSendStream(request, callContext); + CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -1172,17 +1190,21 @@ public void onNext(SendStreamingMessageResponse item) { public void onError(Throwable throwable) { error.set(throwable); subscription.cancel(); + future.completeExceptionally(throwable); } @Override public void onComplete() { subscription.cancel(); + future.complete(null); } }); - assertNull(error.get()); - assertEquals(1, results.size()); - assertInstanceOf(InternalError.class, results.get(0).getError()); + future.join(); + + Assertions.assertNull(error.get()); + Assertions.assertEquals(1, results.size()); + Assertions.assertInstanceOf(InternalError.class, results.get(0).getError()); } @Test @@ -1370,4 +1392,82 @@ public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { assertNull(response.getResult()); } + @Test + public void testStreamingDoesNotBlockMainThread() throws Exception { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + + // Track if the main thread gets blocked during streaming + AtomicBoolean mainThreadBlocked = new AtomicBoolean(true); + AtomicBoolean eventReceived = new AtomicBoolean(false); + CountDownLatch streamStarted = new CountDownLatch(1); + CountDownLatch eventProcessed = new CountDownLatch(1); + + agentExecutorExecute = (context, eventQueue) -> { + // Wait a bit to ensure the main thread continues + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + eventQueue.enqueueEvent(context.getMessage()); + }; + + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(message, null, null)); + + // Start streaming + Flow.Publisher response = handler.onMessageSendStream(request, callContext); + + response.subscribe(new Flow.Subscriber() { + private Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + streamStarted.countDown(); + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(SendStreamingMessageResponse item) { + eventReceived.set(true); + eventProcessed.countDown(); + subscription.cancel(); + } + + @Override + public void onError(Throwable throwable) { + subscription.cancel(); + eventProcessed.countDown(); + } + + @Override + public void onComplete() { + subscription.cancel(); + eventProcessed.countDown(); + } + }); + + // The main thread should not be blocked - we should be able to continue immediately + Assertions.assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), + "Streaming subscription should start quickly without blocking main thread"); + + // This proves the main thread is not blocked - we can do other work + // Simulate main thread doing other work + Thread.sleep(50); + + mainThreadBlocked.set(false); // If we get here, main thread was not blocked + + // Wait for the actual event processing to complete + Assertions.assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), + "Event should be processed within reasonable time"); + + // Verify we received the event and main thread was not blocked + Assertions.assertTrue(eventReceived.get(), "Should have received streaming event"); + Assertions.assertFalse(mainThreadBlocked.get(), "Main thread should not have been blocked"); + } + } From 1ff07d077806f3a94e0d8acd23b9da3d6fa36d50 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 2 Sep 2025 10:38:35 -0400 Subject: [PATCH 104/493] fix: Update JSONRPCTransportConfig option in README (#249) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e3a667ac8..0f0d7b48e 100644 --- a/README.md +++ b/README.md @@ -316,9 +316,9 @@ Different transport protocols can be configured with specific settings using spe ##### JSON-RPC Transport Configuration -For the JSON-RPC transport, if you'd like to use the default `JdkA2AHttpClient`, no additional -configuration is needed. To use a custom HTTP client implementation, simply create a `JSONRPCTransportConfig` -as follows: +For the JSON-RPC transport, to use the default `JdkA2AHttpClient`, provide a `JSONRPCTransportConfig` created with its default constructor. + +To use a custom HTTP client implementation, simply create a `JSONRPCTransportConfig` as follows: ```java // Create a custom HTTP client From 4838d2c246455e34a23857285ca2fd691992b82f Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 2 Sep 2025 13:49:10 -0400 Subject: [PATCH 105/493] chore: Next is 0.3.0.Alpha1 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 6df47c1b7..71d88c4dc 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index b910d6ac7..801bfd3c8 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 172c243a3..52daba994 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index 99b183289..3b90cefa6 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 504856d3b..d288bc5ef 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 3aaa5c221..c349d44f6 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index f7831133f..08fd68f30 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Alpha1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Alpha1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 4cb7c0c3e..718c85dad 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index b35ffa8a9..6d5578846 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/http-client/pom.xml b/http-client/pom.xml index fca8bc4fe..c8ba6c5ef 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index d1677324e..bbaa66243 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 8b2af244f..d296a8eac 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 6a4ec4618..3d168979a 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index a4342f96c..b320294c4 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/server-common/pom.xml b/server-common/pom.xml index 1bcc2b7b9..d8040f799 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index d72663392..062b00d16 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index 13a880aa0..c21df8eb5 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index e03ef70da..ddd586e57 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index fb6cbc5db..a8cdfe2b2 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 93ec2a095..30f5c4665 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 15eecd06e..b2a63a90d 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc From d2826e1e004d6908cfcf1877b5297a64dd159ecd Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 2 Sep 2025 13:49:44 -0400 Subject: [PATCH 106/493] chore: release 0.3.0.Alpha1 Release-As: 0.3.0.Alpha1 From 22c54dd9a7829b78041f641e8b61d64b38d79503 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 2 Sep 2025 14:12:27 -0400 Subject: [PATCH 107/493] chore: Release 0.3.0.Alpha1 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 71d88c4dc..9b4635353 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 801bfd3c8..887e8e8cb 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 52daba994..697a84f1d 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index 3b90cefa6..b05a984b1 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index d288bc5ef..a32f90cb9 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index c349d44f6..05290206b 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 08fd68f30..f37fafac6 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Alpha1-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Alpha1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Alpha1 +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Alpha1 //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 718c85dad..06b56c5f5 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 6d5578846..705093a62 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-examples-server diff --git a/http-client/pom.xml b/http-client/pom.xml index c8ba6c5ef..05cd59715 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index bbaa66243..cc57be886 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index d296a8eac..58a65dd6a 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 3d168979a..ad9935e2b 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index b320294c4..5eff434fc 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/server-common/pom.xml b/server-common/pom.xml index d8040f799..6d7ad43d4 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 062b00d16..c8bbefbae 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index c21df8eb5..cdfd9fa86 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index ddd586e57..bff09f827 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index a8cdfe2b2..4015e28bc 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 30f5c4665..c5e5e9da5 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index b2a63a90d..ad72c8004 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1-SNAPSHOT + 0.3.0.Alpha1 ../../pom.xml a2a-java-sdk-transport-jsonrpc From d6a4d97f719357855a0d0c7aca5419f42e64d431 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 3 Sep 2025 08:11:33 -0400 Subject: [PATCH 108/493] chore: Next is 0.3.0.Beta1 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 9b4635353..6df47c1b7 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 887e8e8cb..b910d6ac7 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 697a84f1d..172c243a3 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index b05a984b1..99b183289 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index a32f90cb9..504856d3b 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 05290206b..3aaa5c221 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index f37fafac6..f7831133f 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Alpha1 -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Alpha1 +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 06b56c5f5..4cb7c0c3e 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 705093a62..b35ffa8a9 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/http-client/pom.xml b/http-client/pom.xml index 05cd59715..fca8bc4fe 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index cc57be886..d1677324e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 58a65dd6a..8b2af244f 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index ad9935e2b..6a4ec4618 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 5eff434fc..a4342f96c 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/server-common/pom.xml b/server-common/pom.xml index 6d7ad43d4..1bcc2b7b9 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index c8bbefbae..d72663392 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index cdfd9fa86..13a880aa0 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index bff09f827..e03ef70da 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 4015e28bc..fb6cbc5db 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index c5e5e9da5..93ec2a095 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index ad72c8004..15eecd06e 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Alpha1 + 0.3.0.Beta1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc From 6ab368019b63ad3115900c2d3a6ee0d70ca7b0c5 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 4 Sep 2025 18:17:11 +0200 Subject: [PATCH 109/493] feat: Use main HTTP port for gRPC (#252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #250 🦕 --- .github/workflows/run-tck.yml | 2 +- .../java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java | 4 +++- reference/grpc/src/test/resources/application.properties | 4 ++-- tck/src/main/resources/application.properties | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 9d1c0d3fa..82c594bf7 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -55,7 +55,7 @@ jobs: - name: Build with Maven, skipping tests run: mvn -B install -DskipTests - name: Start SUT - run: SUT_GRPC_URL=http://localhost:9000 mvn -B quarkus:dev & #SUT_JSONRPC_URL already set + run: SUT_GRPC_URL=${{ env.SUT_JSONRPC_URL }} mvn -B quarkus:dev & #SUT_JSONRPC_URL already set working-directory: tck - name: Wait for SUT to start run: | diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java index 885dad731..b66f15b56 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -29,7 +29,9 @@ protected String getTransportProtocol() { @Override protected String getTransportUrl() { - return "localhost:9001"; // gRPC server runs on port 9001 + // gRPC server runs on port 8081, which is the same port as the main web server. + // This is from the setting on the + return "localhost:8081"; } @Override diff --git a/reference/grpc/src/test/resources/application.properties b/reference/grpc/src/test/resources/application.properties index 9ddcb3e07..e9a00d696 100644 --- a/reference/grpc/src/test/resources/application.properties +++ b/reference/grpc/src/test/resources/application.properties @@ -1,3 +1,3 @@ -# Configure the gRPC server to listen on port 9001 -quarkus.grpc.server.port=9001 +# Use the new gRPC implementation which uses the main HTTP port +quarkus.grpc.server.use-separate-server=false quarkus.http.port=8081 \ No newline at end of file diff --git a/tck/src/main/resources/application.properties b/tck/src/main/resources/application.properties index e850dd2f2..63b5095da 100644 --- a/tck/src/main/resources/application.properties +++ b/tck/src/main/resources/application.properties @@ -1 +1,3 @@ +# Use the new gRPC implementation which uses the main HTTP port +quarkus.grpc.server.use-separate-server=false %dev.quarkus.http.port=9999 From 8e70e489a6cf8bc4f65cacd60242d7908eea91cc Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 4 Sep 2025 18:21:04 +0200 Subject: [PATCH 110/493] feat: Check transports in AgentCard match those on classpath (#251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #246 🦕 --- .../helloworld/AgentCardProducer.java | 2 + .../grpc/quarkus/QuarkusGrpcHandler.java | 4 +- .../quarkus/QuarkusGrpcTransportMetadata.java | 11 ++ .../services/io.a2a.server.TransportMetadata | 1 + .../server/apps/quarkus/A2AServerRoutes.java | 7 +- .../QuarkusJSONRPCTransportMetadata.java | 12 ++ .../services/io.a2a.server.TransportMetadata | 1 + .../io/a2a/server/AgentCardValidator.java | 127 +++++++++++++++ .../java/io/a2a/server/TransportMetadata.java | 27 ++++ .../io/a2a/server/AgentCardValidatorTest.java | 149 ++++++++++++++++++ .../AbstractA2ARequestHandlerTest.java | 35 +++- .../server/apps/common/AgentCardProducer.java | 40 ++++- .../transport/grpc/handler/GrpcHandler.java | 27 +++- .../handler/GrpcTestTransportMetadata.java | 12 ++ .../services/io.a2a.server.TransportMetadata | 2 + .../a2a-requesthandler-test.properties | 1 + .../jsonrpc/handler/JSONRPCHandler.java | 8 +- .../handler/JSONRPCTestTransportMetadata.java | 12 ++ .../services/io.a2a.server.TransportMetadata | 2 + .../a2a-requesthandler-test.properties | 1 + 20 files changed, 455 insertions(+), 26 deletions(-) create mode 100644 reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcTransportMetadata.java create mode 100644 reference/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata create mode 100644 reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/QuarkusJSONRPCTransportMetadata.java create mode 100644 reference/jsonrpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata create mode 100644 server-common/src/main/java/io/a2a/server/AgentCardValidator.java create mode 100644 server-common/src/main/java/io/a2a/server/TransportMetadata.java create mode 100644 server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java create mode 100644 transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java create mode 100644 transport/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata create mode 100644 transport/grpc/src/main/resources/a2a-requesthandler-test.properties create mode 100644 transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java create mode 100644 transport/jsonrpc/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata create mode 100644 transport/jsonrpc/src/test/resources/a2a-requesthandler-test.properties diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 5d7b0c999..9e34aef0a 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -17,6 +17,8 @@ public class AgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { + // NOTE: Transport validation will automatically check that transports specified + // in this AgentCard match those available on the classpath when handlers are initialized return new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 5b30d1b15..9cfba609a 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -3,11 +3,11 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import io.a2a.transport.grpc.handler.CallContextFactory; -import io.a2a.transport.grpc.handler.GrpcHandler; import io.a2a.server.PublicAgentCard; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; +import io.a2a.transport.grpc.handler.CallContextFactory; +import io.a2a.transport.grpc.handler.GrpcHandler; import io.quarkus.grpc.GrpcService; @GrpcService diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcTransportMetadata.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcTransportMetadata.java new file mode 100644 index 000000000..1a5a7a078 --- /dev/null +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcTransportMetadata.java @@ -0,0 +1,11 @@ +package io.a2a.server.grpc.quarkus; + +import io.a2a.server.TransportMetadata; +import io.a2a.spec.TransportProtocol; + +public class QuarkusGrpcTransportMetadata implements TransportMetadata { + @Override + public String getTransportProtocol() { + return TransportProtocol.GRPC.asString(); + } +} diff --git a/reference/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata b/reference/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata new file mode 100644 index 000000000..3eb991c3c --- /dev/null +++ b/reference/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata @@ -0,0 +1 @@ +io.a2a.server.grpc.quarkus.QuarkusGrpcTransportMetadata \ No newline at end of file diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 900ce15ca..29791d653 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -14,14 +14,11 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import jakarta.inject.Singleton; -import jakarta.ws.rs.core.Response; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; -import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; -import io.a2a.server.ExtendedAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; @@ -37,7 +34,6 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidParamsJsonMappingException; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONErrorResponse; import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.JSONRPCErrorResponse; @@ -53,11 +49,11 @@ import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import io.a2a.util.Utils; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; -import io.quarkus.vertx.web.RoutingExchange; import io.smallrye.mutiny.Multi; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; @@ -344,6 +340,5 @@ private static void endOfStream(HttpServerResponse response) { response.end(); } } - } diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/QuarkusJSONRPCTransportMetadata.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/QuarkusJSONRPCTransportMetadata.java new file mode 100644 index 000000000..5cff0d7f7 --- /dev/null +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/QuarkusJSONRPCTransportMetadata.java @@ -0,0 +1,12 @@ +package io.a2a.server.apps.quarkus; + +import io.a2a.server.TransportMetadata; +import io.a2a.spec.TransportProtocol; + +public class QuarkusJSONRPCTransportMetadata implements TransportMetadata { + + @Override + public String getTransportProtocol() { + return TransportProtocol.JSONRPC.asString(); + } +} diff --git a/reference/jsonrpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata b/reference/jsonrpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata new file mode 100644 index 000000000..d9a5494b4 --- /dev/null +++ b/reference/jsonrpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata @@ -0,0 +1 @@ +io.a2a.server.apps.quarkus.QuarkusJSONRPCTransportMetadata \ No newline at end of file diff --git a/server-common/src/main/java/io/a2a/server/AgentCardValidator.java b/server-common/src/main/java/io/a2a/server/AgentCardValidator.java new file mode 100644 index 000000000..d3c80b35b --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/AgentCardValidator.java @@ -0,0 +1,127 @@ +package io.a2a.server; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; + +/** + * Validates AgentCard transport configuration against available transport endpoints. + */ +public class AgentCardValidator { + + private static final Logger LOGGER = Logger.getLogger(AgentCardValidator.class.getName()); + + /** + * Validates the transport configuration of an AgentCard against available transports found on the classpath. + * Logs warnings for missing transports and errors for unsupported transports. + * + * @param agentCard the agent card to validate + */ + public static void validateTransportConfiguration(AgentCard agentCard) { + validateTransportConfiguration(agentCard, getAvailableTransports()); + } + + /** + * Validates the transport configuration of an AgentCard against a given set of available transports. + * This method is package-private for testability. + * + * @param agentCard the agent card to validate + * @param availableTransports the set of available transport protocols + */ + static void validateTransportConfiguration(AgentCard agentCard, Set availableTransports) { + Set agentCardTransports = getAgentCardTransports(agentCard); + + // Check for missing transports (warn if AgentCard doesn't include all available transports) + Set missingTransports = availableTransports.stream() + .filter(transport -> !agentCardTransports.contains(transport)) + .collect(Collectors.toSet()); + + if (!missingTransports.isEmpty()) { + LOGGER.warning(String.format( + "AgentCard does not include all available transports. Missing: %s. " + + "Available transports: %s. AgentCard transports: %s", + formatTransports(missingTransports), + formatTransports(availableTransports), + formatTransports(agentCardTransports) + )); + } + + // Check for unsupported transports (error if AgentCard specifies unavailable transports) + Set unsupportedTransports = agentCardTransports.stream() + .filter(transport -> !availableTransports.contains(transport)) + .collect(Collectors.toSet()); + + if (!unsupportedTransports.isEmpty()) { + String errorMessage = String.format( + "AgentCard specifies transport interfaces for unavailable transports: %s. " + + "Available transports: %s. Consider removing these interfaces or adding the required transport dependencies.", + formatTransports(unsupportedTransports), + formatTransports(availableTransports) + ); + LOGGER.severe(errorMessage); + + // Following the GitHub issue suggestion to use an error instead of warning + throw new IllegalStateException(errorMessage); + } + } + + /** + * Extracts all transport protocols specified in the AgentCard. + * Includes both the preferred transport and additional interface transports. + * + * @param agentCard the agent card to analyze + * @return set of transport protocols specified in the agent card + */ + private static Set getAgentCardTransports(AgentCard agentCard) { + List transportStrings = new ArrayList<>(); + + // Add preferred transport + if (agentCard.preferredTransport() != null) { + transportStrings.add(agentCard.preferredTransport()); + } + + // Add additional interface transports + if (agentCard.additionalInterfaces() != null) { + for (AgentInterface agentInterface : agentCard.additionalInterfaces()) { + if (agentInterface.transport() != null) { + transportStrings.add(agentInterface.transport()); + } + } + } + + return new HashSet<>(transportStrings); + } + + /** + * Formats a set of transport protocols for logging. + * + * @param transports the transport protocols to format + * @return formatted string representation + */ + private static String formatTransports(Set transports) { + return transports.stream() + .collect(Collectors.joining(", ", "[", "]")); + } + + /** + * Discovers available transport endpoints using ServiceLoader. + * This searches the classpath for implementations of TransportMetadata. + * + * @return set of available transport protocols + */ + private static Set getAvailableTransports() { + return ServiceLoader.load(TransportMetadata.class) + .stream() + .map(ServiceLoader.Provider::get) + .filter(TransportMetadata::isAvailable) + .map(TransportMetadata::getTransportProtocol) + .collect(Collectors.toSet()); + } +} diff --git a/server-common/src/main/java/io/a2a/server/TransportMetadata.java b/server-common/src/main/java/io/a2a/server/TransportMetadata.java new file mode 100644 index 000000000..8fd2dbc1e --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/TransportMetadata.java @@ -0,0 +1,27 @@ +package io.a2a.server; + +import io.a2a.spec.TransportProtocol; + +/** + * Interface for transport endpoint implementations to provide metadata about their transport. + * This is used by the validation system to discover available transports on the classpath. + */ +public interface TransportMetadata { + + /** + * Returns the transport protocol this endpoint supports. + * + * @return the transport protocol + */ + String getTransportProtocol(); + + /** + * Checks if this transport endpoint is currently available/functional. + * This can be used for runtime availability checks beyond just classpath presence. + * + * @return true if the transport is available, false otherwise + */ + default boolean isAvailable() { + return true; + } +} \ No newline at end of file diff --git a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java new file mode 100644 index 000000000..7b54eeb6c --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java @@ -0,0 +1,149 @@ +package io.a2a.server; + +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AgentCardValidatorTest { + + @Test + void testValidationWithSimpleAgentCard() { + // Create a simple AgentCard (uses default JSONRPC transport) + AgentCard agentCard = new AgentCard.Builder() + .name("Test Agent") + .description("Test Description") + .url("http://localhost:9999") + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.emptyList()) + .build(); + + // Define available transports + Set availableTransports = Set.of(TransportProtocol.JSONRPC.asString()); + + // Validation should now pass + assertDoesNotThrow(() -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + } + + @Test + void testValidationWithMultipleTransports() { + // Create AgentCard that specifies multiple transports + AgentCard agentCard = new AgentCard.Builder() + .name("Test Agent") + .description("Test Description") + .url("http://localhost:9999") + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.emptyList()) + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"), + new AgentInterface(TransportProtocol.GRPC.asString(), "http://localhost:9000") + )) + .build(); + + // Define available transports + Set availableTransports = + Set.of(TransportProtocol.JSONRPC.asString(), TransportProtocol.GRPC.asString()); + + // Validation should now pass + assertDoesNotThrow(() -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + } + + @Test + void testLogWarningWhenExtraTransportsFound() { + // Create an AgentCard with only JSONRPC + AgentCard agentCard = new AgentCard.Builder() + .name("Test Agent") + .description("Test Description") + .url("http://localhost:9999") + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.emptyList()) + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .build(); + + // Define available transports (more than in AgentCard) + Set availableTransports = + Set.of(TransportProtocol.JSONRPC.asString(), TransportProtocol.GRPC.asString()); + + // Capture logs + Logger logger = Logger.getLogger(AgentCardValidator.class.getName()); + TestLogHandler testLogHandler = new TestLogHandler(); + logger.addHandler(testLogHandler); + + try { + AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports); + } finally { + logger.removeHandler(testLogHandler); + } + + // Assert that a warning was logged + assertTrue(testLogHandler.getLogMessages().stream() + .anyMatch(msg -> msg.contains("AgentCard does not include all available transports. Missing: [GRPC]"))); + } + + @Test + void testValidationWithUnavailableTransport() { + // Create a simple AgentCard (uses default JSONRPC transport) + AgentCard agentCard = new AgentCard.Builder() + .name("Test Agent") + .description("Test Description") + .url("http://localhost:9999") + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.emptyList()) + .build(); + + // Define available transports (empty) + Set availableTransports = Collections.emptySet(); + + // Should throw exception because no transports are available + IllegalStateException exception = assertThrows(IllegalStateException.class, + () -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + assertTrue(exception.getMessage().contains("unavailable transports: [JSONRPC]")); + } + + // A simple log handler for testing + private static class TestLogHandler extends Handler { + private final List logMessages = new java.util.ArrayList<>(); + + @Override + public void publish(LogRecord record) { + logMessages.add(record.getMessage()); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + + public List getLogMessages() { + return logMessages; + } + } +} diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 1e3a0af5f..acef022c3 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -1,17 +1,20 @@ package io.a2a.server.requesthandlers; -import jakarta.enterprise.context.Dependent; - import java.io.IOException; +import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.function.Consumer; +import jakarta.enterprise.context.Dependent; + import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.server.agentexecution.AgentExecutor; @@ -36,6 +39,7 @@ import io.quarkus.arc.profile.IfBuildProfile; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; public class AbstractA2ARequestHandlerTest { @@ -53,6 +57,8 @@ public class AbstractA2ARequestHandlerTest { .role(Message.Role.AGENT) .parts(new TextPart("test message")) .build(); + private static final String PREFERRED_TRANSPORT = "preferred-transport"; + private static final String A2A_REQUESTHANDLER_TEST_PROPERTIES = "/a2a-requesthandler-test.properties"; protected AgentExecutor executor; protected TaskStore taskStore; @@ -98,7 +104,8 @@ public void cleanup() { } protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory) { - return new AgentCard.Builder() + String preferredTransport = loadPreferredTransportFromProperties(); + AgentCard.Builder builder = new AgentCard.Builder() .name("test-card") .description("A test agent card") .url("http://example.com") @@ -111,9 +118,27 @@ protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifi .build()) .defaultInputModes(new ArrayList<>()) .defaultOutputModes(new ArrayList<>()) + .preferredTransport(preferredTransport) .skills(new ArrayList<>()) - .protocolVersion("0.2.5") - .build(); + .protocolVersion("0.2.5"); + return builder.build(); + } + + private static String loadPreferredTransportFromProperties() { + URL url = AbstractA2ARequestHandlerTest.class.getResource(A2A_REQUESTHANDLER_TEST_PROPERTIES); + Assertions.assertNotNull(url); + Properties properties = new Properties(); + try { + try (InputStream in = url.openStream()){ + properties.load(in); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + String preferredTransport = properties.getProperty(PREFERRED_TRANSPORT); + Assertions.assertNotNull(preferredTransport); + return preferredTransport; } protected interface AgentExecutorMethod { diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index 7a50b7697..31516c4dc 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -1,7 +1,11 @@ package io.a2a.server.apps.common; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; +import java.util.Properties; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; @@ -10,16 +14,21 @@ import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.quarkus.arc.profile.IfBuildProfile; +import org.junit.jupiter.api.Assertions; @ApplicationScoped @IfBuildProfile("test") public class AgentCardProducer { + private static final String PREFERRED_TRANSPORT = "preferred-transport"; + private static final String A2A_REQUESTHANDLER_TEST_PROPERTIES = "/a2a-requesthandler-test.properties"; + @Produces @PublicAgentCard public AgentCard agentCard() { String port = System.getProperty("test.agent.card.port", "8081"); - return new AgentCard.Builder() + + AgentCard.Builder builder = new AgentCard.Builder() .name("test-card") .description("A test agent card") .url("http://localhost:" + port) @@ -33,8 +42,33 @@ public AgentCard agentCard() { .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(new ArrayList<>()) - .protocolVersion("0.2.5") - .build(); + .protocolVersion("0.2.5"); + + String preferredTransport = loadPreferredTransportFromProperties(); + if (preferredTransport != null) { + builder.preferredTransport(preferredTransport); + } + + return builder.build(); + } + + private static String loadPreferredTransportFromProperties() { + URL url = AgentCardProducer.class.getResource(A2A_REQUESTHANDLER_TEST_PROPERTIES); + if (url == null) { + return null; + } + Properties properties = new Properties(); + try { + try (InputStream in = url.openStream()){ + properties.load(in); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + String preferredTransport = properties.getProperty(PREFERRED_TRANSPORT); + Assertions.assertNotNull(preferredTransport); + return preferredTransport; } } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index b32ccc23f..00c05c4b6 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -10,10 +10,12 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; import com.google.protobuf.Empty; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.StreamResponse; +import io.a2a.server.AgentCardValidator; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; @@ -51,8 +53,10 @@ public abstract class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { // Without this we get intermittent failures private static volatile Runnable streamingSubscribedRunnable; + private AtomicBoolean initialised = new AtomicBoolean(false); public GrpcHandler() { + } @Override @@ -115,7 +119,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, @Override public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (!getAgentCard().capabilities().pushNotifications()) { + if (!getAgentCardInternal().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -136,7 +140,7 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat @Override public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (!getAgentCard().capabilities().pushNotifications()) { + if (!getAgentCardInternal().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -157,7 +161,7 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon @Override public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (!getAgentCard().capabilities().pushNotifications()) { + if (!getAgentCardInternal().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -183,7 +187,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC @Override public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, StreamObserver responseObserver) { - if (!getAgentCard().capabilities().streaming()) { + if (!getAgentCardInternal().capabilities().streaming()) { handleError(responseObserver, new InvalidRequestError()); return; } @@ -203,7 +207,7 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, @Override public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, StreamObserver responseObserver) { - if (!getAgentCard().capabilities().streaming()) { + if (!getAgentCardInternal().capabilities().streaming()) { handleError(responseObserver, new InvalidRequestError()); return; } @@ -271,7 +275,7 @@ public void onComplete() { public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, StreamObserver responseObserver) { try { - responseObserver.onNext(ToProto.agentCard(getAgentCard())); + responseObserver.onNext(ToProto.agentCard(getAgentCardInternal())); responseObserver.onCompleted(); } catch (Throwable t) { handleInternalError(responseObserver, t); @@ -281,7 +285,7 @@ public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, @Override public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { - if (!getAgentCard().capabilities().pushNotifications()) { + if (!getAgentCardInternal().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); return; } @@ -373,6 +377,15 @@ private void handleInternalError(StreamObserver responseObserver, Throwab handleError(responseObserver, new InternalError(t.getMessage())); } + private AgentCard getAgentCardInternal() { + AgentCard agentCard = getAgentCard(); + if (initialised.compareAndSet(false, true)) { + // Validate transport configuration + AgentCardValidator.validateTransportConfiguration(agentCard); + } + return agentCard; + } + public static void setStreamingSubscribedRunnable(Runnable runnable) { streamingSubscribedRunnable = runnable; } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java new file mode 100644 index 000000000..5bdf4750d --- /dev/null +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java @@ -0,0 +1,12 @@ +package io.a2a.transport.grpc.handler; + +import io.a2a.server.TransportMetadata; +import io.a2a.spec.TransportProtocol; + +public class GrpcTestTransportMetadata implements TransportMetadata { + @Override + public String getTransportProtocol() { + return TransportProtocol.GRPC.asString(); + } + +} diff --git a/transport/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata b/transport/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata new file mode 100644 index 000000000..8fce961b2 --- /dev/null +++ b/transport/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata @@ -0,0 +1,2 @@ +# Add a test TransportMetadata so we pass AgentCard validation +io.a2a.transport.grpc.handler.GrpcTestTransportMetadata \ No newline at end of file diff --git a/transport/grpc/src/main/resources/a2a-requesthandler-test.properties b/transport/grpc/src/main/resources/a2a-requesthandler-test.properties new file mode 100644 index 000000000..102c971fb --- /dev/null +++ b/transport/grpc/src/main/resources/a2a-requesthandler-test.properties @@ -0,0 +1 @@ +preferred-transport=GRPC \ No newline at end of file diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 4f06348d9..62bb422b4 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -9,6 +9,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; +import io.a2a.server.AgentCardValidator; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.PublicAgentCard; import io.a2a.server.ServerCallContext; @@ -61,12 +62,13 @@ public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard I this.agentCard = agentCard; this.extendedAgentCard = extendedAgentCard; this.requestHandler = requestHandler; + + // Validate transport configuration + AgentCardValidator.validateTransportConfiguration(agentCard); } public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { - this.agentCard = agentCard; - this.extendedAgentCard = null; - this.requestHandler = requestHandler; + this(agentCard, null, requestHandler); } public SendMessageResponse onMessageSend(SendMessageRequest request, ServerCallContext context) { diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java new file mode 100644 index 000000000..c2087cffc --- /dev/null +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java @@ -0,0 +1,12 @@ +package io.a2a.transport.jsonrpc.handler; + +import io.a2a.server.TransportMetadata; +import io.a2a.spec.TransportProtocol; + +public class JSONRPCTestTransportMetadata implements TransportMetadata { + @Override + public String getTransportProtocol() { + return TransportProtocol.JSONRPC.asString(); + } + +} diff --git a/transport/jsonrpc/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata b/transport/jsonrpc/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata new file mode 100644 index 000000000..e4ec9c45f --- /dev/null +++ b/transport/jsonrpc/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata @@ -0,0 +1,2 @@ +# Add a test TransportMetadata so we pass AgentCard validation +io.a2a.transport.jsonrpc.handler.JSONRPCTestTransportMetadata \ No newline at end of file diff --git a/transport/jsonrpc/src/test/resources/a2a-requesthandler-test.properties b/transport/jsonrpc/src/test/resources/a2a-requesthandler-test.properties new file mode 100644 index 000000000..c069ffd5c --- /dev/null +++ b/transport/jsonrpc/src/test/resources/a2a-requesthandler-test.properties @@ -0,0 +1 @@ +preferred-transport=JSONRPC \ No newline at end of file From daeaa31393d6acd7f20f6095f06494ad4166f61e Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 5 Sep 2025 10:40:15 +0100 Subject: [PATCH 111/493] fix: TransportMetaData implementations for tests should be in src/test (#254) --- .../io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java | 0 .../resources/META-INF/services/io.a2a.server.TransportMetadata | 0 .../transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename transport/grpc/src/{main => test}/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java (100%) rename transport/grpc/src/{main => test}/resources/META-INF/services/io.a2a.server.TransportMetadata (100%) rename transport/jsonrpc/src/{main => test}/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java (100%) diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java similarity index 100% rename from transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java rename to transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcTestTransportMetadata.java diff --git a/transport/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata b/transport/grpc/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata similarity index 100% rename from transport/grpc/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata rename to transport/grpc/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java similarity index 100% rename from transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java rename to transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCTestTransportMetadata.java From 2bb1fa107d2c27bbd2c8689d51de1618b48442fc Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 5 Sep 2025 10:58:08 -0400 Subject: [PATCH 112/493] fix: Add blog posts to the community page (#255) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- COMMUNITY_ARTICLES.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/COMMUNITY_ARTICLES.md b/COMMUNITY_ARTICLES.md index 31208d135..b4f392d92 100644 --- a/COMMUNITY_ARTICLES.md +++ b/COMMUNITY_ARTICLES.md @@ -7,7 +7,18 @@ Here are some articles and videos from the community that you may find helpful. * [Agents are not tools](https://www.googlecloudcommunity.com/gc/Community-Blogs/Agents-are-not-tools/ba-p/922716): A nice explanation of what agents are from one of the main contributors of A2A spec. * [Introducing A2A](https://goo.gle/a2a-video): A good video introducing A2A. -## A2A Java SDK Specific +## A2A Java SDK * [Quarkus and WildFly teams from Red Hat collaborating with Google on launch of Agent2Agent Java SDK](https://quarkus.io/blog/a2a-project-launches-java-sdk/): Announcing the new availability of the A2A Java SDK and giving a quick introduction to it. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/quarkus-and-wildfly-teams-from-red-hat-collaborating-with-google-on-launch-of-agent2agent-java-sdk-5f8cc64921cb). + * [A2A Java SDK Introduction Video](https://www.youtube.com/watch?v=5CZzW-wvEQs): A great video introducing the A2A Java SDK and giving a walkthrough of a multi-language interoperability example. + +* [Getting Started with Quarkus and A2A Java SDK](https://quarkus.io/blog/quarkus-and-a2a-java-sdk/): An introduction on how to easily get started with Quarkus and the A2A Java SDK. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/getting-started-with-quarkus-and-the-a2a-java-sdk-c8b1529c8445). + +* [Getting Started with Quarkus and A2A Java SDK 0.3.0](https://quarkus.io/blog/quarkus-a2a-java-0-3-0-alpha-release/): Learn about what's new in the 0.3.0 version of the A2A Java SDK. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/getting-started-with-quarkus-and-a2a-java-sdk-0-3-0-c4e70620a805). + +## A2A Java SDK for Jakarta Servers + +* [Building your First A2A Agent](https://www.wildfly.org/news/2025/08/07/Building-your-First-A2A-Agent/): An introduction on how to use the A2A Java SDK for Jakarta Servers with WildFly. + + From b77cad2f3078eff2ecd9405c9af123f1dbce3d59 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 8 Sep 2025 16:25:09 -0400 Subject: [PATCH 113/493] fix: The default gRPC port for the TCK agent should default to 9999 (#259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 59c055fc8..68e6c7e26 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -17,11 +17,13 @@ @ApplicationScoped public class AgentCardProducer { + private static final String DEFAULT_SUT_URL = "http://localhost:9999"; + @Produces @PublicAgentCard public AgentCard agentCard() { - String sutJsonRpcUrl = getEnvOrDefault("SUT_JSONRPC_URL", "http://localhost:9999"); - String sutGrpcUrl = getEnvOrDefault("SUT_GRPC_URL", "http://localhost:9000"); + String sutJsonRpcUrl = getEnvOrDefault("SUT_JSONRPC_URL", DEFAULT_SUT_URL); + String sutGrpcUrl = getEnvOrDefault("SUT_GRPC_URL", DEFAULT_SUT_URL); return new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") From bb31a8e7d19fdec793714405c5156a9e13a0b4ed Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 8 Sep 2025 16:25:40 -0400 Subject: [PATCH 114/493] fix!: messageId should be required for a Message (#260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../agentexecution/RequestContextTest.java | 28 ++++++++++--------- .../io/a2a/server/tasks/TaskUpdaterTest.java | 2 ++ spec/src/main/java/io/a2a/spec/Message.java | 3 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 1 + 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index 081cc873a..60b80061d 100644 --- a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -23,6 +23,8 @@ public class RequestContextTest { + private static final String MESSAGE_ID = "123"; + @Test public void testInitWithoutParams() { RequestContext context = new RequestContext(null, null, null, null, null, null); @@ -35,7 +37,7 @@ public void testInitWithoutParams() { @Test public void testInitWithParamsNoIds() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); UUID taskId = UUID.fromString("00000000-0000-0000-0000-000000000001"); @@ -59,7 +61,7 @@ public void testInitWithParamsNoIds() { @Test public void testInitWithTaskId() { String taskId = "task-123"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, taskId, null, null, null, null); @@ -71,7 +73,7 @@ public void testInitWithTaskId() { @Test public void testInitWithContextId() { String contextId = "context-456"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, contextId, null, null, null); @@ -83,7 +85,7 @@ public void testInitWithContextId() { public void testInitWithBothIds() { String taskId = "task-123"; String contextId = "context-456"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null, null); @@ -95,7 +97,7 @@ public void testInitWithBothIds() { @Test public void testInitWithTask() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); @@ -130,7 +132,7 @@ public void testAttachRelatedTask() { @Test public void testCheckOrGenerateTaskIdWithExistingTaskId() { String existingId = "existing-task-id"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -143,7 +145,7 @@ public void testCheckOrGenerateTaskIdWithExistingTaskId() { public void testCheckOrGenerateContextIdWithExistingContextId() { String existingId = "existing-context-id"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -154,7 +156,7 @@ public void testCheckOrGenerateContextIdWithExistingContextId() { @Test public void testInitRaisesErrorOnTaskIdMismatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -166,7 +168,7 @@ public void testInitRaisesErrorOnTaskIdMismatch() { @Test public void testInitRaisesErrorOnContextIdMismatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -198,7 +200,7 @@ public void testMessagePropertyWithoutParams() { @Test public void testMessagePropertyWithParams() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -210,7 +212,7 @@ public void testInitWithExistingIdsInMessage() { String existingTaskId = "existing-task-id"; String existingContextId = "existing-context-id"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))) + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))) .taskId(existingTaskId).contextId(existingContextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); @@ -222,7 +224,7 @@ public void testInitWithExistingIdsInMessage() { @Test public void testInitWithTaskIdAndExistingTaskIdMatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -235,7 +237,7 @@ public void testInitWithTaskIdAndExistingTaskIdMatch() { @Test public void testInitWithContextIdAndExistingContextIdMatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 291767b63..31d553cfe 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -25,8 +25,10 @@ public class TaskUpdaterTest { public static final String TEST_TASK_ID = "test-task-id"; public static final String TEST_TASK_CONTEXT_ID = "test-task-context-id"; + private static final String TEST_MESSAGE_ID = "123"; private static final Message SAMPLE_MESSAGE = new Message.Builder() + .messageId(TEST_MESSAGE_ID) .taskId(TEST_TASK_ID) .contextId(TEST_TASK_CONTEXT_ID) .parts(new TextPart("Test message")) diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index be3f6c847..713ac5b58 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -55,9 +55,10 @@ public Message(@JsonProperty("role") Role role, @JsonProperty("parts") List Date: Tue, 9 Sep 2025 16:07:15 -0400 Subject: [PATCH 115/493] fix: Avoid NPE for gRPC when acceptedOutputModes isn't specified for the client side (#256) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java | 7 +++++-- .../io/a2a/server/apps/common/AbstractA2AServerTest.java | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 8a889cbba..1b5bc8f17 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -337,7 +337,9 @@ private static io.a2a.grpc.AuthenticationInfo authenticationInfo(PushNotificatio public static io.a2a.grpc.SendMessageConfiguration messageSendConfiguration(MessageSendConfiguration messageSendConfiguration) { io.a2a.grpc.SendMessageConfiguration.Builder builder = io.a2a.grpc.SendMessageConfiguration.newBuilder(); - builder.addAllAcceptedOutputModes(messageSendConfiguration.acceptedOutputModes()); + if (messageSendConfiguration.acceptedOutputModes() != null) { + builder.addAllAcceptedOutputModes(messageSendConfiguration.acceptedOutputModes()); + } if (messageSendConfiguration.historyLength() != null) { builder.setHistoryLength(messageSendConfiguration.historyLength()); } @@ -772,7 +774,8 @@ private static AgentExtension agentExtension(io.a2a.grpc.AgentExtension agentExt private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.SendMessageConfiguration sendMessageConfiguration) { return new MessageSendConfiguration( - new ArrayList<>(sendMessageConfiguration.getAcceptedOutputModesList()), + sendMessageConfiguration.getAcceptedOutputModesList().isEmpty() ? null : + new ArrayList<>(sendMessageConfiguration.getAcceptedOutputModesList()), sendMessageConfiguration.getHistoryLength(), pushNotification(sendMessageConfiguration.getPushNotification()), sendMessageConfiguration.getBlocking() diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 9811c401c..0abba1f77 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1314,7 +1314,6 @@ private AgentCard createTestAgentCard() { private ClientConfig createClientConfig(boolean streaming) { return new ClientConfig.Builder() .setStreaming(streaming) - .setAcceptedOutputModes(List.of("text")) .build(); } From 6ceb36485bc3390f70e5dcdd7959f47825b1407a Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 9 Sep 2025 16:16:05 -0400 Subject: [PATCH 116/493] chore: Update to a2a-tck 0.3.0.beta (#262) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- .github/workflows/run-tck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 82c594bf7..a36f5dc6c 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -12,7 +12,7 @@ on: env: # Tag of the TCK - TCK_VERSION: 0.3.0.alpha + TCK_VERSION: 0.3.0.beta # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 # SUT_JSONRPC_URL to use for the TCK and the server agent From 1adb96c2862438a13530a91485f3919bd404078c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 10 Sep 2025 09:31:14 -0400 Subject: [PATCH 117/493] fix: Ensure messageId is required for a Message. Allow the messageId to be randomly generated if not set when using the Message.Builder (#261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../agentexecution/RequestContextTest.java | 49 +++++++++++++------ .../io/a2a/server/tasks/TaskUpdaterTest.java | 2 - spec/src/main/java/io/a2a/spec/Message.java | 3 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 1 - 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index 60b80061d..f005c5456 100644 --- a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -1,6 +1,8 @@ package io.a2a.server.agentexecution; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -23,8 +25,6 @@ public class RequestContextTest { - private static final String MESSAGE_ID = "123"; - @Test public void testInitWithoutParams() { RequestContext context = new RequestContext(null, null, null, null, null, null); @@ -37,7 +37,7 @@ public void testInitWithoutParams() { @Test public void testInitWithParamsNoIds() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); UUID taskId = UUID.fromString("00000000-0000-0000-0000-000000000001"); @@ -61,7 +61,7 @@ public void testInitWithParamsNoIds() { @Test public void testInitWithTaskId() { String taskId = "task-123"; - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, taskId, null, null, null, null); @@ -73,7 +73,7 @@ public void testInitWithTaskId() { @Test public void testInitWithContextId() { String contextId = "context-456"; - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, contextId, null, null, null); @@ -85,7 +85,7 @@ public void testInitWithContextId() { public void testInitWithBothIds() { String taskId = "task-123"; String contextId = "context-456"; - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null, null); @@ -97,7 +97,7 @@ public void testInitWithBothIds() { @Test public void testInitWithTask() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); @@ -132,7 +132,7 @@ public void testAttachRelatedTask() { @Test public void testCheckOrGenerateTaskIdWithExistingTaskId() { String existingId = "existing-task-id"; - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -145,7 +145,7 @@ public void testCheckOrGenerateTaskIdWithExistingTaskId() { public void testCheckOrGenerateContextIdWithExistingContextId() { String existingId = "existing-context-id"; - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -156,7 +156,7 @@ public void testCheckOrGenerateContextIdWithExistingContextId() { @Test public void testInitRaisesErrorOnTaskIdMismatch() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -168,7 +168,7 @@ public void testInitRaisesErrorOnTaskIdMismatch() { @Test public void testInitRaisesErrorOnContextIdMismatch() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -200,7 +200,7 @@ public void testMessagePropertyWithoutParams() { @Test public void testMessagePropertyWithParams() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -212,7 +212,7 @@ public void testInitWithExistingIdsInMessage() { String existingTaskId = "existing-task-id"; String existingContextId = "existing-context-id"; - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))) + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))) .taskId(existingTaskId).contextId(existingContextId).build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); @@ -224,7 +224,7 @@ public void testInitWithExistingIdsInMessage() { @Test public void testInitWithTaskIdAndExistingTaskIdMatch() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -237,7 +237,7 @@ public void testInitWithTaskIdAndExistingTaskIdMatch() { @Test public void testInitWithContextIdAndExistingContextIdMatch() { - var mockMessage = new Message.Builder().messageId(MESSAGE_ID).role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); @@ -247,4 +247,23 @@ public void testInitWithContextIdAndExistingContextIdMatch() { assertEquals(mockTask.getContextId(), context.getContextId()); assertEquals(mockTask, context.getTask()); } + + @Test + void testMessageBuilderGeneratesId() { + var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); + assertNotNull(mockMessage.getMessageId()); + assertFalse(mockMessage.getMessageId().isEmpty()); + } + + @Test + void testMessageBuilderUsesProvidedId() { + var mockMessage = new Message.Builder().messageId("123").role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + + RequestContext context = new RequestContext(mockParams, null, null, null, null, null); + assertEquals("123", mockMessage.getMessageId()); + } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 31d553cfe..291767b63 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -25,10 +25,8 @@ public class TaskUpdaterTest { public static final String TEST_TASK_ID = "test-task-id"; public static final String TEST_TASK_CONTEXT_ID = "test-task-context-id"; - private static final String TEST_MESSAGE_ID = "123"; private static final Message SAMPLE_MESSAGE = new Message.Builder() - .messageId(TEST_MESSAGE_ID) .taskId(TEST_TASK_ID) .contextId(TEST_TASK_CONTEXT_ID) .parts(new TextPart("Test message")) diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index 713ac5b58..d3140b684 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -187,7 +187,8 @@ public Builder metadata(Map metadata) { } public Message build() { - return new Message(role, parts, messageId, contextId, taskId, referenceTaskIds, metadata); + return new Message(role, parts, messageId == null ? UUID.randomUUID().toString() : messageId, + contextId, taskId, referenceTaskIds, metadata); } } } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index f18dae6b6..19137ab01 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -718,7 +718,6 @@ public void testOnResubscribeExistingTaskSuccess() { // We need to send some events in order for those to end up in the queue Message message = new Message.Builder() - .messageId("123") .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) .role(Message.Role.AGENT) From c0910e181598d2bb12a05c009e3b82ad756f9737 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 10 Sep 2025 17:45:39 +0200 Subject: [PATCH 118/493] feat: add support for JSON+HTTP/REST (#229) * Create JSON+HTTP/REST Client. * Integrate PR from @ronantakizawa using regexp for server routing. * Update server code to use proper JSON (de)serialization with Proto. Signed-off-by: Emmanuel Hugonnet Co-authored-by: Farah Juma --- .github/workflows/run-tck.yml | 6 +- .gitignore | 1 - README.md | 57 +- client/base/pom.xml | 10 + client/transport/grpc/pom.xml | 8 + .../jsonrpc/JSONRPCTransportTest.java | 2 - client/transport/rest/pom.xml | 62 ++ .../transport/rest/RestErrorMapper.java | 80 +++ .../client/transport/rest/RestTransport.java | 389 +++++++++++ .../transport/rest/RestTransportConfig.java | 21 + .../rest/RestTransportConfigBuilder.java | 28 + .../transport/rest/RestTransportProvider.java | 29 + .../rest/sse/RestSSEEventListener.java | 72 ++ ...ient.transport.spi.ClientTransportProvider | 1 + .../transport/rest/JsonRestMessages.java | 654 ++++++++++++++++++ .../transport/rest/RestTransportTest.java | 426 ++++++++++++ examples/helloworld/server/pom.xml | 1 - .../io/a2a/client/http/A2AHttpClient.java | 8 + .../io/a2a/client/http/JdkA2AHttpClient.java | 28 + .../a2a/client/http/A2ACardResolverTest.java | 10 + pom.xml | 72 +- reference/common/pom.xml | 3 - reference/grpc/pom.xml | 6 - reference/jsonrpc/pom.xml | 5 - reference/rest/README.md | 7 + reference/rest/pom.xml | 106 +++ .../server/rest/quarkus/A2AServerRoutes.java | 411 +++++++++++ .../rest/quarkus/CallContextFactory.java | 8 + .../quarkus/QuarkusRestTransportMetadata.java | 11 + .../src/main/resources/META-INF/beans.xml | 0 .../services/io.a2a.server.TransportMetadata | 1 + .../server/rest/quarkus/A2ATestRoutes.java | 190 +++++ .../rest/quarkus/QuarkusA2ARestTest.java | 60 ++ .../src/test/resources/application.properties | 1 + server-common/pom.xml | 3 - .../requesthandlers/RequestHandler.java | 3 + .../InMemoryPushNotificationConfigStore.java | 2 +- .../a2a/server/tasks/InMemoryTaskStore.java | 3 - .../java/io/a2a/server/tasks/TaskStore.java | 1 + .../AbstractA2ARequestHandlerTest.java | 11 + spec-grpc/pom.xml | 3 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 111 ++- spec/pom.xml | 1 - spec/src/main/java/io/a2a/util/Utils.java | 2 + tck/pom.xml | 8 +- .../io/a2a/tck/server/AgentCardProducer.java | 6 +- tests/server-common/pom.xml | 10 +- .../server/apps/common/TestHttpClient.java | 11 + transport/grpc/pom.xml | 3 - transport/jsonrpc/pom.xml | 4 +- transport/rest/pom.xml | 70 ++ .../transport/rest/handler/RestHandler.java | 427 ++++++++++++ .../src/main/resources/META-INF/beans.xml | 7 + .../a2a-requesthandler-test.properties | 1 + .../rest/handler/RestHandlerTest.java | 403 +++++++++++ .../handler/RestTestTransportMetadata.java | 12 + .../services/io.a2a.server.TransportMetadata | 2 + 57 files changed, 3796 insertions(+), 82 deletions(-) create mode 100644 client/transport/rest/pom.xml create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java create mode 100644 client/transport/rest/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider create mode 100644 client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java create mode 100644 client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java create mode 100644 reference/rest/README.md create mode 100644 reference/rest/pom.xml create mode 100644 reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java create mode 100644 reference/rest/src/main/java/io/a2a/server/rest/quarkus/CallContextFactory.java create mode 100644 reference/rest/src/main/java/io/a2a/server/rest/quarkus/QuarkusRestTransportMetadata.java create mode 100644 reference/rest/src/main/resources/META-INF/beans.xml create mode 100644 reference/rest/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata create mode 100644 reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java create mode 100644 reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java create mode 100644 reference/rest/src/test/resources/application.properties create mode 100644 transport/rest/pom.xml create mode 100644 transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java create mode 100644 transport/rest/src/main/resources/META-INF/beans.xml create mode 100644 transport/rest/src/main/resources/a2a-requesthandler-test.properties create mode 100644 transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java create mode 100644 transport/rest/src/test/java/io/a2a/transport/rest/handler/RestTestTransportMetadata.java create mode 100644 transport/rest/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index a36f5dc6c..545c1b495 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -17,6 +17,8 @@ env: UV_SYSTEM_PYTHON: 1 # SUT_JSONRPC_URL to use for the TCK and the server agent SUT_JSONRPC_URL: http://localhost:9999 + # Slow system on CI + TCK_STREAMING_TIMEOUT: 5.0 # Only run the latest job concurrency: @@ -55,7 +57,7 @@ jobs: - name: Build with Maven, skipping tests run: mvn -B install -DskipTests - name: Start SUT - run: SUT_GRPC_URL=${{ env.SUT_JSONRPC_URL }} mvn -B quarkus:dev & #SUT_JSONRPC_URL already set + run: SUT_GRPC_URL=${{ env.SUT_JSONRPC_URL }} SUT_REST_URL=${{ env.SUT_JSONRPC_URL }} mvn -B quarkus:dev & #SUT_JSONRPC_URL already set working-directory: tck - name: Wait for SUT to start run: | @@ -93,5 +95,5 @@ jobs: - name: Run TCK run: | - ./run_tck.py --sut-url ${{ env.SUT_JSONRPC_URL }} --category all --transports jsonrpc,grpc --compliance-report report.json + ./run_tck.py --sut-url ${{ env.SUT_JSONRPC_URL }} --category all --transports jsonrpc,grpc,rest --compliance-report report.json working-directory: tck/a2a-tck diff --git a/.gitignore b/.gitignore index d8908d914..04650afea 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,3 @@ nb-configuration.xml # TLS Certificates .certs/ nbproject/ - diff --git a/README.md b/README.md index 0f0d7b48e..bb50eba9c 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,18 @@ To use the reference implementation with the gRPC protocol, add the following de Note that you can add more than one of the above dependencies to your project depending on the transports you'd like to support. -Support for the HTTP+JSON/REST transport will be coming soon. +To use the reference implementation with the HTTP+JSON/REST protocol, add the following dependency to your project: + +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* + +```xml + + io.github.a2asdk + a2a-java-sdk-reference-rest + + ${io.a2a.sdk.version} + +``` ### 2. Add a class that creates an A2A Agent Card @@ -117,7 +128,7 @@ public class WeatherAgentCardProducer { .tags(Collections.singletonList("weather")) .examples(List.of("weather in LA, CA")) .build())) - .protocolVersion("0.2.5") + .protocolVersion("0.3.0") .build(); } } @@ -247,7 +258,7 @@ By default, the sdk-client is coming with the JSONRPC transport dependency. Desp dependency is included by default, you still need to add the transport to the Client as described in [JSON-RPC Transport section](#json-rpc-transport-configuration). -If you want to use another transport (such as GRPC or HTTP+JSON), you'll need to add a relevant dependency: +If you want to use the gRPC transport, you'll need to add a relevant dependency: ---- > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* @@ -262,7 +273,21 @@ If you want to use another transport (such as GRPC or HTTP+JSON), you'll need to ``` -Support for the HTTP+JSON/REST transport will be coming soon. + +If you want to use the HTTP+JSON/REST transport, you'll need to add a relevant dependency: + +---- +> *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* +---- + +```xml + + io.github.a2asdk + a2a-java-sdk-client-transport-rest + + ${io.a2a.sdk.version} + +``` ### Sample Usage @@ -360,6 +385,29 @@ Client client = Client .build(); ``` + +##### HTTP+JSON/REST Transport Configuration + +For the HTTP+JSON/REST transport, if you'd like to use the default `JdkA2AHttpClient`, provide a `RestTransportConfig` created with its default constructor. + +To use a custom HTTP client implementation, simply create a `RestTransportConfig` as follows: + +```java +// Create a custom HTTP client +A2AHttpClient customHttpClient = ... + +// Configure the client settings +ClientConfig clientConfig = new ClientConfig.Builder() + .setAcceptedOutputModes(List.of("text")) + .build(); + +Client client = Client + .builder(agentCard) + .clientConfig(clientConfig) + .withTransport(RestTransport.class, new RestTransportConfig(customHttpClient)) + .build(); +``` + ##### Multiple Transport Configurations You can specify configuration for multiple transports, the appropriate configuration @@ -371,6 +419,7 @@ Client client = Client .builder(agentCard) .withTransport(GrpcTransport.class, new GrpcTransportConfig(channelFactory)) .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .withTransport(RestTransport.class, new RestTransportConfig()) .build(); ``` diff --git a/client/base/pom.xml b/client/base/pom.xml index 6df47c1b7..c5b45b583 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -35,6 +35,11 @@ a2a-java-sdk-client-transport-grpc test + + ${project.groupId} + a2a-java-sdk-client-transport-rest + test + ${project.groupId} a2a-java-sdk-common @@ -54,6 +59,11 @@ mockserver-netty test + + org.slf4j + slf4j-jdk14 + test + \ No newline at end of file diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index b910d6ac7..158ae7ea8 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -33,6 +33,14 @@ ${project.groupId} a2a-java-sdk-client-transport-spi + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + org.junit.jupiter junit-jupiter-api diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 99e5ef151..25de32947 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -2,7 +2,6 @@ import static io.a2a.client.transport.jsonrpc.JsonMessages.AGENT_CARD; import static io.a2a.client.transport.jsonrpc.JsonMessages.AGENT_CARD_SUPPORTS_EXTENDED; -import static io.a2a.client.transport.jsonrpc.JsonMessages.AUTHENTICATION_EXTENDED_AGENT_CARD; import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_RESPONSE; import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST; @@ -50,7 +49,6 @@ import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml new file mode 100644 index 000000000..5d3be70ae --- /dev/null +++ b/client/transport/rest/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta1-SNAPSHOT + ../../../pom.xml + + a2a-java-sdk-client-transport-rest + jar + + Java SDK A2A Client Transport: JSON+HTTP/REST + Java SDK for the Agent2Agent Protocol (A2A) - JSON+HTTP/REST Client Transport + + + + ${project.groupId} + a2a-java-sdk-common + + + ${project.groupId} + a2a-java-sdk-spec + + + ${project.groupId} + a2a-java-sdk-spec-grpc + + + ${project.groupId} + a2a-java-sdk-client-transport-spi + + + io.github.a2asdk + a2a-java-sdk-http-client + + + com.google.protobuf + protobuf-java-util + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + org.slf4j + slf4j-jdk14 + test + + + + \ No newline at end of file diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java new file mode 100644 index 000000000..103f7bb0c --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java @@ -0,0 +1,80 @@ +package io.a2a.client.transport.rest; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.a2a.client.http.A2AHttpResponse; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; +import io.a2a.spec.ContentTypeNotSupportedError; +import io.a2a.spec.InternalError; +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 java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility class to A2AHttpResponse to appropriate A2A error types + */ +public class RestErrorMapper { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().registerModule(new JavaTimeModule()); + + public static A2AClientException mapRestError(A2AHttpResponse response) { + return RestErrorMapper.mapRestError(response.body(), response.status()); + } + + public static A2AClientException mapRestError(String body, int code) { + try { + if (body != null && !body.isBlank()) { + JsonNode node = OBJECT_MAPPER.readTree(body); + String className = node.findValue("error").asText(); + String errorMessage = node.findValue("message").asText(); + return mapRestError(className, errorMessage, code); + } + return mapRestError("", "", code); + } catch (JsonProcessingException ex) { + Logger.getLogger(RestErrorMapper.class.getName()).log(Level.SEVERE, null, ex); + return new A2AClientException("Failed to parse error response: " + ex.getMessage()); + } + } + + public static A2AClientException mapRestError(String className, String errorMessage, int code) { + switch (className) { + case "io.a2a.spec.TaskNotFoundError": + return new A2AClientException(errorMessage, new TaskNotFoundError()); + case "io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError": + return new A2AClientException(errorMessage, new AuthenticatedExtendedCardNotConfiguredError()); + case "io.a2a.spec.ContentTypeNotSupportedError": + return new A2AClientException(errorMessage, new ContentTypeNotSupportedError(null, null, errorMessage)); + case "io.a2a.spec.InternalError": + return new A2AClientException(errorMessage, new InternalError(errorMessage)); + case "io.a2a.spec.InvalidAgentResponseError": + return new A2AClientException(errorMessage, new InvalidAgentResponseError(null, null, errorMessage)); + case "io.a2a.spec.InvalidParamsError": + return new A2AClientException(errorMessage, new InvalidParamsError()); + case "io.a2a.spec.InvalidRequestError": + return new A2AClientException(errorMessage, new InvalidRequestError()); + case "io.a2a.spec.JSONParseError": + return new A2AClientException(errorMessage, new JSONParseError()); + case "io.a2a.spec.MethodNotFoundError": + return new A2AClientException(errorMessage, new MethodNotFoundError()); + case "io.a2a.spec.PushNotificationNotSupportedError": + return new A2AClientException(errorMessage, new PushNotificationNotSupportedError()); + case "io.a2a.spec.TaskNotCancelableError": + return new A2AClientException(errorMessage, new TaskNotCancelableError()); + case "io.a2a.spec.UnsupportedOperationError": + return new A2AClientException(errorMessage, new UnsupportedOperationError()); + default: + return new A2AClientException(errorMessage); + } + } +} diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java new file mode 100644 index 000000000..5420600d4 --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -0,0 +1,389 @@ +package io.a2a.client.transport.rest; + +import static io.a2a.util.Assert.checkNotNullParam; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.MessageOrBuilder; +import com.google.protobuf.util.JsonFormat; +import io.a2a.client.http.A2ACardResolver; +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.rest.sse.RestSSEEventListener; +import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; +import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; +import io.a2a.grpc.CancelTaskRequest; +import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskPushNotificationConfigRequest; +import io.a2a.grpc.GetTaskRequest; +import io.a2a.grpc.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskQueryParams; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.spec.A2AClientError; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.util.Utils; +import java.io.IOException; +import java.util.List; +import java.util.logging.Logger; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +public class RestTransport implements ClientTransport { + + private static final Logger log = Logger.getLogger(RestTransport.class.getName()); + private final A2AHttpClient httpClient; + private final String agentUrl; + private final List interceptors; + private AgentCard agentCard; + private boolean needsExtendedCard = false; + + public RestTransport(String agentUrl) { + this(null, null, agentUrl, null); + } + + public RestTransport(AgentCard agentCard) { + this(null, agentCard, agentCard.url(), null); + } + + public RestTransport(A2AHttpClient httpClient, AgentCard agentCard, + String agentUrl, List interceptors) { + this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; + this.agentCard = agentCard; + this.agentUrl = agentUrl.endsWith("/") ? agentUrl.substring(0, agentUrl.length() - 1) : agentUrl; + this.interceptors = interceptors; + } + + @Override + public EventKind sendMessage(MessageSendParams messageSendParams, ClientCallContext context) throws A2AClientException { + checkNotNullParam("messageSendParams", messageSendParams); + io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.SendMessageRequest.METHOD, builder, agentCard, context); + try { + String httpResponseBody = sendPostRequest(agentUrl + "/v1/message:send", payloadAndHeaders); + io.a2a.grpc.SendMessageResponse.Builder responseBuilder = io.a2a.grpc.SendMessageResponse.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + if (responseBuilder.hasMsg()) { + return ProtoUtils.FromProto.message(responseBuilder.getMsg()); + } + if (responseBuilder.hasTask()) { + return ProtoUtils.FromProto.task(responseBuilder.getTask()); + } + throw new A2AClientException("Failed to send message, wrong response:" + httpResponseBody); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to send message: " + e, e); + } + } + + @Override + public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer eventConsumer, Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", messageSendParams); + checkNotNullParam("eventConsumer", eventConsumer); + checkNotNullParam("messageSendParams", messageSendParams); + io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD, + builder, agentCard, context); + AtomicReference> ref = new AtomicReference<>(); + RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); + try { + A2AHttpClient.PostBuilder postBuilder = createPostBuilder(agentUrl + "/v1/message:stream", payloadAndHeaders); + ref.set(postBuilder.postAsyncSSE( + msg -> sseEventListener.onMessage(msg, ref.get()), + throwable -> sseEventListener.onError(throwable, ref.get()), + () -> { + // We don't need to do anything special on completion + })); + } catch (IOException e) { + throw new A2AClientException("Failed to send streaming message request: " + e, e); + } catch (InterruptedException e) { + throw new A2AClientException("Send streaming message request timed out: " + e, e); + } + } + + @Override + public Task getTask(TaskQueryParams taskQueryParams, ClientCallContext context) throws A2AClientException { + checkNotNullParam("taskQueryParams", taskQueryParams); + GetTaskRequest.Builder builder = GetTaskRequest.newBuilder(); + builder.setName("tasks/" + taskQueryParams.id()); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskRequest.METHOD, builder, + agentCard, context); + try { + String url; + if (taskQueryParams.historyLength() != null) { + url = agentUrl + String.format("/v1/tasks/%1s?historyLength=%2d", taskQueryParams.id(), taskQueryParams.historyLength()); + } else { + url = agentUrl + String.format("/v1/tasks/%1s", taskQueryParams.id()); + } + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + getBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + A2AHttpResponse response = getBuilder.get(); + if (!response.success()) { + throw RestErrorMapper.mapRestError(response); + } + String httpResponseBody = response.body(); + io.a2a.grpc.Task.Builder responseBuilder = io.a2a.grpc.Task.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + return ProtoUtils.FromProto.task(responseBuilder); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to get task: " + e, e); + } + } + + @Override + public Task cancelTask(TaskIdParams taskIdParams, ClientCallContext context) throws A2AClientException { + checkNotNullParam("taskIdParams", taskIdParams); + CancelTaskRequest.Builder builder = CancelTaskRequest.newBuilder(); + builder.setName("tasks/" + taskIdParams.id()); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.CancelTaskRequest.METHOD, builder, + agentCard, context); + try { + String httpResponseBody = sendPostRequest(agentUrl + String.format("/v1/tasks/%1s:cancel", taskIdParams.id()), payloadAndHeaders); + io.a2a.grpc.Task.Builder responseBuilder = io.a2a.grpc.Task.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + return ProtoUtils.FromProto.task(responseBuilder); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to cancel task: " + e, e); + } + } + + @Override + public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + CreateTaskPushNotificationConfigRequest.Builder builder = CreateTaskPushNotificationConfigRequest.newBuilder(); + builder.setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(request)) + .setParent("tasks/" + request.taskId()); + if (request.pushNotificationConfig().id() != null) { + builder.setConfigId(request.pushNotificationConfig().id()); + } + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); + try { + String httpResponseBody = sendPostRequest(agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs", request.taskId()), payloadAndHeaders); + io.a2a.grpc.TaskPushNotificationConfig.Builder responseBuilder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + return ProtoUtils.FromProto.taskPushNotificationConfig(responseBuilder); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to set task push notification config: " + e, e); + } + } + + @Override + public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + GetTaskPushNotificationConfigRequest.Builder builder = GetTaskPushNotificationConfigRequest.newBuilder(); + builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId())); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskPushNotificationConfigRequest.METHOD, builder, + agentCard, context); + try { + String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + getBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + A2AHttpResponse response = getBuilder.get(); + if (!response.success()) { + throw RestErrorMapper.mapRestError(response); + } + String httpResponseBody = response.body(); + io.a2a.grpc.TaskPushNotificationConfig.Builder responseBuilder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + return ProtoUtils.FromProto.taskPushNotificationConfig(responseBuilder); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to get push notifications: " + e, e); + } + } + + @Override + public List listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + ListTaskPushNotificationConfigRequest.Builder builder = ListTaskPushNotificationConfigRequest.newBuilder(); + builder.setParent(String.format("/tasks/%1s/pushNotificationConfigs", request.id())); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.ListTaskPushNotificationConfigRequest.METHOD, builder, + agentCard, context); + try { + String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs", request.id()); + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + getBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + A2AHttpResponse response = getBuilder.get(); + if (!response.success()) { + throw RestErrorMapper.mapRestError(response); + } + String httpResponseBody = response.body(); + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + return ProtoUtils.FromProto.listTaskPushNotificationConfigParams(responseBuilder); + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to list push notifications: " + e, e); + } + } + + @Override + public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.DeleteTaskPushNotificationConfigRequest.METHOD, builder, + agentCard, context); + try { + String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); + A2AHttpClient.DeleteBuilder deleteBuilder = httpClient.createDelete().url(url); + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + deleteBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + A2AHttpResponse response = deleteBuilder.delete(); + if (!response.success()) { + throw RestErrorMapper.mapRestError(response); + } + } catch (A2AClientException e) { + throw e; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to delete push notification config: " + e, e); + } + } + + @Override + public void resubscribe(TaskIdParams request, Consumer eventConsumer, + Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + io.a2a.grpc.TaskSubscriptionRequest.Builder builder = io.a2a.grpc.TaskSubscriptionRequest.newBuilder(); + builder.setName("tasks/" + request.id()); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.TaskResubscriptionRequest.METHOD, builder, + agentCard, context); + AtomicReference> ref = new AtomicReference<>(); + RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); + try { + String url = agentUrl + String.format("/v1/tasks/%1s:subscribe", request.id()); + A2AHttpClient.PostBuilder postBuilder = createPostBuilder(url, payloadAndHeaders); + ref.set(postBuilder.postAsyncSSE( + msg -> sseEventListener.onMessage(msg, ref.get()), + throwable -> sseEventListener.onError(throwable, ref.get()), + () -> { + // We don't need to do anything special on completion + })); + } catch (IOException e) { + throw new A2AClientException("Failed to send streaming message request: " + e, e); + } catch (InterruptedException e) { + throw new A2AClientException("Send streaming message request timed out: " + e, e); + } + } + + @Override + public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + A2ACardResolver resolver; + try { + if (agentCard == null) { + resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context)); + agentCard = resolver.getAgentCard(); + needsExtendedCard = agentCard.supportsAuthenticatedExtendedCard(); + } + if (!needsExtendedCard) { + return agentCard; + } + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskRequest.METHOD, null, + agentCard, context); + String url = agentUrl + String.format("/v1/card"); + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + getBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + A2AHttpResponse response = getBuilder.get(); + if (!response.success()) { + throw RestErrorMapper.mapRestError(response); + } + String httpResponseBody = response.body(); + agentCard = Utils.OBJECT_MAPPER.readValue(httpResponseBody, AgentCard.class); + needsExtendedCard = false; + return agentCard; + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); + } catch (A2AClientError e) { + throw new A2AClientException("Failed to get agent card: " + e, e); + } + } + + @Override + public void close() { + // no-op + } + + private PayloadAndHeaders applyInterceptors(String methodName, MessageOrBuilder payload, + AgentCard agentCard, ClientCallContext clientCallContext) { + PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, getHttpHeaders(clientCallContext)); + if (interceptors != null && !interceptors.isEmpty()) { + for (ClientCallInterceptor interceptor : interceptors) { + payloadAndHeaders = interceptor.intercept(methodName, payloadAndHeaders.getPayload(), + payloadAndHeaders.getHeaders(), agentCard, clientCallContext); + } + } + return payloadAndHeaders; + } + + private String sendPostRequest(String url, PayloadAndHeaders payloadAndHeaders) throws IOException, InterruptedException { + A2AHttpClient.PostBuilder builder = createPostBuilder(url, payloadAndHeaders); + A2AHttpResponse response = builder.post(); + if (!response.success()) { + log.fine("Error on POST processing " + JsonFormat.printer().print((MessageOrBuilder) payloadAndHeaders.getPayload())); + throw RestErrorMapper.mapRestError(response); + } + return response.body(); + } + + private A2AHttpClient.PostBuilder createPostBuilder(String url, PayloadAndHeaders payloadAndHeaders) throws JsonProcessingException, InvalidProtocolBufferException { + log.fine(JsonFormat.printer().print((MessageOrBuilder) payloadAndHeaders.getPayload())); + A2AHttpClient.PostBuilder postBuilder = httpClient.createPost() + .url(url) + .addHeader("Content-Type", "application/json") + .body(JsonFormat.printer().print((MessageOrBuilder) payloadAndHeaders.getPayload())); + + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + postBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + return postBuilder; + } + + private Map getHttpHeaders(ClientCallContext context) { + return context != null ? context.getHeaders() : null; + } +} diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java new file mode 100644 index 000000000..bbb583a1b --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java @@ -0,0 +1,21 @@ +package io.a2a.client.transport.rest; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.transport.spi.ClientTransportConfig; + +public class RestTransportConfig extends ClientTransportConfig { + + private final A2AHttpClient httpClient; + + public RestTransportConfig() { + this.httpClient = null; + } + + public RestTransportConfig(A2AHttpClient httpClient) { + this.httpClient = httpClient; + } + + public A2AHttpClient getHttpClient() { + return httpClient; + } +} \ No newline at end of file diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java new file mode 100644 index 000000000..30f8b412a --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java @@ -0,0 +1,28 @@ +package io.a2a.client.transport.rest; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.spi.ClientTransportConfigBuilder; + +public class RestTransportConfigBuilder extends ClientTransportConfigBuilder { + + private A2AHttpClient httpClient; + + public RestTransportConfigBuilder httpClient(A2AHttpClient httpClient) { + this.httpClient = httpClient; + + return this; + } + + @Override + public RestTransportConfig build() { + // No HTTP client provided, fallback to the default one (JDK-based implementation) + if (httpClient == null) { + httpClient = new JdkA2AHttpClient(); + } + + RestTransportConfig config = new RestTransportConfig(httpClient); + config.setInterceptors(this.interceptors); + return config; + } +} \ No newline at end of file diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java new file mode 100644 index 000000000..99d155968 --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java @@ -0,0 +1,29 @@ +package io.a2a.client.transport.rest; + +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.spi.ClientTransportProvider; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.TransportProtocol; + +public class RestTransportProvider implements ClientTransportProvider { + + @Override + public String getTransportProtocol() { + return TransportProtocol.HTTP_JSON.asString(); + } + + @Override + public RestTransport create(RestTransportConfig clientTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { + RestTransportConfig transportConfig = clientTransportConfig; + if (transportConfig == null) { + transportConfig = new RestTransportConfig(new JdkA2AHttpClient()); + } + return new RestTransport(clientTransportConfig.getHttpClient(), agentCard, agentUrl, transportConfig.getInterceptors()); + } + + @Override + public Class getTransportProtocolClass() { + return RestTransport.class; + } +} diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java new file mode 100644 index 000000000..c34d615cb --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java @@ -0,0 +1,72 @@ +package io.a2a.client.transport.rest.sse; + +import static io.a2a.grpc.StreamResponse.PayloadCase.ARTIFACT_UPDATE; +import static io.a2a.grpc.StreamResponse.PayloadCase.MSG; +import static io.a2a.grpc.StreamResponse.PayloadCase.STATUS_UPDATE; +import static io.a2a.grpc.StreamResponse.PayloadCase.TASK; + +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.a2a.client.transport.rest.RestErrorMapper; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.spec.StreamingEventKind; + +public class RestSSEEventListener { + + private static final Logger log = Logger.getLogger(RestSSEEventListener.class.getName()); + private final Consumer eventHandler; + private final Consumer errorHandler; + + public RestSSEEventListener(Consumer eventHandler, + Consumer errorHandler) { + this.eventHandler = eventHandler; + this.errorHandler = errorHandler; + } + + public void onMessage(String message, Future completableFuture) { + try { + System.out.println("Streaming message received: " + message); + io.a2a.grpc.StreamResponse.Builder builder = io.a2a.grpc.StreamResponse.newBuilder(); + JsonFormat.parser().merge(message, builder); + handleMessage(builder.build(), completableFuture); + } catch (InvalidProtocolBufferException e) { + errorHandler.accept(RestErrorMapper.mapRestError(message, 500)); + } + } + + public void onError(Throwable throwable, Future future) { + if (errorHandler != null) { + errorHandler.accept(throwable); + } + future.cancel(true); // close SSE channel + } + + private void handleMessage(StreamResponse response, Future future) { + StreamingEventKind event; + switch (response.getPayloadCase()) { + case MSG: + event = ProtoUtils.FromProto.message(response.getMsg()); + break; + case TASK: + event = ProtoUtils.FromProto.task(response.getTask()); + break; + case STATUS_UPDATE: + event = ProtoUtils.FromProto.taskStatusUpdateEvent(response.getStatusUpdate()); + break; + case ARTIFACT_UPDATE: + event = ProtoUtils.FromProto.taskArtifactUpdateEvent(response.getArtifactUpdate()); + break; + default: + log.warning("Invalid stream response " + response.getPayloadCase()); + errorHandler.accept(new IllegalStateException("Invalid stream response from server: " + response.getPayloadCase())); + return; + } + eventHandler.accept(event); + } + +} diff --git a/client/transport/rest/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider b/client/transport/rest/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider new file mode 100644 index 000000000..894866aab --- /dev/null +++ b/client/transport/rest/src/main/resources/META-INF/services/io.a2a.client.transport.spi.ClientTransportProvider @@ -0,0 +1 @@ +io.a2a.client.transport.rest.RestTransportProvider \ No newline at end of file diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java new file mode 100644 index 000000000..e94fcca1f --- /dev/null +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java @@ -0,0 +1,654 @@ +package io.a2a.client.transport.rest; + +/** + * Request and response messages used by the tests. These have been created following examples from + * the A2A sample messages. + */ +public class JsonRestMessages { + + static final String SEND_MESSAGE_TEST_REQUEST = """ + { + "message": + { + "messageId": "message-1234", + "contextId": "context-1234", + "role": "ROLE_USER", + "content": [{ + "text": "tell me a joke" + }], + "metadata": { + } + } + }"""; + + static final String SEND_MESSAGE_TEST_RESPONSE = """ + { + "task": { + "id": "9b511af4-b27c-47fa-aecf-2a93c08a44f8", + "contextId": "context-1234", + "status": { + "state": "TASK_STATE_SUBMITTED" + }, + "history": [ + { + "messageId": "message-1234", + "contextId": "context-1234", + "taskId": "9b511af4-b27c-47fa-aecf-2a93c08a44f8", + "role": "ROLE_USER", + "content": [ + { + "text": "tell me a joke" + } + ], + "metadata": {} + } + ] + } + }"""; + + static final String CANCEL_TASK_TEST_REQUEST = """ + { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + }"""; + + static final String CANCEL_TASK_TEST_RESPONSE = """ + { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "TASK_STATE_CANCELLED" + }, + "metadata": {} + }"""; + + static final String GET_TASK_TEST_RESPONSE = """ + { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "TASK_STATE_COMPLETED" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "parts": [ + { + "text": "Why did the chicken cross the road? To get to the other side!" + } + ] + } + ], + "history": [ + { + "role": "ROLE_USER", + "content": [ + { + "text": "tell me a joke" + }, + { + "file": { + "file_with_uri": "file:///path/to/file.txt", + "mimeType": "text/plain" + } + }, + { + "file": { + "file_with_bytes": "aGVsbG8=", + "mimeType": "text/plain" + } + } + ], + "messageId": "message-123" + } + ], + "metadata": {} + } + """; + + static final String AGENT_CARD = """ + { + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": false, + "protocolVersion": "0.2.5" + }"""; + + static final String AGENT_CARD_SUPPORTS_EXTENDED = """ + { + "name": "GeoSpatial Route Planner Agent", + "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + } + ], + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" + }"""; + + static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """ + { + "name": "GeoSpatial Route Planner Agent Extended", + "description": "Extended description", + "url": "https://georoute-agent.example.com/a2a/v1", + "provider": { + "organization": "Example Geo Services Inc.", + "url": "https://www.examplegeoservices.com" + }, + "iconUrl": "https://georoute-agent.example.com/icon.png", + "version": "1.2.0", + "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", + "capabilities": { + "streaming": true, + "pushNotifications": true, + "stateTransitionHistory": false + }, + "securitySchemes": { + "google": { + "type": "openIdConnect", + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } + }, + "security": [{ "google": ["openid", "profile", "email"] }], + "defaultInputModes": ["application/json", "text/plain"], + "defaultOutputModes": ["application/json", "image/png"], + "skills": [ + { + "id": "route-optimizer-traffic", + "name": "Traffic-Aware Route Optimizer", + "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", + "tags": ["maps", "routing", "navigation", "directions", "traffic"], + "examples": [ + "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", + "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}" + ], + "inputModes": ["application/json", "text/plain"], + "outputModes": [ + "application/json", + "application/vnd.geo+json", + "text/html" + ] + }, + { + "id": "custom-map-generator", + "name": "Personalized Map Generator", + "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", + "tags": ["maps", "customization", "visualization", "cartography"], + "examples": [ + "Generate a map of my upcoming road trip with all planned stops highlighted.", + "Show me a map visualizing all coffee shops within a 1-mile radius of my current location." + ], + "inputModes": ["application/json"], + "outputModes": [ + "image/png", + "image/jpeg", + "application/json", + "text/html" + ] + }, + { + "id": "skill-extended", + "name": "Extended Skill", + "description": "This is an extended skill.", + "tags": ["extended"] + } + ], + "supportsAuthenticatedExtendedCard": true, + "protocolVersion": "0.2.5" + }"""; + + static final String SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + }, + } + }"""; + + static final String SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE = """ + { + "jsonrpc": "2.0", + "id": 1, + "result": { + "role": "agent", + "parts": [ + { + "kind": "text", + "text": "Why did the chicken cross the road? To get to the other side!" + } + ], + "messageId": "msg-456", + "kind": "message" + } + }"""; + + static final String SEND_MESSAGE_WITH_ERROR_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + }, + } + }"""; + + static final String SEND_MESSAGE_ERROR_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "error": { + "code": -32702, + "message": "Invalid parameters", + "data": "Hello world" + } + }"""; + + static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ + { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + }"""; + static final String LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ + { + "configs":[ + { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + }, + { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/5", + "pushNotificationConfig": { + "url": "https://test.com/callback" + } + } + ] + }"""; + + + static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ + { + "parent": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "config": { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": [ "jwt" ] + } + } + } + }"""; + + static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ + { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + }"""; + + static final String SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "analyze this image" + }, + { + "kind": "file", + "file": { + "uri": "file:///path/to/image.jpg", + "mimeType": "image/jpeg" + } + } + ], + "messageId": "message-1234-with-file", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "image-analysis", + "parts": [ + { + "kind": "text", + "text": "This is an image of a cat sitting on a windowsill." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "process this data" + }, + { + "kind": "data", + "data": { + "temperature": 25.5, + "humidity": 60.2, + "location": "San Francisco", + "timestamp": "2024-01-15T10:30:00Z" + } + } + ], + "messageId": "message-1234-with-data", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "data-analysis", + "parts": [ + { + "kind": "text", + "text": "Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "analyze this data and image" + }, + { + "kind": "file", + "file": { + "bytes": "aGVsbG8=", + "name": "chart.png", + "mimeType": "image/png" + } + }, + { + "kind": "data", + "data": { + "chartType": "bar", + "dataPoints": [10, 20, 30, 40], + "labels": ["Q1", "Q2", "Q3", "Q4"] + } + } + ], + "messageId": "message-1234-with-mixed", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + + static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE = """ + { + "jsonrpc": "2.0", + "result": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "mixed-analysis", + "parts": [ + { + "kind": "text", + "text": "Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." + } + ] + } + ], + "metadata": {}, + "kind": "task" + } + }"""; + + public static final String SEND_MESSAGE_STREAMING_TEST_REQUEST = """ + { + "message": { + "role": "ROLE_USER", + "content": [ + { + "text": "tell me some jokes" + } + ], + "messageId": "message-1234", + "contextId": "context-1234" + }, + "configuration": { + "acceptedOutputModes": ["text"] + } + }"""; + static final String SEND_MESSAGE_STREAMING_TEST_RESPONSE + = "event: message\n" + + "data: {\"task\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"TASK_STATE_SUBMITTED\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{}}}\n\n"; + + static final String TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE + = "event: message\n" + + "data: {\"task\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"TASK_STATE_COMPLETED\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{}}}\n\n"; + public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ + { + "jsonrpc": "2.0", + "method": "tasks/resubscribe", + "params": { + "id": "task-1234" + } + }"""; +} diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java new file mode 100644 index 000000000..fc6fc23a6 --- /dev/null +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -0,0 +1,426 @@ +package io.a2a.client.transport.rest; + + +import static io.a2a.client.transport.rest.JsonRestMessages.CANCEL_TASK_TEST_REQUEST; +import static io.a2a.client.transport.rest.JsonRestMessages.CANCEL_TASK_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.GET_TASK_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_TEST_REQUEST; +import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST; +import static io.a2a.client.transport.rest.JsonRestMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; +import static io.a2a.client.transport.rest.JsonRestMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.spec.Artifact; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.FilePart; +import io.a2a.spec.FileWithBytes; +import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendConfiguration; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.Part; +import io.a2a.spec.Part.Kind; +import io.a2a.spec.PushNotificationAuthenticationInfo; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; +import io.a2a.spec.TextPart; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.logging.Logger; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; +import org.mockserver.matchers.MatchType; +import org.mockserver.model.JsonBody; + +public class RestTransportTest { + + private static final Logger log = Logger.getLogger(RestTransportTest.class.getName()); + private ClientAndServer server; + + @BeforeEach + public void setUp() throws IOException { + server = new ClientAndServer(4001); + } + + @AfterEach + public void tearDown() { + server.stop(); + } + + public RestTransportTest() { + } + + /** + * Test of sendMessage method, of class JSONRestTransport. + */ + @Test + public void testSendMessage() throws Exception { + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("tell me a joke"))) + .contextId("context-1234") + .messageId("message-1234") + .taskId("") + .build(); + this.server.when( + request() + .withMethod("POST") + .withPath("/v1/message:send") + .withBody(JsonBody.json(SEND_MESSAGE_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) + ) + .respond( + response() + .withStatusCode(200) + .withBody(SEND_MESSAGE_TEST_RESPONSE) + ); + MessageSendParams messageSendParams = new MessageSendParams(message, null, null); + ClientCallContext context = null; + RestTransport instance = new RestTransport("http://localhost:4001"); + EventKind result = instance.sendMessage(messageSendParams, context); + assertEquals("task", result.getKind()); + Task task = (Task) result; + assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", task.getId()); + assertEquals("context-1234", task.getContextId()); + assertEquals(TaskState.SUBMITTED, task.getStatus().state()); + assertNull(task.getStatus().message()); + assertNull(task.getMetadata()); + assertEquals(true, task.getArtifacts().isEmpty()); + assertEquals(1, task.getHistory().size()); + Message history = task.getHistory().get(0); + assertEquals("message", history.getKind()); + assertEquals(Message.Role.USER, history.getRole()); + assertEquals("context-1234", history.getContextId()); + assertEquals("message-1234", history.getMessageId()); + assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.getTaskId()); + assertEquals(1, history.getParts().size()); + assertEquals(Kind.TEXT, history.getParts().get(0).getKind()); + assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).getText()); + assertNull(history.getMetadata()); + assertNull(history.getReferenceTaskIds()); + } + + /** + * Test of cancelTask method, of class JSONRestTransport. + */ + @Test + public void testCancelTask() throws Exception { + this.server.when( + request() + .withMethod("POST") + .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64:cancel") + .withBody(JsonBody.json(CANCEL_TASK_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) + ) + .respond( + response() + .withStatusCode(200) + .withBody(CANCEL_TASK_TEST_RESPONSE) + ); + ClientCallContext context = null; + RestTransport instance = new RestTransport("http://localhost:4001"); + Task task = instance.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64", + new HashMap<>()), context); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); + assertEquals(TaskState.CANCELED, task.getStatus().state()); + assertNull(task.getStatus().message()); + assertNull(task.getMetadata()); + } + + /** + * Test of getTask method, of class JSONRestTransport. + */ + @Test + public void testGetTask() throws Exception { + this.server.when( + request() + .withMethod("GET") + .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64") + ) + .respond( + response() + .withStatusCode(200) + .withBody(GET_TASK_TEST_RESPONSE) + ); + ClientCallContext context = null; + TaskQueryParams request = new TaskQueryParams("de38c76d-d54c-436c-8b9f-4c2703648d64", 10); + RestTransport instance = new RestTransport("http://localhost:4001"); + Task task = instance.getTask(request, context); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + assertNull(task.getStatus().message()); + assertNull(task.getMetadata()); + assertEquals(false, task.getArtifacts().isEmpty()); + assertEquals(1, task.getArtifacts().size()); + Artifact artifact = task.getArtifacts().get(0); + assertEquals("artifact-1", artifact.artifactId()); + assertEquals("", artifact.name()); + assertEquals(false, artifact.parts().isEmpty()); + assertEquals(Kind.TEXT, artifact.parts().get(0).getKind()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).getText()); + assertEquals(1, task.getHistory().size()); + Message history = task.getHistory().get(0); + assertEquals("message", history.getKind()); + assertEquals(Message.Role.USER, history.getRole()); + assertEquals("message-123", history.getMessageId()); + assertEquals(3, history.getParts().size()); + assertEquals(Kind.TEXT, history.getParts().get(0).getKind()); + assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).getText()); + assertEquals(Kind.FILE, history.getParts().get(1).getKind()); + FilePart part = (FilePart) history.getParts().get(1); + assertEquals("text/plain", part.getFile().mimeType()); + assertEquals("file:///path/to/file.txt", ((FileWithUri) part.getFile()).uri()); + part = (FilePart) history.getParts().get(2); + assertEquals(Kind.FILE, part.getKind()); + assertEquals("text/plain", part.getFile().mimeType()); + assertEquals("hello", ((FileWithBytes) part.getFile()).bytes()); + assertNull(history.getMetadata()); + assertNull(history.getReferenceTaskIds()); + } + + /** + * Test of sendMessageStreaming method, of class JSONRestTransport. + */ + @Test + public void testSendMessageStreaming() throws Exception { + this.server.when( + request() + .withMethod("POST") + .withPath("/v1/message:stream") + .withBody(JsonBody.json(SEND_MESSAGE_STREAMING_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) + ) + .respond( + response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody(SEND_MESSAGE_STREAMING_TEST_RESPONSE) + ); + + RestTransport client = new RestTransport("http://localhost:4001"); + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("tell me some jokes"))) + .contextId("context-1234") + .messageId("message-1234") + .build(); + MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + .acceptedOutputModes(List.of("text")) + .blocking(false) + .build(); + MessageSendParams params = new MessageSendParams.Builder() + .message(message) + .configuration(configuration) + .build(); + AtomicReference receivedEvent = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + Consumer eventHandler = event -> { + receivedEvent.set(event); + latch.countDown(); + }; + Consumer errorHandler = error -> { + }; + client.sendMessageStreaming(params, eventHandler, errorHandler, null); + + boolean eventReceived = latch.await(10, TimeUnit.SECONDS); + assertTrue(eventReceived); + assertNotNull(receivedEvent.get()); + assertEquals("task", receivedEvent.get().getKind()); + Task task = (Task) receivedEvent.get(); + assertEquals("2", task.getId()); + } + + /** + * Test of setTaskPushNotificationConfiguration method, of class JSONRestTransport. + */ + @Test + public void testSetTaskPushNotificationConfiguration() throws Exception { + log.info("Testing setTaskPushNotificationConfiguration"); + this.server.when( + request() + .withMethod("POST") + .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs") + .withBody(JsonBody.json(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) + ) + .respond( + response() + .withStatusCode(200) + .withBody(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) + ); + RestTransport client = new RestTransport("http://localhost:4001"); + TaskPushNotificationConfig pushedConfig = new TaskPushNotificationConfig( + "de38c76d-d54c-436c-8b9f-4c2703648d64", + new PushNotificationConfig.Builder() + .url("https://example.com/callback") + .authenticationInfo( + new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), null)) + .build()); + TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration(pushedConfig, null); + PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); + assertNotNull(pushNotificationConfig); + assertEquals("https://example.com/callback", pushNotificationConfig.url()); + PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + assertEquals(1, authenticationInfo.schemes().size()); + assertEquals("jwt", authenticationInfo.schemes().get(0)); + } + + /** + * Test of getTaskPushNotificationConfiguration method, of class JSONRestTransport. + */ + @Test + public void testGetTaskPushNotificationConfiguration() throws Exception { + this.server.when( + request() + .withMethod("GET") + .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10") + ) + .respond( + response() + .withStatusCode(200) + .withBody(GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) + ); + + RestTransport client = new RestTransport("http://localhost:4001"); + TaskPushNotificationConfig taskPushNotificationConfig = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "10", + new HashMap<>()), null); + PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); + assertNotNull(pushNotificationConfig); + assertEquals("https://example.com/callback", pushNotificationConfig.url()); + PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + assertTrue(authenticationInfo.schemes().size() == 1); + assertEquals("jwt", authenticationInfo.schemes().get(0)); + } + + /** + * Test of listTaskPushNotificationConfigurations method, of class JSONRestTransport. + */ + @Test + public void testListTaskPushNotificationConfigurations() throws Exception { + this.server.when( + request() + .withMethod("GET") + .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs") + ) + .respond( + response() + .withStatusCode(200) + .withBody(LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) + ); + + RestTransport client = new RestTransport("http://localhost:4001"); + List taskPushNotificationConfigs = client.listTaskPushNotificationConfigurations( + new ListTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", new HashMap<>()), null); + assertEquals(2, taskPushNotificationConfigs.size()); + PushNotificationConfig pushNotificationConfig = taskPushNotificationConfigs.get(0).pushNotificationConfig(); + assertNotNull(pushNotificationConfig); + assertEquals("https://example.com/callback", pushNotificationConfig.url()); + assertEquals("10", pushNotificationConfig.id()); + PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + assertTrue(authenticationInfo.schemes().size() == 1); + assertEquals("jwt", authenticationInfo.schemes().get(0)); + assertEquals("", authenticationInfo.credentials()); + pushNotificationConfig = taskPushNotificationConfigs.get(1).pushNotificationConfig(); + assertNotNull(pushNotificationConfig); + assertEquals("https://test.com/callback", pushNotificationConfig.url()); + assertEquals("5", pushNotificationConfig.id()); + authenticationInfo = pushNotificationConfig.authentication(); + assertNull(authenticationInfo); + } + + /** + * Test of deleteTaskPushNotificationConfigurations method, of class JSONRestTransport. + */ + @Test + public void testDeleteTaskPushNotificationConfigurations() throws Exception { + log.info("Testing deleteTaskPushNotificationConfigurations"); + this.server.when( + request() + .withMethod("DELETE") + .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10") + ) + .respond( + response() + .withStatusCode(200) + ); + ClientCallContext context = null; + RestTransport instance = new RestTransport("http://localhost:4001"); + instance.deleteTaskPushNotificationConfigurations(new DeleteTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "10"), context); + } + + /** + * Test of resubscribe method, of class JSONRestTransport. + */ + @Test + public void testResubscribe() throws Exception { + log.info("Testing resubscribe"); + + this.server.when( + request() + .withMethod("POST") + .withPath("/v1/tasks/task-1234:subscribe") + ) + .respond( + response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody(TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE) + ); + + RestTransport client = new RestTransport("http://localhost:4001"); + TaskIdParams taskIdParams = new TaskIdParams("task-1234"); + + AtomicReference receivedEvent = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + Consumer eventHandler = event -> { + receivedEvent.set(event); + latch.countDown(); + }; + Consumer errorHandler = error -> {}; + client.resubscribe(taskIdParams, eventHandler, errorHandler, null); + + boolean eventReceived = latch.await(10, TimeUnit.SECONDS); + assertTrue(eventReceived); + + StreamingEventKind eventKind = receivedEvent.get();; + assertNotNull(eventKind); + assertInstanceOf(Task.class, eventKind); + Task task = (Task) eventKind; + assertEquals("2", task.getId()); + assertEquals("context-1234", task.getContextId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + List artifacts = task.getArtifacts(); + assertEquals(1, artifacts.size()); + Artifact artifact = artifacts.get(0); + assertEquals("artifact-1", artifact.artifactId()); + assertEquals("joke", artifact.name()); + Part part = artifact.parts().get(0); + assertEquals(Part.Kind.TEXT, part.getKind()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).getText()); + } +} diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index b35ffa8a9..03f06158a 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -23,7 +23,6 @@ io.github.a2asdk a2a-java-sdk-reference-jsonrpc - ${project.version} io.quarkus diff --git a/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java index f59e079f2..52c252a8f 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java @@ -1,6 +1,7 @@ package io.a2a.client.http; import java.io.IOException; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -10,8 +11,11 @@ public interface A2AHttpClient { PostBuilder createPost(); + DeleteBuilder createDelete(); + interface Builder> { T url(String s); + T addHeaders(Map headers); T addHeader(String name, String value); } @@ -31,4 +35,8 @@ CompletableFuture postAsyncSSE( Consumer errorConsumer, Runnable completeRunnable) throws IOException, InterruptedException; } + + interface DeleteBuilder extends Builder { + A2AHttpResponse delete() throws IOException, InterruptedException; + } } diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index 2cdbb2d37..abcecc8ed 100644 --- a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -35,6 +35,11 @@ public PostBuilder createPost() { return new JdkPostBuilder(); } + @Override + public DeleteBuilder createDelete() { + return new JdkDeleteBuilder(); + } + private abstract class JdkBuilder> implements Builder { private String url; private Map headers = new HashMap<>(); @@ -51,6 +56,16 @@ public T addHeader(String name, String value) { return self(); } + @Override + public T addHeaders(Map headers) { + if(headers != null && ! headers.isEmpty()) { + for (Map.Entry entry : headers.entrySet()) { + addHeader(entry.getKey(), entry.getValue()); + } + } + return self(); + } + @SuppressWarnings("unchecked") T self() { return (T) this; @@ -145,6 +160,19 @@ public CompletableFuture getAsyncSSE( .build(); return super.asyncRequest(request, messageConsumer, errorConsumer, completeRunnable); } + + } + + private class JdkDeleteBuilder extends JdkBuilder implements A2AHttpClient.DeleteBuilder { + + @Override + public A2AHttpResponse delete() throws IOException, InterruptedException { + HttpRequest request = super.createRequestBuilder().DELETE().build(); + HttpResponse response = + httpClient.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); + return new JdkHttpResponse(response); + } + } private class JdkPostBuilder extends JdkBuilder implements A2AHttpClient.PostBuilder { diff --git a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java index 0b855007b..99d26adad 100644 --- a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java +++ b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java @@ -14,6 +14,7 @@ import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; +import java.util.Map; import org.junit.jupiter.api.Test; public class A2ACardResolverTest { @@ -126,6 +127,11 @@ public PostBuilder createPost() { return null; } + @Override + public DeleteBuilder createDelete() { + return null; + } + class TestGetBuilder implements A2AHttpClient.GetBuilder { @Override @@ -161,7 +167,11 @@ public GetBuilder url(String s) { @Override public GetBuilder addHeader(String name, String value) { + return this; + } + @Override + public GetBuilder addHeaders(Map headers) { return this; } } diff --git a/pom.xml b/pom.xml index d1677324e..cfe777bae 100644 --- a/pom.xml +++ b/pom.xml @@ -81,22 +81,22 @@ ${project.groupId} - a2a-java-sdk-client-config + a2a-java-sdk-client-transport-spi ${project.version} ${project.groupId} - a2a-java-sdk-client-transport-spi + a2a-java-sdk-client-transport-jsonrpc ${project.version} ${project.groupId} - a2a-java-sdk-client-transport-jsonrpc + a2a-java-sdk-client-transport-grpc ${project.version} ${project.groupId} - a2a-java-sdk-client-transport-grpc + a2a-java-sdk-client-transport-rest ${project.version} @@ -119,6 +119,46 @@ a2a-java-sdk-spec-grpc ${project.version} + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-rest + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-rest + ${project.version} + io.grpc grpc-bom @@ -212,6 +252,25 @@ ${logback.version} test + + ${project.groupId} + a2a-java-sdk-tests-server-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-tests-server-common + test-jar + test + ${project.version} + + + ${project.groupId} + a2a-java-sdk-server-common + test-jar + test + ${project.version} +
@@ -302,7 +361,7 @@ sign - + @@ -327,6 +386,7 @@ client/base client/transport/grpc client/transport/jsonrpc + client/transport/rest client/transport/spi common examples/helloworld @@ -334,6 +394,7 @@ reference/common reference/grpc reference/jsonrpc + reference/rest server-common spec spec-grpc @@ -341,6 +402,7 @@ tests/server-common transport/jsonrpc transport/grpc + transport/rest diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 8b2af244f..2e149a343 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -21,12 +21,10 @@ ${project.groupId} a2a-java-sdk-server-common - ${project.version} ${project.groupId} a2a-java-sdk-tests-server-common - ${project.version} provided @@ -34,7 +32,6 @@ a2a-java-sdk-tests-server-common test-jar test - ${project.version} io.quarkus diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 6a4ec4618..e11adc2b5 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -18,22 +18,18 @@ ${project.groupId} a2a-java-sdk-reference-common - ${project.version} ${project.groupId} a2a-java-sdk-transport-grpc - ${project.version} ${project.groupId} a2a-java-sdk-server-common - ${project.version} ${project.groupId} a2a-java-sdk-tests-server-common - ${project.version} provided @@ -41,12 +37,10 @@ a2a-java-sdk-tests-server-common test-jar test - ${project.version} ${project.groupId} a2a-java-sdk-client-transport-grpc - ${project.version} test diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index a4342f96c..de441f6b3 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -21,22 +21,18 @@ ${project.groupId} a2a-java-sdk-reference-common - ${project.version} ${project.groupId} a2a-java-sdk-transport-jsonrpc - ${project.version} ${project.groupId} a2a-java-sdk-server-common - ${project.version} ${project.groupId} a2a-java-sdk-tests-server-common - ${project.version} provided @@ -44,7 +40,6 @@ a2a-java-sdk-tests-server-common test-jar test - ${project.version} io.quarkus diff --git a/reference/rest/README.md b/reference/rest/README.md new file mode 100644 index 000000000..2a7f0f902 --- /dev/null +++ b/reference/rest/README.md @@ -0,0 +1,7 @@ +# A2A Java SDK Reference Server Integration + +This is a reference server for the A2A SDK for Java, that we use to run tests, as well as to demonstrate examples. + +It is based on [Quarkus](https://quarkus.io), and makes use of Quarkus's [Reactive Routes](https://quarkus.io/guides/reactive-routes). + +It is a great choice if you use Quarkus! \ No newline at end of file diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml new file mode 100644 index 000000000..e96a16971 --- /dev/null +++ b/reference/rest/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-reference-rest + + jar + + Java A2A Reference Server: JSON+HTTP/REST + Java SDK for the Agent2Agent Protocol (A2A) - A2A JSON+HTTP/REST Reference Server (based on Quarkus) + + + + ${project.groupId} + a2a-java-sdk-reference-common + + + ${project.groupId} + a2a-java-sdk-transport-rest + + + ${project.groupId} + a2a-java-sdk-server-common + + + ${project.groupId} + a2a-java-sdk-client-transport-rest + test + + + ${project.groupId} + a2a-java-sdk-tests-server-common + provided + + + ${project.groupId} + a2a-java-sdk-tests-server-common + test-jar + test + + + com.google.protobuf + protobuf-java-util + test + + + io.quarkus + quarkus-reactive-routes + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + org.slf4j + slf4j-api + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-rest-client-jackson + test + + + org.junit.jupiter + junit-jupiter-api + test + + + io.rest-assured + rest-assured + test + + + + + + maven-surefire-plugin + 3.5.3 + + + org.jboss.logmanager.LogManager + INFO + ${maven.home} + + + + + + diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java new file mode 100644 index 000000000..1a90cda75 --- /dev/null +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -0,0 +1,411 @@ +package io.a2a.server.rest.quarkus; + +import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import io.a2a.server.ServerCallContext; +import io.a2a.server.auth.UnauthenticatedUser; +import io.a2a.server.auth.User; +import io.a2a.server.util.async.Internal; +import io.a2a.spec.InternalError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.transport.rest.handler.RestHandler; +import io.a2a.transport.rest.handler.RestHandler.HTTPRestResponse; +import io.a2a.transport.rest.handler.RestHandler.HTTPRestStreamingResponse; +import io.quarkus.vertx.web.Body; +import io.quarkus.vertx.web.ReactiveRoutes; +import io.quarkus.vertx.web.Route; +import io.smallrye.mutiny.Multi; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@Singleton +public class A2AServerRoutes { + + @Inject + RestHandler jsonRestHandler; + + // Hook so testing can wait until the MultiSseSupport is subscribed. + // Without this we get intermittent failures + private static volatile Runnable streamingMultiSseSupportSubscribedRunnable; + + @Inject + @Internal + Executor executor; + + @Inject + Instance callContextFactory; + + @Route(regex = "^/v1/message:send$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void sendMessage(@Body String body, RoutingContext rc) { + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + response = jsonRestHandler.sendMessage(body, context); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + @Route(regex = "^/v1/message:stream$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void sendMessageStreaming(@Body String body, RoutingContext rc) { + ServerCallContext context = createCallContext(rc); + HTTPRestStreamingResponse streamingResponse = null; + HTTPRestResponse error = null; + try { + HTTPRestResponse response = jsonRestHandler.sendStreamingMessage(body, context); + if (response instanceof HTTPRestStreamingResponse) { + streamingResponse = (HTTPRestStreamingResponse) response; + } else { + error = response; + } + } finally { + if (error != null) { + rc.response() + .setStatusCode(error.getStatusCode()) + .putHeader(CONTENT_TYPE, APPLICATION_JSON) + .end(error.getBody()); + } else { + Multi events = Multi.createFrom().publisher(streamingResponse.getPublisher()); + executor.execute(() -> { + MultiSseSupport.subscribeObject( + events.map(i -> (Object) i), rc); + }); + } + } + } + + @Route(path = "/v1/tasks/:id", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + public void getTask(RoutingContext rc) { + String taskId = rc.pathParam("id"); + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + Integer historyLength = null; + if (rc.request().params().contains("history_length")) { + historyLength = Integer.valueOf(rc.request().params().get("history_length")); + } + response = jsonRestHandler.getTask(taskId, historyLength, context); + } catch (NumberFormatException e) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad history_length")); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + @Route(regex = "^/v1/tasks/([^/]+):cancel$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) + public void cancelTask(RoutingContext rc) { + String taskId = rc.pathParam("param0"); + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + response = jsonRestHandler.cancelTask(taskId, context); + } catch (Throwable t) { + if (t instanceof JSONRPCError error) { + response = jsonRestHandler.createErrorResponse(error); + } else { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + @Route(regex = "^/v1/tasks/([^/]+):subscribe$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) + public void resubscribeTask(RoutingContext rc) { + String taskId = rc.pathParam("param0"); + ServerCallContext context = createCallContext(rc); + HTTPRestStreamingResponse streamingResponse = null; + HTTPRestResponse error = null; + try { + HTTPRestResponse response = jsonRestHandler.resubscribeTask(taskId, context); + if (response instanceof HTTPRestStreamingResponse) { + streamingResponse = (HTTPRestStreamingResponse) response; + } else { + error = response; + } + } finally { + if (error != null) { + rc.response() + .setStatusCode(error.getStatusCode()) + .putHeader(CONTENT_TYPE, APPLICATION_JSON) + .end(error.getBody()); + } else { + Multi events = Multi.createFrom().publisher(streamingResponse.getPublisher()); + executor.execute(() -> { + MultiSseSupport.subscribeObject( + events.map(i -> (Object) i), rc); + }); + } + } + } + + @Route(path = "/v1/tasks/:id/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void setTaskPushNotificationConfiguration(@Body String body, RoutingContext rc) { + String taskId = rc.pathParam("id"); + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + response = jsonRestHandler.setTaskPushNotificationConfiguration(taskId, body, context); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + @Route(path = "/v1/tasks/:id/pushNotificationConfigs/:configId", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + public void getTaskPushNotificationConfiguration(RoutingContext rc) { + String taskId = rc.pathParam("id"); + String configId = rc.pathParam("configId"); + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, context); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + @Route(path = "/v1/tasks/:id/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + public void listTaskPushNotificationConfigurations(RoutingContext rc) { + String taskId = rc.pathParam("id"); + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, context); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + @Route(path = "/v1/tasks/:id/pushNotificationConfigs/:configId", order = 1, methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) + public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { + String taskId = rc.pathParam("id"); + String configId = rc.pathParam("configId"); + ServerCallContext context = createCallContext(rc); + HTTPRestResponse response = null; + try { + response = jsonRestHandler.deleteTaskPushNotificationConfiguration(taskId, configId, context); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + } + + /** + * /** + * Handles incoming GET requests to the agent card endpoint. + * Returns the agent card in JSON format. + * + * @param rc + */ + @Route(path = "/.well-known/agent-card.json", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) + public void getAgentCard(RoutingContext rc) { + HTTPRestResponse response = jsonRestHandler.getAgentCard(); + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + + @Route(path = "/v1/card", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) + public void getAuthenticatedExtendedCard(RoutingContext rc) { + HTTPRestResponse response = jsonRestHandler.getAuthenticatedExtendedCard(); + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + + @Route(path = "^/v1/.*", order = 100, methods = {Route.HttpMethod.DELETE, Route.HttpMethod.GET, Route.HttpMethod.HEAD, Route.HttpMethod.OPTIONS, Route.HttpMethod.POST, Route.HttpMethod.PUT}, produces = APPLICATION_JSON) + public void methodNotFoundMessage(RoutingContext rc) { + HTTPRestResponse response = jsonRestHandler.createErrorResponse(new MethodNotFoundError()); + rc.response() + .setStatusCode(response.getStatusCode()) + .putHeader(CONTENT_TYPE, response.getContentType()) + .end(response.getBody()); + } + + static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { + streamingMultiSseSupportSubscribedRunnable = runnable; + } + + private ServerCallContext createCallContext(RoutingContext rc) { + + if (callContextFactory.isUnsatisfied()) { + User user; + if (rc.user() == null) { + user = UnauthenticatedUser.INSTANCE; + } else { + user = new User() { + @Override + public boolean isAuthenticated() { + return rc.userContext().authenticated(); + } + + @Override + public String getUsername() { + return rc.user().subject(); + } + }; + } + Map state = new HashMap<>(); + // TODO Python's impl has + // state['auth'] = request.auth + // in jsonrpc_app.py. Figure out what this maps to in what Vert.X gives us + + Map headers = new HashMap<>(); + Set headerNames = rc.request().headers().names(); + headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); + state.put("headers", headers); + + return new ServerCallContext(user, state); + } else { + CallContextFactory builder = callContextFactory.get(); + return builder.build(rc); + } + } + + // Port of import io.quarkus.vertx.web.runtime.MultiSseSupport, which is considered internal API + private static class MultiSseSupport { + + private MultiSseSupport() { + // Avoid direct instantiation. + } + + private static void initialize(HttpServerResponse response) { + if (response.bytesWritten() == 0) { + MultiMap headers = response.headers(); + if (headers.get("content-type") == null) { + headers.set("content-type", "text/event-stream"); + } + response.setChunked(true); + } + } + + private static void onWriteDone(Flow.Subscription subscription, AsyncResult ar, RoutingContext rc) { + if (ar.failed()) { + rc.fail(ar.cause()); + } else { + subscription.request(1); + } + } + + public static void write(Multi multi, RoutingContext rc) { + HttpServerResponse response = rc.response(); + multi.subscribe().withSubscriber(new Flow.Subscriber() { + Flow.Subscription upstream; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.upstream = subscription; + this.upstream.request(1); + + // Notify tests that we are subscribed + Runnable runnable = streamingMultiSseSupportSubscribedRunnable; + if (runnable != null) { + runnable.run(); + } + } + + @Override + public void onNext(Buffer item) { + initialize(response); + response.write(item, new Handler>() { + @Override + public void handle(AsyncResult ar) { + onWriteDone(upstream, ar, rc); + } + }); + } + + @Override + public void onError(Throwable throwable) { + rc.fail(throwable); + } + + @Override + public void onComplete() { + endOfStream(response); + } + }); + } + + public static void subscribeObject(Multi multi, RoutingContext rc) { + AtomicLong count = new AtomicLong(); + write(multi.map(new Function() { + @Override + public Buffer apply(Object o) { + if (o instanceof ReactiveRoutes.ServerSentEvent) { + ReactiveRoutes.ServerSentEvent ev = (ReactiveRoutes.ServerSentEvent) o; + long id = ev.id() != -1 ? ev.id() : count.getAndIncrement(); + String e = ev.event() == null ? "" : "event: " + ev.event() + "\n"; + return Buffer.buffer(e + "data: " + ev.data() + "\nid: " + id + "\n\n"); + } else { + return Buffer.buffer("data: " + o + "\nid: " + count.getAndIncrement() + "\n\n"); + } + } + }), rc); + } + + private static void endOfStream(HttpServerResponse response) { + if (response.bytesWritten() == 0) { // No item + MultiMap headers = response.headers(); + if (headers.get("content-type") == null) { + headers.set("content-type", "text/event-stream"); + } + } + response.end(); + } + } + +} diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/CallContextFactory.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/CallContextFactory.java new file mode 100644 index 000000000..7aa5caf5e --- /dev/null +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/CallContextFactory.java @@ -0,0 +1,8 @@ +package io.a2a.server.rest.quarkus; + +import io.a2a.server.ServerCallContext; +import io.vertx.ext.web.RoutingContext; + +public interface CallContextFactory { + ServerCallContext build(RoutingContext rc); +} diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/QuarkusRestTransportMetadata.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/QuarkusRestTransportMetadata.java new file mode 100644 index 000000000..ee9d3ae98 --- /dev/null +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/QuarkusRestTransportMetadata.java @@ -0,0 +1,11 @@ +package io.a2a.server.rest.quarkus; + +import io.a2a.server.TransportMetadata; +import io.a2a.spec.TransportProtocol; + +public class QuarkusRestTransportMetadata implements TransportMetadata { + @Override + public String getTransportProtocol() { + return TransportProtocol.HTTP_JSON.asString(); + } +} diff --git a/reference/rest/src/main/resources/META-INF/beans.xml b/reference/rest/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..e69de29bb diff --git a/reference/rest/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata b/reference/rest/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata new file mode 100644 index 000000000..cb50024df --- /dev/null +++ b/reference/rest/src/main/resources/META-INF/services/io.a2a.server.TransportMetadata @@ -0,0 +1 @@ +io.a2a.server.rest.quarkus.QuarkusRestTransportMetadata \ No newline at end of file diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java new file mode 100644 index 000000000..24d49c9a0 --- /dev/null +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java @@ -0,0 +1,190 @@ +package io.a2a.server.rest.quarkus; + +import io.a2a.server.rest.quarkus.A2AServerRoutes; + +import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; + +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import io.a2a.server.apps.common.TestUtilsBean; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.util.Utils; +import io.quarkus.vertx.web.Body; +import io.quarkus.vertx.web.Param; +import io.quarkus.vertx.web.Route; +import io.vertx.ext.web.RoutingContext; + +/** + * Exposes the {@link TestUtilsBean} via REST using Quarkus Reactive Routes + */ +@Singleton +public class A2ATestRoutes { + @Inject + TestUtilsBean testUtilsBean; + + @Inject + A2AServerRoutes a2AServerRoutes; + + AtomicInteger streamingSubscribedCount = new AtomicInteger(0); + + @PostConstruct + public void init() { + A2AServerRoutes.setStreamingMultiSseSupportSubscribedRunnable(() -> streamingSubscribedCount.incrementAndGet()); + } + + + @Route(path = "/test/task", methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void saveTask(@Body String body, RoutingContext rc) { + try { + Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class); + testUtilsBean.saveTask(task); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.GET}, produces = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + public void getTask(@Param String taskId, RoutingContext rc) { + try { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + rc.response() + .setStatusCode(200) + .putHeader(CONTENT_TYPE, APPLICATION_JSON) + .end(Utils.OBJECT_MAPPER.writeValueAsString(task)); + + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) + public void deleteTask(@Param String taskId, RoutingContext rc) { + try { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + testUtilsBean.deleteTask(taskId); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/queue/ensure/:taskId", methods = {Route.HttpMethod.POST}) + public void ensureTaskQueue(@Param String taskId, RoutingContext rc) { + try { + testUtilsBean.ensureQueue(taskId); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/queue/enqueueTaskStatusUpdateEvent/:taskId", methods = {Route.HttpMethod.POST}) + public void enqueueTaskStatusUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { + + try { + TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class); + testUtilsBean.enqueueEvent(taskId, event); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/queue/enqueueTaskArtifactUpdateEvent/:taskId", methods = {Route.HttpMethod.POST}) + public void enqueueTaskArtifactUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { + + try { + TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class); + testUtilsBean.enqueueEvent(taskId, event); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/streamingSubscribedCount", methods = {Route.HttpMethod.GET}, produces = {TEXT_PLAIN}) + public void getStreamingSubscribedCount(RoutingContext rc) { + rc.response() + .setStatusCode(200) + .end(String.valueOf(streamingSubscribedCount.get())); + } + + @Route(path = "/test/task/:taskId/config/:configId", methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) + public void deleteTaskPushNotificationConfig(@Param String taskId, @Param String configId, RoutingContext rc) { + try { + Task task = testUtilsBean.getTask(taskId); + if (task == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + testUtilsBean.deleteTaskPushNotificationConfig(taskId, configId); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) + public void saveTaskPushNotificationConfig(@Param String taskId, @Body String body, RoutingContext rc) { + try { + PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class); + if (notificationConfig == null) { + rc.response() + .setStatusCode(404) + .end(); + return; + } + testUtilsBean.saveTaskPushNotificationConfig(taskId, notificationConfig); + rc.response() + .setStatusCode(200) + .end(); + } catch (Throwable t) { + errorResponse(t, rc); + } + } + + private void errorResponse(Throwable t, RoutingContext rc) { + t.printStackTrace(); + rc.response() + .setStatusCode(500) + .putHeader(CONTENT_TYPE, TEXT_PLAIN) + .end(); + } + +} diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java new file mode 100644 index 000000000..f2231ffc8 --- /dev/null +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java @@ -0,0 +1,60 @@ +package io.a2a.server.rest.quarkus; + +import static io.a2a.server.apps.common.AbstractA2AServerTest.APPLICATION_JSON; + +import io.a2a.client.ClientBuilder; +import io.a2a.client.transport.rest.RestTransport; +import io.a2a.client.transport.rest.RestTransportConfigBuilder; +import io.a2a.server.apps.common.AbstractA2AServerTest; +import io.a2a.spec.TransportProtocol; +import io.quarkus.test.junit.QuarkusTest; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class QuarkusA2ARestTest extends AbstractA2AServerTest { + + public QuarkusA2ARestTest() { + super(8081); + } + + @Override + protected String getTransportProtocol() { + return TransportProtocol.HTTP_JSON.asString(); + } + + @Override + protected String getTransportUrl() { + return "http://localhost:8081"; + } + + @Override + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(RestTransport.class, new RestTransportConfigBuilder()); + } + @Test + public void testMethodNotFound() throws Exception { + + // Create the client + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + // Create the request + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/v1/message:send")) + .PUT(HttpRequest.BodyPublishers.ofString("test")) + .header("Content-Type", APPLICATION_JSON); + HttpResponse response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString()); + Assertions.assertEquals(405, response.statusCode()); + builder = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/v1/message:send")) + .DELETE() + .header("Content-Type", APPLICATION_JSON); + response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString()); + Assertions.assertEquals(405, response.statusCode()); + } +} diff --git a/reference/rest/src/test/resources/application.properties b/reference/rest/src/test/resources/application.properties new file mode 100644 index 000000000..d3366bece --- /dev/null +++ b/reference/rest/src/test/resources/application.properties @@ -0,0 +1 @@ +quarkus.arc.selected-alternatives=io.a2a.server.apps.common.TestHttpClient \ No newline at end of file diff --git a/server-common/pom.xml b/server-common/pom.xml index 1bcc2b7b9..7f1d810f6 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -20,17 +20,14 @@ ${project.groupId} a2a-java-sdk-spec - ${project.version} ${project.groupId} a2a-java-sdk-http-client - ${project.version} ${project.groupId} a2a-java-sdk-client-transport-jsonrpc - ${project.version} com.fasterxml.jackson.core diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index e45bc3c62..50ba260c0 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -20,6 +20,9 @@ public interface RequestHandler { Task onGetTask( TaskQueryParams params, ServerCallContext context) throws JSONRPCError; +// +// List onListTask(ServerCallContext context) +// throws JSONRPCError; Task onCancelTask( TaskIdParams params, diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index 451b60451..4ae6520c6 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -75,4 +75,4 @@ public void deleteInfo(String taskId, String configId) { pushNotificationInfos.remove(taskId); } } -} +} \ No newline at end of file diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 5cb42d8bb..78d0567fb 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -1,8 +1,5 @@ package io.a2a.server.tasks; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java index 73ac8f38f..26f66f023 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java @@ -1,5 +1,6 @@ package io.a2a.server.tasks; + import io.a2a.spec.Task; public interface TaskStore { diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index acef022c3..9f12ee792 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -37,6 +37,7 @@ import io.a2a.spec.TextPart; import io.a2a.util.Utils; import io.quarkus.arc.profile.IfBuildProfile; +import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -161,6 +162,11 @@ public PostBuilder createPost() { return new TestHttpClient.TestPostBuilder(); } + @Override + public DeleteBuilder createDelete() { + return null; + } + class TestPostBuilder implements A2AHttpClient.PostBuilder { private volatile String body; @Override @@ -209,6 +215,11 @@ public PostBuilder addHeader(String name, String value) { return this; } + @Override + public PostBuilder addHeaders(Map headers) { + return this; + } + } } } diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index d72663392..ea5ac3fba 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -20,7 +20,6 @@ ${project.groupId} a2a-java-sdk-spec - ${project.version} com.google.protobuf @@ -29,10 +28,12 @@ io.grpc grpc-protobuf + provided io.grpc grpc-stub + provided jakarta.enterprise diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 1b5bc8f17..5572cf440 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -13,6 +13,7 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Struct; import com.google.protobuf.Value; +import io.a2a.grpc.ListTaskPushNotificationConfigResponse; import io.a2a.grpc.StreamResponse; import io.a2a.spec.APIKeySecurityScheme; @@ -167,9 +168,9 @@ public static io.a2a.grpc.Message message(Message message) { } public static io.a2a.grpc.TaskPushNotificationConfig taskPushNotificationConfig(TaskPushNotificationConfig config) { + String id = config.pushNotificationConfig().id(); io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); - String configId = config.pushNotificationConfig().id(); - builder.setName("tasks/" + config.taskId() + "/pushNotificationConfigs/" + (configId != null ? configId : config.taskId())); + builder.setName("tasks/" + config.taskId() + "/pushNotificationConfigs" + (id == null ? "" : ('/' + id))); builder.setPushNotificationConfig(pushNotificationConfig(config.pushNotificationConfig())); return builder.build(); } @@ -367,6 +368,17 @@ private static io.a2a.grpc.AgentCapabilities agentCapabilities(AgentCapabilities return builder.build(); } + public static io.a2a.grpc.SendMessageRequest sendMessageRequest(MessageSendParams request) { + io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); + builder.setRequest(message(request.message())); + if (request.configuration() != null) { + builder.setConfiguration(messageSendConfiguration(request.configuration())); + } + if (request.metadata() != null && ! request.metadata().isEmpty()) { + builder.setMetadata(struct(request.metadata())); + } + return builder.build(); + } private static io.a2a.grpc.AgentExtension agentExtension(AgentExtension agentExtension) { io.a2a.grpc.AgentExtension.Builder builder = io.a2a.grpc.AgentExtension.newBuilder(); if (agentExtension.description() != null) { @@ -521,6 +533,15 @@ private static io.a2a.grpc.AuthorizationCodeOAuthFlow authorizationCodeOAuthFlow return builder.build(); } + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfigResponse(List configs) { + List confs = new ArrayList<>(configs.size()); + ListTaskPushNotificationConfigResponse.Builder response = ListTaskPushNotificationConfigResponse.newBuilder(); + for(TaskPushNotificationConfig config: configs) { + confs.add(taskPushNotificationConfig(config)); + } + return io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder().addAllConfigs(confs).build(); + } + private static io.a2a.grpc.ClientCredentialsOAuthFlow clientCredentialsOAuthFlow(ClientCredentialsOAuthFlow clientCredentialsOAuthFlow) { io.a2a.grpc.ClientCredentialsOAuthFlow.Builder builder = io.a2a.grpc.ClientCredentialsOAuthFlow.newBuilder(); if (clientCredentialsOAuthFlow.refreshUrl() != null) { @@ -661,23 +682,45 @@ public static io.a2a.grpc.SendMessageResponse taskOrMessage(EventKind eventKind) } } + public static io.a2a.grpc.StreamResponse taskOrMessageStream(StreamingEventKind eventKind) { + if (eventKind instanceof Task task) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setTask(task(task)) + .build(); + } else if (eventKind instanceof Message msg) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setMsg(message(msg)) + .build(); + } else if (eventKind instanceof TaskArtifactUpdateEvent update) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setArtifactUpdate(taskArtifactUpdateEvent(update)) + .build(); + } else if (eventKind instanceof TaskStatusUpdateEvent update) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setStatusUpdate(taskStatusUpdateEvent(update)) + .build(); + } else { + throw new IllegalArgumentException("Unsupported event type: " + eventKind); + } + } + } public static class FromProto { - public static TaskQueryParams taskQueryParams(io.a2a.grpc.GetTaskRequest request) { + public static TaskQueryParams taskQueryParams(io.a2a.grpc.GetTaskRequestOrBuilder request) { String name = request.getName(); String id = name.substring(name.lastIndexOf('/') + 1); return new TaskQueryParams(id, request.getHistoryLength()); } - public static TaskIdParams taskIdParams(io.a2a.grpc.CancelTaskRequest request) { + public static TaskIdParams taskIdParams(io.a2a.grpc.CancelTaskRequestOrBuilder request) { String name = request.getName(); String id = name.substring(name.lastIndexOf('/') + 1); return new TaskIdParams(id); } - public static MessageSendParams messageSendParams(io.a2a.grpc.SendMessageRequest request) { + public static MessageSendParams messageSendParams(io.a2a.grpc.SendMessageRequestOrBuilder request) { MessageSendParams.Builder builder = new MessageSendParams.Builder(); builder.message(message(request.getRequest())); if (request.hasConfiguration()) { @@ -700,6 +743,7 @@ public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc. private static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfigOrBuilder config, boolean create) { String name = config.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" String[] parts = name.split("/"); + String taskId = parts[1]; String configId = ""; if (create) { if (parts.length < 3) { @@ -707,6 +751,8 @@ private static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc } if (parts.length == 4) { configId = parts[3]; + } else { + configId = taskId; } } else { if (parts.length < 4) { @@ -714,12 +760,11 @@ private static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc } configId = parts[3]; } - String taskId = parts[1]; PushNotificationConfig pnc = pushNotification(config.getPushNotificationConfig(), configId); return new TaskPushNotificationConfig(taskId, pnc); } - public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { + public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams(io.a2a.grpc.GetTaskPushNotificationConfigRequestOrBuilder request) { String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" String[] parts = name.split("/"); String taskId = parts[1]; @@ -734,19 +779,28 @@ public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigP return new GetTaskPushNotificationConfigParams(taskId, configId); } - public static TaskIdParams taskIdParams(io.a2a.grpc.TaskSubscriptionRequest request) { + public static TaskIdParams taskIdParams(io.a2a.grpc.TaskSubscriptionRequestOrBuilder request) { String name = request.getName(); String id = name.substring(name.lastIndexOf('/') + 1); return new TaskIdParams(id); } - public static ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigRequest request) { + public static List listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigResponseOrBuilder response) { + List configs = response.getConfigsList(); + List result = new ArrayList<>(configs.size()); + for(io.a2a.grpc.TaskPushNotificationConfig config : configs) { + result.add(taskPushNotificationConfig(config, false)); + } + return result; + } + + public static ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigRequestOrBuilder request) { String parent = request.getParent(); String id = parent.substring(parent.lastIndexOf('/') + 1); return new ListTaskPushNotificationConfigParams(id); } - public static DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) { + public static DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams(io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder request) { String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" String[] parts = name.split("/"); if (parts.length < 4) { @@ -757,13 +811,13 @@ public static DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationC return new DeleteTaskPushNotificationConfigParams(taskId, configId); } - private static AgentCapabilities agentCapabilities(io.a2a.grpc.AgentCapabilities agentCapabilities) { + private static AgentCapabilities agentCapabilities(io.a2a.grpc.AgentCapabilitiesOrBuilder agentCapabilities) { return new AgentCapabilities(agentCapabilities.getStreaming(), agentCapabilities.getPushNotifications(), false, agentCapabilities.getExtensionsList().stream().map(item -> agentExtension(item)).collect(Collectors.toList()) ); } - private static AgentExtension agentExtension(io.a2a.grpc.AgentExtension agentExtension) { + private static AgentExtension agentExtension(io.a2a.grpc.AgentExtensionOrBuilder agentExtension) { return new AgentExtension( agentExtension.getDescription(), struct(agentExtension.getParams()), @@ -772,7 +826,7 @@ private static AgentExtension agentExtension(io.a2a.grpc.AgentExtension agentExt ); } - private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.SendMessageConfiguration sendMessageConfiguration) { + private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.SendMessageConfigurationOrBuilder sendMessageConfiguration) { return new MessageSendConfiguration( sendMessageConfiguration.getAcceptedOutputModesList().isEmpty() ? null : new ArrayList<>(sendMessageConfiguration.getAcceptedOutputModesList()), @@ -782,8 +836,8 @@ private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.Sen ); } - private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification, String configId) { - if (pushNotification == null || pushNotification.getDefaultInstanceForType().equals(pushNotification)) { + private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification, String configId) { + if(pushNotification == null || pushNotification.getDefaultInstanceForType().equals(pushNotification)) { return null; } return new PushNotificationConfig( @@ -794,18 +848,18 @@ private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificat ); } - private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfig pushNotification) { + private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification) { return pushNotification(pushNotification, pushNotification.getId()); } - private static PushNotificationAuthenticationInfo authenticationInfo(io.a2a.grpc.AuthenticationInfo authenticationInfo) { + private static PushNotificationAuthenticationInfo authenticationInfo(io.a2a.grpc.AuthenticationInfoOrBuilder authenticationInfo) { return new PushNotificationAuthenticationInfo( new ArrayList<>(authenticationInfo.getSchemesList()), authenticationInfo.getCredentials() ); } - public static Task task(io.a2a.grpc.Task task) { + public static Task task(io.a2a.grpc.TaskOrBuilder task) { return new Task( task.getId(), task.getContextId(), @@ -816,7 +870,7 @@ public static Task task(io.a2a.grpc.Task task) { ); } - public static Message message(io.a2a.grpc.Message message) { + public static Message message(io.a2a.grpc.MessageOrBuilder message) { if (message.getMessageId().isEmpty()) { throw new InvalidParamsError(); } @@ -832,7 +886,7 @@ public static Message message(io.a2a.grpc.Message message) { ); } - public static TaskStatusUpdateEvent taskStatusUpdateEvent(io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent) { + public static TaskStatusUpdateEvent taskStatusUpdateEvent(io.a2a.grpc.TaskStatusUpdateEventOrBuilder taskStatusUpdateEvent) { return new TaskStatusUpdateEvent.Builder() .taskId(taskStatusUpdateEvent.getTaskId()) .status(taskStatus(taskStatusUpdateEvent.getStatus())) @@ -842,7 +896,7 @@ public static TaskStatusUpdateEvent taskStatusUpdateEvent(io.a2a.grpc.TaskStatus .build(); } - public static TaskArtifactUpdateEvent taskArtifactUpdateEvent(io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent) { + public static TaskArtifactUpdateEvent taskArtifactUpdateEvent(io.a2a.grpc.TaskArtifactUpdateEventOrBuilder taskArtifactUpdateEvent) { return new TaskArtifactUpdateEvent.Builder() .taskId(taskArtifactUpdateEvent.getTaskId()) .append(taskArtifactUpdateEvent.getAppend()) @@ -853,7 +907,7 @@ public static TaskArtifactUpdateEvent taskArtifactUpdateEvent(io.a2a.grpc.TaskAr .build(); } - private static Artifact artifact(io.a2a.grpc.Artifact artifact) { + private static Artifact artifact(io.a2a.grpc.ArtifactOrBuilder artifact) { return new Artifact( artifact.getArtifactId(), artifact.getName(), @@ -863,7 +917,7 @@ private static Artifact artifact(io.a2a.grpc.Artifact artifact) { ); } - private static Part part(io.a2a.grpc.Part part) { + private static Part part(io.a2a.grpc.PartOrBuilder part) { if (part.hasText()) { return textPart(part.getText()); } else if (part.hasFile()) { @@ -878,7 +932,7 @@ private static TextPart textPart(String text) { return new TextPart(text); } - private static FilePart filePart(io.a2a.grpc.FilePart filePart) { + private static FilePart filePart(io.a2a.grpc.FilePartOrBuilder filePart) { if (filePart.hasFileWithBytes()) { return new FilePart(new FileWithBytes(filePart.getMimeType(), null, filePart.getFileWithBytes().toStringUtf8())); } else if (filePart.hasFileWithUri()) { @@ -887,17 +941,18 @@ private static FilePart filePart(io.a2a.grpc.FilePart filePart) { throw new InvalidRequestError(); } - private static DataPart dataPart(io.a2a.grpc.DataPart dataPart) { + private static DataPart dataPart(io.a2a.grpc.DataPartOrBuilder dataPart) { return new DataPart(struct(dataPart.getData())); } - private static TaskStatus taskStatus(io.a2a.grpc.TaskStatus taskStatus) { + private static TaskStatus taskStatus(io.a2a.grpc.TaskStatusOrBuilder taskStatus) { TaskState state = taskState(taskStatus.getState()); if (state == null) { return null; } - return new TaskStatus(state, - taskStatus.hasUpdate() ? message(taskStatus.getUpdate()) : null, + return new TaskStatus( + taskState(taskStatus.getState()), + taskStatus.hasUpdate() ? message(taskStatus.getUpdateOrBuilder()) : null, OffsetDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) ); } diff --git a/spec/pom.xml b/spec/pom.xml index 13a880aa0..bd06dd1df 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -20,7 +20,6 @@ ${project.groupId} a2a-java-sdk-common - ${project.version} diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index c9e982910..ec702130a 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -93,4 +93,6 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event .build(); } + + } diff --git a/tck/pom.xml b/tck/pom.xml index e03ef70da..85a4b215a 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -17,14 +17,16 @@ - io.github.a2asdk + ${project.groupId} a2a-java-sdk-reference-jsonrpc - ${project.version} io.github.a2asdk a2a-java-sdk-reference-grpc - ${project.version} + + + io.github.a2asdk + a2a-java-sdk-reference-rest io.quarkus diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 68e6c7e26..2ab3d9a4e 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -1,6 +1,5 @@ package io.a2a.tck.server; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -22,8 +21,10 @@ public class AgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { + String sutJsonRpcUrl = getEnvOrDefault("SUT_JSONRPC_URL", DEFAULT_SUT_URL); String sutGrpcUrl = getEnvOrDefault("SUT_GRPC_URL", DEFAULT_SUT_URL); + String sutRestcUrl = getEnvOrDefault("SUT_REST_URL", DEFAULT_SUT_URL); return new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") @@ -47,7 +48,8 @@ public AgentCard agentCard() { .protocolVersion("0.3.0") .additionalInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), sutJsonRpcUrl), - new AgentInterface(TransportProtocol.GRPC.asString(), sutGrpcUrl))) + new AgentInterface(TransportProtocol.GRPC.asString(), sutGrpcUrl), + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), sutRestcUrl))) .build(); } diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index fb6cbc5db..1d5234b4e 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -21,17 +21,14 @@ ${project.groupId} a2a-java-sdk-spec - ${project.version} ${project.groupId} a2a-java-sdk-client - ${project.version} ${project.groupId} a2a-java-sdk-server-common - ${project.version} jakarta.ws.rs @@ -56,13 +53,16 @@ io.github.a2asdk a2a-java-sdk-client-transport-jsonrpc - ${project.version} test io.github.a2asdk a2a-java-sdk-client-transport-grpc - ${project.version} + test + + + io.github.a2asdk + a2a-java-sdk-client-transport-rest test diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java index 87d2e536b..f161307aa 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java @@ -15,6 +15,7 @@ import io.a2a.client.http.A2AHttpResponse; import io.a2a.spec.Task; import io.a2a.util.Utils; +import java.util.Map; @Dependent @Alternative @@ -32,6 +33,11 @@ public PostBuilder createPost() { return new TestPostBuilder(); } + @Override + public DeleteBuilder createDelete() { + return null; + } + class TestPostBuilder implements A2AHttpClient.PostBuilder { private volatile String body; @Override @@ -79,5 +85,10 @@ public PostBuilder url(String s) { public PostBuilder addHeader(String name, String value) { return this; } + + @Override + public PostBuilder addHeaders(Map headers) { + return this; + } } } \ No newline at end of file diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 93ec2a095..76d72db24 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -21,19 +21,16 @@ io.github.a2asdk a2a-java-sdk-server-common - ${project.version} ${project.groupId} a2a-java-sdk-server-common - ${project.version} test-jar test ${project.groupId} a2a-java-sdk-spec-grpc - ${project.version} com.google.protobuf diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 15eecd06e..3565d042f 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -19,14 +19,12 @@ - io.github.a2asdk + ${project.groupId} a2a-java-sdk-server-common - ${project.version} ${project.groupId} a2a-java-sdk-server-common - ${project.version} test-jar test diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml new file mode 100644 index 000000000..712abbce4 --- /dev/null +++ b/transport/rest/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-transport-rest + + jar + + Java SDK A2A Transport: JSON+HTTP/REST + Java SDK for the Agent2Agent Protocol (A2A) - JSON+HTTP/REST Transport + + + + io.github.a2asdk + a2a-java-sdk-server-common + + + io.github.a2asdk + a2a-java-sdk-spec-grpc + + + io.github.a2asdk + a2a-java-sdk-spec + + + ${project.groupId} + a2a-java-sdk-server-common + test-jar + test + + + ch.qos.logback + logback-classic + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.google.protobuf + protobuf-java-util + + + org.slf4j + slf4j-jdk14 + test + + + + + \ No newline at end of file diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java new file mode 100644 index 000000000..f6029b266 --- /dev/null +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -0,0 +1,427 @@ +package io.a2a.transport.rest.handler; + +import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; + +import com.fasterxml.jackson.core.JacksonException; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.server.ExtendedAgentCard; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; +import java.util.concurrent.Flow; + +import io.a2a.server.PublicAgentCard; +import io.a2a.server.ServerCallContext; +import io.a2a.server.requesthandlers.RequestHandler; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; +import io.a2a.spec.ContentTypeNotSupportedError; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.EventKind; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.InternalError; +import io.a2a.spec.InvalidAgentResponseError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskNotFoundError; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.UnsupportedOperationError; +import io.a2a.util.Utils; +import jakarta.enterprise.inject.Instance; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; +import mutiny.zero.ZeroPublisher; + +@ApplicationScoped +public class RestHandler { + + private static final Logger log = Logger.getLogger(RestHandler.class.getName()); + private AgentCard agentCard; + private Instance extendedAgentCard; + private RequestHandler requestHandler; + + protected RestHandler() { + // For CDI + } + + @Inject + public RestHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard Instance extendedAgentCard, + RequestHandler requestHandler) { + this.agentCard = agentCard; + this.extendedAgentCard = extendedAgentCard; + this.requestHandler = requestHandler; + } + + public RestHandler(AgentCard agentCard, RequestHandler requestHandler) { + this.agentCard = agentCard; + this.requestHandler = requestHandler; + } + + public HTTPRestResponse sendMessage(String body, ServerCallContext context) { + try { + io.a2a.grpc.SendMessageRequest.Builder request = io.a2a.grpc.SendMessageRequest.newBuilder(); + parseRequestBody(body, request); + EventKind result = requestHandler.onMessageSend(ProtoUtils.FromProto.messageSendParams(request), context); + return createSuccessResponse(200, io.a2a.grpc.SendMessageResponse.newBuilder(ProtoUtils.ToProto.taskOrMessage(result))); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + public HTTPRestResponse sendStreamingMessage(String body, ServerCallContext context) { + try { + if (!agentCard.capabilities().streaming()) { + return createErrorResponse(new InvalidRequestError("Streaming is not supported by the agent")); + } + io.a2a.grpc.SendMessageRequest.Builder request = io.a2a.grpc.SendMessageRequest.newBuilder(); + parseRequestBody(body, request); + Flow.Publisher publisher = requestHandler.onMessageSendStream(ProtoUtils.FromProto.messageSendParams(request), context); + return createStreamingResponse(publisher); + } catch (JSONRPCError e) { + return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(e).toJson())); + } catch (Throwable throwable) { + return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(new InternalError(throwable.getMessage())).toJson())); + } + } + + public HTTPRestResponse cancelTask(String taskId, ServerCallContext context) { + try { + if(taskId == null || taskId.isEmpty()) { + throw new InvalidParamsError(); + } + TaskIdParams params = new TaskIdParams(taskId); + Task task = requestHandler.onCancelTask(params, context); + if (task != null) { + return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); + } + throw new UnsupportedOperationError(); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, String body, ServerCallContext context) { + try { + if (!agentCard.capabilities().pushNotifications()) { + throw new PushNotificationNotSupportedError(); + } + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder(); + parseRequestBody(body, builder); + TaskPushNotificationConfig result = requestHandler.onSetTaskPushNotificationConfig(ProtoUtils.FromProto.taskPushNotificationConfig(builder), context); + return createSuccessResponse(201, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(result))); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + public HTTPRestResponse resubscribeTask(String taskId, ServerCallContext context) { + try { + if (!agentCard.capabilities().streaming()) { + return createErrorResponse(new InvalidRequestError("Streaming is not supported by the agent")); + } + TaskIdParams params = new TaskIdParams(taskId); + Flow.Publisher publisher = requestHandler.onResubscribeToTask(params, context); + return createStreamingResponse(publisher); + } catch (JSONRPCError e) { + return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(e).toJson())); + } catch (Throwable throwable) { + return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(new InternalError(throwable.getMessage())).toJson())); + } + } + + public HTTPRestResponse getTask(String taskId, Integer historyLength, ServerCallContext context) { + try { + TaskQueryParams params = new TaskQueryParams(taskId,historyLength); + Task task = requestHandler.onGetTask(params, context); + if (task != null) { + return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); + } + throw new TaskNotFoundError(); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, String configId, ServerCallContext context) { + try { + if (!agentCard.capabilities().pushNotifications()) { + throw new PushNotificationNotSupportedError(); + } + GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams(taskId, configId); + TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(params, context); + return createSuccessResponse(200, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(config))); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, ServerCallContext context) { + try { + if (!agentCard.capabilities().pushNotifications()) { + throw new PushNotificationNotSupportedError(); + } + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId); + List configs = requestHandler.onListTaskPushNotificationConfig(params, context); + return createSuccessResponse(200, io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(ProtoUtils.ToProto.listTaskPushNotificationConfigResponse(configs))); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + public HTTPRestResponse deleteTaskPushNotificationConfiguration(String taskId, String configId, ServerCallContext context) { + try { + if (!agentCard.capabilities().pushNotifications()) { + throw new PushNotificationNotSupportedError(); + } + DeleteTaskPushNotificationConfigParams params = new DeleteTaskPushNotificationConfigParams(taskId, configId); + requestHandler.onDeleteTaskPushNotificationConfig(params, context); + return new HTTPRestResponse(204, "application/json", ""); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + + private void parseRequestBody(String body, com.google.protobuf.Message.Builder builder) throws JSONRPCError { + try { + if (body == null || body.trim().isEmpty()) { + throw new InvalidRequestError("Request body is required"); + } + validate(body); + JsonFormat.parser().merge(body, builder); + } catch (InvalidProtocolBufferException e) { + log.log(Level.SEVERE, "Error parsing JSON request body: {0}", body); + log.log(Level.SEVERE, "Parse error details", e); + throw new InvalidParamsError("Failed to parse request body: " + e.getMessage()); + } + } + + private void validate(String json) { + try { + Utils.OBJECT_MAPPER.readTree(json); + } catch (JacksonException e) { + throw new JSONParseError(JSONParseError.DEFAULT_CODE, "Failed to parse json", e.getMessage()); + } + } + + private HTTPRestResponse createSuccessResponse(int statusCode, com.google.protobuf.Message.Builder builder) { + try { + String jsonBody = JsonFormat.printer().print(builder); + return new HTTPRestResponse(statusCode, "application/json", jsonBody); + } catch (InvalidProtocolBufferException e) { + return createErrorResponse(new InternalError("Failed to serialize response: " + e.getMessage())); + } + } + + public HTTPRestResponse createErrorResponse(JSONRPCError error) { + int statusCode = mapErrorToHttpStatus(error); + return createErrorResponse(statusCode, error); + } + + private HTTPRestResponse createErrorResponse(int statusCode, JSONRPCError error) { + String jsonBody = new HTTPRestErrorResponse(error).toJson(); + return new HTTPRestResponse(statusCode, "application/json", jsonBody); + } + + private HTTPRestStreamingResponse createStreamingResponse(Flow.Publisher publisher) { + return new HTTPRestStreamingResponse(convertToSendStreamingMessageResponse(publisher)); + } + + private Flow.Publisher convertToSendStreamingMessageResponse( + Flow.Publisher publisher) { + // We can't use the normal convertingProcessor since that propagates any errors as an error handled + // via Subscriber.onError() rather than as part of the SendStreamingResponse payload + return ZeroPublisher.create(createTubeConfig(), tube -> { + CompletableFuture.runAsync(() -> { + publisher.subscribe(new Flow.Subscriber() { + Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(StreamingEventKind item) { + try { + String payload = JsonFormat.printer().omittingInsignificantWhitespace().print(ProtoUtils.ToProto.taskOrMessageStream(item)); + tube.send(payload); + subscription.request(1); + } catch (InvalidProtocolBufferException ex) { + onError(ex); + } + } + + @Override + public void onError(Throwable throwable) { + if (throwable instanceof JSONRPCError jsonrpcError) { + tube.send(new HTTPRestErrorResponse(jsonrpcError).toJson()); + } else { + tube.send(new HTTPRestErrorResponse(new InternalError(throwable.getMessage())).toJson()); + } + onComplete(); + } + + @Override + public void onComplete() { + tube.complete(); + } + }); + }); + }); + } + + private int mapErrorToHttpStatus(JSONRPCError error) { + if (error instanceof InvalidRequestError || error instanceof JSONParseError) { + return 400; + } + if (error instanceof InvalidParamsError) { + return 422; + } + if (error instanceof MethodNotFoundError || error instanceof TaskNotFoundError || error instanceof AuthenticatedExtendedCardNotConfiguredError) { + return 404; + } + if (error instanceof TaskNotCancelableError) { + return 409; + } + if (error instanceof PushNotificationNotSupportedError || error instanceof UnsupportedOperationError) { + return 501; + } + if (error instanceof ContentTypeNotSupportedError) { + return 415; + } + if (error instanceof InvalidAgentResponseError) { + return 502; + } + if (error instanceof InternalError ) { + return 500; + } + return 500; + } + + public HTTPRestResponse getAuthenticatedExtendedCard() { + try { + if (!agentCard.supportsAuthenticatedExtendedCard() || !extendedAgentCard.isResolvable()) { + throw new AuthenticatedExtendedCardNotConfiguredError(); + } + return new HTTPRestResponse(200, "application/json", Utils.OBJECT_MAPPER.writeValueAsString(extendedAgentCard.get())); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable t) { + return createErrorResponse(500, new InternalError(t.getMessage())); + } + } + + public HTTPRestResponse getAgentCard() { + try { + return new HTTPRestResponse(200, "application/json", Utils.OBJECT_MAPPER.writeValueAsString(agentCard)); + } catch (Throwable t) { + return createErrorResponse(500, new InternalError(t.getMessage())); + } + } + + public static class HTTPRestResponse { + + private final int statusCode; + private final String contentType; + private final String body; + + public HTTPRestResponse(int statusCode, String contentType, String body) { + this.statusCode = statusCode; + this.contentType = contentType; + this.body = body; + } + + public int getStatusCode() { + return statusCode; + } + + public String getContentType() { + return contentType; + } + + public String getBody() { + return body; + } + + @Override + public String toString() { + return "HTTPRestResponse{" + "statusCode=" + statusCode + ", contentType=" + contentType + ", body=" + body + '}'; + } + } + + public static class HTTPRestStreamingResponse extends HTTPRestResponse { + + private final Flow.Publisher publisher; + + public HTTPRestStreamingResponse(Flow.Publisher publisher) { + super(200, "text/event-stream", null); + this.publisher = publisher; + } + + public Flow.Publisher getPublisher() { + return publisher; + } + } + + private static class HTTPRestErrorResponse { + + private final String error; + private final String message; + + public HTTPRestErrorResponse(String error, String message) { + this.error = error; + this.message = message; + } + + public HTTPRestErrorResponse(JSONRPCError jsonRpcError) { + this.error = jsonRpcError.getClass().getName(); + this.message = jsonRpcError.getMessage(); + } + + public String getError() { + return error; + } + + public String getMessage() { + return message; + } + + public String toJson() { + return "{\"error\": \"" + error + "\", \"message\": \"" + message + "\"}"; + } + + @Override + public String toString() { + return "HTTPRestErrorResponse{" + "error=" + error + ", message=" + message + '}'; + } + } +} diff --git a/transport/rest/src/main/resources/META-INF/beans.xml b/transport/rest/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..0d9b1c17d --- /dev/null +++ b/transport/rest/src/main/resources/META-INF/beans.xml @@ -0,0 +1,7 @@ + + + diff --git a/transport/rest/src/main/resources/a2a-requesthandler-test.properties b/transport/rest/src/main/resources/a2a-requesthandler-test.properties new file mode 100644 index 000000000..723a7f87f --- /dev/null +++ b/transport/rest/src/main/resources/a2a-requesthandler-test.properties @@ -0,0 +1 @@ +preferred-transport=HTTP_JSON \ No newline at end of file diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java new file mode 100644 index 000000000..d8b355dc3 --- /dev/null +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -0,0 +1,403 @@ +package io.a2a.transport.rest.handler; + +import com.google.protobuf.InvalidProtocolBufferException; +import java.util.Map; + +import io.a2a.server.ServerCallContext; +import io.a2a.server.auth.UnauthenticatedUser; +import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Task; +import io.a2a.server.tasks.TaskUpdater; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RestHandlerTest extends AbstractA2ARequestHandlerTest { + + private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar")); + + @Test + public void testGetTaskSuccess() { + RestHandler handler = new RestHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(),null, callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); + + response = handler.getTask(MINIMAL_TASK.getId(),2 , callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); + } + + @Test + public void testGetTaskNotFound() { + RestHandler handler = new RestHandler(CARD, requestHandler); + + RestHandler.HTTPRestResponse response = handler.getTask("nonexistent", null, callContext); + + Assertions.assertEquals(404, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("TaskNotFoundError")); + } + + @Test + public void testSendMessage() throws InvalidProtocolBufferException { + RestHandler handler = new RestHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getMessage()); + }; + String requestBody = """ + { + "message": + { + "messageId": "message-1234", + "contextId": "context-1234", + "role": "ROLE_USER", + "content": [{ + "text": "tell me a joke" + }], + "metadata": { + } + }, + "configuration": + { + "blocking": true + } + }"""; + + RestHandler.HTTPRestResponse response = handler.sendMessage(requestBody, callContext); + Assertions.assertEquals(200, response.getStatusCode(), response.toString()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertNotNull(response.getBody()); + } + + @Test + public void testSendMessageInvalidBody() { + RestHandler handler = new RestHandler(CARD, requestHandler); + + String invalidBody = "invalid json"; + RestHandler.HTTPRestResponse response = handler.sendMessage(invalidBody, callContext); + + Assertions.assertEquals(400, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("JSONParseError"),response.getBody()); + } + + @Test + public void testSendMessageWrongValueBody() { + RestHandler handler = new RestHandler(CARD, requestHandler); + String requestBody = """ + { + "message": + { + "messageId": "message-1234", + "contextId": "context-1234", + "role": "user", + "content": [{ + "text": "tell me a joke" + }], + "metadata": { + } + } + }"""; + RestHandler.HTTPRestResponse response = handler.sendMessage(requestBody, callContext); + + Assertions.assertEquals(422, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("InvalidParamsError")); + } + + @Test + public void testSendMessageEmptyBody() { + RestHandler handler = new RestHandler(CARD, requestHandler); + + RestHandler.HTTPRestResponse response = handler.sendMessage("", callContext); + + Assertions.assertEquals(400, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("InvalidRequestError")); + } + + @Test + public void testCancelTaskSuccess() { + RestHandler handler = new RestHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + agentExecutorCancel = (context, eventQueue) -> { + // We need to cancel the task or the EventConsumer never finds a 'final' event. + // Looking at the Python implementation, they typically use AgentExecutors that + // don't support cancellation. So my theory is the Agent updates the task to the CANCEL status + Task task = context.getTask(); + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + taskUpdater.cancel(); + }; + + RestHandler.HTTPRestResponse response = handler.cancelTask(MINIMAL_TASK.getId(), callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); + } + + @Test + public void testCancelTaskNotFound() { + RestHandler handler = new RestHandler(CARD, requestHandler); + + RestHandler.HTTPRestResponse response = handler.cancelTask("nonexistent", callContext); + + Assertions.assertEquals(404, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("TaskNotFoundError")); + } + + @Test + public void testSendStreamingMessageSuccess() { + RestHandler handler = new RestHandler(CARD, requestHandler); + agentExecutorExecute = (context, eventQueue) -> { + eventQueue.enqueueEvent(context.getMessage()); + }; + String requestBody = """ + { + "message": { + "role": "ROLE_USER", + "content": [ + { + "text": "tell me some jokes" + } + ], + "messageId": "message-1234", + "contextId": "context-1234" + }, + "configuration": { + "acceptedOutputModes": ["text"] + } + }"""; + + RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, callContext); + Assertions.assertEquals(200, response.getStatusCode(), response.toString()); + Assertions.assertInstanceOf(RestHandler.HTTPRestStreamingResponse.class, response); + RestHandler.HTTPRestStreamingResponse streamingResponse = (RestHandler.HTTPRestStreamingResponse) response; + Assertions.assertNotNull(streamingResponse.getPublisher()); + Assertions.assertEquals("text/event-stream", streamingResponse.getContentType()); + } + + @Test + public void testSendStreamingMessageNotSupported() { + AgentCard card = createAgentCard(false, true, true); + RestHandler handler = new RestHandler(card, requestHandler); + + String requestBody = """ + { + "contextId": "ctx123", + "role": "ROLE_USER", + "content": [{ + "text": "Hello" + }] + } + """; + + RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, callContext); + + Assertions.assertEquals(400, response.getStatusCode()); + Assertions.assertTrue(response.getBody().contains("InvalidRequestError")); + } + + @Test + public void testPushNotificationConfigSuccess() { + RestHandler handler = new RestHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + String requestBody = """ + { + "parent": "tasks/%s", + "config": { + "name": "tasks/%s/pushNotificationConfigs/", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + }""".formatted(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration( MINIMAL_TASK.getId(), requestBody, callContext); + + Assertions.assertEquals(201, response.getStatusCode(), response.toString()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertNotNull(response.getBody()); + } + + @Test + public void testPushNotificationConfigNotSupported() { + AgentCard card = createAgentCard(true, false, true); + RestHandler handler = new RestHandler(card, requestHandler); + + String requestBody = """ + { + "taskId": "%s", + "pushNotificationConfig": { + "url": "http://example.com" + } + } + """.formatted(MINIMAL_TASK.getId()); + + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), requestBody, callContext); + + Assertions.assertEquals(501, response.getStatusCode()); + Assertions.assertTrue(response.getBody().contains("PushNotificationNotSupportedError")); + } + + @Test + public void testGetPushNotificationConfig() { + RestHandler handler = new RestHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + // First, create a push notification config + String createRequestBody = """ + { + "parent": "tasks/%s", + "config": { + "name": "tasks/%s/pushNotificationConfigs/", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + }""".formatted(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), createRequestBody, callContext); + Assertions.assertEquals(201, response.getStatusCode(), response.toString()); + Assertions.assertEquals("application/json", response.getContentType()); + // Now get it + response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", callContext); + Assertions.assertEquals(200, response.getStatusCode(), response.toString()); + Assertions.assertEquals("application/json", response.getContentType()); + } + + @Test + public void testDeletePushNotificationConfig() { + RestHandler handler = new RestHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", callContext); + Assertions.assertEquals(204, response.getStatusCode()); + } + + @Test + public void testListPushNotificationConfigs() { + RestHandler handler = new RestHandler(CARD, requestHandler); + taskStore.save(MINIMAL_TASK); + + RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.getId(), callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertNotNull(response.getBody()); + } + + @Test + public void testHttpStatusCodeMapping() { + RestHandler handler = new RestHandler(CARD, requestHandler); + + // Test 400 for invalid request + RestHandler.HTTPRestResponse response = handler.sendMessage("", callContext); + Assertions.assertEquals(400, response.getStatusCode()); + + // Test 404 for not found + response = handler.getTask("nonexistent", null, callContext); + Assertions.assertEquals(404, response.getStatusCode()); + } + + @Test + public void testStreamingDoesNotBlockMainThread() throws Exception { + RestHandler handler = new RestHandler(CARD, requestHandler); + + // Track if the main thread gets blocked during streaming + AtomicBoolean eventReceived = new AtomicBoolean(false); + CountDownLatch streamStarted = new CountDownLatch(1); + CountDownLatch eventProcessed = new CountDownLatch(1); + agentExecutorExecute = (context, eventQueue) -> { + // Wait a bit to ensure the main thread continues + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + eventQueue.enqueueEvent(context.getMessage()); + }; + + String requestBody = """ + { + "message": { + "role": "ROLE_USER", + "content": [ + { + "text": "tell me some jokes" + } + ], + "messageId": "message-1234", + "contextId": "context-1234" + }, + "configuration": { + "acceptedOutputModes": ["text"] + } + }"""; + + // Start streaming + RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertInstanceOf(RestHandler.HTTPRestStreamingResponse.class, response); + + RestHandler.HTTPRestStreamingResponse streamingResponse = (RestHandler.HTTPRestStreamingResponse) response; + Flow.Publisher publisher = streamingResponse.getPublisher(); + publisher.subscribe(new Flow.Subscriber() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + streamStarted.countDown(); + subscription.request(1); + } + + @Override + public void onNext(String item) { + eventReceived.set(true); + eventProcessed.countDown(); + } + + @Override + public void onError(Throwable throwable) { + eventProcessed.countDown(); + } + + @Override + public void onComplete() { + eventProcessed.countDown(); + } + }); + + // The main thread should not be blocked - we should be able to continue immediately + Assertions.assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), + "Streaming subscription should start quickly without blocking main thread"); + + // This proves the main thread is not blocked - we can do other work + // Simulate main thread doing other work + Thread.sleep(50); + + // Wait for the actual event processing to complete + Assertions.assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), + "Event should be processed within reasonable time"); + + // Verify we received the event + Assertions.assertTrue(eventReceived.get(), "Should have received streaming event"); + } +} diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestTestTransportMetadata.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestTestTransportMetadata.java new file mode 100644 index 000000000..68aad41bb --- /dev/null +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestTestTransportMetadata.java @@ -0,0 +1,12 @@ +package io.a2a.transport.rest.handler; + +import io.a2a.server.TransportMetadata; +import io.a2a.spec.TransportProtocol; + +public class RestTestTransportMetadata implements TransportMetadata { + @Override + public String getTransportProtocol() { + return TransportProtocol.HTTP_JSON.asString(); + } + +} diff --git a/transport/rest/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata b/transport/rest/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata new file mode 100644 index 000000000..3604945b4 --- /dev/null +++ b/transport/rest/src/test/resources/META-INF/services/io.a2a.server.TransportMetadata @@ -0,0 +1,2 @@ +# Add a test TransportMetadata so we pass AgentCard validation +io.a2a.transport.rest.handler.RestTestTransportMetadata \ No newline at end of file From f2aeed1501867d0969c5fcf89efce22081cc2e1d Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 11 Sep 2025 12:50:24 +0100 Subject: [PATCH 119/493] fix: Use deployment CL to load TransportMetadata services if different from TCCL (#263) I found working on https://github.com/wildfly-extras/a2a-java-sdk-server-jakarta/pull/26 that in WildFly gRPC happens on a separate thread with its own classloader. Hence the serviceloader lookup from the default TCCL didn't work in WildFLy. --- .../transport/grpc/handler/GrpcHandler.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 00c05c4b6..e19ba80ef 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -380,12 +380,35 @@ private void handleInternalError(StreamObserver responseObserver, Throwab private AgentCard getAgentCardInternal() { AgentCard agentCard = getAgentCard(); if (initialised.compareAndSet(false, true)) { - // Validate transport configuration - AgentCardValidator.validateTransportConfiguration(agentCard); + // Validate transport configuration with proper classloader context + validateTransportConfigurationWithCorrectClassLoader(agentCard); } return agentCard; } + private void validateTransportConfigurationWithCorrectClassLoader(AgentCard agentCard) { + ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); + ClassLoader deploymentCl = getDeploymentClassLoader(); + boolean switchCl = deploymentCl != null && deploymentCl != originalTccl; + + try { + if (switchCl) { + // Set TCCL to the classloader that loaded this class, which should have access + // to the deployment classpath containing META-INF/services files + Thread.currentThread().setContextClassLoader(deploymentCl); + } + AgentCardValidator.validateTransportConfiguration(agentCard); + } finally { + if (switchCl) { + Thread.currentThread().setContextClassLoader(originalTccl); + } + } + } + + protected ClassLoader getDeploymentClassLoader() { + return this.getClass().getClassLoader(); + } + public static void setStreamingSubscribedRunnable(Runnable runnable) { streamingSubscribedRunnable = runnable; } From 0e27821e0955c4be18ad0c562261cd3afd0fa033 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 11 Sep 2025 14:45:18 +0100 Subject: [PATCH 120/493] chore: Use TCK 0.3.0.beta2 on CI (#265) --- .github/workflows/run-tck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 545c1b495..d5004bf0c 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -12,7 +12,7 @@ on: env: # Tag of the TCK - TCK_VERSION: 0.3.0.beta + TCK_VERSION: 0.3.0.beta2 # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 # SUT_JSONRPC_URL to use for the TCK and the server agent From e5dcb4d1cae5dcfb8660c0ebad921bbda711b4fc Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 11 Sep 2025 17:34:26 +0100 Subject: [PATCH 121/493] feat: Add ability to skip transport validation (#266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add missing validation to the RestHandler. Fixes #264 🦕 --- .../rest/quarkus/QuarkusA2ARestTest.java | 9 +- .../io/a2a/server/AgentCardValidator.java | 59 +++++- .../io/a2a/server/AgentCardValidatorTest.java | 168 ++++++++++++++---- .../transport/rest/handler/RestHandler.java | 4 + .../a2a-requesthandler-test.properties | 2 +- 5 files changed, 196 insertions(+), 46 deletions(-) diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java index f2231ffc8..f9af7b378 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java @@ -1,6 +1,9 @@ package io.a2a.server.rest.quarkus; -import static io.a2a.server.apps.common.AbstractA2AServerTest.APPLICATION_JSON; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import io.a2a.client.ClientBuilder; import io.a2a.client.transport.rest.RestTransport; @@ -8,10 +11,6 @@ import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.TransportProtocol; import io.quarkus.test.junit.QuarkusTest; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/server-common/src/main/java/io/a2a/server/AgentCardValidator.java b/server-common/src/main/java/io/a2a/server/AgentCardValidator.java index d3c80b35b..8d22bee59 100644 --- a/server-common/src/main/java/io/a2a/server/AgentCardValidator.java +++ b/server-common/src/main/java/io/a2a/server/AgentCardValidator.java @@ -10,6 +10,7 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; /** * Validates AgentCard transport configuration against available transport endpoints. @@ -17,7 +18,13 @@ public class AgentCardValidator { private static final Logger LOGGER = Logger.getLogger(AgentCardValidator.class.getName()); - + + // Properties to turn off validation globally, or per known transport + public static final String SKIP_PROPERTY = "io.a2a.transport.skipValidation"; + public static final String SKIP_JSONRPC_PROPERTY = "io.a2a.transport.jsonrpc.skipValidation"; + public static final String SKIP_GRPC_PROPERTY = "io.a2a.transport.grpc.skipValidation"; + public static final String SKIP_REST_PROPERTY = "io.a2a.transport.rest.skipValidation"; + /** * Validates the transport configuration of an AgentCard against available transports found on the classpath. * Logs warnings for missing transports and errors for unsupported transports. @@ -36,11 +43,18 @@ public static void validateTransportConfiguration(AgentCard agentCard) { * @param availableTransports the set of available transport protocols */ static void validateTransportConfiguration(AgentCard agentCard, Set availableTransports) { + boolean skip = Boolean.getBoolean(SKIP_PROPERTY); + if (skip) { + return; + } + Set agentCardTransports = getAgentCardTransports(agentCard); + Set filteredAvailableTransports = filterSkippedTransports(availableTransports); + Set filteredAgentCardTransports = filterSkippedTransports(agentCardTransports); // Check for missing transports (warn if AgentCard doesn't include all available transports) - Set missingTransports = availableTransports.stream() - .filter(transport -> !agentCardTransports.contains(transport)) + Set missingTransports = filteredAvailableTransports.stream() + .filter(transport -> !filteredAgentCardTransports.contains(transport)) .collect(Collectors.toSet()); if (!missingTransports.isEmpty()) { @@ -48,14 +62,14 @@ static void validateTransportConfiguration(AgentCard agentCard, Set avai "AgentCard does not include all available transports. Missing: %s. " + "Available transports: %s. AgentCard transports: %s", formatTransports(missingTransports), - formatTransports(availableTransports), - formatTransports(agentCardTransports) + formatTransports(filteredAvailableTransports), + formatTransports(filteredAgentCardTransports) )); } // Check for unsupported transports (error if AgentCard specifies unavailable transports) - Set unsupportedTransports = agentCardTransports.stream() - .filter(transport -> !availableTransports.contains(transport)) + Set unsupportedTransports = filteredAgentCardTransports.stream() + .filter(transport -> !filteredAvailableTransports.contains(transport)) .collect(Collectors.toSet()); if (!unsupportedTransports.isEmpty()) { @@ -63,7 +77,7 @@ static void validateTransportConfiguration(AgentCard agentCard, Set avai "AgentCard specifies transport interfaces for unavailable transports: %s. " + "Available transports: %s. Consider removing these interfaces or adding the required transport dependencies.", formatTransports(unsupportedTransports), - formatTransports(availableTransports) + formatTransports(filteredAvailableTransports) ); LOGGER.severe(errorMessage); @@ -110,6 +124,35 @@ private static String formatTransports(Set transports) { .collect(Collectors.joining(", ", "[", "]")); } + /** + * Filters out transports that have been configured to skip validation. + * + * @param transports the set of transport protocols to filter + * @return filtered set with skipped transports removed + */ + private static Set filterSkippedTransports(Set transports) { + return transports.stream() + .filter(transport -> !isTransportSkipped(transport)) + .collect(Collectors.toSet()); + } + + /** + * Checks if validation should be skipped for a specific transport. + * + * @param transport the transport protocol to check + * @return true if validation should be skipped for this transport + */ + private static boolean isTransportSkipped(String transport) { + if (transport.equals(TransportProtocol.JSONRPC.asString())) { + return Boolean.getBoolean(SKIP_JSONRPC_PROPERTY); + } else if (transport.equals(TransportProtocol.GRPC.asString())){ + return Boolean.getBoolean(SKIP_GRPC_PROPERTY); + } else if (transport.equals(TransportProtocol.HTTP_JSON.asString())) { + return Boolean.getBoolean(SKIP_REST_PROPERTY); + } + return false; + } + /** * Discovers available transport endpoints using ServiceLoader. * This searches the classpath for implementations of TransportMetadata. diff --git a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java index 7b54eeb6c..83bbae073 100644 --- a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java +++ b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java @@ -16,13 +16,12 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; public class AgentCardValidatorTest { - @Test - void testValidationWithSimpleAgentCard() { - // Create a simple AgentCard (uses default JSONRPC transport) - AgentCard agentCard = new AgentCard.Builder() + private AgentCard.Builder createTestAgentCardBuilder() { + return new AgentCard.Builder() .name("Test Agent") .description("Test Description") .url("http://localhost:9999") @@ -30,7 +29,13 @@ void testValidationWithSimpleAgentCard() { .capabilities(new AgentCapabilities.Builder().build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.emptyList()) + .skills(Collections.emptyList()); + } + + @Test + void testValidationWithSimpleAgentCard() { + // Create a simple AgentCard (uses default JSONRPC transport) + AgentCard agentCard = createTestAgentCardBuilder() .build(); // Define available transports @@ -43,15 +48,7 @@ void testValidationWithSimpleAgentCard() { @Test void testValidationWithMultipleTransports() { // Create AgentCard that specifies multiple transports - AgentCard agentCard = new AgentCard.Builder() - .name("Test Agent") - .description("Test Description") - .url("http://localhost:9999") - .version("1.0.0") - .capabilities(new AgentCapabilities.Builder().build()) - .defaultInputModes(Collections.singletonList("text")) - .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.emptyList()) + AgentCard agentCard = createTestAgentCardBuilder() .preferredTransport(TransportProtocol.JSONRPC.asString()) .additionalInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"), @@ -70,15 +67,7 @@ void testValidationWithMultipleTransports() { @Test void testLogWarningWhenExtraTransportsFound() { // Create an AgentCard with only JSONRPC - AgentCard agentCard = new AgentCard.Builder() - .name("Test Agent") - .description("Test Description") - .url("http://localhost:9999") - .version("1.0.0") - .capabilities(new AgentCapabilities.Builder().build()) - .defaultInputModes(Collections.singletonList("text")) - .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.emptyList()) + AgentCard agentCard = createTestAgentCardBuilder() .preferredTransport(TransportProtocol.JSONRPC.asString()) .build(); @@ -105,15 +94,7 @@ void testLogWarningWhenExtraTransportsFound() { @Test void testValidationWithUnavailableTransport() { // Create a simple AgentCard (uses default JSONRPC transport) - AgentCard agentCard = new AgentCard.Builder() - .name("Test Agent") - .description("Test Description") - .url("http://localhost:9999") - .version("1.0.0") - .capabilities(new AgentCapabilities.Builder().build()) - .defaultInputModes(Collections.singletonList("text")) - .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.emptyList()) + AgentCard agentCard = createTestAgentCardBuilder() .build(); // Define available transports (empty) @@ -125,6 +106,129 @@ void testValidationWithUnavailableTransport() { assertTrue(exception.getMessage().contains("unavailable transports: [JSONRPC]")); } + @Test + void testGlobalSkipProperty() { + System.setProperty(AgentCardValidator.SKIP_PROPERTY, "true"); + try { + AgentCard agentCard = createTestAgentCardBuilder() + .build(); + + Set availableTransports = Collections.emptySet(); + + assertDoesNotThrow(() -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + } finally { + System.clearProperty(AgentCardValidator.SKIP_PROPERTY); + } + } + + @Test + void testSkipJsonrpcProperty() { + System.setProperty(AgentCardValidator.SKIP_JSONRPC_PROPERTY, "true"); + try { + AgentCard agentCard = createTestAgentCardBuilder() + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .build(); + + Set availableTransports = Set.of(TransportProtocol.GRPC.asString()); + + assertDoesNotThrow(() -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + } finally { + System.clearProperty(AgentCardValidator.SKIP_JSONRPC_PROPERTY); + } + } + + @Test + void testSkipGrpcProperty() { + System.setProperty(AgentCardValidator.SKIP_GRPC_PROPERTY, "true"); + try { + AgentCard agentCard = createTestAgentCardBuilder() + .preferredTransport(TransportProtocol.GRPC.asString()) + .build(); + + Set availableTransports = Set.of(TransportProtocol.JSONRPC.asString()); + + assertDoesNotThrow(() -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + } finally { + System.clearProperty(AgentCardValidator.SKIP_GRPC_PROPERTY); + } + } + + @Test + void testSkipRestProperty() { + System.setProperty(AgentCardValidator.SKIP_REST_PROPERTY, "true"); + try { + AgentCard agentCard = createTestAgentCardBuilder() + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "http://localhost:8080") + )) + .build(); + + Set availableTransports = Set.of(TransportProtocol.JSONRPC.asString()); + + assertDoesNotThrow(() -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + } finally { + System.clearProperty(AgentCardValidator.SKIP_REST_PROPERTY); + } + } + + @Test + void testMultipleTransportsWithMixedSkipProperties() { + System.setProperty(AgentCardValidator.SKIP_GRPC_PROPERTY, "true"); + try { + AgentCard agentCard = createTestAgentCardBuilder() + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.GRPC.asString(), "http://localhost:9000"), + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "http://localhost:8080") + )) + .build(); + + Set availableTransports = Set.of(TransportProtocol.JSONRPC.asString()); + + IllegalStateException exception = assertThrows(IllegalStateException.class, + () -> AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports)); + assertTrue(exception.getMessage().contains("unavailable transports: [HTTP+JSON]")); + } finally { + System.clearProperty(AgentCardValidator.SKIP_GRPC_PROPERTY); + } + } + + @Test + void testSkipPropertiesFilterWarnings() { + System.setProperty(AgentCardValidator.SKIP_GRPC_PROPERTY, "true"); + try { + AgentCard agentCard = createTestAgentCardBuilder() + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .build(); + + Set availableTransports = Set.of( + TransportProtocol.JSONRPC.asString(), + TransportProtocol.GRPC.asString(), + TransportProtocol.HTTP_JSON.asString() + ); + + Logger logger = Logger.getLogger(AgentCardValidator.class.getName()); + TestLogHandler testLogHandler = new TestLogHandler(); + logger.addHandler(testLogHandler); + + try { + AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports); + } finally { + logger.removeHandler(testLogHandler); + } + + boolean foundWarning = testLogHandler.getLogMessages().stream() + .anyMatch(msg -> msg.contains("Missing: [HTTP+JSON]")); + assertTrue(foundWarning); + + boolean grpcMentioned = testLogHandler.getLogMessages().stream() + .anyMatch(msg -> msg.contains("GRPC")); + assertFalse(grpcMentioned); + } finally { + System.clearProperty(AgentCardValidator.SKIP_GRPC_PROPERTY); + } + } + // A simple log handler for testing private static class TestLogHandler extends Handler { private final List logMessages = new java.util.ArrayList<>(); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index f6029b266..2ddfdd259 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -6,6 +6,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.server.AgentCardValidator; import io.a2a.server.ExtendedAgentCard; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -64,6 +65,9 @@ public RestHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard Inst this.agentCard = agentCard; this.extendedAgentCard = extendedAgentCard; this.requestHandler = requestHandler; + + // Validate transport configuration + AgentCardValidator.validateTransportConfiguration(agentCard); } public RestHandler(AgentCard agentCard, RequestHandler requestHandler) { diff --git a/transport/rest/src/main/resources/a2a-requesthandler-test.properties b/transport/rest/src/main/resources/a2a-requesthandler-test.properties index 723a7f87f..33abcce34 100644 --- a/transport/rest/src/main/resources/a2a-requesthandler-test.properties +++ b/transport/rest/src/main/resources/a2a-requesthandler-test.properties @@ -1 +1 @@ -preferred-transport=HTTP_JSON \ No newline at end of file +preferred-transport=HTTP+JSON \ No newline at end of file From 72c063c728b5be1c33d804bb21f735c8918b4b0a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 15 Sep 2025 15:18:55 +0100 Subject: [PATCH 122/493] fix: use proper annotation for default beans (#271) Fixes #267 --- .../java/io/a2a/server/common/quarkus/DefaultProducers.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java b/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java index 4afeae0e9..cbc38f13f 100644 --- a/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java +++ b/reference/common/src/main/java/io/a2a/server/common/quarkus/DefaultProducers.java @@ -1,8 +1,7 @@ package io.a2a.server.common.quarkus; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Default; -import jakarta.ws.rs.Produces; +import jakarta.enterprise.inject.Produces; import io.a2a.server.PublicAgentCard; import io.a2a.server.agentexecution.AgentExecutor; From 33d4549805def2f8160a93b1d7fa1e5b96895c04 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 15 Sep 2025 12:01:07 -0400 Subject: [PATCH 123/493] chore: Release 0.3.0.Beta1 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 24 files changed, 25 insertions(+), 25 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index c5b45b583..528bca17c 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 158ae7ea8..51382d099 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 172c243a3..feaa937b5 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 5d3be70ae..23cd21865 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index 99b183289..ca510684c 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 504856d3b..2d751c179 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 3aaa5c221..1e38c71df 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index f7831133f..fc9dd5172 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1 +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta1 //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 4cb7c0c3e..6c0259808 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 03f06158a..0ca00080d 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-examples-server diff --git a/http-client/pom.xml b/http-client/pom.xml index fca8bc4fe..ac97dcc6e 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index cfe777bae..811d377f3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 2e149a343..47c707224 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index e11adc2b5..0db6d4edd 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index de441f6b3..cdbdd8626 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index e96a16971..a47e416c4 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index 7f1d810f6..c31cfad81 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index ea5ac3fba..23efd37d9 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index bd06dd1df..fc2519df8 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 85a4b215a..14bce9c35 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 1d5234b4e..d0282a1a3 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 76d72db24..65f38dd51 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 3565d042f..feb5fe7d7 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 712abbce4..ef03e1d7c 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1-SNAPSHOT + 0.3.0.Beta1 ../../pom.xml a2a-java-sdk-transport-rest From c58d173b21848aa1a8deffed6a42693d366ec1b7 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 15 Sep 2025 12:03:30 -0400 Subject: [PATCH 124/493] chore: Next is 0.3.0.Beta2 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 24 files changed, 25 insertions(+), 25 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 528bca17c..c7cfa51cc 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 51382d099..fe8b6d9e1 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index feaa937b5..e9a899ee5 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 23cd21865..3741a8624 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index ca510684c..054e96603 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 2d751c179..b5af50e25 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 1e38c71df..5c90e2868 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index fc9dd5172..4349ca9fa 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta1 -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta1 +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta2-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta2-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 6c0259808..8a50379f4 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 0ca00080d..97ff6e561 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-examples-server diff --git a/http-client/pom.xml b/http-client/pom.xml index ac97dcc6e..0e86ac38a 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index 811d377f3..bf79b4ada 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 47c707224..6e0dcce27 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 0db6d4edd..8623b4154 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index cdbdd8626..c06bc6830 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index a47e416c4..2df2a4037 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index c31cfad81..0d6c0df3d 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 23efd37d9..636656536 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index fc2519df8..872ad0cda 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 14bce9c35..a13e10125 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index d0282a1a3..dd094c4a2 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 65f38dd51..97025bed5 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index feb5fe7d7..987fcb53c 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index ef03e1d7c..562c1b4bd 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta1 + 0.3.0.Beta2-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-rest From ce15a4292c25e5ac91774635cad8f7944e2ccb12 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 16 Sep 2025 16:09:52 -0400 Subject: [PATCH 125/493] fix: Add recent blog posts (#273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- COMMUNITY_ARTICLES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/COMMUNITY_ARTICLES.md b/COMMUNITY_ARTICLES.md index b4f392d92..f28ef059a 100644 --- a/COMMUNITY_ARTICLES.md +++ b/COMMUNITY_ARTICLES.md @@ -17,6 +17,10 @@ Here are some articles and videos from the community that you may find helpful. * [Getting Started with Quarkus and A2A Java SDK 0.3.0](https://quarkus.io/blog/quarkus-a2a-java-0-3-0-alpha-release/): Learn about what's new in the 0.3.0 version of the A2A Java SDK. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/getting-started-with-quarkus-and-a2a-java-sdk-0-3-0-c4e70620a805). +* [A2A Java SDK: Support for the REST Transport is Now Here](https://medium.com/google-cloud/a2a-java-sdk-support-for-the-rest-transport-is-now-here-56c666060b32): Learn about the new support for the REST transport introduced in A2A Java SDK 0.3.0.Beta1. + +* [Multi-Language Agent Collaboration and Interoperability with A2A](https://medium.com/google-cloud/multi-language-agent-collaboration-and-interoperability-with-a2a-e5fe80b4962d): A deep dive into a multi-language, multi-agent sample where agents written in Java, Python, and TypeScript work together to achieve a common goal: content creation. + ## A2A Java SDK for Jakarta Servers * [Building your First A2A Agent](https://www.wildfly.org/news/2025/08/07/Building-your-First-A2A-Agent/): An introduction on how to use the A2A Java SDK for Jakarta Servers with WildFly. From 8903e3d7d4e6366b0aabd3f1626be968d0d36838 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 22 Sep 2025 20:19:29 +0100 Subject: [PATCH 126/493] =?UTF-8?q?fix:=20Implement=20background=20cleanup?= =?UTF-8?q?=20and=20producer=20continuation=20for=20clien=E2=80=A6=20(#284?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …t disconnect Backports key fixes from a2a-python PRs #440 and #472 to resolve event queue lifecycle issues that prevented task resubscription after client disconnect. - Add background task tracking to DefaultRequestHandler with CompletableFuture - Implement asynchronous cleanup when clients disconnect during streaming - Add EventQueue graceful vs immediate close behavior Fixes #283 🦕 --- .../java/io/a2a/server/events/EventQueue.java | 39 +- .../DefaultRequestHandler.java | 30 +- .../io/a2a/server/events/EventQueueTest.java | 72 ++++ .../DefaultRequestHandlerBackgroundTest.java | 401 ++++++++++++++++++ .../a2a/tck/server/AgentExecutorProducer.java | 1 + 5 files changed, 530 insertions(+), 13 deletions(-) create mode 100644 server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index c73292a09..46069f55d 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -100,20 +100,31 @@ public void taskDone() { public abstract void close(); + public abstract void close(boolean immediate); + + public boolean isClosed() { + return closed; + } + public void doClose() { + doClose(false); + } + + public void doClose(boolean immediate) { synchronized (this) { if (closed) { return; } - LOGGER.debug("Closing {}", this); + LOGGER.debug("Closing {} (immediate={})", this, immediate); closed = true; } - // Although the Python implementation drains the queue on closing, - // here it makes events go missing - // TODO do we actually need to drain it? If we do, we need some mechanism to determine that noone is - // polling any longer and drain it asynchronously once it is all done. That could perhaps be done - // via an EnhancedRunnable.DoneCallback. - //queue.drainTo(new ArrayList<>()); + + if (immediate) { + // Immediate close: clear pending events + queue.clear(); + LOGGER.debug("Cleared queue for immediate close: {}", this); + } + // For graceful close, let the queue drain naturally through normal consumption } static class MainQueue extends EventQueue { @@ -151,8 +162,13 @@ void signalQueuePollerStarted() { @Override public void close() { - doClose(); - children.forEach(EventQueue::doClose); + close(false); + } + + @Override + public void close(boolean immediate) { + doClose(immediate); + children.forEach(child -> child.doClose(immediate)); } } @@ -191,5 +207,10 @@ void signalQueuePollerStarted() { public void close() { parent.close(); } + + @Override + public void close(boolean immediate) { + parent.close(immediate); + } } } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 7cb223c98..baed1b98c 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -15,6 +15,8 @@ import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import java.util.Set; +import java.util.concurrent.TimeUnit; import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; @@ -67,6 +69,7 @@ public class DefaultRequestHandler implements RequestHandler { private final Supplier requestContextBuilder; private final ConcurrentMap> runningAgents = new ConcurrentHashMap<>(); + private final Set> backgroundTasks = ConcurrentHashMap.newKeySet(); private final Executor executor; @@ -190,8 +193,8 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte } finally { if (interrupted) { - // TODO Make this async - cleanupProducer(taskId); + CompletableFuture cleanupTask = CompletableFuture.runAsync(() -> cleanupProducer(taskId), executor); + trackBackgroundTask(cleanupTask); } else { cleanupProducer(taskId); } @@ -212,9 +215,9 @@ public Flow.Publisher onMessageSendStream( ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId.get(), mss.requestContext, queue); + EventConsumer consumer = new EventConsumer(queue); try { - EventConsumer consumer = new EventConsumer(queue); // This callback must be added before we start consuming. Otherwise, // any errors thrown by the producerRunnable are not picked up by the consumer @@ -258,7 +261,8 @@ public Flow.Publisher onMessageSendStream( return convertingProcessor(eventPublisher, event -> (StreamingEventKind) event); } finally { - cleanupProducer(taskId.get()); + CompletableFuture cleanupTask = CompletableFuture.runAsync(() -> cleanupProducer(taskId.get()), executor); + trackBackgroundTask(cleanupTask); } } @@ -396,6 +400,24 @@ public void run() { return runnable; } + private void trackBackgroundTask(CompletableFuture task) { + backgroundTasks.add(task); + + task.whenComplete((result, throwable) -> { + try { + if (throwable != null) { + if (throwable instanceof java.util.concurrent.CancellationException) { + LOGGER.debug("Background task cancelled: {}", task); + } else { + LOGGER.error("Background task failed", throwable); + } + } + } finally { + backgroundTasks.remove(task); + } + }); + } + private void cleanupProducer(String taskId) { // TODO the Python implementation waits for the producerRunnable runningAgents.get(taskId) diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 9510f8d3f..2fd2766d6 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -113,4 +114,75 @@ public void testEnqueueDifferentEventTypes() throws Exception { assertSame(event, dequeuedEvent); } } + + /** + * Test close behavior sets flag and handles graceful close. + * Backported from Python test: test_close_sets_flag_and_handles_internal_queue_old_python + */ + @Test + public void testCloseGracefulSetsFlag() throws Exception { + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + eventQueue.enqueueEvent(event); + + eventQueue.close(false); // Graceful close + assertTrue(eventQueue.isClosed()); + } + + /** + * Test immediate close behavior. + * Backported from Python test behavior + */ + @Test + public void testCloseImmediateClearsQueue() throws Exception { + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + eventQueue.enqueueEvent(event); + + eventQueue.close(true); // Immediate close + assertTrue(eventQueue.isClosed()); + + // After immediate close, queue should be cleared + // Attempting to dequeue should return null or throw exception + try { + Event dequeuedEvent = eventQueue.dequeueEvent(-1); + // If we get here, the event should be null (queue was cleared) + assertNull(dequeuedEvent); + } catch (EventQueueClosedException e) { + // This is also acceptable - queue is closed + } + } + + /** + * Test that close is idempotent. + * Backported from Python test: test_close_idempotent + */ + @Test + public void testCloseIdempotent() throws Exception { + eventQueue.close(); + assertTrue(eventQueue.isClosed()); + + // Calling close again should not cause issues + eventQueue.close(); + assertTrue(eventQueue.isClosed()); + + // Test with immediate close as well + EventQueue eventQueue2 = EventQueue.create(); + eventQueue2.close(true); + assertTrue(eventQueue2.isClosed()); + + eventQueue2.close(true); + assertTrue(eventQueue2.isClosed()); + } + + /** + * Test that child queues are closed when parent closes. + */ + @Test + public void testCloseChildQueues() throws Exception { + EventQueue childQueue = eventQueue.tap(); + assertTrue(childQueue != null); + + eventQueue.close(); + assertTrue(eventQueue.isClosed()); + assertTrue(childQueue.isClosed()); + } } diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java new file mode 100644 index 000000000..412f1a8cf --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java @@ -0,0 +1,401 @@ +package io.a2a.server.requesthandlers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import io.a2a.server.ServerCallContext; +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.agentexecution.SimpleRequestContextBuilder; +import io.a2a.server.auth.UnauthenticatedUser; +import io.a2a.server.events.EventQueue; +import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.tasks.InMemoryTaskStore; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TextPart; + +/** + * Tests for DefaultRequestHandler background cleanup and task tracking functionality, + * backported from Python PR #440 and #472. + */ +public class DefaultRequestHandlerBackgroundTest { + + private DefaultRequestHandler requestHandler; + private InMemoryTaskStore taskStore; + private InMemoryQueueManager queueManager; + private TestAgentExecutor agentExecutor; + private ServerCallContext serverCallContext; + + @BeforeEach + void setUp() { + taskStore = new InMemoryTaskStore(); + queueManager = new InMemoryQueueManager(); + agentExecutor = new TestAgentExecutor(); + + requestHandler = new DefaultRequestHandler( + agentExecutor, + taskStore, + queueManager, + null, // pushConfigStore + null, // pushSender + Executors.newCachedThreadPool() + ); + + serverCallContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, java.util.Map.of()); + } + + /** + * Test that background cleanup tasks are properly tracked and cleared. + * Backported from Python test: test_background_cleanup_task_is_tracked_and_cleared + */ + @Test + @Timeout(10) + void testBackgroundCleanupTaskIsTrackedAndCleared() throws Exception { + String taskId = "track-task-1"; + String contextId = "track-ctx-1"; + + // Create a task that will trigger background cleanup + Task task = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + taskStore.save(task); + + Message message = new Message.Builder() + .messageId("msg-track") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params = new MessageSendParams(message, null, null); + + // Set up agent to finish quickly so cleanup runs + CountDownLatch agentStarted = new CountDownLatch(1); + CountDownLatch allowAgentFinish = new CountDownLatch(1); + + agentExecutor.setExecuteCallback((context, queue) -> { + agentStarted.countDown(); + try { + allowAgentFinish.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + // Start streaming (this will create background tasks) + var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); + + // Wait for agent to start + assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); + + // Allow agent to finish, which should trigger cleanup + allowAgentFinish.countDown(); + + // Give some time for background tasks to be tracked and cleaned up + Thread.sleep(1000); + + // Background tasks should eventually be cleared + // Note: We can't directly access the backgroundTasks field without reflection, + // but the test verifies the mechanism doesn't hang or leak tasks + assertTrue(true, "Background cleanup completed without hanging"); + } + + /** + * Test that client disconnect triggers background cleanup and producer continues. + * Backported from Python test: test_on_message_send_stream_client_disconnect_triggers_background_cleanup_and_producer_continues + */ + @Test + @Timeout(10) + void testStreamingClientDisconnectTriggersBackgroundCleanup() throws Exception { + String taskId = "disc-task-1"; + String contextId = "disc-ctx-1"; + + Message message = new Message.Builder() + .messageId("mid") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params = new MessageSendParams(message, null, null); + + // Agent should start and then wait + CountDownLatch agentStarted = new CountDownLatch(1); + CountDownLatch allowAgentFinish = new CountDownLatch(1); + AtomicBoolean agentCompleted = new AtomicBoolean(false); + + agentExecutor.setExecuteCallback((context, queue) -> { + agentStarted.countDown(); + try { + allowAgentFinish.await(10, TimeUnit.SECONDS); + agentCompleted.set(true); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + // Start streaming + var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); + + // Wait for agent to start + assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start executing"); + + // Simulate client disconnect by not consuming the stream + // In real scenarios, the client would close the connection + + // Agent should still be running (not finished immediately on "disconnect") + Thread.sleep(500); + assertTrue(agentExecutor.isExecuting(), "Producer should still be running after simulated disconnect"); + + // Allow agent to finish + allowAgentFinish.countDown(); + + // Wait a bit for completion + Thread.sleep(1000); + + assertTrue(agentCompleted.get(), "Agent should have completed execution"); + } + + /** + * Test that resubscription works after client disconnect. + * Backported from Python test: test_stream_disconnect_then_resubscribe_receives_future_events + */ + @Test + @Timeout(15) + void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception { + String taskId = "reconn-task-1"; + String contextId = "reconn-ctx-1"; + + // Create initial task + Task initialTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + taskStore.save(initialTask); + + Message message = new Message.Builder() + .messageId("msg-reconn") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params = new MessageSendParams(message, null, null); + + // Set up agent to emit events with controlled timing + CountDownLatch agentStarted = new CountDownLatch(1); + CountDownLatch allowSecondEvent = new CountDownLatch(1); + CountDownLatch allowFinish = new CountDownLatch(1); + + agentExecutor.setExecuteCallback((context, queue) -> { + agentStarted.countDown(); + + // Emit first event + Task firstEvent = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + queue.enqueueEvent(firstEvent); + + // Wait for permission to emit second event + try { + allowSecondEvent.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + + // Emit second event + Task secondEvent = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + queue.enqueueEvent(secondEvent); + + try { + allowFinish.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + // Start streaming and simulate getting first event then disconnecting + var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); + + // Wait for agent to start and emit first event + assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); + + // Simulate client disconnect (in real scenario, client would close connection) + // The background cleanup should keep the producer running + + // Now try to resubscribe to the task + io.a2a.spec.TaskIdParams resubParams = new io.a2a.spec.TaskIdParams(taskId); + + // Allow agent to emit second event + allowSecondEvent.countDown(); + + // Try resubscription - this should work because queue is still alive + var resubResult = requestHandler.onResubscribeToTask(resubParams, serverCallContext); + // If we get here without exception, resubscription worked + assertTrue(true, "Resubscription succeeded"); + + // Clean up + allowFinish.countDown(); + } + + /** + * Test that task state is persisted to task store after client disconnect. + * Backported from Python test: test_disconnect_persists_final_task_to_store + */ + @Test + @Timeout(15) + void testDisconnectPersistsFinalTaskToStore() throws Exception { + String taskId = "persist-task-1"; + String contextId = "persist-ctx-1"; + + Message message = new Message.Builder() + .messageId("msg-persist") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params = new MessageSendParams(message, null, null); + + // Agent that completes after some delay + CountDownLatch agentStarted = new CountDownLatch(1); + CountDownLatch allowCompletion = new CountDownLatch(1); + + agentExecutor.setExecuteCallback((context, queue) -> { + agentStarted.countDown(); + + // Emit working status + Task workingTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + queue.enqueueEvent(workingTask); + + try { + allowCompletion.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + + // Emit final completed status + Task completedTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + queue.enqueueEvent(completedTask); + }); + + // Start streaming and simulate client disconnect + var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); + + // Wait for agent to start + assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); + + // Simulate client disconnect by not consuming the stream further + // In real scenarios, the reactive stream would be cancelled + + // Allow agent to complete in background + allowCompletion.countDown(); + + // Give time for background processing to persist the final state + Thread.sleep(2000); + + // Verify the final task state was persisted despite client disconnect + Task persistedTask = taskStore.get(taskId); + if (persistedTask != null) { + // If task was persisted, it should have the final state + assertTrue( + persistedTask.getStatus().state() == TaskState.COMPLETED || + persistedTask.getStatus().state() == TaskState.WORKING, + "Task should be persisted with working or completed state, got: " + persistedTask.getStatus().state() + ); + } + // Note: In some architectures, the task might not be persisted if the + // background consumption isn't implemented. This test documents the expected behavior. + } + + /** + * Simple test agent executor that allows controlling execution timing + */ + private static class TestAgentExecutor implements AgentExecutor { + private ExecuteCallback executeCallback; + private volatile boolean executing = false; + + interface ExecuteCallback { + void call(RequestContext context, EventQueue queue) throws JSONRPCError; + } + + void setExecuteCallback(ExecuteCallback callback) { + this.executeCallback = callback; + } + + boolean isExecuting() { + return executing; + } + + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + executing = true; + try { + if (executeCallback != null) { + executeCallback.call(context, eventQueue); + } + + // Enqueue a simple task completion event + Task completedTask = new Task.Builder() + .id(context.getTaskId()) + .contextId(context.getContextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + eventQueue.enqueueEvent(completedTask); + + } finally { + executing = false; + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + // Simple cancel implementation + executing = false; + } + } +} \ No newline at end of file diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index 8d2cee335..859790bcb 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -39,6 +39,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP eventQueue.enqueueEvent(task); } + // Sleep to allow task state persistence before TCK resubscribe test if (context.getMessage().getMessageId().startsWith("test-resubscribe-message-id")) { int timeoutMs = Integer.parseInt(System.getenv().getOrDefault("RESUBSCRIBE_TIMEOUT_MS", "3000")); System.out.println("====> task id starts with test-resubscribe-message-id, sleeping for " + timeoutMs + " ms"); From 75d4b98f3c39d29ae5ed51e0f9a71241de3f7c19 Mon Sep 17 00:00:00 2001 From: Niravkumar Patel Date: Mon, 22 Sep 2025 12:19:38 -0700 Subject: [PATCH 127/493] fix: Task.artifacts and Task.history null deserialization (fixes #257) (#258) # Description - Initialize artifacts and history fields with empty lists when null - Add comprehensive unit tests for deserialization scenarios - Ensures type safety for optional array fields per A2A specification Fixes #257 Co-authored-by: nirav patel --- spec/src/main/java/io/a2a/spec/Task.java | 4 +- .../io/a2a/spec/TaskDeserializationTest.java | 90 +++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index 9d948f383..e964d6b55 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -51,8 +51,8 @@ public Task(@JsonProperty("id") String id, @JsonProperty("contextId") String con this.id = id; this.contextId = contextId; this.status = status; - this.artifacts = artifacts; - this.history = history; + this.artifacts = artifacts != null ? List.copyOf(artifacts) : List.of(); + this.history = history != null ? List.copyOf(history) : List.of(); this.metadata = metadata; this.kind = kind; } diff --git a/spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java b/spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java new file mode 100644 index 000000000..fa67b59f5 --- /dev/null +++ b/spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java @@ -0,0 +1,90 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TaskDeserializationTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void testTaskWithMissingHistoryAndArtifacts() throws Exception { + // JSON without history and artifacts fields (common server response) + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "kind": "task" + } + """; + + Task task = objectMapper.readValue(json, Task.class); + + assertNotNull(task.getHistory(), "history should not be null"); + assertNotNull(task.getArtifacts(), "artifacts should not be null"); + + assertTrue(task.getHistory().isEmpty(), "history should be empty list when not provided"); + assertTrue(task.getArtifacts().isEmpty(), "artifacts should be empty list when not provided"); + } + + @Test + void testTaskWithExplicitNullValues() throws Exception { + // JSON with explicit null values + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "history": null, + "artifacts": null, + "kind": "task" + } + """; + + Task task = objectMapper.readValue(json, Task.class); + + // Should never be null even with explicit null in JSON + assertNotNull(task.getHistory(), "history should not be null even when JSON contains null"); + assertNotNull(task.getArtifacts(), "artifacts should not be null even when JSON contains null"); + + assertTrue(task.getHistory().isEmpty()); + assertTrue(task.getArtifacts().isEmpty()); + } + + @Test + void testTaskWithPopulatedArrays() throws Exception { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "history": [ + { + "role": "user", + "parts": [{"kind": "text", "text": "hello"}], + "messageId": "msg-1", + "kind": "message" + } + ], + "artifacts": [], + "kind": "task" + } + """; + + Task task = objectMapper.readValue(json, Task.class); + + assertNotNull(task.getHistory()); + assertEquals(1, task.getHistory().size()); + + assertNotNull(task.getArtifacts()); + assertTrue(task.getArtifacts().isEmpty()); + } +} From ca5f532c2bb321005705883f70a38a291da87e2c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 22 Sep 2025 16:56:27 -0400 Subject: [PATCH 128/493] fix: Update transport descriptions in README (#291) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bb50eba9c..e33743679 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,11 @@ The A2A Java SDK provides [reference A2A server implementations](reference) base [Server Integrations](#server-integrations) contains a list of community contributed integrations of the server with various runtimes. You might be able to use one of these for your target runtime, or you can use them as inspiration to create your own. #### Server Transports -The A2A Java SDK Reference Server implementations support a couple transports: JSON-RPC 2.0 and gRPC. +The A2A Java SDK Reference Server implementations support the following transports: + +* JSON-RPC 2.0 +* gRPC +* HTTP+JSON/REST To use the reference implementation with the JSON-RPC protocol, add the following dependency to your project: @@ -78,9 +82,6 @@ To use the reference implementation with the gRPC protocol, add the following de ``` -Note that you can add more than one of the above dependencies to your project depending on the transports -you'd like to support. - To use the reference implementation with the HTTP+JSON/REST protocol, add the following dependency to your project: > *⚠️ The `io.github.a2asdk` `groupId` below is temporary and will likely change for future releases.* @@ -94,6 +95,9 @@ To use the reference implementation with the HTTP+JSON/REST protocol, add the fo ``` +Note that you can add more than one of the above dependencies to your project depending on the transports +you'd like to support. + ### 2. Add a class that creates an A2A Agent Card ```java @@ -230,7 +234,11 @@ public class WeatherAgentExecutorProducer { ## A2A Client -The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. The Java client implementation currently supports two transport protocols: JSON-RPC 2.0 and gRPC. +The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. The Java client implementation supports the following transports: + +* JSON-RPC 2.0 +* gRPC +* HTTP+JSON/REST To make use of the Java `Client`: From a20116868eb189e3e486be47758c18d2328b1027 Mon Sep 17 00:00:00 2001 From: Maxwell Powell Date: Tue, 23 Sep 2025 06:17:35 -0700 Subject: [PATCH 129/493] fix!: Rename pushNotification to pushNotificationConfig (#286) (#290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes #286 🦕 --- client/base/src/main/java/io/a2a/client/Client.java | 4 ++-- .../requesthandlers/DefaultRequestHandler.java | 8 ++++---- .../src/main/java/io/a2a/grpc/utils/ProtoUtils.java | 4 ++-- .../java/io/a2a/spec/MessageSendConfiguration.java | 12 +++++------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index c7152a584..faae2ccdb 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -70,7 +70,7 @@ public void sendMessage(Message request, PushNotificationConfig pushNotification .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) .blocking(clientConfig.isPolling()) .historyLength(clientConfig.getHistoryLength()) - .pushNotification(pushNotificationConfiguration) + .pushNotificationConfig(pushNotificationConfiguration) .build(); MessageSendParams messageSendParams = new MessageSendParams.Builder() @@ -229,7 +229,7 @@ private MessageSendParams getMessageSendParams(Message request, ClientConfig cli .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) .blocking(clientConfig.isPolling()) .historyLength(clientConfig.getHistoryLength()) - .pushNotification(clientConfig.getPushNotificationConfig()) + .pushNotificationConfig(clientConfig.getPushNotificationConfig()) .build(); return new MessageSendParams.Builder() diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index baed1b98c..7a1e5b1f5 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -241,11 +241,11 @@ public Flow.Publisher onMessageSendStream( } if (pushConfigStore != null && params.configuration() != null && - params.configuration().pushNotification() != null) { + params.configuration().pushNotificationConfig() != null) { pushConfigStore.setInfo( createdTask.getId(), - params.configuration().pushNotification()); + params.configuration().pushNotificationConfig()); } } @@ -372,7 +372,7 @@ public void onDeleteTaskPushNotificationConfig( } private boolean shouldAddPushInfo(MessageSendParams params) { - return pushConfigStore != null && params.configuration() != null && params.configuration().pushNotification() != null; + return pushConfigStore != null && params.configuration() != null && params.configuration().pushNotificationConfig() != null; } private EnhancedRunnable registerAndExecuteAgentAsync(String taskId, RequestContext requestContext, EventQueue queue) { @@ -441,7 +441,7 @@ private MessageSendSetup initMessageSend(MessageSendParams params, ServerCallCon if (shouldAddPushInfo(params)) { LOGGER.debug("Adding push info"); - pushConfigStore.setInfo(task.getId(), params.configuration().pushNotification()); + pushConfigStore.setInfo(task.getId(), params.configuration().pushNotificationConfig()); } } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 5572cf440..6771814f8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -344,8 +344,8 @@ public static io.a2a.grpc.SendMessageConfiguration messageSendConfiguration(Mess if (messageSendConfiguration.historyLength() != null) { builder.setHistoryLength(messageSendConfiguration.historyLength()); } - if (messageSendConfiguration.pushNotification() != null) { - builder.setPushNotification(pushNotificationConfig(messageSendConfiguration.pushNotification())); + if (messageSendConfiguration.pushNotificationConfig() != null) { + builder.setPushNotification(pushNotificationConfig(messageSendConfiguration.pushNotificationConfig())); } builder.setBlocking(messageSendConfiguration.blocking()); return builder.build(); diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index 9693b221b..54a614fc7 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -5,15 +5,13 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; -import io.a2a.util.Assert; - /** * Defines configuration options for a `message/send` or `message/stream` request. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record MessageSendConfiguration(List acceptedOutputModes, Integer historyLength, - PushNotificationConfig pushNotification, boolean blocking) { + PushNotificationConfig pushNotificationConfig, boolean blocking) { public MessageSendConfiguration { if (historyLength != null && historyLength < 0) { @@ -24,7 +22,7 @@ public record MessageSendConfiguration(List acceptedOutputModes, Integer public static class Builder { List acceptedOutputModes; Integer historyLength; - PushNotificationConfig pushNotification; + PushNotificationConfig pushNotificationConfig; boolean blocking; public Builder acceptedOutputModes(List acceptedOutputModes) { @@ -32,8 +30,8 @@ public Builder acceptedOutputModes(List acceptedOutputModes) { return this; } - public Builder pushNotification(PushNotificationConfig pushNotification) { - this.pushNotification = pushNotification; + public Builder pushNotificationConfig(PushNotificationConfig pushNotificationConfig) { + this.pushNotificationConfig = pushNotificationConfig; return this; } @@ -48,7 +46,7 @@ public Builder blocking(boolean blocking) { } public MessageSendConfiguration build() { - return new MessageSendConfiguration(acceptedOutputModes, historyLength, pushNotification, blocking); + return new MessageSendConfiguration(acceptedOutputModes, historyLength, pushNotificationConfig, blocking); } } } From 9b8bbb22b64e6efeea91eb1deece1e786de01486 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 23 Sep 2025 17:38:20 +0100 Subject: [PATCH 130/493] feat: ServerCallContext ports from Python implementation (#285) Port server-side extension support and implement complete gRPC context access equivalent to Python's ServicerContext. Adds client/server metadata support, rich context information, and interceptor infrastructure while maintaining backward compatibility. --- .../client/transport/grpc/GrpcTransport.java | 75 ++++++++-- .../main/java/io/a2a/common/A2AHeaders.java | 17 +++ reference/grpc/pom.xml | 4 + .../quarkus/A2AExtensionsInterceptor.java | 70 +++++++++ .../grpc/quarkus/QuarkusGrpcHandler.java | 2 + .../server/apps/quarkus/A2AServerRoutes.java | 9 +- .../server/rest/quarkus/A2AServerRoutes.java | 11 +- .../java/io/a2a/server/ServerCallContext.java | 32 ++++- .../a2a/server/extensions/A2AExtensions.java | 46 ++++++ .../DefaultRequestHandler.java | 9 +- .../io/a2a/server/ServerCallContextTest.java | 133 ++++++++++++++++++ .../server/extensions/A2AExtensionsTest.java | 127 +++++++++++++++++ .../DefaultRequestHandlerBackgroundTest.java | 18 +-- .../grpc/context/GrpcContextKeys.java | 45 ++++++ .../transport/grpc/handler/GrpcHandler.java | 124 ++++++++++++++-- .../jsonrpc/handler/JSONRPCHandlerTest.java | 3 +- .../rest/handler/RestHandlerTest.java | 3 +- 17 files changed, 687 insertions(+), 41 deletions(-) create mode 100644 common/src/main/java/io/a2a/common/A2AHeaders.java create mode 100644 reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java create mode 100644 server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java create mode 100644 server-common/src/test/java/io/a2a/server/ServerCallContextTest.java create mode 100644 server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java create mode 100644 transport/grpc/src/main/java/io/a2a/transport/grpc/context/GrpcContextKeys.java diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index b313fb43a..87de05ecd 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -12,6 +12,7 @@ import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.common.A2AHeaders; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.CancelTaskRequest; import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; @@ -37,8 +38,9 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.grpc.Channel; - +import io.grpc.Metadata; import io.grpc.StatusRuntimeException; +import io.grpc.stub.MetadataUtils; import io.grpc.stub.StreamObserver; public class GrpcTransport implements ClientTransport { @@ -61,7 +63,8 @@ public EventKind sendMessage(MessageSendParams request, ClientCallContext contex SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest(request, context); try { - SendMessageResponse response = blockingStub.sendMessage(sendMessageRequest); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + SendMessageResponse response = stubWithMetadata.sendMessage(sendMessageRequest); if (response.hasMsg()) { return FromProto.message(response.getMsg()); } else if (response.hasTask()) { @@ -83,7 +86,8 @@ public void sendMessageStreaming(MessageSendParams request, Consumer streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); try { - asyncStub.sendStreamingMessage(grpcRequest, streamObserver); + A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context); + stubWithMetadata.sendStreamingMessage(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to send streaming message request: "); } @@ -101,7 +105,8 @@ public Task getTask(TaskQueryParams request, ClientCallContext context) throws A GetTaskRequest getTaskRequest = requestBuilder.build(); try { - return FromProto.task(blockingStub.getTask(getTaskRequest)); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + return FromProto.task(stubWithMetadata.getTask(getTaskRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to get task: "); } @@ -116,7 +121,8 @@ public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A .build(); try { - return FromProto.task(blockingStub.cancelTask(cancelTaskRequest)); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + return FromProto.task(stubWithMetadata.cancelTask(cancelTaskRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to cancel task: "); } @@ -135,7 +141,8 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN .build(); try { - return FromProto.taskPushNotificationConfig(blockingStub.createTaskPushNotificationConfig(grpcRequest)); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + return FromProto.taskPushNotificationConfig(stubWithMetadata.createTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to create task push notification config: "); } @@ -152,7 +159,8 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( .build(); try { - return FromProto.taskPushNotificationConfig(blockingStub.getTaskPushNotificationConfig(grpcRequest)); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + return FromProto.taskPushNotificationConfig(stubWithMetadata.getTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to get task push notification config: "); } @@ -169,7 +177,8 @@ public List listTaskPushNotificationConfigurations( .build(); try { - return blockingStub.listTaskPushNotificationConfig(grpcRequest).getConfigsList().stream() + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + return stubWithMetadata.listTaskPushNotificationConfig(grpcRequest).getConfigsList().stream() .map(FromProto::taskPushNotificationConfig) .collect(Collectors.toList()); } catch (StatusRuntimeException e) { @@ -187,7 +196,8 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC .build(); try { - blockingStub.deleteTaskPushNotificationConfig(grpcRequest); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + stubWithMetadata.deleteTaskPushNotificationConfig(grpcRequest); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to delete task push notification config: "); } @@ -206,7 +216,8 @@ public void resubscribe(TaskIdParams request, Consumer event StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); try { - asyncStub.taskSubscription(grpcRequest, streamObserver); + A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context); + stubWithMetadata.taskSubscription(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to resubscribe task push notification config: "); } @@ -234,6 +245,50 @@ private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messag return builder.build(); } + /** + * Creates gRPC metadata from ClientCallContext headers. + * Extracts headers like X-A2A-Extensions and sets them as gRPC metadata. + */ + private Metadata createGrpcMetadata(ClientCallContext context) { + Metadata metadata = new Metadata(); + + if (context != null && context.getHeaders() != null) { + // Set X-A2A-Extensions header if present + String extensionsHeader = context.getHeaders().get(A2AHeaders.X_A2A_EXTENSIONS); + if (extensionsHeader != null) { + Metadata.Key extensionsKey = Metadata.Key.of(A2AHeaders.X_A2A_EXTENSIONS, Metadata.ASCII_STRING_MARSHALLER); + metadata.put(extensionsKey, extensionsHeader); + } + + // Add other headers as needed in the future + // For now, we only handle X-A2A-Extensions + } + + return metadata; + } + + /** + * Creates a blocking stub with metadata attached from the ClientCallContext. + * + * @param context the client call context + * @return blocking stub with metadata interceptor + */ + private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(ClientCallContext context) { + Metadata metadata = createGrpcMetadata(context); + return blockingStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); + } + + /** + * Creates an async stub with metadata attached from the ClientCallContext. + * + * @param context the client call context + * @return async stub with metadata interceptor + */ + private A2AServiceStub createAsyncStubWithMetadata(ClientCallContext context) { + Metadata metadata = createGrpcMetadata(context); + return asyncStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); + } + private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams params) { return getTaskPushNotificationConfigName(params.id(), params.pushNotificationConfigId()); } diff --git a/common/src/main/java/io/a2a/common/A2AHeaders.java b/common/src/main/java/io/a2a/common/A2AHeaders.java new file mode 100644 index 000000000..5118a4365 --- /dev/null +++ b/common/src/main/java/io/a2a/common/A2AHeaders.java @@ -0,0 +1,17 @@ +package io.a2a.common; + +/** + * Common A2A protocol headers and constants. + */ +public final class A2AHeaders { + + /** + * HTTP header name for A2A extensions. + * Used to communicate which extensions are requested by the client. + */ + public static final String X_A2A_EXTENSIONS = "X-A2A-Extensions"; + + private A2AHeaders() { + // Utility class + } +} diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 8623b4154..4d8a08e9d 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -19,6 +19,10 @@ ${project.groupId} a2a-java-sdk-reference-common + + ${project.groupId} + a2a-java-sdk-common + ${project.groupId} a2a-java-sdk-transport-grpc diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java new file mode 100644 index 000000000..9f0559cdb --- /dev/null +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java @@ -0,0 +1,70 @@ +package io.a2a.server.grpc.quarkus; + +import jakarta.enterprise.context.ApplicationScoped; +import io.grpc.Context; +import io.grpc.Contexts; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.a2a.common.A2AHeaders; +import io.a2a.transport.grpc.context.GrpcContextKeys; + +/** + * gRPC server interceptor that captures request metadata and context information, + * providing equivalent functionality to Python's grpc.aio.ServicerContext. + * + * This interceptor: + * - Extracts A2A extension headers from incoming requests + * - Captures ServerCall and Metadata for rich context access + * - Stores context information in gRPC Context for service method access + * - Provides proper equivalence to Python's ServicerContext + */ +@ApplicationScoped +public class A2AExtensionsInterceptor implements ServerInterceptor { + + + @Override + public ServerCall.Listener interceptCall( + ServerCall serverCall, + Metadata metadata, + ServerCallHandler serverCallHandler) { + + // Extract A2A extensions header + Metadata.Key extensionsKey = + Metadata.Key.of(A2AHeaders.X_A2A_EXTENSIONS, Metadata.ASCII_STRING_MARSHALLER); + String extensions = metadata.get(extensionsKey); + + // Create enhanced context with rich information (equivalent to Python's ServicerContext) + Context context = Context.current() + // Store complete metadata for full header access + .withValue(GrpcContextKeys.METADATA_KEY, metadata) + // Store method name (equivalent to Python's context.method()) + .withValue(GrpcContextKeys.METHOD_NAME_KEY, serverCall.getMethodDescriptor().getFullMethodName()) + // Store peer information for client connection details + .withValue(GrpcContextKeys.PEER_INFO_KEY, getPeerInfo(serverCall)); + + // Store A2A extensions if present + if (extensions != null) { + context = context.withValue(GrpcContextKeys.EXTENSIONS_HEADER_KEY, extensions); + } + + // Proceed with the call in the enhanced context + return Contexts.interceptCall(context, serverCall, metadata, serverCallHandler); + } + + /** + * Safely extracts peer information from the ServerCall. + * + * @param serverCall the gRPC ServerCall + * @return peer information string, or "unknown" if not available + */ + private String getPeerInfo(ServerCall serverCall) { + try { + Object remoteAddr = serverCall.getAttributes().get(io.grpc.Grpc.TRANSPORT_ATTR_REMOTE_ADDR); + return remoteAddr != null ? remoteAddr.toString() : "unknown"; + } catch (Exception e) { + return "unknown"; + } + } +} diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 9cfba609a..355416441 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -9,8 +9,10 @@ import io.a2a.transport.grpc.handler.CallContextFactory; import io.a2a.transport.grpc.handler.GrpcHandler; import io.quarkus.grpc.GrpcService; +import io.quarkus.grpc.RegisterInterceptor; @GrpcService +@RegisterInterceptor(A2AExtensionsInterceptor.class) public class QuarkusGrpcHandler extends GrpcHandler { private final AgentCard agentCard; diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 29791d653..3958d8eed 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -4,6 +4,7 @@ import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; @@ -19,9 +20,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.io.JsonEOFException; import com.fasterxml.jackson.databind.JsonNode; +import io.a2a.common.A2AHeaders; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; +import io.a2a.server.extensions.A2AExtensions; import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; import io.a2a.spec.CancelTaskRequest; @@ -241,7 +244,11 @@ public String getUsername() { headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); state.put("headers", headers); - return new ServerCallContext(user, state); + // Extract requested extensions from X-A2A-Extensions header + List extensionHeaderValues = rc.request().headers().getAll(A2AHeaders.X_A2A_EXTENSIONS); + Set requestedExtensions = A2AExtensions.getRequestedExtensions(extensionHeaderValues); + + return new ServerCallContext(user, state, requestedExtensions); } else { CallContextFactory builder = callContextFactory.get(); return builder.build(rc); diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 1a90cda75..5e0f3e0f8 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -12,6 +12,7 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; +import io.a2a.common.A2AHeaders; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; @@ -34,9 +35,13 @@ import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import io.a2a.server.extensions.A2AExtensions; + @Singleton public class A2AServerRoutes { @@ -308,7 +313,11 @@ public String getUsername() { headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); state.put("headers", headers); - return new ServerCallContext(user, state); + // Extract requested extensions from X-A2A-Extensions header + List extensionHeaderValues = rc.request().headers().getAll(A2AHeaders.X_A2A_EXTENSIONS); + Set requestedExtensions = A2AExtensions.getRequestedExtensions(extensionHeaderValues); + + return new ServerCallContext(user, state, requestedExtensions); } else { CallContextFactory builder = callContextFactory.get(); return builder.build(rc); diff --git a/server-common/src/main/java/io/a2a/server/ServerCallContext.java b/server-common/src/main/java/io/a2a/server/ServerCallContext.java index 558f01eda..cef84700e 100644 --- a/server-common/src/main/java/io/a2a/server/ServerCallContext.java +++ b/server-common/src/main/java/io/a2a/server/ServerCallContext.java @@ -1,6 +1,8 @@ package io.a2a.server; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import io.a2a.server.auth.User; @@ -10,10 +12,14 @@ public class ServerCallContext { private final Map modelConfig = new ConcurrentHashMap<>(); private final Map state; private final User user; + private final Set requestedExtensions; + private final Set activatedExtensions; - public ServerCallContext(User user, Map state) { + public ServerCallContext(User user, Map state, Set requestedExtensions) { this.user = user; this.state = state; + this.requestedExtensions = new HashSet<>(requestedExtensions); + this.activatedExtensions = new HashSet<>(); // Always starts empty, populated later by application code } public Map getState() { @@ -23,4 +29,28 @@ public Map getState() { public User getUser() { return user; } + + public Set getRequestedExtensions() { + return new HashSet<>(requestedExtensions); + } + + public Set getActivatedExtensions() { + return new HashSet<>(activatedExtensions); + } + + public void activateExtension(String extensionUri) { + activatedExtensions.add(extensionUri); + } + + public void deactivateExtension(String extensionUri) { + activatedExtensions.remove(extensionUri); + } + + public boolean isExtensionActivated(String extensionUri) { + return activatedExtensions.contains(extensionUri); + } + + public boolean isExtensionRequested(String extensionUri) { + return requestedExtensions.contains(extensionUri); + } } diff --git a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java new file mode 100644 index 000000000..fec151366 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java @@ -0,0 +1,46 @@ +package io.a2a.server.extensions; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import io.a2a.common.A2AHeaders; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentExtension; + +public class A2AExtensions { + + public static Set getRequestedExtensions(List values) { + Set extensions = new HashSet<>(); + if (values == null) { + return extensions; + } + + for (String value : values) { + if (value != null) { + // Split by comma and trim whitespace + String[] parts = value.split(","); + for (String part : parts) { + String trimmed = part.trim(); + if (!trimmed.isEmpty()) { + extensions.add(trimmed); + } + } + } + } + + return extensions; + } + + public static AgentExtension findExtensionByUri(AgentCard card, String uri) { + if (card.capabilities() == null || card.capabilities().extensions() == null) { + return null; + } + for (AgentExtension extension : card.capabilities().extensions()) { + if (extension.uri().equals(uri)) { + return extension; + } + } + return null; + } +} diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 7a1e5b1f5..8bace7be2 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -15,8 +16,9 @@ import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; -import java.util.Set; -import java.util.concurrent.TimeUnit; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; @@ -50,9 +52,6 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.UnsupportedOperationError; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java b/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java new file mode 100644 index 000000000..c12a48e27 --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java @@ -0,0 +1,133 @@ +package io.a2a.server; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import io.a2a.server.auth.User; + +class ServerCallContextTest { + + @Test + void testDefaultConstructor() { + User user = new TestUser(); + Map state = new HashMap<>(); + + ServerCallContext context = new ServerCallContext(user, state, new HashSet<>()); + + assertEquals(user, context.getUser()); + assertEquals(state, context.getState()); + assertTrue(context.getRequestedExtensions().isEmpty()); + assertTrue(context.getActivatedExtensions().isEmpty()); + } + + @Test + void testConstructorWithRequestedExtensions() { + User user = new TestUser(); + Map state = new HashMap<>(); + Set requestedExtensions = Set.of("foo", "bar"); + + ServerCallContext context = new ServerCallContext(user, state, requestedExtensions); + + assertEquals(user, context.getUser()); + assertEquals(state, context.getState()); + assertEquals(requestedExtensions, context.getRequestedExtensions()); + assertTrue(context.getActivatedExtensions().isEmpty()); + } + + @Test + void testConstructorWithRequestedAndActivatedExtensions() { + User user = new TestUser(); + Map state = new HashMap<>(); + Set requestedExtensions = Set.of("foo", "bar"); + ServerCallContext context = new ServerCallContext(user, state, requestedExtensions); + + // Manually activate extensions since they start empty + context.activateExtension("foo"); + + assertEquals(user, context.getUser()); + assertEquals(state, context.getState()); + assertEquals(requestedExtensions, context.getRequestedExtensions()); + assertEquals(Set.of("foo"), context.getActivatedExtensions()); + } + + @Test + void testExtensionActivation() { + User user = new TestUser(); + Map state = new HashMap<>(); + Set requestedExtensions = Set.of("foo", "bar"); + + ServerCallContext context = new ServerCallContext(user, state, requestedExtensions); + + // Initially no extensions are activated + assertFalse(context.isExtensionActivated("foo")); + assertFalse(context.isExtensionActivated("bar")); + + // Activate an extension + context.activateExtension("foo"); + assertTrue(context.isExtensionActivated("foo")); + assertFalse(context.isExtensionActivated("bar")); + + // Activate another extension + context.activateExtension("bar"); + assertTrue(context.isExtensionActivated("foo")); + assertTrue(context.isExtensionActivated("bar")); + + // Deactivate an extension + context.deactivateExtension("foo"); + assertFalse(context.isExtensionActivated("foo")); + assertTrue(context.isExtensionActivated("bar")); + } + + @Test + void testExtensionRequested() { + User user = new TestUser(); + Map state = new HashMap<>(); + Set requestedExtensions = Set.of("foo", "bar"); + + ServerCallContext context = new ServerCallContext(user, state, requestedExtensions); + + assertTrue(context.isExtensionRequested("foo")); + assertTrue(context.isExtensionRequested("bar")); + assertFalse(context.isExtensionRequested("baz")); + } + + @Test + void testExtensionCollectionsAreDefensiveCopies() { + User user = new TestUser(); + Map state = new HashMap<>(); + Set requestedExtensions = Set.of("foo", "bar"); + + ServerCallContext context = new ServerCallContext(user, state, requestedExtensions); + + // Modifying returned sets should not affect the context + Set returnedRequested = context.getRequestedExtensions(); + returnedRequested.add("baz"); + assertFalse(context.isExtensionRequested("baz")); + + context.activateExtension("foo"); + Set returnedActivated = context.getActivatedExtensions(); + returnedActivated.add("bar"); + assertFalse(context.isExtensionActivated("bar")); + } + + // Simple test implementation of User interface + private static class TestUser implements User { + @Override + public boolean isAuthenticated() { + return true; + } + + @Override + public String getUsername() { + return "test-user"; + } + } +} diff --git a/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java b/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java new file mode 100644 index 000000000..1fc00ded3 --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java @@ -0,0 +1,127 @@ +package io.a2a.server.extensions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentExtension; + +class A2AExtensionsTest { + + @Test + void testGetRequestedExtensions() { + // Test empty list + Set result = A2AExtensions.getRequestedExtensions(Collections.emptyList()); + assertTrue(result.isEmpty()); + + // Test single extension + result = A2AExtensions.getRequestedExtensions(Arrays.asList("foo")); + assertEquals(Set.of("foo"), result); + + // Test multiple extensions in separate values + result = A2AExtensions.getRequestedExtensions(Arrays.asList("foo", "bar")); + assertEquals(Set.of("foo", "bar"), result); + + // Test comma-separated extensions with space + result = A2AExtensions.getRequestedExtensions(Arrays.asList("foo, bar")); + assertEquals(Set.of("foo", "bar"), result); + + // Test comma-separated extensions without space + result = A2AExtensions.getRequestedExtensions(Arrays.asList("foo,bar")); + assertEquals(Set.of("foo", "bar"), result); + + // Test mixed format + result = A2AExtensions.getRequestedExtensions(Arrays.asList("foo", "bar,baz")); + assertEquals(Set.of("foo", "bar", "baz"), result); + + // Test with empty values and extra spaces + result = A2AExtensions.getRequestedExtensions(Arrays.asList("foo,, bar", "baz")); + assertEquals(Set.of("foo", "bar", "baz"), result); + + // Test with leading/trailing spaces + result = A2AExtensions.getRequestedExtensions(Arrays.asList(" foo , bar ", "baz")); + assertEquals(Set.of("foo", "bar", "baz"), result); + + // Test null list + result = A2AExtensions.getRequestedExtensions(null); + assertTrue(result.isEmpty()); + + // Test list with null values + List listWithNulls = Arrays.asList("foo", null, "bar"); + result = A2AExtensions.getRequestedExtensions(listWithNulls); + assertEquals(Set.of("foo", "bar"), result); + } + + @Test + void testFindExtensionByUri() { + AgentExtension ext1 = new AgentExtension.Builder() + .uri("foo") + .description("The Foo extension") + .build(); + AgentExtension ext2 = new AgentExtension.Builder() + .uri("bar") + .description("The Bar extension") + .build(); + + AgentCard card = new AgentCard.Builder() + .name("Test Agent") + .description("Test Agent Description") + .version("1.0") + .url("http://test.com") + .skills(Collections.emptyList()) + .defaultInputModes(Arrays.asList("text/plain")) + .defaultOutputModes(Arrays.asList("text/plain")) + .capabilities(new AgentCapabilities.Builder() + .extensions(Arrays.asList(ext1, ext2)) + .build()) + .build(); + + assertEquals(ext1, A2AExtensions.findExtensionByUri(card, "foo")); + assertEquals(ext2, A2AExtensions.findExtensionByUri(card, "bar")); + assertNull(A2AExtensions.findExtensionByUri(card, "baz")); + } + + @Test + void testFindExtensionByUriNoExtensions() { + AgentCard card = new AgentCard.Builder() + .name("Test Agent") + .description("Test Agent Description") + .version("1.0") + .url("http://test.com") + .skills(Collections.emptyList()) + .defaultInputModes(Arrays.asList("text/plain")) + .defaultOutputModes(Arrays.asList("text/plain")) + .capabilities(new AgentCapabilities.Builder() + .extensions(null) + .build()) + .build(); + + assertNull(A2AExtensions.findExtensionByUri(card, "foo")); + } + + @Test + void testFindExtensionByUriNoCapabilities() { + // Test with empty capabilities (no extensions list) + AgentCard card = new AgentCard.Builder() + .name("Test Agent") + .description("Test Agent Description") + .version("1.0") + .url("http://test.com") + .skills(Collections.emptyList()) + .defaultInputModes(Arrays.asList("text/plain")) + .defaultOutputModes(Arrays.asList("text/plain")) + .capabilities(new AgentCapabilities.Builder().build()) + .build(); + + assertNull(A2AExtensions.findExtensionByUri(card, "foo")); + } +} diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java index 412f1a8cf..284465882 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java @@ -1,26 +1,17 @@ package io.a2a.server.requesthandlers; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.agentexecution.SimpleRequestContextBuilder; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventQueue; import io.a2a.server.events.InMemoryQueueManager; @@ -32,6 +23,9 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TextPart; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; /** * Tests for DefaultRequestHandler background cleanup and task tracking functionality, @@ -60,7 +54,7 @@ void setUp() { Executors.newCachedThreadPool() ); - serverCallContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, java.util.Map.of()); + serverCallContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of(), Set.of()); } /** diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/context/GrpcContextKeys.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/context/GrpcContextKeys.java new file mode 100644 index 000000000..483daf7e8 --- /dev/null +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/context/GrpcContextKeys.java @@ -0,0 +1,45 @@ +package io.a2a.transport.grpc.context; + +import io.grpc.Context; + +/** + * Shared gRPC context keys for A2A protocol data. + * + * These keys provide access to gRPC context information similar to + * Python's grpc.aio.ServicerContext, enabling rich context access + * in service method implementations. + */ +public final class GrpcContextKeys { + + /** + * Context key for storing the X-A2A-Extensions header value. + * Set by server interceptors and accessed by service handlers. + */ + public static final Context.Key EXTENSIONS_HEADER_KEY = + Context.key("x-a2a-extensions"); + + /** + * Context key for storing the complete gRPC Metadata object. + * Provides access to all request headers and metadata. + */ + public static final Context.Key METADATA_KEY = + Context.key("grpc-metadata"); + + /** + * Context key for storing the method name being called. + * Equivalent to Python's context.method() functionality. + */ + public static final Context.Key METHOD_NAME_KEY = + Context.key("grpc-method-name"); + + /** + * Context key for storing the peer information. + * Provides access to client connection details. + */ + public static final Context.Key PEER_INFO_KEY = + Context.key("grpc-peer-info"); + + private GrpcContextKeys() { + // Utility class + } +} diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index e19ba80ef..b259e91ab 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -6,11 +6,16 @@ import jakarta.enterprise.inject.Vetoed; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; + +import io.grpc.Context; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; import com.google.protobuf.Empty; import io.a2a.grpc.A2AServiceGrpc; @@ -19,6 +24,8 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; +import io.a2a.server.extensions.A2AExtensions; +import io.a2a.transport.grpc.context.GrpcContextKeys; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.spec.AgentCard; import io.a2a.spec.ContentTypeNotSupportedError; @@ -55,6 +62,8 @@ public abstract class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { private AtomicBoolean initialised = new AtomicBoolean(false); + private static final Logger LOGGER = Logger.getLogger(GrpcHandler.class.getName()); + public GrpcHandler() { } @@ -312,17 +321,48 @@ private ServerCallContext createCallContext(StreamObserver responseObserv User user = UnauthenticatedUser.INSTANCE; Map state = new HashMap<>(); - // TODO: ARCHITECTURAL LIMITATION - StreamObserver is NOT equivalent to Python's grpc.aio.ServicerContext - // In Python: context parameter provides metadata, peer info, cancellation, etc. - // In Java: the proper equivalent would be ServerCall + Metadata from ServerInterceptor - // Current compromise: Store StreamObserver for basic functionality, but it lacks rich context - // - // FUTURE ENHANCEMENT: Implement ServerInterceptor to capture ServerCall + Metadata, - // store in gRPC Context using Context.Key, then access via Context.current().get(key) - // This would provide proper equivalence to Python's ServicerContext + // Enhanced gRPC context access - equivalent to Python's grpc.aio.ServicerContext + // The A2AExtensionsInterceptor captures ServerCall + Metadata and stores them in gRPC Context + // This provides proper equivalence to Python's ServicerContext for metadata access + // Note: StreamObserver is still stored for response handling state.put("grpc_response_observer", responseObserver); - return new ServerCallContext(user, state); + // Add rich gRPC context information if available (set by interceptor) + // This provides equivalent functionality to Python's grpc.aio.ServicerContext + try { + Context currentContext = Context.current(); + if (currentContext != null) { + state.put("grpc_context", currentContext); + + // Add specific context information for easy access + io.grpc.Metadata grpcMetadata = GrpcContextKeys.METADATA_KEY.get(currentContext); + if (grpcMetadata != null) { + state.put("grpc_metadata", grpcMetadata); + } + + String methodName = GrpcContextKeys.METHOD_NAME_KEY.get(currentContext); + if (methodName != null) { + state.put("grpc_method_name", methodName); + } + + String peerInfo = GrpcContextKeys.PEER_INFO_KEY.get(currentContext); + if (peerInfo != null) { + state.put("grpc_peer_info", peerInfo); + } + } + } catch (Exception e) { + // Context not available - continue without it + LOGGER.fine(() -> "Error getting data from current context" + e); + } + + // Extract requested extensions from gRPC context (set by interceptor) + Set requestedExtensions = new HashSet<>(); + String extensionsHeader = getExtensionsFromContext(); + if (extensionsHeader != null) { + requestedExtensions = A2AExtensions.getRequestedExtensions(List.of(extensionsHeader)); + } + + return new ServerCallContext(user, state, requestedExtensions); } else { // TODO: CallContextFactory interface expects ServerCall + Metadata, but we only have StreamObserver // This is another manifestation of the architectural limitation mentioned above @@ -418,4 +458,70 @@ public static void setStreamingSubscribedRunnable(Runnable runnable) { protected abstract AgentCard getAgentCard(); protected abstract CallContextFactory getCallContextFactory(); + + /** + * Attempts to extract the X-A2A-Extensions header from the current gRPC context. + * This will only work if a server interceptor has been configured to capture + * the metadata and store it in the context. + * + * @return the extensions header value, or null if not available + */ + private String getExtensionsFromContext() { + try { + return GrpcContextKeys.EXTENSIONS_HEADER_KEY.get(); + } catch (Exception e) { + // Context not available or key not set + return null; + } + } + + /** + * Utility methods for accessing gRPC context information. + * These provide equivalent functionality to Python's grpc.aio.ServicerContext methods. + */ + + /** + * Generic helper method to safely access gRPC context values. + * + * @param key the context key to retrieve + * @return the context value, or null if not available + */ + private static T getFromContext(Context.Key key) { + try { + return key.get(); + } catch (Exception e) { + // Context not available or key not set + return null; + } + } + + /** + * Gets the complete gRPC metadata from the current context. + * Equivalent to Python's context.invocation_metadata. + * + * @return the gRPC Metadata object, or null if not available + */ + protected static io.grpc.Metadata getCurrentMetadata() { + return getFromContext(GrpcContextKeys.METADATA_KEY); + } + + /** + * Gets the current gRPC method name. + * Equivalent to Python's context.method(). + * + * @return the method name, or null if not available + */ + protected static String getCurrentMethodName() { + return getFromContext(GrpcContextKeys.METHOD_NAME_KEY); + } + + /** + * Gets the peer information for the current gRPC call. + * Equivalent to Python's context.peer(). + * + * @return the peer information, or null if not available + */ + protected static String getCurrentPeerInfo() { + return getFromContext(GrpcContextKeys.PEER_INFO_KEY); + } } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 19137ab01..b37724e56 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.HashSet; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -76,7 +77,7 @@ public class JSONRPCHandlerTest extends AbstractA2ARequestHandlerTest { - private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar")); + private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar"), new HashSet<>()); @Test public void testOnGetTaskSuccess() throws Exception { diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index d8b355dc3..409998f6b 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -1,6 +1,7 @@ package io.a2a.transport.rest.handler; import com.google.protobuf.InvalidProtocolBufferException; +import java.util.HashSet; import java.util.Map; import io.a2a.server.ServerCallContext; @@ -18,7 +19,7 @@ public class RestHandlerTest extends AbstractA2ARequestHandlerTest { - private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar")); + private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar"), new HashSet<>()); @Test public void testGetTaskSuccess() { From 3be6ee9dbba4b811c5ee70e6f01fa0648ab74eec Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 25 Sep 2025 05:20:35 -0400 Subject: [PATCH 131/493] fix: Fix usages of ClientConfig#polling (#297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes https://github.com/a2aproject/a2a-java/issues/294 🦕 --- .../src/main/java/io/a2a/client/Client.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index faae2ccdb..da5b56cec 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -66,12 +66,7 @@ public void sendMessage(Message request, List @Override public void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, Map metatadata, ClientCallContext context) throws A2AClientException { - MessageSendConfiguration messageSendConfiguration = new MessageSendConfiguration.Builder() - .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) - .blocking(clientConfig.isPolling()) - .historyLength(clientConfig.getHistoryLength()) - .pushNotificationConfig(pushNotificationConfiguration) - .build(); + MessageSendConfiguration messageSendConfiguration = createMessageSendConfiguration(pushNotificationConfiguration); MessageSendParams messageSendParams = new MessageSendParams.Builder() .message(request) @@ -155,6 +150,15 @@ private ClientEvent getClientEvent(StreamingEventKind event, ClientTaskManager t } } + private MessageSendConfiguration createMessageSendConfiguration(PushNotificationConfig pushNotificationConfig) { + return new MessageSendConfiguration.Builder() + .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) + .blocking(!clientConfig.isPolling()) + .historyLength(clientConfig.getHistoryLength()) + .pushNotificationConfig(pushNotificationConfig) + .build(); + } + private void sendMessage(MessageSendParams messageSendParams, List> consumers, Consumer errorHandler, ClientCallContext context) throws A2AClientException { if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { @@ -225,12 +229,7 @@ private void consume(ClientEvent clientEvent, AgentCard agentCard, List Date: Thu, 25 Sep 2025 14:45:10 +0100 Subject: [PATCH 132/493] feat: Add JPA Database TaskStore (#293) # Description Fixes #279 and #278 --- README.md | 3 +- extras/README.md | 7 + extras/taskstore-database-jpa/README.md | 79 ++++++++ extras/taskstore-database-jpa/pom.xml | 102 ++++++++++ .../database/jpa/JpaDatabaseTaskStore.java | 72 +++++++ .../taskstore/database/jpa/JpaTask.java | 72 +++++++ .../src/main/resources/META-INF/beans.xml | 6 + .../JpaDatabaseTaskStoreIntegrationTest.java | 153 +++++++++++++++ .../jpa/JpaDatabaseTaskStoreTest.java | 182 ++++++++++++++++++ .../database/jpa/TestAgentCardProducer.java | 36 ++++ .../database/jpa/TestAgentExecutor.java | 64 ++++++ .../test/resources/META-INF/persistence.xml | 45 +++++ .../src/test/resources/application.properties | 11 ++ pom.xml | 1 + 14 files changed, 832 insertions(+), 1 deletion(-) create mode 100644 extras/README.md create mode 100644 extras/taskstore-database-jpa/README.md create mode 100644 extras/taskstore-database-jpa/pom.xml create mode 100644 extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java create mode 100644 extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java create mode 100644 extras/taskstore-database-jpa/src/main/resources/META-INF/beans.xml create mode 100644 extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java create mode 100644 extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java create mode 100644 extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java create mode 100644 extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java create mode 100644 extras/taskstore-database-jpa/src/test/resources/META-INF/persistence.xml create mode 100644 extras/taskstore-database-jpa/src/test/resources/application.properties diff --git a/README.md b/README.md index e33743679..52bdea529 100644 --- a/README.md +++ b/README.md @@ -638,5 +638,6 @@ To contribute an integration, please see [CONTRIBUTING_INTEGRATIONS.md](CONTRIBU * [reference/grpc/README.md](reference/grpc/README.md) - gRPC Reference implementation, based on Quarkus. * https://github.com/wildfly-extras/a2a-java-sdk-server-jakarta - This integration is based on Jakarta EE, and should work in all runtimes supporting the [Jakarta EE Web Profile](https://jakarta.ee/specifications/webprofile/). - +# Extras +See the [`extras`](./extras/README.md) folder for extra functionality not provided by the SDK itself! diff --git a/extras/README.md b/extras/README.md new file mode 100644 index 000000000..1c54e6eb4 --- /dev/null +++ b/extras/README.md @@ -0,0 +1,7 @@ +# A2A Java SDK - Extras + +This directory contains additions to what is provided by the default SDK implementations. + +Please see the README's of each child directory for more details. + +[`taskstore-database-jpa`](./taskstore-database-jpa/README.md) - Replaces the default `InMemoryTaskStore` with a `TaskStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. \ No newline at end of file diff --git a/extras/taskstore-database-jpa/README.md b/extras/taskstore-database-jpa/README.md new file mode 100644 index 000000000..a1312aad2 --- /dev/null +++ b/extras/taskstore-database-jpa/README.md @@ -0,0 +1,79 @@ +# A2A Java SDK - JPA Database TaskStore + +This module provides a JPA-based implementation of the `TaskStore` interface that persists tasks to a relational database instead of keeping them in memory. + +The persistence is done with the Jakarta Persistence API, so this should be suitable for any JPA 3.0+ provider and Jakarta EE application server. + +## Quick Start + +### 1. Add Dependency + +Add this module to your project's `pom.xml`: + +```xml + + io.github.a2asdk + a2a-java-extras-taskstore-database-jpa + ${a2a.version} + +``` + +The `JpaDatabaseTaskStore` is annotated in such a way that it should take precedence over the default `InMemoryTaskStore`. Hence, it is a drop-in replacement. + +### 2. Configure Database + +The following examples assume you are using PostgreSQL as your database. To use another database, adjust as needed for your environment. + +#### For Quarkus Reference Servers + +Add to your `application.properties`: + +```properties +# Database configuration +quarkus.datasource.db-kind=postgresql +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/a2a_tasks +quarkus.datasource.username=your_username +quarkus.datasource.password=your_password + +# Hibernate configuration +quarkus.hibernate-orm.database.generation=update +``` + +#### For WildFly/Jakarta EE Servers + +Create or update your `persistence.xml`: + +```xml + + + + java:jboss/datasources/A2ATasksDS + + io.a2a.extras.taskstore.database.jpa.JpaTask + true + + + + + + + + +``` + +### 3. Database Schema + +The module will automatically create the required table: + +```sql +CREATE TABLE a2a_tasks ( + task_id VARCHAR(255) PRIMARY KEY, + task_data TEXT NOT NULL +); +``` + +## Configuration Options + +### Persistence Unit Name + +The module uses the persistence unit name `"a2a-java"`. Ensure your `persistence.xml` defines a persistence unit with this name. diff --git a/extras/taskstore-database-jpa/pom.xml b/extras/taskstore-database-jpa/pom.xml new file mode 100644 index 000000000..872cbb874 --- /dev/null +++ b/extras/taskstore-database-jpa/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta2-SNAPSHOT + ../../pom.xml + + a2a-java-extras-taskstore-database-jpa + + jar + + Java A2A Extras: JPA Database TaskStore + Java SDK for the Agent2Agent Protocol (A2A) - Extras - JPA Database TaskStore + + + + ${project.groupId} + a2a-java-sdk-server-common + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + jakarta.persistence + jakarta.persistence-api + + + org.slf4j + slf4j-api + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-rest-client-jackson + test + + + org.junit.jupiter + junit-jupiter-api + test + + + io.rest-assured + rest-assured + test + + + jakarta.transaction + jakarta.transaction-api + + + io.quarkus + quarkus-hibernate-orm + test + + + io.quarkus + quarkus-jdbc-h2 + test + + + + ${project.groupId} + a2a-java-sdk-reference-jsonrpc + test + + + ${project.groupId} + a2a-java-sdk-client-transport-jsonrpc + test + + + ${project.groupId} + a2a-java-sdk-client + test + + + io.quarkus + quarkus-reactive-routes + test + + + \ No newline at end of file diff --git a/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java new file mode 100644 index 000000000..5eecc20b5 --- /dev/null +++ b/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -0,0 +1,72 @@ +package io.a2a.extras.taskstore.database.jpa; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.Task; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ApplicationScoped +@Alternative +@Priority(50) +public class JpaDatabaseTaskStore implements TaskStore { + + private static final Logger LOGGER = LoggerFactory.getLogger(JpaDatabaseTaskStore.class); + + @PersistenceContext(unitName = "a2a-java") + EntityManager em; + + @Transactional + @Override + public void save(Task task) { + LOGGER.debug("Saving task with ID: {}", task.getId()); + try { + JpaTask jpaTask = JpaTask.createFromTask(task); + em.merge(jpaTask); + LOGGER.debug("Persisted/updated task with ID: {}", task.getId()); + } catch (JsonProcessingException e) { + LOGGER.error("Failed to serialize task with ID: {}", task.getId(), e); + throw new RuntimeException("Failed to serialize task with ID: " + task.getId(), e); + } + } + + @Transactional + @Override + public Task get(String taskId) { + LOGGER.debug("Retrieving task with ID: {}", taskId); + JpaTask jpaTask = em.find(JpaTask.class, taskId); + if (jpaTask == null) { + LOGGER.debug("Task not found with ID: {}", taskId); + return null; + } + + try { + Task task = jpaTask.getTask(); + LOGGER.debug("Successfully retrieved task with ID: {}", taskId); + return task; + } catch (JsonProcessingException e) { + LOGGER.error("Failed to deserialize task with ID: {}", taskId, e); + throw new RuntimeException("Failed to deserialize task with ID: " + taskId, e); + } + } + + @Transactional + @Override + public void delete(String taskId) { + LOGGER.debug("Deleting task with ID: {}", taskId); + JpaTask jpaTask = em.find(JpaTask.class, taskId); + if (jpaTask != null) { + em.remove(jpaTask); + LOGGER.debug("Successfully deleted task with ID: {}", taskId); + } else { + LOGGER.debug("Task not found for deletion with ID: {}", taskId); + } + } +} diff --git a/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java new file mode 100644 index 000000000..a3bae673f --- /dev/null +++ b/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -0,0 +1,72 @@ +package io.a2a.extras.taskstore.database.jpa; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.spec.Task; +import io.a2a.util.Utils; + +@Entity +@Table(name = "a2a_tasks") +public class JpaTask { + @Id + @Column(name = "task_id") + private String id; + + @Column(name = "task_data", columnDefinition = "TEXT", nullable = false) + private String taskJson; + + @Transient + private Task task; + + // Default constructor required by JPA + public JpaTask() { + } + + public JpaTask(String id, String taskJson) { + this.id = id; + this.taskJson = taskJson; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTaskJson() { + return taskJson; + } + + public void setTaskJson(String taskJson) { + this.taskJson = taskJson; + } + + public Task getTask() throws JsonProcessingException { + if (task == null) { + this.task = Utils.unmarshalFrom(taskJson, Task.TYPE_REFERENCE); + } + return task; + } + + public void setTask(Task task) throws JsonProcessingException { + taskJson = Utils.OBJECT_MAPPER.writeValueAsString(task); + if (id == null) { + id = task.getId(); + } + this.task = task; + } + + static JpaTask createFromTask(Task task) throws JsonProcessingException { + String json = Utils.OBJECT_MAPPER.writeValueAsString(task); + JpaTask jpaTask = new JpaTask(task.getId(), json); + jpaTask.task = task; + return jpaTask; + } +} diff --git a/extras/taskstore-database-jpa/src/main/resources/META-INF/beans.xml b/extras/taskstore-database-jpa/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..ffc8abb42 --- /dev/null +++ b/extras/taskstore-database-jpa/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java new file mode 100644 index 000000000..3d1066553 --- /dev/null +++ b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java @@ -0,0 +1,153 @@ +package io.a2a.extras.taskstore.database.jpa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import jakarta.inject.Inject; + +import io.a2a.client.Client; +import io.a2a.client.ClientEvent; +import io.a2a.client.TaskEvent; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.server.PublicAgentCard; +import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; +import io.a2a.spec.TextPart; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * End-to-end integration test that verifies the JPA TaskStore works correctly + * with the full client-server flow using the Client API. + */ +@QuarkusTest +public class JpaDatabaseTaskStoreIntegrationTest { + + @Inject + TaskStore taskStore; + + @Inject + @PublicAgentCard + AgentCard agentCard; + + private Client client; + + @BeforeEach + public void setup() throws A2AClientException { + ClientConfig clientConfig = new ClientConfig.Builder() + .setStreaming(false) + .build(); + + client = Client.builder(agentCard) + .clientConfig(clientConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + } + + @Test + public void testIsJpaDatabaseTaskStore() { + assertInstanceOf(JpaDatabaseTaskStore.class, taskStore); + } + + @Test + public void testJpaDatabaseTaskStore() throws Exception { + final String taskId = "test-task-1"; + final String contextId = "contextId"; + + // Send a message creating the Task + assertNull(taskStore.get(taskId)); + Message userMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("create"))) + .taskId(taskId) + .messageId("test-msg-1") + .contextId(contextId) + .build(); + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference taskRef = new AtomicReference<>(); + + java.util.function.BiConsumer consumer = (event, card) -> { + if (event instanceof TaskEvent taskEvent) { + taskRef.set(taskEvent.getTask()); + latch.countDown(); + } + }; + + client.sendMessage(userMessage, List.of(consumer), (Throwable e) -> { + latch.countDown(); + }); + + assertTrue(latch.await(10, TimeUnit.SECONDS), "Timeout waiting for task creation"); + Task createdTask = taskRef.get(); + assertNotNull(createdTask); + assertEquals(0, createdTask.getArtifacts().size()); + assertEquals(TaskState.SUBMITTED, createdTask.getStatus().state()); + + // Send a message updating the Task + userMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("add-artifact"))) + .taskId(taskId) + .messageId("test-msg-2") + .contextId(contextId) + .build(); + + CountDownLatch latch2 = new CountDownLatch(1); + AtomicReference taskRef2 = new AtomicReference<>(); + + consumer = (event, card) -> { + if (event instanceof TaskEvent taskEvent) { + taskRef2.set(taskEvent.getTask()); + latch2.countDown(); + } + }; + + client.sendMessage(userMessage, List.of(consumer), (Throwable e) -> { + latch2.countDown(); + }); + + assertTrue(latch2.await(10, TimeUnit.SECONDS), "Timeout waiting for task creation"); + Task updatedTask = taskRef2.get(); + assertNotNull(updatedTask); + assertEquals(1, updatedTask.getArtifacts().size()); + assertEquals(TaskState.SUBMITTED, updatedTask.getStatus().state()); + + Task retrievedTask = client.getTask(new TaskQueryParams(taskId), null); + assertNotNull(retrievedTask); + assertEquals(1, retrievedTask.getArtifacts().size()); + assertEquals(TaskState.SUBMITTED, retrievedTask.getStatus().state()); + + // Cancel the task + Task cancelledTask = client.cancelTask(new TaskIdParams(taskId), null); + assertNotNull(cancelledTask); + assertEquals(1, cancelledTask.getArtifacts().size()); + assertEquals(TaskState.CANCELED, cancelledTask.getStatus().state()); + + Task retrievedCancelledTask = client.getTask(new TaskQueryParams(taskId), null); + assertNotNull(retrievedCancelledTask); + assertEquals(1, retrievedCancelledTask.getArtifacts().size()); + assertEquals(TaskState.CANCELED, retrievedCancelledTask.getStatus().state()); + + // None of the framework code deletes tasks, so just do this manually + taskStore.delete(taskId); + } +} diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java new file mode 100644 index 000000000..4ac03f948 --- /dev/null +++ b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -0,0 +1,182 @@ +package io.a2a.extras.taskstore.database.jpa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Collections; +import java.util.HashMap; + +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + +import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TextPart; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class JpaDatabaseTaskStoreTest { + + @Inject + TaskStore taskStore; + + @Test + public void testIsJpaDatabaseTaskStore() { + assertInstanceOf(JpaDatabaseTaskStore.class, taskStore); + } + + @Test + @Transactional + public void testSaveAndRetrieveTask() { + // Create a test task + Task task = new Task.Builder() + .id("test-task-1") + .contextId("test-context-1") + .status(new TaskStatus(TaskState.SUBMITTED)) + .metadata(new HashMap<>()) + .build(); + + // Save the task + taskStore.save(task); + + // Retrieve the task + Task retrieved = taskStore.get("test-task-1"); + + assertNotNull(retrieved); + assertEquals("test-task-1", retrieved.getId()); + assertEquals("test-context-1", retrieved.getContextId()); + assertEquals(TaskState.SUBMITTED, retrieved.getStatus().state()); + } + + @Test + @Transactional + public void testSaveAndRetrieveTaskWithHistory() { + // Create a message for the task history + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("Hello, agent!"))) + .messageId("msg-1") + .build(); + + // Create a task with history + Task task = new Task.Builder() + .id("test-task-2") + .contextId("test-context-2") + .status(new TaskStatus(TaskState.WORKING)) + .history(Collections.singletonList(message)) + .build(); + + // Save the task + taskStore.save(task); + + // Retrieve the task + Task retrieved = taskStore.get("test-task-2"); + + assertNotNull(retrieved); + assertEquals("test-task-2", retrieved.getId()); + assertEquals("test-context-2", retrieved.getContextId()); + assertEquals(TaskState.WORKING, retrieved.getStatus().state()); + assertEquals(1, retrieved.getHistory().size()); + assertEquals("msg-1", retrieved.getHistory().get(0).getMessageId()); + assertEquals("Hello, agent!", ((TextPart) retrieved.getHistory().get(0).getParts().get(0)).getText()); + } + + @Test + @Transactional + public void testUpdateExistingTask() { + // Create and save initial task + Task initialTask = new Task.Builder() + .id("test-task-3") + .contextId("test-context-3") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + taskStore.save(initialTask); + + // Update the task + Task updatedTask = new Task.Builder() + .id("test-task-3") + .contextId("test-context-3") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + taskStore.save(updatedTask); + + // Retrieve and verify the update + Task retrieved = taskStore.get("test-task-3"); + + assertNotNull(retrieved); + assertEquals("test-task-3", retrieved.getId()); + assertEquals(TaskState.COMPLETED, retrieved.getStatus().state()); + } + + @Test + @Transactional + public void testGetNonExistentTask() { + Task retrieved = taskStore.get("non-existent-task"); + assertNull(retrieved); + } + + @Test + @Transactional + public void testDeleteTask() { + // Create and save a task + Task task = new Task.Builder() + .id("test-task-4") + .contextId("test-context-4") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + taskStore.save(task); + + // Verify it exists + assertNotNull(taskStore.get("test-task-4")); + + // Delete the task + taskStore.delete("test-task-4"); + + // Verify it's gone + assertNull(taskStore.get("test-task-4")); + } + + @Test + @Transactional + public void testDeleteNonExistentTask() { + // This should not throw an exception + taskStore.delete("non-existent-task"); + } + + @Test + @Transactional + public void testTaskWithComplexMetadata() { + // Create a task with complex metadata + HashMap metadata = new HashMap<>(); + metadata.put("key1", "value1"); + metadata.put("key2", 42); + metadata.put("key3", true); + + Task task = new Task.Builder() + .id("test-task-5") + .contextId("test-context-5") + .status(new TaskStatus(TaskState.SUBMITTED)) + .metadata(metadata) + .build(); + + // Save and retrieve + taskStore.save(task); + Task retrieved = taskStore.get("test-task-5"); + + assertNotNull(retrieved); + assertEquals("test-task-5", retrieved.getId()); + assertNotNull(retrieved.getMetadata()); + assertEquals("value1", retrieved.getMetadata().get("key1")); + assertEquals(42, retrieved.getMetadata().get("key2")); + assertEquals(true, retrieved.getMetadata().get("key3")); + } +} diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java new file mode 100644 index 000000000..f78d0cf8a --- /dev/null +++ b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java @@ -0,0 +1,36 @@ +package io.a2a.extras.taskstore.database.jpa; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.TransportProtocol; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Simple test AgentCard producer for our integration test. + */ +@ApplicationScoped +@IfBuildProfile("test") +public class TestAgentCardProducer { + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + return new AgentCard.Builder() + .name("JPA TaskStore Integration Test Agent") + .description("Test agent for verifying JPA TaskStore integration") + .version("1.0.0") + .url("http://localhost:8081") + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .capabilities(new AgentCapabilities.Builder().build()) + .skills(List.of()) + .build(); + } +} diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java new file mode 100644 index 000000000..9b93259bb --- /dev/null +++ b/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java @@ -0,0 +1,64 @@ +package io.a2a.extras.taskstore.database.jpa; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Simple test AgentExecutor that responds to messages and uses TaskUpdater.addArtifact() + * to trigger TaskUpdateEvents for our integration test. + */ +@IfBuildProfile("test") +@ApplicationScoped +public class TestAgentExecutor { + + @Produces + public AgentExecutor agentExecutor() { + return new AgentExecutor() { + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + System.out.println("TestAgentExecutor.execute() called for task: " + context.getTaskId()); + System.out.println("Message " + context.getMessage()); + + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + String lastText = getLastTextPart(context.getMessage()); + switch (lastText) { + case "create": + taskUpdater.submit(); + break; + case "add-artifact": + taskUpdater.addArtifact(List.of(new TextPart(lastText)), "art-1", "test", null); + break; + default: + throw new InvalidRequestError(lastText + " is unknown"); + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + taskUpdater.cancel(); + } + }; + } + + private String getLastTextPart(Message message) throws JSONRPCError { + Part part = message.getParts().get(message.getParts().size() - 1); + if (part.getKind() == Part.Kind.TEXT) { + return ((TextPart) part).getText(); + } + throw new InvalidRequestError("No parts"); + } +} diff --git a/extras/taskstore-database-jpa/src/test/resources/META-INF/persistence.xml b/extras/taskstore-database-jpa/src/test/resources/META-INF/persistence.xml new file mode 100644 index 000000000..e80e61c86 --- /dev/null +++ b/extras/taskstore-database-jpa/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,45 @@ + + + + + + A2A Java SDK JPA TaskStore Test Configuration + + + io.a2a.extras.taskstore.database.jpa.JpaTask + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extras/taskstore-database-jpa/src/test/resources/application.properties b/extras/taskstore-database-jpa/src/test/resources/application.properties new file mode 100644 index 000000000..1d1592ad9 --- /dev/null +++ b/extras/taskstore-database-jpa/src/test/resources/application.properties @@ -0,0 +1,11 @@ +# Test configuration for JPA TaskStore +# H2 in-memory database for testing +quarkus.datasource.db-kind=h2 +quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +quarkus.datasource.username=sa +quarkus.datasource.password= + +# Hibernate configuration +quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-orm.log.sql=true +quarkus.hibernate-orm.log.format-sql=true diff --git a/pom.xml b/pom.xml index bf79b4ada..af0651689 100644 --- a/pom.xml +++ b/pom.xml @@ -390,6 +390,7 @@ client/transport/spi common examples/helloworld + extras/taskstore-database-jpa http-client reference/common reference/grpc From 26ec19620b123c7c556440ca8b99c507c2e07006 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 25 Sep 2025 15:06:09 +0100 Subject: [PATCH 133/493] chore: Port PushNotification sender test from Python (#296) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../tasks/BasePushNotificationSender.java | 4 +- .../tasks/PushNotificationSenderTest.java | 265 ++++++++++++++++++ 2 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index aec3f3cc3..2601e5bbf 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -69,7 +69,9 @@ private CompletableFuture dispatch(Task task, PushNotificationConfig pu private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo) { String url = pushInfo.url(); - // TODO auth + // TODO: Implement authentication and token header support + // The Python implementation adds X-A2A-Notification-Token header when pushInfo.token is present + // See: https://github.com/a2aproject/a2a-python/blob/main/src/a2a/server/tasks/base_push_notification_sender.py#L55-57 String body; try { diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java new file mode 100644 index 000000000..0b472591e --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java @@ -0,0 +1,265 @@ +package io.a2a.server.tasks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; +import io.a2a.util.Utils; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; + +public class PushNotificationSenderTest { + + private TestHttpClient testHttpClient; + private InMemoryPushNotificationConfigStore configStore; + private BasePushNotificationSender sender; + + /** + * Simple test implementation of A2AHttpClient that captures HTTP calls for verification + */ + private static class TestHttpClient implements A2AHttpClient { + final List tasks = Collections.synchronizedList(new ArrayList<>()); + final List urls = Collections.synchronizedList(new ArrayList<>()); + final List> headers = Collections.synchronizedList(new ArrayList<>()); + volatile CountDownLatch latch; + volatile boolean shouldThrowException = false; + + @Override + public GetBuilder createGet() { + return null; + } + + @Override + public PostBuilder createPost() { + return new TestPostBuilder(); + } + + @Override + public DeleteBuilder createDelete() { + return null; + } + + class TestPostBuilder implements A2AHttpClient.PostBuilder { + private volatile String body; + private volatile String url; + private final Map requestHeaders = new java.util.HashMap<>(); + + @Override + public PostBuilder body(String body) { + this.body = body; + return this; + } + + @Override + public A2AHttpResponse post() throws IOException, InterruptedException { + if (shouldThrowException) { + throw new IOException("Simulated network error"); + } + + try { + Task task = Utils.OBJECT_MAPPER.readValue(body, Task.TYPE_REFERENCE); + tasks.add(task); + urls.add(url); + headers.add(new java.util.HashMap<>(requestHeaders)); + + return new A2AHttpResponse() { + @Override + public int status() { + return 200; + } + + @Override + public boolean success() { + return true; + } + + @Override + public String body() { + return ""; + } + }; + } finally { + if (latch != null) { + latch.countDown(); + } + } + } + + @Override + public CompletableFuture postAsyncSSE(Consumer messageConsumer, Consumer errorConsumer, Runnable completeRunnable) throws IOException, InterruptedException { + return null; + } + + @Override + public PostBuilder url(String url) { + this.url = url; + return this; + } + + @Override + public PostBuilder addHeader(String name, String value) { + requestHeaders.put(name, value); + return this; + } + + @Override + public PostBuilder addHeaders(Map headers) { + requestHeaders.putAll(headers); + return this; + } + } + } + + @BeforeEach + public void setUp() { + testHttpClient = new TestHttpClient(); + configStore = new InMemoryPushNotificationConfigStore(); + sender = new BasePushNotificationSender(configStore, testHttpClient); + } + + private Task createSampleTask(String taskId, TaskState state) { + return new Task.Builder() + .id(taskId) + .contextId("ctx456") + .status(new TaskStatus(state)) + .build(); + } + + private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { + return new PushNotificationConfig.Builder() + .url(url) + .id(configId) + .token(token) + .build(); + } + + @Test + public void testSendNotificationSuccess() throws InterruptedException { + String taskId = "task_send_success"; + Task taskData = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", null); + + // Set up the configuration in the store + configStore.setInfo(taskId, config); + + // Set up latch to wait for async completion + testHttpClient.latch = new CountDownLatch(1); + + sender.sendNotification(taskData); + + // Wait for the async operation to complete + assertTrue(testHttpClient.latch.await(5, TimeUnit.SECONDS), "HTTP call should complete within 5 seconds"); + + // Verify the task was sent via HTTP + assertEquals(1, testHttpClient.tasks.size()); + Task sentTask = testHttpClient.tasks.get(0); + assertEquals(taskData.getId(), sentTask.getId()); + assertEquals(taskData.getContextId(), sentTask.getContextId()); + assertEquals(taskData.getStatus().state(), sentTask.getStatus().state()); + } + + @Test + public void testSendNotificationWithTokenSuccess() throws InterruptedException { + String taskId = "task_send_with_token"; + Task taskData = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", "unique_token"); + + // Set up the configuration in the store + configStore.setInfo(taskId, config); + + // Set up latch to wait for async completion + testHttpClient.latch = new CountDownLatch(1); + + sender.sendNotification(taskData); + + // Wait for the async operation to complete + assertTrue(testHttpClient.latch.await(5, TimeUnit.SECONDS), "HTTP call should complete within 5 seconds"); + + // Verify the task was sent via HTTP + assertEquals(1, testHttpClient.tasks.size()); + Task sentTask = testHttpClient.tasks.get(0); + assertEquals(taskData.getId(), sentTask.getId()); + + // TODO: When authentication is implemented in BasePushNotificationSender, verify that the + // X-A2A-Notification-Token header is sent. + } + + @Test + public void testSendNotificationNoConfig() { + String taskId = "task_send_no_config"; + Task taskData = createSampleTask(taskId, TaskState.COMPLETED); + + // Don't set any configuration in the store + sender.sendNotification(taskData); + + // Verify no HTTP calls were made + assertEquals(0, testHttpClient.tasks.size()); + } + + @Test + public void testSendNotificationMultipleConfigs() throws InterruptedException { + String taskId = "task_multiple_configs"; + Task taskData = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config1 = createSamplePushConfig("http://notify.me/cfg1", "cfg1", null); + PushNotificationConfig config2 = createSamplePushConfig("http://notify.me/cfg2", "cfg2", null); + + // Set up multiple configurations in the store + configStore.setInfo(taskId, config1); + configStore.setInfo(taskId, config2); + + // Set up latch to wait for async completion (2 calls expected) + testHttpClient.latch = new CountDownLatch(2); + + sender.sendNotification(taskData); + + // Wait for the async operations to complete + assertTrue(testHttpClient.latch.await(5, TimeUnit.SECONDS), "HTTP calls should complete within 5 seconds"); + + // Verify both tasks were sent via HTTP + assertEquals(2, testHttpClient.tasks.size()); + assertEquals(2, testHttpClient.urls.size()); + assertTrue(testHttpClient.urls.containsAll(java.util.List.of("http://notify.me/cfg1", "http://notify.me/cfg2"))); + + // Both tasks should be identical (same task sent to different endpoints) + for (Task sentTask : testHttpClient.tasks) { + assertEquals(taskData.getId(), sentTask.getId()); + assertEquals(taskData.getContextId(), sentTask.getContextId()); + assertEquals(taskData.getStatus().state(), sentTask.getStatus().state()); + } + } + + @Test + public void testSendNotificationHttpError() { + String taskId = "task_send_http_err"; + Task taskData = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/http_error", "cfg1", null); + + // Set up the configuration in the store + configStore.setInfo(taskId, config); + + // Configure the test client to throw an exception + testHttpClient.shouldThrowException = true; + + // This should not throw an exception - errors should be handled gracefully + sender.sendNotification(taskData); + + // Verify no tasks were successfully processed due to the error + assertEquals(0, testHttpClient.tasks.size()); + } +} From 655e1d337f9780300cb9c48c1ac632681133139b Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 25 Sep 2025 15:11:20 +0100 Subject: [PATCH 134/493] chore: Backport ResultAggregator tests (#298) --- .../server/tasks/ResultAggregatorTest.java | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java new file mode 100644 index 000000000..e4a80d71e --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -0,0 +1,186 @@ +package io.a2a.server.tasks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import io.a2a.spec.EventKind; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TextPart; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Comprehensive tests for ResultAggregator based on Python test patterns. + * This is not a strict backport of the Python test, but it implements the same testing patterns + * adapted for Java's reactive streams and concurrency model. + * + * Note: This simplified version focuses on the core functionality without complex reactive stream testing + * that was causing issues with the original implementation. + */ +public class ResultAggregatorTest { + + @Mock + private TaskManager mockTaskManager; + + private ResultAggregator aggregator; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + aggregator = new ResultAggregator(mockTaskManager, null); + } + + // Helper methods for creating sample data + private Message createSampleMessage(String content, String msgId, Message.Role role) { + return new Message.Builder() + .messageId(msgId) + .role(role) + .parts(Collections.singletonList(new TextPart(content))) + .build(); + } + + private Task createSampleTask(String taskId, TaskState statusState, String contextId) { + return new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(statusState)) + .build(); + } + + + // Basic functionality tests + + @Test + void testConstructorWithMessage() { + Message initialMessage = createSampleMessage("initial", "msg1", Message.Role.USER); + ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, initialMessage); + + // Test that the message is properly stored by checking getCurrentResult + assertEquals(initialMessage, aggregatorWithMessage.getCurrentResult()); + // TaskManager should not be called when message is set + verifyNoInteractions(mockTaskManager); + } + + @Test + void testGetCurrentResultWithMessageSet() { + Message sampleMessage = createSampleMessage("hola", "msg1", Message.Role.USER); + ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, sampleMessage); + + EventKind result = aggregatorWithMessage.getCurrentResult(); + + assertEquals(sampleMessage, result); + // TaskManager.getTask() should not be called when message is set + verifyNoInteractions(mockTaskManager); + } + + @Test + void testGetCurrentResultWithMessageNull() { + Task expectedTask = createSampleTask("task_from_tm", TaskState.SUBMITTED, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(expectedTask); + + EventKind result = aggregator.getCurrentResult(); + + assertEquals(expectedTask, result); + verify(mockTaskManager).getTask(); + } + + @Test + void testConstructorStoresTaskManagerCorrectly() { + // Test that constructor properly initializes the aggregator + // We can't access the private field directly, but we can test behavior + Task expectedTask = createSampleTask("test_task", TaskState.SUBMITTED, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(expectedTask); + + EventKind result = aggregator.getCurrentResult(); + + assertEquals(expectedTask, result); + verify(mockTaskManager).getTask(); + } + + @Test + void testConstructorWithNullMessage() { + ResultAggregator aggregatorWithNullMessage = new ResultAggregator(mockTaskManager, null); + Task expectedTask = createSampleTask("null_msg_task", TaskState.WORKING, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(expectedTask); + + EventKind result = aggregatorWithNullMessage.getCurrentResult(); + + assertEquals(expectedTask, result); + verify(mockTaskManager).getTask(); + } + + @Test + void testGetCurrentResultReturnsTaskWhenNoMessage() { + Task expectedTask = createSampleTask("no_message_task", TaskState.COMPLETED, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(expectedTask); + + EventKind result = aggregator.getCurrentResult(); + + assertNotNull(result); + assertEquals(expectedTask, result); + verify(mockTaskManager).getTask(); + } + + @Test + void testGetCurrentResultWithDifferentTaskStates() { + // Test with WORKING and COMPLETED states using chained returns + Task workingTask = createSampleTask("working_task", TaskState.WORKING, "ctx1"); + Task completedTask = createSampleTask("completed_task", TaskState.COMPLETED, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(workingTask, completedTask); + + // First call returns WORKING task + EventKind result1 = aggregator.getCurrentResult(); + assertEquals(workingTask, result1); + + // Second call returns COMPLETED task + EventKind result2 = aggregator.getCurrentResult(); + assertEquals(completedTask, result2); + } + + @Test + void testMultipleGetCurrentResultCalls() { + // Test that multiple calls to getCurrentResult behave consistently + Task expectedTask = createSampleTask("multi_call_task", TaskState.SUBMITTED, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(expectedTask); + + EventKind result1 = aggregator.getCurrentResult(); + EventKind result2 = aggregator.getCurrentResult(); + EventKind result3 = aggregator.getCurrentResult(); + + assertEquals(expectedTask, result1); + assertEquals(expectedTask, result2); + assertEquals(expectedTask, result3); + + // Verify getTask was called multiple times + verify(mockTaskManager, times(3)).getTask(); + } + + @Test + void testGetCurrentResultWithMessageTakesPrecedence() { + // Test that when both message and task are available, message takes precedence + Message message = createSampleMessage("priority message", "pri1", Message.Role.USER); + ResultAggregator messageAggregator = new ResultAggregator(mockTaskManager, message); + + // Even if we set up the task manager to return something, message should take precedence + Task task = createSampleTask("should_not_be_returned", TaskState.WORKING, "ctx1"); + when(mockTaskManager.getTask()).thenReturn(task); + + EventKind result = messageAggregator.getCurrentResult(); + + assertEquals(message, result); + // Task manager should not be called when message is present + verifyNoInteractions(mockTaskManager); + } +} From 1cf3660221682875922296fcbc3744fcb58cf86e Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 25 Sep 2025 15:19:53 +0100 Subject: [PATCH 135/493] chore: sync changes to existing tasks tests from Python (#299) --- .../java/io/a2a/server/events/EventQueue.java | 35 ++- .../java/io/a2a/server/tasks/TaskManager.java | 5 + .../java/io/a2a/server/tasks/TaskUpdater.java | 84 +++++- .../a2a/server/events/EventConsumerTest.java | 179 +++++++++++++ .../io/a2a/server/events/EventQueueTest.java | 188 ++++++++++++++ .../events/InMemoryQueueManagerTest.java | 213 ++++++++++++++++ .../io/a2a/server/tasks/TaskManagerTest.java | 163 ++++++++++++ .../io/a2a/server/tasks/TaskUpdaterTest.java | 241 ++++++++++++++++++ 8 files changed, 1094 insertions(+), 14 deletions(-) create mode 100644 server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 46069f55d..48d4e6c79 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -17,28 +17,45 @@ public abstract class EventQueue implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(EventQueue.class); - // TODO decide on a capacity - private static final int queueSize = 1000; + public static final int DEFAULT_QUEUE_SIZE = 1000; + private final int queueSize; private final BlockingQueue queue = new LinkedBlockingDeque<>(); - private final Semaphore semaphore = new Semaphore(queueSize, true); + private final Semaphore semaphore; private volatile boolean closed = false; protected EventQueue() { - this(null); + this(DEFAULT_QUEUE_SIZE); + } + + protected EventQueue(int queueSize) { + if (queueSize <= 0) { + throw new IllegalArgumentException("Queue size must be greater than 0"); + } + this.queueSize = queueSize; + this.semaphore = new Semaphore(queueSize, true); + LOGGER.trace("Creating {} with queue size: {}", this, queueSize); } protected EventQueue(EventQueue parent) { + this(DEFAULT_QUEUE_SIZE); LOGGER.trace("Creating {}, parent: {}", this, parent); } public static EventQueue create() { - return new MainQueue(); } + public static EventQueue create(int queueSize) { + return new MainQueue(queueSize); + } + + public int getQueueSize() { + return queueSize; + } + public abstract void awaitQueuePollerStart() throws InterruptedException ; abstract void signalQueuePollerStarted(); @@ -132,6 +149,14 @@ static class MainQueue extends EventQueue { private final CountDownLatch pollingStartedLatch = new CountDownLatch(1); private final AtomicBoolean pollingStarted = new AtomicBoolean(false); + MainQueue() { + super(); + } + + MainQueue(int queueSize) { + super(queueSize); + } + EventQueue tap() { ChildQueue child = new ChildQueue(this); children.add(child); diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 73d3d2ed6..067aa2832 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -76,6 +76,11 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { builder.history(newHistory); } + // Handle metadata from the event + if (event.getMetadata() != null) { + builder.metadata(event.getMetadata()); + } + task = builder.build(); return saveTask(task); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index 2e7a7cafb..1991c837a 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; @@ -18,6 +19,8 @@ public class TaskUpdater { private final EventQueue eventQueue; private final String taskId; private final String contextId; + private final AtomicBoolean terminalStateReached = new AtomicBoolean(false); + private final Object stateLock = new Object(); public TaskUpdater(RequestContext context, EventQueue eventQueue) { this.eventQueue = eventQueue; @@ -26,20 +29,41 @@ public TaskUpdater(RequestContext context, EventQueue eventQueue) { } private void updateStatus(TaskState taskState) { - updateStatus(taskState, null); + updateStatus(taskState, null, taskState.isFinal()); } - private void updateStatus(TaskState state, Message message) { - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() - .taskId(taskId) - .contextId(contextId) - .isFinal(state.isFinal()) - .status(new TaskStatus(state, message, null)) - .build(); - eventQueue.enqueueEvent(event); + private void updateStatus(TaskState taskState, Message message) { + updateStatus(taskState, message, taskState.isFinal()); + } + + private void updateStatus(TaskState state, Message message, boolean isFinal) { + synchronized (stateLock) { + // Check if we're already in a terminal state + if (terminalStateReached.get()) { + throw new IllegalStateException("Cannot update task status - terminal state already reached"); + } + + // If this is a final state, set the flag + if (isFinal) { + terminalStateReached.set(true); + } + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId(taskId) + .contextId(contextId) + .isFinal(isFinal) + .status(new TaskStatus(state, message, null)) + .build(); + eventQueue.enqueueEvent(event); + } } public void addArtifact(List> parts, String artifactId, String name, Map metadata) { + addArtifact(parts, artifactId, name, metadata, null, null); + } + + public void addArtifact(List> parts, String artifactId, String name, Map metadata, + Boolean append, Boolean lastChunk) { if (artifactId == null) { artifactId = UUID.randomUUID().toString(); } @@ -54,6 +78,8 @@ public void addArtifact(List> parts, String artifactId, String name, Map .metadata(metadata) .build() ) + .append(append) + .lastChunk(lastChunk) .build(); eventQueue.enqueueEvent(event); } @@ -98,6 +124,46 @@ public void cancel(Message message) { updateStatus(TaskState.CANCELED, message); } + public void reject() { + reject(null); + } + + public void reject(Message message) { + updateStatus(TaskState.REJECTED, message); + } + + public void requiresInput() { + requiresInput(null, false); + } + + public void requiresInput(Message message) { + requiresInput(message, false); + } + + public void requiresInput(boolean isFinal) { + requiresInput(null, isFinal); + } + + public void requiresInput(Message message, boolean isFinal) { + updateStatus(TaskState.INPUT_REQUIRED, message, isFinal); + } + + public void requiresAuth() { + requiresAuth(null, false); + } + + public void requiresAuth(Message message) { + requiresAuth(message, false); + } + + public void requiresAuth(boolean isFinal) { + requiresAuth(null, isFinal); + } + + public void requiresAuth(Message message, boolean isFinal) { + updateStatus(TaskState.AUTH_REQUIRED, message, isFinal); + } + public Message newAgentMessage(List> parts, Map metadata) { return new Message.Builder() .role(Message.Role.AGENT) diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 7d8d6cbad..b9a2c8228 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -1,9 +1,11 @@ package io.a2a.server.events; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; @@ -256,9 +258,186 @@ public void onComplete() { assertSame(message, receivedEvents.get(0)); } + @Test + public void testCreateAgentRunnableDoneCallbackSetsError() { + EnhancedRunnable mockRunnable = new EnhancedRunnable() { + @Override + public void run() { + // Mock implementation + } + }; + + Throwable testError = new RuntimeException("Test error"); + mockRunnable.setError(testError); + + EnhancedRunnable.DoneCallback callback = eventConsumer.createAgentRunnableDoneCallback(); + callback.done(mockRunnable); + + // The error should be stored in the event consumer + assertEquals(testError, getEventConsumerError()); + } + + @Test + public void testCreateAgentRunnableDoneCallbackNoError() { + EnhancedRunnable mockRunnable = new EnhancedRunnable() { + @Override + public void run() { + // Mock implementation + } + }; + + // No error set on runnable + assertNull(mockRunnable.getError()); + + EnhancedRunnable.DoneCallback callback = eventConsumer.createAgentRunnableDoneCallback(); + callback.done(mockRunnable); + + // The error should remain null + assertNull(getEventConsumerError()); + } + + @Test + public void testConsumeAllRaisesStoredException() { + // Set an error in the event consumer + setEventConsumerError(new RuntimeException("Stored error")); + + Flow.Publisher publisher = eventConsumer.consumeAll(); + final AtomicReference receivedError = new AtomicReference<>(); + + publisher.subscribe(new Flow.Subscriber<>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscription.request(1); + } + + @Override + public void onNext(Event item) { + // Should not be called + } + + @Override + public void onError(Throwable throwable) { + receivedError.set(throwable); + } + + @Override + public void onComplete() { + // Should not be called + } + }); + + assertNotNull(receivedError.get()); + assertEquals("Stored error", receivedError.get().getMessage()); + } + + @Test + public void testConsumeAllStopsOnQueueClosed() throws Exception { + EventQueue queue = EventQueue.create(); + EventConsumer consumer = new EventConsumer(queue); + + // Close the queue immediately + queue.close(); + + Flow.Publisher publisher = consumer.consumeAll(); + final List receivedEvents = new ArrayList<>(); + final AtomicReference completed = new AtomicReference<>(false); + + publisher.subscribe(new Flow.Subscriber<>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscription.request(Long.MAX_VALUE); + } + + @Override + public void onNext(Event item) { + receivedEvents.add(item); + } + + @Override + public void onError(Throwable throwable) { + // Should not be called + } + + @Override + public void onComplete() { + completed.set(true); + } + }); + + // Should complete immediately with no events + assertTrue(completed.get()); + assertEquals(0, receivedEvents.size()); + } + + + @Test + public void testConsumeAllHandlesQueueClosedException() throws Exception { + EventQueue queue = EventQueue.create(); + EventConsumer consumer = new EventConsumer(queue); + + // Add a message event (which will complete the stream) + Event message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + queue.enqueueEvent(message); + + // Close the queue before consuming + queue.close(); + + Flow.Publisher publisher = consumer.consumeAll(); + final List receivedEvents = new ArrayList<>(); + final AtomicReference completed = new AtomicReference<>(false); + + publisher.subscribe(new Flow.Subscriber<>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscription.request(Long.MAX_VALUE); + } + + @Override + public void onNext(Event item) { + receivedEvents.add(item); + } + + @Override + public void onError(Throwable throwable) { + // Should not be called + } + + @Override + public void onComplete() { + completed.set(true); + } + }); + + // Should have received the message and completed + assertTrue(completed.get()); + assertEquals(1, receivedEvents.size()); + assertSame(message, receivedEvents.get(0)); + } + private void enqueueAndConsumeOneEvent(Event event) throws Exception { eventQueue.enqueueEvent(event); Event result = eventConsumer.consumeOne(); assertSame(event, result); } + + // Helper methods to access private error field via reflection + private Throwable getEventConsumerError() { + try { + java.lang.reflect.Field errorField = EventConsumer.class.getDeclaredField("error"); + errorField.setAccessible(true); + return (Throwable) errorField.get(eventConsumer); + } catch (Exception e) { + throw new RuntimeException("Failed to access error field", e); + } + } + + private void setEventConsumerError(Throwable error) { + try { + java.lang.reflect.Field errorField = EventConsumer.class.getDeclaredField("error"); + errorField.setAccessible(true); + errorField.set(eventConsumer, error); + } catch (Exception e) { + throw new RuntimeException("Failed to set error field", e); + } + } } diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 2fd2766d6..e11c2a738 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -1,7 +1,12 @@ package io.a2a.server.events; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -50,6 +55,189 @@ public void init() { } + @Test + public void testConstructorDefaultQueueSize() { + EventQueue queue = EventQueue.create(); + assertEquals(EventQueue.DEFAULT_QUEUE_SIZE, queue.getQueueSize()); + } + + @Test + public void testConstructorCustomQueueSize() { + int customSize = 500; + EventQueue queue = EventQueue.create(customSize); + assertEquals(customSize, queue.getQueueSize()); + } + + @Test + public void testConstructorInvalidQueueSize() { + // Test zero queue size + assertThrows(IllegalArgumentException.class, () -> EventQueue.create(0)); + + // Test negative queue size + assertThrows(IllegalArgumentException.class, () -> EventQueue.create(-10)); + } + + @Test + public void testTapCreatesChildQueue() { + EventQueue parentQueue = EventQueue.create(); + EventQueue childQueue = parentQueue.tap(); + + assertNotNull(childQueue); + assertNotSame(parentQueue, childQueue); + assertEquals(EventQueue.DEFAULT_QUEUE_SIZE, childQueue.getQueueSize()); + } + + @Test + public void testTapOnChildQueueThrowsException() { + EventQueue parentQueue = EventQueue.create(); + EventQueue childQueue = parentQueue.tap(); + + assertThrows(IllegalStateException.class, () -> childQueue.tap()); + } + + @Test + public void testEnqueueEventPropagagesToChildren() throws Exception { + EventQueue parentQueue = EventQueue.create(); + EventQueue childQueue = parentQueue.tap(); + + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + parentQueue.enqueueEvent(event); + + // Event should be available in both parent and child queues + Event parentEvent = parentQueue.dequeueEvent(-1); + Event childEvent = childQueue.dequeueEvent(-1); + + assertSame(event, parentEvent); + assertSame(event, childEvent); + } + + @Test + public void testMultipleChildQueuesReceiveEvents() throws Exception { + EventQueue parentQueue = EventQueue.create(); + EventQueue childQueue1 = parentQueue.tap(); + EventQueue childQueue2 = parentQueue.tap(); + + Event event1 = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event2 = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + + parentQueue.enqueueEvent(event1); + parentQueue.enqueueEvent(event2); + + // All queues should receive both events + assertSame(event1, parentQueue.dequeueEvent(-1)); + assertSame(event2, parentQueue.dequeueEvent(-1)); + + assertSame(event1, childQueue1.dequeueEvent(-1)); + assertSame(event2, childQueue1.dequeueEvent(-1)); + + assertSame(event1, childQueue2.dequeueEvent(-1)); + assertSame(event2, childQueue2.dequeueEvent(-1)); + } + + @Test + public void testChildQueueDequeueIndependently() throws Exception { + EventQueue parentQueue = EventQueue.create(); + EventQueue childQueue1 = parentQueue.tap(); + EventQueue childQueue2 = parentQueue.tap(); + + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + parentQueue.enqueueEvent(event); + + // Dequeue from child1 first + Event child1Event = childQueue1.dequeueEvent(-1); + assertSame(event, child1Event); + + // child2 should still have the event available + Event child2Event = childQueue2.dequeueEvent(-1); + assertSame(event, child2Event); + + // Parent should still have the event available + Event parentEvent = parentQueue.dequeueEvent(-1); + assertSame(event, parentEvent); + } + + + @Test + public void testCloseImmediatePropagationToChildren() throws Exception { + EventQueue parentQueue = EventQueue.create(); + EventQueue childQueue = parentQueue.tap(); + + // Add events to both parent and child + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + parentQueue.enqueueEvent(event); + + assertFalse(childQueue.isClosed()); + try { + assertNotNull(childQueue.dequeueEvent(-1)); // Child has the event + } catch (EventQueueClosedException e) { + // This is fine if queue closed before dequeue + } + + // Add event again for immediate close test + parentQueue.enqueueEvent(event); + + // Close with immediate=true + parentQueue.close(true); + + assertTrue(parentQueue.isClosed()); + assertTrue(childQueue.isClosed()); + + // Child queue should be cleared due to immediate close + try { + Event result = childQueue.dequeueEvent(-1); + assertNull(result); + } catch (EventQueueClosedException e) { + // This is expected when queue is closed + } + } + + @Test + public void testEnqueueEventWhenClosed() throws Exception { + EventQueue queue = EventQueue.create(); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + + queue.close(); // Close the queue first + assertTrue(queue.isClosed()); + + // Attempt to enqueue should be ignored (no exception thrown) + queue.enqueueEvent(event); + + // Verify the queue is still empty + try { + Event dequeuedEvent = queue.dequeueEvent(-1); + assertNull(dequeuedEvent); + } catch (EventQueueClosedException e) { + // This is expected when queue is closed and empty + } + } + + @Test + public void testDequeueEventWhenClosedAndEmpty() throws Exception { + EventQueue queue = EventQueue.create(); + queue.close(); + assertTrue(queue.isClosed()); + + // Dequeue from closed empty queue should throw exception + assertThrows(EventQueueClosedException.class, () -> queue.dequeueEvent(-1)); + } + + @Test + public void testDequeueEventWhenClosedButHasEvents() throws Exception { + EventQueue queue = EventQueue.create(); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + queue.enqueueEvent(event); + + queue.close(); // Graceful close - events should remain + assertTrue(queue.isClosed()); + + // Should still be able to dequeue existing events + Event dequeuedEvent = queue.dequeueEvent(-1); + assertSame(event, dequeuedEvent); + + // Now queue is closed and empty, should throw exception + assertThrows(EventQueueClosedException.class, () -> queue.dequeueEvent(-1)); + } + @Test public void testEnqueueAndDequeueEvent() throws Exception { Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); diff --git a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java new file mode 100644 index 000000000..85368b2e2 --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java @@ -0,0 +1,213 @@ +package io.a2a.server.events; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class InMemoryQueueManagerTest { + + private InMemoryQueueManager queueManager; + + @BeforeEach + public void setUp() { + queueManager = new InMemoryQueueManager(); + } + + @Test + public void testAddNewQueue() { + String taskId = "test_task_id"; + EventQueue queue = EventQueue.create(); + + queueManager.add(taskId, queue); + + EventQueue retrievedQueue = queueManager.get(taskId); + assertSame(queue, retrievedQueue); + } + + @Test + public void testAddExistingQueueThrowsException() { + String taskId = "test_task_id"; + EventQueue queue1 = EventQueue.create(); + EventQueue queue2 = EventQueue.create(); + + queueManager.add(taskId, queue1); + + assertThrows(TaskQueueExistsException.class, () -> { + queueManager.add(taskId, queue2); + }); + } + + @Test + public void testGetExistingQueue() { + String taskId = "test_task_id"; + EventQueue queue = EventQueue.create(); + + queueManager.add(taskId, queue); + EventQueue result = queueManager.get(taskId); + + assertSame(queue, result); + } + + @Test + public void testGetNonexistentQueue() { + EventQueue result = queueManager.get("nonexistent_task_id"); + assertNull(result); + } + + @Test + public void testTapExistingQueue() { + String taskId = "test_task_id"; + EventQueue queue = EventQueue.create(); + + queueManager.add(taskId, queue); + EventQueue tappedQueue = queueManager.tap(taskId); + + assertNotNull(tappedQueue); + assertNotSame(queue, tappedQueue, "Tapped queue should be a different instance from the original."); + } + + @Test + public void testTapNonexistentQueue() { + EventQueue result = queueManager.tap("nonexistent_task_id"); + assertNull(result); + } + + @Test + public void testCloseExistingQueue() { + String taskId = "test_task_id"; + EventQueue queue = EventQueue.create(); + + queueManager.add(taskId, queue); + queueManager.close(taskId); + + // Queue should be removed from manager + EventQueue result = queueManager.get(taskId); + assertNull(result); + } + + @Test + public void testCloseNonexistentQueueThrowsException() { + assertThrows(NoTaskQueueException.class, () -> { + queueManager.close("nonexistent_task_id"); + }); + } + + @Test + public void testCreateOrTapNewQueue() { + String taskId = "test_task_id"; + + EventQueue result = queueManager.createOrTap(taskId); + + assertNotNull(result); + // Should be added to manager + EventQueue retrievedQueue = queueManager.get(taskId); + assertSame(result, retrievedQueue); + } + + @Test + public void testCreateOrTapExistingQueue() { + String taskId = "test_task_id"; + EventQueue originalQueue = EventQueue.create(); + + queueManager.add(taskId, originalQueue); + EventQueue result = queueManager.createOrTap(taskId); + + assertNotNull(result); + // Should be a tapped (child) queue, not the original + // Original should still be in manager + EventQueue retrievedQueue = queueManager.get(taskId); + assertSame(originalQueue, retrievedQueue); + } + + @Test + public void testConcurrentOperations() throws InterruptedException, ExecutionException { + // Create 10 different task IDs + List taskIds = IntStream.range(0, 10) + .mapToObj(i -> "task_" + i) + .toList(); + + // Add tasks concurrently + List> addFutures = taskIds.stream() + .map(taskId -> CompletableFuture.supplyAsync(() -> { + EventQueue queue = EventQueue.create(); + queueManager.add(taskId, queue); + return taskId; + })) + .toList(); + + // Wait for all add operations to complete + List addedTaskIds = new ArrayList<>(); + for (CompletableFuture future : addFutures) { + addedTaskIds.add(future.get()); + } + + // Verify all tasks were added + assertEquals(taskIds.size(), addedTaskIds.size()); + assertTrue(addedTaskIds.containsAll(taskIds)); + + // Get tasks concurrently + List> getFutures = taskIds.stream() + .map(taskId -> CompletableFuture.supplyAsync(() -> queueManager.get(taskId))) + .toList(); + + // Wait for all get operations to complete + List queues = new ArrayList<>(); + for (CompletableFuture future : getFutures) { + queues.add(future.get()); + } + + // Verify all queues are not null + assertEquals(taskIds.size(), queues.size()); + for (EventQueue queue : queues) { + assertNotNull(queue); + } + + // Verify all tasks are in the manager + for (String taskId : taskIds) { + assertNotNull(queueManager.get(taskId)); + } + } + + @Test + public void testCreateOrTapRaceCondition() throws InterruptedException, ExecutionException { + String taskId = "race_condition_task"; + + // Try to create/tap the same task ID concurrently + List> futures = IntStream.range(0, 5) + .mapToObj(i -> CompletableFuture.supplyAsync(() -> queueManager.createOrTap(taskId))) + .toList(); + + // Wait for all operations to complete + List results = new ArrayList<>(); + for (CompletableFuture future : futures) { + results.add(future.get()); + } + + // All results should be non-null + for (EventQueue result : results) { + assertNotNull(result); + } + + // There should be exactly one queue in the manager + EventQueue managerQueue = queueManager.get(taskId); + assertNotNull(managerQueue); + + // One of the results should be the main queue instance, and the rest should be + // different (tapped) queue instances. + long mainQueueCount = results.stream().filter(q -> q == managerQueue).count(); + assertEquals(1, mainQueueCount, "Exactly one returned queue should be the main queue instance."); + } +} diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 525ed1086..25b5ead11 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -3,8 +3,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -529,4 +531,165 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce && "content 2".equals(((TextPart) a.parts().get(0)).getText())) , "Artifact 2 should be present"); } + + @Test + public void testInvalidTaskIdValidation() { + // Test that creating TaskManager with null taskId is allowed (Python allows None) + TaskManager taskManagerWithNullId = new TaskManager(null, "context", taskStore, null); + assertNull(taskManagerWithNullId.getTaskId()); + + // Test that empty string task ID is handled (Java doesn't have explicit validation like Python) + TaskManager taskManagerWithEmptyId = new TaskManager("", "context", taskStore, null); + assertEquals("", taskManagerWithEmptyId.getTaskId()); + } + + @Test + public void testSaveTaskEventMetadataUpdate() throws A2AServerException { + // Test that metadata from TaskStatusUpdateEvent gets saved to the task + Task initialTask = minimalTask; + taskStore.save(initialTask); + + Map newMetadata = new HashMap<>(); + newMetadata.put("meta_key_test", "meta_value_test"); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .metadata(newMetadata) + .build(); + + taskManager.saveTaskEvent(event); + + Task updatedTask = taskManager.getTask(); + assertEquals(newMetadata, updatedTask.getMetadata()); + } + + @Test + public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { + // Test that null metadata in TaskStatusUpdateEvent doesn't affect task + Task initialTask = minimalTask; + taskStore.save(initialTask); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .metadata(null) + .build(); + + taskManager.saveTaskEvent(event); + + Task updatedTask = taskManager.getTask(); + // Should preserve original task's metadata (which is likely null for minimal task) + assertNull(updatedTask.getMetadata()); + } + + @Test + public void testSaveTaskEventMetadataUpdateOverwritesExisting() throws A2AServerException { + // Test that metadata update overwrites existing metadata + Map originalMetadata = new HashMap<>(); + originalMetadata.put("original_key", "original_value"); + + Task taskWithMetadata = new Task.Builder(minimalTask) + .metadata(originalMetadata) + .build(); + taskStore.save(taskWithMetadata); + + Map newMetadata = new HashMap<>(); + newMetadata.put("new_key", "new_value"); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId(minimalTask.getId()) + .contextId(minimalTask.getContextId()) + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .metadata(newMetadata) + .build(); + + taskManager.saveTaskEvent(event); + + Task updatedTask = taskManager.getTask(); + assertEquals(newMetadata, updatedTask.getMetadata()); + assertNotEquals(originalMetadata, updatedTask.getMetadata()); + } + + @Test + public void testCreateTaskWithInitialMessage() throws A2AServerException { + // Test equivalent of _init_task_obj functionality + Message initialMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("initial message"))) + .messageId("initial-msg-id") + .build(); + + TaskManager taskManagerWithMessage = new TaskManager(null, null, taskStore, initialMessage); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("new-task-id") + .contextId("some-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .isFinal(false) + .build(); + + Task savedTask = taskManagerWithMessage.saveTaskEvent(event); + + // Verify task was created properly + assertNotNull(savedTask); + assertEquals("new-task-id", savedTask.getId()); + assertEquals("some-context", savedTask.getContextId()); + assertEquals(TaskState.SUBMITTED, savedTask.getStatus().state()); + + // Verify initial message is in history + assertNotNull(savedTask.getHistory()); + assertEquals(1, savedTask.getHistory().size()); + Message historyMessage = savedTask.getHistory().get(0); + assertEquals("initial-msg-id", historyMessage.getMessageId()); + assertEquals("initial message", ((TextPart) historyMessage.getParts().get(0)).getText()); + } + + @Test + public void testCreateTaskWithoutInitialMessage() throws A2AServerException { + // Test task creation without initial message + TaskManager taskManagerWithoutMessage = new TaskManager(null, null, taskStore, null); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("new-task-id") + .contextId("some-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .isFinal(false) + .build(); + + Task savedTask = taskManagerWithoutMessage.saveTaskEvent(event); + + // Verify task was created properly + assertNotNull(savedTask); + assertEquals("new-task-id", savedTask.getId()); + assertEquals("some-context", savedTask.getContextId()); + assertEquals(TaskState.SUBMITTED, savedTask.getStatus().state()); + + // Verify no history since there was no initial message + assertTrue(savedTask.getHistory().isEmpty()); + } + + @Test + public void testSaveTaskInternal() throws A2AServerException { + // Test equivalent of _save_task functionality through saveTaskEvent + TaskManager taskManagerWithoutId = new TaskManager(null, null, taskStore, null); + + Task newTask = new Task.Builder() + .id("test-task-id") + .contextId("test-context") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + Task savedTask = taskManagerWithoutId.saveTaskEvent(newTask); + + // Verify internal state was updated + assertEquals("test-task-id", taskManagerWithoutId.getTaskId()); + assertEquals("test-context", taskManagerWithoutId.getContextId()); + assertSame(savedTask, taskManagerWithoutId.getTask()); + } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 291767b63..2e6404594 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -6,6 +6,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Map; @@ -128,6 +130,86 @@ public void testCanceledWithMessage() throws Exception { checkTaskStatusUpdateEventOnQueue(true, TaskState.CANCELED, SAMPLE_MESSAGE); } + @Test + public void testRejectWithoutMessage() throws Exception { + taskUpdater.reject(); + checkTaskStatusUpdateEventOnQueue(true, TaskState.REJECTED, null); + } + + @Test + public void testRejectWithMessage() throws Exception { + taskUpdater.reject(SAMPLE_MESSAGE); + checkTaskStatusUpdateEventOnQueue(true, TaskState.REJECTED, SAMPLE_MESSAGE); + } + + @Test + public void testRequiresInputWithoutMessage() throws Exception { + taskUpdater.requiresInput(); + checkTaskStatusUpdateEventOnQueue(false, TaskState.INPUT_REQUIRED, null); + } + + @Test + public void testRequiresInputWithMessage() throws Exception { + taskUpdater.requiresInput(SAMPLE_MESSAGE); + checkTaskStatusUpdateEventOnQueue(false, TaskState.INPUT_REQUIRED, SAMPLE_MESSAGE); + } + + @Test + public void testRequiresInputWithFinalTrue() throws Exception { + taskUpdater.requiresInput(true); + checkTaskStatusUpdateEventOnQueue(true, TaskState.INPUT_REQUIRED, null); + } + + @Test + public void testRequiresInputWithMessageAndFinalTrue() throws Exception { + taskUpdater.requiresInput(SAMPLE_MESSAGE, true); + checkTaskStatusUpdateEventOnQueue(true, TaskState.INPUT_REQUIRED, SAMPLE_MESSAGE); + } + + @Test + public void testRequiresAuthWithoutMessage() throws Exception { + taskUpdater.requiresAuth(); + checkTaskStatusUpdateEventOnQueue(false, TaskState.AUTH_REQUIRED, null); + } + + @Test + public void testRequiresAuthWithMessage() throws Exception { + taskUpdater.requiresAuth(SAMPLE_MESSAGE); + checkTaskStatusUpdateEventOnQueue(false, TaskState.AUTH_REQUIRED, SAMPLE_MESSAGE); + } + + @Test + public void testRequiresAuthWithFinalTrue() throws Exception { + taskUpdater.requiresAuth(true); + checkTaskStatusUpdateEventOnQueue(true, TaskState.AUTH_REQUIRED, null); + } + + @Test + public void testRequiresAuthWithMessageAndFinalTrue() throws Exception { + taskUpdater.requiresAuth(SAMPLE_MESSAGE, true); + checkTaskStatusUpdateEventOnQueue(true, TaskState.AUTH_REQUIRED, SAMPLE_MESSAGE); + } + + @Test + public void testNonTerminalStateUpdatesAllowed() throws Exception { + // Non-terminal states should be allowed multiple times + taskUpdater.submit(); + checkTaskStatusUpdateEventOnQueue(false, TaskState.SUBMITTED, null); + + taskUpdater.startWork(); + checkTaskStatusUpdateEventOnQueue(false, TaskState.WORKING, null); + + taskUpdater.requiresInput(); + checkTaskStatusUpdateEventOnQueue(false, TaskState.INPUT_REQUIRED, null); + + taskUpdater.requiresAuth(); + checkTaskStatusUpdateEventOnQueue(false, TaskState.AUTH_REQUIRED, null); + + // Should still be able to complete + taskUpdater.complete(); + checkTaskStatusUpdateEventOnQueue(true, TaskState.COMPLETED, null); + } + @Test public void testNewAgentMessage() throws Exception { Message message = taskUpdater.newAgentMessage(SAMPLE_PARTS, null); @@ -153,6 +235,165 @@ public void testNewAgentMessageWithMetadata() throws Exception { assertEquals(metadata, message.getMetadata()); } + @Test + public void testAddArtifactWithAppendTrue() throws Exception { + taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, true, null); + Event event = eventQueue.dequeueEvent(0); + assertNotNull(event); + assertInstanceOf(TaskArtifactUpdateEvent.class, event); + + TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; + assertEquals(TEST_TASK_ID, taue.getTaskId()); + assertEquals(TEST_TASK_CONTEXT_ID, taue.getContextId()); + assertEquals("artifact-id", taue.getArtifact().artifactId()); + assertEquals("Test Artifact", taue.getArtifact().name()); + assertSame(SAMPLE_PARTS, taue.getArtifact().parts()); + assertEquals(true, taue.isAppend()); + assertNull(taue.isLastChunk()); + + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testAddArtifactWithLastChunkTrue() throws Exception { + taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, null, true); + Event event = eventQueue.dequeueEvent(0); + assertNotNull(event); + assertInstanceOf(TaskArtifactUpdateEvent.class, event); + + TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; + assertEquals("artifact-id", taue.getArtifact().artifactId()); + assertNull(taue.isAppend()); + assertEquals(true, taue.isLastChunk()); + + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testAddArtifactWithAppendAndLastChunk() throws Exception { + taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, true, false); + Event event = eventQueue.dequeueEvent(0); + assertNotNull(event); + assertInstanceOf(TaskArtifactUpdateEvent.class, event); + + TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; + assertEquals(true, taue.isAppend()); + assertEquals(false, taue.isLastChunk()); + + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testAddArtifactGeneratesIdWhenNull() throws Exception { + taskUpdater.addArtifact(SAMPLE_PARTS, null, "Test Artifact", null); + Event event = eventQueue.dequeueEvent(0); + assertNotNull(event); + assertInstanceOf(TaskArtifactUpdateEvent.class, event); + + TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; + assertNotNull(taue.getArtifact().artifactId()); + // Check that it's a valid UUID format + String artifactId = taue.getArtifact().artifactId(); + assertEquals(36, artifactId.length()); // Standard UUID length + assertTrue(artifactId.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")); + + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testTerminalStateProtectionAfterComplete() throws Exception { + // Complete the task first + taskUpdater.complete(); + checkTaskStatusUpdateEventOnQueue(true, TaskState.COMPLETED, null); + + // Try to update status again - should throw RuntimeException + RuntimeException exception = assertThrows(RuntimeException.class, () -> taskUpdater.startWork()); + assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); + + // Verify no additional events were queued + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testTerminalStateProtectionAfterFail() throws Exception { + // Fail the task first + taskUpdater.fail(); + checkTaskStatusUpdateEventOnQueue(true, TaskState.FAILED, null); + + // Try to update status again - should throw RuntimeException + RuntimeException exception = assertThrows(RuntimeException.class, () -> taskUpdater.complete()); + assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); + + // Verify no additional events were queued + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testTerminalStateProtectionAfterReject() throws Exception { + // Reject the task first + taskUpdater.reject(); + checkTaskStatusUpdateEventOnQueue(true, TaskState.REJECTED, null); + + // Try to update status again - should throw RuntimeException + RuntimeException exception = assertThrows(RuntimeException.class, () -> taskUpdater.startWork()); + assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); + + // Verify no additional events were queued + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testTerminalStateProtectionAfterCancel() throws Exception { + // Cancel the task first + taskUpdater.cancel(); + checkTaskStatusUpdateEventOnQueue(true, TaskState.CANCELED, null); + + // Try to update status again - should throw RuntimeException + RuntimeException exception = assertThrows(RuntimeException.class, () -> taskUpdater.submit()); + assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); + + // Verify no additional events were queued + assertNull(eventQueue.dequeueEvent(0)); + } + + @Test + public void testConcurrentCompletionAttempts() throws Exception { + // This test simulates race condition between multiple completion attempts + Thread thread1 = new Thread(() -> { + try { + taskUpdater.complete(); + } catch (RuntimeException e) { + // Expected for one of the threads + } + }); + + Thread thread2 = new Thread(() -> { + try { + taskUpdater.fail(); + } catch (RuntimeException e) { + // Expected for one of the threads + } + }); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + // Exactly one event should have been queued + Event event = eventQueue.dequeueEvent(0); + assertNotNull(event); + assertInstanceOf(TaskStatusUpdateEvent.class, event); + + TaskStatusUpdateEvent tsue = (TaskStatusUpdateEvent) event; + assertTrue(tsue.isFinal()); + assertTrue(tsue.getStatus().state() == TaskState.COMPLETED || tsue.getStatus().state() == TaskState.FAILED); + + // No additional events should be queued + assertNull(eventQueue.dequeueEvent(0)); + } + private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, TaskState state, Message statusMessage) throws Exception { Event event = eventQueue.dequeueEvent(0); From 4eb55b2369e99bf7270c7a04801f4c0aca4a8b8a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 25 Sep 2025 17:22:06 +0100 Subject: [PATCH 136/493] chore: tighten up tests (#300) --- .../a2a/server/events/EventConsumerTest.java | 29 +++++++++++++++++-- .../io/a2a/server/events/EventQueueTest.java | 16 +++------- .../events/InMemoryQueueManagerTest.java | 11 +++++-- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index b9a2c8228..94dbd8db9 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -9,7 +9,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.core.JsonProcessingException; @@ -297,13 +299,16 @@ public void run() { } @Test - public void testConsumeAllRaisesStoredException() { + public void testConsumeAllRaisesStoredException() throws InterruptedException { // Set an error in the event consumer setEventConsumerError(new RuntimeException("Stored error")); Flow.Publisher publisher = eventConsumer.consumeAll(); final AtomicReference receivedError = new AtomicReference<>(); + final CountDownLatch errorLatch = new CountDownLatch(1); + + publisher.subscribe(new Flow.Subscriber<>() { @Override public void onSubscribe(Flow.Subscription subscription) { @@ -313,19 +318,25 @@ public void onSubscribe(Flow.Subscription subscription) { @Override public void onNext(Event item) { // Should not be called + errorLatch.countDown(); } @Override public void onError(Throwable throwable) { receivedError.set(throwable); + errorLatch.countDown(); } @Override public void onComplete() { // Should not be called + errorLatch.countDown(); } }); + // Wait for error callback with timeout + assertTrue(errorLatch.await(5, TimeUnit.SECONDS), "Test timed out waiting for onError callback."); + assertNotNull(receivedError.get()); assertEquals("Stored error", receivedError.get().getMessage()); } @@ -341,6 +352,7 @@ public void testConsumeAllStopsOnQueueClosed() throws Exception { Flow.Publisher publisher = consumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference completed = new AtomicReference<>(false); + final CountDownLatch completionLatch = new CountDownLatch(1); publisher.subscribe(new Flow.Subscriber<>() { @Override @@ -356,14 +368,19 @@ public void onNext(Event item) { @Override public void onError(Throwable throwable) { // Should not be called + completionLatch.countDown(); } @Override public void onComplete() { completed.set(true); + completionLatch.countDown(); } }); + // Wait for completion with timeout + assertTrue(completionLatch.await(5, TimeUnit.SECONDS), "Test timed out waiting for onComplete callback."); + // Should complete immediately with no events assertTrue(completed.get()); assertEquals(0, receivedEvents.size()); @@ -385,6 +402,7 @@ public void testConsumeAllHandlesQueueClosedException() throws Exception { Flow.Publisher publisher = consumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference completed = new AtomicReference<>(false); + final CountDownLatch completionLatch = new CountDownLatch(1); publisher.subscribe(new Flow.Subscriber<>() { @Override @@ -400,14 +418,19 @@ public void onNext(Event item) { @Override public void onError(Throwable throwable) { // Should not be called + completionLatch.countDown(); } @Override public void onComplete() { completed.set(true); + completionLatch.countDown(); } }); + // Wait for completion with timeout + assertTrue(completionLatch.await(5, TimeUnit.SECONDS), "Test timed out waiting for onComplete callback."); + // Should have received the message and completed assertTrue(completed.get()); assertEquals(1, receivedEvents.size()); @@ -426,7 +449,7 @@ private Throwable getEventConsumerError() { java.lang.reflect.Field errorField = EventConsumer.class.getDeclaredField("error"); errorField.setAccessible(true); return (Throwable) errorField.get(eventConsumer); - } catch (Exception e) { + } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("Failed to access error field", e); } } @@ -436,7 +459,7 @@ private void setEventConsumerError(Throwable error) { java.lang.reflect.Field errorField = EventConsumer.class.getDeclaredField("error"); errorField.setAccessible(true); errorField.set(eventConsumer, error); - } catch (Exception e) { + } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException("Failed to set error field", e); } } diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index e11c2a738..14f3c1346 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -183,12 +183,8 @@ public void testCloseImmediatePropagationToChildren() throws Exception { assertTrue(childQueue.isClosed()); // Child queue should be cleared due to immediate close - try { - Event result = childQueue.dequeueEvent(-1); - assertNull(result); - } catch (EventQueueClosedException e) { - // This is expected when queue is closed - } + // Child queue should be cleared and closed, so dequeueing should throw + assertThrows(EventQueueClosedException.class, () -> childQueue.dequeueEvent(-1)); } @Test @@ -203,12 +199,8 @@ public void testEnqueueEventWhenClosed() throws Exception { queue.enqueueEvent(event); // Verify the queue is still empty - try { - Event dequeuedEvent = queue.dequeueEvent(-1); - assertNull(dequeuedEvent); - } catch (EventQueueClosedException e) { - // This is expected when queue is closed and empty - } + // Verify the queue is still empty and dequeueing from a closed, empty queue throws + assertThrows(EventQueueClosedException.class, () -> queue.dequeueEvent(-1)); } @Test diff --git a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java index 85368b2e2..3586d318d 100644 --- a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java @@ -76,6 +76,9 @@ public void testTapExistingQueue() { EventQueue tappedQueue = queueManager.tap(taskId); assertNotNull(tappedQueue); + + // Tapped queue should be different from original + // (it's a child queue in Java implementation) assertNotSame(queue, tappedQueue, "Tapped queue should be a different instance from the original."); } @@ -205,9 +208,11 @@ public void testCreateOrTapRaceCondition() throws InterruptedException, Executio EventQueue managerQueue = queueManager.get(taskId); assertNotNull(managerQueue); - // One of the results should be the main queue instance, and the rest should be - // different (tapped) queue instances. + // One of the results should be the main queue instance, the others should be tapped child queues. long mainQueueCount = results.stream().filter(q -> q == managerQueue).count(); - assertEquals(1, mainQueueCount, "Exactly one returned queue should be the main queue instance."); + assertEquals(1, mainQueueCount, "Exactly one thread should have created the main queue."); + + long childQueueCount = results.stream().filter(q -> q != managerQueue).count(); + assertEquals(results.size() - 1, childQueueCount, "All other threads should have received a tapped queue."); } } From 39f68f0d3b2154c1a1779288588e51beeb88fcbc Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 25 Sep 2025 18:27:41 +0100 Subject: [PATCH 137/493] chore: Backport Python InMemoryPushNotificationConfigStore tests (#295) --- ...MemoryPushNotificationConfigStoreTest.java | 333 ++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java new file mode 100644 index 000000000..4caf0c7d7 --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java @@ -0,0 +1,333 @@ +package io.a2a.server.tasks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.mockito.ArgumentCaptor; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; + +class InMemoryPushNotificationConfigStoreTest { + + private InMemoryPushNotificationConfigStore configStore; + private BasePushNotificationSender notificationSender; + + @Mock + private A2AHttpClient mockHttpClient; + + @Mock + private A2AHttpClient.PostBuilder mockPostBuilder; + + @Mock + private A2AHttpResponse mockHttpResponse; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + configStore = new InMemoryPushNotificationConfigStore(); + notificationSender = new BasePushNotificationSender(configStore, mockHttpClient); + } + + private Task createSampleTask(String taskId, TaskState state) { + return new Task.Builder() + .id(taskId) + .contextId("ctx456") + .status(new TaskStatus(state)) + .build(); + } + + private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { + return new PushNotificationConfig.Builder() + .url(url) + .id(configId) + .token(token) + .build(); + } + + @Test + public void testSetInfoAddsNewConfig() { + String taskId = "task_new"; + PushNotificationConfig config = createSamplePushConfig("http://new.url/callback", "cfg1", null); + + PushNotificationConfig result = configStore.setInfo(taskId, config); + + assertNotNull(result); + assertEquals(config.url(), result.url()); + assertEquals(config.id(), result.id()); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(1, configs.size()); + assertEquals(config.url(), configs.get(0).url()); + assertEquals(config.id(), configs.get(0).id()); + } + + @Test + public void testSetInfoAppendsToExistingConfig() { + String taskId = "task_update"; + PushNotificationConfig initialConfig = createSamplePushConfig( + "http://initial.url/callback", "cfg_initial", null); + configStore.setInfo(taskId, initialConfig); + + PushNotificationConfig updatedConfig = createSamplePushConfig( + "http://updated.url/callback", "cfg_updated", null); + configStore.setInfo(taskId, updatedConfig); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(2, configs.size()); + + // Find the configs by ID since order might vary + PushNotificationConfig foundInitial = configs.stream() + .filter(c -> "cfg_initial".equals(c.id())) + .findFirst() + .orElse(null); + PushNotificationConfig foundUpdated = configs.stream() + .filter(c -> "cfg_updated".equals(c.id())) + .findFirst() + .orElse(null); + + assertNotNull(foundInitial); + assertNotNull(foundUpdated); + assertEquals(initialConfig.url(), foundInitial.url()); + assertEquals(updatedConfig.url(), foundUpdated.url()); + } + + @Test + public void testSetInfoWithoutConfigId() { + String taskId = "task1"; + PushNotificationConfig initialConfig = new PushNotificationConfig.Builder() + .url("http://initial.url/callback") + .build(); // No ID set + + PushNotificationConfig result = configStore.setInfo(taskId, initialConfig); + assertEquals(taskId, result.id(), "Config ID should default to taskId when not provided"); + + List configs = configStore.getInfo(taskId); + assertEquals(1, configs.size()); + assertEquals(taskId, configs.get(0).id()); + + PushNotificationConfig updatedConfig = new PushNotificationConfig.Builder() + .url("http://initial.url/callback_new") + .build(); // No ID set + + PushNotificationConfig updatedResult = configStore.setInfo(taskId, updatedConfig); + assertEquals(taskId, updatedResult.id()); + + configs = configStore.getInfo(taskId); + assertEquals(1, configs.size(), "Should replace existing config with same ID rather than adding new one"); + assertEquals(updatedConfig.url(), configs.get(0).url()); + } + + @Test + public void testGetInfoExistingConfig() { + String taskId = "task_get_exist"; + PushNotificationConfig config = createSamplePushConfig("http://get.this/callback", "cfg1", null); + configStore.setInfo(taskId, config); + + List retrievedConfigs = configStore.getInfo(taskId); + assertNotNull(retrievedConfigs); + assertEquals(1, retrievedConfigs.size()); + assertEquals(config.url(), retrievedConfigs.get(0).url()); + assertEquals(config.id(), retrievedConfigs.get(0).id()); + } + + @Test + public void testGetInfoNonExistentConfig() { + String taskId = "task_get_non_exist"; + List retrievedConfigs = configStore.getInfo(taskId); + assertNull(retrievedConfigs, "Should return null for non-existent task ID"); + } + + @Test + public void testDeleteInfoExistingConfig() { + String taskId = "task_delete_exist"; + PushNotificationConfig config = createSamplePushConfig("http://delete.this/callback", "cfg1", null); + configStore.setInfo(taskId, config); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(1, configs.size()); + + configStore.deleteInfo(taskId, config.id()); + + List configsAfterDelete = configStore.getInfo(taskId); + assertNull(configsAfterDelete, "Should return null when no configs remain after deletion"); + } + + @Test + public void testDeleteInfoNonExistentConfig() { + String taskId = "task_delete_non_exist"; + // Should not throw an error + configStore.deleteInfo(taskId, "non_existent_id"); + + List configs = configStore.getInfo(taskId); + assertNull(configs, "Should still return null for non-existent task ID"); + } + + @Test + public void testDeleteInfoWithNullConfigId() { + String taskId = "task_delete_null_config"; + PushNotificationConfig config = new PushNotificationConfig.Builder() + .url("http://delete.this/callback") + .build(); // No ID set, will use taskId + configStore.setInfo(taskId, config); + + // Delete with null configId should use taskId + configStore.deleteInfo(taskId, null); + + List configs = configStore.getInfo(taskId); + assertNull(configs, "Should return null after deletion when using taskId as configId"); + } + + @Test + public void testSendNotificationSuccess() throws Exception { + String taskId = "task_send_success"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", null); + configStore.setInfo(taskId, config); + + // Mock successful HTTP response + when(mockHttpClient.createPost()).thenReturn(mockPostBuilder); + when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.post()).thenReturn(mockHttpResponse); + when(mockHttpResponse.success()).thenReturn(true); + + notificationSender.sendNotification(task); + + // Verify HTTP client was called + ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + verify(mockHttpClient).createPost(); + verify(mockPostBuilder).url(config.url()); + verify(mockPostBuilder).body(bodyCaptor.capture()); + verify(mockPostBuilder).post(); + + // Verify the request body contains the task data + String sentBody = bodyCaptor.getValue(); + assertTrue(sentBody.contains(task.getId())); + assertTrue(sentBody.contains(task.getStatus().state().asString())); + } + + @Test + @Disabled("Token authentication is not yet implemented in BasePushNotificationSender (TODO auth)") + public void testSendNotificationWithToken() throws Exception { + String taskId = "task_send_with_token"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", "unique_token"); + configStore.setInfo(taskId, config); + + // Mock successful HTTP response + when(mockHttpClient.createPost()).thenReturn(mockPostBuilder); + when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.post()).thenReturn(mockHttpResponse); + when(mockHttpResponse.success()).thenReturn(true); + + notificationSender.sendNotification(task); + + // TODO: Once token authentication is implemented, verify that: + // 1. The token is included in request headers (e.g., X-A2A-Notification-Token) + // 2. The HTTP client is called with proper authentication + // 3. The token from the config is actually used + + // For now, just verify basic HTTP client interaction + ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + verify(mockHttpClient).createPost(); + verify(mockPostBuilder).url(config.url()); + verify(mockPostBuilder).body(bodyCaptor.capture()); + verify(mockPostBuilder).post(); + + // Verify the request body contains the task data + String sentBody = bodyCaptor.getValue(); + assertTrue(sentBody.contains(task.getId())); + assertTrue(sentBody.contains(task.getStatus().state().asString())); + } + + @Test + public void testSendNotificationNoConfig() throws Exception { + String taskId = "task_send_no_config"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + + notificationSender.sendNotification(task); + + // Verify HTTP client was never called + verify(mockHttpClient, never()).createPost(); + } + + @Test + public void testMultipleConfigsForSameTask() { + String taskId = "task_multiple"; + PushNotificationConfig config1 = createSamplePushConfig("http://url1.com/callback", "cfg1", null); + PushNotificationConfig config2 = createSamplePushConfig("http://url2.com/callback", "cfg2", null); + + configStore.setInfo(taskId, config1); + configStore.setInfo(taskId, config2); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(2, configs.size()); + + // Verify both configs are present + assertTrue(configs.stream().anyMatch(c -> "cfg1".equals(c.id()))); + assertTrue(configs.stream().anyMatch(c -> "cfg2".equals(c.id()))); + } + + @Test + public void testDeleteSpecificConfigFromMultiple() { + String taskId = "task_delete_specific"; + PushNotificationConfig config1 = createSamplePushConfig("http://url1.com/callback", "cfg1", null); + PushNotificationConfig config2 = createSamplePushConfig("http://url2.com/callback", "cfg2", null); + + configStore.setInfo(taskId, config1); + configStore.setInfo(taskId, config2); + + // Delete only config1 + configStore.deleteInfo(taskId, "cfg1"); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(1, configs.size()); + assertEquals("cfg2", configs.get(0).id()); + } + + @Test + public void testConfigStoreIntegration() { + String taskId = "integration_test"; + PushNotificationConfig config = createSamplePushConfig("http://example.com", "test_id", "test_token"); + + // Test that we can store and retrieve configurations + PushNotificationConfig storedConfig = configStore.setInfo(taskId, config); + assertEquals(config.url(), storedConfig.url()); + assertEquals(config.token(), storedConfig.token()); + + List retrievedConfigs = configStore.getInfo(taskId); + assertEquals(1, retrievedConfigs.size()); + assertEquals(config.url(), retrievedConfigs.get(0).url()); + + // Test deletion + configStore.deleteInfo(taskId, storedConfig.id()); + List afterDeletion = configStore.getInfo(taskId); + assertNull(afterDeletion); + } + +} From 19119ab43ba9961a069a33b1102e7ccf98e7fb51 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Fri, 26 Sep 2025 05:01:20 -0400 Subject: [PATCH 138/493] fix: Ensure authentication is required for all endpoints that require it and add an AuthInterceptor that can obtain credentials from a CredentialService (#292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #275 and #274 🦕 --- .../client/transport/grpc/GrpcTransport.java | 111 ++++-- .../grpc/GrpcTransportConfigBuilder.java | 4 +- .../transport/grpc/GrpcTransportProvider.java | 2 +- client/transport/spi/pom.xml | 10 + .../interceptors/auth/AuthInterceptor.java | 70 ++++ .../interceptors/auth/CredentialService.java | 18 + .../InMemoryContextCredentialService.java | 55 +++ .../auth/AuthInterceptorTest.java | 328 ++++++++++++++++++ reference/grpc/pom.xml | 1 + .../grpc/quarkus/QuarkusGrpcHandler.java | 4 +- .../server/apps/quarkus/A2AServerRoutes.java | 2 + .../server/rest/quarkus/A2AServerRoutes.java | 4 + 12 files changed, 586 insertions(+), 23 deletions(-) create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java create mode 100644 client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 87de05ecd..2023339d2 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -7,11 +7,15 @@ import static io.a2a.util.Assert.checkNotNullParam; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; +import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; +import io.a2a.client.transport.spi.interceptors.auth.AuthInterceptor; import io.a2a.common.A2AHeaders; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.CancelTaskRequest; @@ -32,11 +36,14 @@ import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.MessageSendParams; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskResubscriptionRequest; import io.grpc.Channel; import io.grpc.Metadata; import io.grpc.StatusRuntimeException; @@ -45,15 +52,27 @@ public class GrpcTransport implements ClientTransport { + private static final Metadata.Key AUTHORIZATION_METADATA_KEY = Metadata.Key.of( + AuthInterceptor.AUTHORIZATION, + Metadata.ASCII_STRING_MARSHALLER); + private static final Metadata.Key EXTENSIONS_KEY = Metadata.Key.of( + A2AHeaders.X_A2A_EXTENSIONS, + Metadata.ASCII_STRING_MARSHALLER); private final A2AServiceBlockingV2Stub blockingStub; private final A2AServiceStub asyncStub; + private final List interceptors; private AgentCard agentCard; public GrpcTransport(Channel channel, AgentCard agentCard) { + this(channel, agentCard, null); + } + + public GrpcTransport(Channel channel, AgentCard agentCard, List interceptors) { checkNotNullParam("channel", channel); this.asyncStub = A2AServiceGrpc.newStub(channel); this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); this.agentCard = agentCard; + this.interceptors = interceptors; } @Override @@ -61,9 +80,11 @@ public EventKind sendMessage(MessageSendParams request, ClientCallContext contex checkNotNullParam("request", request); SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest(request, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.SendMessageRequest.METHOD, sendMessageRequest, + agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); SendMessageResponse response = stubWithMetadata.sendMessage(sendMessageRequest); if (response.hasMsg()) { return FromProto.message(response.getMsg()); @@ -83,10 +104,12 @@ public void sendMessageStreaming(MessageSendParams request, Consumer streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); try { - A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context); + A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context, payloadAndHeaders); stubWithMetadata.sendStreamingMessage(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to send streaming message request: "); @@ -103,9 +126,11 @@ public Task getTask(TaskQueryParams request, ClientCallContext context) throws A requestBuilder.setHistoryLength(request.historyLength()); } GetTaskRequest getTaskRequest = requestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskRequest.METHOD, getTaskRequest, + agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.task(stubWithMetadata.getTask(getTaskRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to get task: "); @@ -119,9 +144,11 @@ public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A CancelTaskRequest cancelTaskRequest = CancelTaskRequest.newBuilder() .setName("tasks/" + request.id()) .build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.CancelTaskRequest.METHOD, cancelTaskRequest, + agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.task(stubWithMetadata.cancelTask(cancelTaskRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to cancel task: "); @@ -139,9 +166,11 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN .setConfig(ToProto.taskPushNotificationConfig(request)) .setConfigId(configId != null ? configId : request.taskId()) .build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, + grpcRequest, agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.taskPushNotificationConfig(stubWithMetadata.createTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to create task push notification config: "); @@ -157,9 +186,11 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( GetTaskPushNotificationConfigRequest grpcRequest = GetTaskPushNotificationConfigRequest.newBuilder() .setName(getTaskPushNotificationConfigName(request)) .build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskPushNotificationConfigRequest.METHOD, + grpcRequest, agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.taskPushNotificationConfig(stubWithMetadata.getTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to get task push notification config: "); @@ -175,9 +206,11 @@ public List listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigRequest grpcRequest = ListTaskPushNotificationConfigRequest.newBuilder() .setParent("tasks/" + request.id()) .build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.ListTaskPushNotificationConfigRequest.METHOD, + grpcRequest, agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return stubWithMetadata.listTaskPushNotificationConfig(grpcRequest).getConfigsList().stream() .map(FromProto::taskPushNotificationConfig) .collect(Collectors.toList()); @@ -194,9 +227,11 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC DeleteTaskPushNotificationConfigRequest grpcRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(getTaskPushNotificationConfigName(request.id(), request.pushNotificationConfigId())) .build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.DeleteTaskPushNotificationConfigRequest.METHOD, + grpcRequest, agentCard, context); try { - A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context); + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); stubWithMetadata.deleteTaskPushNotificationConfig(grpcRequest); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to delete task push notification config: "); @@ -212,11 +247,13 @@ public void resubscribe(TaskIdParams request, Consumer event TaskSubscriptionRequest grpcRequest = TaskSubscriptionRequest.newBuilder() .setName("tasks/" + request.id()) .build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(TaskResubscriptionRequest.METHOD, + grpcRequest, agentCard, context); StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); try { - A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context); + A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context, payloadAndHeaders); stubWithMetadata.taskSubscription(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to resubscribe task push notification config: "); @@ -249,43 +286,64 @@ private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messag * Creates gRPC metadata from ClientCallContext headers. * Extracts headers like X-A2A-Extensions and sets them as gRPC metadata. */ - private Metadata createGrpcMetadata(ClientCallContext context) { + private Metadata createGrpcMetadata(ClientCallContext context, PayloadAndHeaders payloadAndHeaders) { Metadata metadata = new Metadata(); if (context != null && context.getHeaders() != null) { // Set X-A2A-Extensions header if present String extensionsHeader = context.getHeaders().get(A2AHeaders.X_A2A_EXTENSIONS); if (extensionsHeader != null) { - Metadata.Key extensionsKey = Metadata.Key.of(A2AHeaders.X_A2A_EXTENSIONS, Metadata.ASCII_STRING_MARSHALLER); - metadata.put(extensionsKey, extensionsHeader); + metadata.put(EXTENSIONS_KEY, extensionsHeader); } // Add other headers as needed in the future // For now, we only handle X-A2A-Extensions } + if (payloadAndHeaders != null && payloadAndHeaders.getHeaders() != null) { + // Handle all headers from interceptors (including auth headers) + for (Map.Entry headerEntry : payloadAndHeaders.getHeaders().entrySet()) { + String headerName = headerEntry.getKey(); + String headerValue = headerEntry.getValue(); + + if (headerValue != null) { + // Use static key for common Authorization header, create dynamic keys for others + if (AuthInterceptor.AUTHORIZATION.equals(headerName)) { + metadata.put(AUTHORIZATION_METADATA_KEY, headerValue); + } else { + // Create a metadata key dynamically for API keys and other custom headers + Metadata.Key metadataKey = Metadata.Key.of(headerName, Metadata.ASCII_STRING_MARSHALLER); + metadata.put(metadataKey, headerValue); + } + } + } + } return metadata; } /** * Creates a blocking stub with metadata attached from the ClientCallContext. - * - * @param context the client call context + * + * @param context the client call context + * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors * @return blocking stub with metadata interceptor */ - private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(ClientCallContext context) { - Metadata metadata = createGrpcMetadata(context); + private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(ClientCallContext context, + PayloadAndHeaders payloadAndHeaders) { + Metadata metadata = createGrpcMetadata(context, payloadAndHeaders); return blockingStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); } /** * Creates an async stub with metadata attached from the ClientCallContext. - * - * @param context the client call context + * + * @param context the client call context + * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors * @return async stub with metadata interceptor */ - private A2AServiceStub createAsyncStubWithMetadata(ClientCallContext context) { - Metadata metadata = createGrpcMetadata(context); + private A2AServiceStub createAsyncStubWithMetadata(ClientCallContext context, + PayloadAndHeaders payloadAndHeaders) { + Metadata metadata = createGrpcMetadata(context, payloadAndHeaders); return asyncStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); } @@ -307,4 +365,17 @@ private String getTaskPushNotificationConfigName(String taskId, String pushNotif return name.toString(); } + private PayloadAndHeaders applyInterceptors(String methodName, Object payload, + AgentCard agentCard, ClientCallContext clientCallContext) { + PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, + clientCallContext != null ? clientCallContext.getHeaders() : null); + if (interceptors != null && ! interceptors.isEmpty()) { + for (ClientCallInterceptor interceptor : interceptors) { + payloadAndHeaders = interceptor.intercept(methodName, payloadAndHeaders.getPayload(), + payloadAndHeaders.getHeaders(), agentCard, clientCallContext); + } + } + return payloadAndHeaders; + } + } \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java index 68909e065..1f1d0a2b8 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java @@ -20,6 +20,8 @@ public GrpcTransportConfigBuilder channelFactory(Function chann @Override public GrpcTransportConfig build() { - return new GrpcTransportConfig(channelFactory); + GrpcTransportConfig config = new GrpcTransportConfig(channelFactory); + config.setInterceptors(interceptors); + return config; } } \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java index 229ac4149..f087ac5d1 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -17,7 +17,7 @@ public GrpcTransport create(GrpcTransportConfig grpcTransportConfig, AgentCard a Channel channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); if (channel != null) { - return new GrpcTransport(channel, agentCard); + return new GrpcTransport(channel, agentCard, grpcTransportConfig.getInterceptors()); } throw new A2AClientException("Missing required GrpcTransportConfig"); diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index 054e96603..dca7aca3a 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -21,6 +21,16 @@ io.github.a2asdk a2a-java-sdk-spec + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java new file mode 100644 index 000000000..1995e4fb0 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java @@ -0,0 +1,70 @@ +package io.a2a.client.transport.spi.interceptors.auth; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; +import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; +import io.a2a.spec.APIKeySecurityScheme; +import io.a2a.spec.AgentCard; +import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.OAuth2SecurityScheme; +import io.a2a.spec.OpenIdConnectSecurityScheme; +import io.a2a.spec.SecurityScheme; + +/** + * An interceptor that automatically adds authentication details to requests + * based on the agent's security schemes and the credentials available. + */ +public class AuthInterceptor extends ClientCallInterceptor { + + private static final String BEARER_SCHEME = "bearer"; + public static final String AUTHORIZATION = "Authorization"; + private static final String BEARER = "Bearer "; + private final CredentialService credentialService; + + public AuthInterceptor(final CredentialService credentialService) { + this.credentialService = credentialService; + } + + @Override + public PayloadAndHeaders intercept(String methodName, Object payload, Map headers, + AgentCard agentCard, ClientCallContext clientCallContext) { + Map updatedHeaders = new HashMap<>(headers == null ? new HashMap<>() : headers); + if (agentCard == null || agentCard.security() == null || agentCard.securitySchemes() == null) { + return new PayloadAndHeaders(payload, updatedHeaders); + } + for (Map> requirement : agentCard.security()) { + for (String securitySchemeName : requirement.keySet()) { + String credential = credentialService.getCredential(securitySchemeName, clientCallContext); + if (credential != null && agentCard.securitySchemes().containsKey(securitySchemeName)) { + SecurityScheme securityScheme = agentCard.securitySchemes().get(securitySchemeName); + if (securityScheme == null) { + continue; + } + if (securityScheme instanceof HTTPAuthSecurityScheme httpAuthSecurityScheme) { + if (httpAuthSecurityScheme.getScheme().toLowerCase(Locale.ROOT).equals(BEARER_SCHEME)) { + updatedHeaders.put(AUTHORIZATION, getBearerValue(credential)); + return new PayloadAndHeaders(payload, updatedHeaders); + } + } else if (securityScheme instanceof OAuth2SecurityScheme + || securityScheme instanceof OpenIdConnectSecurityScheme) { + updatedHeaders.put(AUTHORIZATION, getBearerValue(credential)); + return new PayloadAndHeaders(payload, updatedHeaders); + } else if (securityScheme instanceof APIKeySecurityScheme apiKeySecurityScheme) { + updatedHeaders.put(apiKeySecurityScheme.getName(), credential); + return new PayloadAndHeaders(payload, updatedHeaders); + } + } + } + } + return new PayloadAndHeaders(payload, updatedHeaders); + } + + private static String getBearerValue(String credential) { + return BEARER + credential; + } +} diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java new file mode 100644 index 000000000..6bccdadd9 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java @@ -0,0 +1,18 @@ +package io.a2a.client.transport.spi.interceptors.auth; + +import io.a2a.client.transport.spi.interceptors.ClientCallContext; + +/** + * Used to retrieve credentials. + */ +public interface CredentialService { + + /** + * Retrieves a credential (e.g., token) for a security scheme. + * + * @param securitySchemeName the name of the security scheme + * @param clientCallContext the client call context, which may be {@code null}. + * @return the credential or {@code null} if the credential could not be retrieved + */ + String getCredential(String securitySchemeName, ClientCallContext clientCallContext); +} diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java new file mode 100644 index 000000000..a1974cd13 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java @@ -0,0 +1,55 @@ +package io.a2a.client.transport.spi.interceptors.auth; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import io.a2a.client.transport.spi.interceptors.ClientCallContext; + +/** + * A simple in-memory store for session-keyed credentials. + * This class uses the 'sessionId' from the {@code ClientCallContext} state to + * store and retrieve credentials + */ +public class InMemoryContextCredentialService implements CredentialService { + + private static final String SESSION_ID = "sessionId"; + + // maps a sessionId to a map of security scheme names to credentials + private final ConcurrentMap> credentialStore; + + public InMemoryContextCredentialService() { + credentialStore = new ConcurrentHashMap<>(); + } + + @Override + public String getCredential(String securitySchemeName, + ClientCallContext clientCallContext) { + if (clientCallContext == null || !clientCallContext.getState().containsKey(SESSION_ID)) { + // no credential to retrieve + return null; + } + + Object sessionIdObj = clientCallContext.getState().get(SESSION_ID); + if (! (sessionIdObj instanceof String)) { + return null; + } + String sessionId = (String) sessionIdObj; + Map sessionCredentials = credentialStore.get(sessionId); + if (sessionCredentials == null) { + return null; + } + return sessionCredentials.get(securitySchemeName); + } + + /** + * Method to populate the in-memory credential service. + * + * @param sessionId the session ID + * @param securitySchemeName the name of the security scheme + * @param credential the credential string + */ + public void setCredential(String sessionId, String securitySchemeName, String credential) { + credentialStore.computeIfAbsent(sessionId, k -> new ConcurrentHashMap<>()).put(securitySchemeName, credential); + } +} diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java new file mode 100644 index 000000000..53319c4b5 --- /dev/null +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java @@ -0,0 +1,328 @@ +package io.a2a.client.transport.spi.interceptors.auth; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; +import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; +import io.a2a.spec.APIKeySecurityScheme; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.OAuth2SecurityScheme; +import io.a2a.spec.OAuthFlows; +import io.a2a.spec.OpenIdConnectSecurityScheme; +import io.a2a.spec.SecurityScheme; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AuthInterceptorTest { + + private InMemoryContextCredentialService credentialStore; + private AuthInterceptor authInterceptor; + + @BeforeEach + void setUp() { + credentialStore = new InMemoryContextCredentialService(); + authInterceptor = new AuthInterceptor(credentialStore); + } + + private static class HeaderInterceptor extends ClientCallInterceptor { + private final String headerName; + private final String headerValue; + + public HeaderInterceptor(String headerName, String headerValue) { + this.headerName = headerName; + this.headerValue = headerValue; + } + + @Override + public PayloadAndHeaders intercept(String methodName, Object payload, Map headers, + AgentCard agentCard, ClientCallContext clientCallContext) { + Map updatedHeaders = new HashMap<>(headers); + updatedHeaders.put(headerName, headerValue); + return new PayloadAndHeaders(payload, updatedHeaders); + } + } + + private static class AuthTestCase { + final String url; + final String sessionId; + final String schemeName; + final String credential; + final SecurityScheme securityScheme; + final String expectedHeaderKey; + final String expectedHeaderValue; + + AuthTestCase(String url, String sessionId, String schemeName, String credential, + SecurityScheme securityScheme, String expectedHeaderKey, String expectedHeaderValue) { + this.url = url; + this.sessionId = sessionId; + this.schemeName = schemeName; + this.credential = credential; + this.securityScheme = securityScheme; + this.expectedHeaderKey = expectedHeaderKey; + this.expectedHeaderValue = expectedHeaderValue; + } + } + + @Test + public void testAPIKeySecurityScheme() { + AuthTestCase authTestCase = new AuthTestCase( + "http://agent.com/rpc", + "session-id", + APIKeySecurityScheme.API_KEY, + "secret-api-key", + new APIKeySecurityScheme("header", "x-api-key", "API Key authentication"), + "x-api-key", + "secret-api-key" + ); + testSecurityScheme(authTestCase); + } + + @Test + public void testOAuth2SecurityScheme() { + AuthTestCase authTestCase = new AuthTestCase( + "http://agent.com/rpc", + "session-id", + OAuth2SecurityScheme.OAUTH2, + "secret-oauth-access-token", + new OAuth2SecurityScheme(new OAuthFlows.Builder().build(), "OAuth2 authentication", null), + "Authorization", + "Bearer secret-oauth-access-token" + ); + testSecurityScheme(authTestCase); + } + + @Test + public void testOidcSecurityScheme() { + AuthTestCase authTestCase = new AuthTestCase( + "http://agent.com/rpc", + "session-id", + OpenIdConnectSecurityScheme.OPENID_CONNECT, + "secret-oidc-id-token", + new OpenIdConnectSecurityScheme("http://provider.com/.well-known/openid-configuration", "OIDC authentication"), + "Authorization", + "Bearer secret-oidc-id-token" + ); + testSecurityScheme(authTestCase); + } + + @Test + public void testBearerSecurityScheme() { + AuthTestCase authTestCase = new AuthTestCase( + "http://agent.com/rpc", + "session-id", + "bearer", + "bearer-token-123", + new HTTPAuthSecurityScheme(null, "bearer", "Bearer token authentication"), + "Authorization", + "Bearer bearer-token-123" + ); + testSecurityScheme(authTestCase); + } + + private void testSecurityScheme(AuthTestCase authTestCase) { + credentialStore.setCredential(authTestCase.sessionId, authTestCase.schemeName, authTestCase.credential); + + AgentCard agentCard = createAgentCard(authTestCase.schemeName, authTestCase.securityScheme); + Map requestPayload = Map.of("test", "payload"); + Map headers = Map.of(); + ClientCallContext context = new ClientCallContext(Map.of("sessionId", authTestCase.sessionId), Map.of()); + + PayloadAndHeaders result = authInterceptor.intercept( + "message/send", + requestPayload, + headers, + agentCard, + context + ); + + assertEquals(requestPayload, result.getPayload()); + assertEquals(authTestCase.expectedHeaderValue, result.getHeaders().get(authTestCase.expectedHeaderKey)); + } + + @Test + void testAuthInterceptorWithoutAgentCard() { + Map requestPayload = Map.of("foo", "bar"); + Map headers = Map.of("foo", "bar"); + + PayloadAndHeaders result = authInterceptor.intercept( + "message/send", + requestPayload, + headers, + null, // no agent card + new ClientCallContext(Map.of(), Map.of()) + ); + + // should be unchanged + assertEquals(requestPayload, result.getPayload()); + assertEquals(headers, result.getHeaders()); + } + + @Test + void testInMemoryContextCredentialStore() { + String sessionId = "session-id"; + String schemeName = "test-scheme"; + String credential = "test-token"; + + credentialStore.setCredential(sessionId, schemeName, credential); + ClientCallContext context = new ClientCallContext(Map.of("sessionId", sessionId), Map.of()); + String retrievedCredential = credentialStore.getCredential(schemeName, context); + assertEquals(credential, retrievedCredential); + + // wrong session ID + ClientCallContext wrongContext = new ClientCallContext(Map.of("sessionId", "wrong-session"), Map.of()); + retrievedCredential = credentialStore.getCredential(schemeName, wrongContext); + assertNull(retrievedCredential); + + retrievedCredential = credentialStore.getCredential(schemeName, null); + assertNull(retrievedCredential); + + // no session ID in context + ClientCallContext emptyContext = new ClientCallContext(Map.of(), Map.of()); + retrievedCredential = credentialStore.getCredential(schemeName, emptyContext); + assertNull(retrievedCredential); + + String newCredential = "new-token"; + credentialStore.setCredential(sessionId, schemeName, newCredential); + retrievedCredential = credentialStore.getCredential(schemeName, context); + assertEquals(newCredential, retrievedCredential); + } + + @Test + void testCustomInterceptor() { + String headerName = "X-Test-Header"; + String headerValue = "Test-Value-123"; + HeaderInterceptor interceptor = new HeaderInterceptor(headerName, headerValue); + + Map payload = Map.of("test", "payload"); + Map headers = Map.of(); + + PayloadAndHeaders result = interceptor.intercept( + "message/send", + payload, + headers, + null, + null + ); + + assertEquals(payload, result.getPayload()); + assertEquals(headerValue, result.getHeaders().get(headerName)); + } + + @Test + void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() { + String schemeName = "missing"; + String sessionId = "session-id"; + String credential = "dummy-token"; + + credentialStore.setCredential(sessionId, schemeName, credential); + + // Create agent card with security requirement but no scheme definition + AgentCard agentCard = new AgentCard.Builder() + .name("missing") + .description("Uses missing scheme definition") + .url("http://agent.com/rpc") + .version("1.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .skills(List.of()) + .security(List.of(Map.of(schemeName, List.of()))) + .securitySchemes(Map.of()) // no security schemes + .build(); + + Map requestPayload = Map.of("foo", "bar"); + Map headers = Map.of("fizz", "buzz"); + ClientCallContext context = new ClientCallContext(Map.of("sessionId", sessionId), Map.of()); + + PayloadAndHeaders result = authInterceptor.intercept( + "message/send", + requestPayload, + headers, + agentCard, + context + ); + + assertEquals(requestPayload, result.getPayload()); + assertEquals(headers, result.getHeaders()); + } + + @Test + void testNoCredentialAvailable() { + String schemeName = "apikey"; + SecurityScheme securityScheme = new APIKeySecurityScheme("header", "X-API-Key", "API Key authentication"); + AgentCard agentCard = createAgentCard(schemeName, securityScheme); + + Map requestPayload = Map.of("test", "payload"); + Map headers = Map.of(); + ClientCallContext context = new ClientCallContext(Map.of("sessionId", "session-id"), Map.of()); + + PayloadAndHeaders result = authInterceptor.intercept( + "message/send", + requestPayload, + headers, + agentCard, + context + ); + + assertEquals(requestPayload, result.getPayload()); + assertEquals(headers, result.getHeaders()); // headers should be unchanged + } + + @Test + void testNoAgentCardSecuritySpecified() { + // Arrange + AgentCard agentCard = new AgentCard.Builder() + .name("nosecuritybot") + .description("A bot with no security requirements") + .url("http://agent.com/rpc") + .version("1.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .skills(List.of()) + .security(null) // no security info + .build(); + + Map requestPayload = Map.of("test", "payload"); + Map headers = Map.of(); + ClientCallContext context = new ClientCallContext(Map.of("sessionId", "session-id"), Map.of()); + + PayloadAndHeaders result = authInterceptor.intercept( + "message/send", + requestPayload, + headers, + agentCard, + context + ); + + assertEquals(requestPayload, result.getPayload()); + assertEquals(headers, result.getHeaders()); + } + + /** + * Helper method to create an AgentCard with specified security scheme. + */ + private AgentCard createAgentCard(String schemeName, SecurityScheme securityScheme) { + return new AgentCard.Builder() + .name(schemeName + "bot") + .description("A bot that uses " + schemeName) + .url("http://agent.com/rpc") + .version("1.0") + .capabilities(new AgentCapabilities.Builder().build()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .skills(List.of()) + .security(List.of(Map.of(schemeName, List.of()))) + .securitySchemes(Map.of(schemeName, securityScheme)) + .build(); + } +} diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 4d8a08e9d..aab281760 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -55,6 +55,7 @@ io.quarkus quarkus-rest + test io.quarkus diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 355416441..74fd4a217 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -10,9 +10,11 @@ import io.a2a.transport.grpc.handler.GrpcHandler; import io.quarkus.grpc.GrpcService; import io.quarkus.grpc.RegisterInterceptor; +import io.quarkus.security.Authenticated; @GrpcService @RegisterInterceptor(A2AExtensionsInterceptor.class) +@Authenticated public class QuarkusGrpcHandler extends GrpcHandler { private final AgentCard agentCard; @@ -42,4 +44,4 @@ protected AgentCard getAgentCard() { protected CallContextFactory getCallContextFactory() { return callContextFactoryInstance.isUnsatisfied() ? null : callContextFactoryInstance.get(); } -} \ No newline at end of file +} diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 3958d8eed..af1ec55c2 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -54,6 +54,7 @@ import io.a2a.spec.UnsupportedOperationError; import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import io.a2a.util.Utils; +import io.quarkus.security.Authenticated; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; @@ -84,6 +85,7 @@ public class A2AServerRoutes { Instance callContextFactory; @Route(path = "/", methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + @Authenticated public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { boolean streaming = false; ServerCallContext context = createCallContext(rc); diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 5e0f3e0f8..a08fb400a 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -8,6 +8,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; +import jakarta.annotation.security.PermitAll; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import jakarta.inject.Singleton; @@ -24,6 +25,7 @@ import io.a2a.transport.rest.handler.RestHandler; import io.a2a.transport.rest.handler.RestHandler.HTTPRestResponse; import io.a2a.transport.rest.handler.RestHandler.HTTPRestStreamingResponse; +import io.quarkus.security.Authenticated; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; @@ -43,6 +45,7 @@ import io.a2a.server.extensions.A2AExtensions; @Singleton +@Authenticated public class A2AServerRoutes { @Inject @@ -254,6 +257,7 @@ public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { * @param rc */ @Route(path = "/.well-known/agent-card.json", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) + @PermitAll public void getAgentCard(RoutingContext rc) { HTTPRestResponse response = jsonRestHandler.getAgentCard(); rc.response() From c5d6dd2cc961ea950add3263dbfbc9ad4ea4efe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diana=20K=C5=99epinsk=C3=A1?= Date: Fri, 26 Sep 2025 17:26:00 +0200 Subject: [PATCH 139/493] fix: Validate that the primary URL specified in the AgentCard matches the URL for the preferred transport (#306) fixes #272 --- .../io/a2a/server/AgentCardValidator.java | 16 ++++++++++ .../io/a2a/server/AgentCardValidatorTest.java | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/server-common/src/main/java/io/a2a/server/AgentCardValidator.java b/server-common/src/main/java/io/a2a/server/AgentCardValidator.java index 8d22bee59..25c3a8572 100644 --- a/server-common/src/main/java/io/a2a/server/AgentCardValidator.java +++ b/server-common/src/main/java/io/a2a/server/AgentCardValidator.java @@ -84,6 +84,22 @@ static void validateTransportConfiguration(AgentCard agentCard, Set avai // Following the GitHub issue suggestion to use an error instead of warning throw new IllegalStateException(errorMessage); } + + // check that the primary URL matches the URL for the preferred transport + if (agentCard.additionalInterfaces() != null) { + agentCard.additionalInterfaces().stream() + .filter(agentInterface -> agentInterface.transport().equals(agentCard.preferredTransport())) + .findFirst() + .ifPresent(preferredTransportAgentInterface -> { + if (!preferredTransportAgentInterface.url().equals(agentCard.url())) { + LOGGER.warning(String.format( + "AgentCard's URL=%s does not correspond to the URL of the preferred transport=%s.", + agentCard.url(), + preferredTransportAgentInterface.url() + )); + } + }); + } } /** diff --git a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java index 83bbae073..27b6b8632 100644 --- a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java +++ b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java @@ -229,6 +229,37 @@ void testSkipPropertiesFilterWarnings() { } } + @Test + void testPrimaryURLMatchesPreferredTransport() { + // Create a simple AgentCard (with preferred HTTP_JSON transport even though primary URL points to JSONRPC transport) + AgentCard agentCard = createTestAgentCardBuilder() + .preferredTransport(TransportProtocol.HTTP_JSON.asString()) + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"), + new AgentInterface(TransportProtocol.GRPC.asString(), "http://localhost:9000"), + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "http://localhost:8000") + )) + .build(); + + // Define available transports + Set availableTransports = Set.of(TransportProtocol.JSONRPC.asString(), TransportProtocol.GRPC.asString(), TransportProtocol.HTTP_JSON.asString()); + + // Capture logs + Logger logger = Logger.getLogger(AgentCardValidator.class.getName()); + TestLogHandler testLogHandler = new TestLogHandler(); + logger.addHandler(testLogHandler); + + try { + AgentCardValidator.validateTransportConfiguration(agentCard, availableTransports); + } finally { + logger.removeHandler(testLogHandler); + } + + // Test that the warning was logged + assertTrue(testLogHandler.getLogMessages().stream() + .anyMatch(msg -> msg.contains("AgentCard's URL=http://localhost:9999 does not correspond to the URL of the preferred transport=http://localhost:8000."))); + } + // A simple log handler for testing private static class TestLogHandler extends Handler { private final List logMessages = new java.util.ArrayList<>(); From 3f89c77445f0affb8d3f37f13c67527d9aa79d94 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 26 Sep 2025 21:41:42 +0100 Subject: [PATCH 140/493] feat: Implement a RDMS backed PushNotificationConfigStore (#301) Interaction with the RDBMS happens with JPA. Also renamed the JPA TaskStore AgentCard and AgentExecutor test producers since I am not a fan of having lots of classes with the same name --- extras/README.md | 3 +- .../README.md | 81 ++++ .../pom.xml | 107 ++++++ ...paDatabasePushNotificationConfigStore.java | 118 ++++++ .../jpa/JpaPushNotificationConfig.java | 70 ++++ .../database/jpa/TaskConfigId.java | 59 +++ .../src/main/resources/META-INF/beans.xml | 0 ...otificationConfigStoreIntegrationTest.java | 186 +++++++++ ...ationConfigStoreTestAgentCardProducer.java | 40 ++ ...ificationConfigStoreTestAgentExecutor.java | 75 ++++ .../JpaPushNotificationConfigStoreTest.java | 363 ++++++++++++++++++ .../jpa/MockPushNotificationSender.java | 36 ++ .../test/resources/META-INF/persistence.xml | 45 +++ .../src/test/resources/application.properties | 0 .../README.md | 8 +- .../pom.xml | 2 +- .../database/jpa/JpaDatabaseTaskStore.java | 0 .../taskstore/database/jpa/JpaTask.java | 0 .../src/main/resources/META-INF/beans.xml | 6 + .../JpaDatabaseTaskStoreIntegrationTest.java | 0 .../jpa/JpaDatabaseTaskStoreTest.java | 0 ...tabaseTaskStoreTestAgentCardProducer.java} | 2 +- ...paDatabaseTaskStoreTestAgentExecutor.java} | 2 +- .../test/resources/META-INF/persistence.xml | 0 .../src/test/resources/application.properties | 11 + pom.xml | 3 +- .../io/a2a/spec/PushNotificationConfig.java | 2 + 27 files changed, 1210 insertions(+), 9 deletions(-) create mode 100644 extras/push-notification-config-store-database-jpa/README.md create mode 100644 extras/push-notification-config-store-database-jpa/pom.xml create mode 100644 extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java create mode 100644 extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java create mode 100644 extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/TaskConfigId.java rename extras/{taskstore-database-jpa => push-notification-config-store-database-jpa}/src/main/resources/META-INF/beans.xml (100%) create mode 100644 extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java create mode 100644 extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java create mode 100644 extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java create mode 100644 extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java create mode 100644 extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/MockPushNotificationSender.java create mode 100644 extras/push-notification-config-store-database-jpa/src/test/resources/META-INF/persistence.xml rename extras/{taskstore-database-jpa => push-notification-config-store-database-jpa}/src/test/resources/application.properties (100%) rename extras/{taskstore-database-jpa => task-store-database-jpa}/README.md (93%) rename extras/{taskstore-database-jpa => task-store-database-jpa}/pom.xml (98%) rename extras/{taskstore-database-jpa => task-store-database-jpa}/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java (100%) rename extras/{taskstore-database-jpa => task-store-database-jpa}/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java (100%) create mode 100644 extras/task-store-database-jpa/src/main/resources/META-INF/beans.xml rename extras/{taskstore-database-jpa => task-store-database-jpa}/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java (100%) rename extras/{taskstore-database-jpa => task-store-database-jpa}/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java (100%) rename extras/{taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java => task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java} (95%) rename extras/{taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java => task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java} (97%) rename extras/{taskstore-database-jpa => task-store-database-jpa}/src/test/resources/META-INF/persistence.xml (100%) create mode 100644 extras/task-store-database-jpa/src/test/resources/application.properties diff --git a/extras/README.md b/extras/README.md index 1c54e6eb4..e8d0c859f 100644 --- a/extras/README.md +++ b/extras/README.md @@ -4,4 +4,5 @@ This directory contains additions to what is provided by the default SDK impleme Please see the README's of each child directory for more details. -[`taskstore-database-jpa`](./taskstore-database-jpa/README.md) - Replaces the default `InMemoryTaskStore` with a `TaskStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. \ No newline at end of file +[`task-store-database-jpa`](./task-store-database-jpa/README.md) - Replaces the default `InMemoryTaskStore` with a `TaskStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. +[`push-notification-config-store-database-jpa`](./push-notification-config-store-database-jpa/README.md) - Replaces the default `InMemoryPushNotificationConfigStore` with a `PushNotificationConfigStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. \ No newline at end of file diff --git a/extras/push-notification-config-store-database-jpa/README.md b/extras/push-notification-config-store-database-jpa/README.md new file mode 100644 index 000000000..3e1d49143 --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/README.md @@ -0,0 +1,81 @@ +# A2A Java SDK - JPA Database PushNotificationConfigStore + +This module provides a JPA-based implementation of the `PushNotificationConfigStore` interface that persists push notification configurations to a relational database instead of keeping them in memory. + +The persistence is done with the Jakarta Persistence API, so this should be suitable for any JPA 3.0+ provider and Jakarta EE application server. + +## Quick Start + +### 1. Add Dependency + +Add this module to your project's `pom.xml`: + +```xml + + io.github.a2asdk + a2a-java-extras-push-notification-config-store-database-jpa + ${a2a.version} + +``` + +The `JpaDatabasePushNotificationConfigStore` is annotated in such a way that it should take precedence over the default `InMemoryPushNotificationConfigStore`. Hence, it is a drop-in replacement. + +### 2. Configure Database + +The following examples assume you are using PostgreSQL as your database. To use another database, adjust as needed for your environment. + +#### For Quarkus Reference Servers + +Add to your `application.properties`: + +```properties +# Database configuration +quarkus.datasource.db-kind=postgresql +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/a2a_db +quarkus.datasource.username=your_username +quarkus.datasource.password=your_password + +# Hibernate configuration +quarkus.hibernate-orm.database.generation=update +``` + +#### For WildFly/Jakarta EE Servers + +Create or update your `persistence.xml`: + +```xml + + + + java:jboss/datasources/A2ADataSource + + io.a2a.extras.pushnotificationconfigstore.database.jpa.JpaPushNotificationConfig + true + + + + + + + + +``` + +### 3. Database Schema + +The module will automatically create the required table, which uses a composite primary key: + +```sql +CREATE TABLE a2a_push_notification_configs ( + task_id VARCHAR(255) NOT NULL, + config_id VARCHAR(255) NOT NULL, + task_data TEXT NOT NULL, + PRIMARY KEY (task_id, config_id) +); +``` + +## Configuration Options + +### Persistence Unit Name + +The module uses the persistence unit name `"a2a-java"`. Ensure your `persistence.xml` defines a persistence unit with this name. diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml new file mode 100644 index 000000000..8e5a1ee8e --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta2-SNAPSHOT + ../../pom.xml + + a2a-java-extras-push-notification-config-store-database-jpa + + jar + + Java A2A Extras: JPA Database PushNotificationConfigStore + Java SDK for the Agent2Agent Protocol (A2A) - Extras - JPA Database PushNotificationConfigStore + + + + ${project.groupId} + a2a-java-sdk-server-common + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + jakarta.persistence + jakarta.persistence-api + + + org.slf4j + slf4j-api + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-rest-client-jackson + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.mockito + mockito-core + test + + + io.rest-assured + rest-assured + test + + + jakarta.transaction + jakarta.transaction-api + + + io.quarkus + quarkus-hibernate-orm + test + + + io.quarkus + quarkus-jdbc-h2 + test + + + + ${project.groupId} + a2a-java-sdk-reference-jsonrpc + test + + + ${project.groupId} + a2a-java-sdk-client-transport-jsonrpc + test + + + ${project.groupId} + a2a-java-sdk-client + test + + + io.quarkus + quarkus-reactive-routes + test + + + diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java new file mode 100644 index 000000000..f002256cd --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -0,0 +1,118 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import java.util.List; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.spec.PushNotificationConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ApplicationScoped +@Alternative +@Priority(50) +public class JpaDatabasePushNotificationConfigStore implements PushNotificationConfigStore { + + private static final Logger LOGGER = LoggerFactory.getLogger(JpaDatabasePushNotificationConfigStore.class); + + @PersistenceContext(unitName = "a2a-java") + EntityManager em; + + @Transactional + @Override + public PushNotificationConfig setInfo(String taskId, PushNotificationConfig notificationConfig) { + // Ensure config has an ID - default to taskId if not provided (mirroring InMemoryPushNotificationConfigStore behavior) + PushNotificationConfig.Builder builder = new PushNotificationConfig.Builder(notificationConfig); + if (notificationConfig.id() == null || notificationConfig.id().isEmpty()) { + builder.id(taskId); + } + notificationConfig = builder.build(); + + LOGGER.debug("Saving PushNotificationConfig for Task '{}' with ID: {}", taskId, notificationConfig.id()); + try { + TaskConfigId configId = new TaskConfigId(taskId, notificationConfig.id()); + + // Check if entity already exists + JpaPushNotificationConfig existingJpaConfig = em.find(JpaPushNotificationConfig.class, configId); + + if (existingJpaConfig != null) { + // Update existing entity + existingJpaConfig.setConfig(notificationConfig); + LOGGER.debug("Updated existing PushNotificationConfig for Task '{}' with ID: {}", + taskId, notificationConfig.id()); + } else { + // Create new entity + JpaPushNotificationConfig jpaConfig = JpaPushNotificationConfig.createFromConfig(taskId, notificationConfig); + em.persist(jpaConfig); + LOGGER.debug("Persisted new PushNotificationConfig for Task '{}' with ID: {}", + taskId, notificationConfig.id()); + } + } catch (JsonProcessingException e) { + LOGGER.error("Failed to serialize PushNotificationConfig for Task '{}' with ID: {}", + taskId, notificationConfig.id(), e); + throw new RuntimeException("Failed to serialize PushNotificationConfig for Task '" + + taskId + "' with ID: " + notificationConfig.id(), e); + } + return notificationConfig; + } + + @Transactional + @Override + public List getInfo(String taskId) { + LOGGER.debug("Retrieving PushNotificationConfigs for Task '{}'", taskId); + try { + List jpaConfigs = em.createQuery( + "SELECT c FROM JpaPushNotificationConfig c WHERE c.id.taskId = :taskId", + JpaPushNotificationConfig.class) + .setParameter("taskId", taskId) + .getResultList(); + + List configs = jpaConfigs.stream() + .map(jpaConfig -> { + try { + return jpaConfig.getConfig(); + } catch (JsonProcessingException e) { + LOGGER.error("Failed to deserialize PushNotificationConfig for Task '{}' with ID: {}", + taskId, jpaConfig.getId().getConfigId(), e); + throw new RuntimeException("Failed to deserialize PushNotificationConfig for Task '" + + taskId + "' with ID: " + jpaConfig.getId().getConfigId(), e); + } + }) + .toList(); + + LOGGER.debug("Successfully retrieved {} PushNotificationConfigs for Task '{}'", configs.size(), taskId); + return configs; + } catch (Exception e) { + LOGGER.error("Failed to retrieve PushNotificationConfigs for Task '{}'", taskId, e); + throw e; + } + } + + @Transactional + @Override + public void deleteInfo(String taskId, String configId) { + if (configId == null) { + configId = taskId; + } + + LOGGER.debug("Deleting PushNotificationConfig for Task '{}' with Config ID: {}", taskId, configId); + JpaPushNotificationConfig jpaConfig = em.find(JpaPushNotificationConfig.class, + new TaskConfigId(taskId, configId)); + + if (jpaConfig != null) { + em.remove(jpaConfig); + LOGGER.debug("Successfully deleted PushNotificationConfig for Task '{}' with Config ID: {}", + taskId, configId); + } else { + LOGGER.debug("PushNotificationConfig not found for deletion with Task '{}' and Config ID: {}", + taskId, configId); + } + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java new file mode 100644 index 000000000..a55e252b5 --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java @@ -0,0 +1,70 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.util.Utils; + +@Entity +@Table(name = "a2a_push_notification_configs") +public class JpaPushNotificationConfig { + @EmbeddedId + private TaskConfigId id; + + @Column(name = "task_data", columnDefinition = "TEXT", nullable = false) + private String configJson; + + @Transient + private PushNotificationConfig config; + + // Default constructor required by JPA + public JpaPushNotificationConfig() { + } + + public JpaPushNotificationConfig(TaskConfigId id, String configJson) { + this.id = id; + this.configJson = configJson; + } + + + public TaskConfigId getId() { + return id; + } + + public void setId(TaskConfigId id) { + this.id = id; + } + + public void setConfigJson(String configJson) { + this.configJson = configJson; + } + + public PushNotificationConfig getConfig() throws JsonProcessingException { + if (config == null) { + this.config = Utils.unmarshalFrom(configJson, PushNotificationConfig.TYPE_REFERENCE); + } + return config; + } + + public void setConfig(PushNotificationConfig config) throws JsonProcessingException { + if (config.id() == null || !config.id().equals(id.getConfigId())) { + throw new IllegalArgumentException("Mismatched config id. " + + "Expected '" + id.getConfigId() + "'. Got: '" + config.id() + "'"); + } + configJson = Utils.OBJECT_MAPPER.writeValueAsString(config); + this.config = config; + } + + static JpaPushNotificationConfig createFromConfig(String taskId, PushNotificationConfig config) throws JsonProcessingException { + String json = Utils.OBJECT_MAPPER.writeValueAsString(config); + JpaPushNotificationConfig jpaPushNotificationConfig = + new JpaPushNotificationConfig(new TaskConfigId(taskId, config.id()), json); + jpaPushNotificationConfig.config = config; + return jpaPushNotificationConfig; + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/TaskConfigId.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/TaskConfigId.java new file mode 100644 index 000000000..9b66a8ea4 --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/TaskConfigId.java @@ -0,0 +1,59 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import java.io.Serializable; +import java.util.Objects; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; + +@Embeddable +public class TaskConfigId implements Serializable { + + @Column(name = "task_id") + private String taskId; + + @Column(name = "config_id") + private String configId; + + // No-argument constructor (required by JPA) + public TaskConfigId() { + } + + public TaskConfigId(String taskId, String configId) { + this.taskId = taskId; + this.configId = configId; + } + + // Getters and setters... + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getConfigId() { + return configId; + } + + public void setConfigId(String configId) { + this.configId = configId; + } + + // hashCode() and equals() implementations + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TaskConfigId that = (TaskConfigId) o; + return Objects.equals(taskId, that.taskId) && + Objects.equals(configId, that.configId); + } + + @Override + public int hashCode() { + return Objects.hash(taskId, configId); + } +} diff --git a/extras/taskstore-database-jpa/src/main/resources/META-INF/beans.xml b/extras/push-notification-config-store-database-jpa/src/main/resources/META-INF/beans.xml similarity index 100% rename from extras/taskstore-database-jpa/src/main/resources/META-INF/beans.xml rename to extras/push-notification-config-store-database-jpa/src/main/resources/META-INF/beans.xml diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java new file mode 100644 index 000000000..c8f577c9a --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -0,0 +1,186 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.inject.Inject; + +import java.util.List; +import java.util.Queue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import io.a2a.client.Client; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.server.PublicAgentCard; +import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.Message; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.spec.TextPart; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * End-to-end integration test that verifies the JPA PushNotificationConfigStore works correctly + * with the full client-server flow using the Client API. + */ +@QuarkusTest +public class JpaDatabasePushNotificationConfigStoreIntegrationTest { + + @Inject + PushNotificationConfigStore pushNotificationConfigStore; + + @Inject + @PublicAgentCard + AgentCard agentCard; + + @Inject + MockPushNotificationSender mockPushNotificationSender; + + private Client client; + + @BeforeEach + public void setup() throws A2AClientException { + // Clear any previous notifications + mockPushNotificationSender.clear(); + + // Create client configuration - enable streaming for automatic push notifications + ClientConfig clientConfig = new ClientConfig.Builder() + .setStreaming(true) + .build(); + + // Build client with JSON-RPC transport + client = Client.builder(agentCard) + .clientConfig(clientConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + } + + @Test + public void testIsJpaDatabasePushNotificationConfigStore() { + assertInstanceOf(JpaDatabasePushNotificationConfigStore.class, pushNotificationConfigStore); + } + + @Test + public void testDirectNotificationTrigger() { + // Simple test to verify the mock notification mechanism works + mockPushNotificationSender.clear(); + + Task testTask = new io.a2a.spec.Task.Builder() + .id("direct-test-task") + .contextId("test-context") + .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.SUBMITTED)) + .build(); + + // Directly trigger the mock + mockPushNotificationSender.sendNotification(testTask); + + // Verify it was captured + Queue captured = mockPushNotificationSender.getCapturedTasks(); + assertEquals(1, captured.size()); + assertEquals("direct-test-task", captured.peek().getId()); + } + + @Test + public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Exception { + final String taskId = "push-notify-test-" + System.currentTimeMillis(); + final String contextId = "test-context"; + + // Step 1: Create the task + Message createMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("create"))) // Send the "create" command + .taskId(taskId) + .messageId("test-msg-1") + .contextId(contextId) + .build(); + + // Use a latch to wait for the first operation to complete + CountDownLatch createLatch = new CountDownLatch(1); + client.sendMessage(createMessage, List.of((event, card) -> createLatch.countDown()), (e) -> createLatch.countDown()); + assertTrue(createLatch.await(10, TimeUnit.SECONDS), "Timeout waiting for task creation"); + + // Step 2: Set the push notification configuration + PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() + .url("http://localhost:9999/mock-endpoint") + .token("test-token-123") + .id("test-config-1") + .build(); + + TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig(taskId, pushConfig); + TaskPushNotificationConfig setResult = client.setTaskPushNotificationConfiguration(taskPushConfig); + assertNotNull(setResult); + + // Step 3: Verify the configuration was stored using client API + TaskPushNotificationConfig storedConfig = client.getTaskPushNotificationConfiguration( + new GetTaskPushNotificationConfigParams(taskId)); + + assertNotNull(storedConfig); + assertEquals(taskId, storedConfig.taskId()); + assertEquals("test-config-1", storedConfig.pushNotificationConfig().id()); + assertEquals("http://localhost:9999/mock-endpoint", storedConfig.pushNotificationConfig().url()); + assertEquals("test-token-123", storedConfig.pushNotificationConfig().token()); + + // Step 4: Update the task to trigger the notification + Message updateMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("update"))) // Send the "update" command + .taskId(taskId) + .messageId("test-msg-2") + .contextId(contextId) + .build(); + + CountDownLatch updateLatch = new CountDownLatch(1); + client.sendMessage(updateMessage, List.of((event, card) -> updateLatch.countDown()), (e) -> updateLatch.countDown()); + assertTrue(updateLatch.await(10, TimeUnit.SECONDS), "Timeout waiting for task update"); + + // Step 5: Poll for the async notification to be captured + long end = System.currentTimeMillis() + 5000; + boolean notificationReceived = false; + + while (System.currentTimeMillis() < end) { + if (!mockPushNotificationSender.getCapturedTasks().isEmpty()) { + notificationReceived = true; + break; + } + Thread.sleep(100); + } + + assertTrue(notificationReceived, "Timeout waiting for push notification."); + + // Step 6: Verify the captured notification + Queue capturedTasks = mockPushNotificationSender.getCapturedTasks(); + + // Verify the notification contains the correct task with artifacts + Task notifiedTaskWithArtifact = capturedTasks.stream() + .filter(t -> taskId.equals(t.getId()) && t.getArtifacts() != null && t.getArtifacts().size() > 0) + .findFirst() + .orElse(null); + + assertNotNull(notifiedTaskWithArtifact, "Notification should contain the updated task with artifacts"); + assertEquals(taskId, notifiedTaskWithArtifact.getId()); + assertEquals(1, notifiedTaskWithArtifact.getArtifacts().size(), "Task should have one artifact from the update"); + + // Step 7: Clean up - delete the push notification configuration + client.deleteTaskPushNotificationConfigurations( + new DeleteTaskPushNotificationConfigParams(taskId, "test-config-1")); + + // Verify deletion by asserting that getting the config now throws an exception + assertThrows(A2AClientException.class, () -> { + client.getTaskPushNotificationConfiguration(new GetTaskPushNotificationConfigParams(taskId)); + }, "Getting a deleted config should throw an A2AClientException"); + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java new file mode 100644 index 000000000..a795bfcd6 --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java @@ -0,0 +1,40 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.TransportProtocol; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Simple test AgentCard producer for our integration test. + * It declares that the agent supports push notifications. + */ +@ApplicationScoped +@IfBuildProfile("test") +public class JpaDatabasePushNotificationConfigStoreTestAgentCardProducer { + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + return new AgentCard.Builder() + .name("JPA PushNotificationConfigStore Integration Test Agent") + .description("Test agent for verifying JPA PushNotificationConfigStore integration") + .version("1.0.0") + .url("http://localhost:8081") // Port is managed by QuarkusTest + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .capabilities(new AgentCapabilities.Builder() + .pushNotifications(true) // Enable push notifications + .streaming(true) // Enable streaming for automatic push notifications + .build()) + .skills(List.of()) + .build(); + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java new file mode 100644 index 000000000..09211b4fc --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java @@ -0,0 +1,75 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.server.tasks.PushNotificationSender; +import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Simple test AgentExecutor that updates the task, which in turn + * will trigger the PushNotificationSender. + */ +@IfBuildProfile("test") +@ApplicationScoped +public class JpaDatabasePushNotificationConfigStoreTestAgentExecutor { + + @Inject + PushNotificationSender pushNotificationSender; + + @Produces + public AgentExecutor agentExecutor() { + return new AgentExecutor() { + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + String command = getLastTextPart(context.getMessage()); + + // Switch based on the command from the test client + switch (command) { + case "create": + taskUpdater.submit(); + break; + case "update": + // Perform a meaningful update, like adding an artifact. + // This state change is what will trigger the notification. + taskUpdater.addArtifact(List.of(new TextPart("updated-artifact")), "art-1", "test", null); + break; + default: + // On the first message (which might have no text), just submit. + taskUpdater.submit(); + break; + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + taskUpdater.cancel(); + } + }; + } + + private String getLastTextPart(Message message) throws JSONRPCError { + if (message.getParts() == null || message.getParts().isEmpty()) { + return ""; + } + Part part = message.getParts().get(message.getParts().size() - 1); + if (part.getKind() == Part.Kind.TEXT) { + return ((TextPart) part).getText(); + } + throw new InvalidRequestError("Last part is not text"); + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java new file mode 100644 index 000000000..70f9d1e5f --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java @@ -0,0 +1,363 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.mockito.ArgumentCaptor; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpResponse; +import io.a2a.server.tasks.BasePushNotificationSender; +import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class JpaPushNotificationConfigStoreTest { + + @Inject + PushNotificationConfigStore configStore; + + private BasePushNotificationSender notificationSender; + + @Mock + private A2AHttpClient mockHttpClient; + + @Mock + private A2AHttpClient.PostBuilder mockPostBuilder; + + @Mock + private A2AHttpResponse mockHttpResponse; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + notificationSender = new BasePushNotificationSender(configStore, mockHttpClient); + } + + @Test + public void testIsJpaDatabasePushNotificationConfigStore() { + assertInstanceOf(JpaDatabasePushNotificationConfigStore.class, configStore); + } + + private Task createSampleTask(String taskId, TaskState state) { + return new Task.Builder() + .id(taskId) + .contextId("ctx456") + .status(new TaskStatus(state)) + .build(); + } + + private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { + return new PushNotificationConfig.Builder() + .url(url) + .id(configId) + .token(token) + .build(); + } + + @Test + @Transactional + public void testSetInfoAddsNewConfig() { + String taskId = "task_new"; + PushNotificationConfig config = createSamplePushConfig("http://new.url/callback", "cfg1", null); + + PushNotificationConfig result = configStore.setInfo(taskId, config); + + assertNotNull(result); + assertEquals(config.url(), result.url()); + assertEquals(config.id(), result.id()); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(1, configs.size()); + assertEquals(config.url(), configs.get(0).url()); + assertEquals(config.id(), configs.get(0).id()); + } + + @Test + @Transactional + public void testSetInfoAppendsToExistingConfig() { + String taskId = "task_update"; + PushNotificationConfig initialConfig = createSamplePushConfig( + "http://initial.url/callback", "cfg_initial", null); + configStore.setInfo(taskId, initialConfig); + + PushNotificationConfig updatedConfig = createSamplePushConfig( + "http://updated.url/callback", "cfg_updated", null); + configStore.setInfo(taskId, updatedConfig); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(2, configs.size()); + + // Find the configs by ID since order might vary + PushNotificationConfig foundInitial = configs.stream() + .filter(c -> "cfg_initial".equals(c.id())) + .findFirst() + .orElse(null); + PushNotificationConfig foundUpdated = configs.stream() + .filter(c -> "cfg_updated".equals(c.id())) + .findFirst() + .orElse(null); + + assertNotNull(foundInitial); + assertNotNull(foundUpdated); + assertEquals(initialConfig.url(), foundInitial.url()); + assertEquals(updatedConfig.url(), foundUpdated.url()); + } + + @Test + @Transactional + public void testSetInfoWithoutConfigId() { + String taskId = "task1"; + PushNotificationConfig initialConfig = new PushNotificationConfig.Builder() + .url("http://initial.url/callback") + .build(); // No ID set + + PushNotificationConfig result = configStore.setInfo(taskId, initialConfig); + assertEquals(taskId, result.id(), "Config ID should default to taskId when not provided"); + + List configs = configStore.getInfo(taskId); + assertEquals(1, configs.size()); + assertEquals(taskId, configs.get(0).id()); + + PushNotificationConfig updatedConfig = new PushNotificationConfig.Builder() + .url("http://initial.url/callback_new") + .build(); // No ID set + + PushNotificationConfig updatedResult = configStore.setInfo(taskId, updatedConfig); + assertEquals(taskId, updatedResult.id()); + + configs = configStore.getInfo(taskId); + assertEquals(1, configs.size(), "Should replace existing config with same ID rather than adding new one"); + assertEquals(updatedConfig.url(), configs.get(0).url()); + } + + @Test + @Transactional + public void testGetInfoExistingConfig() { + String taskId = "task_get_exist"; + PushNotificationConfig config = createSamplePushConfig("http://get.this/callback", "cfg1", null); + configStore.setInfo(taskId, config); + + List retrievedConfigs = configStore.getInfo(taskId); + assertNotNull(retrievedConfigs); + assertEquals(1, retrievedConfigs.size()); + assertEquals(config.url(), retrievedConfigs.get(0).url()); + assertEquals(config.id(), retrievedConfigs.get(0).id()); + } + + @Test + @Transactional + public void testGetInfoNonExistentConfig() { + String taskId = "task_get_non_exist"; + List retrievedConfigs = configStore.getInfo(taskId); + assertNotNull(retrievedConfigs); + assertTrue(retrievedConfigs.isEmpty(), "Should return empty list for non-existent task ID"); + } + + @Test + @Transactional + public void testDeleteInfoExistingConfig() { + String taskId = "task_delete_exist"; + PushNotificationConfig config = createSamplePushConfig("http://delete.this/callback", "cfg1", null); + configStore.setInfo(taskId, config); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(1, configs.size()); + + configStore.deleteInfo(taskId, config.id()); + + List configsAfterDelete = configStore.getInfo(taskId); + assertNotNull(configsAfterDelete); + assertTrue(configsAfterDelete.isEmpty(), "Should return empty list when no configs remain after deletion"); + } + + @Test + @Transactional + public void testDeleteInfoNonExistentConfig() { + String taskId = "task_delete_non_exist"; + // Should not throw an error + configStore.deleteInfo(taskId, "non_existent_id"); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertTrue(configs.isEmpty(), "Should return empty list for non-existent task ID"); + } + + @Test + @Transactional + public void testDeleteInfoWithNullConfigId() { + String taskId = "task_delete_null_config"; + PushNotificationConfig config = new PushNotificationConfig.Builder() + .url("http://delete.this/callback") + .build(); // No ID set, will use taskId + configStore.setInfo(taskId, config); + + // Delete with null configId should use taskId + configStore.deleteInfo(taskId, null); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertTrue(configs.isEmpty(), "Should return empty list after deletion when using taskId as configId"); + } + + @Test + @Transactional + public void testSendNotificationSuccess() throws Exception { + String taskId = "task_send_success"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", null); + configStore.setInfo(taskId, config); + + // Mock successful HTTP response + when(mockHttpClient.createPost()).thenReturn(mockPostBuilder); + when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.post()).thenReturn(mockHttpResponse); + when(mockHttpResponse.success()).thenReturn(true); + + notificationSender.sendNotification(task); + + // Verify HTTP client was called + ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + verify(mockHttpClient).createPost(); + verify(mockPostBuilder).url(config.url()); + verify(mockPostBuilder).body(bodyCaptor.capture()); + verify(mockPostBuilder).post(); + + // Verify the request body contains the task data + String sentBody = bodyCaptor.getValue(); + assertTrue(sentBody.contains(task.getId())); + assertTrue(sentBody.contains(task.getStatus().state().asString())); + } + + @Test + @Transactional + @Disabled("Token authentication is not yet implemented in BasePushNotificationSender (TODO auth)") + public void testSendNotificationWithToken() throws Exception { + String taskId = "task_send_with_token"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", "unique_token"); + configStore.setInfo(taskId, config); + + // Mock successful HTTP response + when(mockHttpClient.createPost()).thenReturn(mockPostBuilder); + when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.post()).thenReturn(mockHttpResponse); + when(mockHttpResponse.success()).thenReturn(true); + + notificationSender.sendNotification(task); + + // TODO: Once token authentication is implemented, verify that: + // 1. The token is included in request headers (e.g., X-A2A-Notification-Token) + // 2. The HTTP client is called with proper authentication + // 3. The token from the config is actually used + + // For now, just verify basic HTTP client interaction + ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + verify(mockHttpClient).createPost(); + verify(mockPostBuilder).url(config.url()); + verify(mockPostBuilder).body(bodyCaptor.capture()); + verify(mockPostBuilder).post(); + + // Verify the request body contains the task data + String sentBody = bodyCaptor.getValue(); + assertTrue(sentBody.contains(task.getId())); + assertTrue(sentBody.contains(task.getStatus().state().asString())); + } + + @Test + @Transactional + public void testSendNotificationNoConfig() throws Exception { + String taskId = "task_send_no_config"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + + notificationSender.sendNotification(task); + + // Verify HTTP client was never called + verify(mockHttpClient, never()).createPost(); + } + + @Test + @Transactional + public void testMultipleConfigsForSameTask() { + String taskId = "task_multiple"; + PushNotificationConfig config1 = createSamplePushConfig("http://url1.com/callback", "cfg1", null); + PushNotificationConfig config2 = createSamplePushConfig("http://url2.com/callback", "cfg2", null); + + configStore.setInfo(taskId, config1); + configStore.setInfo(taskId, config2); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(2, configs.size()); + + // Verify both configs are present + assertTrue(configs.stream().anyMatch(c -> "cfg1".equals(c.id()))); + assertTrue(configs.stream().anyMatch(c -> "cfg2".equals(c.id()))); + } + + @Test + @Transactional + public void testDeleteSpecificConfigFromMultiple() { + String taskId = "task_delete_specific"; + PushNotificationConfig config1 = createSamplePushConfig("http://url1.com/callback", "cfg1", null); + PushNotificationConfig config2 = createSamplePushConfig("http://url2.com/callback", "cfg2", null); + + configStore.setInfo(taskId, config1); + configStore.setInfo(taskId, config2); + + // Delete only config1 + configStore.deleteInfo(taskId, "cfg1"); + + List configs = configStore.getInfo(taskId); + assertNotNull(configs); + assertEquals(1, configs.size()); + assertEquals("cfg2", configs.get(0).id()); + } + + @Test + @Transactional + public void testConfigStoreIntegration() { + String taskId = "integration_test"; + PushNotificationConfig config = createSamplePushConfig("http://example.com", "test_id", "test_token"); + + // Test that we can store and retrieve configurations + PushNotificationConfig storedConfig = configStore.setInfo(taskId, config); + assertEquals(config.url(), storedConfig.url()); + assertEquals(config.token(), storedConfig.token()); + + List retrievedConfigs = configStore.getInfo(taskId); + assertEquals(1, retrievedConfigs.size()); + assertEquals(config.url(), retrievedConfigs.get(0).url()); + + // Test deletion + configStore.deleteInfo(taskId, storedConfig.id()); + List afterDeletion = configStore.getInfo(taskId); + assertTrue(afterDeletion.isEmpty()); + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/MockPushNotificationSender.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/MockPushNotificationSender.java new file mode 100644 index 000000000..0a6bba415 --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/MockPushNotificationSender.java @@ -0,0 +1,36 @@ +package io.a2a.extras.pushnotificationconfigstore.database.jpa; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; + +import io.a2a.server.tasks.PushNotificationSender; +import io.a2a.spec.Task; + +/** + * Mock implementation of PushNotificationSender for integration testing. + * Captures notifications in a thread-safe queue for test verification. + */ +@ApplicationScoped +@Alternative +@Priority(100) +public class MockPushNotificationSender implements PushNotificationSender { + + private final Queue capturedTasks = new ConcurrentLinkedQueue<>(); + + @Override + public void sendNotification(Task task) { + capturedTasks.add(task); + } + + public Queue getCapturedTasks() { + return capturedTasks; + } + + public void clear() { + capturedTasks.clear(); + } +} diff --git a/extras/push-notification-config-store-database-jpa/src/test/resources/META-INF/persistence.xml b/extras/push-notification-config-store-database-jpa/src/test/resources/META-INF/persistence.xml new file mode 100644 index 000000000..4ae3b38d0 --- /dev/null +++ b/extras/push-notification-config-store-database-jpa/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,45 @@ + + + + + + A2A Java SDK JPA TaskStore Test Configuration + + + io.a2a.extras.pushnotificationconfigstore.database.jpa.JpaPushNotificationConfig + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extras/taskstore-database-jpa/src/test/resources/application.properties b/extras/push-notification-config-store-database-jpa/src/test/resources/application.properties similarity index 100% rename from extras/taskstore-database-jpa/src/test/resources/application.properties rename to extras/push-notification-config-store-database-jpa/src/test/resources/application.properties diff --git a/extras/taskstore-database-jpa/README.md b/extras/task-store-database-jpa/README.md similarity index 93% rename from extras/taskstore-database-jpa/README.md rename to extras/task-store-database-jpa/README.md index a1312aad2..eb89611bd 100644 --- a/extras/taskstore-database-jpa/README.md +++ b/extras/task-store-database-jpa/README.md @@ -13,7 +13,7 @@ Add this module to your project's `pom.xml`: ```xml io.github.a2asdk - a2a-java-extras-taskstore-database-jpa + a2a-java-extras-task-store-database-jpa ${a2a.version} ``` @@ -31,7 +31,7 @@ Add to your `application.properties`: ```properties # Database configuration quarkus.datasource.db-kind=postgresql -quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/a2a_tasks +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/a2a_db quarkus.datasource.username=your_username quarkus.datasource.password=your_password @@ -47,7 +47,7 @@ Create or update your `persistence.xml`: - java:jboss/datasources/A2ATasksDS + java:jboss/datasources/A2ADataSource io.a2a.extras.taskstore.database.jpa.JpaTask true @@ -76,4 +76,4 @@ CREATE TABLE a2a_tasks ( ### Persistence Unit Name -The module uses the persistence unit name `"a2a-java"`. Ensure your `persistence.xml` defines a persistence unit with this name. +The module uses the persistence unit name `"a2a-java"`. Ensure your `persistence.xml` defines a persistence unit with this name. \ No newline at end of file diff --git a/extras/taskstore-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml similarity index 98% rename from extras/taskstore-database-jpa/pom.xml rename to extras/task-store-database-jpa/pom.xml index 872cbb874..8745db013 100644 --- a/extras/taskstore-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -10,7 +10,7 @@ 0.3.0.Beta2-SNAPSHOT ../../pom.xml - a2a-java-extras-taskstore-database-jpa + a2a-java-extras-task-store-database-jpa jar diff --git a/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java similarity index 100% rename from extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java rename to extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java diff --git a/extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java similarity index 100% rename from extras/taskstore-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java rename to extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java diff --git a/extras/task-store-database-jpa/src/main/resources/META-INF/beans.xml b/extras/task-store-database-jpa/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..ffc8abb42 --- /dev/null +++ b/extras/task-store-database-jpa/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java similarity index 100% rename from extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java rename to extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java similarity index 100% rename from extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java rename to extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java similarity index 95% rename from extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java rename to extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java index f78d0cf8a..291519131 100644 --- a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentCardProducer.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java @@ -16,7 +16,7 @@ */ @ApplicationScoped @IfBuildProfile("test") -public class TestAgentCardProducer { +public class JpaDatabaseTaskStoreTestAgentCardProducer { @Produces @PublicAgentCard diff --git a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java similarity index 97% rename from extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java rename to extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java index 9b93259bb..8e4a1c8eb 100644 --- a/extras/taskstore-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/TestAgentExecutor.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java @@ -22,7 +22,7 @@ */ @IfBuildProfile("test") @ApplicationScoped -public class TestAgentExecutor { +public class JpaDatabaseTaskStoreTestAgentExecutor { @Produces public AgentExecutor agentExecutor() { diff --git a/extras/taskstore-database-jpa/src/test/resources/META-INF/persistence.xml b/extras/task-store-database-jpa/src/test/resources/META-INF/persistence.xml similarity index 100% rename from extras/taskstore-database-jpa/src/test/resources/META-INF/persistence.xml rename to extras/task-store-database-jpa/src/test/resources/META-INF/persistence.xml diff --git a/extras/task-store-database-jpa/src/test/resources/application.properties b/extras/task-store-database-jpa/src/test/resources/application.properties new file mode 100644 index 000000000..1d1592ad9 --- /dev/null +++ b/extras/task-store-database-jpa/src/test/resources/application.properties @@ -0,0 +1,11 @@ +# Test configuration for JPA TaskStore +# H2 in-memory database for testing +quarkus.datasource.db-kind=h2 +quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +quarkus.datasource.username=sa +quarkus.datasource.password= + +# Hibernate configuration +quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-orm.log.sql=true +quarkus.hibernate-orm.log.format-sql=true diff --git a/pom.xml b/pom.xml index af0651689..33183fdf1 100644 --- a/pom.xml +++ b/pom.xml @@ -390,7 +390,8 @@ client/transport/spi common examples/helloworld - extras/taskstore-database-jpa + extras/task-store-database-jpa + extras/push-notification-config-store-database-jpa http-client reference/common reference/grpc diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index 7d577ffdb..fcffa2c7f 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.util.Assert; /** @@ -10,6 +11,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record PushNotificationConfig(String url, String token, PushNotificationAuthenticationInfo authentication, String id) { + public static final TypeReference TYPE_REFERENCE = new TypeReference<>() {}; public PushNotificationConfig { Assert.checkNotNullParam("url", url); From e20a3120fbe3b97ceb9b0c9f4bed1ce23ad87c0c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 29 Sep 2025 12:38:29 -0400 Subject: [PATCH 141/493] feat: Add support for notification tokens in BasePushNotificationSender (#308) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #307 🦕 --- .../main/java/io/a2a/common/A2AHeaders.java | 5 ++ .../tasks/BasePushNotificationSender.java | 11 ++-- ...MemoryPushNotificationConfigStoreTest.java | 60 ++++++++++++++++--- .../tasks/PushNotificationSenderTest.java | 45 +++++++++++++- 4 files changed, 107 insertions(+), 14 deletions(-) diff --git a/common/src/main/java/io/a2a/common/A2AHeaders.java b/common/src/main/java/io/a2a/common/A2AHeaders.java index 5118a4365..40bc2346c 100644 --- a/common/src/main/java/io/a2a/common/A2AHeaders.java +++ b/common/src/main/java/io/a2a/common/A2AHeaders.java @@ -10,6 +10,11 @@ public final class A2AHeaders { * Used to communicate which extensions are requested by the client. */ public static final String X_A2A_EXTENSIONS = "X-A2A-Extensions"; + + /** + * HTTP header name for a push notification token. + */ + public static final String X_A2A_NOTIFICATION_TOKEN = "X-A2A-Notification-Token"; private A2AHeaders() { // Utility class diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index 2601e5bbf..4afaf3b4e 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -1,5 +1,6 @@ package io.a2a.server.tasks; +import static io.a2a.common.A2AHeaders.X_A2A_NOTIFICATION_TOKEN; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -68,10 +69,12 @@ private CompletableFuture dispatch(Task task, PushNotificationConfig pu private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo) { String url = pushInfo.url(); + String token = pushInfo.token(); - // TODO: Implement authentication and token header support - // The Python implementation adds X-A2A-Notification-Token header when pushInfo.token is present - // See: https://github.com/a2aproject/a2a-python/blob/main/src/a2a/server/tasks/base_push_notification_sender.py#L55-57 + A2AHttpClient.PostBuilder postBuilder = httpClient.createPost(); + if (token != null && !token.isBlank()) { + postBuilder.addHeader(X_A2A_NOTIFICATION_TOKEN, token); + } String body; try { @@ -85,7 +88,7 @@ private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo) } try { - httpClient.createPost() + postBuilder .url(url) .body(body) .post(); diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java index 4caf0c7d7..9156f78b2 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java @@ -14,13 +14,13 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; +import io.a2a.common.A2AHeaders; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskState; @@ -47,6 +47,29 @@ public void setUp() { notificationSender = new BasePushNotificationSender(configStore, mockHttpClient); } + private void setupBasicMockHttpResponse() throws Exception { + when(mockHttpClient.createPost()).thenReturn(mockPostBuilder); + when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.post()).thenReturn(mockHttpResponse); + when(mockHttpResponse.success()).thenReturn(true); + } + + private void verifyHttpCallWithoutToken(PushNotificationConfig config, Task task, String expectedToken) throws Exception { + ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); + verify(mockHttpClient).createPost(); + verify(mockPostBuilder).url(config.url()); + verify(mockPostBuilder).body(bodyCaptor.capture()); + verify(mockPostBuilder).post(); + // Verify that addHeader was never called for authentication token + verify(mockPostBuilder, never()).addHeader(A2AHeaders.X_A2A_NOTIFICATION_TOKEN, expectedToken); + + // Verify the request body contains the task data + String sentBody = bodyCaptor.getValue(); + assertTrue(sentBody.contains(task.getId())); + assertTrue(sentBody.contains(task.getStatus().state().asString())); + } + private Task createSampleTask(String taskId, TaskState state) { return new Task.Builder() .id(taskId) @@ -229,7 +252,6 @@ public void testSendNotificationSuccess() throws Exception { } @Test - @Disabled("Token authentication is not yet implemented in BasePushNotificationSender (TODO auth)") public void testSendNotificationWithToken() throws Exception { String taskId = "task_send_with_token"; Task task = createSampleTask(taskId, TaskState.COMPLETED); @@ -240,21 +262,19 @@ public void testSendNotificationWithToken() throws Exception { when(mockHttpClient.createPost()).thenReturn(mockPostBuilder); when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder); when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder); + when(mockPostBuilder.addHeader(any(String.class), any(String.class))).thenReturn(mockPostBuilder); when(mockPostBuilder.post()).thenReturn(mockHttpResponse); when(mockHttpResponse.success()).thenReturn(true); notificationSender.sendNotification(task); - // TODO: Once token authentication is implemented, verify that: - // 1. The token is included in request headers (e.g., X-A2A-Notification-Token) - // 2. The HTTP client is called with proper authentication - // 3. The token from the config is actually used - - // For now, just verify basic HTTP client interaction + // Verify HTTP client was called with proper authentication ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class); verify(mockHttpClient).createPost(); verify(mockPostBuilder).url(config.url()); verify(mockPostBuilder).body(bodyCaptor.capture()); + // Verify that the token is included in request headers as X-A2A-Notification-Token + verify(mockPostBuilder).addHeader(A2AHeaders.X_A2A_NOTIFICATION_TOKEN, config.token()); verify(mockPostBuilder).post(); // Verify the request body contains the task data @@ -274,6 +294,30 @@ public void testSendNotificationNoConfig() throws Exception { verify(mockHttpClient, never()).createPost(); } + @Test + public void testSendNotificationWithEmptyToken() throws Exception { + String taskId = "task_send_empty_token"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", ""); + configStore.setInfo(taskId, config); + + setupBasicMockHttpResponse(); + notificationSender.sendNotification(task); + verifyHttpCallWithoutToken(config, task, ""); + } + + @Test + public void testSendNotificationWithBlankToken() throws Exception { + String taskId = "task_send_blank_token"; + Task task = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", " "); + configStore.setInfo(taskId, config); + + setupBasicMockHttpResponse(); + notificationSender.sendNotification(task); + verifyHttpCallWithoutToken(config, task, " "); + } + @Test public void testMultipleConfigsForSameTask() { String taskId = "task_multiple"; diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java index 0b472591e..2ab974edb 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java @@ -18,6 +18,7 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; +import io.a2a.common.A2AHeaders; import io.a2a.util.Utils; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; @@ -133,6 +134,33 @@ public void setUp() { sender = new BasePushNotificationSender(configStore, testHttpClient); } + private void testSendNotificationWithInvalidToken(String token, String testName) throws InterruptedException { + String taskId = testName; + Task taskData = createSampleTask(taskId, TaskState.COMPLETED); + PushNotificationConfig config = createSamplePushConfig("http://notify.me/here", "cfg1", token); + + // Set up the configuration in the store + configStore.setInfo(taskId, config); + + // Set up latch to wait for async completion + testHttpClient.latch = new CountDownLatch(1); + + sender.sendNotification(taskData); + + // Wait for the async operation to complete + assertTrue(testHttpClient.latch.await(5, TimeUnit.SECONDS), "HTTP call should complete within 5 seconds"); + + // Verify the task was sent via HTTP + assertEquals(1, testHttpClient.tasks.size()); + Task sentTask = testHttpClient.tasks.get(0); + assertEquals(taskData.getId(), sentTask.getId()); + + // Verify that no authentication header was sent (invalid token should not add header) + assertEquals(1, testHttpClient.headers.size()); + Map sentHeaders = testHttpClient.headers.get(0); + assertTrue(sentHeaders.isEmpty(), "No headers should be sent when token is invalid"); + } + private Task createSampleTask(String taskId, TaskState state) { return new Task.Builder() .id(taskId) @@ -196,8 +224,11 @@ public void testSendNotificationWithTokenSuccess() throws InterruptedException { Task sentTask = testHttpClient.tasks.get(0); assertEquals(taskData.getId(), sentTask.getId()); - // TODO: When authentication is implemented in BasePushNotificationSender, verify that the - // X-A2A-Notification-Token header is sent. + // Verify that the X-A2A-Notification-Token header is sent with the correct token + assertEquals(1, testHttpClient.headers.size()); + Map sentHeaders = testHttpClient.headers.get(0); + assertTrue(sentHeaders.containsKey(A2AHeaders.X_A2A_NOTIFICATION_TOKEN)); + assertEquals(config.token(), sentHeaders.get(A2AHeaders.X_A2A_NOTIFICATION_TOKEN)); } @Test @@ -212,6 +243,16 @@ public void testSendNotificationNoConfig() { assertEquals(0, testHttpClient.tasks.size()); } + @Test + public void testSendNotificationWithEmptyToken() throws InterruptedException { + testSendNotificationWithInvalidToken("", "task_send_empty_token"); + } + + @Test + public void testSendNotificationWithBlankToken() throws InterruptedException { + testSendNotificationWithInvalidToken(" ", "task_send_blank_token"); + } + @Test public void testSendNotificationMultipleConfigs() throws InterruptedException { String taskId = "task_multiple_configs"; From 77dbaf8de128af819a05b3ca158d94b1d051b009 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 30 Sep 2025 11:05:37 +0100 Subject: [PATCH 142/493] chore: Add .claude to .gitignore (#311) So users can experiment with their own CLAUDE.md files, until we have an official one (if ever) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 04650afea..742dfd180 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ nb-configuration.xml # TLS Certificates .certs/ nbproject/ + +# Private Claude config +.claude/ From 018990c6ec08b952a865fa9cc494f9f4be90e995 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 30 Sep 2025 14:27:38 +0100 Subject: [PATCH 143/493] chore: Upgrade to Quarkus 3.28.1 (#316) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33183fdf1..46938fc05 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 1.7.1 4.31.1 0.6.1 - 3.24.5 + 3.28.1 5.5.1 2.0.17 1.5.18 From 014a899b0b47f0f817174b43e039b206ba76d262 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 30 Sep 2025 14:28:26 +0100 Subject: [PATCH 144/493] fix: Disable JPA PushNotificationConfigStore on Java 24 (#312) The currently released versions of Quarkus use Vert.X 4.5.x, which is not compatible with Java 24. Java 24 support will come in Vert.X 4.6.0. --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../pom.xml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 8e5a1ee8e..40fbec72c 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -104,4 +104,31 @@ test + + + + java24-exclude-vertx-incompatible-tests + + [24,) + + + + + maven-surefire-plugin + + --add-opens java.base/java.lang=ALL-UNNAMED + + + **/JpaDatabasePushNotificationConfigStoreIntegrationTest.java + + + + + + + + + From 1828b4d955dbd5b8ea5da9f6dde88f675c91c2a0 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 1 Oct 2025 10:55:22 +0100 Subject: [PATCH 145/493] feat: Replicated Queue Manager based on Mp Reactive Messaging with Kafka (#309) It is implemented in such a way that the replication mechanism can be swapped with something else --- extras/README.md | 3 +- extras/queue-manager-replicated/README.md | 231 +++++++++++++ extras/queue-manager-replicated/core/pom.xml | 43 +++ .../replicated/core/ReplicatedEvent.java | 114 +++++++ .../core/ReplicatedQueueManager.java | 122 +++++++ .../replicated/core/ReplicationStrategy.java | 7 + .../src/main/resources/META-INF/beans.xml | 5 + .../core/EventSerializationTest.java | 291 ++++++++++++++++ .../core/ReplicatedQueueManagerTest.java | 312 ++++++++++++++++++ extras/queue-manager-replicated/pom.xml | 24 ++ .../replication-mp-reactive/pom.xml | 54 +++ .../ReactiveMessagingReplicationStrategy.java | 63 ++++ .../src/main/resources/META-INF/beans.xml | 5 + ...ctiveMessagingReplicationStrategyTest.java | 110 ++++++ extras/queue-manager-replicated/tests/pom.xml | 89 +++++ .../KafkaReplicationIntegrationTest.java | 261 +++++++++++++++ .../tests/ReplicatedQueueManagerTest.java | 28 ++ .../ReplicationTestAgentCardProducer.java | 44 +++ .../tests/ReplicationTestAgentExecutor.java | 64 ++++ .../tests/TestKafkaEventConsumer.java | 136 ++++++++ .../src/test/resources/application.properties | 29 ++ pom.xml | 1 + .../a2a/server/events/EventEnqueueHook.java | 7 + .../java/io/a2a/server/events/EventQueue.java | 45 ++- .../a2a/server/events/EventQueueFactory.java | 12 + .../server/events/InMemoryQueueManager.java | 24 +- .../io/a2a/server/events/QueueManager.java | 4 + .../DefaultRequestHandler.java | 2 +- .../a2a/server/events/EventConsumerTest.java | 6 +- .../io/a2a/server/events/EventQueueTest.java | 30 +- .../io/a2a/server/events/EventQueueUtil.java | 8 + .../events/InMemoryQueueManagerTest.java | 16 +- .../io/a2a/server/tasks/TaskUpdaterTest.java | 3 +- 33 files changed, 2154 insertions(+), 39 deletions(-) create mode 100644 extras/queue-manager-replicated/README.md create mode 100644 extras/queue-manager-replicated/core/pom.xml create mode 100644 extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java create mode 100644 extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java create mode 100644 extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicationStrategy.java create mode 100644 extras/queue-manager-replicated/core/src/main/resources/META-INF/beans.xml create mode 100644 extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java create mode 100644 extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java create mode 100644 extras/queue-manager-replicated/pom.xml create mode 100644 extras/queue-manager-replicated/replication-mp-reactive/pom.xml create mode 100644 extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java create mode 100644 extras/queue-manager-replicated/replication-mp-reactive/src/main/resources/META-INF/beans.xml create mode 100644 extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java create mode 100644 extras/queue-manager-replicated/tests/pom.xml create mode 100644 extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java create mode 100644 extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java create mode 100644 extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java create mode 100644 extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java create mode 100644 extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java create mode 100644 extras/queue-manager-replicated/tests/src/test/resources/application.properties create mode 100644 server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java create mode 100644 server-common/src/main/java/io/a2a/server/events/EventQueueFactory.java create mode 100644 server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java diff --git a/extras/README.md b/extras/README.md index e8d0c859f..3f85e4f90 100644 --- a/extras/README.md +++ b/extras/README.md @@ -5,4 +5,5 @@ This directory contains additions to what is provided by the default SDK impleme Please see the README's of each child directory for more details. [`task-store-database-jpa`](./task-store-database-jpa/README.md) - Replaces the default `InMemoryTaskStore` with a `TaskStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. -[`push-notification-config-store-database-jpa`](./push-notification-config-store-database-jpa/README.md) - Replaces the default `InMemoryPushNotificationConfigStore` with a `PushNotificationConfigStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. \ No newline at end of file +[`push-notification-config-store-database-jpa`](./push-notification-config-store-database-jpa/README.md) - Replaces the default `InMemoryPushNotificationConfigStore` with a `PushNotificationConfigStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. +[`queue-manager-replicated`](./queue-manager-replicated/README.md) - Replaces the default `InMemoryQueueManager` with a `QueueManager` supporting replication to other A2A servers implementing the same agent. You can write your own `ReplicationStrategy`, or use the provided `MicroProfile Reactive Messaging implementation`. \ No newline at end of file diff --git a/extras/queue-manager-replicated/README.md b/extras/queue-manager-replicated/README.md new file mode 100644 index 000000000..c832001fd --- /dev/null +++ b/extras/queue-manager-replicated/README.md @@ -0,0 +1,231 @@ +# A2A Java SDK - Replicated Queue Manager + +This module provides a replicated implementation of the `QueueManager` interface that enables event replication across multiple A2A instances using message brokers like Apache Kafka. It ensures that events generated in one A2A instance are propagated to other instances for distributed operation. + +The replication works by intercepting events as they are enqueued and sending them to a message broker. Events received from the broker are then processed by the local A2A instance, maintaining consistency across the distributed system. + +## Architecture + +The main components in the replicated queue manager are: + +- **[`ReplicatedQueueManager`](./core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java)**: Core queue manager that wraps the default `InMemoryQueueManager` and handles event replication. +- **[`ReplicationStrategy`](./core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicationStrategy.java)**: Interface for different replication implementations. If `ReplicatedQueueManager` is used, a `ReplicationStrategy` **must** be provided. + +Currently, one implementation is provided: [`ReactiveMessagingReplicationStrategy`](./replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java), which uses MicroProfile Reactive Messaging with message brokers like Apache Kafka. + +## Quick Start + +This section will get you up and running quickly with a `ReplicatedQueueManager` using the `ReactiveMessagingReplicationStrategy` set up to use Kafka as the message broker. + +### 1. Add Dependencies + +#### Core Module (Required) + +Add the core replicated queue manager module to your project's `pom.xml`: + +```xml + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + ${a2a.version} + +``` + +The `ReplicatedQueueManager` is annotated in such a way that it should take precedence over the default `InMemoryQueueManager`. Hence, it is a drop-in replacement. + +#### Replication Strategy Implementation (Required) + +You must also include a replication strategy implementation. Currently, we provide one implementation using MicroProfile Reactive Messaging: + +```xml + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + ${a2a.version} + +``` + +### 2. Basic Configuration + +Add to your `application.properties`: + +```properties +# Configure the outgoing channel (QueueManager -> Kafka) +mp.messaging.outgoing.replicated-events-out.connector=smallrye-kafka +mp.messaging.outgoing.replicated-events-out.topic=replicated-events +mp.messaging.outgoing.replicated-events-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer + +# Configure the incoming channel (Kafka -> QueueManager) +mp.messaging.incoming.replicated-events-in.connector=smallrye-kafka +mp.messaging.incoming.replicated-events-in.topic=replicated-events +mp.messaging.incoming.replicated-events-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +``` + +The channel names `replicated-events-in` and `replicated-events-out` correspond to the `@Incoming` and `@Channel` annotations in the ReactiveMessagingReplicationStrategy. + +### 3. Kafka Topic Setup + +Ensure your Kafka broker has the topic configured: + +```bash +# Create the replicated-events topic +kafka-topics.sh --create --topic replicated-events --bootstrap-server localhost:9092 --partitions 3 --replication-factor 1 +``` + +## Configuration + +### Kafka Configuration + +#### Basic Settings + +```properties +# Kafka broker configuration +kafka.bootstrap.servers=kafka-broker-1:9092,kafka-broker-2:9092 + +# Topic configuration +mp.messaging.outgoing.replicated-events-out.topic=my-replicated-events +mp.messaging.incoming.replicated-events-in.topic=my-replicated-events + +# Consumer behavior +mp.messaging.incoming.replicated-events-in.auto.offset.reset=earliest +``` + +#### Advanced Settings + +```properties +# Consumer group configuration (important for multiple A2A instances) +mp.messaging.incoming.replicated-events-in.group.id=a2a-instance-group + +# Reliability configuration +mp.messaging.outgoing.replicated-events-out.acks=all +mp.messaging.outgoing.replicated-events-out.retries=3 + +# Performance tuning +mp.messaging.outgoing.replicated-events-out.batch.size=16384 +mp.messaging.incoming.replicated-events-in.max.poll.records=500 + +# Serialization configuration +mp.messaging.outgoing.replicated-events-out.key.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.incoming.replicated-events-in.key.deserializer=org.apache.kafka.common.serialization.StringDeserializer +``` + +### Alternative Message Brokers + +While Kafka is the primary tested message broker, Quarkus Reactive Messaging supports other brokers: + +#### Apache Pulsar + +```properties +mp.messaging.outgoing.replicated-events-out.connector=smallrye-pulsar +mp.messaging.incoming.replicated-events-in.connector=smallrye-pulsar +pulsar.client.serviceUrl=pulsar://localhost:6650 +``` + +#### AMQP (RabbitMQ, etc.) + +```properties +mp.messaging.outgoing.replicated-events-out.connector=smallrye-amqp +mp.messaging.incoming.replicated-events-in.connector=smallrye-amqp +amqp-host=localhost +amqp-port=5672 +``` + +**Note**: Alternative message brokers have not been tested in this project yet. + +### WildFly/Jakarta EE Servers + +For non-Quarkus environments, you'll need to configure MicroProfile Reactive Messaging according to your application server's documentation. The exact configuration will depend on your server's messaging capabilities, but generally, you will need to make sure the same properties as above are made available to the server application. + +## How It Works + +### Event Flow + +1. **Event Generation**: When an event is generated in the A2A system (e.g., TaskStatusUpdateEvent), it's enqueued in the local queue +2. **Replication Hook**: The `ReplicationHook` intercepts the event and sends it to the replication strategy +3. **Message Broker**: The replication strategy serializes the event and sends it to the configured message broker +4. **Event Reception**: Other A2A instances receive the event from the message broker +5. **Local Processing**: The received event is deserialized and enqueued in the local instance's queue +6. **Event Processing**: The local instance processes the replicated event, updating its state accordingly + +### Event Types + +The system replicates various event types while preserving their specific types: + +- **TaskStatusUpdateEvent**: Task state changes (SUBMITTED, COMPLETED, etc.) +- **TaskArtifactUpdateEvent**: Task artifact changes +- **Message**: Chat messages and responses +- **Task**: Complete task objects +- **JSONRPCError**: Error events + +### Serialization + +Events are serialized using Jackson with polymorphic type information to ensure proper deserialization: + +```json +{ + "taskId": "task-123", + "event": { + "@type": "TaskStatusUpdateEvent", + "taskId": "task-123", + "status": { + "state": "completed", + "timestamp": "2023-09-29T10:30:00Z" + }, + "final": true, + "kind": "status-update" + } +} +``` + +## Advanced Topics + +### Custom Replication Strategies + +The architecture is designed to support additional replication strategies in the future. To implement a custom replication strategy, create a class that implements the `ReplicationStrategy` interface and ensure it's discoverable via CDI: + +```java +@ApplicationScoped +public class CustomReplicationStrategy implements ReplicationStrategy { + + @Override + public void send(String taskId, Event event) { + // Implement custom replication logic + // e.g., send to database, REST API, etc. + } +} +``` + +### Monitoring Events + +You can monitor replicated events by observing CDI events: + +```java +@ApplicationScoped +public class ReplicationMonitor { + + public void onReplicatedEvent(@Observes ReplicatedEvent event) { + // Monitor replicated events for metrics, logging, etc. + LOGGER.info("Received replicated event for task: " + event.getTaskId()); + } +} +``` + +### Logging + +Enable debug logging to monitor replication activity: + +```properties +# For Quarkus +quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG + +# For other servers, configure your logging framework accordingly +``` + +### Health Checks + +When using Quarkus, the module integrates with MicroProfile Health to provide health checks: + +```properties +# Configure health check timeout +quarkus.messaging.kafka.health.timeout=5s +``` diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml new file mode 100644 index 000000000..5d2742f9f --- /dev/null +++ b/extras/queue-manager-replicated/core/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-parent + 0.3.0.Beta2-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-core + Java A2A Extras: Replicated Queue Manager Core + + + + io.github.a2asdk + a2a-java-sdk-server-common + ${project.version} + + + io.quarkus + quarkus-core + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + org.junit.jupiter + junit-jupiter-api + test + + + ch.qos.logback + logback-classic + test + + + + \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java new file mode 100644 index 000000000..77eeb0406 --- /dev/null +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java @@ -0,0 +1,114 @@ +package io.a2a.extras.queuemanager.replicated.core; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.a2a.spec.Event; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.StreamingEventKind; + +public class ReplicatedEvent { + private String taskId; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private StreamingEventKind event; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private JSONRPCError error; + + // Default constructor for JSON deserialization + public ReplicatedEvent() { + } + + // Constructor for creating from A2A StreamingEventKind objects + public ReplicatedEvent(String taskId, StreamingEventKind event) { + this.taskId = taskId; + this.event = event; + this.error = null; + } + + // Constructor for creating from A2A JSONRPCError objects + public ReplicatedEvent(String taskId, JSONRPCError error) { + this.taskId = taskId; + this.event = null; + this.error = error; + } + + // Backward compatibility constructor for generic Event objects + public ReplicatedEvent(String taskId, Event event) { + this.taskId = taskId; + if (event instanceof StreamingEventKind streamingEvent) { + this.event = streamingEvent; + this.error = null; + } else if (event instanceof JSONRPCError jsonRpcError) { + this.event = null; + this.error = jsonRpcError; + } else { + throw new IllegalArgumentException("Event must be either StreamingEventKind or JSONRPCError, got: " + event.getClass()); + } + } + + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public StreamingEventKind getEvent() { + return event; + } + + public void setEvent(StreamingEventKind event) { + this.event = event; + this.error = null; // Clear error when setting event + } + + public JSONRPCError getError() { + return error; + } + + public void setError(JSONRPCError error) { + this.error = error; + this.event = null; // Clear event when setting error + } + + /** + * Get the contained event as the generic Event interface for backward compatibility. + * @return the event (either StreamingEventKind or JSONRPCError) or null if neither is set + */ + @JsonIgnore + public Event getEventAsGeneric() { + if (event != null) { + return event; + } + return error; + } + + /** + * Check if this ReplicatedEvent contains an event (vs an error). + * @return true if it contains a StreamingEventKind event + */ + public boolean hasEvent() { + return event != null; + } + + /** + * Check if this ReplicatedEvent contains an error. + * @return true if it contains a JSONRPCError + */ + public boolean hasError() { + return error != null; + } + + @Override + public String toString() { + return "ReplicatedEvent{" + + "taskId='" + taskId + '\'' + + ", event=" + event + + ", error=" + error + + '}'; + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java new file mode 100644 index 000000000..0b31b805d --- /dev/null +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -0,0 +1,122 @@ +package io.a2a.extras.queuemanager.replicated.core; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Alternative; +import jakarta.inject.Inject; + +import io.a2a.server.events.EventEnqueueHook; +import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueFactory; +import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.events.QueueManager; +import io.a2a.spec.Event; + +@ApplicationScoped +@Alternative +@Priority(50) +public class ReplicatedQueueManager implements QueueManager { + + private final InMemoryQueueManager delegate; + private final ThreadLocal isHandlingReplicatedEvent = new ThreadLocal<>(); + + @Inject + private ReplicationStrategy replicationStrategy; + + public ReplicatedQueueManager() { + this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory()); + } + + // For testing + ReplicatedQueueManager(ReplicationStrategy replicationStrategy) { + this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory()); + this.replicationStrategy = replicationStrategy; + } + + @Override + public void add(String taskId, EventQueue queue) { + delegate.add(taskId, queue); + } + + @Override + public EventQueue get(String taskId) { + return delegate.get(taskId); + } + + @Override + public EventQueue tap(String taskId) { + return delegate.tap(taskId); + } + + @Override + public void close(String taskId) { + delegate.close(taskId); + } + + @Override + public EventQueue createOrTap(String taskId) { + EventQueue queue = delegate.createOrTap(taskId); + return queue; + } + + @Override + public void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedException { + delegate.awaitQueuePollerStart(eventQueue); + } + + public void onReplicatedEvent(@Observes ReplicatedEvent replicatedEvent) { + isHandlingReplicatedEvent.set(true); + try { + EventQueue queue = delegate.get(replicatedEvent.getTaskId()); + + if (queue == null) { + // If no queue exists, create or tap one to handle the replicated event + // This can happen when events arrive after the original queue has been closed + queue = delegate.createOrTap(replicatedEvent.getTaskId()); + } + + if (queue != null) { + // Use the backward compatibility method to get the event as the generic Event interface + Event event = replicatedEvent.getEventAsGeneric(); + if (event != null) { + queue.enqueueEvent(event); + } + } + } finally { + isHandlingReplicatedEvent.remove(); + } + } + + @Override + public EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { + return QueueManager.super.getEventQueueBuilder(taskId) + .hook(new ReplicationHook(taskId)); + } + + private class ReplicatingEventQueueFactory implements EventQueueFactory { + @Override + public EventQueue.EventQueueBuilder builder(String taskId) { + // Use the taskId parameter directly instead of ThreadLocal + return delegate.getEventQueueBuilder(taskId).hook(new ReplicationHook(taskId)); + } + } + + private class ReplicationHook implements EventEnqueueHook { + private final String taskId; + + public ReplicationHook(String taskId) { + this.taskId = taskId; + } + + @Override + public void onEnqueue(Event event) { + // Only replicate if this isn't already a replicated event being processed + if (isHandlingReplicatedEvent.get() != Boolean.TRUE) { + if (replicationStrategy != null && taskId != null) { + replicationStrategy.send(taskId, event); + } + } + } + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicationStrategy.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicationStrategy.java new file mode 100644 index 000000000..fed85db64 --- /dev/null +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicationStrategy.java @@ -0,0 +1,7 @@ +package io.a2a.extras.queuemanager.replicated.core; + +import io.a2a.spec.Event; + +public interface ReplicationStrategy { + void send(String taskId, Event event); +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/main/resources/META-INF/beans.xml b/extras/queue-manager-replicated/core/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..6bb944ce2 --- /dev/null +++ b/extras/queue-manager-replicated/core/src/main/resources/META-INF/beans.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java new file mode 100644 index 000000000..749f0a308 --- /dev/null +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -0,0 +1,291 @@ +package io.a2a.extras.queuemanager.replicated.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.spec.Artifact; +import io.a2a.spec.Event; +import io.a2a.spec.InternalError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.Part; +import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskNotFoundError; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import io.a2a.spec.UnsupportedOperationError; +import io.a2a.util.Utils; +import org.junit.jupiter.api.Test; + +/** + * Comprehensive test for serialization/deserialization of all StreamingEventKind classes + * and JSONRPCError subclasses to ensure proper type handling in replication. + */ +public class EventSerializationTest { + + @Test + public void testTaskSerialization() throws JsonProcessingException { + // Create a Task + TaskStatus status = new TaskStatus(TaskState.SUBMITTED); + Task originalTask = new Task.Builder() + .id("test-task-123") + .contextId("test-context-456") + .status(status) + .build(); + + // Test serialization as Event + String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalTask); + assertTrue(json.contains("\"kind\":\"task\""), "JSON should contain task kind"); + assertTrue(json.contains("\"id\":\"test-task-123\""), "JSON should contain task ID"); + + // Test deserialization back to StreamingEventKind + StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(Task.class, deserializedEvent, "Should deserialize to Task"); + + Task deserializedTask = (Task) deserializedEvent; + assertEquals(originalTask.getId(), deserializedTask.getId()); + assertEquals(originalTask.getKind(), deserializedTask.getKind()); + assertEquals(originalTask.getContextId(), deserializedTask.getContextId()); + assertEquals(originalTask.getStatus().state(), deserializedTask.getStatus().state()); + + // Test as StreamingEventKind + StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(Task.class, deserializedAsStreaming, "Should deserialize to Task as StreamingEventKind"); + } + + @Test + public void testMessageSerialization() throws JsonProcessingException { + // Create a Message + Message originalMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("Hello, world!"))) + .taskId("test-task-789") + .messageId("test-msg-456") + .contextId("test-context-123") + .build(); + + // Test serialization as Event + String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalMessage); + assertTrue(json.contains("\"kind\":\"message\""), "JSON should contain message kind"); + assertTrue(json.contains("\"taskId\":\"test-task-789\""), "JSON should contain task ID"); + + // Test deserialization back to StreamingEventKind + StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(Message.class, deserializedEvent, "Should deserialize to Message"); + + Message deserializedMessage = (Message) deserializedEvent; + assertEquals(originalMessage.getTaskId(), deserializedMessage.getTaskId()); + assertEquals(originalMessage.getKind(), deserializedMessage.getKind()); + assertEquals(originalMessage.getRole(), deserializedMessage.getRole()); + assertEquals(originalMessage.getParts().size(), deserializedMessage.getParts().size()); + + // Test as StreamingEventKind + StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(Message.class, deserializedAsStreaming, "Should deserialize to Message as StreamingEventKind"); + } + + @Test + public void testTaskStatusUpdateEventSerialization() throws JsonProcessingException { + // Create a TaskStatusUpdateEvent + TaskStatus status = new TaskStatus(TaskState.COMPLETED); + TaskStatusUpdateEvent originalEvent = new TaskStatusUpdateEvent.Builder() + .taskId("test-task-abc") + .contextId("test-context-def") + .status(status) + .isFinal(true) + .build(); + + // Test serialization as Event + String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalEvent); + assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain status-update kind"); + assertTrue(json.contains("\"taskId\":\"test-task-abc\""), "JSON should contain task ID"); + assertTrue(json.contains("\"final\":true"), "JSON should contain final flag"); + + // Test deserialization back to StreamingEventKind + StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(TaskStatusUpdateEvent.class, deserializedEvent, "Should deserialize to TaskStatusUpdateEvent"); + + TaskStatusUpdateEvent deserializedStatusEvent = (TaskStatusUpdateEvent) deserializedEvent; + assertEquals(originalEvent.getTaskId(), deserializedStatusEvent.getTaskId()); + assertEquals(originalEvent.getKind(), deserializedStatusEvent.getKind()); + assertEquals(originalEvent.getContextId(), deserializedStatusEvent.getContextId()); + assertEquals(originalEvent.getStatus().state(), deserializedStatusEvent.getStatus().state()); + assertEquals(originalEvent.isFinal(), deserializedStatusEvent.isFinal()); + + // Test as StreamingEventKind + StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(TaskStatusUpdateEvent.class, deserializedAsStreaming, "Should deserialize to TaskStatusUpdateEvent as StreamingEventKind"); + } + + @Test + public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingException { + // Create a TaskArtifactUpdateEvent + List> parts = List.of(new TextPart("Test artifact content")); + Artifact artifact = new Artifact("test-artifact-123", "Test Artifact", "Test description", parts, null); + TaskArtifactUpdateEvent originalEvent = new TaskArtifactUpdateEvent.Builder() + .taskId("test-task-xyz") + .contextId("test-context-uvw") + .artifact(artifact) + .build(); + + // Test serialization as Event + String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalEvent); + assertTrue(json.contains("\"kind\":\"artifact-update\""), "JSON should contain artifact-update kind"); + assertTrue(json.contains("\"taskId\":\"test-task-xyz\""), "JSON should contain task ID"); + assertTrue(json.contains("\"test-artifact-123\""), "JSON should contain artifact ID"); + + // Test deserialization back to StreamingEventKind + StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(TaskArtifactUpdateEvent.class, deserializedEvent, "Should deserialize to TaskArtifactUpdateEvent"); + + TaskArtifactUpdateEvent deserializedArtifactEvent = (TaskArtifactUpdateEvent) deserializedEvent; + assertEquals(originalEvent.getTaskId(), deserializedArtifactEvent.getTaskId()); + assertEquals(originalEvent.getKind(), deserializedArtifactEvent.getKind()); + assertEquals(originalEvent.getContextId(), deserializedArtifactEvent.getContextId()); + assertEquals(originalEvent.getArtifact().artifactId(), deserializedArtifactEvent.getArtifact().artifactId()); + assertEquals(originalEvent.getArtifact().name(), deserializedArtifactEvent.getArtifact().name()); + + // Test as StreamingEventKind + StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + assertInstanceOf(TaskArtifactUpdateEvent.class, deserializedAsStreaming, "Should deserialize to TaskArtifactUpdateEvent as StreamingEventKind"); + } + + @Test + public void testJSONRPCErrorSubclassesSerialization() throws JsonProcessingException { + // Test various JSONRPCError subclasses + JSONRPCError[] errors = { + new InvalidRequestError("Invalid request"), + new MethodNotFoundError(), + new InvalidParamsError("Invalid params"), + new InternalError("Internal error"), + new JSONParseError("Parse error"), + new TaskNotFoundError(), + new TaskNotCancelableError(), + new UnsupportedOperationError(), + new PushNotificationNotSupportedError() + // Note: ContentTypeNotSupportedError and InvalidAgentResponseError need specific constructor parameters + }; + + for (JSONRPCError originalError : errors) { + // Test serialization + String json = Utils.OBJECT_MAPPER.writeValueAsString(originalError); + assertTrue(json.contains("\"message\""), "JSON should contain error message for " + originalError.getClass().getSimpleName()); + + // Test deserialization - it's acceptable to deserialize as base JSONRPCError + JSONRPCError deserializedError = Utils.OBJECT_MAPPER.readValue(json, JSONRPCError.class); + assertNotNull(deserializedError, "Should deserialize successfully for " + originalError.getClass().getSimpleName()); + assertEquals(originalError.getMessage(), deserializedError.getMessage(), "Error message should match for " + originalError.getClass().getSimpleName()); + assertEquals(originalError.getCode(), deserializedError.getCode(), "Error code should match for " + originalError.getClass().getSimpleName()); + + // The deserialized error might be the base class, which is acceptable per the requirements + } + } + + @Test + public void testReplicatedEventWithStreamingEventSerialization() throws JsonProcessingException { + // Test that ReplicatedEvent can properly handle StreamingEventKind + TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + .taskId("replicated-test-task") + .contextId("replicated-test-context") + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .build(); + + // Create ReplicatedEvent with StreamingEventKind + ReplicatedEvent originalReplicatedEvent = new ReplicatedEvent("replicated-test-task", statusEvent); + + // Serialize the ReplicatedEvent + String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); + assertTrue(json.contains("\"taskId\":\"replicated-test-task\""), "JSON should contain task ID"); + assertTrue(json.contains("\"event\""), "JSON should contain event field"); + assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain the event kind"); + assertFalse(json.contains("\"error\""), "JSON should not contain error field"); + + // Deserialize the ReplicatedEvent + ReplicatedEvent deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEvent.class); + assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); + + // Now we should get the proper type back! + StreamingEventKind retrievedEvent = deserializedReplicatedEvent.getEvent(); + assertNotNull(retrievedEvent); + assertInstanceOf(TaskStatusUpdateEvent.class, retrievedEvent, "Should deserialize to TaskStatusUpdateEvent"); + + TaskStatusUpdateEvent retrievedStatusEvent = (TaskStatusUpdateEvent) retrievedEvent; + assertEquals(statusEvent.getTaskId(), retrievedStatusEvent.getTaskId()); + assertEquals(statusEvent.getContextId(), retrievedStatusEvent.getContextId()); + assertEquals(statusEvent.getStatus().state(), retrievedStatusEvent.getStatus().state()); + assertEquals(statusEvent.isFinal(), retrievedStatusEvent.isFinal()); + + // Test helper methods + assertTrue(deserializedReplicatedEvent.hasEvent()); + assertFalse(deserializedReplicatedEvent.hasError()); + assertNull(deserializedReplicatedEvent.getError()); + } + + @Test + public void testReplicatedEventWithErrorSerialization() throws JsonProcessingException { + // Test that ReplicatedEvent can properly handle JSONRPCError + InvalidRequestError error = new InvalidRequestError("Invalid request for testing"); + + // Create ReplicatedEvent with JSONRPCError + ReplicatedEvent originalReplicatedEvent = new ReplicatedEvent("error-test-task", error); + + // Serialize the ReplicatedEvent + String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); + assertTrue(json.contains("\"taskId\":\"error-test-task\""), "JSON should contain task ID"); + assertTrue(json.contains("\"error\""), "JSON should contain error field"); + assertTrue(json.contains("\"message\""), "JSON should contain error message"); + assertFalse(json.contains("\"event\""), "JSON should not contain event field"); + + // Deserialize the ReplicatedEvent + ReplicatedEvent deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEvent.class); + assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); + + // Should get the error back + JSONRPCError retrievedError = deserializedReplicatedEvent.getError(); + assertNotNull(retrievedError); + assertEquals(error.getMessage(), retrievedError.getMessage()); + assertEquals(error.getCode(), retrievedError.getCode()); + + // Test helper methods + assertFalse(deserializedReplicatedEvent.hasEvent()); + assertTrue(deserializedReplicatedEvent.hasError()); + assertNull(deserializedReplicatedEvent.getEvent()); + } + + @Test + public void testReplicatedEventBackwardCompatibility() throws JsonProcessingException { + // Test backward compatibility with generic Event constructor + TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + .taskId("backward-compat-task") + .contextId("backward-compat-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(); + + // Use the backward compatibility constructor + ReplicatedEvent replicatedEvent = new ReplicatedEvent("backward-compat-task", (Event) statusEvent); + + // Should work the same as the specific constructor + assertTrue(replicatedEvent.hasEvent()); + assertFalse(replicatedEvent.hasError()); + assertInstanceOf(TaskStatusUpdateEvent.class, replicatedEvent.getEvent()); + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java new file mode 100644 index 000000000..5f6a6ebd4 --- /dev/null +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -0,0 +1,312 @@ +package io.a2a.extras.queuemanager.replicated.core; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueClosedException; +import io.a2a.spec.Event; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.util.Utils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ReplicatedQueueManagerTest { + + private ReplicatedQueueManager queueManager; + private StreamingEventKind testEvent; + + @BeforeEach + void setUp() { + queueManager = new ReplicatedQueueManager(); + testEvent = new TaskStatusUpdateEvent.Builder() + .taskId("test-task") + .contextId("test-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .isFinal(false) + .build(); + } + + @Test + void testReplicationStrategyTriggeredOnNormalEnqueue() throws InterruptedException { + CountingReplicationStrategy strategy = new CountingReplicationStrategy(); + queueManager = new ReplicatedQueueManager(strategy); + + String taskId = "test-task-1"; + EventQueue queue = queueManager.createOrTap(taskId); + + queue.enqueueEvent(testEvent); + + assertEquals(1, strategy.getCallCount()); + assertEquals(taskId, strategy.getLastTaskId()); + assertEquals(testEvent, strategy.getLastEvent()); + } + + @Test + void testReplicationStrategyNotTriggeredOnReplicatedEvent() throws InterruptedException { + CountingReplicationStrategy strategy = new CountingReplicationStrategy(); + queueManager = new ReplicatedQueueManager(strategy); + + String taskId = "test-task-2"; + EventQueue queue = queueManager.createOrTap(taskId); + + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + queueManager.onReplicatedEvent(replicatedEvent); + + assertEquals(0, strategy.getCallCount()); + } + + @Test + void testReplicationStrategyWithCountingImplementation() throws InterruptedException { + CountingReplicationStrategy countingStrategy = new CountingReplicationStrategy(); + queueManager = new ReplicatedQueueManager(countingStrategy); + + String taskId = "test-task-3"; + EventQueue queue = queueManager.createOrTap(taskId); + + queue.enqueueEvent(testEvent); + queue.enqueueEvent(testEvent); + + assertEquals(2, countingStrategy.getCallCount()); + assertEquals(taskId, countingStrategy.getLastTaskId()); + assertEquals(testEvent, countingStrategy.getLastEvent()); + + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + queueManager.onReplicatedEvent(replicatedEvent); + + assertEquals(2, countingStrategy.getCallCount()); + } + + @Test + void testReplicatedEventDeliveredToCorrectQueue() throws InterruptedException { + String taskId = "test-task-4"; + EventQueue queue = queueManager.createOrTap(taskId); + + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + queueManager.onReplicatedEvent(replicatedEvent); + + Event dequeuedEvent; + try { + dequeuedEvent = queue.dequeueEvent(100); + } catch (EventQueueClosedException e) { + fail("Queue should not be closed"); + return; + } + assertEquals(testEvent, dequeuedEvent); + } + + @Test + void testReplicatedEventCreatesQueueIfNeeded() throws InterruptedException { + String taskId = "non-existent-task"; + + // Verify no queue exists initially + assertNull(queueManager.get(taskId)); + + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + + // Process the replicated event + assertDoesNotThrow(() -> queueManager.onReplicatedEvent(replicatedEvent)); + + // Verify that a queue was created and the event was enqueued + EventQueue queue = queueManager.get(taskId); + assertNotNull(queue, "Queue should be created when processing replicated event for non-existent task"); + + // Verify the event was enqueued by dequeuing it + Event dequeuedEvent; + try { + dequeuedEvent = queue.dequeueEvent(100); + } catch (EventQueueClosedException e) { + fail("Queue should not be closed"); + return; + } + assertEquals(testEvent, dequeuedEvent, "The replicated event should be enqueued in the newly created queue"); + } + + @Test + void testBasicQueueManagerFunctionality() throws InterruptedException { + String taskId = "test-task-5"; + + assertNull(queueManager.get(taskId)); + assertNull(queueManager.tap(taskId)); + + EventQueue queue = queueManager.createOrTap(taskId); + assertNotNull(queue); + + EventQueue retrievedQueue = queueManager.get(taskId); + assertEquals(queue, retrievedQueue); + + EventQueue tappedQueue = queueManager.tap(taskId); + assertNotNull(tappedQueue); + assertNotEquals(queue, tappedQueue); + + queueManager.close(taskId); + assertNull(queueManager.get(taskId)); + } + + @Test + void testQueueToTaskIdMappingMaintained() throws InterruptedException { + String taskId = "test-task-6"; + CountingReplicationStrategy countingStrategy = new CountingReplicationStrategy(); + queueManager = new ReplicatedQueueManager(countingStrategy); + + EventQueue queue = queueManager.createOrTap(taskId); + queue.enqueueEvent(testEvent); + + assertEquals(taskId, countingStrategy.getLastTaskId()); + + queueManager.close(taskId); + + EventQueue newQueue = queueManager.createOrTap(taskId); + newQueue.enqueueEvent(testEvent); + + assertEquals(taskId, countingStrategy.getLastTaskId()); + assertEquals(2, countingStrategy.getCallCount()); + } + + @Test + void testReplicatedEventJsonSerialization() throws Exception { + // Test that ReplicatedEvent can be properly serialized and deserialized with StreamingEventKind + TaskStatusUpdateEvent originalEvent = new TaskStatusUpdateEvent.Builder() + .taskId("json-test-task") + .contextId("json-test-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(); + ReplicatedEvent original = new ReplicatedEvent("json-test-task", originalEvent); + + // Serialize to JSON + String json = Utils.OBJECT_MAPPER.writeValueAsString(original); + assertNotNull(json); + assertTrue(json.contains("json-test-task")); + assertTrue(json.contains("\"event\":{")); + assertTrue(json.contains("\"kind\":\"status-update\"")); + + // Deserialize back + ReplicatedEvent deserialized = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEvent.class); + assertNotNull(deserialized); + assertEquals("json-test-task", deserialized.getTaskId()); + assertNotNull(deserialized.getEvent()); + assertTrue(deserialized.hasEvent()); + assertFalse(deserialized.hasError()); + } + + @Test + void testParallelReplicationBehavior() throws InterruptedException { + CountingReplicationStrategy strategy = new CountingReplicationStrategy(); + queueManager = new ReplicatedQueueManager(strategy); + + String taskId = "parallel-test-task"; + EventQueue queue = queueManager.createOrTap(taskId); + + int numThreads = 10; + int eventsPerThread = 5; + ExecutorService executor = Executors.newFixedThreadPool(numThreads); + CountDownLatch startLatch = new CountDownLatch(1); + CountDownLatch doneLatch = new CountDownLatch(numThreads); + + // Launch threads that will enqueue events normally (should trigger replication) + for (int i = 0; i < numThreads / 2; i++) { + final int threadId = i; + executor.submit(() -> { + try { + startLatch.await(); + for (int j = 0; j < eventsPerThread; j++) { + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("normal-" + threadId + "-" + j) + .contextId("test-context") + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .build(); + queue.enqueueEvent(event); + Thread.sleep(1); // Small delay to interleave operations + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + doneLatch.countDown(); + } + }); + } + + // Launch threads that will send replicated events (should NOT trigger replication) + for (int i = numThreads / 2; i < numThreads; i++) { + final int threadId = i; + executor.submit(() -> { + try { + startLatch.await(); + for (int j = 0; j < eventsPerThread; j++) { + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("replicated-" + threadId + "-" + j) + .contextId("test-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(); + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, event); + queueManager.onReplicatedEvent(replicatedEvent); + Thread.sleep(1); // Small delay to interleave operations + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + doneLatch.countDown(); + } + }); + } + + // Start all threads simultaneously + startLatch.countDown(); + + // Wait for all threads to complete + assertTrue(doneLatch.await(10, TimeUnit.SECONDS), "All threads should complete within 10 seconds"); + + executor.shutdown(); + assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS), "Executor should shutdown within 5 seconds"); + + // Only the normal enqueue operations should have triggered replication + // numThreads/2 threads * eventsPerThread events each = total expected replication calls + int expectedReplicationCalls = (numThreads / 2) * eventsPerThread; + assertEquals(expectedReplicationCalls, strategy.getCallCount(), + "Only normal enqueue operations should trigger replication, not replicated events"); + } + + private static class CountingReplicationStrategy implements ReplicationStrategy { + private final AtomicInteger callCount = new AtomicInteger(0); + private volatile String lastTaskId; + private volatile Event lastEvent; + + @Override + public void send(String taskId, Event event) { + callCount.incrementAndGet(); + this.lastTaskId = taskId; + this.lastEvent = event; + } + + public int getCallCount() { + return callCount.get(); + } + + public String getLastTaskId() { + return lastTaskId; + } + + public Event getLastEvent() { + return lastEvent; + } + } + +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml new file mode 100644 index 000000000..69c859762 --- /dev/null +++ b/extras/queue-manager-replicated/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta2-SNAPSHOT + ../../pom.xml + + + a2a-java-queue-manager-replicated-parent + pom + Java A2A Extras: Replicated Queue Manager Parent + + + core + replication-mp-reactive + tests + + + \ No newline at end of file diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml new file mode 100644 index 000000000..aa17435a9 --- /dev/null +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-parent + 0.3.0.Beta2-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replication-mp-reactive + Java A2A Extras: Replicated Queue Manager MicroProfile Reactive Messaging + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + ${project.version} + + + io.quarkus + quarkus-messaging + + + io.quarkus + quarkus-smallrye-context-propagation + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + + \ No newline at end of file diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java new file mode 100644 index 000000000..9836095bd --- /dev/null +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java @@ -0,0 +1,63 @@ +package io.a2a.extras.queuemanager.replicated.mp_reactive; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +import com.fasterxml.jackson.core.type.TypeReference; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.extras.queuemanager.replicated.core.ReplicationStrategy; +import io.a2a.util.Utils; +import org.eclipse.microprofile.reactive.messaging.Channel; +import org.eclipse.microprofile.reactive.messaging.Emitter; +import org.eclipse.microprofile.reactive.messaging.Incoming; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ApplicationScoped +public class ReactiveMessagingReplicationStrategy implements ReplicationStrategy { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveMessagingReplicationStrategy.class); + private static final TypeReference REPLICATED_EVENT_TYPE_REF = new TypeReference() {}; + + @Inject + @Channel("replicated-events-out") + private Emitter emitter; + + @Inject + private Event cdiEvent; + + @Override + public void send(String taskId, io.a2a.spec.Event event) { + LOGGER.debug("Sending replicated event for task: {}, event: {}", taskId, event); + + try { + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, event); + String json = Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); + emitter.send(json); + LOGGER.debug("Successfully sent replicated event for task: {}", taskId); + } catch (Exception e) { + LOGGER.error("Failed to send replicated event for task: {}, event: {}", taskId, event, e); + throw new RuntimeException("Failed to send replicated event", e); + } + } + + @Incoming("replicated-events-in") + public void onReplicatedEvent(String jsonMessage) { + LOGGER.debug("Received replicated event JSON: {}", jsonMessage); + + try { + ReplicatedEvent replicatedEvent = Utils.unmarshalFrom(jsonMessage, REPLICATED_EVENT_TYPE_REF); + LOGGER.debug("Deserialized replicated event for task: {}, event: {}", + replicatedEvent.getTaskId(), replicatedEvent.getEvent()); + + // Fire the CDI event directly + cdiEvent.fire(replicatedEvent); + LOGGER.debug("Successfully fired CDI event for task: {}", replicatedEvent.getTaskId()); + } catch (Exception e) { + LOGGER.error("Failed to deserialize replicated event from JSON: {}", jsonMessage, e); + // Don't throw - just log the error and continue processing other messages + // This prevents one bad message from stopping the entire message processing + } + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/main/resources/META-INF/beans.xml b/extras/queue-manager-replicated/replication-mp-reactive/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..6bb944ce2 --- /dev/null +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/main/resources/META-INF/beans.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java new file mode 100644 index 000000000..32ae1d21a --- /dev/null +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java @@ -0,0 +1,110 @@ +package io.a2a.extras.queuemanager.replicated.mp_reactive; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import jakarta.enterprise.event.Event; + +import org.eclipse.microprofile.reactive.messaging.Emitter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.util.Utils; + +@ExtendWith(MockitoExtension.class) +class ReactiveMessagingReplicationStrategyTest { + + @Mock + private Emitter emitter; + + @Mock + private Event cdiEvent; + + @InjectMocks + private ReactiveMessagingReplicationStrategy strategy; + + private StreamingEventKind testEvent; + + @BeforeEach + public void setUp() { + testEvent = new TaskStatusUpdateEvent.Builder() + .taskId("test-task") + .contextId("test-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .isFinal(false) + .build(); + } + + private String createValidJsonMessage(String taskId, String contextId) throws Exception { + // Create a proper ReplicatedEvent JSON with StreamingEventKind + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .build(); + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, event); + return Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); + } + + @Test + public void testSendCallsEmitter() { + String taskId = "test-task-123"; + + strategy.send(taskId, testEvent); + + // Verify that emitter.send was called (don't care about exact JSON format) + verify(emitter).send(any(String.class)); + } + + @Test + public void testSendPropagatesEmitterExceptions() { + String taskId = "test-task-456"; + RuntimeException emitterException = new RuntimeException("Failed to send replicated event"); + + doThrow(emitterException).when(emitter).send(any(String.class)); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> + strategy.send(taskId, testEvent) + ); + + // The implementation wraps the original exception, so check the cause + assertEquals(emitterException, exception.getCause()); + assertEquals("Failed to send replicated event", exception.getMessage()); + } + + @Test + public void testOnReplicatedEventWithValidJson() throws Exception { + String validJsonMessage = createValidJsonMessage("test-task-101", "test-context"); + + // Should not throw - valid JSON should be handled gracefully + assertDoesNotThrow(() -> strategy.onReplicatedEvent(validJsonMessage)); + + // Note: We test the actual CDI firing in integration tests + // Unit tests here focus on error handling and basic flow + } + + + @Test + public void testOnReplicatedEventHandlesInvalidJson() { + String invalidJsonMessage = "invalid-json"; + + // Should not throw - invalid JSON should be handled gracefully + assertDoesNotThrow(() -> strategy.onReplicatedEvent(invalidJsonMessage)); + + // CDI event should not be fired for invalid JSON + verify(cdiEvent, never()).fire(any(ReplicatedEvent.class)); + } + + +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests/pom.xml new file mode 100644 index 000000000..4e991c4d3 --- /dev/null +++ b/extras/queue-manager-replicated/tests/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + io.github.a2asdk + a2a-java-queue-manager-replicated-parent + 0.3.0.Beta2-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-tests + Java A2A Extras: Replicated Queue Manager Tests + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + ${project.version} + + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + ${project.version} + + + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + ${project.version} + + + + + io.github.a2asdk + a2a-java-sdk-client + ${project.version} + test + + + io.github.a2asdk + a2a-java-sdk-transport-jsonrpc + ${project.version} + test + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-messaging-kafka + test + + + org.awaitility + awaitility + test + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java new file mode 100644 index 000000000..55220aa54 --- /dev/null +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -0,0 +1,261 @@ +package io.a2a.extras.queuemanager.replicated.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import jakarta.inject.Inject; + +import io.a2a.client.Client; +import io.a2a.client.ClientEvent; +import io.a2a.client.TaskEvent; +import io.a2a.client.TaskUpdateEvent; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import io.a2a.util.Utils; +import io.quarkus.test.junit.QuarkusTest; +import org.eclipse.microprofile.reactive.messaging.Channel; +import org.eclipse.microprofile.reactive.messaging.Emitter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Integration test for Kafka replication functionality. + * Tests the full A2A message flow with Kafka replication verification. + */ +@QuarkusTest +public class KafkaReplicationIntegrationTest { + + @Inject + @PublicAgentCard + AgentCard agentCard; + + @Inject + TestKafkaEventConsumer testConsumer; + + @Inject + @Channel("replicated-events-out") + Emitter testEmitter; + + private Client streamingClient; + private Client nonStreamingClient; + + @BeforeEach + public void setup() throws A2AClientException { + // Create non-streaming client for initial task creation + ClientConfig nonStreamingConfig = new ClientConfig.Builder() + .setStreaming(false) + .build(); + + nonStreamingClient = Client.builder(agentCard) + .clientConfig(nonStreamingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + // Create streaming client for resubscription + ClientConfig streamingConfig = new ClientConfig.Builder() + .setStreaming(true) + .build(); + + streamingClient = Client.builder(agentCard) + .clientConfig(streamingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + } + + @Test + public void testA2AMessageReplicatedToKafka() throws Exception { + String taskId = "kafka-replication-test-" + System.currentTimeMillis(); + String contextId = "test-context-" + System.currentTimeMillis(); + + // Clear any previous events + testConsumer.clear(); + + // Send A2A message that should trigger events and replication + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("create"))) + .taskId(taskId) + .messageId("test-msg-" + System.currentTimeMillis()) + .contextId(contextId) + .build(); + + CountDownLatch a2aLatch = new CountDownLatch(1); + AtomicReference createdTask = new AtomicReference<>(); + AtomicBoolean a2aError = new AtomicBoolean(false); + + // Send message and verify A2A processing works + nonStreamingClient.sendMessage(message, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof TaskEvent taskEvent) { + createdTask.set(taskEvent.getTask()); + a2aLatch.countDown(); + } + }), (Throwable error) -> { + a2aError.set(true); + a2aLatch.countDown(); + }); + + // Wait for A2A processing to complete + assertTrue(a2aLatch.await(15, TimeUnit.SECONDS), "A2A message processing timed out"); + assertFalse(a2aError.get(), "A2A processing failed"); + + Task task = createdTask.get(); + assertNotNull(task, "Task should be created"); + assertEquals(taskId, task.getId()); + assertEquals(TaskState.SUBMITTED, task.getStatus().state()); + + // Wait for the event to be replicated to Kafka + ReplicatedEvent replicatedEvent = testConsumer.waitForEvent(taskId, 30); + assertNotNull(replicatedEvent, "Event should be replicated to Kafka within 30 seconds"); + + // Verify the replicated event content + assertEquals(taskId, replicatedEvent.getTaskId()); + io.a2a.spec.Event receivedEvent = replicatedEvent.getEvent(); + assertNotNull(receivedEvent); + + // The event should now maintain its proper type through Kafka serialization + // This verifies that our polymorphic serialization is working correctly + assertInstanceOf(TaskStatusUpdateEvent.class, receivedEvent, "Event should maintain TaskStatusUpdateEvent type after Kafka round-trip"); + TaskStatusUpdateEvent statusUpdateEvent = (TaskStatusUpdateEvent) receivedEvent; + + // Verify the event data is consistent with the task returned from the client + assertEquals(taskId, statusUpdateEvent.getTaskId(), "Event task ID should match original task ID"); + assertEquals(contextId, statusUpdateEvent.getContextId(), "Event context ID should match original context ID"); + assertEquals(TaskState.SUBMITTED, statusUpdateEvent.getStatus().state(), "Event should show SUBMITTED state"); + assertFalse(statusUpdateEvent.isFinal(), "Event should show final:false"); + assertEquals("status-update", statusUpdateEvent.getKind(), "Event should indicate status-update type"); + } + + @Test + public void testKafkaEventReceivedByA2AServer() throws Exception { + String taskId = "kafka-to-a2a-test-" + System.currentTimeMillis(); + String contextId = "test-context-" + System.currentTimeMillis(); + + // Clear any previous events + testConsumer.clear(); + + // First create a task in the A2A system using non-streaming client + Message createMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("create"))) + .taskId(taskId) + .messageId("create-msg-" + System.currentTimeMillis()) + .contextId(contextId) + .build(); + + CountDownLatch createLatch = new CountDownLatch(1); + AtomicReference createdTask = new AtomicReference<>(); + + nonStreamingClient.sendMessage(createMessage, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof TaskEvent taskEvent) { + createdTask.set(taskEvent.getTask()); + createLatch.countDown(); + } + }), (Throwable error) -> { + createLatch.countDown(); + }); + + assertTrue(createLatch.await(15, TimeUnit.SECONDS), "Task creation timed out"); + Task initialTask = createdTask.get(); + assertNotNull(initialTask, "Task should be created"); + assertEquals(TaskState.SUBMITTED, initialTask.getStatus().state(), "Initial task should be in SUBMITTED state"); + + // Add a small delay to ensure the task is fully processed before resubscription + Thread.sleep(1000); + + // Set up resubscription to listen for task updates using streaming client + CountDownLatch resubscribeLatch = new CountDownLatch(1); + AtomicReference receivedCompletedEvent = new AtomicReference<>(); + AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); + AtomicReference errorRef = new AtomicReference<>(); + + // Create consumer to handle resubscribed events + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof TaskUpdateEvent taskUpdateEvent) { + if (taskUpdateEvent.getUpdateEvent() instanceof TaskStatusUpdateEvent statusEvent) { + if (statusEvent.getStatus().state() == TaskState.COMPLETED) { + receivedCompletedEvent.set(statusEvent); + resubscribeLatch.countDown(); + } + } else { + wasUnexpectedEvent.set(true); + } + } else { + wasUnexpectedEvent.set(true); + } + }; + + // Create error handler + Consumer errorHandler = error -> { + errorRef.set(error); + resubscribeLatch.countDown(); + }; + + // Resubscribe to the task to listen for updates + streamingClient.resubscribe(new TaskIdParams(taskId), List.of(consumer), errorHandler); + + // Now manually send a TaskStatusUpdateEvent to Kafka using reactive messaging + TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + .taskId(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(); + + ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, statusEvent); + String eventJson = Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); + + // Send to Kafka using reactive messaging + testEmitter.send(eventJson); + + // Wait for the replicated event to be received via streaming resubscription + // This tests the full round-trip: Manual Kafka Event -> A2A System -> Streaming Client + assertTrue(resubscribeLatch.await(15, TimeUnit.SECONDS), "Should receive COMPLETED event via resubscription"); + + // Verify no unexpected events or errors + assertFalse(wasUnexpectedEvent.get(), "Should not receive unexpected events"); + assertNull(errorRef.get(), "Should not receive errors during resubscription"); + + // Verify the received event + TaskStatusUpdateEvent completedEvent = receivedCompletedEvent.get(); + assertNotNull(completedEvent, "Should have received a TaskStatusUpdateEvent"); + assertEquals(TaskState.COMPLETED, completedEvent.getStatus().state(), "Event should show COMPLETED state"); + assertTrue(completedEvent.isFinal(), "Event should be marked as final"); + assertEquals(taskId, completedEvent.getTaskId(), "Event should have correct task ID"); + assertEquals(contextId, completedEvent.getContextId(), "Event should have correct context ID"); + + // Also verify via client API that the task state was updated + Task updatedTask = nonStreamingClient.getTask(new TaskQueryParams(taskId, null)); + assertNotNull(updatedTask, "Task should still exist"); + assertEquals(TaskState.COMPLETED, updatedTask.getStatus().state(), "Task should now show COMPLETED state after Kafka replication"); + + // Note: The replicated event goes to the local queue, but since we created the task + // and immediately sent a completion event, the task lifecycle might not reflect this + // in the client API. The important thing is that the replication system works. + } + +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java new file mode 100644 index 000000000..83642b183 --- /dev/null +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java @@ -0,0 +1,28 @@ +package io.a2a.extras.queuemanager.replicated.tests; + +import static org.junit.jupiter.api.Assertions.*; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; + +import io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager; +import io.a2a.server.events.QueueManager; +import io.quarkus.test.junit.QuarkusTest; + +/** + * Basic test to verify the ReplicatedQueueManager is properly configured. + * For full integration testing with Kafka replication, see KafkaReplicationIntegrationTest. + */ +@QuarkusTest +public class ReplicatedQueueManagerTest { + + @Inject + QueueManager queueManager; + + @Test + public void testReplicationSystemIsConfigured() { + // Verify that we're using the ReplicatedQueueManager + assertInstanceOf(ReplicatedQueueManager.class, queueManager); + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java new file mode 100644 index 000000000..a8596f05c --- /dev/null +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java @@ -0,0 +1,44 @@ +package io.a2a.extras.queuemanager.replicated.tests; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Produces the AgentCard for replicated queue manager integration tests. + */ +@IfBuildProfile("test") +@ApplicationScoped +public class ReplicationTestAgentCardProducer { + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + return new AgentCard.Builder() + .name("replication-test-agent") + .description("Test agent for replicated queue manager integration testing") + .url("http://localhost:8081") + .version("1.0.0") + .documentationUrl("http://localhost:8081/docs") + .preferredTransport(TransportProtocol.JSONRPC.asString()) + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(true) + .stateTransitionHistory(true) + .build()) + .defaultInputModes(List.of("text")) + .defaultOutputModes(List.of("text")) + .skills(List.of()) + .additionalInterfaces(List.of(new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) + .protocolVersion("0.2.5") + .build(); + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java new file mode 100644 index 000000000..556ceab49 --- /dev/null +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java @@ -0,0 +1,64 @@ +package io.a2a.extras.queuemanager.replicated.tests; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Test AgentExecutor for replicated queue manager integration testing. + * Handles different message types to trigger various events that should be replicated. + */ +@IfBuildProfile("test") +@ApplicationScoped +public class ReplicationTestAgentExecutor { + + @Produces + public AgentExecutor agentExecutor() { + return new AgentExecutor() { + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + String lastText = getLastTextPart(context.getMessage()); + + switch (lastText) { + case "create": + // Submit task - this should trigger TaskStatusUpdateEvent + taskUpdater.submit(); + break; + default: + throw new InvalidRequestError("Unknown command: " + lastText); + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); + taskUpdater.cancel(); + } + }; + } + + private String getLastTextPart(Message message) throws JSONRPCError { + if (message.getParts().isEmpty()) { + throw new InvalidRequestError("No parts in message"); + } + Part part = message.getParts().get(message.getParts().size() - 1); + if (part.getKind() == Part.Kind.TEXT) { + return ((TextPart) part).getText(); + } + throw new InvalidRequestError("Last part is not text"); + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java new file mode 100644 index 000000000..f82719733 --- /dev/null +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java @@ -0,0 +1,136 @@ +package io.a2a.extras.queuemanager.replicated.tests; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.reactive.messaging.Incoming; + +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.util.Utils; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * Test consumer for Kafka replicated events using reactive messaging. + * Uses a separate incoming channel to avoid interfering with the main application consumer. + */ +@IfBuildProfile("test") +@ApplicationScoped +public class TestKafkaEventConsumer { + + private final ConcurrentLinkedQueue receivedEvents = new ConcurrentLinkedQueue<>(); + private volatile CountDownLatch eventLatch; + + @Incoming("test-replicated-events-in") + public void onTestReplicatedEvent(String jsonMessage) { + try { + ReplicatedEvent event = Utils.OBJECT_MAPPER.readValue(jsonMessage, ReplicatedEvent.class); + receivedEvents.offer(event); + + // Signal any waiting threads + if (eventLatch != null) { + eventLatch.countDown(); + } + } catch (Exception e) { + // Log error but don't fail the message processing + System.err.println("Failed to process test Kafka message: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * Wait for an event matching the given task ID. + * @param taskId the task ID to wait for + * @param timeoutSeconds maximum time to wait + * @return the matching ReplicatedEvent, or null if timeout + */ + public ReplicatedEvent waitForEvent(String taskId, int timeoutSeconds) throws InterruptedException { + // Check if we already have the event + ReplicatedEvent existing = findEventByTaskId(taskId); + if (existing != null) { + return existing; + } + + // Set up latch to wait for new events + eventLatch = new CountDownLatch(1); + + try { + // Wait for new events + boolean received = eventLatch.await(timeoutSeconds, TimeUnit.SECONDS); + if (received) { + // Check again for the event + return findEventByTaskId(taskId); + } + return null; + } finally { + eventLatch = null; + } + } + + /** + * Wait for an event matching the given task ID and containing specific content. + * @param taskId the task ID to wait for + * @param contentMatch a string that must be present in the event + * @param timeoutSeconds maximum time to wait + * @return the matching ReplicatedEvent, or null if timeout + */ + public ReplicatedEvent waitForEventWithContent(String taskId, String contentMatch, int timeoutSeconds) throws InterruptedException { + // Check if we already have the event + ReplicatedEvent existing = findEventByTaskIdWithContent(taskId, contentMatch); + if (existing != null) { + return existing; + } + + // Set up latch to wait for new events + eventLatch = new CountDownLatch(1); + + try { + // Wait for new events + boolean received = eventLatch.await(timeoutSeconds, TimeUnit.SECONDS); + if (received) { + // Check again for the event + return findEventByTaskIdWithContent(taskId, contentMatch); + } + return null; + } finally { + eventLatch = null; + } + } + + /** + * Find an event by task ID in the received events. + */ + private ReplicatedEvent findEventByTaskId(String taskId) { + return receivedEvents.stream() + .filter(event -> taskId.equals(event.getTaskId())) + .findFirst() + .orElse(null); + } + + /** + * Find an event by task ID and content match in the received events. + */ + private ReplicatedEvent findEventByTaskIdWithContent(String taskId, String contentMatch) { + return receivedEvents.stream() + .filter(event -> taskId.equals(event.getTaskId()) && + event.getEvent().toString().contains(contentMatch)) + .findFirst() + .orElse(null); + } + + /** + * Clear all received events (useful for test cleanup). + */ + public void clear() { + receivedEvents.clear(); + } + + /** + * Get count of received events. + */ + public int getEventCount() { + return receivedEvents.size(); + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/resources/application.properties b/extras/queue-manager-replicated/tests/src/test/resources/application.properties new file mode 100644 index 000000000..69110ff3a --- /dev/null +++ b/extras/queue-manager-replicated/tests/src/test/resources/application.properties @@ -0,0 +1,29 @@ +# Select our ReplicatedQueueManager as the active implementation +quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager + +# Configure the outgoing channel (QueueManager -> Kafka) +mp.messaging.outgoing.replicated-events-out.connector=smallrye-kafka +mp.messaging.outgoing.replicated-events-out.topic=replicated-events +mp.messaging.outgoing.replicated-events-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer + +# Configure the incoming channel (Kafka -> QueueManager) +mp.messaging.incoming.replicated-events-in.connector=smallrye-kafka +mp.messaging.incoming.replicated-events-in.topic=replicated-events +mp.messaging.incoming.replicated-events-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer + +# Dev Services for Kafka will automatically start a broker for us +quarkus.kafka.devservices.enabled=true + +# Enhanced configuration for faster and more reliable tests +quarkus.kafka.devservices.topic-partitions."replicated-events"=1 +mp.messaging.incoming.replicated-events-in.auto.offset.reset=earliest + +# Test consumer channel - uses same topic but different consumer group +mp.messaging.incoming.test-replicated-events-in.connector=smallrye-kafka +mp.messaging.incoming.test-replicated-events-in.topic=replicated-events +mp.messaging.incoming.test-replicated-events-in.auto.offset.reset=earliest +mp.messaging.incoming.test-replicated-events-in.group.id=test-consumer-group + +# Reduce timeouts for faster tests +quarkus.messaging.kafka.health.timeout=5s + diff --git a/pom.xml b/pom.xml index 46938fc05..13171cfaf 100644 --- a/pom.xml +++ b/pom.xml @@ -392,6 +392,7 @@ examples/helloworld extras/task-store-database-jpa extras/push-notification-config-store-database-jpa + extras/queue-manager-replicated http-client reference/common reference/grpc diff --git a/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java b/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java new file mode 100644 index 000000000..81f860ce7 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java @@ -0,0 +1,7 @@ +package io.a2a.server.events; + +import io.a2a.spec.Event; + +public interface EventEnqueueHook { + void onEnqueue(Event event); +} \ No newline at end of file diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 48d4e6c79..3e7ede598 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -24,8 +24,6 @@ public abstract class EventQueue implements AutoCloseable { private final Semaphore semaphore; private volatile boolean closed = false; - - protected EventQueue() { this(DEFAULT_QUEUE_SIZE); } @@ -44,12 +42,31 @@ protected EventQueue(EventQueue parent) { LOGGER.trace("Creating {}, parent: {}", this, parent); } - public static EventQueue create() { - return new MainQueue(); + static EventQueueBuilder builder() { + return new EventQueueBuilder(); } - public static EventQueue create(int queueSize) { - return new MainQueue(queueSize); + public static class EventQueueBuilder { + private int queueSize = DEFAULT_QUEUE_SIZE; + private EventEnqueueHook hook; + + public EventQueueBuilder queueSize(int queueSize) { + this.queueSize = queueSize; + return this; + } + + public EventQueueBuilder hook(EventEnqueueHook hook) { + this.hook = hook; + return this; + } + + public EventQueue build() { + if (hook != null) { + return new MainQueue(queueSize, hook); + } else { + return new MainQueue(queueSize); + } + } } public int getQueueSize() { @@ -148,13 +165,26 @@ static class MainQueue extends EventQueue { private final List children = new CopyOnWriteArrayList<>(); private final CountDownLatch pollingStartedLatch = new CountDownLatch(1); private final AtomicBoolean pollingStarted = new AtomicBoolean(false); + private final EventEnqueueHook enqueueHook; MainQueue() { super(); + this.enqueueHook = null; } MainQueue(int queueSize) { super(queueSize); + this.enqueueHook = null; + } + + MainQueue(EventEnqueueHook hook) { + super(); + this.enqueueHook = hook; + } + + MainQueue(int queueSize, EventEnqueueHook hook) { + super(queueSize); + this.enqueueHook = hook; } EventQueue tap() { @@ -166,6 +196,9 @@ EventQueue tap() { public void enqueueEvent(Event event) { super.enqueueEvent(event); children.forEach(eq -> eq.internalEnqueueEvent(event)); + if (enqueueHook != null) { + enqueueHook.onEnqueue(event); + } } @Override diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueueFactory.java b/server-common/src/main/java/io/a2a/server/events/EventQueueFactory.java new file mode 100644 index 000000000..3f062e660 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/EventQueueFactory.java @@ -0,0 +1,12 @@ +package io.a2a.server.events; + +public interface EventQueueFactory { + /** + * Creates an EventQueueBuilder with the specified taskId context. + * This allows the factory to create queues with task-specific configuration. + * + * @param taskId the task ID for which the queue is being created + * @return an EventQueueBuilder configured for the specified task + */ + EventQueue.EventQueueBuilder builder(String taskId); +} \ No newline at end of file diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index c32178061..41dfac222 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -1,8 +1,5 @@ package io.a2a.server.events; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -11,6 +8,15 @@ @ApplicationScoped public class InMemoryQueueManager implements QueueManager { private final ConcurrentMap queues = new ConcurrentHashMap<>(); + private final EventQueueFactory factory; + + public InMemoryQueueManager() { + this.factory = new DefaultEventQueueFactory(); + } + + public InMemoryQueueManager(EventQueueFactory factory) { + this.factory = factory; + } @Override public void add(String taskId, EventQueue queue) { @@ -45,7 +51,9 @@ public EventQueue createOrTap(String taskId) { EventQueue existing = queues.get(taskId); EventQueue newQueue = null; if (existing == null) { - newQueue = EventQueue.create(); + // Use builder pattern for cleaner queue creation + // Use the new taskId-aware builder method if available + newQueue = factory.builder(taskId).build(); // Make sure an existing queue has not been added in the meantime existing = queues.putIfAbsent(taskId, newQueue); } @@ -56,4 +64,12 @@ public EventQueue createOrTap(String taskId) { public void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedException { eventQueue.awaitQueuePollerStart(); } + + private static class DefaultEventQueueFactory implements EventQueueFactory { + @Override + public EventQueue.EventQueueBuilder builder(String taskId) { + // Default implementation doesn't need task-specific configuration + return EventQueue.builder(); + } + } } diff --git a/server-common/src/main/java/io/a2a/server/events/QueueManager.java b/server-common/src/main/java/io/a2a/server/events/QueueManager.java index ddba13cb8..ebdf67e74 100644 --- a/server-common/src/main/java/io/a2a/server/events/QueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/QueueManager.java @@ -12,4 +12,8 @@ public interface QueueManager { EventQueue createOrTap(String taskId); void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedException; + + default EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { + return EventQueue.builder(); + } } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 8bace7be2..0b1acc7e8 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -132,7 +132,7 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws EventQueue queue = queueManager.tap(task.getId()); if (queue == null) { - queue = EventQueue.create(); + queue = queueManager.getEventQueueBuilder(task.getId()).build(); } agentExecutor.cancel( requestContextBuilder.get() diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 94dbd8db9..c6a12d737 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -57,7 +57,7 @@ public class EventConsumerTest { @BeforeEach public void init() { - eventQueue = EventQueue.create(); + eventQueue = EventQueue.builder().build(); eventConsumer = new EventConsumer(eventQueue); } @@ -343,7 +343,7 @@ public void onComplete() { @Test public void testConsumeAllStopsOnQueueClosed() throws Exception { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); EventConsumer consumer = new EventConsumer(queue); // Close the queue immediately @@ -389,7 +389,7 @@ public void onComplete() { @Test public void testConsumeAllHandlesQueueClosedException() throws Exception { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); EventConsumer consumer = new EventConsumer(queue); // Add a message event (which will complete the stream) diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 14f3c1346..67757cc69 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -51,35 +51,35 @@ public class EventQueueTest { @BeforeEach public void init() { - eventQueue = EventQueue.create(); + eventQueue = EventQueue.builder().build(); } @Test public void testConstructorDefaultQueueSize() { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); assertEquals(EventQueue.DEFAULT_QUEUE_SIZE, queue.getQueueSize()); } @Test public void testConstructorCustomQueueSize() { int customSize = 500; - EventQueue queue = EventQueue.create(customSize); + EventQueue queue = EventQueue.builder().queueSize(customSize).build(); assertEquals(customSize, queue.getQueueSize()); } @Test public void testConstructorInvalidQueueSize() { // Test zero queue size - assertThrows(IllegalArgumentException.class, () -> EventQueue.create(0)); + assertThrows(IllegalArgumentException.class, () -> EventQueue.builder().queueSize(0).build()); // Test negative queue size - assertThrows(IllegalArgumentException.class, () -> EventQueue.create(-10)); + assertThrows(IllegalArgumentException.class, () -> EventQueue.builder().queueSize(-10).build()); } @Test public void testTapCreatesChildQueue() { - EventQueue parentQueue = EventQueue.create(); + EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue = parentQueue.tap(); assertNotNull(childQueue); @@ -89,7 +89,7 @@ public void testTapCreatesChildQueue() { @Test public void testTapOnChildQueueThrowsException() { - EventQueue parentQueue = EventQueue.create(); + EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue = parentQueue.tap(); assertThrows(IllegalStateException.class, () -> childQueue.tap()); @@ -97,7 +97,7 @@ public void testTapOnChildQueueThrowsException() { @Test public void testEnqueueEventPropagagesToChildren() throws Exception { - EventQueue parentQueue = EventQueue.create(); + EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue = parentQueue.tap(); Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); @@ -113,7 +113,7 @@ public void testEnqueueEventPropagagesToChildren() throws Exception { @Test public void testMultipleChildQueuesReceiveEvents() throws Exception { - EventQueue parentQueue = EventQueue.create(); + EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue1 = parentQueue.tap(); EventQueue childQueue2 = parentQueue.tap(); @@ -136,7 +136,7 @@ public void testMultipleChildQueuesReceiveEvents() throws Exception { @Test public void testChildQueueDequeueIndependently() throws Exception { - EventQueue parentQueue = EventQueue.create(); + EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue1 = parentQueue.tap(); EventQueue childQueue2 = parentQueue.tap(); @@ -159,7 +159,7 @@ public void testChildQueueDequeueIndependently() throws Exception { @Test public void testCloseImmediatePropagationToChildren() throws Exception { - EventQueue parentQueue = EventQueue.create(); + EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue = parentQueue.tap(); // Add events to both parent and child @@ -189,7 +189,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { @Test public void testEnqueueEventWhenClosed() throws Exception { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); queue.close(); // Close the queue first @@ -205,7 +205,7 @@ public void testEnqueueEventWhenClosed() throws Exception { @Test public void testDequeueEventWhenClosedAndEmpty() throws Exception { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); queue.close(); assertTrue(queue.isClosed()); @@ -215,7 +215,7 @@ public void testDequeueEventWhenClosedAndEmpty() throws Exception { @Test public void testDequeueEventWhenClosedButHasEvents() throws Exception { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); queue.enqueueEvent(event); @@ -345,7 +345,7 @@ public void testCloseIdempotent() throws Exception { assertTrue(eventQueue.isClosed()); // Test with immediate close as well - EventQueue eventQueue2 = EventQueue.create(); + EventQueue eventQueue2 = EventQueue.builder().build(); eventQueue2.close(true); assertTrue(eventQueue2.isClosed()); diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java b/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java new file mode 100644 index 000000000..39201c1f6 --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java @@ -0,0 +1,8 @@ +package io.a2a.server.events; + +public class EventQueueUtil { + // Since EventQueue.builder() is package protected, add a method to expose it + public static EventQueue.EventQueueBuilder getEventQueueBuilder() { + return EventQueue.builder(); + } +} diff --git a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java index 3586d318d..a1f4e197a 100644 --- a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java @@ -29,7 +29,7 @@ public void setUp() { @Test public void testAddNewQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); queueManager.add(taskId, queue); @@ -40,8 +40,8 @@ public void testAddNewQueue() { @Test public void testAddExistingQueueThrowsException() { String taskId = "test_task_id"; - EventQueue queue1 = EventQueue.create(); - EventQueue queue2 = EventQueue.create(); + EventQueue queue1 = EventQueue.builder().build(); + EventQueue queue2 = EventQueue.builder().build(); queueManager.add(taskId, queue1); @@ -53,7 +53,7 @@ public void testAddExistingQueueThrowsException() { @Test public void testGetExistingQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); queueManager.add(taskId, queue); EventQueue result = queueManager.get(taskId); @@ -70,7 +70,7 @@ public void testGetNonexistentQueue() { @Test public void testTapExistingQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); queueManager.add(taskId, queue); EventQueue tappedQueue = queueManager.tap(taskId); @@ -91,7 +91,7 @@ public void testTapNonexistentQueue() { @Test public void testCloseExistingQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); queueManager.add(taskId, queue); queueManager.close(taskId); @@ -123,7 +123,7 @@ public void testCreateOrTapNewQueue() { @Test public void testCreateOrTapExistingQueue() { String taskId = "test_task_id"; - EventQueue originalQueue = EventQueue.create(); + EventQueue originalQueue = EventQueue.builder().build(); queueManager.add(taskId, originalQueue); EventQueue result = queueManager.createOrTap(taskId); @@ -145,7 +145,7 @@ public void testConcurrentOperations() throws InterruptedException, ExecutionExc // Add tasks concurrently List> addFutures = taskIds.stream() .map(taskId -> CompletableFuture.supplyAsync(() -> { - EventQueue queue = EventQueue.create(); + EventQueue queue = EventQueue.builder().build(); queueManager.add(taskId, queue); return taskId; })) diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 2e6404594..c18dc415b 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -14,6 +14,7 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueUtil; import io.a2a.spec.Event; import io.a2a.spec.Message; import io.a2a.spec.Part; @@ -44,7 +45,7 @@ public class TaskUpdaterTest { @BeforeEach public void init() { - eventQueue = EventQueue.create(); + eventQueue = EventQueueUtil.getEventQueueBuilder().build(); RequestContext context = new RequestContext.Builder() .setTaskId(TEST_TASK_ID) .setContextId(TEST_TASK_CONTEXT_ID) From 0165e0cf8dac2885c9b89a819eaeb4e10c5b2599 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 2 Oct 2025 04:56:53 -0400 Subject: [PATCH 146/493] fix: Ensure proper errors are reported when authentication fails (#318) --- client/base/pom.xml | 15 + .../AuthenticationAuthorizationTest.java | 395 ++++++++++++++++++ .../transport/grpc/GrpcErrorMapper.java | 5 + .../java/io/a2a/common/A2AErrorMessages.java | 11 + .../io/a2a/client/http/JdkA2AHttpClient.java | 91 +++- .../transport/grpc/handler/GrpcHandler.java | 47 ++- 6 files changed, 554 insertions(+), 10 deletions(-) create mode 100644 client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java create mode 100644 common/src/main/java/io/a2a/common/A2AErrorMessages.java diff --git a/client/base/pom.xml b/client/base/pom.xml index c7cfa51cc..0bdda1e0b 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -48,6 +48,11 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-spec-grpc + test + org.junit.jupiter junit-jupiter-api @@ -64,6 +69,16 @@ slf4j-jdk14 test + + io.grpc + grpc-testing + test + + + io.grpc + grpc-inprocess + test + \ No newline at end of file diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java new file mode 100644 index 000000000..a0a9b2ff4 --- /dev/null +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -0,0 +1,395 @@ +package io.a2a.client; + +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.grpc.GrpcTransport; +import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.client.transport.rest.RestTransport; +import io.a2a.client.transport.rest.RestTransportConfigBuilder; +import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.AgentSkill; +import io.a2a.spec.Message; +import io.a2a.spec.TextPart; +import io.a2a.spec.TransportProtocol; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +/** + * Tests for handling HTTP 401 (Unauthorized) and 403 (Forbidden) responses + * when the client sends streaming and non-streaming messages. + * + * These tests verify that the client properly fails when the server returns + * authentication or authorization errors. + */ +public class AuthenticationAuthorizationTest { + + private static final String AGENT_URL = "http://localhost:4001"; + private static final String AUTHENTICATION_FAILED_MESSAGE = "Authentication failed"; + private static final String AUTHORIZATION_FAILED_MESSAGE = "Authorization failed"; + + private ClientAndServer server; + private Message MESSAGE; + private AgentCard agentCard; + private Server grpcServer; + private ManagedChannel grpcChannel; + private String grpcServerName; + + @BeforeEach + public void setUp() { + server = new ClientAndServer(4001); + MESSAGE = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("test message"))) + .contextId("context-1234") + .messageId("message-1234") + .build(); + + grpcServerName = InProcessServerBuilder.generateName(); + + agentCard = new AgentCard.Builder() + .name("Test Agent") + .description("Test agent for auth tests") + .url(AGENT_URL) + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) // Support streaming for all tests + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("test_skill") + .name("Test skill") + .description("Test skill") + .tags(Collections.singletonList("test")) + .build())) + .protocolVersion("0.3.0") + .additionalInterfaces(java.util.Arrays.asList( + new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL), + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL), + new AgentInterface(TransportProtocol.GRPC.asString(), grpcServerName))) + .build(); + } + + @AfterEach + public void tearDown() { + server.stop(); + if (grpcChannel != null) { + grpcChannel.shutdownNow(); + } + if (grpcServer != null) { + grpcServer.shutdownNow(); + } + } + + // ========== JSON-RPC Transport Tests ========== + + @Test + public void testJsonRpcNonStreamingUnauthenticated() throws A2AClientException { + // Mock server to return 401 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(401) + ); + + Client client = Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + IOException exception = assertThrows(IOException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); + } + + @Test + public void testJsonRpcNonStreamingUnauthorized() throws A2AClientException { + // Mock server to return 403 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(403) + ); + + Client client = Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + IOException exception = assertThrows(IOException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); + } + + @Test + public void testJsonRpcStreamingUnauthenticated() throws Exception { + // Mock server to return 401 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(401) + ); + + assertStreamingError( + Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()), + AUTHENTICATION_FAILED_MESSAGE); + } + + @Test + public void testJsonRpcStreamingUnauthorized() throws Exception { + // Mock server to return 403 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(403) + ); + + assertStreamingError( + Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()), + AUTHORIZATION_FAILED_MESSAGE); + } + + // ========== REST Transport Tests ========== + + @Test + public void testRestNonStreamingUnauthenticated() throws A2AClientException { + // Mock server to return 401 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:send") + ).respond( + response() + .withStatusCode(401) + ); + + Client client = Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) + .withTransport(RestTransport.class, new RestTransportConfigBuilder()) + .build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); + } + + @Test + public void testRestNonStreamingUnauthorized() throws A2AClientException { + // Mock server to return 403 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:send") + ).respond( + response() + .withStatusCode(403) + ); + + Client client = Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) + .withTransport(RestTransport.class, new RestTransportConfigBuilder()) + .build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); + } + + @Test + public void testRestStreamingUnauthenticated() throws Exception { + // Mock server to return 401 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:stream") + ).respond( + response() + .withStatusCode(401) + ); + + assertStreamingError( + Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) + .withTransport(RestTransport.class, new RestTransportConfigBuilder()), + AUTHENTICATION_FAILED_MESSAGE); + } + + @Test + public void testRestStreamingUnauthorized() throws Exception { + // Mock server to return 403 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:stream") + ).respond( + response() + .withStatusCode(403) + ); + + assertStreamingError( + Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) + .withTransport(RestTransport.class, new RestTransportConfigBuilder()), + AUTHORIZATION_FAILED_MESSAGE); + } + + // ========== gRPC Transport Tests ========== + + @Test + public void testGrpcNonStreamingUnauthenticated() throws Exception { + setupGrpcServer(Status.UNAUTHENTICATED); + + Client client = Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) + .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() + .channelFactory(target -> grpcChannel)) + .build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); + } + + @Test + public void testGrpcNonStreamingUnauthorized() throws Exception { + setupGrpcServer(Status.PERMISSION_DENIED); + + Client client = Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) + .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() + .channelFactory(target -> grpcChannel)) + .build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); + } + + @Test + public void testGrpcStreamingUnauthenticated() throws Exception { + setupGrpcServer(Status.UNAUTHENTICATED); + + assertStreamingError( + Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) + .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() + .channelFactory(target -> grpcChannel)), + AUTHENTICATION_FAILED_MESSAGE); + } + + @Test + public void testGrpcStreamingUnauthorized() throws Exception { + setupGrpcServer(Status.PERMISSION_DENIED); + + assertStreamingError( + Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) + .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() + .channelFactory(target -> grpcChannel)), + AUTHORIZATION_FAILED_MESSAGE); + } + + private void assertStreamingError(ClientBuilder clientBuilder, String expectedErrorMessage) throws Exception { + AtomicReference errorRef = new AtomicReference<>(); + CountDownLatch errorLatch = new CountDownLatch(1); + + Consumer errorHandler = error -> { + errorRef.set(error); + errorLatch.countDown(); + }; + + Client client = clientBuilder.streamingErrorHandler(errorHandler).build(); + + try { + client.sendMessage(MESSAGE); + // If no immediate exception, wait for async error + assertTrue(errorLatch.await(5, TimeUnit.SECONDS), "Expected error handler to be called"); + Throwable error = errorRef.get(); + assertTrue(error.getMessage().contains(expectedErrorMessage), + "Expected error message to contain '" + expectedErrorMessage + "' but got: " + error.getMessage()); + } catch (Exception e) { + // Immediate exception is also acceptable + assertTrue(e.getMessage().contains(expectedErrorMessage), + "Expected error message to contain '" + expectedErrorMessage + "' but got: " + e.getMessage()); + } + } + + private void setupGrpcServer(Status status) throws IOException { + grpcServerName = InProcessServerBuilder.generateName(); + grpcServer = InProcessServerBuilder.forName(grpcServerName) + .directExecutor() + .addService(new A2AServiceGrpc.A2AServiceImplBase() { + @Override + public void sendMessage(SendMessageRequest request, StreamObserver responseObserver) { + responseObserver.onError(status.asRuntimeException()); + } + + @Override + public void sendStreamingMessage(SendMessageRequest request, StreamObserver responseObserver) { + responseObserver.onError(status.asRuntimeException()); + } + }) + .build() + .start(); + + grpcChannel = InProcessChannelBuilder.forName(grpcServerName) + .directExecutor() + .build(); + } +} \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java index 7340f7cef..5f0db8f0f 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java @@ -1,5 +1,6 @@ package io.a2a.client.transport.grpc; +import io.a2a.common.A2AErrorMessages; import io.a2a.spec.A2AClientException; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.InvalidAgentResponseError; @@ -64,6 +65,10 @@ public static A2AClientException mapGrpcError(StatusRuntimeException e, String e return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new InvalidParamsError()); case INTERNAL: return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new io.a2a.spec.InternalError(null, e.getMessage(), null)); + case UNAUTHENTICATED: + return new A2AClientException(errorPrefix + A2AErrorMessages.AUTHENTICATION_FAILED); + case PERMISSION_DENIED: + return new A2AClientException(errorPrefix + A2AErrorMessages.AUTHORIZATION_FAILED); default: return new A2AClientException(errorPrefix + e.getMessage(), e); } diff --git a/common/src/main/java/io/a2a/common/A2AErrorMessages.java b/common/src/main/java/io/a2a/common/A2AErrorMessages.java new file mode 100644 index 000000000..22b587d84 --- /dev/null +++ b/common/src/main/java/io/a2a/common/A2AErrorMessages.java @@ -0,0 +1,11 @@ +package io.a2a.common; + +public final class A2AErrorMessages { + + private A2AErrorMessages() { + // prevent instantiation + } + + public static final String AUTHENTICATION_FAILED = "Authentication failed: Client credentials are missing or invalid"; + public static final String AUTHORIZATION_FAILED = "Authorization failed: Client does not have permission for the operation"; +} diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index abcecc8ed..754cd8919 100644 --- a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -7,15 +7,26 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodyHandlers; +import java.net.http.HttpResponse.BodySubscribers; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import java.util.function.Consumer; +import io.a2a.common.A2AErrorMessages; +import io.a2a.spec.A2AClientException; + public class JdkA2AHttpClient implements A2AHttpClient { + private static final int HTTP_OK = 200; + private static final int HTTP_MULTIPLE_CHOICES = 300; + private static final int HTTP_UNAUTHORIZED = 401; + private static final int HTTP_FORBIDDEN = 403; + private final HttpClient httpClient; public JdkA2AHttpClient() { @@ -88,6 +99,7 @@ protected CompletableFuture asyncRequest( ) { Flow.Subscriber subscriber = new Flow.Subscriber() { private Flow.Subscription subscription; + private volatile boolean errorRaised = false; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -109,25 +121,75 @@ public void onNext(String item) { @Override public void onError(Throwable throwable) { - errorConsumer.accept(throwable); - subscription.cancel(); + if (!errorRaised) { + errorRaised = true; + errorConsumer.accept(throwable); + } + if (subscription != null) { + subscription.cancel(); + } } @Override public void onComplete() { - completeRunnable.run(); - subscription.cancel(); + if (!errorRaised) { + completeRunnable.run(); + } + if (subscription != null) { + subscription.cancel(); + } } }; - BodyHandler bodyHandler = BodyHandlers.fromLineSubscriber(subscriber); + // Create a custom body handler that checks status before processing body + BodyHandler bodyHandler = responseInfo -> { + // Check status code first, before creating the subscriber + if (!isSuccessStatus(responseInfo.statusCode())) { + final String errorMessage; + if (responseInfo.statusCode() == HTTP_UNAUTHORIZED) { + errorMessage = A2AErrorMessages.AUTHENTICATION_FAILED; + } else if (responseInfo.statusCode() == HTTP_FORBIDDEN) { + errorMessage = A2AErrorMessages.AUTHORIZATION_FAILED; + } else { + errorMessage = "Request failed with status " + responseInfo.statusCode(); + } + // Return a body subscriber that immediately signals error + return BodySubscribers.fromSubscriber(new Flow.Subscriber>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscriber.onError(new IOException(errorMessage)); + } + + @Override + public void onNext(List item) { + // Should not be called + } + + @Override + public void onError(Throwable throwable) { + // Should not be called + } + + @Override + public void onComplete() { + // Should not be called + } + }); + } else { + // Status is OK, proceed with normal line subscriber + return BodyHandlers.fromLineSubscriber(subscriber).apply(responseInfo); + } + }; // Send the response async, and let the subscriber handle the lines. return httpClient.sendAsync(request, bodyHandler) .thenAccept(response -> { - if (!JdkHttpResponse.success(response)) { - subscriber.onError(new IOException("Request failed " + response.statusCode())); - } + // Status checking is now handled in the body handler + }) + .exceptionally(throwable -> { + // handle any other async errors (network issues, etc.) + subscriber.onError(new IOException("Request failed: " + throwable.getMessage(), throwable)); + return null; }); } } @@ -200,6 +262,13 @@ public A2AHttpResponse post() throws IOException, InterruptedException { .build(); HttpResponse response = httpClient.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); + + if (response.statusCode() == HTTP_UNAUTHORIZED) { + throw new IOException(A2AErrorMessages.AUTHENTICATION_FAILED); + } else if (response.statusCode() == HTTP_FORBIDDEN) { + throw new IOException(A2AErrorMessages.AUTHORIZATION_FAILED); + } + return new JdkHttpResponse(response); } @@ -227,7 +296,7 @@ public boolean success() {// Send the request and get the response } static boolean success(HttpResponse response) { - return response.statusCode() >= 200 && response.statusCode() < 300; + return response.statusCode() >= HTTP_OK && response.statusCode() < HTTP_MULTIPLE_CHOICES; } @Override @@ -235,4 +304,8 @@ public String body() { return response.body(); } } + + private static boolean isSuccessStatus(int statusCode) { + return statusCode >= HTTP_OK && statusCode < HTTP_MULTIPLE_CHOICES; + } } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index b259e91ab..57dc5ae58 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -18,6 +18,7 @@ import java.util.logging.Logger; import com.google.protobuf.Empty; +import io.a2a.common.A2AErrorMessages; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.StreamResponse; import io.a2a.server.AgentCardValidator; @@ -80,6 +81,8 @@ public void sendMessage(io.a2a.grpc.SendMessageRequest request, responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -100,6 +103,8 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, } } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -120,6 +125,8 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, } } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -141,6 +148,8 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -162,6 +171,8 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -179,7 +190,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC ServerCallContext context = createCallContext(responseObserver); ListTaskPushNotificationConfigParams params = FromProto.listTaskPushNotificationConfigParams(request); List configList = getRequestHandler().onListTaskPushNotificationConfig(params, context); - io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); for (TaskPushNotificationConfig config : configList) { responseBuilder.addConfigs(ToProto.taskPushNotificationConfig(config)); @@ -188,6 +199,8 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -208,6 +221,8 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -228,6 +243,8 @@ public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -308,6 +325,8 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -413,6 +432,32 @@ private void handleError(StreamObserver responseObserver, JSONRPCError er responseObserver.onError(status.withDescription(description).asRuntimeException()); } + private void handleSecurityException(StreamObserver responseObserver, SecurityException e) { + Status status; + String description; + + String exceptionClassName = e.getClass().getName(); + + // Attempt to detect common authentication and authorization related exceptions + if (exceptionClassName.contains("Unauthorized") || + exceptionClassName.contains("Unauthenticated") || + exceptionClassName.contains("Authentication")) { + status = Status.UNAUTHENTICATED; + description = A2AErrorMessages.AUTHENTICATION_FAILED; + } else if (exceptionClassName.contains("Forbidden") || + exceptionClassName.contains("AccessDenied") || + exceptionClassName.contains("Authorization")) { + status = Status.PERMISSION_DENIED; + description = A2AErrorMessages.AUTHORIZATION_FAILED; + } else { + // If the security exception type cannot be detected, default to PERMISSION_DENIED + status = Status.PERMISSION_DENIED; + description = "Authorization failed: " + (e.getMessage() != null ? e.getMessage() : "Access denied"); + } + + responseObserver.onError(status.withDescription(description).asRuntimeException()); + } + private void handleInternalError(StreamObserver responseObserver, Throwable t) { handleError(responseObserver, new InternalError(t.getMessage())); } From 8815fbaf9752894992808ba96d904a55eae14936 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 2 Oct 2025 12:26:27 +0100 Subject: [PATCH 147/493] =?UTF-8?q?chore:=20Revert=20"fix:=20Ensure=20prop?= =?UTF-8?q?er=20errors=20are=20reported=20when=20authentication=20fa?= =?UTF-8?q?=E2=80=A6=20(#325)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …ils (#318)" This reverts commit 0165e0cf8dac2885c9b89a819eaeb4e10c5b2599 from #318 since we started seeing failures on CI --- client/base/pom.xml | 15 - .../AuthenticationAuthorizationTest.java | 395 ------------------ .../transport/grpc/GrpcErrorMapper.java | 5 - .../java/io/a2a/common/A2AErrorMessages.java | 11 - .../io/a2a/client/http/JdkA2AHttpClient.java | 91 +--- .../transport/grpc/handler/GrpcHandler.java | 47 +-- 6 files changed, 10 insertions(+), 554 deletions(-) delete mode 100644 client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java delete mode 100644 common/src/main/java/io/a2a/common/A2AErrorMessages.java diff --git a/client/base/pom.xml b/client/base/pom.xml index 0bdda1e0b..c7cfa51cc 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -48,11 +48,6 @@ ${project.groupId} a2a-java-sdk-spec - - ${project.groupId} - a2a-java-sdk-spec-grpc - test - org.junit.jupiter junit-jupiter-api @@ -69,16 +64,6 @@ slf4j-jdk14 test - - io.grpc - grpc-testing - test - - - io.grpc - grpc-inprocess - test - \ No newline at end of file diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java deleted file mode 100644 index a0a9b2ff4..000000000 --- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java +++ /dev/null @@ -1,395 +0,0 @@ -package io.a2a.client; - -import io.a2a.client.config.ClientConfig; -import io.a2a.client.transport.grpc.GrpcTransport; -import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; -import io.a2a.client.transport.jsonrpc.JSONRPCTransport; -import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; -import io.a2a.client.transport.rest.RestTransport; -import io.a2a.client.transport.rest.RestTransportConfigBuilder; -import io.a2a.grpc.A2AServiceGrpc; -import io.a2a.grpc.SendMessageRequest; -import io.a2a.grpc.SendMessageResponse; -import io.a2a.grpc.StreamResponse; -import io.a2a.spec.A2AClientException; -import io.a2a.spec.AgentCapabilities; -import io.a2a.spec.AgentCard; -import io.a2a.spec.AgentInterface; -import io.a2a.spec.AgentSkill; -import io.a2a.spec.Message; -import io.a2a.spec.TextPart; -import io.a2a.spec.TransportProtocol; -import io.grpc.ManagedChannel; -import io.grpc.Server; -import io.grpc.Status; -import io.grpc.inprocess.InProcessChannelBuilder; -import io.grpc.inprocess.InProcessServerBuilder; -import io.grpc.stub.StreamObserver; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockserver.integration.ClientAndServer; - -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; - -/** - * Tests for handling HTTP 401 (Unauthorized) and 403 (Forbidden) responses - * when the client sends streaming and non-streaming messages. - * - * These tests verify that the client properly fails when the server returns - * authentication or authorization errors. - */ -public class AuthenticationAuthorizationTest { - - private static final String AGENT_URL = "http://localhost:4001"; - private static final String AUTHENTICATION_FAILED_MESSAGE = "Authentication failed"; - private static final String AUTHORIZATION_FAILED_MESSAGE = "Authorization failed"; - - private ClientAndServer server; - private Message MESSAGE; - private AgentCard agentCard; - private Server grpcServer; - private ManagedChannel grpcChannel; - private String grpcServerName; - - @BeforeEach - public void setUp() { - server = new ClientAndServer(4001); - MESSAGE = new Message.Builder() - .role(Message.Role.USER) - .parts(Collections.singletonList(new TextPart("test message"))) - .contextId("context-1234") - .messageId("message-1234") - .build(); - - grpcServerName = InProcessServerBuilder.generateName(); - - agentCard = new AgentCard.Builder() - .name("Test Agent") - .description("Test agent for auth tests") - .url(AGENT_URL) - .version("1.0.0") - .capabilities(new AgentCapabilities.Builder() - .streaming(true) // Support streaming for all tests - .build()) - .defaultInputModes(Collections.singletonList("text")) - .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() - .id("test_skill") - .name("Test skill") - .description("Test skill") - .tags(Collections.singletonList("test")) - .build())) - .protocolVersion("0.3.0") - .additionalInterfaces(java.util.Arrays.asList( - new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL), - new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL), - new AgentInterface(TransportProtocol.GRPC.asString(), grpcServerName))) - .build(); - } - - @AfterEach - public void tearDown() { - server.stop(); - if (grpcChannel != null) { - grpcChannel.shutdownNow(); - } - if (grpcServer != null) { - grpcServer.shutdownNow(); - } - } - - // ========== JSON-RPC Transport Tests ========== - - @Test - public void testJsonRpcNonStreamingUnauthenticated() throws A2AClientException { - // Mock server to return 401 for non-streaming message - server.when( - request() - .withMethod("POST") - .withPath("/") - ).respond( - response() - .withStatusCode(401) - ); - - Client client = Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) - .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) - .build(); - - IOException exception = assertThrows(IOException.class, () -> { - client.sendMessage(MESSAGE); - }); - - assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); - } - - @Test - public void testJsonRpcNonStreamingUnauthorized() throws A2AClientException { - // Mock server to return 403 for non-streaming message - server.when( - request() - .withMethod("POST") - .withPath("/") - ).respond( - response() - .withStatusCode(403) - ); - - Client client = Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) - .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) - .build(); - - IOException exception = assertThrows(IOException.class, () -> { - client.sendMessage(MESSAGE); - }); - - assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); - } - - @Test - public void testJsonRpcStreamingUnauthenticated() throws Exception { - // Mock server to return 401 for streaming message - server.when( - request() - .withMethod("POST") - .withPath("/") - ).respond( - response() - .withStatusCode(401) - ); - - assertStreamingError( - Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) - .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()), - AUTHENTICATION_FAILED_MESSAGE); - } - - @Test - public void testJsonRpcStreamingUnauthorized() throws Exception { - // Mock server to return 403 for streaming message - server.when( - request() - .withMethod("POST") - .withPath("/") - ).respond( - response() - .withStatusCode(403) - ); - - assertStreamingError( - Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) - .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()), - AUTHORIZATION_FAILED_MESSAGE); - } - - // ========== REST Transport Tests ========== - - @Test - public void testRestNonStreamingUnauthenticated() throws A2AClientException { - // Mock server to return 401 for non-streaming message - server.when( - request() - .withMethod("POST") - .withPath("/v1/message:send") - ).respond( - response() - .withStatusCode(401) - ); - - Client client = Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) - .withTransport(RestTransport.class, new RestTransportConfigBuilder()) - .build(); - - A2AClientException exception = assertThrows(A2AClientException.class, () -> { - client.sendMessage(MESSAGE); - }); - - assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); - } - - @Test - public void testRestNonStreamingUnauthorized() throws A2AClientException { - // Mock server to return 403 for non-streaming message - server.when( - request() - .withMethod("POST") - .withPath("/v1/message:send") - ).respond( - response() - .withStatusCode(403) - ); - - Client client = Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) - .withTransport(RestTransport.class, new RestTransportConfigBuilder()) - .build(); - - A2AClientException exception = assertThrows(A2AClientException.class, () -> { - client.sendMessage(MESSAGE); - }); - - assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); - } - - @Test - public void testRestStreamingUnauthenticated() throws Exception { - // Mock server to return 401 for streaming message - server.when( - request() - .withMethod("POST") - .withPath("/v1/message:stream") - ).respond( - response() - .withStatusCode(401) - ); - - assertStreamingError( - Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) - .withTransport(RestTransport.class, new RestTransportConfigBuilder()), - AUTHENTICATION_FAILED_MESSAGE); - } - - @Test - public void testRestStreamingUnauthorized() throws Exception { - // Mock server to return 403 for streaming message - server.when( - request() - .withMethod("POST") - .withPath("/v1/message:stream") - ).respond( - response() - .withStatusCode(403) - ); - - assertStreamingError( - Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) - .withTransport(RestTransport.class, new RestTransportConfigBuilder()), - AUTHORIZATION_FAILED_MESSAGE); - } - - // ========== gRPC Transport Tests ========== - - @Test - public void testGrpcNonStreamingUnauthenticated() throws Exception { - setupGrpcServer(Status.UNAUTHENTICATED); - - Client client = Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) - .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() - .channelFactory(target -> grpcChannel)) - .build(); - - A2AClientException exception = assertThrows(A2AClientException.class, () -> { - client.sendMessage(MESSAGE); - }); - - assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); - } - - @Test - public void testGrpcNonStreamingUnauthorized() throws Exception { - setupGrpcServer(Status.PERMISSION_DENIED); - - Client client = Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(false).build()) - .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() - .channelFactory(target -> grpcChannel)) - .build(); - - A2AClientException exception = assertThrows(A2AClientException.class, () -> { - client.sendMessage(MESSAGE); - }); - - assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); - } - - @Test - public void testGrpcStreamingUnauthenticated() throws Exception { - setupGrpcServer(Status.UNAUTHENTICATED); - - assertStreamingError( - Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) - .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() - .channelFactory(target -> grpcChannel)), - AUTHENTICATION_FAILED_MESSAGE); - } - - @Test - public void testGrpcStreamingUnauthorized() throws Exception { - setupGrpcServer(Status.PERMISSION_DENIED); - - assertStreamingError( - Client.builder(agentCard) - .clientConfig(new ClientConfig.Builder().setStreaming(true).build()) - .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() - .channelFactory(target -> grpcChannel)), - AUTHORIZATION_FAILED_MESSAGE); - } - - private void assertStreamingError(ClientBuilder clientBuilder, String expectedErrorMessage) throws Exception { - AtomicReference errorRef = new AtomicReference<>(); - CountDownLatch errorLatch = new CountDownLatch(1); - - Consumer errorHandler = error -> { - errorRef.set(error); - errorLatch.countDown(); - }; - - Client client = clientBuilder.streamingErrorHandler(errorHandler).build(); - - try { - client.sendMessage(MESSAGE); - // If no immediate exception, wait for async error - assertTrue(errorLatch.await(5, TimeUnit.SECONDS), "Expected error handler to be called"); - Throwable error = errorRef.get(); - assertTrue(error.getMessage().contains(expectedErrorMessage), - "Expected error message to contain '" + expectedErrorMessage + "' but got: " + error.getMessage()); - } catch (Exception e) { - // Immediate exception is also acceptable - assertTrue(e.getMessage().contains(expectedErrorMessage), - "Expected error message to contain '" + expectedErrorMessage + "' but got: " + e.getMessage()); - } - } - - private void setupGrpcServer(Status status) throws IOException { - grpcServerName = InProcessServerBuilder.generateName(); - grpcServer = InProcessServerBuilder.forName(grpcServerName) - .directExecutor() - .addService(new A2AServiceGrpc.A2AServiceImplBase() { - @Override - public void sendMessage(SendMessageRequest request, StreamObserver responseObserver) { - responseObserver.onError(status.asRuntimeException()); - } - - @Override - public void sendStreamingMessage(SendMessageRequest request, StreamObserver responseObserver) { - responseObserver.onError(status.asRuntimeException()); - } - }) - .build() - .start(); - - grpcChannel = InProcessChannelBuilder.forName(grpcServerName) - .directExecutor() - .build(); - } -} \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java index 5f0db8f0f..7340f7cef 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java @@ -1,6 +1,5 @@ package io.a2a.client.transport.grpc; -import io.a2a.common.A2AErrorMessages; import io.a2a.spec.A2AClientException; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.InvalidAgentResponseError; @@ -65,10 +64,6 @@ public static A2AClientException mapGrpcError(StatusRuntimeException e, String e return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new InvalidParamsError()); case INTERNAL: return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new io.a2a.spec.InternalError(null, e.getMessage(), null)); - case UNAUTHENTICATED: - return new A2AClientException(errorPrefix + A2AErrorMessages.AUTHENTICATION_FAILED); - case PERMISSION_DENIED: - return new A2AClientException(errorPrefix + A2AErrorMessages.AUTHORIZATION_FAILED); default: return new A2AClientException(errorPrefix + e.getMessage(), e); } diff --git a/common/src/main/java/io/a2a/common/A2AErrorMessages.java b/common/src/main/java/io/a2a/common/A2AErrorMessages.java deleted file mode 100644 index 22b587d84..000000000 --- a/common/src/main/java/io/a2a/common/A2AErrorMessages.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.a2a.common; - -public final class A2AErrorMessages { - - private A2AErrorMessages() { - // prevent instantiation - } - - public static final String AUTHENTICATION_FAILED = "Authentication failed: Client credentials are missing or invalid"; - public static final String AUTHORIZATION_FAILED = "Authorization failed: Client does not have permission for the operation"; -} diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index 754cd8919..abcecc8ed 100644 --- a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -7,26 +7,15 @@ import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodyHandlers; -import java.net.http.HttpResponse.BodySubscribers; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import java.util.function.Consumer; -import io.a2a.common.A2AErrorMessages; -import io.a2a.spec.A2AClientException; - public class JdkA2AHttpClient implements A2AHttpClient { - private static final int HTTP_OK = 200; - private static final int HTTP_MULTIPLE_CHOICES = 300; - private static final int HTTP_UNAUTHORIZED = 401; - private static final int HTTP_FORBIDDEN = 403; - private final HttpClient httpClient; public JdkA2AHttpClient() { @@ -99,7 +88,6 @@ protected CompletableFuture asyncRequest( ) { Flow.Subscriber subscriber = new Flow.Subscriber() { private Flow.Subscription subscription; - private volatile boolean errorRaised = false; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -121,75 +109,25 @@ public void onNext(String item) { @Override public void onError(Throwable throwable) { - if (!errorRaised) { - errorRaised = true; - errorConsumer.accept(throwable); - } - if (subscription != null) { - subscription.cancel(); - } + errorConsumer.accept(throwable); + subscription.cancel(); } @Override public void onComplete() { - if (!errorRaised) { - completeRunnable.run(); - } - if (subscription != null) { - subscription.cancel(); - } + completeRunnable.run(); + subscription.cancel(); } }; - // Create a custom body handler that checks status before processing body - BodyHandler bodyHandler = responseInfo -> { - // Check status code first, before creating the subscriber - if (!isSuccessStatus(responseInfo.statusCode())) { - final String errorMessage; - if (responseInfo.statusCode() == HTTP_UNAUTHORIZED) { - errorMessage = A2AErrorMessages.AUTHENTICATION_FAILED; - } else if (responseInfo.statusCode() == HTTP_FORBIDDEN) { - errorMessage = A2AErrorMessages.AUTHORIZATION_FAILED; - } else { - errorMessage = "Request failed with status " + responseInfo.statusCode(); - } - // Return a body subscriber that immediately signals error - return BodySubscribers.fromSubscriber(new Flow.Subscriber>() { - @Override - public void onSubscribe(Flow.Subscription subscription) { - subscriber.onError(new IOException(errorMessage)); - } - - @Override - public void onNext(List item) { - // Should not be called - } - - @Override - public void onError(Throwable throwable) { - // Should not be called - } - - @Override - public void onComplete() { - // Should not be called - } - }); - } else { - // Status is OK, proceed with normal line subscriber - return BodyHandlers.fromLineSubscriber(subscriber).apply(responseInfo); - } - }; + BodyHandler bodyHandler = BodyHandlers.fromLineSubscriber(subscriber); // Send the response async, and let the subscriber handle the lines. return httpClient.sendAsync(request, bodyHandler) .thenAccept(response -> { - // Status checking is now handled in the body handler - }) - .exceptionally(throwable -> { - // handle any other async errors (network issues, etc.) - subscriber.onError(new IOException("Request failed: " + throwable.getMessage(), throwable)); - return null; + if (!JdkHttpResponse.success(response)) { + subscriber.onError(new IOException("Request failed " + response.statusCode())); + } }); } } @@ -262,13 +200,6 @@ public A2AHttpResponse post() throws IOException, InterruptedException { .build(); HttpResponse response = httpClient.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); - - if (response.statusCode() == HTTP_UNAUTHORIZED) { - throw new IOException(A2AErrorMessages.AUTHENTICATION_FAILED); - } else if (response.statusCode() == HTTP_FORBIDDEN) { - throw new IOException(A2AErrorMessages.AUTHORIZATION_FAILED); - } - return new JdkHttpResponse(response); } @@ -296,7 +227,7 @@ public boolean success() {// Send the request and get the response } static boolean success(HttpResponse response) { - return response.statusCode() >= HTTP_OK && response.statusCode() < HTTP_MULTIPLE_CHOICES; + return response.statusCode() >= 200 && response.statusCode() < 300; } @Override @@ -304,8 +235,4 @@ public String body() { return response.body(); } } - - private static boolean isSuccessStatus(int statusCode) { - return statusCode >= HTTP_OK && statusCode < HTTP_MULTIPLE_CHOICES; - } } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 57dc5ae58..b259e91ab 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -18,7 +18,6 @@ import java.util.logging.Logger; import com.google.protobuf.Empty; -import io.a2a.common.A2AErrorMessages; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.StreamResponse; import io.a2a.server.AgentCardValidator; @@ -81,8 +80,6 @@ public void sendMessage(io.a2a.grpc.SendMessageRequest request, responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -103,8 +100,6 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, } } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -125,8 +120,6 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, } } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -148,8 +141,6 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -171,8 +162,6 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -190,7 +179,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC ServerCallContext context = createCallContext(responseObserver); ListTaskPushNotificationConfigParams params = FromProto.listTaskPushNotificationConfigParams(request); List configList = getRequestHandler().onListTaskPushNotificationConfig(params, context); - io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); for (TaskPushNotificationConfig config : configList) { responseBuilder.addConfigs(ToProto.taskPushNotificationConfig(config)); @@ -199,8 +188,6 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -221,8 +208,6 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -243,8 +228,6 @@ public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -325,8 +308,6 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); - } catch (SecurityException e) { - handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -432,32 +413,6 @@ private void handleError(StreamObserver responseObserver, JSONRPCError er responseObserver.onError(status.withDescription(description).asRuntimeException()); } - private void handleSecurityException(StreamObserver responseObserver, SecurityException e) { - Status status; - String description; - - String exceptionClassName = e.getClass().getName(); - - // Attempt to detect common authentication and authorization related exceptions - if (exceptionClassName.contains("Unauthorized") || - exceptionClassName.contains("Unauthenticated") || - exceptionClassName.contains("Authentication")) { - status = Status.UNAUTHENTICATED; - description = A2AErrorMessages.AUTHENTICATION_FAILED; - } else if (exceptionClassName.contains("Forbidden") || - exceptionClassName.contains("AccessDenied") || - exceptionClassName.contains("Authorization")) { - status = Status.PERMISSION_DENIED; - description = A2AErrorMessages.AUTHORIZATION_FAILED; - } else { - // If the security exception type cannot be detected, default to PERMISSION_DENIED - status = Status.PERMISSION_DENIED; - description = "Authorization failed: " + (e.getMessage() != null ? e.getMessage() : "Access denied"); - } - - responseObserver.onError(status.withDescription(description).asRuntimeException()); - } - private void handleInternalError(StreamObserver responseObserver, Throwable t) { handleError(responseObserver, new InternalError(t.getMessage())); } From cfb3cd9d20656e805abf17f8e13052b9adef5042 Mon Sep 17 00:00:00 2001 From: Sumit Kumar <115339907+whatsupsumit@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:09:30 +0530 Subject: [PATCH 148/493] ci: add Java version matrix (17, 21, 24) to run-tck workflow (#323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated run-tck.yml to use a matrix strategy - Tests will now run against Java versions 17, 21, and 24 - Fixes #314 Screenshot 2025-10-01 232520 # Description Added a matrix strategy to the tck-test job with Java versions: [17, 21, 24] Updated the JDK setup step to use ${{ matrix.java-version }} instead of hardcoded '17' Updated the step name to dynamically show which Java version is being used Fixes #<314> 🦕 --- .github/workflows/run-tck.yml | 9 ++++--- extras/queue-manager-replicated/tests/pom.xml | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index d5004bf0c..714953c9f 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -28,6 +28,9 @@ concurrency: jobs: tck-test: runs-on: ubuntu-latest + strategy: + matrix: + java-version: [17, 21, 25] steps: - name: Checkout a2a-java uses: actions/checkout@v4 @@ -37,10 +40,10 @@ jobs: repository: a2aproject/a2a-tck path: tck/a2a-tck ref: ${{ env.TCK_VERSION }} - - name: Set up JDK 17 - uses: actions/setup-java@v4 + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v5 with: - java-version: '17' + java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: maven - name: check java_home diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests/pom.xml index 4e991c4d3..f8249dc58 100644 --- a/extras/queue-manager-replicated/tests/pom.xml +++ b/extras/queue-manager-replicated/tests/pom.xml @@ -71,6 +71,31 @@ + + + java24-exclude-vertx-incompatible-tests + + [24,) + + + + + maven-surefire-plugin + + --add-opens java.base/java.lang=ALL-UNNAMED + + + **/KafkaReplicationIntegrationTest.java + + + + + + + + From b976bcf95c433bc876b7fc7dd9acbcb620e265b2 Mon Sep 17 00:00:00 2001 From: Siddheya Kulkarni <115717746+Asymtode712@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:45:14 +0530 Subject: [PATCH 149/493] chore: upgrade build-and-test workflow to test Java 17 and 21 (#321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description - Added matrix strategy to test against multiple Java versions (17, 21) - Dynamically sets JDK version in workflow steps using matrix variable - Ensures compatibility across different Java environments image Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - [X] Ensure the tests pass Fixes #313 🦕 --- .github/workflows/build-and-test.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index bdc097791..0a5050ed2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -14,12 +14,15 @@ concurrency: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + java-version: ['17', '21'] steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@v4 with: - java-version: '17' + java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: maven - name: Build with Maven and run tests From 248c7c5b69bc82d2ed52b38e061700bcbd3e9ca4 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 7 Oct 2025 08:55:28 +0100 Subject: [PATCH 150/493] fix: Support non-blocking sendMessage (#327) This is a backport of the following PRs from the a2a-python repo: 349, 294, 449, 440 and 472 These include: - Non blocking sendMessage should invoke push notification - Not possible to cancel Task in final state - Handle concurrent task completion during cancellation - Persist task state after client disconnect Also add DefaultRequestHandlerTest, and backport the parts that make sense. --- .../java/document_symbols_cache_v23-06-25.pkl | Bin 0 -> 1473486 bytes .../DefaultRequestHandler.java | 117 ++++++++++++++++-- .../io/a2a/server/tasks/ResultAggregator.java | 36 +++++- ...st.java => DefaultRequestHandlerTest.java} | 9 +- .../server/tasks/ResultAggregatorTest.java | 35 ++++++ .../io/a2a/spec/MessageSendConfiguration.java | 6 +- .../io/a2a/spec/TaskNotCancelableError.java | 4 + .../jsonrpc/handler/JSONRPCHandlerTest.java | 9 +- 8 files changed, 188 insertions(+), 28 deletions(-) create mode 100644 .serena/cache/java/document_symbols_cache_v23-06-25.pkl rename server-common/src/test/java/io/a2a/server/requesthandlers/{DefaultRequestHandlerBackgroundTest.java => DefaultRequestHandlerTest.java} (97%) diff --git a/.serena/cache/java/document_symbols_cache_v23-06-25.pkl b/.serena/cache/java/document_symbols_cache_v23-06-25.pkl new file mode 100644 index 0000000000000000000000000000000000000000..a0c1ae55d5d5ab18d03f277b05ef64b4d5644aef GIT binary patch literal 1473486 zcmeFa378zmbtVXk0Emm=2@t#!O%g1CjcN)MFA#*#02*YoaWuOdVv7<)Wmjc)Rdsb$ zDKiTPL1-jd=1Ou&mewWTk}r)tYp*|j_8dOOV}Blx$M-Sz+T*o7KE~tmr}cVvyz8Cu z=WoB=eJ|ogWJF|SW);v!HdX!MLsdn_@iN{YFJ8QO@#13}zqnz;O&jp2MgqSyvgB49BQ^J;J5p_qxQE;ko@K;c@EW0a*(uv`&?=M`S%=p=Gmvcxu@OIocm1oLbrg8Z)~_r zkpF>dvk2`Jc{@eDoo;92Vzp83PHgOK^4-RQ*Ts^Xg3$Ft_H|>u+VHv)8#-G{71wu5 zq36S|cIRdfNvunukMwQ`YBOUWm}YGL%Xsw`h^8Hm%Y%f*1Mez z-OiSJv*cn~*6Ph|zuN75a=u#k4v&nCoCn&2kwtf|>W>7?wqNo}%|_@q>m#G%*oQ5@ zS%a4G>p|AF%h>H*(J_^+ogMC6(5$yZZ_*7b-OjZ&CTxfA)uGQW5;+sg`PS{umQtl! zFZ*5t@YLDda^YLpJ=;bWZtC2s;j>%0Wnz0}3qB%-Zvsq24BraBZfdtHw;MU`kdVOq z4?&K*E4#FY5y;ToBYa?Zg(>eNZ7&E<0Q+mVRQEzV)*VzXNAR`yngD!Y-0Jvo76<-SU>a{pRmukv8!p?2lr3ths) zP|0nSyn289*`o0We@z?w!L*1!`0KWHz@HcL#2+tr@O?Bd-Lv;>yE0sP-n#(9dt+tD zd%OE~xAXqqrKrz^QN?YP>o`H3^yb}mJtP_RGEe4n_l)1hsrz~Nsps9N4?X+T^M}gb z({s`R!t&!gDmSwNJ(Kl+6Q z#N3VY+A?*2b`_>>qdQW%Kbpg5%sKq+m7K%-q0pGjXI27+-F9c=T(f+o+u7H0ON*ci zI;jYI0rWDP764EX+T|`Q=mj)8cYwZBE!Trqu^zk;UTJwjx3g{f%F*!CYmbvqCVdEM9>A zKZi`1>?A;>GBvTOGL2bC&^!;aND7)WzkCz?M5XZqN|wWrcB_;{_-hDqykS!sCp()@ zpL^-0F~HW;#7?cDBTx?XLXv3s>qAC6nC56XK;~gPm8Hrj^9!!e=L%evO1o0+yx#Jw z7v0cvU`-iTOU`_?;ntng&4mTecQ}Wf7wPvYSYI~%E5!vbyBxx!(YP&>oDzM$vXV?#Cl-ePvnqw1)X)wG(wzlR5oc0ysgy^DqvHYuP!7E zzfp~c`0J>_Lrg159^$VT*8>m#O%8c@Sm)s+Fz0MDtj<@tg;ZgbV=zwR>Nb2h4R%`F zpggWI6o0+PU?`^Tmkh;UPpt=r{<|D9^vQIFavm{W@~;{jchM@6Y@Wy4V3oCT^n}Jy z{55WH6w{uP9K~NRuLq9)Ne($WrgL<<-D=gVo_}OYvriX^Sw|f!4>dO5>bDz>YGZ*Z zOF?`V9n;v0zZMPlV%n%=FaA2a9@zWWIb`pG&R!ULrM8b7AZOgxk?GI}J`Fo3noF%Z z>|o8+y;JS5?R!Vw2U`Y5f!Qi2VR6&&EUw-A?icfW6MCiLIJ_;}s1n?}uNT46IB*+IEQ*{N=0%tlXJP ztjzxH%^Nlx8*iZ7!8Ql}TJ5C>*AQG*vhgp?aZQ`GbCT(FCM-Rv%v9L4O z3q3Ss0yUoesT!JYwCnYvGrd@C!812>=ArFI^U|;rRACF>X+i=2QZ?|LaR=BT3b9}{v)C5keSw{z)vU8~#R-8+I6FUjjCD{&xnN{Bg9u@XG2ahs^ETu;a z5^TfHJUU(N=PbRZ8k`R{8!vg`3^ZN9|6n}&S0<5#&`;CM>+6JDufy0wM-?WpH){g> z1De1NEdu+4kaoKi*!b&S6xhr;1)J>VdNZQ-a6Se1?u{V0&w*2(UoCqKG$gvQOPxff z@-bxNEe>MFV*tP3yc9UEOrJYDHF;tT2aO{B_+Ce`?9H_o3PZ9X=jyegVXD1YEf@9^ z*iiB_l0u|Q5rDWmQeeU&F!RH3XV`UKghdAob5;6ceL4weLAGGs^SI<-V&LkvqBDap zfuaW+2eZ>De9GP>kxZM8sm26f%#z=saoGh()L# z;2AFbMt$GCavB@L1)qF<%y`Z5K)4t*=`|pOwD&It?K!Nipw)zh#Gj=IdwJ)eK$+3s z$x1*jB&rJZnUIT325LuMiyGG9jqMV)*$_jL z6rz0(mh3PgL#kc6pI{zsSPK*IKlo2WCehGY+#)To{O_q{<{`Vp~V?@@yh{ zAsVGqUI0#VqYDeZx8S1U>zfIjWHTR=9rRYie0o!AjU0jb8UWa!t_6yr+hP@n_f#YS zig=bcY1*WRdWFzmQJboGlPwf2vPwuhm~a&LesZ3T$wjLci}e@m(h+M1KYyl`0-pru zkY(uwBHoHJQA$S9830d;SQjM4RN{hAn__P*`bhL&98!!Sz(HB`Tnmp5xb!FW=_7W`E;8pk+ zj$G{_hn502_7t7p9Q20+oSOJAQgx&CI(8DPSt-wyJSg%!_-N{ zOh=C+9YmQrBXJ|v@=WC`3^99_Q?8a_jTi!{Y|?a{i!QkJA>}O~&>JH1v<1>r%?7Df zxNt5g-}{+Jzobp1zh;?8zYJ-+VP0wvfm`!EB!h%U* zY@EqAKC$Xboz@&^v^b;>!xHKRu?o|cQ%hX6FYL$Rk3)k^UP2Alp2AqhMH~|i(FjrA z4J`NVma`NDFk-8XYN$M-bIgy;fuqf z4EtcbtTxKn^C~*!*eAo1?}10&Ie&&GMOIpfU31=z$tQRVT-ccT3ixVBOX3_o*KF23 zw*lP;RxZEYLNm^b+`^)Y)&@A#m%Q>6?4qF{2djLu+%9>(OhA#j0Ba!6ABCkUvx$35 zPK^dv8YMOvjhBZlT|LaZLlLItOB7kx&474?s+lN6IvqTxhOZ5COT?nkzhbQ2SYbd? zAG3m-QLy{bUI@z{rv#QG+*5Lv+Ck{dfrYGGhQ6Bfz*grumzXc5AJVh~YxSaYPFU^q z$r37b8&}AF$Jgi$XYL9!)5W?KPIxgP->R0-ItOT{oMIBvXf=x$fyR@~hKE)!q+L#3 zMUFPcAi+W{qiXTMUZBo~$wku><+Ru+9|!y{o@+?{$hOo$fK}mK$*becGp9z(qmMK& zitvVETnS5vHYJL=4vUJv#Mx8UW|oTVAXtY!AdujqlBBM7F}F_eYc3d+qX9)SK}j+D zB$C~D@)J4ZDzKjHw$i)cNp1L(N_;g^J++02Wg=2Khr2Z(QLr1`EDQ zV#lGdcE(RSOVwb>4NDbxMZQ%K(>bsj{8N;fYuw4WZVpd;6}4k51SM?oIwcfbZKhs9 zkHvBoLD}TOhZ~qF`y@#&DU!J2IxM#e6iLM20=fha*)AT}TmcG7>qd!06H+<_kr3lu zCmL5eQAgPnQ~W^G5WY;zC}fe2Xy)KeY8vA6+H4LprROfiRfyiq>A#`siD$OnWKk4< z6Vf6NMf^3&EG`Zxij%l~7r@R3dUpUB@TPK{9L>4%IG#`C@&DeQpgf+cH!l@aJ6zW) z2^iY!qiA1zHL;?KCZ}LMsGFl5r^Gu2bXV`lANxfdYs#ks%;hAEOH;>Vvra z;@=_!DVpKfQWdN>kD93Q3R{p(HOh-_spPf70*Z@q)GooeMi!x7R5L|li0biEQ7_bFL_NAm;ATJhNf>?% zjiytFoqjO2RY2XveDmN3!1pQV;P~+(I0>M2YN>etcWMr%$k9R;uS2%b^QDFVkR zjFz^~98Kv? zJULCqVI{~b{yM;>Mp@-KRm^J87c`>vcn?`5kT8#fiC?efnr89GIw>na8T>~=kTkHs zqiI2(MM&`DCPE>;px95-LeyN+iZ!;-OC+!MHwyX1C@NKo4KG?I*wLCt< zab-yn=8-Hy`q_dE1ZxQ2Su1QX(o}(o#zq9vg~{2(VC|aQmMWSR@n;{8Ox~(OmUjeD znlxz|G)q@D%pb2vb8jPYiaI7zZKp^$iHS~u2COwxS8LMeHR)xSQ1?q|vT3q+Z_YFK z>0;;!n;5!Z7enmlJ3tJ1`4mG_=%AnE=V^ySvRoyFf`DGDv zkF@vs(S+fSMJ!SYHycouC#htiyk(UhygI%~8i|*}yc>Cv5}%JrLO!jIeV- z$*<0NGfh<5`uB}K^SnOFU$Kqy!&~#B?~L8LkLPo+A52o045NA6nsF4=rZ_CL zv5Y_w{_{7k6Jc$s>nF>skE@5}TRR~I!oM187T>vJl=76V*&=%|=EYwya)q-Qci-Z! z)5574hb{hb(^|C8LHi6!0&mdF$68JF!U6;E3FiL==kA#42jvr3%*#zLXgm(SPH0V* z>|@5jJ~sTs%YyvS>wtIe!!3$;z_M-yi7H9>l`KX)pKmdN^)A_RXA_L?Z#GbJBIE5d zNFn`kKP%1Cy7Y8y((?=^4oe-0{XETdq(?wULRyX_)0%g8hi}fO04*e)E6nO{5TEJj z(3hR*c{!49T*jix(tD`J5wuAIP%WB=N2Zmtj`2lxqF`ru1b+CWXU^R?xP%2UJ(xE;|Me@ zhrJlaEF=2SpwevD%cJEoN;p<&%w5r9{POjI8QKOW%X7C>?AT8h<+ig8q%)nb$ho!a zM{+qfJHISUUE-8V4_qWYOP%{@8ZZDRCSmlHZ)N$zB;-i+7!j3Qpm;3xpbWQ|4Z}JU z)`3Zv>P*38SQYLsVL)GdD5l$_Hsg;Y+rvQ+ztTqE%p@`=PtKvt4$(mGmK z?*uajaP1SWvQMwQ&}{>1bA0PY?Wghy**^t~zR+pRDHSZqDFafc#c+A51?q0 zwm_$A6;T{-R~+i)77g<`PW6)YICuub38H}Axb&BOdQCCQCpwqTgAFG3FgoH0^DR4= ztsG;N^$USlgnNz}REJk6T!g&7pKuNW%iy{batKw{gn_6QKSlHCw#~iluV z4E_>-umi&8aJWaN8Nh*8fHWK&fFsWbVN>@K95KNA+{$?5)I3EQ4;q4XN{XtDi_Jw( zJ=`2vr)Gs(EDMgr>~wShDt;)>ik9ZpY&`IWXi2F0botG&%||!JLGC+j(Q>~THQ309 z_IvQB5k2+{%LDgy@=KHWPmD&)!x8ekGIK4Z|6@L-bW3rvVA^aStYkJ#b*LC|sHIU8 zfFwsKN_As&@@+3V@QxuDECbc94&$(8Zci*wLxN|87qM;Ai5QYLSUr0-ufjs>riSd29 z#Q2|V5`*Q5BnJD*B2Fu}>*iWw{MUR+3|t`zy^cu?I|mu;QGJUT8Q0ImVX)+rceuO_ zm~#~7F@t_csiIF~;Nn2jSbV}XWDjTtN#`RG=h{5|0z18S0w-;D2Ji%4Q2i=~xsh~W z8S$snq>KOYMpF%MZi+ipga6MSn5*Sqw#JL?d4^rbCu!jya zrc-<=SRUbrAdFS!y$12fiIrl9T6QdV@={+Cr43Ra-(=uj6AM+01@zZ}{Uv+@+-U$z z%{Nsia?Fi?#x{gJ>5AkFPc6C21-FC9)+_ z%|`}xtYuPxEC>$pRp$%>^h2yJ=e_*nFyohaVSDii$|g9mT=diu4igE)*17r>13NzM zBmAWZdl@N>!d(~kMhIbeB6b?(Gi#E7$WeyT2w;Aa+d0H7P3gPctR}v-@=N+u!hTkb zoe!3Hms}IFpW^-**jg#@>wmW+M~KAOwN__Xp=b+-{d~dJ&XamOn1|hKRNDqCsQm7% z>_Y1DbmcR@m3IvBN=#9O8NZH%rDPcXU@4|yY9q2afxj>6ygOy%@9}t^^}yeueDZe~ z`FlNV+^I{C1ctt>Gt09v^tE`N^}x`Pd@^)z>Wmr#yrDp&51o+F1;?P2v+Co8 zEKETY42Qr=%X}-nUVrut=o&dZJED#IT;rt5zHx0(P|dS3(}$SEnN5Oz*fOIpTHL3X zw_+?4dMQpJX0RmxYAYnJb?Wn4R9g>%0=_a=a9X+_H0$NN>(TZyJZ3Q*IE=0gV`L02 zl*7xa2N%kLUD1a{a)b`Pnva5a4K9>}9sz5uv(7J+lZlaEn&$@BkuhuA%Wwh&?l}WjsKIq)u*EF= zoaw?Zas69zL_LHa!p-bQsOlYf9a*+);9KN4Dz#JiH!d5Nd!VzOaCU?ZYX&Vex>Qg# z=SP-g%qA4uQ2E}&rUqsgmfkpbtdYtO`FTl~@BGApL2qCOIFOLw%UE0bhCp}XjY zrF4_OC?hQEWY-sbja%^50ugU5+)gh#1K>%p;;Sw6Va=Dt=rXW4q}aku-5VNQW`(B9 zwZF_NMF2CO3Kk+&yTTxTdBKg#VpC63wr1juD5|$6p@2371nc9$y*Og(;-5Ij z4erIMf@A35UYxg2BBwaGu(Qy&aq)2lwI(?!|#+ zlHw)LE#+ydKd!6n4muDO4BSzYpX=&>a=sg12jI+VI0|J))E!)n!tX?f4GhqnX`38- zG46`B@>ipz7_?J>BV7aR0M!BbQW{p5Yekw1B=+g+(DN3koNQUlP`IJR`v_d^7DV8(ft@KD@mDl0wEJ7)wlNh1&|@924Fp--z9L zkk6|UQpBsVa_4q4Sft5)#6b{!>F*Ku@d!aX5?lZLkQ>vpm9wYpqziZ17zELmduw8O zW}xpz>_(!>WeTF7GGy5Fm#miHfWa)uIdsJ&J@0zZOr&Cfugn0yOm3L@RZTK6fRe#J z-(pedpF!C4rmz9g;sf>8bs9E3MOv^xD1!jnBJ#kBFHTypgyotXcLKlR0NR|lLWf@O zF_`tYF{2#93H?Fz#mjK-pm2Q@T7~$u2OGnu>M6IPyG~;7%9(o*eGynFf-J53=!^Ow z9|Z4%b^9QApAcb5K3MqthJyE5P-l8uX+F1u0B{srHE#jnbVe$UTP(28AWRqx6k4(@ zi2!BlDG75GOvi=LGK7y*r%^O2;!0871Q+8JKXr%FL6|T+b;GW$!A^?|Z^R95o^AUq zGAo>A#HB!d??Btl!cLBk3n`wesXTxzGL%*NnIWW_+i?bA!c1w>dzdg?Dy?WZ zlx+|{YN^tIpeRciDXiJKjgE-%0Nj4it$d^sPHg&+QfX6Wh6#L8Oghh|Ghb}p>lO%L%Z^p`l}70Qx;d1qJ_@$*Z)54%HjncoUPf} zNh6S=qhOC;tR%nh3+(ONm5;{N)K~iu zJT&ueXxHW}$ymkaGM;UzcPqO(Tk1`|pUQ~2x$RfGoxI>k^jPfzyhmq;I~O$T?a-Ta z!%BDUq1D;pdl(k|A`!IQIO}YjYnHEcJNsI0X%UY0I;l8&q1)L~!Z^&n*XUm8b~b~S z;5EW-?JV>iUQbYaKX#%iXKEjS)QRo255fm#(tva)2b~F|=Xvr??RKr4QtS=`e^lGU zP^<#Q5PJ&27^ISucu+dJn87xz!I$|PY_`9_U7Xa71}VFEGQ_U5zX287Y80d7JLwHr z{B+cDyS0n(LbXfyUzK`f5k7M>kZMav+Be{T4Ww($#c~a=Tc~{m{%hBKq^ODg2wREd zx1{eG;I;}b~3f=1lRvE$GHB% z%C=c1$QL{a(69Ao?^vica*m2~*%5XxCMp+I=QVAxc^@{hWq!dg1fpV{;yXd_wu~2a3#MSp8eV;xB(KaHYIT6 zg00ngx8%idB0=jdiUE*rtF-|^w&Qo0<(Zqi*Us3;+wVzHQ_MuGW<`p21MDhHo{RxK z(3-kF(-nc#tjnhd^uhmCR@6CV>r}ns#OHjn1UELm56?Uvz?rRf944lAr6K*=HV15z z7@VtWWAI40P0g*m+Z4D&fqz4m@7f1e&|9ioS?KKN@85&(U6*?~u^vRio1IM|%#Y{^ zsC8Xs<-2Z#8t$b7-4*OWLB~R;4;MyGyL1_Yetcvc_TQG^_7VFGz)taHdH)M?RXY#ZeE=Dl zUg)A9F-&*&K*unDrXl#!o1m#Z3Rx#M)gFTngo*~4og7{!WX|wpbpJbOD|R=3eyf2r zN`4ZGy&Hs6=Dru^wi!!hXWu zRy+3g*s*sXPu`RSxqJEZ?FQs1xwyU{_iC;ocg%8~XHSfWJ8ID~6kV@gV-o6daV$#! z*N$0%o6UCIJYdJolNq>qkUt+Xa6`%a2{)a4nYWVdKp4&x`4?gz$}^&7pW6ucA)kZO z9)7j#F^rIn(T)U12MmiDG+rJCcQPA;oOqlyxK>=6k(mj>Uh=4=C}syK*)p19(i4vq zp7T$7^KQEyvZHB8RV-AG5<%78ZjlZII_z#O53fwcO42i1!pSKO;Zf0eKd^XnBG{<| zkq{HI6sy75QY(ZT9@U=1u}lM^u#8cMkJ#VX z#uiZoyDAn|4|!IsQl;tv+DmZ2!?SgasIFLXgR|iL9)EE=Y3L+BUtO?LA?s|g3i-X# z*oXKR#Rp_OxtI0c+)b27ON#vnW6%O{hJN#l@x(s(*!(s+tLFBp>sC69i?=lJv6 z4KPyjeFBVfn|od8D{tl*bhGaR>a4h9CjA)yrGmm?K$8T_m5Le?O#tI%F(a$bQDF1Q z1)o!mk>HL*bt?4ZQWUd8eLZ3wsj#aOydIc++x5C#lfWFwiUc2X>I)6j0aPWTaKW7W z$svN{yj{NeKbhyr9JNC#DOwE&Fv(Ic-xi_6xfV`;wROXWkpl;|IR~8a#=#}FIpS28 zT6J#;4uG-fCQfr64!U^>D+;z}61jI2p(d;h|Jb8|NefNSCecK}QvxXL@P?t_$hKpn ztrmEUu%@R=&6Zc*M(ev$-3Z^Pev8F~bPQr~ENvM{=s#pYi9juB6 ztK$9x=da|f7_Xb@IC)qVr$`T^G10QJ+-sv0ItIG(Kv!le!3l0NyK&Rm*O{)&;*9ZK zYV^*!6|_>^iUX&Z?K5DDrNDt%&M!YGLtk}Jt);ElE34-GqL3<`To@%@$6t9Tmw4+7 zon!qvv&1TCi%4WE>$LLSz%X!H`4WC4IjwvrJVZ|fH7|G|eA$w0cUoCnBzyR5!VwMb6LPw_umGEgoMm9^M`@hKr+aDHezu3e8da$KQhGxz# z#Y=~AaVuzA7}0ASj=V9^lEBFO?Tn;k9EeuYLeEV`Qu1T@WaPig1tW)&7%4dx<0Z0~ zJ2|KF@&P+9DLIjs^xWhnCFhivf08d=av_X5A~&o`j+wmNS#4Cqs#~X%ROy5BK|4b! z8U2>5gOi@$XGmE}F647?{&g-G`j{A;2i}~#npt|n-GfQWjmX9Ldg?im5{~hVLPXb8JwnKCZ{R+p1gC~ z$pxpMjX16FE6!{~P?*DdDfL=9}}LmFdIcvLF72bJkZ)cM!CnzlP(paT2w7bdTK?LDUWF$ zV^a`Xn{@9<^eRcJ(nRT9fqRjK7h(Z1n7t}v5_-Xo*P|JDrD6~{NW&{73l%GhSD&jX z=twT``a=vuafUGsO2&{MJv2JaVhD^~E_p4ygF_KrOnn+J2cwPh`BvEtz47_8UI~|7 z{uKryL=!3^*1OXcrHb$+h+$>vu?ew9MWzU1(_l+&2b=*!O9YihjUSe0A$;@J2!B-T|i%a6>x};fxIhoKh z7|!c>mtF?16WDH`$p9;(6qv+bB)8%B#?MNDMqr+w!k*L0D_x6k6*X2wPaKgwPPUTR$^xqF5(!+Y=L@2$?_ z_yU3uQEkMZsH~kNd{^jUQGq;Jg|*^eAA)dSTo8`>VHM(ig}sZy*X*M3gBhZbikYI2 zl5;K!XLBJ6PlBZbC4kct8`rZiTa3!}x5Nza-Zcv~!AmjEbFb z{T^x{g)ugJq+38fWJmu(2KuQOT=%3YpOh@N<~v(JG%_mkwOX3b1^j<1GW4-Nax}j6 z19zPMnCu4g7)+Oqk_W!?Y-Ay*Owlbyo2p_WK(9GoJ%Ag>k}BhqlOkBj&}eF&igt|E zisslmvNmHCsS`y85n|AoWLVOZ-a8`1R>-#Mq`}9!*lvq*+D!R>tr}KHp(xNNH_T?J z<5l}83^PU{6*Em1l)P71r}AmC2y-zAe>WP0Y``S)5fms`)Gc_NpR_`}D%B@lmn8LU zg%!!`(|U=v-u2GNV4@bK?ofs<${KiMRE4oCh033#*HdK$`rI$O5`_l-6=SNQ5GAJM zqVb6Y*oWtE>M~v5B+D487wtp!6B$F5ikTB8B_C9WD*Bw~YrgzME{5t|5WZ1tZ!^FA z9+=8!QG_E#xga|5>(w&X8mr}@;n)zttT^5%Cy%MhE<2>1-{O4JUvk635Yh64_Iq@`ahtIf)k47@(c zF0)G0ICh_6t>|lDf6_j%zd2)IQ!#U3Q!-v|hfbB7X!P5bFg2%2-Z`swZ>Se zVk&VMUzMY9$9Rt}&%78%vA-V~_kbvv( zNIVKFLrO)&BP~fYVMJr>6=DNLX}GU}@&)@q`HqZ%LdDF1LdiKFC|}OSK&hJpCB{NF zS141Rl%p!blXO+xw?d2!=e;aQU$Hq~&@=2uyQj8KN0^IgfEL{@ip3;HE#eygijwGp z{AQ^E7P@E`7LD;mWnudK@iXHyGh-+BTZ;6la+)J%ryO-SMl-?X_z<5_Ik7Kw1oXRa%R1W%53g5_nb6Eyha=87(=C5A!PV|L7S zB63@aGDosonX{-+l#9lgl7&Abu*#?uYkMT>NAuu3Is0vQN>@4_!~w^x&~%L`3@A>P z<1`B2_Yx3HIPAb=UQ!+j7|vLcE!jm|t1Z>>SIa4B!(P$#B%Y67p`!E|Eh-WxnXhXn zM9z3xV!{~5enV-Xm_rqvOggoh#T3yHFKIx7Hu}CEy>587G2{vfKZe^K%;AQ_Jl)N{ z_Wm2G#Kx@QT=VbEERkvgVJirnZUhu*QDMwL%6!H8GemJP*!73q^}(*cB7#sg7n}C0 zyX&7id@Or7>v;fSFXJB#!e!nm<~>RC1(TP9O-JbLtesCA#*jglbalE%KI)TGQlUt_ zj}`%>EHEKXD!#_P85Gll?JC@pW6ogM+xQpt;QHCFBF2&pbg;R>ISK!Ny>D*lz+gju zU>siwkBY^@=@?P5w&ECPZsA>P>u1sIN)cu*t!hjx&W@UU(S^$nYm5%B(^{Q~SZdn~ouk(i4@;?Hm7-z&Ca?;k zVf_|B z4|WP3c-1#Z)X-#%k30FT3%#*&o1K-E{BRO0>AA^DO1@2HW!}c)rcG-<9`9%4QR5RE zlXw_|UnD4ZSl4oz{nH$v{u0>{s}QKohcj2)d5h^(WHz?SHm>(4jTrtkg$Z1BW;3v% zI^Q8e^kdzOj4T!UA*n*&gV3WX=^w3{3LPU@2A5^@c%92DuSzG;u+TZxuS%Uz)hemh zj{^%qwJzXCl4|`JJVd=r%?tY2eoL}lwax=J9tCPaZFz@aqeqo{7rZo6xgGe@u?+Rv z5IleYw2#0CvRGsFRt^i3)erDwRK1_D72C$2LjYP0A(Z?O6dQqJHr1O^xhVue1h*J7 z%C^`k+rgUvty^n?k|&cWqvu6?v8N_nNBNYCh83lqabH__F$iq7Ltv*J0(bCa46$Yf=WYBs9RAXf zL&+F~3c;x@yz&Nxb2);-Z1P=c#_CMj6K4awD6WbW{(t=VF1b}GPNWPL`dTgx7kAll zu{#47ciVA6$#c=eZK5K!GE4); z9y>7Z&j7}~c3@C4Ml8s^z*x)?FwR?nk-TzIu))QK?Up&yi#HGY=5zV!^w}SS6A-=L z)P`e}uSVs?asfMEB$lX}H!Sy?jb`@i(E#}3H5nH-Hj$*%e#7D@rS2O=G+(eY{oPow z?>4U-o`$Fxo}aa9zg@1{uOTu?wf*F(t=jEW%cVOb%IjQP`K5a*Aq$;}e%J45^{vt( z{XraZP^}UZ1V$--a)p zBxz9l6g-7JwNJwbvRz~UMh+E|{X2OwF6_TxD|QQi4tr`EYAE?}DE37tW-RQnaZYIf zatcBwSLf-zP!Z*kQM%bq=~mt(EcCS|Z{^7t1Kmm~JqL5Xtr+ggA*I?X@4J!oyh+7> zf<)y2>b36xLU8H-d3OCQ`js4TaNY?V*M4Pe=)$qb#sUkO1~->@a1}M`Zw?+9^5EWC zUd6GN>$?zF4WHnxwfKdejj&H{3%3D>4-sKG{T%}^-(@?2ypYnosJse$F@Vx~W zLIh?hQ=ga0)K4p7DTZh zNp<>Vc!<-OniuBIU$-Rds#9l6y~*#!?n3#_&27Kh?W{FQI-7jAvEX%UzXbGmcDQpv zv)&H9NjI!?*A6(H9lnP*TwNqWmJ3CljdRWNm2T&OmRni`r+25?EV_r>A_r-aNw*ie zoh_wGwO)n*+1(4>&SsdE;80w*_LI;!{r8Pv!2a@Qc z=MUM7;q646zS^GwnYBO1|9+R=^Y`F$&Ea4B_ej#;;D3LB^sJevtopuulSesrDi`ya5c zbH?wzg5UB;`57K8PXx`guzH_4S+YeGK|{Trh)Jfc_K|;|9SINeWZXKlB7us*tsa`d zpkxe1vmQqNEt~U)gfEa!P#SYp86X)gkfNTSleYwkkPSS5h7ccoc?HWamX9O{?9kFT zIx`m+GIzpm%n5#n1Sb#1HLhf7DrTTFuJWUkh05k0p%J(EaGn#Mt-A#wI`49~@brktubFBQ!$#hcI)0U#dwssyxGvaU5 zl!}B=3M$?73v`~XJ?8=F=Bo>MCka$F>=X)~?>Bu&Wl*Fj5&NBkN0o7d1E2)i6=iOc z;*7H+%1RUo%qm8R-}aQl%_6p>Wpv!Dyaq#{ASYvFjMzuUOBo}BikZThl9_k)dK1o1 z=5R=0boiKjPU5tLv^oSv!{$=8H08~Eu#zo#TsoncKS~($$_y^#nq_7`Dg-iu@;ZhS z7Q;WY-kFNuyyVW+y`v5rJiRne@9oFKy;_E*9LKzUhYi(OA`cm98LFZbPIqj4s-fY} z2<<#rP`g@`5uak31eGdSh(@t^EIzRbKHP~F%-u!CPDyJ4tI&%E;3)&G8PY^FB|b^I z%)1bG+}Wwg6Jw}gDB8p_N@5abN5#6Y z7fH;e`SE76?zs(+j0A75tQDr`1tw;c@s%|fz+|P>6HQ*)Y_ww2L0oYYQpq|S$f?YM ztH!RzrWjF{i7$8+FgSxVqtYEBgZRuA&XIr;W$@EVT#r+gX#z-_uV`9h?~y*$^ztUw z{>qq5tYz@^<5M-N+st5s;pRj5fdZUj-R@_1qe1i=jG~SiG2JVUo}YR7jj6F$&yP<* z7+h;xqTAnk%c32KnzZ`_oiJ#7j0X7ZmxeT0!3||@JFFb^y^pl3K8(OB?!*TzuLKv? z^G)`{Y)4&aBSEFvu9uxTFY(QwN-qEwKxaTc*e!9wCaVJ3;`zf6#tioGVUr$qQ5qpO z=v{{PFn8EYs})9USZzQ*z@n<$F5zA?)2#vr4i3PcKUN9v()6ou!UhoB6A4xOa2q7- z1(nn4ZMMUM&H00KZlhduW*}@47KC8^*q-mA!$A{%f}6SQrD5k1EY+d8v}*CF0(L99 zET}IDH8*{5wBx(tvLB$4C3lg%9PAzkL0p3ouz?N^c#rZPB$jxWt6?!(fQsDW^F(v0 zRfmh^KxsbJ4&g?@qTgU7HAis-T|`X7n2+%%eGEdMXtmCTQ2 zD4A3Y9Kf_407@PbN+#Z%gpNBo*C|iuP^Y{{iB->hpW>`eR07rc0*W!Kl+An<6MksU ziY4;pZ_Jn_25`n)EwZVf=>j26LC7?+Ysur_!2}}MfXx^IpRkXBPiKq(DrSxVN=6q* zwmY#-M!@U&8v&jFC)yRQmXmC!jKd;esiJ&`a1E{U6VRthTX9n$gSIO6p&7iT&8H0QP+Ta0?&b41n1pq9j0n+PEqa9ZD{y?mN#TrgP|V@{t^lwI>+3*zTWXA?W$>1wIUd_|DvRMUm`@ z@h{prh{v_l7(bR=Y&|gkqd8>!?hGD#`QA{k#t;-#Mt+OIViVgzj1(tY% zmV5GLRjt)Dyvu3^d%>X35B7owd%>Y;M~t>wgweugwBvJxew`2-T;_6349mezAg-V@ zy+Q^%fo;D5HiSesjR>BsKk2{0PT*iCaIh0N*a<{ORWSY!b^`fs;X7w1kQM{leK)kX z9{k=49#zV#nllJ%qVwd%Wg$wm0=tRWt1OF%&F zdx79u14&s6RMcA7@-hIU=3_ssm)E}9_S&%X$jIAb zC+|*92b^`++M(n^5_$9-&OY0U;f^RLkN4e=1F1QK@y{YjYYXGrSAm8$2jZi`p3Y70 zY$9gdaaEnuBX=(%HzWJ-Qd{(1eE`Qo(^euz=X@Uc|MG=~Un7r)WS-)dm&sd2%TtX}>=aU@$)kI}RO|T`8(N`QF0M!L zO?|8#_wA5|@v`bn$+F5xNzYA?iwb~Y!_+tL;rD~%jMWa969=2T1T;6>q4|&cCE4FmRzkhd?B!y8@-*Q%qfTSR&g8F@*?6`j$xb`tI>g~QDC7CRa*s3xw z31f6vs85*f*Q!H-NRU0lLl#J%-VD6$v*Yb>2HvQci8o4KM|dmb3U9L)Fbs&g+hb=l z*TQ0OlT3$Gj7(6e_`~yqa0ISVeG_(!3xQC@)hsya!eNIK6%W)q@GZFHJ&s2w>k!MLsgP|{32zNcd})|ey>|DkNOa!-YtcN)72n6GU@u^k@p@wI_z*; zR`jXfb;1`uM>FK>5xaannjv4Q7~EN=sfd(}ffv__iug>f47MAR2HO z2(~-$gP~)*ER!1Uk_Y0~Z5~P= zYMuR~cGw|njr@{<<70Ll&t%}3ikUd3|3ZO@CbS<59MLUD$&JEK}d4k zRtxSGkbrXP5Rl*^XJD;79oInxw(X23;T;jnvKP=BB z{tEJ&C09(T=tZHhs1e&rJZcA=@g;j^g3;)Gc1b#$AxWv2DM=|=?3=FxqtR5ZBx&av zRg6YFR0NVtOrMeOkojl{o`Hkc?Kt>g1`ep0i33VrM>zO!zHs1LaX_pT>l1Y*v#-pH zC(fOjJUuouc2X{Cl9jm47&IVDUVO-olMiR$go>Fsp=7-Ec%7^u8u`KrTw)CIpGz#- z4Ga1JA(^66iHxD?a^hB&r#BffXXkx6gZEU-7Inc zmY_X{(a$rck$F2rDj5)=VkSf=S!|E419jpv`GUwH6(WLzih!V`bP;hI*{(ItK`Ur>2YgG$6iRbI%{bdhmqgn{0qhHr;Lm;nVUWwF0;wH%GI09c+-x6w=xhw#Y_ZH@;XAmZ|4gEA5vw8&d7cY7NciR zjPqqjUc7Bb%cnBXLd8t9 zQ1UwRLHmb%q2(oYS}_=@i4ArS0kIgR$1o3IlWgzat=HC@EcuKb9i0qxP%#r7l)R46 zu?6m&$z=UZ|Lf7fN17c-fsV zyv!!!h4^W6R@L#sg^O@6q!?`RLmUt5r3jAFDK-N)|Dzo@-3;7NF%vhGypC}5Xufds zjmfw%f19gMa4`^ zQ8Gl)STFY0L;1qgm<>~$j~2y-J?KfV*`;BE;F%$AzS9nk@6Lb*6*Hkh$?FIj$MOY@ z*Ak&&NE>yxUt{}Hk4lXWmfm!lFWbTLy%}JkVkTH9c^!e}Y`(xE0>3lYTMYy~s5FXL z$((E+(kdq;%K0us!hF9SG+)U84HYv%L&@t1G#|_tXuhFW2_yL{l@VofO3-Jts|@>5 zXPyFg<59fbkHBS~fUz8L$bj7s+F|$A4A@bzyNnYrlq{k*bhg1oV?-*&ugYNzrdqC$ zH~S5E!x;BkqprgUYjShtPf4mGNKje{Y8%63g{ZS+eWAr4i{~ zt#TsrrB3H+l&^m(ll*k!H1_7(nOLrW(oQ6K1jgIw&7CZrEsIw9HGP#siM{tBxq z_fE2G@nlt&LbqP`l66xctNI?S$SUcE89A$VgZR{yM1oA}b^A1#l)iV}GO99O%EoQh zWWNkyg?ivevQ@u7!mo!LJsN*CLPUQJ41q>O5sMnqgO-!7Hwm4GoC>z#)hf~53&zzd z_fNKi%GoA_9IC<{^$-RP;+M`>7pB9e?{zC5sa&j-;Vy>CW&H2Wi4E*>mC9TE*CX<( zmAAkIEv{Pm^nA7M9Ud83`O8jHNS9ZyFv`5nC-OkeLT9qyt5`B$ah0xS=>UV_YL?I9 z2W-l9b_QM@eDeV>Q*L9y>*9SKpMi(?spf@?89rx8#ws?KAx=xR-mUD)CvrXwbXVTd z$f;&r+)`P*(5)=h-h_@|cV&DdeCebLjN1PISthpEJ_R2!NNnctF(I*+C*u_^pSKm; z%AZ5g)yBfE+n+ zfG_zEIMZGjI8C3zdIk`B({oxd={XSVQ_gz0odMHc+h=p2GY|P8`Y5c5i=vuiV5@MK zSlW1*Rf3P5@smadwN^ft_R^xPL|g}qL^ea%DX+pEb61G?au(rK%?SInBMM`TD&v7( zXgC3XS|{<>f!O;f7$S*q8^lOgYRni(ciBhM9-jPM(nzA`dySDq$#}sM8%YntNaFDc zt!mBDe2=Gpp2zXj`AcOmO?jbTg_vb4IE>U0kUmTUWt?irPeXvCqm}R<1ND7nP;f#| z!L^9SnSt;7?D&3|C*v(K)>)38Z#D2u$#)5S-vao?&4qm9_!n}C<3Cbxe3Ef@1yRgr z3qGsRe33EfXb5H-NHGXXVNI?DqI!W|FQ00{m+}aQ*`I-+rVJ4WoN-_U^D$d#gAJXrUqR?VZP5%8 z%b>Qzy7-+tnZ;i9ohI+ls3n%BhDGbX<8I)YV{IB@1!vzKJTDDezlS1=)?A-jHG@_% z<7}tLxF;P++i555Ufu>6sE^EvE$@^J5DJ1i6orRa}y_& zd|+MRGdhp5KqQSP_6UZ z!~8j1f~w6ul>FGbz{=x!#L7<|8*gAJG-e|pyTbJ`p7uEyu2ioTcB5$|X6W9MSIUc; zAX0BY5qF>pX8|ap;*^mJYc#P_z@*iw>mz#D(WuA(@1u6`KAr(yDrSP0lGg`#-;+o1 z{`9f|*1&->Eykd%K+y(FbPtY0KEl6<$DzY_cK}ud)mMgH-7Da@*)B`>+u``$3^*R( z&*5COCQB*VAz2zlOwY4oKAJ;V&Q5LIu;JLbi=OXS%bqG&nd#liX=Z*`p_aPO)Pl_` zK8puB1W~_d`$}wo8GpbvgUSVxcxjbZnBQ9Ey!95dK~tS@$ZKq=6<&d&iU&3Vn{S|T z2;=W-+!}~q8Nny`)j{rD-8j%1by0qpH~n+hL_%%LR;Z=N6Kx&Np$Y-v|3(N8u*UI)4i! zHkPRfkI&5)jMswdM}6PD!mfvmmHEJv7gTn{p3CksdJEc*h$(`bL1VPls$VHwRNtOD zHl^pFjx)%DLagD#hilDhL!~YBuNcf1&ZAHZ*gQj^Nq={KJ!%9FWF-P_FVc=BOf+Ez zzD_M(a(%cZb-KA^aiDpYa zfD>Y=;uP49jNfjBURhqy>b&KQUG_?C^g4?YJ(J)YBQ{1WkUn(Sxq5AA*zu5la%C;- zhu_Me6&HT$GsZsA`D~_0a<&b2kU?U?58ca;$WTB#*JS1Wb6oWPjoKZZGTY!s#6rrnWEjTQ> z0A@AI2%0)}4+-pQWoKAf`_eK4$76OJmojin#qKt6Ov&i^gH}@(s)K}Iu0eYthoGIE z2W3d$R?HxY?C&426>!eU1?uzhJo!}``*j)4)YBrXD8KwH6tpf}Fr^Zi=&WxWs`^L<V428t1>2SnXdFv3XKu!;(Sq%AX$o4i9d#LKzaO-2j(au+Y9(#!gH)%MXT(u z@pBRP-r~+HrmB>r6=@xaiwtBcULcKn8>(qIMtjqE@EsBuD|>^Uhj0r$=Q~-1q@z-< zHc9^*?rh~`lT>UV%@%jSIu#cZ>as~@5-K30D zMgI#d?ikG+9i?M5ez_TrJD}@hrP#opbQSrO))%)4-ti$+$n50_S{4@ao=UV3tVrF&8kv(R}@fBi`XrO!7Y za|_$|X}E~a)X)<)}h;ISjfggSsKlZlmQ6;_r57E6<%?pm#U$i7+74fJNYYmFe z1EnC*);TDuoxLr$wCFB)P7)rtZz4=JABQ()UiKaMqKxU<=ioO)hxlgrK$dE(y@kWT zWbHPdj6U_>W-GRpKflvJ1SQ`G#l8!QMKMO!8Vz4TYSs=7weNxU(J%eXu~*w}9pWE& z{x>$&b8(gAdW?dF&dKYN>}Ex)DcgS@ zI0&-+U*kuTZ2viUh=M@P3j*L5EXj7+zV^`gS)dih^E&_<{Ydz)!do*5|I_fLqmJ;} z&p-;CoBCJqfvnY7d^?AO$>RTmC!>u2#gt-jZc0N2)pidQ`xPi=J2%D1+jDa4)ad!s zGjGhCKR))x@$;unjZM8VJ^uPwmlM0wPVAkWDmY1{wMxlHl8B|}588`8Er|so`P)c! zZiL()BQcl~GDa~^k^#0&Ho zJ~!K0ez%?FyLmF6d$8h@ialzuoRTr*2ja7HH+Wh_v{0xa^i|0h4!)Kf9JI-EoL~^^ zJ15Rx#T$}LC$T(97$ScF<;(&D(XLnp0K^O&?XlzNfeai`F%w6Wyxws1-*bbbKkgex zC~M~0^YflRU42vYr65#6Ak+9*w)sI{s)m*0uyAVm;CBPQ*5ITAjz|kv;Am$&vB)Q< zxIajdRU-*1fUrw|>nxmn2--{FkrW#)=wmonuLc#*KQeQ4*g4g1un3MvW`>>PVBTQHA&1A8kX&Li6;f!AK(As8buINhELN`7??WPeX$uxJ+&V?6neB-uj69yUZ0 zB^OOG7|H4#a5n=I*g{JaN$4$hMSO(Q06yQ^ygB#}nX;jp@`7@l$G3q=?-g;B>W0$sf_vU-F8Ml&69T}G5QdH?ih@wWQ<MXHuuC~(l4FM=RUW6q!<3|=HvDAgZdEM7;$8h2If$#J@VlC;h&Nw& z;2`~OV`3DA%yrz(X5*}PIV|w|4>Mr-tR0qzGhq3g9hQ`gQFL++%d@$G<=g$hQemtE z=OLET;CDkHx)V+w;(3UITpVQp>4+Uj@678z6nQA0QdbjWqY3 z5hboqvP&^jDDA-eHqb*q^s;P|qjn^n%s|p{JCZ0F12*LxNwwS{=~_QXir8u_D#ZL0 zo6vef4)f%~9?8wN!GXN+XLbZU+;GXKswS`)I>KlCqp{_k;Dp$1P|H~~Uhc#z3W?8t zFflC_Xod8q(uB=wX-_x)wktO&-#Be6F6VEY#AO#C(sCf6dkvxowo#zuYyW9eZUoBj zZ<1HT(-sui!CClyJY>bK>{_qJOzw0TYgo(02ES^GcYbDfvW4$P2)E2zds_x?xutBP{49 zL(WME#6YGviZFvDW zM#Og_26JPW`p3B$5!bn=xdVbu11SOv(=?8~r^2XaA&kb*1 z64pZ=(&l=nZ*xEWE+$0bW^Py{o*-L8Is{&~Lx7T>NrC`9H-|SRYwqg#8s0yb8weD2 z2ncqet;oVflhgNN+*EK3JqMN=z7r%WNayzl?EI$WlS%xh=Ld`-Psu0pJLG>oH~c=5 z#BUM2?kJ0eYvrlQeK)RS6#9@Ms=F?D^a|uC9&TcPdaK>h>Vh3b9#2kmg{5L}>QZyG zqU1;Oi=zKEFDRNFf!GVMo($ny2Z)+%fq}Nf#ARk5xX@N*x}g|rig02TQE3Hwz__Z4 zV{4-U99|5IrHWTtgjlj?+x7Y+-lbg_QWYGCudM!5R+3l;+xAkQGdpx-4A-~YS#FE$ zg+jJe5&ZML)~AP^klA^!S=vaH-Oy6l^Z*Y9W7-|lcMx7Df6gxywhq{}+Guw;kXH)g=B zVTTzdcMBzAGm7wN(uw8}ZO;p8;*vHt^I>uPW7&DL5RN1)n1>>GF8;sp)HCxK zVq64cD>WeiLZj_PwoWF(>j4F>6;)%%Xq61O5da)ZUgY7U&6y7i#iLq@s;o__a~vK0 z&^l?Dvr3ijans8Qs<#7PAx)y};Z@ExoI~I@Y0)o#c!h^`9w4`}QRL zlwF*DE<>Dt+AdBh`TqQi(|2vp%Q3L8Wy#Vp-8DGbdsM@Y9rB3mWbH6zI3*Sm2u})t zo{ZH#yi`ALN9ng_p!5rNlv46T`A6yPxk2gwl7&)XW{UYkt|=-Tuqrgxd@(@g&sXic zbEE71FdU2)b}8CFCut)RZe@NZZ(t5NfxI@{1f?a&@3Kn zpVt@Zl2!voG=J-4M8zT%G*MnE>g&Kx6IogYb{agbA&>-4HZkjva^GH-ES{Erg6ZSR zRAgf-cUY{b;yW4uI2L2M?d$x=0WOQ|!J?7fmxwM5gGHkh(FisCi{|b4ufd{Go`AOw zLcPa8lE~i88k6ss#YgY2ydE`K#!uD%bT^+$bnoyiH>2<1>m}25^vgUs(RGxH-D_;y zQSvY?v!t7mj9_!#LgV#rQ%cP%V`uU^`Ma3zN?+p1i93^2>^9>SDS1e~BDl0TxMs_Z zyV4W6Swa3C?M2)m)3xoq_?99{I^&Nwh|#GeNRoHaOqB@kzJ$Ohs`(T)6k$<0oKi)k zCXsm#!|CMfG}KUDU@?0ZouvKhMF?L8xBt0ewL}X`3r=ul2^$L#=R2@$ZR1mAWy#$| zEw%I`V%8VhVzaDSK>$7?g)ANUnDVdVB zDLG#}oyrYQ?-zoXo4@eD2?|kUndwpXicONRqfJl(f}P1&iP=Y(2n`9YV_&hO;m3J$ zqJl=nOa+aSMaW0ybDS?Uypb0)%-(^nV|><3V>W|FUmVvaQM-aA*Q2WLrfFT)9EjAz zhXYUu?3!T4#y^@3!n9p>l^SfTSdw8?A|+4Fgx?dlmDP-_HZ&5adK>6A4^duEHMTdB z-QuuGFtg%^QHELaFdcm*;3IlBQegA9MRFuJ!{+Ci$E8JqwGAQ^uRUw9q!p*za3fx& z2=mDJF&N-t6vaLf`CoqnJhoeSU~)9K3SV~r2oQ`6YWS;%v?ZF z^0UjbfcO`7^@5TU7ZCK^TtHB=wt#4$WXg^G=Sp7W>D#&C=`Jzn%z*4X0taZB_Ry^?UQj|I+*RTN#q*<8Li&pEC+!TSSs6eunldK}bKP;pQ(jyXjh(+~+wby<;^CR;MGAzOPO~>A|P)Ukb z77d_?hD1wD=D7NK`?&hI8RLqInd6F*b3CqoG&kew_3Ip0J#jq(UX*>623*vjI^?3% z0nR1Z8NB00+xBmauB882E8 z;u*_`@5-vM=aXr@o=VgDN}g0PIvI)Mv7Pi+(9P`3#Zc``c0|vdKH00I9TE2=FsLW$ zY+l*YqUu-Mi_f01sP6lq*I(GB$zNqilmBFwCX{SF=#?*N^80y_CY^7}5+!5Hc+iU( zv-$p#@On}}Xlg=z9L4&Y9Y=qYfusM;jw4F89>vNRj{Z|_aC9*XM>pcYnz#v)+nvBx zUKlf5*plqZma=vyTgXZunbJ(OaxPykdtYpa5mc9ycNX@Ffmz7&`!WmNSU*Rr;uPbM zO*1p~oMamt3J|-OL2?7nTAB%>KF*S~(y6k~t2bgtr$8T86+&GRjperLi+!9A z8?0Gy1vXf-h-Deru~%fx()&PKMb)==qE<$a5)K#M7r%7_SbO0wKi&erlb6fYXMgx% z>5NF{%!4b9()qAj55TU#`cSAYbZUU_SmG7yC&x~Wo zEr;_{HB7ans)t$?O*ONkbgIdAu!pIX0Vk)#&H?{J|T*IANeJ9AK;M3Em<@R%h8Pm!13NU?x5MYE}#W%;#N zTCR9?wu{XAkfpKn2eP?b-DqO(-gR@$1qBr_w6a08ATwwYpVxXkj;ze9Fs24ko$2;M zunwt*gOr^O6f?|TqCUsfKAgG`?p7A-3Y(Jjdl8_JEj5a=YNgUNH9hVn8!hyXhQ7v)ZML;{KPMNzZewqd4sVNRGB^&( z1Tzn)6O-5YOx4j6&U1sAc5vQX>%8P$Rov6D@5C|%86Sh@i640@A#)kbC)z|IXGafx(&T9ClBlY%Y7 zXC$Iz;LCtcri$}~I-aGl$XZWLze9t@ioFrVcNd^L@V~YU>4E>XAsMf0MbPu6K@Teg zl*&b1B1EEAA#s(KDU67cCGw5X7Qi=jsrFUy9cW{qm`nHtf_zq*on522Yd?0`E49O> z4;pZ}UGn^H|zjQ_nkv7vHxVne&~7XLNGezhwft^EV~c2X>bPt8~B-r-I+vWhc zX=KT*Hb&U9Y7_HOmXS927mT<_^N5x5B9`e^%A7E-^YI)Iv(P!W+%MS11N?X!bZ2A3 zUGln}J=JCrx(QA-U{4mYn~JQPx}A-S)ke8Hv9YrW?!;d3x>#xxcvbiz`?|4SZFt>@ z4V|r}3f#^Iw-WmBtKGR7&PQN|+O{1VHcV`&ZO4zj?RIBp;MIY9)n;Q#)K}XA56!$A z+O-{)WUOLy*$ZLFbm63b9~}OCpY*KVUD0_n>m(pabtkD)Wf)$B?j` zpuDylh-p_4m}Hp|Q_?gDv}(zNm|T2{QsP!HL~gD&s&Eq2o~a7R`M z-oc;WZg82B?-nFjaax9N< zdHL9+@167lP$jR#utJ=*fTb8z+F`kQsgZ<=t)P&i-kXJs`|P-QC<7M{@aGU#K$~MI z8Bb{9WYO6UcghG*qq?43QF1nq*mxgcgE28+$T&9M07bUp)>WK{-bL>x&Tj=R20V44SR9RPB#jP{N!lgZuE zWKzRnOACUJty0I2SSXgJ5=M~Mxw6vTobNA3hChT2S-qG?zv0^Yr-%$-lRs93ktxOngwljIFoyj{gn7oZYzsq1U zC8GrvncUe1>()82xp-~^m#w)Q`d`fxzfbF%{3Hmd`MU*l6!-#5+dspPq^0eDfQLBmsCi+n^XHai z`_gu882-mVAFKk_5yN|1t^NkQ7hA3V0etCfWaF^*hwy!3d+m?l16if9>|LC_Cd)SQ zWL%#9MM|+d3{F#RxWn~VP|UVGWrW%ERz~G!JC!$ca$udR0gI9`B%zf`dJc;dTQR^R zqtet{{|i#SrsC)ykb>1lXYH?{Ra}(bmpHR1>rBQ*Q_oCe;YK?PDf!+c7Si+W1`8>9 zw_u^JXRbX5x7@mx9NcA_$%%PVHC!9P`(M`-+EcX5f+#(nKktG!38{DJB9IJwrX*SW zlPj65zbN}GcaAQ={=f;>FTc;NSIaJVS7vPy{7b1C-G?|LS@h;As77&GrH;f% z@jAgu>PU&rFLXwGSC*1$iD^x@Y8wJK?jU&pxCIK+6Ziq5$+{O%fQL8>t9fDe9kwK6 z74f}*HHN|CKq$;j>ly~t&TgyaUmE@Qz)LbU>Jj+Tv5nx`WAFrQ{`=qq*{QL0Cx?K^ z)}P|Zs8khg#qQwG!RD`_fs)addI*ZylqyE%=F{h1dTDH`%Sqd5C+#lY253@R8-5C<a@CLw{IKWrd@ zl81AOgsFTW;oC+=9B^}K!6C4!7dT-P&UE?>r#V0GT;$3DILbH+_2!&guU{Eh zoy7+DtT`cg?FX%2R+d3zVFQ1$u5` zfs*rzg)8d>3mX5dSn$EcI!i9>rgieLELWnf(#i6dO5kfY~{m0}iPMsG(H6NbkV-RS{!B zYjNC$Behh=U4WySLFmA_%W55C_CzHEXTI5PlpRoRutuiFw%o8%6fJ_v8#tF-zX3bN znAvxhszCrh)H+&GgGMs&XN~anq_c7amYw=P^@2ZT!t_uzC^LkJiyjoDR8iVjD+8M{n8LlXMczQA{OL5%3P(58 zTI2l6sy|L!TK)0dO8p#f&|$EeK2h;Xi%Ank0cR8kx?A>)Q*L@ehl> z9Wh!N68mK%YH!h`6!nCB2Wt-&gAmRkE)}B6kj?m4^6|2G6QY*XyWfVsvEXZwDMVhAz zd?G1=R9JGuf~*yD;@^rSJY-R(u^44?N8JC6lLXUKWNQmp`=!Y-aYYEbrTfKP_v zA<8(>5hDw+ZrI;LA;~a7QdD7I#pC?ec`Igq?A9pIiR>C}9N5(&r@%BPeJ}(quT-5! zOH{jI)7l~;)Ip#$5WTKC5aMrh-2)-suPZkYtnZaP7|71Ty7i0~1R!al6M7#LQRhpr zf>fe?NulA|xN==X1J+~#xO@39$t*Q5GR3;$1rYWjY7r)Pw_K)2knbbyDohN~AalxI z3qpZ-jS?WH*vpu*_Gn)NBhLQ_lQIdrfT44+dVFPoRd zyT4QoUvAHV1{AbC2*?k1`T$Qb;!>dveFm!@8ceXJ9NaPZqvv9HjDs zTb~Z$kl5JelGnoOlIT;WeV`1Ev%>@zR@{(jM4*Z-xo~0c`Ki+mNL_f!7isvLsb{df zYWdzg5UBD_(uE=)kjgiti^y*z#jUebvbZA-%M2$WoM~3MCmSjCFS#R00Y#+(5lHID7jq{Ri#j@10J5sAZQ0TNOq`I1g}g-3t@nLr@(;D#Y^hsWHR5V&N3%fP_gnfvAP7%mrP zfDncY;&XYaGlS^r*Z?X}lh8$bwWEgKZ8cT=JV)Rc`0 zd>H9c3V(|Ao(WL zoKUfnzWJ$I*$aPawfjIDVe-U%ZL)eu|CXD83)n<}l{9WX09X(>fM5Joy@{Ul(AhWB z&F5)U&>JPSrg^h^gkqoloyEsWd}tiP)PZtHtqZY5o_;~U(-=m+1g(_!q*%#B1cqqj zEQ+iCMe#T1EQ*`ry6eW|X62*Wh{5uDfsPUL0lBJvIE=ONs+HE-c<3b%7v)Zy40$Cv z^y)B@Y8Do>!uh0lEtjv+uviPUAp)pVO`8cI^(&+wg}l^?Q%{z*8c_iFLAAV<0+$9a z;a|%4R082U`?83`2G`q@Tz#M|R_lGeksgqr%ML8Nw6TG#w8f(OG1DgR-KJdkQfeeaxuWtGzH)W9qZmOt5T{X2rD$%GTlm@WpCU?)Yiigu zT`&4VNm;o-O?*=Ca0bie2>P>Pi~dXhroJCzA93_dsa-|H9@6Vwrhp&eiuOAbD>?)4 zIb!`={9?VABi7$5uJ3Yuo!tDO^mW>?sR2p!p9w>vpR+d~j*&Y71iJKCDhL`<9cRlF z1kixRo+flvH+L69c@EJ85~vXf)Y4b9;>JRolGk801ab&1MJi>&xBJDc=paToBWF*$ zV4zV?sbbA^W!3jT4y$MCK8k2>+0}=lBcKqMjz0zuOr^~%*u04a!oLTdJ z|E&4$oLMs`uDiw%H@{xany`(bUkqdFeBPNl3AbRmw;#jp_| zckyt2*o?@1%^OIt5|pz^%?Po3W{n1G;&kf-=M<5lMqpe^Um112SOZH79}RwGlT`9f zK$Rd;9_-VXyASN!HvpRIpEmE&q+jV|6O?O3M84Wu`;7d;>>t-Rcow9-8EboH-5I*e z^8}#+bLQpu`sd{*bLQm_iR*Vb+Kij`%X#S}c?YDz|Z$8qBLP1<q2eIOQOiBcoLH0_K4Zb%%am1BQx244> zWJeWz$}gKfn08dQ+owx2uo9p`#{3OI&|Scd znS%#`IL}yYd?X%z@F3I(ksR~%!zXz%PXe6u5V6Z{RMoZ8^@9KC%oM3pmd6 zS~F}p5|A^fAR0mkGV2I+?O$Jx1f0_G{J6Ix%#h_sK(PotB*W8{Ev3KFawMP-ujIlL z|I;A}Iuao-0CPDKP$YY{ZR=e6^YO!acF@2kBZm_$yNvKFy`*a|M*`-C#2Oz?Pw?eP zK)rlfjs#qe1f-HSDRBtt9uSmU%aMR~OyhDSAeA*BKZ%Yip|6rKB@jQR4#dClpVo!+ zSHi0;uG4VF=FD>R7(_Fxa5Q^ayc|8Y96k2hjUFScyW-&3a_A8+I?@%mFOVT8^vM6M zmt$2hcl}xihAmL6O2RX_9Oht@Dlh{S`iw1D4s*cSAa^8-ix%dP0QU0!I0mijNSEdP zaadsZl%TUeE-J%PAxWno>}CuAh+9V>^&IvgI`K=Lyn3*2QuSc}9QV1oxZ*6x`uO1B z;PouXnjlq`gC$DhUTZO)KG}P+-FNBylvlX!#bEydSOvvk{}F$r6@&eIxQNw5&3mD0 z=s$Uy{l#Dx7X$wj=!E*3OBe&Wm0l4uEX358&Qa(-W#&U&1_%-E%+AE3#ElH+5y31YrJ2V^&()#U$3P^0!k(mN zJ7?no2FhK?_-%ABp9=e6R-QgBj^MHiqh#W2pxojI3O8fbS1*wHx(gI;eke?!+#3W? zASJza0r&zt?~oxHA@9WAv*tJP&E@7ws8G<#X66PzGr4(p8Z-GiRNAv9_}SL49V&=ulwfkt$q%3b1H}Vy31j14wS>kL+7xEdoG9Hlvr>SaJ~=t zM7R}Z25DVnORn|XYMcEy*ddy+KB`v*b2m6Z$CBFIjKV%ZsXZP74#v;uqXPXr#cDu) z#zNjO=x&R>R8h&1?WSV|6RwIlAlPoC8^m@TcpK;n4G5Qn(zma9?p=+aRbpzbEZ25Z(WJ-|E=iUY*d<=nW~jVkb4=d zQzF|)B_jKC9pog8;pf7mN@)hH_RoOZ{4-!z&J5sgkUV6~0B(MOXFzWU)K~CWSy+W7 zoJC9=!o=tu;h1U9=fct=%*>?I(7>(>g0;{O#bh1^cKb1KR}Kca83vMNu+=c6+sEW&D%o#kLG;s4+9{E|mH;3ii&0Vf=Gv+g+ z;_G?p41_b?zc~obPcY|AQ#Xy3Nj%uqT+-E$4=sn2#&RB7?)9VP{v5P$Hy173yfo1A zfsoPSWn~^(JQfKXEiPJam$#~gGnc2$u_+3ExICBx7w+cj6K>ukg$wEvtVJD`KKcG2 za8hHbLbbZnxEI0hnDB!<*Q%M;vg}|iqp8}Lb-!N?-BF2U?|Fh{zz>#U4p_LG3l?s^ zCv32MDhRMNOt7T$Z(eW_wPp%^AZ9mI#FjWbELBo}^5uXZGLPgyhP!Qb>{V`7YZj*% zOE`wh&jo>+ON<&_p(K@!smS4Y2PrX3c~h1p+2;u%WdrvIp-24?dVLOrxSIEK|UQOE<&9#^ZSikBQJX{1!$hvyYJJgqP5!vL(N z`*NCD0}(#@bHrHvOz2#PWqj7Nl<}nWus68Sb{TlEDi?v)v#$R_BKf%MZ;D#aTns}d zkAUm021O(#1!6golp85`DSjy~cd;rrgi+Rqpt_ZeH+pda0wi4uDi8x!eF^{f^6?cE zwpo3}{HqHgNcHO(GwBjE*?S{6WFg*kf4Mn5-E0iDTjjxNs5dlNhmu-@wdP>ykB?wdE=tqgaN0J@~8Jy9o-z;SKe_2JS+X>)+sy`?}rU#&(PcP{C|GEyt_>54h;w z3n7tT_B7)Vt1D0vt~NDS-5wrn{yWeNGx#+Kn_BNaKJG8dLX3!ZfIZRw3%&#e#{N0{ zl8_GdzkpVViv3IYfvmS6uvx&xg}`r!W{iscN=7$`inWl#qhb8n{|DWCQL#kgYN})m z;Z#K08b4`U1q~1oYdwRT0iAx*_&NmJ_`2PzNQ3Wo zg%5BV1Br7APjba$De;F9cO&@QDYefJSF-uL&Cg$M-j&8*z7D<=i@)4_d-(jlBRKx< zko*-3)n(@`t(bzp@P-^EyVK8B zZbohC)&G1Qw&*P(%+0us7Wia+Dmb=6y?BviKqodK>kdJfqUNK^DAkuik z7+5FW6+$w|24Cy9!FTzg^E%PIDh)c^4Fa7l@xaYXz()Iq(4aH^Iyh^O_X8XtsQ6>7 zwv<2xTj=&tkd~e2y`)S9`>IkocC&_?B64--t_f*>TK8UP_6I$DF03e;sA(j_I ztrnccp%dOIG+HbN}*p=s% z#FYWOaW*nlHsfAuV=oUYR!7VBXS*d#qY?EiLU%L4I0wkRJ^khoJULZ^}wN1v}niZ zO_Keq{j&cJe%b$~9NEv^?sQ~7H{Z`@LvI^~_zT!*cLK)dh3S;3g{Ba6;J^D3F(6!W z*pQtZJR@eTz>E(Ya{gpv<1s%rp2)!lciZA%gPWz>uxFpr7$zVt1_y{Y%5VX3>Y*Wt zlPEwTJ6!RjU?cQ2#7j01-s}g$;T#}vHx~%pj8zqI{kSB6@U6iC;ZOny20N2jU}cmB z2h0XSp^&F8j`)!8;FpA(c4HxxEkWa!Qnt=3E_7`r69PN1V9I_ z`$fTK=ylrgLF8fJX+H*rb1=Z&b~qT|X58r^3~T@l=#@seNcebgNRU}W`s*PcY|bWe zaYP(!i-ix0-3C-NsZG>M=cZyDrC_dTWC)NhWNVur^>EKZF4Xj7_(;;LOJCkS;hNV2i9M-|2L9Y#g3AjS`OPVb(IA|&3 zB$%Al$*3PO&*mV8ySYwHZhllE<_?V*T%A~yg$toy2o6HCHiQ!25;0|cTwi^|G-QF{ zV&WweUz_kj+P9J?kH-AydM*cD+|6D4ax>OgB6);s--I_TTv&Y}I9QEH3)gjL6P`>q zC*_poVI$v=qqepIR4|@*7!sX2IxQs2Rr0(aE-&Q3g}b?{AZ}h#tDxTx4kLdjF`|~k zE>k0@%vYPPK^&c#&1iT=pAbG>^C|*{Jt=$l3ROlJQf^7?uEvd0Mlf!O_I^XdI}kqw zz{pDX48&fr&sHnd0plh7nssNX(^2D2@fH|t6S=m(xi2K|Gl7aSG|{K6 z_L81^Zun>sf}$(UY5W!TnV`Zm$#vL*Nuw$;9*9zue8UwmGE+9a>@CEs5!|P`zVuzr zX9p`+1kdG}o-g`m)TNvm#ogQ)#m)Pcj^w>*Q()n*;iZMOA(K`Ji}Qt>l5nikuhlF^14Y|KIOcZzWiML3&IQO@WSxJ*QIn*J>C zP^hCsgQ-*43>SkG@pK(+-b18?97GvaAKA46#0C7ToxH;GG^CE-W9S9*8`|iyc#Yz&(>5(1y)EBq zXPorJX9;2VX#XJ^2Jw0vGRDA~0kMy0D&n8TGehHN?1O62x++eWW^naL-3t=@`)H(; zyX8#>4>EW?_m#ySL4o%QnyogNX86j=Fmnx~o1f~|rYbSi#U_tRN?y`;_)-6b9Mp5S z+Z@z$^WAI(RkzpIf!%&~u7LSa{bwY-Pdt5%Gdee;jpYrN=IeX?-3p2*2!448K^|O! z=IcY7den~-d3!22zFy0Wr1$%|%FU_ygM59vBaOM)icJqw8lMf0tJgFm=>vYYax-d6?_}lc zcQ|b2W-PIVp67sta6LG-&Nm}zGi;hUhpA=eC4JD(X>P{7crT~5IW} zTID4rcBP{PPu;wv-QY`t#Rp`8CsUGgmbM@A1K=Z~d2Jd1xSOk&xmoJv9!CAhwMMX} z*Smwmf(+T3eg~ z#NAvpar4qb(}#jXlPnf%po#Zj*u5wAj=T_(+e(R4uD&6(&GK{ig`$u7QS^g3DB^A| zinv)OL@y1Q^h3d+=w;sfHc+JZ8W>1wQ^AWRb=9Bv*Egazuu$qJhT`LXDE(*-l(?G< zC2m$xK}$lteL6TORgzFbfkgaF#7`hB)Ru4}C$w+S+yS65;&0jo@(JT(^PK-b=7-QH zb0EasTnKTq3a?la5c*tj5c-ZJgi^R`<^6i)k{O}nq!eq*sBfS>06Jf{vS2@avKQ-LC})x=$oQm*8uX)5+zV7lnyM9zZeCb_ICsKiT+a{FwXa zIhf;admOJ1H$TX}fb?YJFg<*~7aaPIGx|8ZLT7B_UMH{qU1aHuHKOhOJ54>xWOEO@eaknw;bo&sawrT zaI*@eO(r9UiHJ?XAwniH=tVH%!dgM42nuttl#Y`fFj~?y&ao|i)en~cEe9;z&9y(d z`E^p2q$d1@iJ7~C!%RtP6?cabq2pn;-}uN_(zsUX&F3&mEzctT_kPs;S`KQsn_Hrq zo0YS8Gi+elwcao~Gfc<~1P7T9F=Xs77%^Y{0Kvmf?E5+44kCTYa2?Q9*F@8R<>DDg zuA0sC6tTgD2WNzt%(Oo=YjXSxt5wxQJe#Rjj>J(@awFi9T2{)a@#G2>f?ll!sJzqK zfYR#3x$H4c`EfYr~}B5mTwQ91c@u+I;APID zDFNWccS0t}P09qY8s_P>N(_3%9*>NXG^AZNvNO#ijV>p~Be?``NZ?EvsAGw(r7-`|Hl_82x_20P zNpM=~(B$+sRldB-(oX`Q&LwAZ$*VM-^n8)c#tcb9U*6PDoo{}b?zt&2Z|pZMDDwtY zWZHW=WtK&~3nX~B;=QEa)4rjD|B7w>!TvCEahHlaoL0(#K z<2{V!w%?Mm#~@MUvtW#;`<`vcsw}I8N11nhL*M5{>xI9(<<{nB2u&o*P2^U^^F3h! zi>6tRPs((7RjY=YhpW2wmXAN$X8szf#bkZZYgW03M_n%RBF`|;^{}i$GuOVzOa7Pl z3%#qYu%k@25TBwVBWCo42CmoLC*5bale;k#A}AJ-a)?gvt%;) zYb=|vIcp@pC1DJvjDjr6QkPQ3KsFqcG#TiEZ)E{5<%^jZ%c9P^#z-_;3oC=}wi^wQ z^(fo37JxH&-8u+5;KRFdPxwFLY0FTMrrPXnI6N6QIzunTl-t*5`b)pypP!d-YSAM0GTf*lu=Pu^jw{<^aA>)rsKKZ?yRHQ=Xxv8Lv5zH<12fs zppNBaJcm73ff>{4(61|}YK?gAI2?IXuEKtG8Hy6aU)|nKP<(9nuDSUp>+2`s2eRE_|0)3$m;Gx+^9>N0_STGU zP)5{34UdM&hi`*!)$P4CydhY&Y)45Qs-~ zulCcuS`fI>A%L6Dq|wdS*ZR9{V!C@~LE#dEn}l6PoFwMr+FIXBM32j%cVv+p1N|ttnmYeo5#`s!`EHFaPzH8 z1~8XG1I(x>I_=}?Rp3SKn+IdoltcUQzs_rs8q^JLO_a)UWZ7fvtiRUNSvUi(G0%l% zOv=x(jPDq$P0vilwtC@sxI8=gUS%hLC-RO(|A!XdPR@7-Wi=CtaqoQ#Xf@}PHQD?6 zE4HC?M_*`*`uhO{U{U`7{z$W^zYi{=X=UCECe;smn*A1apy>KucmXgozNXMMU)%eA z@Quj!{wVy?%M?8I?}l5Bud06!{6M%^uvsG@=E7#LXhuu?;~CvHIB4V1unyiQpqtMU zCwkYSQ)djk2l1SsZjGP1wSo+=w=Ga}GnR_*QpeYK`@6whVCrBE^OHzs*uwU+NMdl) zq5erA3vKYdb~qWj^oX$wW=w<;Sn1T=bFu_xYUR;c<$n3Fhd(tJUgLkAmX86 z%}x?-tzU-R;>XObqIp#sX1LpW2Q%D^qGu^#=C{Je%opWOMY{d08LtJ4f>#lU;W-K9r zNbUI(_LhMD`!7LbcYNl?6)T<`h0?OES|uhFF_`UTpl46V*ts&dqyS+0C|cZ5!uT9z zFsW!gIB+Kehj<>3xpm0VMhfLPF8;G`vqP0gF~{HlL(M!~X~RxeO3;!^K4dB$Zna9Y z^oEBDMw%f$bE4Ub?2IhPB{hi6qr#lgI3E5(5=k)d1~yG}oqH5Jj2ncu7orj`b3W6$nY8XVl~s?3=%VWQ7-Hkh!#!qtxKtL(GnqX@5SrsqD^`DWbe)WJ7WD~B;2hVtR3mAk65^23;9=X#2-6SA{M?1 zyhMkYc`rE1Zh}5;GY*lASXfj5{FBhWOBMjN-VSdxf(k$QOL#$Yf&M-G(o4?S`aeKB z_&xs-ejvXrcCM@w>s@wk6wT=OT*YkmbpyYrg#sQ8Pg7b8-F$veB61b15+GzsP`1WT z*(yN-_&lvga5FkDy_E6wyZznnRg{6G+k_;BAft98A&VrV>KowU=)c@!n`2TQA)_K< zm*JiwCf+4)k=mgk8?X;}qX>6v{oK9T&)v168NE?n?s7NqMp>ZXW~>1xa2FDH_l3aS zQC^Ho7I}Fl_-rmK$q7W<2a`;ofGm8&u|rr`2P@1WWIGmPBEGNo^L@Ra?;CRX&fVb1 zK#T9(jORZf-+OparhG$^Fd^aK^&#NkaR&!9y*2Za$%09ytL65hs`V`)e9gb)uPF*3 zNaV@6r54LWvc{>`VSM_`M1>@FzUiR-x-Fo~J$J=*^Y!01S3KiFy%W&X(FA#}RP zubUZVKByL8r*VF;ar5ee@?h|=NwHdp5kYZpc45A~mx#&U(^tX{-&l!-*=^RstY|Rv zs-w!!0V^Q}ri?$}987Oi`8(hu8u#YCVANMU%{WAIjTQt(UI21oe!gZff>yORl3c?> zE#pqXyVEl6?eGgkk(-lfu6`C8VGZ|<@B`Uwv3s3>hRg2#q8Zn46B*rL4QHW*N5i8z z>(I@&h9e5^1*?I#cH)YbP~zAW&W;6RkO*JxC;S#cAuRDM3L?>rivTa-+zl#oTHUxA zk0eCGdmF(R9E&@TGcB0L^CG@bNJ8;4612#GuQz}QSOJdz5U6c7XgGD(_45=PIPV-BBiINOHrv&iJ%zEbab&=>%?uC@M8NDhm`*W2H$>G z&koar+5TWG96do5qNhI33K-yWiNc+ zJ$M7$U#zu8*xDj zqW6U)h`#FvR}k4>Fv{XORPfTQkGidz321~oUN4qQGmvWuv#%E8e*P08)*yQ273%I> zHmo^)uQ@CNoDpk>ENEoYbaq}tO((#Jhbrr_?&I)HRR1EpW?^43ez9AcY8UXG-P;Wc zRWB_z@v0_O|CV?so`1>(5QNp(4hg%Zl?uDUD_|LNAY09~{j!?H>EgrcvYMs!{$iIgXu?$V2yp8*<%5U`jC`@^Y zba4UvMh>CMT|{)my_xIjF7iKnvUjL|FOheMgmdUx@7n(qFbOE9j=HOe`k%e-SBJ9PZbD4xWwM;f*Js1!KB4 zQHxvao;)4H+6@Ds3a6htd1iQIXy3Xg&y-r=5-D6dHav9V=srV|0L?c6Io*9L| zP8~jd7=9l*eR*b*_$gVO&n|#C zPmrnvI#Ck$TGvycr#*eL_hkPfJ!O#VF4X@3SOr4;5AjEuQ2%|nh!VlP7bL-d_B8v2 z`r=~X_kd2A*h?4#xuX5^@QNhbe+Pc)d8Tgtci{pE_x}h#kd+ooHwgB-EZr=cQMmsX zU$;%-ItX_Q3EYflRecG%Y2m)ODEM=vWwB6D{}XsR3iz4pDBu$!#1!#_5J$wXrh+Vq z&2vyc*S$QLd~!b<=jX1c@Ro#3_73zfthtjC&ah*?X{b@f!X}l|-A1EyZYs`O{OnPS zpPRO7ZTU?L_U#845e5kYq4!}cFeIPQTys6nzsTE zL5*(19}tM@-7Vh?7g6?__kzsb?rFv$lDp+0fMXL-3u5#&0*+em&Xlck!iC%>2d-V+ z6u%e08XLqRaFOVF&2aI2eI0(^X~Cvtt=1a6daYL9E5s7v9w)<4<3+5|_e`^SK2NpX z45|&RMyNXahDE}?L$Fe9M9%`@-cQ37sW$IW3#Hng1|EWHdj@}`skR{jmb!T_sJ3BG z^Wv*E@7IKCv`2y3g;H&P8m=Wc`c!T8*TGxU((y_7161@mo-eH-NcIS|u#g%sTF`xk#zPqUopu;&)5p*Bt z_>@BWcH;qkyJPIk)xKPJmU8q|);AXWcQo744e${1MHo!CQ1RW7Qlm5prKNK!ir=o5 ztsVSAjP^?lw`?_uRY)gV;$CL~maLxVPxhXg_i{Bk%tGn&cLU!*pMNXSQ^em+L`}r_(oJFN*BUa8k3Nc5;r2aFQS7$ zgj#6Br~s#8bOz2bgA*4!)n+Av-x|$mstKpww4nA%}-Q)W&zr>5f87@n^r{6_@(9MfcrpYz;&I*s#u-4$@$ zjB=4UuLN9RLhVli;rU5neCDvwcfQOpo_Nvf&Vc+=^$=VGbvM{K8dO|Vus^-G%@2zm zq8W2Zy?T$k?Q<+vZhk_-Vw+{Ls-rBzQhZ+y1S8!_JW=XSbqpK7nBXd!w24N?#${ic zk-MaO6sZzu3QF(_z`yqEh6BXioSRCy9GJ zMy|ML*+LTLm;?RC5??Sw!-{*atz;HsF3L<6{&Di8IsWn6u$C-=cj%>ByVF*RB+pmA zQ~Ano-ljcD-@b6Z@-)Jg-<;@*d#^5l-#mww$==C%`_Qw8Tqu9~PT(#0({IBcY5w%> za1os*=Dpx_z1`F7_os&hnr%QexFlbrpy}7=z6!nq`P?_bFTHdbQQrdBp@_;>_<<0x zpm3vrjSGeCq8a_~cVu*fA}SVwcr>h1c{g*x`=YyITjBL*&=xETY*1-Y>LxgY6f*Lz5qe+20Y1M~HJfEIMg@8fOZbZ)YhWz?@A z{!_hvpF-1%o-7os_M>1;4hp!Nivn(55-50cfGChPHd9f+C0eHAb{jVM&9rwq+@K&Y zSSuQoRD_j>a>j)mG(5=_Bi?g_$2vc3ZpncScXMIG%}W9{Cjx}cWS;P_VPmiviiULC z={OqT>wY-hlM1JPVYJZ?qfI$5;%+XCxOqvy=<5T7(aAg*DYmA;Mxe=$mM2otBG74| zQk_Pne#sF>Tl_fMmV+bi=HiH(mjsR`0>sh3&BIZSI8t1XV#>Z6>(0QwR~yTj=t~z{ zwGJvO@coz1pJ_qKv{Y@#Qf$CCoM#vdzN$`9NCs>nJf@F7tG$GxYEjyBS2c|xxvo>2 zj+F<74ct8NNH+6fPS4q5p zo@R?V1BDll*5-~EZ=LoX=lJbL_T8WycNcNm@OpG1@|R-)rJ~#B-&OZx7{1qL_?YBVq&VhI+|?93T*;ofxA_^# z%{T?UjO6QgIy0A>(W4h2Bfk^`BMXv|7n*R4E>w+*&NZ7DJT9V(@H~JCs0|*w?7W3$ zHBE5bawb!LtzV(;@-ua}XvQsPFH^bOMu(}~d|Q}I{qI3A^~sr5vlEvwW58sS5f+K( z^A(b&lN}R@i!w5uZZ$>$?VQ8!yZrpVFNfdU&E+>Y2g>h%41wR{qj>ItkL8vrWqLvJ z6accM&>oH+e5n;K?({IGoUF*nE^YD8K8g7nFtT~C&Gg{6M?(A z6M>rpoe1l;hu|mppm!E%hT+*Z0AUmB*^)35Q3E`jg(QB0wVk8{=~@+h&MI@YUu6#X zkyFe;4tI0yAa2GpjL{D2r6whW<96B^1e{K@w?PAlGdLo}M?K9j={AYPu#pu*;4f%0DPRCqdfn4IZYlG1gvo$z|DB5k=MrK z>)ZU@z;Uk)_j?c#LAXSVL16PCX}DWD9u#HLs&j4kl~t?&Cv5f^}g<{L!w+O)~Z-CSmJ zGp5RBYm;y|{7euWekZ%I6Cz(*pU6!8muaS;A<~cCjQ6o3!rQf7Y(x-~WZ!|X5?dn% z*rY|9f}}V3k@R>DlDM0TByN^sA>A$s2T5-Y0!bHBk>q76Nf0lX@ODrcBw^Ijt8WP1 zOjeL`|D<5%kRLNgb1=i*b~&nqo8^9rrzA``V5tNFmM7Ry`$Ay#_<0?T$14{R zl$X>vn)A}^ur!a~j8rU;ef*}1h($vVCy*zXPnPv^y%b&_bRb+4I`Eo_%M1dR_!J~0 zTsC&V^P!gBmQ>;)0v+koM?mD7gS&#_C|<_%kkq zARl^<@r1;=ZZpgf&ul%aDeCuR?ete;$*~`g`#I0eC(~9UeEoKZ^V}@sW~>5$;jsSQ zL9qTU%=*%~HoC?;QAXDPKH>atN5h`dE0Mbt-PNv5HcC@8`;EdpaV1X-jQGL8&HK~9 zz}Ml-B1;T#vs#v#1$n{&!-s+Z!#*1Y=?rvvy)_m`2$13ziZf@%kNTN>Hb>TTw@nU{ zxp}vg^#L;ZBP&+GZXEoBa`ZnG0=LKCudPCw^hq9a5pP2Y(0S)rfUV-L0(gJzVyV?A z@CG2%fD^$TMdA2{oi?Z(*J#p)AU~~A#4R{u$1TP%dB?~Xs>kqID2o%0bv}aS=sHc2 z!vKpHQA`exM~CuI%~m_Q7)O<6xaB1Ng0t2^_lj~> z=FoV*-f(qOWD0wqql_qc`-thH_fpj21^&)-fxa#5ZN?_`>a|I;(*e;g516-=_*{3g zuy;tz^@>1;Vjpz^3kyCdlniW6=&EnRg!KH(uN$j&SIj$>e6&Cr)ks~koaCFl*uq*5d@cm0j{oK=h#L{7%o4XEmvtaRYn>60=C!f7 z+-y|Z$~4p{I6`VdMTHm=RV$I`{`Dl&>@&hWFt=99Z0$;eo>&uZuRsf#pA25m0xvFNsCu5w;^*9QdpmB z)H=nH;S(o@$A*rKo;*6{A#tg&6yFf$)Y4+<{OmF0u#IcOj+2s4%xwM=eo2Dk%-6QV zK8(!<7^7*>yc|&m1lX>Zl(mLkGZtv-J^^sov|87J!goiTja;AHyZl=C-8ovAySeK$ zZkDlXfqG_sEd(XpyTBTl)@GVr1_VIdpBX(m8l8j{RlD1YQBjCe+tF;?DMm7nf|O=a zXqUlar*s~;4zPm?;jvy8v_*Mt7brK=3NFtI|298LxEW9V@>=11-SyvbGaBy{Nf6*_ z9v1Px7X(V~^!o3ZX|4&rQM7X;f6mJ4J${~Y^CM|Ig>AVgWSWIkKORT>Mg2!)U9xHw- zrWpdBO#_;>6=58>@_B8gN!-q3_WS+J{$LKXxtpuUxp`;U68gp+A;g})&eY?ITLzb9 z8|72+TES)WCV5kTvin1RZht(7+uY6NHa7>(?M)$YdwiqgRCo9mX}N76pgr3tLt0G( z_Q)$y0ps&rF9@y|g&1(J=p{tj>H+Vt7dR*}IQgZZ2zuhu<$WseMYTF>S3v`KEbvek z{Xnk1gik_Bb#LP(@gi8P!%}gHy@7-9BQCX~8Ft>#D!Z*#FW5;qN9Zux~JlIYO{GAUX|1Q?b}D5aU)_5a1Mnf2rG|?F97CcPFc` zuhE)@6L-KJ*6xBs3cOGB7Vb=5tKy@wBl>^DKZU=NGljXE>;B|sJdj4Dye1<0*U*VR zuqBZl0RE%m`EEczH;<-Sg?!!3q37m7MHU2S?8bQeN4eq*|M* z(6a83IEe}P29pxQ?~XKQW=9*(Lp{9saI00CrTY)T#Bp-yx8iybguob{mJ?S_;1*Uk z>p$UVJvTp_#(KVfpJOj_^Bcot{i8v!epm+7i2@i$@D#42&5I3UR^(xz!hm^W;o$`H zH`;x(`Tk>mzH@VGa1LL;*Wo)iCg zNjfurJ#Pg+1Hp~i9RG1Y$GJH*Ym2YDLY|ug<@m`UIIgm`#7w6h2i|8#cFA3Q{M{Ag z>y<&F)_^Gm7e47{>raa2)O??mZrr>%%!&N=AlUk>!5haE7WGCQ^O`bwjA@Fq+u9d! zt;vo00T4hUJ$fnp|Foa~pUL5WN;hr}l>gNr_&;f}M=VS*653tF2{6$5f-dWo6)u40 zjfy+;V_iv-1Cf8>hse+7KqRFbHwOxlZV(WeNavL|;hhAb zDN27hM$Fi3yAw|rA)2)a>zGDo3Ic-PuHbt%I$VLnp!tD&qlfsnhoZe0u8Iw)@8HK? zaD_HI=u$&^z{7EnuFAiPSgZ#&vf;*kW<4RKQEr@IM@_ve1m_>M{;PgW|5^^Fxtm+g zf}3UWVJjLU95jD_2x#to)!ap43z^U@vJFHCn%Aruf<} z7w8njmL9F>m?bDnZ3JS`_GD-{B2{R5t>dvxi%tB?hT|9|CE~^h%&Za{(h~0qpYxd) zW$Bf7Hz%D}Xu~VuuBj#CUBvGpUBvsdg(J!kQ%A7Kg;`2bjsNTi{-22ER2Lt2bAivz zw@aftOyK{!Ab{V1jeml93I~N1tN7>-1VFqhYeYn77U=JNkJ0A&X&tnQJMoBOx@_rz zJo{S=;__;%ng(ul%Ipia#K@K!*rqxTlf$GTbi-s z4KPTdBXSj?aH-%zC894w%1oYf4<*Q>3$;=-ReBlU0cs<6XZWe*GUS~&#AXIYAmho+ zC*wub2e8D2DcfAp(O;zQfAb57KhF^me?)4QE-#eF%hej z74wiX9C>co)k6Lm!wK`4uE(UTiG(2TciY9;uJsLV`S0DSeYHD*z6T~=9-aIC2Rjqi zv-603^>WJy{M6shdcg(SpwRD4Z!6$WdFamK@`zef38^JRrd0E5%;&8?jHzfC{`9TB zMm(tcLu4Cb%Z@l)WhgnC{8-D*oC=&k;dAEbueR(7Pz%^)Pr!Be1ZdnPXR9go=e5S1 z-=NED7Lu4|?aOuA)ZlR2l*1eTr&wA$buUNmzd^L87QWJGp9^Cvu+8zpDcTcnBX6rQDzs40+5SbqT6vEoBcelN$QU*02C*ubGZrAuK zsC(u9KuQScKlqvYPog<>e}KEWC04mvI)AF$VZj~_Q`hedA^r1N7QjAcd5#@I#|W*u z#1w_qiTK4>ayeoB^NwN}S;2zEnc5>aaeEpJMsDC695=9P_eQdW1Bly#0L1xh9o_HB zOu!;LxW)Ao-vKN0xaG#P&>~RE4gbF4$f^{`+?)p)*>StQr>+L{MvsI8nfrr)Oi3yR zT2`bp@rt9MT%hz3-#M~21roR9L1G=>;DmUN$o7mF&v1b8Xb`}7IVnqW++VtyC?DSl zKGjaFs`SChh;Tk*6L)37c?OyYPH@C@XV`u1#%7eIiycepq3|)WIY$-HOyzad$TwY%f>S9Q{rF{kViX}M$8=t2?FWH_aIxY2^HC`C+ZN{ zAY(1Q9D+Y?d;^rhwqh-`4=0*Ou!Zu?DOZkS#WnX0_3O01rd|g9(CfZV)sITr8Vj}Z z$)aJA?B^_!uAd2(^;Gjikn~MN)QHwZXyg0_l@eR+HoBs7SxKPsx@sjs5(_rB;xP7W zz8j7sug1OB;B)P`)gC-wI#+8Awwv8n8IL3Aw3<_chldACkAUN>S&z#d{B?i5bfGi| z_d^!e{&I7Ay4e_Px5|U)-x#E8wI<%jw+zOZDcBy=Ql*GD%~dDsABLxQtF`g}`yO>n zf;t7mJy;PU*=MHWX&fp#-0g^BouzYAcX7IG!(uVA6Scz|LbC_}l%{Ow8 zVeJm@i4O^{+u_z^7eQe--bBb6CN)RXU9edp_h1rCRdCpYAq!5q1D5-Zz98C#WSi=z z^Z5iwi8fQ|9`#w7H|m3H+Tk=FJ3)zar|5Vja1A0km$eMz# zSdb67r1&UjmW484RT8S2ud)x(GGt^Qiy%!swsDpV!Aw40;pTcCFs?N^8wH6^Mt!C^``D-lOQm zlu!`XB8v|STy4+__8Evz6WSGdAX z^4%g=#@ZWZ_fgmTJ#54xifEA=9i}NE+e}}cy$Z6rDk^;Gt;#@lF99SYm1!Uh2r0Ms8Mx$|j$nk#YGXfGsw4cpjVl6B;=J{H*`>E0;E zMS@-?r;yicwicQGYQpR^Lrsj(2bwL*g{*L@@ISf9OoqBqkpsY^OmU7uS}tV8-^_?W z(zweOZM+V0W~gX2*B}Eo{FMBB z;?A?4YT99oDXgSET8@lWha{5#o@Zn%i$sW!ovwE=<_8OsJ+u{mxja{>HhZ`0$#;Ze zh1FKuT=m83g=z(FdkO#d^6?ebOUEH-=oRtTD*CHieP{h$7?aI8@b8&`@Royvg9{qf zMC?Mrk^p>uswwixB(;gWC=*n50M{loa<&ThB25hB1LJP8g5JuSzpe-JLZ_pa#H=RD0g#Og}iDb=RtAlp7f z+_rSNZnb)Xb}34f$1gigeAx9awuKHB>%**tZka1{lA&I!ua_$k{cNPx;z)+4z2o;1q%`knr6dlh9M5`Pm( z3`LCn0TQ!_kgfkZ5P;gosRX6h86vjYs01Zg3Fpc*TiL9<)z3<9-kruuzP`g@B{yS@ zF#)pBSox=+u<|w;qz%EA&5M-zLELhAxOV9D>Cw|KjEp_aeB9`tlAHZ})TW0U~14Vb?F zVV9>npM7rVxglcf3nRlPP7L#5S=l_^>gO>xKbFR0zV7mvn;#99$G3*U<2R)8IE9VC z^VdPzoUX-3idc9A<*LF+&x7)6WC5=A3-#@Ow(k_pSOC;(0dlwXj!@_3+k$2L?oinN z7GBj`YjA^Y!rU@r9d)`LAb>1rk!WPp!y+WyQWh)v(FUAR%FJKwXa4Pe=HH#eeC`I- zUoG35o26yeTLbwG^m^#U4$9iz7YGW*Z-Kg2?_ZzQ*EH&mB89bUJOA?!-jg!qn!%Bd>eC z0bqvh+u~MJy;!kK$eXp38U(O=O&RQO0q=cq9HZiNHa{{1eyg|KrOPCtwe#Kr2b(L)iDpd|3ZT zde)o0VC(q#IO?|Js1$W-({Zy4N8#Z~dYt4!=gC4gx{yzFC?1wsB28FSfymB?XYfF8 z_z#1hYAyK8=i+h+pHahlOn^sGt7)p)gp!j@2*-@e=i%d}4fhT#qf~q$mM->pMAcFo z#sn(iT!!cz)UE`Q-T_-Qjm`_0!SO;Fo^lRA_>NQ(p9h%YyJAm%?}3O2nyyV&JMaWp z=D}WI4C}sHCx-f%z}!Yte1~>aYT5ObN(t<7`D=IZA^epjL8>>X%0I*ymQjeO+flax-?;*m)6J)5;Vq$* zK=F~{!;r{0>HWf_mc)zsk>j5al0ep63<*osrP$IL0M1o~@~QGCOm`?zkTrY!xX1qb zVYLRV@}Xu{b0X4i8{kgbM3+z3GZSz2e?57x5L)T?q|-@HX~Pz?_t0=T{^h7#>XfUI zVa~c%N~++(G3F_cL@Pq`2J&!)zCH(>3ZRE6i=Z*Ri>i> z;StchAZN0^rt`8__M9{D${hT-;Jk--ZFHgzsEm!^7zTo=TBx6 zip2KK0=(HT9E?aoyh;8;P?)1C!d5rlcd=#mqm3UP2UKia;0-CtaMCjf?MSb1+vQN{UtAPh8ke5mE(%!Wzqb6Q7*F$VDid> ztTZ`Vavg!NwQfPdHbe{VeOabIneVXXNb74G2O8Cv#RZ13s>!zKTmJtckj0cPm1X2OkvQ1Yu7cB1$Krua>DdnU(|t_1RY;*tiJcS%3h*M|4Z`aH{?7iA|% zBU@_mWzP#P>X$t)!sE2;dFj}yU(TCl&x`yL?oY!VuHS@yi}hftmu%OL(*S*K$L$aI|o#GUMqZNrU`cp8d&)5>$C z)BsmNZW74bR1(PZ=y~Dj>2F!MBoH6bDi?%kihHjtKrToI9h1Gc%sV9{eYl0n44DLe zLS{%6f23uG#BdQ)2+VsSb)e>H_GgBK0+b3+4AJ(lF`)FDF7h0_J*A7h1AgiGNB!LnD$+ofUO*ZH6MXMNnie0=^)}FfNhD*^n<)H};O8rM zTTtbyGg6v)9KvuQaNTsm@yQKs4(`#nKT~Q~u?rsDpyOQ$4L47SBPp`su+9$$ZXQU3 z17E+_fde;VAecaEpm6v=U~nkdaM1p4K2`*d(V*i+TpsW6sYTooBjL2xF9B}xvwWjy z#(-w8aN=$d&}<0~ZoWHomVYcTmVX29hxs{ZY;%!&pZU-8|7bi>>P~eMFp>MM#vUq( zf(N`0Np#P_%_cu?w&dW3yScdG=HPMj$-r>)jaj%!uv7{T(`V!WN{SZRxpI-Svs9@R zlKMy@Wwl?zZ1W>!R}NCPi|d;l3B%1>S;APyxQ1wJ{A^%|`I22^jqyWOm^61OHfXqF z8Xd`XaydloCXcuR?o#BbY=OFg2LjgoM7#XC7BNBZss%J{vE$vE*7-k4V zqP6!BKCG>R`=yve|Ap9icVW98>p%+F=0J6~AF6lfK=n>>9a3*CWysBV)-2jTy$v$K zgI)t24Iu*dR|5m=SubD>@M-QcY)$dTVIg8Iguxz+#$BjX~nyoLW?D0e8z8t9R z^+Sc5ml#xjD=?^hEDtK0yB??*kRrH%d6fnmlr<)@q9;W9PxNNW0)lx?J&RQ6&k6xT z=KvvXgNWSg3u_Pfv9>=4YXx!L)u7zGU5cfffn1dTp$eq`8W_rc#fvggh?k-<1y8dP zru3cySqr?&7h+pHA=BH`+SWulO7EL-H*- zkUSu+yPA@lrB7>#Xv)6`3z*}l;S_8yHVw&^Wb}M-YUxlFunRFUCrU5lVeN6L(VaO6 z;t}C2YAIJ(kVce+1gD3pZWg%N1jMj=IXxiuUZg@K!_CKL+nsp22vL4T@Ru|?Q;ovj zw-fMrbLOm%BCG*Hl9y?sEGdj|kM>3n@uz@a_D1`uLDFGZtvKIUFQiRbyhZlJMLHyg z>L?K-^xNPIWa$y@iQBC&lrsI{^pZ(y4eMA?U?yF)YnN8Dp?K(fLu+05i<~vZye=IdjOj43V@tJnaKd zM<`Jds)fL3(q)q;AZsukL3)K_*a(u@aG)KpoUANg5vOp;dbd8vI~{l1h;KP&-!#e& z#uxFUbQ(@z287D{O|vKYN*eM8&Z#6l41pt=<`IISuUG7JTMc|kqu4dhTrn(foGX9)I0r{faqD%oBdaIhvz5N5pm4mgDC8mWnO+4`Hdy z-bcOqQg}l!kCtTespixaWHVzeSu^xH#yl~)K^+%KDf}(xFi$;?b&sMSMJcP&+tOUT z?AlM+cYM|lwdZr7#@%jrycyj5Fhi}nz5Y&M=2|Xi=`9(u~`U2Rfi!LCd2)-QS*x%e4te*C?BH4J@t5 z@>&z4%nnnRiM`NLpbocnVX=SwAuO`es$GC2&xrG7L~2f&6w;h*8;7kQ6PuLA-qmXj zR)oaMb5LwPu=s|T!qT$i7on!flZR(!rfQJr4$F_Ra&soGtYe`fz~$SNG=W8mmREM( zv@jh8aTQS$#8L@FFCl`;>XRl z9Ncg>7dPA-JZ|0-7;b*)D#VTAG?Wa-P`I(~3>D3Xtu1}&Vyo7niybIueg=*l&XrOF zzTrH>Sa4m8%}3wFP}b*L!eaFJn{IR}O!}yYkhTt#>260*IuyeSRqV9fY80D%zOLGW zW%pFZt@AW{<~yJvmC^(r8&-~9%zuzlifIE|k~Duj`%5GU*VAY*XP9uG(Sq4=pKaEA zOuQ?c=R3Ovd}pvWhJ)n42PX(G8DCwvQZNaM)r=*A;@<26)PYGOVzT%2yo@PqW`;76d+i49Eq4!fOT}{d&s23*MVNWj_qR z^gL6({-bc=`1<-M;0LnVV)u;#8ZNuni)M6_{ZvLbI4sOU36F*+@_q)oIc_q1I1!nQ z!6Xt_`$@b>&;sr;>sjkW^BdDhUbX*LFN^<1na0NpqJ7ClBDLM~=Qv^=+ zF;4K|7$!Qzhq4-EijEjEJ^WIQC<%OWrqn7;bGRoFL&*@sM=25?&R&8uB-Ft}c(%~F zn38ZrE`=I}BRRdVDdRuk^(eX@GF9vs=1~Uer`8L72dQ!;l~Pcs{MR9&@?-ksJ;=3+ zTY#U~03;hhY%MRtmm)6YEWQ5l6^1J5D`lw0-7YDwTegDo+T<2+oU3nycG%*%6@H-l z*vvn-V)N?yHv9p)YrENew`fM)wUgU@-5|Tu>c*qp58dv7ZjSCM;M0l7RaCK`DO=;G z>}Ej%s4VLd+>CiJUds6T-TrQPw6dTKc65Hn&`#g9fabY-5=I>|?dD*&^1li(@X|t9 z=YAR{>vsd&a3^PvjJ>$foPtPRI8{%t3&n8@y6hOSbVJ2BoCRvp5$IA_yT;F2Za$sH zTE2dt!&+{BBxKh9zX7rKq-3p#dOiVpn2(!>u^fslR$`tiL@*y|&diQBo(ES~e7Myr z%~ChJ{2bw69fZ{miDOn61grh?dz~Kyw}@ukvGvYx?zY_l0XM_iispCk7Dx+Hh{bfE z&?fue2ZV?>v4>QBFpQ06CK_2CY`BT(PX?H=l}pUu3THiverhOik&`G$LBU2p3O41S zfV)AGq@^6V8Bf_pDQ}gG3Jm~jcF|HK2x&je0HCs^A;s4rO3&AAvk&$M5N7Po3t_?Z zSU|9UkaVY)fA(2|vS2eNDTij0JQC0c9cMYBolxeqHDpNtAD*RwPU8&$#J?NPLSXzS z^bUIIT)P9I3>~PURBA)Ex8pD{6L!@hoR{bW%jIRN=fJw_+AKn?ZD_Uj=zAGsb-#Ke zI62&PG@OzT3q$EzbY1gFEc~E*(OZRn(64L}8+g?wI6$#<#Zco#tc>?evw1#mzy4;m zU;peP={JX9wOvp2#JvSsBKm2V>>azZ-FoBui`Rk8`uZXC30us%#Z30kW7O>`Dd}wn zqb@C+=hh@vF^Oo2dz}UF+IpTp*?Vf<3`!1z%(;|c&TVI4bC7NxvI{ZiF-m8Tl?_IH z`@%6Qjc~;@qATvbx&Tbep=Gjna^4Kg9uk?kahp5S*6GdC9QKXuxj{{|DT9Br3&%fA ztKt|@68BmQz_G;BCwouMn@8>-3l*&N3UCa9l`i9tv|y!|;Uc=|%zI%6@SUD!f3VWx zLSYu@gdmP3426E9!KbF0Z#;aaW5%_SrM{Ha{b|8KI$lnWsk?QzHkA7QHycJ#8}O*ZLLeE zcFy7VU4DMwm&0%F=JK1H1LgN$g~0FeQ7VG$VYy{WnV|=GP=~UyqQ{?z4wzRlFriaz zH7`=wjJ8-H5x6wG@W_qq$IBus~?g+u{beg>l8bC5jM)A?msT^C3k>-gGsz|m@IUqn7d73;l+fpfR z(F1;94CMfWyWQs4Qrx^x*;4fn13u2qQI?AUtP<1NhWo>U00`i+T%z2JCtY}LJiflo z-wh+-m5p~BA|eQv=siJT^C7tcvvfQt%A{52+U_f>7*Zf?_7o@BBYu8zbLxtkuS2q} z1p{ujR@C9}^FR>%yi4+vWQP%Z5Fxi*7H(AYvS1kGDca;wKQrGTn(_1+@8sleE;G6L zL7trH+9VtfKM@3n-^niQgmTc{PGqM3%QRDzO{^FXQ(gV8?Pk1>6%krC`htOWx#1(&ITu;%+XIxLJyYrGTX4K_Ka3Dw4cRB?;mM6W$IuA_+qwjR+t7 z*B69tCMzh?2+D8X@Yb}s=U z<#c-k7uK1Q=3*TC8;f!5a@lu4Wd2z0C<>k|$S`Cwob}4C5Li8aUdKLL_2!cr=aTxI z((JG_kKc?;^zoZ2B5D>noIsvjJ}Jd@Jr!ObbRb+4I`Eo_%M1dR_!J~0z+qx4f?^{? zTRbcG*Yh;O(?ayS@iW7sIdwyTyWQ$&1a7`V+M}x>eoZjl*Z-N}d=P@q>Yz_osn@uWxa{z|Cq| zdNYUte8;flz_$khhJ7{)(i!OTdTT6lQb_R&#hJ5W9Q8B#Y>uqwZjjJy&3JC!EoFU} zO#Vm+Odfx~HU?>~Cs`N8m@#863~^gf#A%qVXv*;>$i-5tA@}{DI-0n*DE!^9!8SD& zHJY>!$WN;jap%q0XcLdrZV4P)EH-tU-YJ9wc#l9GI4JxkO0dG31rCjeVqTbZO*I?Z z(Zv{(sNpPL*m<0qnvE`&K)YcLw}~$F1UtBVo;CxO_1bC88tM)SQ2UIqp?zV8JN&HpVR|J(SM5#G6-mmvr-D zW-NQhbkTb$YViVpXSzV&7Pd6yCMFKAza=Sjs$JmnfO%Vq&vhpYdxyjnuLyJ~_E9G= ztl)zJ1DX@MG)?qoacj}w(m~NAwpUa3R9E5Ak~!kz1;6;<=DX5#1YdXeJGfbyiW@*j z4B;FgmBVNN<}Fp94m(|vt7rAvO!zM7*LxEoX)wG;z4 z%N?WM8p+wPnEIIzm^%KOt5lJ3;#TCc4rFMpGXm1QHWouYzzS5YATed0n0;ib*^VnJ z7{rjMT8TvW-!FXB#aer4dZsg5*q5SLSoY97#Il=0u?#>VQIJE}vlxv*$#ab40698c zYlvgmxj;B784l%e+(ufuYWzBsn*v!&%cPRP*K)s$H*yBWuK2Do$`p% z=*gpFo_AR)EX6m3IkmJ{IzM|1Ic($Fu;ZlU6EmB?gkO^2IKrCi-?qa(jLilZqiN8* z98ux}*shn9k&0b2)@ABG0dRM7Z(Rq{p0Uw z9bh-zyb$YUL0goEuLya5D!-F4e>GaBy{juz%3{x^a^$(@W6 zWrh=6m}#yFzEQMu0DsQP>OFp*ax<3P^753gyFBIQz`bY4p>mSE9dtqxYa8P1!&PzcN^u(ph`&7D%YIWAGf(GzdU{Z}< zu3XlU3`nW&?Yst9ti$pH2p7&l_z{;{(F{9pXqDYos~3C|@)|;62Dr`jj<~M{x4;*q z4zvTH2$2u-xq2Epcl=rj;b#bUoej9Y1yYK;i|OWhW2C!hzZJ6VN& zjn;Gt`ZOEuE;yv%^hA1#guotw?qrc?2lD@je+vI~&J^ZuuKSalcd@{(ZchaAuPIj{ z^8~gevg5vgR6O5}!WyQ%ZsJgCTmn2gOhkAsNg-yMQP={c$MbSA+OMFtp9|c_1yey8teHwoM>S=y`prlFq_#J#Pg+12K)+9RG1Y$GJH* zS&Of`LY|ug<@k{xIIfbl#7w6h2i|8#cFA3Q{M}veuU7_zS_7ukS@@)%tv@N6Q`3A> zx^XiWQ4eq;p9+Gl&l zw4eW<$>D!WH*OA;|7U~X|D?qposQ)$;sh9IePPrXKt$lbd86VE{a9C$!3cMAw->m12U{DSw7zf_5$_HH zF+CFfG>Vhl&4+A5EXn zK@)d#>ql_2toCcgA%ugY4+VjvubTT8Y?~6aM0mdl{_+|$(vlT-#}rGhF=4?8!nKD= z{WuK^%oriI8MkDrLmT8J2ai+2El?Xq_{t_6fV1^UCs? zzTk)N7jxkI>*Bf#Uv8FVSptRc4}}2V@q3{3*^{FeAhy0%iPgS>iRmlJak`{NLKxfI z5YLO4Iog0AjZ2<7s6!3W_z)c5>O{>(YCNj#-62O`kq9h2e831V^hXxjbO_Op1H!ZE z!LT=y@@N<3SDdr;TYjMbC(*nf)VDWk+{j0Qe&64UNtu2Zhn9_~_q)M~o@sR>VdY*uJH>`qMgG68FFn#dKM3eB%hP zfjruPFkRXv*K2ItzSmpPqo-l7SUn7OmnS-wHnDclqgP&0x27ja;Iqj~B*P8V)B3h+ z`U~{@BR|UjGzaB>EUvqD3^xak@?Q%9<-OzCMhqb?QL#m|nt?@#6j$`kD=hTS{0R7~ z90dG7egtrH;0XA=5D+kazgNMVmaJg63MrJ4HtymqPe@n-$nA^(5^*x+c)yTyn1Bl+ z6XIHwScEtdOQvBjC0^xXj~Z~jN58<~#z!z^%!nRJ6<*eGiO3+vcUT30wg!Qr|8wn% z6|fZO)XJcv@XS-?R*d48IuL_pX7Cxtp}iq}#W5%f6ZUvl5!jG2qO~9H7>`+7<|)IG z`+@xh2H~wt;v}HC3jRXY2`=82$e>?}O7HBHIdDcI9B;uM%R(kG1R$sHC_9 z^jBM6OU`>OsXty4a;E4K2CAz&L~CM|GQUBWtzMe;X3f2IN7P;($NQxejsLP(oH_OO zM((;nw5Jx_<9=?iIXBBxFi)`gYH1nk$*FyQgS!vvrE*pQ243 zM5y^+hp;;6-EEuY%ADn6EZoS$?HXSNb+2@jro^EAou8?HFPc-`q}(DU9Tdd!vL)q8SSN2LsuZq8h3=*!5h zDR|nFho?<^gA)WWBHO*;FX2d|p&;-ymBiEhoL!8U^jMj6$n>>$KC&$ZA3O8#v7K*l z<&A8Y%O5KaFdTf01%Z!1_RmLdPeH+*c_`SGf&$qdI0{NZpy2aKDPd|I;fc(3C8mYi z;M$r0!4^5~W<%-dsR=0G*J|5AJ}hl$=NoT?opXw}AJBn9`yQj6cBt-Fo0#QQxN_|W zxySuyA$YOI-?Gx#cmJu3aWy07W_TtD=>ukiGvlJjq&)(lxR zm@**6V1(reoOhja zdsJy~RdP~95*hQjHa#;H<5BSK=y10q!n#W5reX*vqK_)&4M!r0ng=JElar9BAaWSM z?JWL2Rw{Rztyv617Y!oohypaACKQA)LClF5w0mv}!kVlxF_a{E+${|L@PPvdWWQGJ z0(c=riCsJxN=MfSi(gVJ$h3;8K29%vNc7;iq7${o`M4r)qZ-+dACeL3_E_q;dVRoz z{m2W=DTvjA4<}6$nv)fH6780%H)wX^G8FMK&ML4XkZ?8?lHY-&bvR(adosH9IZRQ2 zF@ofm!F+XZmGgwN=WuJXi=Z(4JtEGgjYCX#+jK6-B%F=`eqs=};0rond3}`rCF!7_ z?mg#}?os8!nK$Z#Yue!i3Mge}1TJwB4IK#r9w{UT!y2Kc`NdkN%B)6`tyA3Rpn+ar&J3NU9V zer6U5%f~NuXFw4u6$3{d8KMk7)S2bumq5IGk6#i~LP1!IPCn4bnlf}%9*;nOkUVvd z#8^HUMo3ooyLe3|#HaYK@$$hi_ys&Ch8dO`elGb>S$fL{!(7G5ck*lEPu3`W@REDh z&+@@A;xw}*e=v+C)XDr=xU)vAXDhu!M6mNou5g8&=etF&jLnQ!)3#FA`z^3<5k<7f z#5B{CkZoqvvTc-FT@@9+oV?}o->>2F-vUm&;$F_VGb-82GPPJ%h{nC#WEtUX&a8LHgTS9+>5vJA_X(-^DrhA}Gta1l=iLJ}AD~wamqT$-=(B+tu^a77U0%k73QP$9o zu}5OzHU4+qqxx8$Ky=@9y$%5%T`Xerl`Zhg)o!AdjyN+=$($a>WkG2)e7GB z68`Vy<14C{j<4ueUlD(;qQAP;ch)arBqnFQzGniW3l0tr4nEh8TkXN~rE|5`V7u9E zmGNBnPOCXJczAfQ^avyYHR}*#fWPjqmoAhB;eIHpxxd_;o^Cb<+pY58G~{Xw(zRL> z@8eqr$(P+8Or^YtedekaL0H`T<^YJ9?2TUQ7wlHg*WbGnKE1`z4fI)#(4k>*E02jy z#l%+)Ccd)3OqA3r#t|`bue(r;a|oF19l0`Q+2bIC{+9%UwhcE%p!Q#Dmej>asZoL` zr93{JQG9x@!KZIoI6nD^R_r30;@&F@z^)8BCVOw0H`mgK1LmEbD}bo$t!$L0kv>yK|;gJ`eLa@?k_&IRGjB?eO-fcIzL4UwVEy zP@jWq$5+)q0zdFpi}g1Pn7FJzEt+qD0v6xz>$XN*hiotlG2D!Ysec^0Rk!!_k&R~w z`hw8a6yVgJ6Qr&2leSho11f%4&*0|g(@5j%kNUeERHVVz`w65mFg*WDq-pW-T>k{n z(XG~cV{#EBJl>#=%H0;Y1v?|9na9UEEr>g0Uo0sfjx%j@Fr&WxnNquoT`qwz$k zJJm_RM8=vMbrnbyJm7suqI(W*Hu-V0B?mX$&BYBj2alUS2n;vhn1!1JOQqm2cSs#T zNzo!45RMYrS*lbDNqr=dvf3|Uw)v5=D+ejt#dSD@-cl;uyp<)4mFpa$t?{RUA?8c= zE`jBJRNRF?&-VG@ifMF+ga;Ej8eWi2S!+{v1gBuMi!u`S{Xy!E(oCSaQb)!lxm-kk&0JH_=K4pg}r z6SC1#_ncK0B4GbEFuJRTOG`4&ObLhgXf1iNY3RvvJcAy|d=0s^Oxc#Btuy_Kzd=7FkLEz~ zfVl2zN^X`uttFxIOdm?!#T3@X>r|E-`tGF;AmA5y814LB_(?wO;_J}d=^1SBqLNl z<1mISwVZ5I9oHIT`Wp2R$9K}Nr|2wB2qtR5nTme%@U}Fs9(!|BwwjLnq4sPJ)VSO2 z&H|R3A7-dQ0r#JQ#o<}n03}!Atx~}BWyYQmR6zM&%cFBjG-LH%ud8`PTpw_{ar4*7 zd9V$vZapQr$NguZB z|D*n2bvWLRYRz>zm8#QTZ*KIv;zmo%F>6l-qotfFOys^kEU5$X>2fXq*$+bODW?;H zAd+Ljvba#PlIB5SC!}A>a97-Duwq34;dmuuC_hpztKHcU2h2S;yS4AAV&O1ooPIUtMs2$%77ppI{H!qdynZYkITR!rKb7-H zZBY#XMS45C=Nn)-HYJEBS}0o$)DGL++(<3-4v`%qqEnshkh^_efTCFScj;L5PhOu5 zvv1HkR$Ywg+JtQ8jAJ*k>e<~ZQuRitT z3YH3~AD^cmI3~)Q`tcerQXYVLFXI2d!P87#q*FhxF9u%4I1wN}#u#W0-sw&5&{-Yl z@rlC4a{<2$JfypM2^UVjY4b9E&{A4UdQdLEyQKHZW=iS!Cf{SX$?G^M)j|R{PvNm| z#bdsd4l&AiVyCK6f4bZ0b!wd!1$c|0-s&If9rB$>@vz?c7THYi^bVD;BT(1(7zOIm zPzSfe>Mo7{WNIJK=-D!MeG&ZKG?MFu;LR`M?UdN@w`U%GR48k~qrr02>v!9+@SOIP zpg;%1GgJAf$8(JmLH{_8S8>g;1M!R}5PkW2@9d4G{q6qM(nQauK}H?F%2jda~JS z_7LSE-+C^(+-cW?O)A+gOuC4AXaLd?!hUO!>Nf>l)LEAUHX)2x>%mRSvi3m-DDPfu z#vEbrsKj+$<~t~m1Qv1)--DtQyw38mYTNzw6`UQ9nd{f)V9mqb{ZRlsp5^)j@T-R9V{H>a+coE?6{Nk%05lfa1(TTuP2Bm=l%JU!xQt<;Lv(v z4$A5zHhxz3V`_$*m&HAu7n+p!=1dCD$S+zmLeX_DSWfNnj0`s!FYMK1&^)};KM!&W zsI&x_$?ec_M0K3loq$j5@dV5mZyJW7EbrA+m@nQK;AA3iou6cMf|oc3d=)wR*>^xO4SsUYN)xm^5?o3v~7_y>{= z|My(`rJ5PyYqaxKj5QkUT*HMMVSF0{6m)R60Yf5o_}6GCs1knfvmCfM*vTe^tFGm@ z3V^_&dxm~IoZ--2#zm+Co94aH(qHy8Qx|E6ZrPFdaeN8vQLl64H3tuS-Nr8Hv>W*t zrcJn!7xBwrD9*Kc3b&$G;A#9INLkQ&pM;?Ut;KJ@Nw$+``K<4``{i{M3$!4~&5z-+ zPvJ42XITvVE+#7GwL5Z*xB18TfP5D`%GSHMnX<*aL*(mI{>PFbVhdcOv9cez?1a;y zv6h!`Ha~;$kuUnN3a#|YBjm&iuF3!~+`|GNw^Mf33QpjT@}cpd9~#_T$$|!7f4>6_ zZhl0gcg&#ixiNvpg9;id-(*#-w#few>+T}rPQaxfsXEBH{Nf=$D7d+r1q!}?zXJ+x zK0L0V_~Mv=;!zhAA2$|U)-vGLccJlSl+>l;-J@C`93!7$JnY8?H&ew?uPou~F2mqv zsyHge-Iy_q?;I0s=nAE@6`M6zJFwQ?MFyLG=fYMCRcm|ErCvTd9`U1tn@MHxqJyuy z=-}p=aYe@uj0rlX73WwHn?+-_*^6YfxCnV;9QDp@Qv{mOl5h2Epojdp_=s$#2sAG) z9+lT`cO(io-^IAFN=lBCCi>&!fseEAMg+!l$09@-Me|{;AwqIQ`Vp_a5yPd`PRdwA z`fN%;QF*hJ=w@mp&i$tBG9R(H6+e^WY4m^$V#7;pJ((Zzs?-(z3=PhD)3kL-f27@P zevW;GsuN99&7kdm+yEm*r39g)S-n~}tssai>RQ*8e2%M_*g332R9|hN3|Reihk}ar zz2?VD>yJUc{QW6HubgRzeMER6b`bgBQJs#JsV*$7tj;dY%}?X7&h2Vvy>e(eSc!V# zH%@Ods^p1%N0qEgiREujR+l36bRkBFSM7XMpKaHvxf|7sxW-5QT%%vWHD>)>gPWfm zcdqgCW5P9l{x&P{m8cYvfFTM~X~VFpJ|=c8h$lroD+DNRM_oj|BA$`Zf&y1{aU1ZW z0#pM67=>F?$SYMSJAE0Iwh(8CS3D*DyOz9TjVqk35!J3W<2CMh4IPVX=vdMYhpRqE zBNZ*rt)f^K!Xqn39+TuHnBHgz$0v6?7g2QM5*PO~n59*B5R^G3hIW_+dicmxQXGr# zLx~mAz!vJst+n1Iai=DJ@R&JK8ZZdOq{n0rq=54lv0!wZoZkjefQqc{d1N3^T_DZh$avp$S(UZ_{5w&IP$Voj!;k>Gr- zsG|B=#vj!)EAz|FO*q=E$DfI1J59x-rU*Q0I8*NT(bwR$@`uxzp3KZ?P0tPwkYowo z9&W{`awmRR^N?Jw@TX6Ua(eQ==5zvFVE;jXTM9>t{Y71mX54@zZ`RI(XycA1X~dz& z8X0qN1NDw~F1B9^d$opq9P2J9SPsU%qjH4h3jxNy6kspXxj0LEm=p5lnH}iYTe2J{ z3-MkzBEd+UqM{~sTIwW+g2T@mbV`E|UGR{rHT5~BYC?DQx|f~j#Iw$bWo5C-lhXk) zm^1BWuevfncWUYQit*L-;1xvo(|lFDMav?yehA%8|D;M#PHo8EL3ciB;kU6`y6flYr1gfrTP{8;I=rCJJ%Joh_tW03YHr z6M4q65?_q04_8RGArbpYwN3^@+=`-Y2(iZ=b3W%-B8kNUs!xchYCs>vahYC1AEJdM z<`yGd>hz+gf>&@YqbP+QAxe*}o0i%;bD`7Xr>h}NvEUodSye_C% zM(GixL#RPfk3c#tI$_AwZ9h!RTjCUV&9$1Cq~nzP&~D@FSEYE3lQ+#t<+n)7z3A6+ zf2=^uz2et$x%p$`uI2uZW1{7LK1<8334F-p7y%HafW;=?RA3}67piZ)UxS85vcB0y z1j`5tZ>PP+(t9liJ6)4}8wxI=bWNn_h*3cO6>4DYQ;|s}R-;(cqmZm=1nqb?G%}K& zOrJBHnk?>2X~`^ilp@BPVs0HqFKpH)8X>VruZ|2iOz zlK##1I$O=!F_=nF1X)V(IV}LB25H?=Sr1!rWXemSM<^pqX_Uw#j}>Z>^!Z}&`h#io z6_SiB>F5snS0;T^04@3)M6?J8>wPYzEN&u#amC z^`FKew|h{Tnp;~@y@@eH>4W8EDiJUSW3<%ICx!MYahBH4HC;7vP!LZOnH#50E1{^> za%WuOI_fzGF;8LLn7pBscRc~_w$k3@>D#beV($?K=XeLgdZbw)-uiG??>3ZEhbpPA zA$_y85DgeM3|*B$)M;mFsUJzS)Z>YA3T{l>uyb*(+dLO7N3{rhjQB(!B@QA{JSQ$v zQ-9p6rY_!bQ-PlP(|$dbo1YtZJ@xm;L{EL<%^B8MB!o#=h@(e>i=8euYm%r^>}Uji zl=aVqFRYP9u%_R3F2e5;g=^~qhHGNi$(y(wZSzTKX3(}+scnvk^^w#~3=|x5IE9n% zO{tvV8*G?`(W=)3ucjThW-GKu#xV`ziuhfsB=l>RKDvJf*jG+1pIlga-Zf9A4UF^& z!jUD-ug3UI#p$ZGZCGLDv>LL71W9#bh9z|Yrl4pG!bsAOux5xYBxa`tQ4Jds+Y@M0g`*zoO8VTMjd>o*+UNsCAyEbiLVyyBoS zM!Rj{+)<|Jg?eI6r90?f6-Lfn*NLBU#iN>fMNj-6;j*qqZShllN+tO!lZNSjrTS2{ z5%}n~nsH2pKotC9fdkKEIg>7n?H;%LJ23H9n$Tw zrLTo3=_DPURNo1ewXxGjVW$hp!C(Yc(baOM?~bnUc5+pmVOS9l$SLtV@QAt4KYaee zdT=o!0t!$uEYcpBNJU9ZyI(?MSAiPn!ayy1c=UMWm&bj$q9PoA=2`)SSH^Ubi3w(= zWVz&MsICvFLc8l^uW=gLrA-`ejK{+k;bXNU^x=Zd^x~boYj#p{7Qen=H@*0FzQGBJ z)a@!(c5GuJ|J9i6ryspDV?RyogPg}O=YhDOClNqM4F`9Tmk_S92OI1}#;oy6;ITRndW`d$Eb+&L_J_;%p>?M7)ED$y3g7 zW%?1(2lc2GUe=<}-ql$FF}vIMu{(nH5Z5_`M}H$vjM)2;#394C+;dsC5T_TGGF%tG z7g}qmEu}xr46j6z_F-jSZbvM^*52OcRD2~GWBEAEm#rwD2$#LNY>-{da97&u% zX~xdxh+TQuFkv%9abY4*4%~-=)F?VDMCo2K^jTXRXM3Sml}-Xqc2CKqP-vP#!j>AW z@510jWSTE14f`#M{30Yk(P2R=wo{U(h?0`+h2 z9`deBIPhp=>6-~6Ed!Ez)YMM@tWnTuX22_101pXMV1(1_aJ)5r6tzcH%>shYeeP=e zVq0PopC?BY1XJ`r^>sj?9-}lpE4c|Pp@N-=rtBdOZ=-_%{i)!C$urcK@4Kwg8%vXz zYB2XQ3=tF~)RnMXYXA#1@@X>Fh?UuDZZvRasW=ilc`;YC*6%@g31yJ|91eH6m28SL zPxVo#U>34AvWVrbcWV7DSm9-qVsr_H0@C)+MOgM`rz<^X?k@6etJp;f?MwN+IepGw zWMc7C8QaA#7Ht>T_y!ylY?gjlRPB$l`xPezpU1}Q(>qi_X;WnKyhVDNb&Paw;^*RL z_(eD?*m{v_&r1E&4+a?zsts-99;62Ud)ge1NtucNZ#*`#1DzTyp{UoJ!&UU(ZsO$&0a6AoLX8wJvZO&cDjswsslVu(&;^8vP`cgCqy_b#mTeUtL4=C;FTSt zOqeHS%o$Dr!`R4Ram^wYryg_)g)ORfI);Zzr&tVQoRIj$m>_XeAgmY&LbA|K!rd#9 zC$1_br&g*UKZym=?aA)b-7Vm#CkZy}u8SN?=QD7&RfIG3;9DKRq}t!BaONEMG)_=E zHYQN}n9{SXg?AVppOz#61!QF7=K%)Y?$w=62CBZf2vysB1I`e$)E(8XQT0X~B1Qy8 zCy0#`q)v|qq|W{Um2f$QjLI%ff6_8;yMM0Ltci(N+dT&)UTsLk!+o5pi|Iq9O_j(2 zszxZ79r@}Lpprq!A1FiyI7Y3?(GD_9aLOv>++G!`9eY)#WmK9Rvi*K~+==R9+~#NpD7 z^bdg`PRJlV!+V*W$H)|$SAId;5VPLvjU=QICAhFd3a!~N{Rs5X6=@em9?5H|xiq08 z_nFo$i7n6-nN!j#?ts};=?nC=Z^__z-&Vx&zBPm6srGx7935AV_g{<$#~b{7KD+Y* zWDmF<7zaY%h+W7xAX5r|kcC(rn`hxS2i^>5Iu&i4Dw_R=@i2Ox{SUWT(L20i!k{m_ zf-DW`SVgK>Btpb+)Fv1%1=W+6i`S0gpN;r>jdZ-lf0Dd8|3Tt--%394-8?mOB{yRZdY9;4E{nvy}zG^ir%F8ZMh3JJ8-G7dDvxj99b-a0t!SeBjRPrN!K{K8sP_zipMsFQl!2hLW7#M-WAZ1emy|%I#ZEq>7x1t)hr8k%C!ut!@+> zQ7*H(G=^9#`fiNlMs+b_m4?vz+C_BsrYprYL+gGeA zt=MeeK&4zI85n0-3+kAZ>}(tQIFikYMG@)x>3vWzHc9OGTQnIs0s`Pa)*_ormq>>V_wXG+dN2EwEu<29)2 zJwyQ$S}L)VpHmemCj}Yx{s0KRM#a6A4@XvfHU|KI(4-)vJPoeeG=tA31sUr>VL`?m z9YmDKpc;&mVtZ0vA&=BCs7#9O(ad;E1CHTjQf$vVOUC|`tMkYsCEy{Y17}i~cX8&4 zsMRB4o#IKcJxY`i*7A|gOwq!YunvhY3NHAnQ_=NR@(v|s>3AinHOJ>W9>o@tuX4lU zOf?7zwv%Fe8YbQYtz}JBZTwFc&NS7+MjubOK@`Zj`Df zhY_`Deh2`M9Cqu(A*-eYVp}K0_A-to%Gs|>itPbdv-Yt~lVW?~45soG+soNGk@h<& zt7bT!x$4GnR#$cN-Wy&~o0~hIlvN|?P26TuR_$WjI;N8+W>Qv7REsmU9-5gP^^Fb0 z$TljK3P+=!lvTqd46VJpDNroT+euk9I`4_9eraRMq^z1GD*4|@ zSv60&IE@vWBM2fjVacW(3e4D)RZHy`z%N9mF2$k<59=h6OIDXKLRe|Z_r6++TngRy zHK~)4ymOMsC2ELFa^w|1RSxpg6~`ly>kgzyZ*3k!jubMwwl+UZzo5KZ zqceEl?75hdiF&g~XSu=~r|nQe!%pL~jSG!B-Fk`s^Xkb1jn_^d=r=wm|N0a0SHJQ3 z%^qdCbJ?{oucQ9mQ!_I&XK=23Jaaxg*X+*3oqo4QCv#9%@XYMOOn5Xz%-|+UfYPsr zH^U3z4DLr%CvVyOM*JY?SP(ia;pRf0dG8gLAXX$9Lqw?(z?8MvegI*H~}h=B2EG@%4xNj~&tj z!zBKfG{VwT#rM%D%SaWQe;p(1H=2VNA(5ykHi}Y{wE*QPWF2y0rJf7B^?+6zzVaGM z%=aSQEY1hVG192mB>#KJDDo;`a_8d0(!%2GOKYpMD<|{ea>NgpCuK7gJ@w+4A46?u z3uW9qJ$`Wc(bB`^f69Q12*NQJyg0goLU5+YZ+G~}(Z^ly5NS>FyvwPW4i4zF4_~_c zs(Tk9Hm_FI%9&$}3#&PKhdZPtB)s^Ii}Nchv(L}xqxD06w0^Vzt^C*n4qCZ6;kskP z+kuW7RF-5JDcM)25CbIpRVwebz8=w6vNWPDoqMDuNAr zafKocSAlLsAof54WR2p^r<|f{+JWEz^&O{|=TFQpFV7!eTV0-AS~-1cc{LxjPy0d3 z%`auKSibJESZ=1`>{3-28_@nr=|TH~DhFZ7V-AG&tUJ&{e9dNEWOJv-eKWmP8?O3505!NJh++G*~>Hdx(g3( zescWaad+wAadZTD$dz}|ai>{kEgu%g{jlI>I$h2S3%>4}WVo5mmK!sD;{MXZVmg6E zak)cBoZQjq^|t3)O~{mdIL!Ouz|EB`IPi5>v*6}O#t#nF(!=4w1P^k8@?0fTfxsRhQl#PGKpD-H{V zx)xVqPMr&Dji@dITu<50!JW`IaoGyeO~ZY$VGM z#m#p!RFWgOHopfN|EERNfbf*PhYZ4&4uykzU#nKDS9rJ??%$L0z3*_|`w7`h$0B;A z9zXUj=P_toK7Yf z*67oO_R{-@KUsE~JbT+szLSB-nQ=`{2cj3zX+PFRL6Uw{6wc^-rrK;@=$wz#F*=1O z|Iqu@KjRb`K@LNmB9liAD}HJ?TR;t~ern+6_m3|%G)qqnw+uxM`xPNcKui&X+&hg# z3-XaYAO(u7JVA?AdPcikdT70XBiOupfKI}o3-ZmGGKGPSrez%8RuO{y#AXk1+%Gg^9NBRh22L%dIJQv-F^Zchh7ka7b;9~-72T*WoalD8 zPU}E4Q)KATt1#X`oJS0)Aw@;%dc_XWCCOQ(j4>IQd$MgXl`@O{u)>g!5&vv`v<(Gihr=RtmvaOyv)$fh;SQ_Vuui^Mz zjd0H(SwbY&Ou3S- z!bL3=!7(r=kcUw0(F5L7MTc$K2DXuiIYoNO8>=*pD8XrrjBYYEemEw)S8L!sr{*u! zB3TwbGG!;--)nR`7e)Q*Y z%%}8b`=2a3iyWL1wQ{`czMB)gIc#Bk%(p*W`W5;D7#e3dCkNSkIEf*w_IOQ&(gpjD zHtXDF+tl)-Ja+s%?2ig~*muh7?{!!SHz&FDsVI+I$gCCfm@-m$zRS<2xcO`rpW^Fo zDi}9Y=8ulI{H3wrPrqJz^q*!=g$$??n;EhcKP!xvF~t2`r^R(j;%a=kU4t&sZoV4T z=~mA!$c?MIr9}!#K3cxpj}~sGP&%(vjZPl)k;ZBL7(;cv#0xkLb{4n}^ z1u*)4dEJ$W+^nR};4Z+3Mlp6c`oBt#qYIfhGA5jXB!Q5DBvC-Y3>UAGMrCwjZrxp^ z-6fHB9QC<+Q4!XD(2unrD!|&`m)G6sac)-H*Wj(dnjFX2q3!0WvL~p0eJ0whDQCPX zIAkEs^0T>!dW(a;e1-mp{gC>@0!Z;=cRA`1H@{CS&nboe;JHrya=wQ4Bl6vDTsAio zT)i3^Uw_aU5;s$xjn?PiLU1X)z2V-{gZh2S9($p~DjM7JXpvpb>`rQ|$Que|>VNA; zz(0`9nUVQFDzCd9TyEB?nMbCM2Ou6PJs`fA=5nNSv3_QVMELYI7{u?c8JQmIHJWji zZp6+|AWA!o=qmkVWsYu4yh(JyWs0|M524EagdcXlS^&GBl-J$FO>S11dV{w^yuy7z zS!Hl1Y{M&n>~kmi=Di~5*1l=D^qZfOgK+i9UzW|8RqOaMmt?qE1wAzGN%8U9$l-}{ zX9bk})AIeU@%m?F^Lw&*`B&t1cS^ZgYb>{7N?}H$-PJQ=!O~wSJq2I(Q!vq#`p49a zi5FEG2?UOwqV7~bnM{v9_Lxnj>N@>&DfafbU#p?YSrNj1&X2I4FF@E=<#pGd!_5l# zgFApQjx-q`l)YSflzqd{DAQp+3Jt?SjY@$IkF{HcG%8?azvu_r|4;z5|6X2y*ilQl zSyfsb*f9g+!OiNW$Jy}Q%urm1WoJ`Ug$SdZq{qiXGJUMF=~7N`J1x4(@#-8#6(BbJ5hwi?#!l{0YP#MM*BUYws>U0a+zjS8PQ^B}Ch9JcyV zg@o_4I61w#`iax?C<}^evhz!G^M{P+97RIu&?j|88TP4s!GuMqS+Rbq?Q*_yG(IdM z#W}9Xg7K}(9yroW2$vxOo!O6Q8s^nRCGl?5Vg^b)LnJXvZt0bk`3mBxV#GanpO)f@{;S1gU7yFb{;$UOTTRA{c^?v2u7QUs(@>8D{OgD7JwUf zy2&jHbBOt(!2VvO8V+3zJ6HVGaz02Ju?yF~31O$h*?Iq|AN9Xkfck$Xue(MoZl;2* z0`+5S=l!35bMW3LX4F&7yDrA#S!1Q$rjyejB@K))caEQZ@G@zo2SEdb!} z`2qL`1pxegKLEK|o507G$^BsI0eEF}0DAEeGz8n)75gIt(s%@py{+u)dGC-G7aW30B0van~>lw_}2yNz7Ww{_>xC=y0!!?@z(Sy$r{wfA+o!*Vvz z6Jvnd9V%D$#9lbOA=7~Lo|x7x>QW1XswC=H^>goy0V7b*jl0_$VG>OCx%-X4E?=mX zHr_8>wN`kw~O!Gx_ldIkR=B1A?VIx3DhaVUc`F z2P?}`3|z|~$>ySP7@Sv`=ob{{BejT3^};YxvW;lZ5@R$oIWyaG)!&DRE{^SXoXqXcX zGtG?&EDVr|h6#CV^4N)nxj!1F&?SVPS^AWTF9#*h13LTV_n?YR|6EJ!in$eNp#O;3 zPwAObQRHG-qnwML+aD!k2jh2ecJ4RvLCRxvyfUwN^8zZ0i3n?8MXonnQasgfkRjne zcek)JC}IT%@lxMIoxtj@gH_ieIi}M=G=a`N$D#LRx8#;UCTOmMy|-DUbLfQhn|xluj&K> zInU}XxoVpz=kofh0H4ebg#=1kmjy5t@n{}kMr4^p1gukPl)B#}HuI55y=Yxq^xD>o zW=3;8$_UF>CtQ;)afxmu4NCfN#G7`gkTpc>q;l+}ax7zDQaSd6ljmv@Kb52_-2mA#QVgqg_M?DTe&XGNIYY;B?WcF^4wN6++U&x8 zB*Lja`~m)@O242~a-%cgQpAnVZqC0UDIeCLm>?XYGIIvC{^ObRIL)LxBM%Fxq2hYC z)0&xGAnQ2{6q`}4Ck*ME;e~Jp_v2`l!^qLv>a=I#Zf&NAu;dwWt=Xab_?8*6n#DVf zI*sEJ{pZz_2O6)PJkW1^&it$2hz8*`7{|t7PI-j>vDmR+B&D8*`PG3dQY_ zL`3EErq^q5kaB5u2JdKgsz78_zM?7-IXHN}*{<)LJUF-oCn{}3bcp=m7TDjqJ@M?BTAn?+ZZqi^o9aSb8z2wSUV4QO5nkVEC2|$ z`YZT!QE&b?@Jj~$Z~k*!J9*3IZ{Y_mx3%o=k`Qs1{byzKO-T6s1K(pGkk@e%g@qPw zz8{bMF&=B&Gq|nRK$+%l)ZY2D;8L4npl{jg)XtM5gKgRpz%W&$U^|C41 zObLVDG4gd3Nb@~L#pC1{F^zvlgDn^5`*$>?>kjJ8KgBTmjpm^G+#;5Y{kqcXXRKe~ ztfx_+QmpqKp%zK`OZ}jKsku3Gsrfp}@LEgF&1GNe0}qsHsUK7cSgvFal2vpU_>FPY zJF`s%`e&?`pxB9<-h5ME(q^A zFQXA0PGsNgb!OY`4#nZ45QjMTikjmJ<;)qD-UJ`)RCWM&hCDdh=adk5?=2PZ&VC%l z#%5-M7orx6&VCQIMtt-ncq9D|LT&CS~GG=uPl{2B1Ez~QbZ_fda)PF_1L>t4EO`^ID z4L(MN8{;q*d@(ACblUnPmQ@@kb{h9pXq$^%nL~#>FF8lYU#I{Lmvla8&Jf9IO*iz% z3LU7aW~hL8p7ayXM+=CDAA6faJlw4GL&36XL?xx1L*=5Jw}Nt%Y^GV`iuRI08mlbM z{2|oq#pY_V*21e}%NFWpHnc$t^mNV( zsiERe*(%05GVt#sL(d$96snZgU;ufg*=sOO7bz#t`jNg+fb>~;-BnJwnJS~xE~D(q z$%o2?^he(yWW=#BrU2c3TNo}voMn7joWLBjzm?2e0((Mt$y<6;p^^L>F`qaZIczaH zps<)cQ(*=tJ&L=grh_T4`y=?tmxL&(o>`e+HWVON-!U|11BpW`NA?i$9!+Qvp!D%5 zIu+7?B2~;PXvTc7CknOE0o+kNICnXyH6mznkgOX~Z?50PcAU;#g{e??C{IzH|7<_R z(Zlw8r~O|+hqqdN;Skg$Q=A3}=D_>=AZ~Q}t$F|*GB_9Euv3V?dT_B3watM;`PAcq z{kFM&d8s1`BEr5!il?hRX6TI%5)j~awgzWR|B5`oDsQ85($5Rd6!3zV{Jemh={(VK z;05#L;st*OsrOu((u)?@L-A6rEFky>&fB;?)NT#WBzAGE>`B;zk)=yS3;vPpvCDqM7qm1Tp3Ya{9(&&fzqiS?pj;#cDB}fTcBlX6zXJk<;=0gh1J#h z;}GYy-lZc`2INT^xe_J;{-G+QY6*nIp6QT;sMN|i&DtpA^kn3!a zmZef+*QT~mi9X3{Ax@2rh0d7>A+5HdlUGQ;Yd_KpLUG85xKr&~*P9Wvz$pEnU>sA-zgV`|Bhd5<}I#4)&*Nq0~14UOWVMaAvlMlQF>?%Tj(^MzH(~$ zx`x=;ckJF+>goTiU>Uf>yhz>HjlBb+g|K+$uKL3+P9VE>uDm+mYSk3 zbv{A%CRyK+?puGz)yZVz+ott_31UFzrWwkY?^;uV>8nr#!}C#m8YZC#g<)$^k@4||{e0_Z3i#HK`1uw$(|Jzg zz_)&=Tzu=Xo4v+IS00g8m9^MeOiVb3&vl?;@jgbVSUvEbAnZl~@ldb8(++ACAi z-?s+G{o1)`O}-?mTXxoLO>_j83Jt*gZkO9)4pZ~$RrvI(Tbz-fRqWQ_wxrDysMJ&r z<=_O!VhBzBuF<0vqLQT>{P|RwV$jdN0ks@w9)0wV;8Ah9UN?f}D{fwhWcZJW7In!f zI2Xo{Vx4wy`Z8il+tQ=39rlnLg`j2XY8Bzi6d^-lC?ORoYSs{sf;5}$PPZ3y)&*f1 zPfG_yvuBnWr(hd+&*eyHd#Bz&F9_=)9o;F$_aa!-BQfZbqE|3-ye&M`6^l=zQ4dFdbbQJALJ8-JV zy~HK=?P5+aXnK`U6{Yx>`_yI}_tCl7)>8Lih8xXx*h;=$#DCLJXYQ~HZMsXbioFT= zx*(6y@!G9+aSJ~ucIP3Gmt_&)( z*cRF8IHO?_j*|rcnQY#(VNBwaM%XEuQ1@&X{noIw4J?!P3?z_Qgy>B%OOxz!RA1+@ zp{)xPfnjWXha+ryXcD%e!k;;?GIMcVDLQfJ$0%#k!>(4ym97Y1H!x_~X!e`(+{Ga} z@_O|>Tv%+1C%#_T8WZKk3W>H{s+K3yWYQ376JIZ7SXncq#EkOmWUCPqhoBDpH(}+< z{)t176Zw2B^*T5NhnAPbl35!{c?CUOS(-3m8V)9oBCytpqev#lZAk+~;(!}L{pV*D zOEz>B-osxzN0BU?C@Or>IkaDehlcAVKB5lmINgsMP_E!5s0rIVHW%y74Y9){8+<|O zzr|N?(zXA7mK@7m0qe`xtoGlK-=WI^icv;`FYSLhz|lswG5FM|6#_?k!8Ixx_)h== z6b<|u{m3dB_>Xass>qr5qI%q~d7Ax21Ivr9U%@w^TD)|VY^Mr@zn=5ltq%0KyTf?wH}IIR0-+fEn_yB? zta3*V@HYPd-!BJ(>Vp=>x%ug=@$vP?{EvN9j}M&vcWI!d=e>VQqbwtJZ2mSzMl}jQ zto+79@Ky?aRO@dPvlG~OJIwJ~q^{6;<=uRUe83M8Zhk5YB7FT32O`}3k@17bzb!pP zzD_{|{?f?Rp5#(fbcnP1Eepoyto4+m#7JU6-2Nv8+>3L%Y zi#I)3cCqqw0W7Ms2>tDvYBe=dLi*adHt;pB)It=hJ;n7 zZamb|he{8Pzi@*TId*an3ho+8EW3e+P<>U=Pzp>*>pI*|wthqv9hK&cAahF-Vsn|1 ztR@ud`w+L@-xeWii+sN#0?PKtgZmLb+&@$RcYX}@N3Bg8H&Z1x;(%ia_s7c)_p?8W zTz92qD~M+TZn7 z)qb~C3EP*sEN)ety@oQoSJC6SPW|#8ce(BVOqvcwKECfXuUF%~sKVpS7GjB_3|f6o zM!1MMM{*x|WDrok+BuKvSnh^tPH+NQV9Tf{oJY?e@ze8(0($1hTzcl_F{bC)($n+D zZg%K-WPq@(MdCz+XDhHIrJV?0hw`|=Yyf+(+OFE)pLN zYR-CA@`Pkq@JhWbGdhJcRG;V-P-aIvLN_YClPau-eX3&DV{Z}C#xUzn5|hYEGlq*2 z{nQIF5Z!r&w#88hyI5R3yM{m3mQJlMoLHEfU0pb}w6=QcpFBXhy1R9K)Dz2N7fO9fn% zA9J}VH&a!1trbypAXQ}_k4?a-vUAbFzf~%c2)oPxKt}3H)?o$I)zuehr$U%dJo$A~ zBh_b*Mybp$_5jRQSH#f{q)15RK_5&YiAD*A0{ox$5RbxVaIZ9o%42eS zlGbVY71sAV5Ar+^qjL3EalnFNP21aWkjJZ?`Ae86q~Feoi6}DbZTgwi7Ydlvc|Vil zW~v~q6<9JojgPp#TzV$;Zw{KJUb2`}78(lKReu|&eMQ9_i)&Czlw~t6%|Hmh^iOdz ze%{B;*7zEj8#6WeXkZby%d@NjnT*5FGf=*TKD55;58&s$AHdf}`v7Y|<_BCi&UZB7 zVw&^m1%=Vz1RaGT3t*7Ei0Oozh2sJ04AiJ<1NhBcZgrQ7f$M>8SN8*^uQYa`Ab4NxO zaJ%2I=88PeR#t6CsanBOM{rae$fc<@gA)y<*jsd)McicOr5(KNQ7}<#Ggwd(f0t*7 z)uZ*0@;p%UOgnKmQzhZ1u1zmkuO{m7PQqA3+M#cnNObfhze*m#Y z`^7y21&nZMQznr zToP%aj9o8E+3}UML6?Z4XvR6VBBlT3aRqs8Xg&_>Znp_PCGV?tX_0Yz$>K&Z&A7LY zWTQzvj7b~KSd-O5j@9P<2De!1v<1T;fBI1NnjiNwjGroC7(d}>7~HG^QOD4z`~#(D z7Q)>ds{M_h!||tr4FKQ*EoB#fE1^6 z#}Kn?&HXmI2pxc_Fd;p-9QC0=yHfx}N$g~z$)rcGniFbqv1Ru+{3Q2>1tj;I;`*0w zI)L@Zzmsm*g}i>UI=-~`>!qi?%^`JxG$e*5Ju03kF*}vGJ&JUKt2gioITJZ*!?4D^ zY1p+uQz$gg_EoQJcHN-zE6x2nc#I?~X(uDU7E47$sjw}#;3e`qECqnNG2b$bGrAWdiLF#pIB_3x9RJ?%PTTas*Zm8_93=Kt8wbN=rFp7X!T z>n^L|<}uEp_@7J9YL;%v51zkxRU|GJglGJ}UV*y!-kS;}>VNi2RBl!^sK(H| z{pY2ZsNZ+%C=%5`!Y*a%MPO-71oDrESe@#(o5-i*AscdBDyDu{yjO{z2-s!|A%(^i zYGuMmlQfbeQ@X%3Y%6=2YA$m?Qz{FoHi)8V8S zE);J!t9RsNUdbA&Vpt+ULMiN7{kw_EdO+sKzMfPR;b|m=*j%io&fGBm_r>?ID&kb3 zTzt4dMOl15-{9=}b^9258~?KOO3Ej4t^3(n*rkrr?xc;4SF!Wg6uY-lz${9jUtw^O zWQT5)9+1`rlD8_W-*x13Qj$$xr1#BlZTBt&m2y;#X0vZF;rb|{0$RQnhwCf)K- z$;4w;8d z5=MA6_@RmZSSxIwAByOo$RPS>i-`Wo45C-E0N>OZviZug*h~N_2dp(?wizB)l5=Ccnk!GRXdP5!ugXkiBXjW0(E?r6>Ep z$aUH0p`p-azlBVd=5}j{6l5uz=!~fXpBEfMM^I&IYlu+zT<_Y^p=_o|~wF0j_M zi>3b&!+cbHHyvB1JKqO?JQshE1Ia9xP?R*x{v~lx6n7Pb-3U2j5!_Qf{Q)vVM^p;q z+jK5)6=uA}uvcp&$-hUY-27Q%>PGA8XjWG)=X*Gb5?sMqs1!XgCyWRux}7aW14VnC z1o(EX|aj|NUYv|C8Z1d6+2Vf->S}EoNL9zK&cDHDQJ-G}&T? zFm-YWRHxk%MGaA|;D}~5lD}leI?D<>rC_c@*P@{@lgv@9lM|bA(vn10z2skFCKlSg zXamWV2$L@2Vf73icABO~UR{HIr;@x1S`LWQcE1J?vy)>Q7M%Dg+~VlD;74Qm%rOnpT{uHMs`+ zqp{^qj|eUmQiH0Yk5*U=v9T5Fc~(YQgN(|5$k^|f{g9xuU-`&^1EO4fFvL*{f)~6d z*@G$oJH%tAfmT{r)x+7xGI)-1V7`=jGKv3GBRSfFhjP!nDd<7PDCn~zRD&Atu?V%Q zqxrDb5Y%Rh2=Kcz+ZD5}Cxxdpd!5-f4A4Srh^vS!Xj5}sp+t0JLuSw{D2w9khgb~d za3ac08@aik>~$IDxC|R0lS30yp)E$Bjj^2GIGlYCjv*ZI3GIq1#fnK6rq`R1A=f1w zCuQ+vd{uQ)7N4LMQ28Y`R^8w+yf)htkqV|oRhaRuGzX(VK6Fr2Au$C3R63EN1FlX_ z=9P1XKGwr6-jdBA%3gGd&H~u4!szyAXYmGql@*>2SeA9AYsp2%y7VonpiNSaEnja9GTsD3b8$5+xtivdQtrf-CMA%s zYYF6GAv!65TSNv{)w686)7?m+m{joP=Y7Pu#@7<3mwH%|mEUG!4alUt+QwEMkIg=Ol1Eq2+CM+S|j=GIWS(Z!Gq=VPw^=#F{BH6mA!+l5K0NwpF> z_YtGZu83GfPBMgn5Hm7GNype;6P4`sI?$B_-TvtOz7Pw&2QsNwe!W4!ieuj}Rq=)g z25^04TQq4KIRysv3!(1jY54L#qnx2M0+ zOCPYF{MVU7HG7Za@>G)KoFutRu6hwYj8#V5q%4lyPJ3f#1oiE0tr{GK^Ma^Wk}i=q zDT{*;VJa1hifDu*7p^$~DB;YyW^s&n#6x!-0iSQIlDd}yJ2w$p1 z_#$R^TpYqQNq|P2YB{PwK!cZSf^$$O3D83-z>;eV+q{(woyWN+DX{t&1If7!Ma&mB z01BbVIV($3v=uxzNr4^L6xciNKo;!Q=2wtl+K=$dSEumHm7PkXGkD7?_PmQ>JKTu6 zl!LI-_-x}sqfQqu(SKe&d7$yy$pihy=fq!&Kl29hXTS0J%~OpLqi#HNK0Md#&cvO5w-y1Dy>6#9GrKSo9t~mS*^FvE`t|T;cp;p@{ZRc6Bg=fN z)1HaDwHeBKm=V{S9lDQinW5czJmZYFN;mE_>T)#E;KKD9!N%bDo_tEbasJag70l}3 zU>o(kb_VZgcB+^K_)KV`s$v#)1_#eK+x4B32M4!w!}dnBLl4{n%R{#(em&S~wxgYs z2L`v-8euoA;RHVX)gRo98oYGF<{B37*4>ZGIXT-Fpx8 zH$zV|b#ZGwf*GZ?)3~QRsQ47d+PEQ5(Hz{j9oEi+P6H1hKDtA&0ckJdgNd{^&*GOf zb#Hz>nor)c`APhsyR0RCpM-<6#EU=u2HAcSsvFgEo_n(c4ek!m*xbNlje7>S)f&im z>_+XKPYa04LEgSPyK-{v#g$V_J92n;`G@y@ISiD6wh;KBY<^qT==ia~d5oLs;C4AW zOkSIYSsIS^DvjoPVt4aAM$m6G2me|n`;uw1Sqs*i?XVTF?NzTHjIUEuQ`$63hMm`} zzo=yjB}AT*k?@DZbG51I^c^HHL>A&yfIH(Tj=_}*axH3WOKS1^1@NC^eKX$zs!TVadD4#V>{KRh4y!;_ou$$}?ezukc+ zH{V@ecz&TY@O&FBPGBsQyNI`w&=i}B{3U$Ezl7ZU?yM!`>vuUz$j$F6?-K5mW(n`6 zB@`ZAT;oRQ79WG&+d<#l%%xmj^k>tw2O3H~1|%W|H*k4jDF zl%$cXnCCV#xH9yEGdQw1uJ+qoVK;7sEh^es(fWg|a8Oo7e~2Cv}T4P;h~Q7M1#v?)NS5vC|Eh5%!UemBl#Kujo)%BW5%)a`p{ z>-v1uqgSbXfg+F}@q_$B1t34_2RS!Cz`Ux&ApdGvKtA}R?4YsKS{fQHEpiZDLbXt6 zn&OXVOb8{>0~$NkIJ^*5%ts7#iym}V3ouKMG4~`m(sdKy(y~V-zpWlAcal?=GC=xn z1EfX{K@mYPT1vq`mk5g`bxcoT%T4`|lt%W$XmeYl*#>KGMqCAHr&ng2=_Y#^` zfNR3%`~}3}EJYV3R`Z4X$NYGjFTm4temrq=rTlpM-Ll~6?A!9sk?^jm67@KEZAI84 z=P%WwZ896Wa3fwNcA)uB;$j@Gc`595ke!)fVVtb)g1PUFgVES z3q?Hhc|XQC3NZeHALHCiN2`*>s>HJWUzP^r|Kea?!Ci}Q*$X>7&^eW!a1FQ{7|hfF zj3)p)Z2+SlRi+__TV9BM5zMTL~|J1dp`pT*0lM74Fr)^%8MYae#8jQy|rNLx( zK~ROaCaS3Wm+?n+_WAjx)pXLBeqtMbhqyA=K`^iIA;c(1+umqJy-u4Ffh#nCRoL`S z<37|DBh9+qn?hV<>T7hDTXMWqi%kDIm2>~OK1|E-Yo>ZM^mXc7O;H$iQiUM6i5jB84t%_C{eH-kT-;gk0r$oLh#1wspF@D-sSBmsCS~6%AW>M!vno}Mcld`^vsO>Hvg+v(Fu9n z`J;t4FPe-O8gqkHmBep7A)EB^qwd!8{*)dz+wC>i;rWvwE)rEuzo`1A0#UW;7ggLm zQ+`qP?@J@9zHN9>3MxxQ69sXLi!@LZ}H0%ZXN@Z(HkBvi{t*6vQ0+5Xqar8*gS%=g?EDBWlE=2>hIva=#=yifxBdVPbLSerQ&PDVDqZigv z04Ge&{J=|I!Ra7$LP!fWf04gMS`0lwXNU;q<3jOKx+598Vez5^5J%XXf6))l?=1l5 zxBJ1#%^xnm!t>U$fb;BIauuH8)_5q*hp-IP5R8#ZiBoAL9(mJ;#J^tH?lnUo3$O*l zl+dL^Ycvei&e7xAq$Dy_fLtJB7d=ekf^VzP-cx#vvRp{2V-vc5EhKfv5IV>d26Pw{ zciyL3C920%+}Vl{f+-wczFRRvl-sC2PY$I*UrN75)s-{H78h1m=Z|}K^wnmJnyq20 zA~vykN{X#*(6H^TtDb4$NTIijnX0&ZSZgmPE{ZJ|?w9HT^~@`jbjdCjp{BXgoplY} zm7xj9b@laT+yvole;X!}hM)C$JeIV(#~lcDBEN}yEK+y;pI|&}k6Njz){N3ion@!f z>ctn)`xzZN5jLfo2wMR^Am@*7ahbh1bjmW)Ey>p`b<~&VI~l&u&*c9>0h9lJKa=O? z$I8#--(MOg|GT-THs-JGP|Tfxuy0&hN`i#1=ds)-HoO3}V7D0?Ugof%*sJEaZ0q&$ zTfOPCd5&;%WN{q2NHaN4c2JQ{lA>DXJw4o|xJ)+lQ{-9Hi@|QPJ2P%VfA&{tKTg({ zW%5$Nh3ISc%2#AYe+>kxaIqg46n<)AMWk)z84E}=nXT>QFl=>>FBk)#V-B`5c{~14?|Ca(h|C}Gs z+`LeJf%{Zh@O<{b8xI`7hG({3Z<1RcJ9Ap>7=`k?a84qYo_6pZWD-#(NnDHCVYk_d zC0ETRgapD@EGoXZ-4CXGEtery1^4|HP^fDSFOCDS1pker-xue zhQQ5IT6!3`Asme-ymE213-3uMkPMQ3Ly`C7+=Gx+r=SwUSlz0_F%vX5+Bj;sp72pM zO%q{hhJvYCF~D;p3rmlgC__vtElvPBTgE~H&>dvV)`FE(eu={d3{QY+LMH)sC^a|) zg`gKTLK&y}VN0MrrUe?k#1*HOOmoG;(}iN;1clx0W*ht;y+UE}zMBf9!hiBh1#W(^ z{8HiN(ny8>=GMH(UjmDVu%%SUS@x@z36l*iL{TNJP5NSi{TrMcZ9}^pIY*0Of<)kE zP?1dV8OL#|a667-x+_0+i=wU7Mq>D-S6^<3;u~*o>#VktvI#M0K_~?8HIdhr$h{#p z!pTP{P*sQA2>T#B{C*WNcBG_Odtp~GDK^iOm46p{$>RH20dZ>REIwDDm@IyPZ*Y?8 zb^92Y=0crb$B88#s^kr609wfU7 z`Ip-pw8W%`9B;0#N2u$EDA*Gy0SQk=Zd~5tu?#)rbder1m!XHK_M_!Dk#tL=heWxt zc||L11jDd%7AgqER|{m=PW+&*01`Co!8*$R;9hy2%a*J?`v{~5z=SC3bpnvJ$H|DZ z@kU%vQ7^Kxpi?_g^e+tRRt+E8Us-QU#wr+C!maaIGX?+YZyYuR(5P&f7~%DPFTR?X zYsH@vZY;NOtBr2wVq8g=Z>ZK7SVMn6)<&29+x3Pq~Kxwo!5rb}dB zp_xrv~tj4mWE*?VA+>0pFjM&1}!6p?~R@Vtv3O zu$s2cF3q8oe~sLKDkvbAo6N{4VY-16X@Z)_2%tge&ghSn*1R`*HSDs(YSbl6nD%&r z#dh6VR%h$(@_m}zq=Kv+E$!(nBerW!lzF@}0gQZ_N%RhVfJ99A zQI*DihjZB=8~N#WeH-qG9-@ML{_fCvi9^GjuCrM0tvD^BvsT=Ts zkE3)N77^6HV8O6RKlttp{oo%K=?8x+LqAaMA1%Lr@FS(s4}KZBWcfK^#1jga8gx%q z#~^KWEZSfRJC3yNgbKGwIX=jCA4}0ZgaLJ z4FJB?;yOxARc6W3SfLt|Vq4Wv_rFjoScwNCHKe?SpaGG#c^#A@E(IW=!`>Q?mOZ-h zk*3L4{?4P?*cB1>M^9Q6`y9^5Xm>7BRz|4w1Q8z@LZ=Yru&C0O2?4$GK-@9pnFv7j zyX1paC=Wy(2PU~gfsy#p7Ppf!9$|J-*c3__b-+4+?)w@kVV1&Epr&Lfc2X}R^{7Z| zp&gO_KujLiKu;J?LyhNAOpq~eL=illx`&)8lGKKzzAXvlY=u=gE!8L-z0CUHbeF?? zvg>~nMjUlcy0Q<(L<3Y4Lp6}nn(xK`g$yO_e=AbbelbHyQ|)75#QB-hC~5yO*N8LJ zGK2VsSI`=;(BO2@4H8>h2>WVox4^a7pczYLjhIOcEa~=zq*k)v!OYCW?-@z^L~TRm zzrn1J6A8PpFR4*z>Qq?PSB|hEqAuhKK-C)wLG^ovy=C7j*#?Mc94eKbW>S4rDm-3& zb;eD3lR&G03Ai@7-)feRc!Yh*q(viRRn=$IID&n!#sT;|p(Limi0c{*5~~hQb%rFz zc?-cP;FSzYO|FBRoxC1rI@hAqtIIH=LJj5 ztV5vAfB+C}GnCzl1h+6fQFUUPqTI|0adx;@V;8?f0m)&+|36fXPrQ} z_^&eb8`b_y`HfJ&QX2i{`P=f0P$Mk%o@+Te3`ffWfJ)08!4Sq+&+#WyOf(%56jdQd zucFFL$old%)pUG#)#?tJ9_n>(IS4gaCGQmr&C?szGmi3n*sH@hpz_``(Q^=74-=jbnl*8XzTZLVJq&UL61oZe$n#rW-R6vNo1Z>Dpmo>s_w z*0?!OKXMB#QQ1R1jX1=r2lw+<#W&~e%Zp*J)~JLOE}iH>%{tg-HTrBHl_M&lUK+7- ziP=dlo%;Fk!+}D4yM#h1V;=57SGvK%0;*r-+grW?>4_T zd2DaK_h|O_x^}Slk;8JKeIaZ$#R0Ah^_S__km@ED*}mS%I-r|L`+*|e@Lha^n@OwM zKUjX<@V82%8y?M#IL}<#EVrZd$iTEkd`d;hT!dXzWVWfGa*9i8wK^AZyk+I|WfcEM z7%2T62RMzwC`)H{(n~rdJ1Ye>c+*#8LZxH|mwMP6(S!b^6}CK6c!aliv01{?g7?_A zkR%u@+S=}2Mp#Z)co}eauyC9LuY{40KJdlxLMS}0?MHfOMX?^7U)*Zen!P~KjUbkw z)jS^saxM~Xl=J=b^jhx(OyQeH_51R3$_!2?K&}WXo(@i(+r%lH&r+7L0y3K?jk6*X&BwT{ zy4^=@9{RYo^U>usq6;EN(Ew`iN3zK^fu+BFFC5SNYI2SXlIyM_1Wu7cznIFku znX3Kq@@r53XK7gGBfDjpa*0Q0o21>q%`Js11sxFR(%gg2TP1ZPL{fPTp$)i66A3-B zcWl%D{t5T$g(BGd`m-&V0Ju=3*t>S2e|8WWPUlGpTp(>+`%sD#6Db&}$*lmJSs&c|bO5|b&M{E>v#H9?5sM^b) z!TjZW58QNs{%!u}(y+$Q=hhS+apgy6lp50#m;s&FAn}ycx>xsT*67m#6|pQTrYnO1 zy^5fFuha%rD`|wJ4C=pyLTH+j^gslU?tzwQtLwa3kk~Ogt3B$PQ3VNEY7I$!U`4%B zb>+F&8y#RokGP{Tl_}r685+_^x-*_r6Ggx3IkM&^E%7E2ha}F8p)V7DbyOhRCtL)N zu62u}@Z|X@L_$Pa3syjAk$~CE5HR0fBw)^G2pH8q2FKyu`AA6)zg?4u12ua|$`yyUD(zbRc>`xhT3lJ#duf*V zcPYQ(n2t(HqUibJ(CLuYTlRV?~Cv=Wq9H$q~}H1cwx+k z346QS*^auc1m~JLi*0NS!*8A5jqa4{A@5MOWymF+70Tz=)BUtjVB5O?IqOhe=fOF7 z)^6VO^bSIcak{S4iNq2D%wxJu7;N+>Boo3RC@Q?1tozt`=ZE_1NuiH?VTgQ7{{tiB zTV`~C*JtNjs#F1$sk6%zOVy2BO zwU2?t?B3Fd@88L_m=!MZND^EyGQq?sN9-aLJ~~gvp=M3*#4R!;7#_1_xp8ln5F<;S z$zK&S>c^&wV=uNXH?HVP=6xc)=xq31NiUQJVFt_x!@!Y-=%0(ICV)#>)F1`8h-%3Y zEX`Orh%>z$?=-y}3pj+YOGnrjxHWz-gGv8X5tIJ=8BAKWKUjXZMx``N`t7+)dW9YR z$(oMDm7RSRp-b$m>^+s}vcJ&~{$Jvq^K`EEkj&Z}7eNeJX2RyhgjjDN2Qa{v}N~`w?7aEm+Q&M?t{kS z^0%n*nJYV$#<{_j^=2!2YG!5zr8&Cs%=z$KvpW-a`rR7kNcB+ZXl8a{COjHq`$XXc z^y}eG1Ov_BepsUq*E(BUo%T%Jt%;M0swogvaLw>98Zw8AZFgZ&=^?QZ$i}TAQzei=7s2Z6%E`4CS57Sf zn^RL$8bq`+e9iicTBcAJ{HeewRh+&lGt zxZ5y7g*tz!7HyMx%9uGL7z8tXriT*I)qZ;`?8c3-bprLI zD|!W{#qw2GKXH0~ZF&C0{PO(L+`O^5@9FVNlg>hWG1@}uA^lx+QXQ=l{iB~*LDJBmzD(x7UA7fnhrx+d) z^AY2NMRz*q+~&0OIdt8eAju^KOEP;jDll>9!+s=UD8+X|UIG#c?d)z~!o;ILXV{@A z9YIL#B`HM{eCC$15yKSB*_k_Pql=A5E)8w;i9gEjJ9P0Naq z?{YUd%G|KZEN!?nf~q0)0{^q-{F+@D?M6>ftXEGR zKNa*YZ%0XeF3dong>=iP!#Oh}zs>*ZRdhlH?)amH)Dca_3yrx+Fk?Q!TN8zHm3yIdLuhyX0Hy&m68}rWBcJM;=O@Urkv#ihy;D{18 zY-&T-OMWL$zcAuJh||NyZ3Qk8lk&~a^0T_s9YZiW?IH2;c)^iHBKnl&Z}u=N%uudH zZsp9e#RW3d@1bX(rr6et^h{?sMbvy+R3_9e#RVk~@-3cuM;5W`t_*gQZ9_9|LWefH z22KbjS)XU(Reb{aW>(TXlGx{yG%Vhikkga*qG$obVPAGiBBu5&u zdU+ZlYB=M#afhH5^@JyzccsMyRe5f4r3a4V0jdU=%^{4vwk>=z)LH$srO%GIMsZTsM~z1psfnAf%6L>Y+t$}lX&O^3$d0&lzEbxpKgcY7 zldxs`1t#2w@B4_a!r-1rGi)vAS@Nj4+5>@xUNx6W_U*)sRbZ6(JwuQ=4AW%W#!g4` z%GE9m03CJMH-2lwA1xLVJVg(~EGO37Xv5)KPiRYh`hl!}%$C$Z*mGKS zS+WL^f}zqoIE+w^UBnSGPW8hUD|sx}s%P=J!i_HtQ%$F_+ENQSfr?=h%~!!3Zq>c_ zUZlj%@HLQcG|hK8+HQq4D&{23%bGrdilmHZgby_#dWG9@(l~}+diCW7 z#oj<3Eau_PYAZ5Ke0rKY3J!ctn7GAGgcCd{1ojCT5b|e`oxEY`iT(n;;9GLo#$#CI$=D1P({d4G zP8PK+Ff+evp7PmJawK&}9snirTyqfS_|QkMUa^l56*79Sp{dj$xtARhoG5S2#a>AiYncrD!_Q&uK}ji6g>sBs9B1dwY0`g ziSN&f3ALBS;NJSBSRY^qt7%Joxg(<}Rz%v$1EFbnw39_u?~RS9E02p7F~Pbsx+NuZ z_eLti^E0ec*x8DZ4UAA@99ZC40QsTWiQu_f;pie+Oq<6(DfB>VSu;HL3MZnYoR}(` z{IlFpb0Xkc6pbgk?9rk&P`tt*QU!LB0bh=l6Hl3c7ln2rZ?8e}}Mi98T{`_u-~VjO!Z z57fL9V^F_eg%HljJY_dScF3IwMgRjp4V?{TK!{6iNhw<>g6Za6$zK+Qt9U*w#0GPaC2+0B6&g z8X3n#C*c@-m#o1N)N--dPUMloV=qWog(4iwsE&5$BC?l&1m^t0ts6o>i0mXvd@>=R zS00EvhL|Yevv|ljLJTsQZYL!s!t8da0-I>LPEvXlIhHH9NhTAu z8_8+ZWF4ReiRw08C>UxK`d#WH^%Zrryt8oO17jf7s9xAPagZMtTyU#oy>bvwX4LiL}Ov5PIYWq(kXYTr zRW>o0BxVgI22Cu#JUMSY?Dh@?lk?USTR>V9G|!jMJ8!+nDn2=&n+TK@LX!*=>;CKV zfbJsPteCYl)v0h&jEtyuPEK>C6Z|yv2#(TjmJ-T_(<61q^|p7!1(l7iBIZjULI@9u zLRy+3COV{^%3i(_>CU>j=fqT-PZK9bXX&cDT%yewSt?mk4O!b$3paQjymsZrI+4cIbgyU`*)t#IFZi&33eN^1$HsS|jWt?ltP-um0d>l!T@mHebYYo;FxRi${jhnyZocOLk{Dh^P0EH z=C@^yjvou0$GG{B9vx=z6Ew_{vA&4Lbp0W{`7*}QZ!`!0`gL@gtDvleq~1!>o)pg6 zcKvzOh{mDjf|_pi^-;#ybcgX8+b@0l~?`OOY-iYIE=?qw+{4(OUTQ1z5 zed4AA2c9EMNbJqcLv-C$Rn8{gak?pajsz#vYG!+7>Z<8_7Q4|NSr4xA9A5E8v3C`@ zSJ?Y1)nB|q=knk^@>cjJ#GT}%@M^O;hkHQHgmg)0;BJ~NfZsTls)!EW>!*XDfDZ2S z(*ZXtsZx46_{MV4!Qh`|6M?&|`$-6Xlun^Gx+;xM@ihwA>htSp<28)cUv0wn3FLwm@2^DI zEw|58*NbLuJaxRHubOBFetjZOZys(=OCnG1fJ9*nr{nH;3sI2_y6+OREO2xze*aC{ z)A3^@4cOdw<(hdqobhTOhZs#XxUl~|4*v)?2FFMBZg@MoMt+T-$Kt`S@eA}L%dhcO zTqGm6c`uCHzvyZ9`!%jVDt-=Qh57deK*f+=j331Z3v<&~@XH`Y-J3s)X814uJN%%f zwU+q35)RH1FaB3I$#!yJ{8G+ya9~)_;O+p8&0oP|J_m*vY(XcD?y4 zdC0$#-27nHO7iu0J1fb}50rN$?=8(r28yVSZL-KuXKRJKrngcO0r@P&7Uxz!VIKA` z=aaITf=axES(VojRATKrxcP2gPRp5Iu8qVaWm(pj_E9r5%?~+^LP&1 zJfLSA*1X?-wz=OLwz=%`toW-u>Sj54B$!p*7$p^1VS>U-HG-H)P^Ur$aN?QnF>~6$4-ztQdj5Tz!JAz^Nuc8kSF--rQ$LC zls109*aaB+!^+adI5gAq_Rw!ohsmda9zQ7z5_M@vFY3Ya6S>kTRfnE@mZfJgNJT*v|7F+BiT z^WF9X@D0`g@Ry4LP|&k)^f7tJY@E^}wka%XpQ@trRfx)k?o!K-l1aOHusorM<(qO~ z`9?h~S#!mHSe~*5mY*@hQcuvLxC{v#`jd&|ICEJjic`}vg(8WSj7KY2D0_4M7CrV( zg(BD!)a(;@TET@XV7@raf}!@Ysyw`z$U{u~d^+fm zBQ=Ywimo1(UJfjm^sr>j-05sFEYDd3%g<)O(nQn3;N%b3q+n#VJ~PE4U6uI@abXs` zlr0%o^bkCq1HpzKf~1R69Bc3RAnHwC8ANeZz;I0$K&}N zJU*|-BWoT3TjHm!!DGgrdB#A}qM)pi`!^>EXQq$L%;w2LYDknuEBGf{8oo=9zAw)~ z-@EnbW6dLANc<^l(3i1mmeD8Z*(db0;%hL87z0uJP*o-pz3{lV3I&h)(+l6L$K?BR zF!_~wOtR(?&QVX4IjH=Q9+j+l!hV;_|Fi~`zn3L0B|s&t#e+doE9Z&{&s>0NpRLMJ zM+?=*{I=U%gy4%j-}Hy|VEsrASie;dR@Pj#AFNmIu_e(WW2ci^8YQw023IuaY$|A? z4`2qVey4xCz{&oKXHA&#X(3%v05XL!d z^d?_mh=@!`*y(Q6=x^WtF+F-enS4`esM9AF zpD3oGlHd!zKJd{OR8T*qN93n-5cz$2M6%`)2)@{74I+Q1I3fj22ZEzkzzwLLD&W*U zTGdW(m306P)4~h?59)#XBRN3*Aw5u8^Q8R?|Ld&*>dzJjszlX+pt|YBUBPEYeEA+7 zDhE(Ze@u_hpUlDMkL&TtnqO-_KIg5$=U)`ZrOP63$*B=zy~80FTfK;jFP{-C6P+mtw?eb(@h7$zD$6x4clK^hI$pe|3w3 z-1YC`k(`^&UK$O}@}z7|DF^mhBkCTjRsGL&)uJ62dPbu>(lhra6A1 zuS{}&vA;>dC0tHyD3Al+yDNSxJg26e3C>gRPFFc#wz;7a&fYsYYrNP4MSp`tb3f z2_|vL0-ETbOgRQ|7{UQZgJ&H}56DV9KEl$nrS5D-vQmqa(;(f8)pnn%Dpd)68ry(e3}NHSKEw3+$_4G!^JEZ|rC8$I*?TMqMoQP2FWc?3*1 z@3n^c5BF2uL?&z&>&u*)QP_mn#$Xjq7D2&N>zUAPvsl1UFyIfVT~gLIp_9QYc?_+# zNs=y_Y9hQtmgX_8l2h}XA(b@IP6OR#keB))1+LIx@CK>+3G4Kf3nfh&-8Ma}R#`ZR zUetG)6jG2^k&fau1q9^$jvZPFIe#5%H`Ps>zl?QI3a5$oy_VY#KQMU{Y#rdIQD6NA zYoz7VnW|DYp|?w5qA4R~ZM-xD<~SEjbVbof9Ns}Ixd@NvNem`s6XHd%G?6evksNp9 zwn_mMm0}PwA-ylt05X5QL44elCq70Uq=t^j+a)?Es7b`3 zWc;`^WJiLbjrs!PeioFub&7*pIr4F@?RC&LKUaUc;kAjR%J$pL-(ukX$vob_)xi6_ zeFS_rpR$JcuPwU2(o%Tyj8C{MjAya4j*imq z8hATA;z93%uvl^Zwlm2+=~1H-oOO#vPO)&X<#uzX`CZMuu~E7u{`WbJObzT z%8giLO8Q~K|9z2(jY^)_pqcP_g5$73a6FwSIG!>H4&MG6`vu4ESR*)=1{NHg$~mHg zb}J%0I$Ke$VFU!orKfo0N)TF$CQpLA$sj?zJPGnvlR2+sgL0X#a>JueF9@df8r{6@%dTnmobL zFbEFbZo88hygXmBMsR$7XI7McHhptM#$v+{Nq(^t`VfgvV)Q$1&BNog=&l+H1p3 z()HoIrHkJ&~3N{2oiH&7Ay2P=e({KvT?8;N%Df z3Wu?c1P+>JCLqFW=FEh=Cu=P%E@gx?W69>Q1#9NX(>yH_35! zD0V# zpNim|@~P>$DR;lS)(J3e0sov_bc*~wo__hT0g2h3J+ zw7_2Smnetbvq2;}#RFD4)3X+dN^LscT!0l7$tobutB@=-3}5&RSTdL6t|*mPgUbX*imosUhWU3>ZO_#|O-SmO7TUDE^t{ z1h#He{2|lk?}2FiD2hl_hm2>K_A_|4Y{3>1_V5t>0k#ny7IIFkATsBQk!x#%Z2CL2 zPd%UaMe$*T^1*LKKWU4qO5j}dG4j4C!G^7!v0`*c)qR!Df7CuU|8samkXo6rFfI54 z`s0k$m#9@YiV=gqoMs@Ctk&Y30lFfKhSJX6fFuzgqJ3yCK`;y@eePFLYN$5J{za7T zn5aB%j>p_LPmlp?XR6rrs}hzEU;D`1$)|3(!KZ&qoO6XZK^}SEL}0_#&e>pO$UE62 z4^cFtVo;g1B~ccI$PkaaZz8Zk50MCYM=InKK`D4X$XdM(g^~(VM){Zl2qZnBvI#n+ z);_fWl=j)V!i$%56{lfrqO2l$Mg@iIru1F)!=wz8{y&EiY=o;r7iBnJ?Q}Ezwq*sv z`rw$$08&N}8LEbHnK-jf`dt+-ZTy0BKCL@f)kB?^_H;kADkxkQjG zFFDU2mbx>p$$BMQvsejA>E7WKV#~ve;trxE4HijJwxT8`$l06b*~`hVQ7Wxy^UJ9g zP}c4`1QX$IDv*QSodr=Qa}~Pq_X;ZRse&EUUxw(uEaaxhsd>(jd_Y7*!v7L02etH( zmu_S2u09z`6ye!kp28(JrC%41i%QNn7i>JRDS36S4Q6vVmi_2*1;r}|krYFh85pJM zPZ$Bb-lpdnVlamT?ju;*HV;(nRS5(NN)Hy9gp-=PRnbc8WT~g^mD)s6mQ?FY17Zof zZ+{h_K15JiF9yIVHiriJG@!iUVo|f~8J17jCeD=gVrX$0rJBqp>&4`;L{`BE9to1Y z0Y=)DHAUjGBz#eaS-bRVifjgM=o`<48g_z}JhyZW@s>;LZ@vkSu3abzX#^7YkE=!+ zz`Lb2Yef8#@$7|BuRXCDcu|LQF2HEFA`oM4=uLqvc07*`%;I365@#5zs8(EKY3Et{ z4O}xxw<0*(l293;`q?Yac|fh;2Fa zlwK%vo|)B-Ykr9=*MX7|iA6If6)^6To4_m%c(XU>nd)UcvrIpe`5Jhc@~WM=Y^$!E zb_a&-GUZi1nuHkUNh)5o?vbQ^Vu!8lO%&iwr@USbNw2NdPe7h&*MmQPd>sD3?AM_8 zs$<=-0R}FA*@w~`P#$Cf&T8^Hm{qpXc(!q-QOADI;s2gLJk~gOc&yua2l?0hy*tQ1 zyN!3Q?#5JgmQVXt+m=sj9=b@aB?I$%@7em~-rT{g^p5tMFr4?yZsYVb8x^2NZzuZS zHhM4hgDRvQSLusLCTqCS+j-h=*EbID?Ct8f?GfM@Ve8d%X zIQq?RZZvjV37c^kcH<(3jo-Uz&8?k=gDM>jVjkQ;T!EZ;DNF;(iPc-+k4#aq>Ohaf zyH;<5UwA_T4mVQhC~%nHw}ZCtfZ7~;GVZ%pff0M$-Ei9^+}7CLyQOL4ux8e8h63SPCh$OQ)rYS{g4AB=tX8y9jvJeFwFPq-C=;_UxA2QLKUEizF zn>9Z;67-(8k=_q*de61zy;jg!SJytU9&pNHoiqNoihwIZqQ7Jl|5iQmS##Aye0KeA zh4`!)&zYq1$Yy4LvyH^RhZFx%H}q7(Q-#!_bXCcH9V)7^JYZxutV6fy$^I_djA!R* zm!Ui8^&1qjv*vgiT73xSR$hR1Mc{l1v!Qq=efU+%!&&niOk-l#)iJT=TVyDDNO2q2 zoi!VwI7h45kUA>3d!?r+gb<`O6EvCD8&<>hb`5gy+y3)j9lKVryvuZ_#7dInr|HmES$Fy3r|W|phx-~bdZXh2g8*OsI&>y zrm|(j-FiH*<|Pvz*!7AcQdl#dg)WvUYh}Y%+lYrZOL&0u%EfW!3>ya>M%B&ZV;2;4 ztu?aIQPHD=HQ!}I2fOYl=wQuvjs!YBXd^mqITHZYpo9ag6mYW7q2O%$DN^i~&{ACv zUPg;^E@CAYQO6~l;Cu80XU$mEU8}yZ>#C~2niE>9wc_AAY$SM95S$zObu~4XM1?^-+64;= z82(`IVl4{3-$oQHK5`U_Kz97PCqfWdfx~!K9f{UTnQLaN$7q?jyc8=@DJ*Z-(EE=C zplRlSWv?DA_vV0QAHA;XkgS=n3cc$9EI5i$K+8|sh?W&2TBK=ZaG~Tj-~}tR!`4^2 zOLm2;0J@F>=n&ZLu?BUvyt!Wwp$Bpx^cs5oW<}nxW^M}YT@MhV;}``H{fdnsdb1Hk ziHT(};zTz9DO2#gNWmBbj={ciKo6XE<^YG?cAWwo)_k+jNK^Keol8M|JzHmbkiPpG z<=LzmL8;Z5*!4T~w^hWDV72@mM2L;%mH%cVbZ_RCKA}6YvvQG?s)}?uY__w>`;eZz ztQqN~B`>?Kl9x5d7CoEE`?ofd_a+m0fs!h9ufu8)XuU0B3VB#hXx98D6QSAls=~*t z8NEEh6k;tO|ErCJp67(7CJ~J_1dfK4$qU;cw2@-T^J*7%$<-&^uz|OMtyD$EH7E=! zR50Y|DzDe$Vum(fYr@4N^!lv|E?BeJ8)?*CYhm)jS6k@CeP3Er!RSP`L6VeLMr!q&cV@1dR_J4vt|zO-qnC3X4A78O9yPk(z^;{NtjJJ&5HpF6B~;o>I$;5 zaDUgyGV! zuyrlD-9n0;{Hq;fV+OUwxLgz#8N*(FXrhO_BJsZq4{2+p*boLbY=+QM@BD)sUDswi z$qhF0$Y=3z*p%N0p~uuLWniFzqv&UzT%*<+r zgij_xd3dT36n_d&8#-Z_59tGc7`m`v57NtI+DO_!{NQX6#HlR%<*q zMRphBW&V@q+GNZ5jX0+9VH8W#7!0|vFk68L?XcVOIxH^g-~L;x08kXguC+ zxAD&$mApZ9Yt?genl>9Pdp^DXYDFby&9CJu`Kv%BXH|Z1ERS`;Zsa}mMmRg|AnNGW zTKV2);cC|VN4?Ntg2ph-WJxn(hnFAb2-0GSEUCPv!tc0@WROj zal%oG+@jhpUI=SNF{^b;kL_o3u-&HDRpT6M9_QE|C98GQMm)ba6VI~MI;J=V2J56R zv01}4P-Ec#>Fs7=i}eklu~1hT!`K9~Ddfn}P>;2T` z&B7BfN;2@PY{ca!`^Tjg1w1Cc7&ND$Brn@nX?9(CvJ!1|)&{TsR}0_Dc^Ll1Vo<;UNpRfx|<;klAzLgwV-gW1xR>2OMjNIshL zX$8$nt56?O?BX>D^f3wYG78Hc+tUqy7dhaNglf+Rw(XmZ%?|tmYNLXY!B&boskN0~ zR&3Nszwer{v8j9Rp_6!z0}dz9L`mJ3SScly%A8<1shfg(%#ECyyN1Tx(0O8U@fiH8 z2?^WFLC1kCEm7o*J$#D%=1L|;u=zD7)`#P|67icAbyzaYOUc2TD;yx;BR!F>H3O%g z>mI4uvTr7XS-e81|JpRzbT{GkA?NBP6Jo(?Mw2IFX^>u?nd-8Mbh`pim*usbjVA#> zTIbOpsHD!w{uXxp3*KP-oK2_l#2*zSTKmb2EEu=|P zMn8a(=TdA!WI8` z>nQ8}wYY92>sz4OrThR|1d-iqE|yr4UIaDN6N6-aH8ptUKJ1017Uf#tc&PMT8yv-c zeK9!gwfl=a_$ocdKazuScH4^;jI-wZM+)Pgun^-XU%qQ>jN3d2uKOUp6Cn;n)B)ak z1fmJz0ITd^WTsGI;O$%;V9f82R|YhlkPzKNZsbA_y4PNC(FlasNYL(Fod*0a;BiG& zah`;ZZRFofD@t?NbFR zt+HPYp%y4P5SXjX1j+b7@(0DbL2HPfuBMq>KzqjI35l_L*=Mz}K0ac5|Z|o6|PmC}<89XXJH60pr zBl#Q}(}p!Q6`1lT>8FUm24|PuU-iS;)>^cVvowAr;9)2HYnD7p+p{nc>^uDsa)rs- z#u1{fiMOYO50zh%@W}~s`gZ*i__3TNklm&(fvov9c?m=`Sgq#$sD(>l?{E3~htro{ zV-m}hL~WA!oQSexrU;_;Azw0+VkPDmE)J?bO*omWYp#KyioEBL$;WHabNAik{mE+}H9S_}{4V=W^t+`56vSoC!u^NB^5PLjH}IYPkq>C0LNR^O?j3y%6#r!@TTDj(m{)WJ5EKI@>AGOX{as zC5!Qq+1s!adpWBJ-YK^><<{ncZfz1<4rDOCDVh;#ld?CGE=XG><8zoQwWmsyDRrZz zUS&^IRjRvm@&qkti?}QnTt$9azl!`>&MNXN`c;HA-#1dL$bYkO6{&;GtoSODruycW z6Etg_B3jMptSIB%whu)B*{~%}EY}vg%9ducl%0s|$S=Iv42_s4Cm|eXQg(<;5?RiS zOAc6&aG=EENbHEUpwoeaF{yz`U_7zxSY$DSJrtb5*$xkT>lKVUqIFoSCr`mYP8~VA zIQQ7x%=F^i(Icl8j~<>qB3G|Sd=lM@Rf%lkYbSpg8t?jewz{U?qFYms1hBk=H9ybC z`rq|y>EGw9rGKGcOIh>8NUf!RXX9FW4vY}R*V35UlHWCB+tm(EQ9urVnG_riV*60g zz8fkbg3HHkuNU$6;GWRy6!xYt?*v%yg{LEQ&Vn<_X~D@+Vk8Mh+*`yop|aQ=Iu=6( zhM3cdyvt*>FXX=H9y8WD28Iue(y+$%bP5O=E(=H=tnO} z(Y+u%^{PvYp@``&;qa9gy#t16F8lfkiD98IJS0Xj8)v>}f1mH!&q8nTxAGpL0>$QO zK8Zch+zG4FneQ$+_Z&Xr291Z*f~Zw)}7%r?Q`At9j3@!7kF19K|Sj zq}Y*+2p$}UczvHbXO@{egiWo8u@Ntz>gQ%dcwie%a=*m$`;FmQ3Ze$B>qu2(vMbLH zC&&ELw_dL=r1es7+V;24wy-p&vE?2Uvw`5qgUgnzlLA03NEBz}{%9{CS4xqTF6AlGOyW4G;i&(*xO zC?hylS0Wi>6G^U_EL;~Mp@pwo7%Q26g)>PDRElmOeH(7O-t_9uO2@5v%TQ8sT|BeT z#X2?+5t+*tJ{qe+9_*}j0z66s-d69ztl%tobVTBviN&oS^O0xe*($HGv7W&hu)(sh z6Pk#vYRC)3GPsC{Jx6sn9-@V9C_-rIEoH5x$|>(!Ad!Tr#YBls-fYe!K?etzEtyVP zmSsRCqAbe*0rFEI97s$pFo#}LmD zPZxoll?qN%yswT=noQQhmAB3Da~wx#M{=VS%8dSOSd{speVMi2^l9?)gBkG4gKZ2Y z1vGQTOsMo=FKJ&TcjbRGx|N0Zpdvw8mZ2=mpzy`TU6z6BT9-^pe{%C!rgk?y+-g(9 zkjAQBrgr1zUJh?8Q@hb_7+YM*)NXk)xQ3nbc8x-6cTzSIFP5AFiw(TgE!tBP6(&YF z?Kk<^s^yW2#!D;Zlj#I#S|uC3G9%Dj?c~CHby!W6{OAd>X;&n8MyNzt>||t%oh1BL zD6e@$L7y=J#^|kTvc;{%rPd4qTT&2*5FY)+ zYl)jmiNlQwVoG2hFOl*}WuDjt$rI~Wiz~B;;=>Szl10=n0WNTY$sohMu(=2Qj<}3R zXHl}srp2`9CHg@bk8Um>S;nK6@#tkdI#ke;HVUZd8uyHS?maV~HPHSOpUC3T`&~WI z=FMW$C5O5`R|&lary6CTGS(o-$_SEhBX5kT(X|Xz#@OVo3{<}Q{Kf~qZVYM-zz@{H ze<{QUlhfFKe4PDJy%qjAzfoxfy`A02Z*DZ6U7dySejeJqvg|iKCU2b2@A>VSe*Gv`dQS{O679nXnJO^Dy`6YE z=0@+8eozJ8W=vE?-mX$y_I953+x3mZJA1o2ZhOT;p6TrhBexTgKX*3$wzqM3toMRi z!-dq4$m_try1mO`sl^VfZwG1~9$R(s7u0|5T^D*y5IK;@a-2VJ^=)ub?K{?8UD7n; zBd(}>kc0?QZ?|1Ac?QM{^6nyniQl_z&8?kwS0MY+fRG0_P?Ewjbp*a17p2uV!5=-9 z{a4=tt%rB5z7>99rv%AgN#UW8eEtV7qwPDOLO?y^zH1ezu*cm9w>99l#_rx#wFZ=w zhV;;lXEqS`Y>c?Jfl?E8TOLVmBxAfnKgL(lu|Q^H0+xGeGgd#)4)I2M9f}_$Zez_i zu^}eX8Uq}&Rr2o~j_5)Id$kFp=r;V5znhuJ7}Jw`q-J^MoU}i{jOB;uCgC6?m|-Fl zq=tw*=4nh2d=&pT!%OVr7Ui*KaJG7^yVUeU2*DEf6GuF@z$4!5bpp|&y4(p`b08Ak zrKSgko$)oGTML~!F$$=^=J{tJ=Z&6kG2Q97>!ia$UJ4zDkUaLn$#4?iE5B(v3R-^c zxJPoNYu-bPSR)umb&%%O@s}#y60Zi;k#Y(RKUwT9vC<(>I!$~MjuC&voTM*>&g4Of z6tqA6Kc9mt#=?ivHbm}3zPVZpy6xytaJDVaj@*sKPt*KI`2zlZK0PeRT@5-14nScn zoH+48^lkJ+4@jaO_;gBD>04;OBalK_A@3wdY4E+el#fm1=*x(V za@#ep86$gxN>hWR+OL;XPv%Ifd-alvHII*mq@6wUh`Q%L=slR?h>wM70A`z4^=PtKnInJFGaaX?0!YX;!}FDSkDoQ z^Lnw!n(;&%)G;kr;67-TSbSHeSX78wO2tNH6jiyckcv_&X|w6AxJ`66ctXE7X@VE4 zsGMtq9R@VRqsx^!-F#F@p7sRQ@H4f{7ojvgMyqbnDnD6U;bpxf-N=!oD|$)Fnr}`? z($%vdwTO|HZr5Lq$*~LFrLflVmvF=GoU2x==b18WFfE*+FTGRQf4`qLzW`pU-L|va z_9(Zp<_FkV8@t&UW1GS?u+HHCNvRe#w5#+Mg|l=F*C_8`%~%#uJ2ZCvF8ys-=#v^f zz$EiY6#G_L55+IY%ZVHF3qZts4S36obLABHsn4jb-u8x}Mfq1??kn`X0;w$${Uy^zi$N9QeIk4?os?%V@yw zH>`r+4;F!+L|Dg65nxM_fI=~TK*$d~_=#lev zImqehk;9s`7ESvW&M#Vp9Ips+VzO$?{0Whji4+es*l)+Lw@QQkluh(F`3K+&!)=kQ zp}^jVyQWeVehMnQvHJrTk0LZ*5DKw8l(a6b6CPWoEcO2t)~&hyf5OnkcK<`zH2(|d z+xa~YZggFc1NQmEKWdd6_{Gdq{v^xJkj$x)*`DybaCvjd6FY#n!6}~bqsPT#x5ANb z`dVJGR!oHRus|`&Q2b#d0|T4JgC0-VghTzp+_3=}ynE6vbMcv4S5J;Euk`={v&FnF z9xpzKZ)cZ0Ql(B7^W};|3Cz-{=|76Co8^krOl=fm6yi)4(LRQ{;>53~Tr~XHk6G#j zrU5|{TrR+|8FW*Aej~OulK2m@rXMS6p8rO=?NKAuet|X{Bh@}gud9)2tU0|~8j5-T zWAvq}dH!>>*=U|;x2fiN);t{Z{P)l?sOEXrY&6fa>#BL4H7CsT*G}5Dxp^gy$d*9A z?KaPULQnATqs>P1{3q#k)jZFdwdQ&I2!E$lguf)s^FN@6-w)@&@6&qtv1YA#-ah!< zYZd%1P4oPZ=<)XxIr#fgJ^om;);w86+ltZBTKls!TGvy7Vk;P2gYu zqPbiKJ;_VSeKJM%IQ`G{P?y2@^~L=UTa}CzM8-VT*hg^_W)GZb*WB((gXUxv_Er26 z-QB6aihrfeMqkA*)9b3Qf;HchiU}Nwui_8rOI2URKhb8RuY%pC`YKrSaC{YiNXMZ1 zDp<48SHZ5Uz6#cy@Kv;|+rvF)lUBCfzKTEA6Z}tUv(Z=aC-l1Nt6&-L(Q&01fDeenARtKfHO`YQfLkH5dm!QU74_+!mlUxj`6`-oNe zyEuIn|Dea$Kjz@;OL}~a}sb`h6IG5?GFqw=>pM`_&;NesNyf}RpeO7`geH7gOjlau$ zJ5%k*0TMWf!!^!6NFB|f4b|-_j4SuuZ|f#@$`?Iw4{{EJuz#a|)XwGIpWi2tWn z5&u$jSj@l70KpsbAh_EALEf%)TG$7{zqbm4m!#8T{zd}^Z^^^pO$H3|cCF*WJ`C=t z+LT;4Vl|NF9Rqr9&qL2`2K4ZDty{uA^t{L_^lZ0V;&aw%v|q4kMJekbm2L@IKt)vV z?8`1Q0#Tv1CP#eUDmidZW*sC5a!=cTa=i_6?Z{j4I+}BW_Q6>O6Ao5q=jK%$Rpv7&2EnW}~n9L3VA?5VcgHC78c=Cx`Rkb_s!hpxzVhHfMF@rDbhIo35| zGR!H_I)4w_WvF)YMb>V#lfRmEQ0?TreQIm$${lHM{ z&$n8U*YZU@V%|`x|0X;|a&@*p94{z7n$n8TTjGPOD_VcW2wjv+O@3U?(S%Oq_K!)Y3o5R`|2MR&x5z)LZFyYAomb z3!3INw@x_t)Yxn&oduxjy+6lBU#=+wgp!wQisVT{=npW57rYWru6;hqX2q=Yx1e>N ztk@dce4c6kjcjwHn&!WpwHrjDtnY~suVZV#D8%^{4Mm2<;w+| zhSEg8Jtq41S|tbmBGW`~HUe$lK0o2yCmG-rOSk2r?EB@Ia%Jd44Q2P!NAYqiJyzTI zorfQWi~z-%%6s7NK1`BU_R1`@N7WlpTV9SF%LSbi2-~)bvxY z=DlpIq#FA_!`h9;{;y;mRAWDHzi(@d{U2nntt#R4-^bdG#(sX|e&t5qK0TB;{135V zsK$QYZZ!7u4yv)Awe=BP@8vDPQbx@7{yj^STw~zR{ zrfe$Ia7h~bzs&%_kLE$}BL)cacCE4BJ_x?lDhOVh#{TazVDNkLF!`{?A-?gOX)zhEz8$gr z>9-!^6=^hK^=qQeO$~}~MBZX|$pd;V`zsYN^GUUDDqg1(bl_6hZF!vu$KR9oAh3sd z^{QL1r_A^HXX+}HW5#+1*oxOX%J>qgm-r?zATb4*MSQqXTzV9LVd+;Wi}^5kBs1tP zHt)7b7TlCu0YnFvd6Hxv-2X5k(xKSnjiO0ch*SzL2!TLEhB7TT%&r}D)pR#tN zgW-o+2i3vA+tbF;VVv4K|8wlM%CWukf5h624hDXsa&WJB;80HPo&R|@4AsHF+l>we z-a&OR@b-j*VIDW5R>fYOv}$|2&4qq`(LnTHW$i`>!!NN8s)K>Ir+KK+TD6b(o>j!Z zBpnRDW`N*-$%Ejp8z9KrwGIaRAQ)N&!AsM@@LL89{%#%yf7^gT-mY~p*oVRQScSn+ zb1?j#0X=`1ho0Xzpoh0>9SruN=lxcpXS*E?Gx+sZX|zXeqDSdqC>;!?gW-ZW7-p@K z1HYD;O)#U{escW@+_W483tk(|@@TNH2FsQ4>G26?{89Yh4F2!XxMaUF*r$b+9G>%L zuM-Gkb9K2BwB{nOg9m9==)2%?!o)_r1biqRujct@w#P@n-zkiAV#vdE5ri5S0ktaD zOedFO?m<}fp6$BLumTj=qf0?xj)Z0C29P23M6F`}gjZM5ETUUpQyr9<<=kci~9ebrhh)rvj$@HFw30{Gh$yw*6>* z5&Gm63Hmd(DN@b)Ph3XNzcrfm|D4^Zn)P`*H8e&%=(q{8bmdZ9n>O5y!|y|cwpN7q;>OlGyiY4+7 zol1pIiyD5&)gOA-!-@NnsutdXKk}1}=lo#D^BaxR&unysxa_NH(3cF5{NH(y{2vBL z@^-D2%szSa8#Y1mIl=>d0yMD7m66y&u3JBU7v3OK8 zFjd9uH9_*M8*^vf<$?P2f*hsbd?Pvh-c`;2|W ze}z@VFYPm6#@=m!taRj%W z_#l(!p`LU{)@DU`P-l0x5rBG4*}^;>Nc%G+(6m^2W5A8R)n%&M$|niR_0wYs@| z#P_Wt{w0|ddanV3ug!zteFg~fc5PCqeGoir6$HOtZ;VLBE)`K_I$qdqMj@>&qmR)j zq;;L&_`v!YM6ke5G9sz(AdS}0NigqRCFq$|3fu1w+KEU zdwXQ^06qLxZB9RGRXEkjYao&s6MM+i$vR>2j36oM&5)o;t8<#NGh2{p=whzt68;au zj-?Du!vk5~V9-fDlU7lz=!z+#87^Hjk&cbW-9rdh8C^!qV};}(_EcmcHt7WpUXc+N zw2R0@yz3&1O;3tE{J^3U)|ZsV2NTotP{W%CAG}d+<3RC;47QdPCo2RzcE_X0@HhV8 z-|QcGB4hvNWrh$xrP!d;AOGJbu~GV61YI^Eq_>G}9Z~|~p`%NyUM)HZI2a??`x#B3s3`d^@BBaI<-tB_DoAgW&ylf&z4Ih4n_ zSz}U+K%BE8Lp5?yoDC-}q2X_E zlHmsD{8EOSKqj#q3ZXiX@IT4qp(?|lVeLlY&u@H_B1n0AZR>>po7mV?;r~|FZWR7+ zW*t=F&)ZGH|Cg=f>ff@7t9xIyW!wl=;m!%TBj4lCXbR$AwTfiFW49FUlkNf{L?YVh zOZk{h^gnr9<{?~JyB~=m4c5yo^`HnViq>sHUp+xoy;8@Z;!^xS-o5iCvm}){DmEY( z2rg)X=4$Z|ErG;U+iA2331plvxrFP%c%trwS(8o?6Q>f1x!ONL?2QKw1k18Pkvy)D z4U*G)Itp5T?YOt>bv!VYNGiAE;fZbBf~j;%+A(3Mw`+4I?1SKqRzdJHnbsF9KMnOSi$pJR9fq98yNR3$)ID-a_p1@^eP>gAjRAsR zmj}U~0fM|;n@wRK1ov14!BIO$>)#vD^9_0E`FaC-c)K=B!anr8)++RD_c>aBX`Mp* zk5<_Rf6I_ek@mus5lPlWBo$e7W5X2oE}}M&;FpRB&xqahI=@UJa>|g|*8dQU!ASTG zgUEt|D1yA7kMkfDNgTSX)yU`%PZ&VEeynQ$Zfe*4_s0IPvZZrt+4yr+b zwVLtJbq^K3V%e7>NF1)^0Qid=u-S8U%Q|)*xUX@lRPr{7cdx@E;8j z{Ej>bewzV;yj^P$un&SQs~~tu8U()60Kt#tLGZf_5ajJzgMfVyJZBXIN6jGcaRYk3 zHxE6ZFrbIGYYhVSq33;8p{Fzm%-KYblchmm1nVl`jyKN=-~Vp>;xh=?87=tNR#^nY zD+^X$FocC<9kFeQDa#Kc4B8&nV2I8Jm|xD%_ zE&FY^N%K4T$=A#=1~<QjgjU;J?NL?ZNS;_%T##xx`) zdv@8`pT!DQwgldt{P~5kF_vRba=V>4Z2_x9L81Z)IX0J!;;Dy_T}U!SLq{A4D`h#5 zFl(*~M8N_a`wE(X2<4qd?c-7W-wdiBeDyM@HDtVD!%G)~TJ8j`xyb8a5^ROOM%Et^ zqv&`w&p+egI5ZU}_z;p>PGJRTGeP3~)&0eYcbA$z&Ph55Jj@CLr*l9`yoS=u^D*G> zoDC4~YRrSc4(HRuBC*m34nTA{l1R{eK>nx1Mj=4INct8kCysQR&59;II~U*N;Mbv} z9H4%vggVF0HbA`~iZLTT6h~r%G}1Zg`LG$}m)&OQ#Zrr3#oy_4A#CL!&1AdH@e{rV ze<19@bL-Wx>3M6F`}gim@x|=3T^K$SmRM8=g=A&#gtMPyWDJ>`i0Pwbrgn5FINN>- z_teRa^N6e)Tfs~N)R#bjU#uLqiIk(fB@J?pYF-!y7_W0N>TC%$k0sA-k)CJLQ0WYN z^8N4x$!+L|UIN36R$1A_|>D)fO%_CmcwtoIEHg*V4nhx^_*;;aBZalSuCq zvnW2OtiN`lEE9=%JyXGI#6&{PFjQ*GLB;@ffs4(%vGrS%gbFsn<$_mdx*DT_c?(Wv zeE33rtG6h_&~*}`p5uL}J9;6utr7J(EL&&?LpH*EMX9>Anzu%MFNWG)(zXF5OH%17 zmk%F2Su_fI4QmDh0OfzCw}$f7?KW%|diRJd9|XpcCx1Rymis(>r_7Yybf)aZBzqIza`CA3 ziLe$bk*Z+?Uc6RiPqpQ)RVd@eyjG0@;vAneGY!{odv%T23=8-Mgz&pya`UNR z%hYUxz~5r2X?QFBPK_jmVv$17@_VIjLR3p_TnIxwM3}u8M%f}e@4OWLvQex*z zY@9h`f&PSy8*yDZ@hz8-yiT&&CZshnt7v5PFhuo)bKl5nx%*{xb1Su6KkawrLYJJR z;XM2>Y!(#HEboE8`?PZKTa(rf-pb7GbVaH%&AZ}DWaK>)u3XJhQ0n8QFPCA3?CEL_ zt9VBw&P7zZCf=1&Cz^ZK^`i=G@fZD;ccSe_)rHxaqel)cNK7=iK8|da5lxq`or|6M zbi2+B8W*`~gZOjBzCuKu4ArB{rJ}M@q~9uHT{KEI8ydt==}sP&EF)_0j-MO6qe}36 z_p0(GL)fFF;+HZ_JbXsWrG?J~`<+mEP3rpqF*ZdKm|-e9py9$_g|tr}b=gG||>a0&!v+q^(%2wq)7R z#e5DU5Qr@e@rPKoc-fdLMX1M!0Gdc~mVV#n>6u)ZuS zI4CmmW!G<3h!N2c>X;h7UUS86o1k`P>OVf447L`u)|y@fr_rG8lT6+}HUF0i@EYQO`yJmpl^|c(z7GK!741fXzS{O`ZBu`7XgRK#0o2aEG~{j8eDRA5y%Ia(PeMy55r3iF zM10e*9fy^2qaFo75uUwx5kFvvfU(9Yv7%fJ=fSpCk3e-%sCe31J%oR_@W%oENA*@{ zIlob91ikCr6*zm|d(NwYUnppAG@f0Zg#!zDq10!W{ib(dYHI34=yk%W)9#YrnWAh8 zAwE$jXiiPfO}YDBP|wlGgMUu0x@X)e=nn~alVJL71?{P@Q=7sw@2AK$Kfr#h%al5T zI*#l){NMA3#~SAjk98aGkpI<%nyIVXIiQu^0|S3ew{iNJ4La2?Iqp5%g*kw@ZJ6)~ z9c?h(jWZ472hU%(gM5Ft@y^vx;u#j~n7H?CJ5{FAW3M79HyU+1GOzb#+eZ@fXjXd1 z1|^m8p@f7LpkZ%k+iiIpy_@`?3it)@5kkDG;J3H)wBN389NyX6)p6S^9#X%zD~#Mu zME=~_ghZ>2!(+V{)EaKbtwE7m_*b`gIryTm!|G1}r4Ns-{wMr(XSdtCF7#NH{Nwz2 zt3L)8)xKlh)t}Tf<0G!9dl5K{HX6I_gwT(|xPhxLVhH)YJJ;OWX~2q;g`NjDP+own z{y5ACis;oJf;c>W{!L?3jSW4HP~KB<8Q#LECq9yQ@Eyao@EHl-T3& z@UG9pZH?W%t7;9uS?_r5jb}EHH|RKDfiiv$!U&%Bqejq;jsd`1kU+RW2YZEnus6~{ z?NnZRJ8j-&8tYB;`t{0ftQlZW#@f5mIY5HgaaMmB2XCbi{cRkXEgZ1=3ox2)!#~;D zk#$muhR~92GPz&$DBXOis)AcISD35!f@p*OZ4Qx^M!`aqav@UK0#SPd$ z=KC9Dexl5DB$%t`a@3uAjyjOTQFrM%iZ$OoS{(KNTFFsIcNXEO1gRySI^%YnR!}?5 zz|NS7{j2(D7eulLwOp&9mwOf7d>sB!J+Ux*d{1=^l#sB(4UlM0S_Gbiw?&n4C~&=} zx`dCA!!-CC>4=gq#M=F|U3ztk@u4(K>9NAgd*a00Atxf72oJ@7Rk370_32i|_p+Z! z{H^?tUD_uXO(&0Id^U%L{0DjB{zRBs)LC~b^Dx<7eIbGXx!Obc%!6e9y*hc}!{i_I zYK6R!oScxV6pnL2Z>gSbcx}@4zyZ(>>p=@|+asohOD_CfjiB#wFY0#M6*^P-{lpZ= zhD3Z=Qs8DwV;nA}YpzUA3_M}wjvrFxkbtrTk|<>G6tWLTi9d;_(C>y!Sq6;cBT$Sg z(mC}6-8gW);P*~26iV-q)|%Ag!~{$I6Q4Ch*SSH5CEYF_J6D%pCJm1I+SE1UW%8QA zzcO0^dq}@rJeIRuJgi?XSTmm0iOS-LC}96!<#MqTY%yjx3Rl25m8F$}@=GjX$0OjC z$$k6p9ghR-xNK*fm=x}kNFR7L!FK{9&|lIjfLw(?d;r5wu{0TjqzafXs=k#pOnn!G(b=iI!_gS|UVsnYmX-=>4 zbqCmZoc&|+zWZOZo%}Awz_&be^{wZ6DoQr1;fSH9x!=h5(mm`?Wc?XVh=gX7;C|^w zlK@CRMU?@&<#qniAZirY7cS@}z^NPwu&9>+toioQTIjF0QUd&3AqgPJoaTSJb|LfQ zU&>0)v;l#;3n(M#IE79ihBQhdgX;-m@O=mk<=!n&7EmCX#0?wqcFL)j$Z~2Cu$gBL zcJ)GPHAhG->4g+)c1BA`-D0JXdSxLYMQJQaD6Zyj**?Q)wZfsWZ4Le@?P>gaB^H!> zs)Ca%3EgQA)LV3;Zb%NJ#wVby2mZqbWcy35Ps{-&P$*K)YOrRRRuTp2CypRkep zdp~D3MCb`EiGY3(E2C8Zw`o^B4w(`XKn3=Qe+gqNacNYMO7_yzQRLPdl6NK#rX4*@ z-;)E=P!ChqJYsw412)3+Z6IxpnPcQ?~qpw680ptHnhZ{H#RhA>ceKPt-Ab|XjhSKt!woXYE(wY)XL_bE$VNP) z8E{DP1!Q*u|Ks0=sK1cA#JM(vVz+jESloP+2ZG8&=Z&=pJaofuanP z2Zf;K--mI5Nu8(?s~k|y?Q91Qst`&dHy~mcoDu$SG~FaNWo%3s5le=NDvJmLBNkXV zf+nntFS&IRx8i^XYBJhXv|>nzucDIZyY-Uj2XZ9Q$MlkjHNR%GBvHpoN%S-QNg@s9 zFR(DGam`xwD3nraTci{hM<``LGlalVCbt-2P)VI2@w-D4TI|VLa_bh9Tc~EsMzCX@ z;EZ4QV367ITc6QOtN)xMt$t81tyuHKXi2MgTPdyX%9K`SdTV6Vg;pv^B?=q_)@zHC zRhXVLEf=eZ%|mieQaqk>pJdL^_K_{J&{wD;N%e!9SlJ+w7mdk7@(Za_BEKPFlu34F ze2kLgxHJXktyOU&u)3WZM|r@MAP@syh3IdxO>>Dc6;~gscDe38<(?V7mj$kly@b+6O48i6S`G*{<@{8Q$QKFM zA^ku(q>o#^#b<1NQi2#|KPz}F4AKHbZZWE{{6py!B;*Kf5@G}=FCmEdtzwQ6cZ*i> z096j%!wqt%z0#M#QFUA((T4dV&VfJD%cH-{kw<^5mq)Dmfzi?uK5L~s`p!b~D4X_@ zT-s_u!R3PNAz&g4>_JMH?^Y`pTv+js##v%268Kg79a(vL z%H%TpY}aju#Gi_DTU?0AwwFg~p%)(T8KQq9dzbgmTA4L}*^V5c^}p!#7brEhSo2+@ zCA9u;D}~mtB#bZQxNXVIrX%%s3a71BjJA5C#1$`Q@C+hi^F)^CrYR5y>`!E)6vI9S z?CKGI)!&d?!%H^tGipL&O30mlxx72SlQF4MA8-DJIm|o1i*-=ASG4aLE%yBrE7|uG zMc7v*_y8Q-qIIFE{tQ^VGkNx^#S5&$v0_B}n`fv3e2|IQCkb({~_BBasH${k4RWG#(%I&6)Y-LxL}mz33DF_K8#JUamdh?%N+W4QE0~cjSL-cnjOSSCbmimAoAO! zMQxGDKh2uPbR~X^j+bN%v*j!I&_BMSODkU*3_^sU50U+&tWRdBhaFRf<3UUxZ-|v2 zSzacfA>G6NENGpQZj46~%Uk_Z3d5uNyVihf@`A)9@Cn-JtlE!HiN2r$-i^MzI-pmVid- zTy#ZzTwC}^S`_>FDEUM5f3snwc&1oAoIFLZESUmP*-UjROQsNZ=ZrBDB~$Y7UDnTl zb5jOTK!z%zRl#PI&OA_UuAaANPw2w(Z6=)-WxTZS&sAs@;LPwSNeqGGk#C|gp(H5r zl}bD&8c z&a;-9!&)?t* zH~m0)spnV3XKej)FGkr9)5jo@A(j3+ceg^PAb~I1WcKx?<|uKum^tx)!uX^wmO|)h z1IwV0a9XN^4?EgO9JpSeDweG=)ClT;>AMBg5H5GgKy5-oZ7v zBioS+pz7uZl9ut|a5DVXkh$OZ@T(zvw6*$ih`;W7@W)5+4;TKZ-U@%5->5W#-pi*~ zymmC>cIt1$`kQVPbTIUJqw#FxOrwr{p2Pn=e|W5M?(kT*@ecB@`I~o;e|8)1T>S@3 zWq3O_FZJHJ>^HpwQ&UrTTuC@}+FkNHQ(@5U)I6LKsFyM|JvZgs#lBf&&gHy zj5`JW&v>25TF`0*?WwR+o5IAQDRRvZupjF(r5|t=du}x9bU0q`>;)UbN^f>JAJfI4 z{t8TNZ)e+Wc^kcJ{h$gHQKgUH=B1PS>*(a9pO zw8!pvQMc13+sxE6Dtm-#ElevY8!O`^Ux)M`*Yv4-VXfn@p=e`W55+CI8@iolWxNqZ zYp|F#gPPlH1YvaGb?~F>4J%hj9=)%+x4Ku?qvo!`TG;d>-=hccu}_Oz`82_CMJy^# z`yI<}w;7Q`=;j0Xs>)2zWHLL1V~*M@O)m=CSoac+1O%5e6rE1bSluH}IogfjEW5!U z6Q1^)kp3-?z-z5V>*Vb``BbW3K?Kt6xU4f(1EvhEgA0CZt?3C`=kfZup4TUHc>Q*I z9r8yLB7!yF!+HHm;B{1NEoAgbdl>z1^@*Z6bd{KVYRZ`*(%x~MkkCIC7*3@NImHe` zsq^H*(Idx?%{UP#IpJE+iDEf4u>y$)!~5fjbV8_~ z+N?Y6;HjR-mvcp+^5NOAlMsem8PqfqFpXQ*oLiIKn_f7xH@!UY(j5=fG&wS$1o%(Bi6w+??vpZ$rB{`3`YYq4z^AcY~fWvAI`04nk9s zFxvO*-K*19bUMSJQPX#MayPYBsyYyfid*>u&RcLT zdj|IKjFjqTnou#Rr=4hvO^aAA@|5sr^b5sm&O-4v{X)T-M`59O+Mb2t%e`)C->V9KqAl3m(>d>j-G5 zo;`x+a=qZu5mOR03+rLzwZIb@30jM#E>>q24$mDsb?o@jW3$H>-vk@b2+m6!XAnYg zW~ds2TlDb3+=MO(18mM!8}nrfK4#lZZ}?J^ro<>wYkF>{FfAk+oF_s_W#}7_p^=rJ z41;!OttOtFW6wLM=jiw4aP&L$9L<`Y5#Z?19**wnIa*82!C0ED2N#C5hi2zPt{PZS z826JI<|lmN=*kY2=(#fFgfaQuQ5}=%RixX$L|D0Es3?Ufwm9ugBq+WjgRF3@mUcLNc&94~&z30RB$gj8P zoppWCcrd}1SRS|4OfuZP)E0|LMKLo6G?)V?P$pi2B6%xK*f^zIZ^p*A48@d9y|AO zdzOq3@6yJ1WRvyMUM14eAjp9Erg>&@({9Iws~fh9rVv~Gs*t`@U1kctP^ZKvZUupm z_&0=a3^6_g=WsC*|Iu%Z2tJ{oI1%ihcHm&E#uGHGcoBTONmk>v{^CZ+(~?wK3Igz? zw4G&)uw#-Kf_KoA2?S%2D1F3-;47-xF(PrehhcHTXGn{mJTw(leE2J1a-E07PKaXl z>RUX1WEkNY#8$q`~c zj*xJ?GhQ3Q&4TVq!-4cS#Fhi#a@GRB9feL5@c7L5o;c1$2+P4e-c>O?v6^W@&&9|k zD^olS%C#t~F0J5qy;gv;G30%s1(EjsBqSAxtk-qXf=p6;kk`s!*}l@Rwl-K6bw(O0 z8#O#zf@SXxQGPzG&5MZxX=?#w`#?ruyN)6jhmS%xSSEi8kr>O|^z$Bo{FkaZy!N`J zLd`PS^hxvcSFojD$w8lgRgOw9eMjpbW}~}G zn;<`b6?@55%1e0rJiJ7g(=Tqk-Ed=7j1Ds6KaaYBJ(=;pW{s-So8@WoB;4J>WHOOF zDiIMhbzF9^mPxf6`OWHdr{j`@>W2-^OQ z6kN;NUtmH39M4Pi+LtLP;O#du6!hebOUuyk`}Uxr`^Zr!8PM_Tp0Eh8qew~McZ#e~ z<(5!aQd&_%k$^)=-_l-*=w5lZrJ(1^-5eo7{|+py@zfLkN0( zt=sXV^*B8t&Zbgva6L^x<7z`5AYNer#0`0X;5Xi+EE&9ARNQzGRNRo)L#(`E+0t?4 zeYTX?=^bMWg*;=9N>WJ-;5xx7>PX9lw5On}0+xrftgg||S;LJdZ-Bri*tt&gQYA2x zPJ?A)E(bIl6B72wV$KyksKXZdHHV%pp4>=4B2ol3*g}VylO=NTyEmmK#un{tDY8X+ zM$y-6)~uqp_9Oq|XAYZn2))m5Ky5{P_B^}o;hHO74b{N9OHIKA!qh1;goyRw9HkJ4 zzUaj(Vr3Tek$9&p9L8L$8mlfVrZh^0!ze0p3)7^khe?RYw(OTxrf69x@DW;6WvU?A z(?NNOL+N753voiNoMk;CETNVaCQ;TSLY-OqF{XN@W1)2}5hb2P_SgeTc!m%maOm$GgR>=g_(1BE@1<8MpBSGwXNi(GNj7jNB~f{S`t-$X7STGlt|z`4kx zAd84?33(L~l#x#i)`(J+Lk%YVE|d*v68y_)=3eXkS5uyhe%aE~C~R zBM{1R3OM`9&+zjNGqh)XH)a1(gdE#Eh=kTYp;sp2Q|JvJap_&1yk#40(B%Y6X9~MA zg_{?HE*M;^OHeFRxS^W9UXvTjLY|YtZ3KAQoM_DBmh2mSR6$E{UYX-dimGe)NLlW6 zEc7~3wA>uGjS_muvD>3icA?DiC5MBAP_ELDMl}=xli=e%bv9PZ2C6}*iN2S%Oer%$ zsV)oF^)e&0*qJnO025A~iL>z<#OlDLRtr`VCr_hV0{)3VJf%`jyWHZ8$}%HV5{O8H zzKl78is)lS(n6(y#Xe@;8CGT9;6;6Cj#>X4vhG>t4Wa-*UMuqkEpk?td4p2kAXOLu zqh*9}SR@lER5xO%ced$Gzyd_iPvb^6Ie^!(=@&;4Ab4ekmOp zlpsjp^&oH8gyQ_)9P z{csk~bWfd7O-4r#oTuq*xXg+%_BSq4a#|_6T2p%RGdTTD27(E9n(gAx7Z^YoLT5>zZ2G09tjlUMOTJ9tt zZatplkj77kAOz#`0h^uh31>S8A9V3x=%J2(sKakz{cb1$*lD>j-)(k^$0^k(mfYw2 zLJ7GIZpi`*F*Z_JL2>Osb}B-q1Exuw=-~lHYe``#Qo(Iugn36Us44yM3&F zQt}GVBi5bX3uQAZRF@E%0OAaPs&y}{b$oJQXk~m6%8NY)l0#?{>V3lwa zL)<^u5XtgZV2F>poq|IY4~r8dYi<;4E#H5^4fSv^*GVC$U}5flAJ^=Hb=TT zhvLcA-3H~6P@$}h@2l#LJgIg&&B{2d4;R5x;S|%B?m6LUA4FTlr1^?T-~Q zN{P~nP>cWpQ)^AvZ^J5Xy#Hu7TI)vh0epU?zX#OZHFpV?y$BBtkw_7@W{^aDhJdO1 zdbqG}0wJmG+0wIHGLf8gtD$5M*h2bR?J+CdRx)=dJohr2a4K-(JuIxuD@Hb{F~Untkn=YD9xZvs)1YEv z@=xJcq$1o@W817S5>>CP@pwI98%MwG#baZ%09--`B|9co^-80Glpu~DBT0GNhqug%{^eu?@aIN z6XoKF<||A&s3Di8h7DE(GtRO+Ym{@Tm*nhfi zGQ;8uJe)B~Wmovy5!KJ$TqLt%X-lK}*+5o$d;198r#FfCd`PKyb`0Hjvdc}9bMU1nT`$ByE_h_2&5$W^_n?UnX z7&XM3U!R{^ERYV1sUu$kYOPN*#;fpRr&$$rXzvWiUd|!GoFH|9U!}Y zoBlR*meK*jqV>Z#QVT`n=WtwhpnUZ+FcwIx=-tB)3(*O7N2aQDW+EZ^m7y1%Si@Ky z0>>-#)P9AY+OML`m`J0g_ABZ2>lJFV=G~0ii2zYsss3vgQvGd@JnBJEzBlXE8e~_+ zPM0BvCNjE8duAC1EUZxn7c66oO1u!RiRHw zMc6N3q%L_SIws_Y7{Grgo1NL`AVmPCg1m5%oHi&98O&pdyY&pQKZhYIdWK-l_l^=n z{FQ|a@nv}okxlX;FbTR=Vo>Y2gxgf&UKHsVJRi5|?i5^;&0qUu2BQH46x@|X;Bf+W zG7X^sdzHj-OgP{+nmi~?0uD9%UHc_&Ja1sPmm|AD+(%;W^Z4sNJ%2r%!(aF7`HMB* zF-rXP&ld964VnCvrm=)23sxNs=ZQ?O1k4kQjo>=UO3BrRCFtUlRTb+72KysH+rx#1 zFQ$3Of4v_0GdakAM2~#deD^3J|El}#+mwAMj%8EzEP#EQ#uD;lq0Tb8LC~k*Hr|e5 zB6KlHxJqG-6th{vcF|qyOW4lpA$L3na*ydD$C^h<*uLC8$esMxOkta*u>?6gg>B@n ztr^sNxD>1Rio`tWe+NlM9y+2XgNX0U-8>+ zQ@*p}M|X#g>yTn4>;X_n;Ym1{$MIWhO|J#F!&%2cJNEf7RtzF}DUb#SxnG6>xzdpQ zHXNedu6cB95oBQU@m0qSyiHaVcywCYWE*@I_3V8zhrLhe*_$YyOR%$O zRY~Yi^uTms9g^ky8_)+LEeW(zHk3cX1))WqyrO?DTP5}dd^b^4GrI@(17wC(`Q4}K zX+_Uj%^c2Z=sAluKQKz1^@x3()%zLq2B8?yB}Su=Vn&-Ifn>I+d8A}RVS^22jl8A6 zWVs;<$4tj_BkyPj7o~#Ug0>!^=W-Al=n)!x;M?v6u?0W$`bcRWN9=>@$(JQ^G1cv? zmfjLL^Hzc4;Kqa9PQ3_V z%@d=vNWIlU&VOl9&R1yN2m5o(OUe}A*ivO|^Ind`^CTOY^hyeEl{qDWF_6Gnkl2zk zAh09~n?2NYK*f!`mzO2ZQD%SO0tbALkShheyhZYcm?si*RnR~BD#G|dyVmWX_2lT9 zN6)PZ^Gw%0J=6bK4%2_Fp6OY0Wt5nH)k3DfE>Wm8wcKmz>_o&J?#$t_ke%)tNm^#g z=Y%u+oLB3jQA~IcB>@3zD^($!eS0N)7cPp<+@ZKuW1?6eT{5vuAG{GX49{&<;kfV> zud;Uno)fQgT)b3U?%HB-bZNEXqt9|2A8=~id9~wf`bGi1EODeWj1t0MNDxHm)WjS# z*9FIt@y!Hi1dLob*zvJ0XcrzYFL=;#>vh_Mqh{l;hGgivVr(#8CUbDSspnS1?%G;& zeL7rk*D5+cEdOvx$URFow|VlE-zfhkcd{5~ZGeW{ng31IEB2{zmCso9-v?J+skOw^BsPGL? z9nC;NXPFSxIUrGTkA!1~}t4j((%p@t4=h$005tX&tQ4^jQL3 zbc@I8{tEQ?&wCX}uT8u*B#qKC$99LEu2)HP1QbW0>4wu#7K}X+$0%M2kOx=Q0FaL% zckrMCHju4_eFB@3#brQv!4nF>OZrHQp6v;j20WMw( zjDZU^891@5I!|UOZ=-V@Y6`u z59(51s!7bdAg8G~F=$lxYm}sRKB zx~J?zSi?dr1{Yg&AkbqnA*@xo%l57Z_`T{ll9HG9Hi}*-?F+Ite!KUaTZ@|O4n&(s zCNP66j%S&R={GQyGeLSu*2bbXIZ7tw$?b|xGX2z3{7a>A#x}fAn7xoeWP4Z);=!hH zQ`WMy+K1$phaFFPox~Z}B3SdN(?8w?H3JC6=GSL}E(9w)%%?-_CaSyxq@dQYkF4b; z)_RUny0}jypA3Ns{ftowLl7m=Y|2vm5etJPzvWe9V{DgA>!axsGZw)a2%8O}j}*`M zq5ZcuQ-r+(2S`WPU26I@hvcE4VQ5;;8jGp(MN`A?OqAGpJ-A z;<`x1i;~MjLqn*cP{A36ZVa&PjGD^B!srCklJpks%zF zvQvH&MoVCB3bxW&i@;?+=v*$HwKy4okJ4G2tioP8Yo!>4#O6k_0}p8WhbNTIS`2Kc zasw`zVE#{OiB&D$q#wjG(Eiyj1JXx&mfgN+5!! zQYiAGbmoDTkS5}m&OE<8vd%nOR+iK?#ImwIaG+s?{``ZA|h;NycZT&Vnb(cq(KMboc>?#xw?cAFR;2+WV+IBgPiKZpamz(8L8P8bB%F1>}vG7vfT z1`;h4(@v^aO2~RGxx`nTY|7rDrz~s6BoQs}*!5Q_lx5ABN=kueBV~WuCd%HCpe(1E zlc1nV-)rXQj?B$ZKXqzxdf_l5?{#{{y<1P-Ds8^rL|%5AqmY+1V;HTyR?AQzKbjfkj=x-b! z{=iqLczx0Q?83tIqtl17A-h)(+1KVkmffa8mNidV4cXtb2(l-yhN1wO zs7(^H7;rm5lVA)^e`rTKK(4u+2=VxqbK?SO0OoNBu0&81L&ncPqroV< zNI78NzJ32cd+!1#*KySe%UG5r;b&~waV*={<5x?b>5)c~ZOO8&@n~d^_U=p?oWElt$AmOze2>bDUEa8!CSRNs~HiVGySU%t*AIl>@ zmOyv}ShBF^oO;}(=?7Q>RXybISH2(Yk0zf$xOGlFAP}xAS-kjWj$kdTbMCaJ&*uoqo#iBtPDV&Y$cFk< z>J~1ObdEkt!&4YEH;yW`n8yoYquem81j&%D#&Ur$#$=xzDN*gxh8WB)bYKazt$r>fXgmhP$w0+IL(Q-Gxt5XRmtw(E;-(5$$%Bam6C zpehW5;I`I0U7UTx@$pmB<44A)$EU`|mE8xgPWus~^>Ww`hAhHl3>>$kcX9Z@B$Kw8 z5IJr~f)Vu1T9IX|9H47qj!X|-|XrGKqT_G5Bc;l;$%{z2#;_>J~ z&(?)_OL zkHNOtMNgD2QgzQd*M$b!yLDOeOtVp+UMgjmC(qgC$%zzsGHsV9yjUz!^R0dr^8~q% zW`UfAy4}IL%1cGNRxx&t zH`{^t-W1@yUH?YU+t@aOQTq%3OHS@zQHhFbM4tawYDTE!nT zt%|%@2<*x>-p7K}{H_6ODt3_aigJxFW^?Tv>%%oh*LX1=US!qy`2G9skBz7r-!!sG zo;1D}^2E0f2y5q&TH|by1B>Y9s6;fa+P5|85`+cysTAwrOLlU!T(hhGimp|Ty1gOeGe|0v!yZtM_(+rb7?VuCQlrGbaP@p z#aO$HupFUf^LWrGp|e+g!Dc?9^u44WKF&7rFrgn778~;gNiHE0R<`LJ^~6D`h1=7k zLCc?q%y=i3%5IBUm5NWcA+tN0Z6a?My@2BrFb#2me9eZ?+by9k7`d3&TVX);c}sW# zJdLPR1=>;Ie`M&E?4D=7xV7XgNM%m?z)aKERp7oX0%rAYbHGec~nN*P|@Vl~_-jD34=yw_?57R$8#Ws)`bG6jixbU+Y0L48Hah-Q;FE+G;HNrD=~&Yb<#O67-p{~pJx?#DtTgA z5igGCeQ0(Ud(Lkkl8*YoW4j)O9|yUB;SuOi^x%W0uwiecjC1^b*e4${*BkF+#hUm* z)Ll?^6IH*$6Ls(<<4!qgEAj7SOKLOH=d-Lh)_3*0*!>#}>ohNp`W3Fqp3YCOmzsMz zpJc_cdpi6vb5DmCug;#%hgl!YJsn;gyQjnNn|nIESliS2@w_daKb9lKx^s#5bbiWC z?vJwK*gc(}X7|lK9bRnR)5#s#e=SdBe>Lvue9Vr$Ur52;&)Tuai>-S)xx?P?x0PC?monN)X=D(%D=GW}7;lJA%YBg7| z&9u(ENTK2g8S8_&Rl|#8w`%x(bE}3IYg;w1 zc{pb);ybWIdC6^eiMMM0!cOl0$%WAE=$ zu=m$??D1mjR!#1(_eh?ww{Ev;{=p8ge@X$^m+S!J#n!Ew+yU%Jo&eTkTQ&d74x9g- z0-Jxa!-f}Iw`y_+o7p_U=2CCfY{rkz(^k#RIif~?tERtI!@?YmU@J7)FqhH9Xvok~ zfe&Qy2!1te)ojfZhyUSK8NzQ2qfF9X4X-Ma)PnD?HK3;Je0fH@BiwPH7t7FJkKaVGE&gQ@->+UvJD#Cl@tkH0&Wu}h z{H8;PV3!?tz6AkKEx0xB2avp*3oN;%p@mrNPPfo20TNPX8CHD^jo2l^IMjfMB4zxe zL+wT}3HfxpUdJQ`hZ}Wo%&pbt+|ub8uhnE!OwLy@xrrOuP2>BmDaQB7t9b>(`Yy{4 zMp9jv=J)Lw_BCFwU}$|=d_<-kSd*Li%L{sx#^klUJkAU+9~?9u6b*`A%RE>z4c1jb zyPgtqGP#An-mt(=Uc<}dY_gMEc?H8LE6Z<>3?`%;2=cb(xsW4`sPk3nf{rL^QHena zOg2iV9e)uL$Sneg!GRZzy5x0~q&)Kj#)Vh%Mut|N$nfqsCf9~@ZD}Q61qpET8OmSH z5y?(AAX!I30?5o0kHT4|ly??^zl6x=YXGxOBsIt-14+2vmCCb&b1qG&ytmN#mcQsp zKexibEFS7XtLXtHo>Bc!%sIp+W!aj#J~0QNs7~lkiO=ltek1BSnILK?wrHbR234V; zc1`UE*MaC$oMxrbu9clR)QK8_S9bh5sX^2Shv(%CB!_8wko?L)2b7s{^*9MO3^wg% z5T9a4&c|vuQw{!nVlJl1+xV<%>NY!hd7NXxkk^VgfY&0V}aR=2r>S1@%OQEt_3-j}yY^*`iDLSH)F<~?~M!}sKg z41L|k3x=5uZdjAYKhaRJFpxgJAyHp zPqiVRi>fOWaWbb|nmB*U+Id!A0&md;#Raq5%l;}}m)W(G`$#*H*{r5rA5t~VkY*IB zf+;4KK|UdxYRL&+9;=oN@d~C|BFfRjPtPuDswFE)I6nC>f34y2I61}3V-*tdpyBi= z8%*$eTwzM;$wzpfOfBV6ULLEZ?B^9sEk%@D{o+2Jx9RayIg+)VSJD@37nhbg*3(?r zx)n=}wBvP=WP0+k7&MKhq3Q7$G>LMnr(5pO^oMyu({dIxWynMY6W76Y)S(g1ci24JGx>gkp{fc<5j z0QQ4f0LJtl#VjNtG;1KmFpKp^aFp`oM)!#vk?rIMGiR~RLf?xC9n7ZrgljXDFIoC@sprq` zdw978aQ8jFbbKsznEd9f{x5mr=daJ=GzY=ZF{^yxKQ{qsn}eXe)PmDX&XqitQvPL0 z3>4L1U~%i*+LFA1f?GtZMKXK6_$3#T?d+skIsdt;IBGosNN)D7Ttg za|f+md4g6w3$!vU73Iu<3T-p)Oh5CmwXTh*jTq=O)1b2y107LrHKOJYI*;cGIzNyF zI$f>dvs$`J4fNJUN8K)s_FdRe+c8KxpN6zEF-Q~TRvT*WkoIhzkoGNEkd~eA(G+{Y9{p_;6rb?Yx8r-6>KGAnyIk@^t&E@QlBN zIW?bbG-{q(e_YI2VE5rit3{4Qn|EtXb+)sbWsRvX!8Xh2!B3pWqv5(fVJ(X!U3JrJ zU(`FvJ}?FX5*%Sd)<7&|q2}ch^!I6-+a&D7Ttvt9e`Pv~#32b=H}g_Pb-C_5L(yeNPOuM7h;W zn>%QIZJwajV`kd#i-FD$r9tQWW1u6-t!CQXLFYYrg3kIj)BeX8q*6P_vZ;|IX2UNHI7rB%(S%}5w35hl`AS-FyX(9+nM146CcX56z-d8lPn83iufG=j+P+}n@H@)tR7C`Y{f5Cz!XxeNvkKBbXfwo@Z>Er z2g`?7E1M-V4|{d`Nv|mIY;LNt*kWs*^T6*9LeGdA;FKq2P2b=gZsGS0&OzXpJ&6qz zg5>;Ma?Uh-aO$l0_ZD$X;9r(mR#;mjd8M#{&{PTz_=VQR`T|Ir!K4-H1HEH#uDEAB zsqc_L<#G+e7|P*b$zCD6VBLR#$5y_eS8aUtl;CkRnyb)R@j!I zw}@uhLLdd3AB(}}C(^L_v%JDB1~x_cg94k^I0qmLZqT5d@ymGx=r867peO(IiVYh; zv_I|DmLNy)qSvZ4%1x)$aNJS}@|ZgUzaBlD4^68!NUBJeC%`3^&qN17{`M9Kc zN~C!!IIsMLsI9tZ++xiIFKym@@d&+;Ue12y{uKgr;NAeZ#Vx|a9{dV9{-|1EfF4nE z8DjfkJLokxN?+(h@?qPY8F;OBPe78bB+<^_X|G08u%DB#ay!Rr}MXk!6w)#rt7ywNC+b|Q zvmHPk1=D5YJ{W4&111`iSkKFqx%NY-w&f%o=;ZeSJMqOK$#dO14ioZ$Xe2Qva88;B z=|N1lpvPNqS&(QWuK*G9Pb7oorMjB2|OifIVK6`3*bmk~%Py+)4 zQn|vZBn*+pzwqCHUmiH%4D24+Gcc%CrJJP&1X`bKO;8<3yVe9>|Dki|U$WI@M|wkJ zFAw0a9ySX}6BhjzL}?FPu(W>KuT#SQNS^u-o2o6mY366>%j)l`DGS^IY=O;Q`0PN-OfP{nGD6QBpyGjSBqLtHLYFV4Q<`1q;m@gw8Y<5Od5 z&|LgthQ77?kC606AvToE`-avRUh+b znvMGOQfcKKeP-s^)b#N&YM=nJ54d@zvFJHe0KAP^E0x)K)jxn3W_)p}bv}eX4Bm_v zobdu14{&P_>ekIdv?g0n3_8%-(Bt~7p{<*{gf~QkPr_Q-hbS+?0~{+Z7!^ryJZRu* z3+5eLmlIEoicB>t?Zkx3>lN&*Cw9L|+zoe^px4G526U^#+7 zA#IidpZ-u@g}u+Rp>Z+s5Uq;e6hRuSGmklXcf=TqpXyz(Exp)3QnQDT)C_UW=5k8( zLd=jHDnKj?!0}%KyLWQf0OE$^i(hX!KJe+TN8!goE?{^QQ1sw~rm!)s=wzEL_|f;F z1<_ZN(QrFmMy3A*oZwR98gr;i^tFd@VVyA7ipjsPJz#L6oF;^{6hGHkhwEz(^=5k! z;tfIl#b5f`0|BqEJqZ4TpSr>rmxTr&h*fbk_EvOUd??FUv!%V%JK3@okL)z3K5URp z!(bIJkQs`qh2{&av4S2XI=^V&UBcryH65J260i7A^JwW#djQLZK@2U>5L>rt40|rC zmY|x{6qAEZP2g39`8Cc!*tK2FXt#f8wg%7s5)m0k{L>u-XV?mJ&MPFZK5cTSzeE&c z+86R(z;c1wmx$o}lFvi4Qx}5CM`zB2hiEICmF78D{j*H@$*8Ki%p9aw(OIS!3A+9v zb94wD+M$@*{3w>*u=T$Bpn?#lmFQmTj5@^)S3e=EABP6F{3ZleP8(=*iJu zUzv!&T#*BxPpo<+wtNxV^vd0uvgqL#V}9<>VuD8aGwXsXl5dz;f*hPiV|M{L`ghkV9p&7%u?D(CYq zLY%d?s`1wBdjK{4*jzQ76-Rr$#pYU|+mGSAWD(SvVmP;6SpK#vHbA5;{DY(d?}YHl zHXfJK*$cm1ST0l=o$E*lkg&jz#pTM2l{1wx-aChvmyT|zynJ*+yYd$LYjXJt`mhi;enlGbjyXJl!ze^BY)?R~aVh)g0FQTf~aXl``v%*Ew^!da%$r zyegm4uAF{hxd4OR*;t3H7R#NR{YDW+0nDU0P(?Nh%bkrFFt~hlW9O;>QV)2`_{3G< zoEWs|*Nrv5?kykP(AiR|xB&#hcme#??p*1?Q&6}1cR;)YgKkjI^JJ&Tmo*H3V zv{(Hi+%)TMXjlKiQjAS(Dtj%6@L#TM%MUF62KovS!|MPRe&>!Qw{#jJRUI259$iM@ z0B?T|z8HDC`aj{92zyt*0HsH-s{S?nz)Bj4w=*~xB%b{DSFrLcAj`o&COmho0S(^V z?eN&Yz+;tdoz0~RL=?d;((((0an{GJglQ5%%dCH!?ftux^#f7_X%OVam@3xVJ%0aI z`(t;@?!n-F#r{0WwCk`pmlM3ze*?O;D}Lurp`nJ-SBX$!*b6~-jT(l|NqU`~q>i1W z53u4J;z-ILgAD%~5_s`oeo1h*>JEg7sk>|ouM0#^Q* z307W=r%^ERm^=AX$O~9cZdJ0ySn;acLBR}{A@xPiCVwzMyosfH;Mm<{7qolq*nOB4 zV|G33+__g^TNkvv_-2mXj+*x4gDiJSsw`6c?#5*lhWbKg%~Goo z9QNj2h~+0OYleaelku|Oj+e($@N&S87hWu6U5@cGmK(hMV)B+)EL~N{Ss6s$qyttTu8TMRmF!?8$Fc~4M0!fTbE)m&L3c*CZZ`ZCc)=F7k zyqM&zLpMo`g>6~RtJGi|*6a$irXZcDpK)t`IWuUF*g-p)0@|nSpykCqAV^Q;1+*t; zL3zxC+bC%j;KH5;8c3a&yr99QDaO*tZ8L^7@{Mg4M7GRe0YYT!ExAdfC+Z2&t{l1= zLVK4)w6Qf${is7a&Xx~JqZy;DabJdZYrVn!3{&HH9SZkZcWI15Jep%@>LD({g9*v( z6r-9RvvbJvDI9X#&LOUk6O5_kbO%>J%aqK|8A(87DWP5l+0XWZ!V-@;j zA0*Jg(@I+q)W4)12h77@8!gJC5k98uZptc@9a?E@&`GGDP3H+4$fo$*%X#BieN3_i z6BSZ4q;bg`>|Amxg-hOO=Mr8#l7B9FIX7H_8)@8>RH^r=Y! zII&)gLVrKvdR#e;1#-eIxeKsK(VT4%)Il7iA=|YhyODzIIXkj>@!tF+`(3#~_ETSx zgzR+sW^}+;Wbm3!4m9ns{Rw!_%2Er4)05I`yE~0k0YOD%uWnWvZAdvk=Q*%D?bk4& zgyYwpxkjtPXEk=J2hIgde{0G{SPSf>?00=8EPJHz(XGPJZe_w_wTW}Zg9;l*pOdd- zGo^Zng7SLh1<`up%aR0yNFqrBfoAvSv{&*#d8K|rj)!Cg@kP6UcqK(Z1a<+zi+eyo zyf-%j;*~3t1VlQ0FSUT!ZC4+0*@FKFc1+C-IRuk@sJw2foQ$VDG^t_p7b{kj=J3td z=JZ{SgnYn~mDLa6j)e3gmZ({k7Rfb7fS+ZfAlD%BiVG@-3pXCb$AlVuLDHepNWC>MAQzUWA18c?T2em&Uq-zrrW!4<> zX>V0LuFFbg($=++XxE}rB(-a-6{Bm%$*8lUt}#-GuBn7E<#AHilvem$VamZBxDcg~ zrHREwuk6FhmM=x|w5RNA(f8Yh;YU-1;Ro!(kQYCk|F!6U&W$iUxiLxA%tYc#EfG@` zM^vBSBwde%Uc)J6)^(u4(^UJC%0ecrj=+wuS0G_T3UbkJ(wJi&_>e`TXD_j*ry>Sf ziwcCHRWzxAe1lWlgm{w!Adey=u|d6HU68gaI4Vprr=dmM?s#MK5H83{lY6I({?Tr%r^m4#92rk(3 zB58@glcZ!F)Adt@(b=P#IFQZaGz4dH!KpLI$y{6s_5)L;zfgx0(#uYv4tLL$Ja$L{ z@nt~e$`_rT)&Q@N2NZsM+9 z8gAj^EEBgfK*0N=iHFj%@=hw`*y`R1)O;8L;GaA^|ae0ZcW`6 z_+2~m{8+CN|7Dr-be6?A`zeUJ z^yb?&zHS&2>py8eUxX!w$#SUJv!-I_@ENwZ$R<(p=zplNtP0sH{h70 z$bM{_of|#rudka;Mt1E|j4$atiM=r`q^~vS+^Gg;c1Z*N-`Iiwr4-$fAxX9#GL6Gs}*&VTX_bC*f7n1v%`)T-r`h3AYa zZ{mr+hZ}XUu_n$0lh<(OGt!PszAl9+C$HrdZqTz-$nx#^XUltX!Tha-(PZ2jQD7I0j%wc;}~$EK!_kFh$tcY|;2 zK(JI|XP^uWI`wv~rX9eu>PN<7uL=Vz_Q^8rKzLxK5Ps$p3WwL~gk5Q^0k@J9jdG?{sE?n%vMm=YiLv z51uoSp%R>NfTCmqu7C$f&1*UfnpwOk8h8>?+buPM7VJOM2Ji9ntqNqzN0c0(>nSoP zpTb5@VWX#@y;Ho=Q$FawLEWvLBIEO^My-5mi5lR|57W>1^<)eXlLQ1~AQBC$;H5?F zkaZ#@#7L<~15%hre=X)EAUKH;f_=JVRq`29p4Rpkh))d;y-RYa^r##|XP_o^GmT3h zis90uXDL3p=tR_A?#&q0mGVcVL{S@8JepQo=am_iY2 zT_XwQl-w33Sf*~17$U)4&gK^ig0kVbC3I|}J<}m`t5S})U1ix83dAyOXf|3fc4=a8 zGDZx#X<~3XMhuGbd-5*^-;^6M_ztigm}0P1afQZ$Zo`tIS;U~Gk%W(SB^2cg3t3b7 zv-w`p!x_sYPV5rWz^Y?z4U$4HFbT#zoH>)$8!#4b8+;7?X0z>uE_8~*aIvo^74fVO z5zyug1R~+ASF6dshU~2@<}pSxypfbnB<%ADccNr+T()l=w-MdZT~=O|)@=F}?GKRr zB42nCXQ?KKQj9z~nwZNWK83X3p4B@di4*+DRyd}pT}P)N(Q>8*k%r4ueT@LD#Ki%>hP)&4_aGzSKZOOu z*aYWp*dsyfhq8%abEM*5$cJsN4I_i&j^h+~=vC!3Ao$h7!P2K%-c$OabT+a!3cH*b z9zz-tjT#Z2I#_mK87mjPYS*>U6GOpth6EkK`cv|T__qRrP0z!`HCn=aYee7ltTvP9V?_0P(nR%JV??zm zpUA)JcQ!Yo`k+NrN5$d+M?wIi6oBL;K~TJcZzZ7*g$oLFI7tIAzzw@Q8RnXXh$q=z zb5UZ`e88ADtLT+I=?;t;J*p+!wMHQ!JR3QH(??OUZ>wDMhx>IPI3@ z?9WhI%Ny{393HV=mX5F}^c@ztIKt@^E-{(tVbVM7P$t2%Ta!7mFKUa_;W!Oa|H={B z0Dp+kq2;!N2hfo=@};Oqu$h2jbm)1+WzCy!nO{nBv&#=PJ^&{@E-tg#EkJu%(a5%7n2e-N0NOqu{nOxnZ>*Kkls#5-NlFY z^A{r&726O&lhTCS@)xI#EM)YwTownCP8|&WYkOpQ$PEnTRQ^Dy9(;5bAt}LgcfJtaD4pK^!SnS>G7$taYaZffW|dy zKl>uC_99+g{p^dl#|!)3961-#cySC3J~WT>5EUa64hmWp%X`W@t+GyCGn+1vwkMt`_wdy;71Sq?gN`kbW+( zI;0|G!Mo_nbUAZ@rJ;`NDg>kub&#NfBGK0feL7quiWC9EYG|TGRs(sVYgjqR9JR7x z*0V@u)Eq7cqtmhrE{rUma*__q*sp4LI&u3shVqePC=H-|_UUJ)skg%Q0Kz_gIiQM8 zk*y<@hc9~06|CsV-C4VFQq&{KE@q?D5IW6yp2=f%)x;+?*33%ADVpCTeZ;hv!g5FX za(;`xvAZ-FY5brPx1T{lfXQ*fz-l=kEA+6=4C3MLkhro83Tb5RN8n4c39^Wv{Rn)_ zV@$)7YCz?)r8OEqNuc%Pwb`nO3G7vf*Jk`~M$M4@LPi1hLwvh1bf*b(DL#~n%v`j3 zA-;B^hVIfjd40s?kMn4 zKNmtiqAazQ*Tk;3`w^kN6cK9YpMEGI#@zHn31Q#uIdniX{HUd92<3quG9xQkWrxuk zDwSM|GAHML2pV~!(%vW$LSNGI4LqThD}${((GNjuVg3O;ZloWAW`&5(k1Gj5v(F;^ zs3Z`bz_H_@`HehgJxHV#+C~KThP9aQY(FZAjSPdV5~Zrcl|c`y;@)r=d(~r6Nn0T< zX|eh_I4Y~{!7rV?@XLkeLZ#8UE(8Yp^etCjtemNo@!mPSymWL!<>jLr+LgDkzqZg{ z?aEuL@4}G9ug223&V_l1`#Uf^Jba?*1Xlmf>rfROQ-`7c87~+rH5L~e_2Fhv8pfcCVY=rxupX~6Ov*rWSnqBTD=t^ctT$fg z%;oCALg(lk~lqxPH5Q8i_@K?KYC7f}L6{_C})H}MN`d#?rj&{3q z9Yn|jVc_V`Y0+Nwy>Qd4yP;iupQRX^*i`mfE+S}KeqizK(AUbkfQ8?=V+m5exeJhT z#Da)NDH$2C_BX&6BWqXxJ^T{k?&>m>9=)piZSVstX(av%1_zVGSF_?PAd32X5+1wO zKn8E^c6jXj;IYcK&Sp{@!P{>61;QZp@D@mg6!P?wZEy$032d45bd$ZOSF$caf?*Ae zn^-X>{kHaWBfCFrJjRR1cuzYwI|r0Wm3Il{=LhjSawGD70()~gv0eQk=s~;UcYY4$ z#u>j}@|0l<<9P6B8v$LA|1A(R9-Kqx!r7s&v*udq_sH!aPrn;+W#NN#BRKVl+wQCZ+)-0Re5$`J`psT|=i z#k-`Iv~7m;6wC+(0iT#V9s@$tl%-dsvh;~dEIF**mC6$JW{o9BQV+UPmJ%9@C`;FB zQ{%;JNxuVR11;%y@y9JOTGD6XCQhYlU6?|D&r*y{TplecVGu!i{tWbWt!I*?9;_Z7 z{LU?6s?nez)uWHYr;>W~Dfp!m!`9VL!%fhVehYpeX*CjG&){H^`0H3PYDvGJ@Yp5; z8N4;rlKu!DGqfaJ2vn)3{4lC{w_Q_1&QVdK&Tg`I_D0q#P?R)0ZfC`)C|NuE8g_q+ z@fa_@i+5H#b@@-R_m@yNO2SjQ`bF%`<&<#M{~vk)y3xtcXxY|f+JM7el$46fg6n7p z(hCM)-=SRTFxz06a0@_##H71)aHbW2TkoKAYz~*zk7F(#Fh{ay;8f5G)_GigNGe5< z@q?6SG_mogBlio~9XOH@-vCM1y|WI!0Fsd73%Iuec1ym2Syp)B0`@R}MG;gs_BG0L zT6O*wrTz@QS9_6aY-C->cBgS$zX=-V_&LZo+JprylnNX_B%_hMu6F`tUXC)*E{X(x-*4c@3ONiFCNZ6yZ&2l*mbXVYKmmn zETp{@?2GKic>Zxn-4{VR@GjKA6Bh>N8x1%cVPMV;1{BOo=8Uj+ckFL@x9u$&Pp_E> z;3CG-r$O;mv64aQwOyX9OdjktQp0HS06d$mttQKcLAz`?kRls~?6QFuKbn8paP6ac znacm~?qow8nR`J_TpIBJhJ#7B-ljCKV59cWpz;E1Yd&u~O|mILw$}5gb_6X4vWU0N zOBp6+ZSkm_@tO2fSX(~Qg93?5H&w3h(Bzs`mhg`^^_1ICF3EA?PlAq=wvISx7e22~ z5k8OEg%2-w@-KXD&yDaouyMnNLuhm&EK9}2O3Pl5Fp?!NnW&kl`z@bjO}(K?8JVRN zwSL(xCMo#7FkfE4DnbY+$#h)SD8eTDlcby}Ps*2rU=%Navw*_R$_m*8>%_3VR?M3KXuE9R&~nv`WdiI&KOlpQ0e$E(Zh%wwQ_ia8CCHA(hP zE>zQIfs^)Gpp-HTyum&T@Zuh@Chf@0Ebu3GYm$wW{Ye0=QTR_v&1BvQ*?<<$*_lpx zP-QxNbqb$rPrfAi++^25O=}x#)VQlgb2RY4kcjFsq*-q)!D$9*EbQ4?_)H25&)Zp; z7w^fxBKL4^SokJLu%4vIrI5GJ*2q`*PqH;GBO7IZ{yb*5V4MALDq<5)Q&fEHT{y0` z)Pk59aMxw}oNPe1s-d%hSC*^^HZ`Gb#hI6x7XkK3yj?zB1UH8QSmA)V!&)no3feb| zE(w)H&Hxcr!}f7Pxh(4+CyQlkAWy<>i;t7l#K&nS#*~kf596r7gS>rHm1a8}3HGIoihKWAJnuST3ZA z+n3mjHXARxz>2rPi>&)^FSGl%8jtbf`-QmO3XV#1jRqVtSx5JzF_dw+*@BzOi^T0b zf9M$am?VE4huz_J<#^#>~JWIo8 zQMdd47rYFgetvu}1 zca5C>KCbEvrK|_#?AqVQjk=sE7v%gz{Ljxc(~?I4F^ciFd&(h1$)V31zyffrHCm3dp8 zJNg?8!tqTp7EV3$8NwRQMkrXi{!y)mPdoeE3g z7S{P&{H6L?xWZo$#RQoqu7wp~nCk&zC#&P&P%F$@M^-0=1muFiw^?&nuuK+hhzok+ zBGW`HR~F3Ap;$!vY_~1{RlctB?#lZrAFO=1@(YzuRsNvz`O257SAy-cdIMhWz{`Vp z*^if}@Nxn#CA_?dm$%{NoAB~Jy! zc0*mAqUZj5Ww->{=7qQzHAME)JU>%2G&v)Vv@My56#Q1u+3HYk2bphw^ zvJ_(z(O}OXUop&83S3g!VS1IXgAWA0`B3+Ka~Zq6&^dqk-i~<@_876K8DJ~|Rhh`V z#m-V`apx~V+&KiILXw{EK)UgbgC2z*eL^34QnoT7Z3hO6@MmH1)wgk>^mh2!i9=HL z9dH*yoxhe>)&$)3Oc)d90=Wb?ifzW2{ExdSFT<7@x2kehwtR(9k=rA9^UiLQg{3w1x&oNK+TcO)1 zKnKNs@4R1kYtBS{(pzi<=T-Fr&XZthVUm+#`w2$3`OT-@dKnU7EriTMIbf1;watzz zUi^FXh^^fs<$bX?6=0?#c!+bFrUgHJMls>i`6CF88 z>)CpO*=KqiW*4}BV;n-1{lQknZx+QI%0-)Po-JU;578dXsbMy?BQ9pVI4_+^6u(HC zh#wSB88{Wi3dB>Od#4?`11ZqG%l;TIe%(5Q?jQFybpHY@v_~2N`2+&YNKgu-qyxw% zH`P>ttB-axr&6myRAK`=SxVU^pvfh-VxRzX793R4C<>0)WEbZ5+M)eG3P;?>?%!Yt zb6$KC=Ljvo(>hoT{Z(&s#%bNhl#6(Z1P`&}wA|+D2}rLQ%4c>|rpg6E+*AC9cz}$n z(P#`3;a>@^&>Wm8fZSmRkDW>$1=Uk9-Km)?fuDJv`$ z_=VWv;8dZ=bF)fBVr7QNI4I(&6iiO0K(T0t;_ehE4%?x~i`O?4w;b$gb5SZ3L+oUL zB34d>;r}q(>1{B7uNBN}A(Vn5<;3nC;`q!7j4%w^OO*4q zQIgQr)iA)NZmWbkx?wyaQR< zSWWMo-)uD@=OIlo4jKpDC8tq`z~P2d!98C14bpmq-C%64y%^gD)8Z_cyPXo`@2a>< zOJ3bM3%MLv?WW^Gj({_sE;!^lP%{x7&kzB47@U#EuVeeHHPc?Z9fpy<^*Qu)6z3LYS_{6> zF3`U!MWDZl-7gq6GG2U8=qjjs6z;dp z_7XVZTG7h&bd7fq@x@A`aa!N-Yt)~{GX6x2sWz?`{9H`QB#OFY648yur1zM5fijW#Ln7NSU%3SHU4^0i+H><)iEe;3NXG zmJcUNfNK(FLs6WRG={ZU(8JD&d5QCFR>sEu@d*#(Jl4U?@j_1n@#HKRqJ+B;OL&qb zYSlzIAr`owhN3;bn$b->1bKBZnuARVazxo&mporpm~1z_kI%82Z13Sz@XW|hw=wo$Ik^^C0N9!Vj6nyvV540S&hHm}m;gn%n zqs&?YGaf8a9XO4+GD*jZ|wFYMd3V=L!Ko%MA`cU1zYrd{BE zBD>D|w%`r9CB-HQ{Q!CGc2(niJR{OSa*ur7dF z@u~qjNMeUkVwJz!jB?bt+GIVQ8hotlNB@YB8KzjGbanfap6XK(|0?Ar z7wHI^@Ff@R=9kd6@&T9JUn`+ox$(SVeN-{r_n&CviJE=?3D~1gT4yPnF@66DW#PDr z{u2oTB$R7Cw*$^a4mb{hFE%&^L7qsdyx4ZZRb4Lp?Ecn_;(x~dZ4yVezcdE4^{riK z(h&b*tzB$s%#s;?J1j3TKOsx6wUH9X8HotLJ)arcvL!bB=A0#H!$gTMtZ5RqWtQ!V z5%i|%Xb^TMz5O2qrf&{F<2D#EP$gt=CT_Ja(f2W!@S(seA?=GqMBX%UL&R=ZkGqb9)qQEK@f( z*}Hia>koM9XspAFcgA&--``??>{{MUZDaZOvEMyBfBYHtzqgpL`n%9)%oKP(8KD$# z=r&8SN6M14xxGxKKj>j93AYn@s9(5N4zuL(P0efsWokW%GThJpF@ysM3rIJ9y-<~S za7vmU7)U+vpKydZAAcz4dZ38Bsk&!eOw>$$_c}kAR~=CA??NZIH2R%u&{)lzKN{dx_ySHYLT)YFHpuJakNy*PdEOI$WquYkdhipT1e zPz;%%H*%0^7~E1NyqXw%krkt^cTK`$kO^9YE^lo+Jaz*-X6Sn4^h|w}PS4b}&GxQs zWqknsPJ0J0#_WF9uJQXf*&o9+KdfsosoaWv?9ut)9_)MXFjnw`@WG35nX)eMv8zY%as1FT%|?BqUaGar zUP1Z2$jgdT$EHqAop|=y9h#2}z{ztL#m>x8zs@~EqsR(^Mv#oG>+Q(8ffe5shb(^o zHUn9_7#-!;1+pINQDi-{LS#KNb8Kq*_}Dn&#({()jljgzjmC-uGd0u9w#7cPy~d8O zn^`f&7+EFPP3%5oYSt!1UW^msy1>^nJ&Lbc@HOD4k3)(Ei0=&Am_ON>hyM{n;Hw7- zK76<4zXbk*jXD!wrdu5C9ZP>WCH8{mMJ44jt0)X+QSq08!CUMYygdbjx7snti`NYX zpYKr&{xM)sIVhd5M;>Ju6)_()7M;zhfKWKfglH%lX=w5ib!$$>?5 zm!1JHPx={#Igf_W}$LV<3LG&y>8ZlY_)?LTIOO!X0)_7)arSJMEmni`NY& zlzWsDKAVXXw7I=|9?%{q)nJ9_$H1W&@H35r`8pWrzcUW~G&iKc{$4xmccsAoK0EAr z@w$Qii@gc^lamm21jv8Vo-4{!OZ4PW-Q%Bd#SGcNw_frf35I1h zX=*Dvm9fw>NSi$Lsv#qV6(chV-n!s;HAt>y?U2UWRalZvFO_Hskk}EOk0Y;F+X;Kg zyYVvdI6i~_T9u%2*1@wmCLn9(f?FF67TSwo6d6CqQyCTp*zhm5n=OcYbUYvOeFe6W zCs|Ws-ipE1!**QlPr(&`46N7c7Q#W0xo@%=(@ZDWGVuvPZD5c{qkh1-*%5Hl!( z0*%8ZD$huLv5T|q09y7ONHe3&6v_649e7Ws0FOUrN;Y2nS^+#YA8;>Awh#6w)V|q} zY;hooQKt!VS?na3e{QVix!hLG65#?UTWA8JR;+7D5|5}Qr-{eIc9b4TK`DRiwT5`) z#g0Jf^&lQq)w|aP<4^V|S`Tuxsx)X*U?@@TY%M^wYc(9!|b+N6PgcYk8TP?$3mqJ9+B3u!Jln$HnflD!nud|B5 zo5ZxQWV%jX=T6#z`g{sdr`dh*me$rIym&y!!_6QMX{P9PLHJv}iPV!PA*I#u&YjFQ zv(tHs=U4|zTFdq8p2$0uoNb-;Fo zT}e|x-e70BQz0=sW|hVf!)TG|RMH^&YO?{%j8@Jw(=sI1*zv%P zcH~1iM;F9tHc)tPJdkos95S)$TM%Ge}w>jWx$??It|-0B&Hh-{F@B-u%D=w#qz<=3{cVE2a{{n^hv9M_>EEg$Fd)%Y=Nr7LsvW zZ=&j?_m%PpEj=9z52oO7viq0wBsGdZ@oM-MUsS8AROWuAtUWN`t-2qfYc#)ycif{UhTLT$};LH8JlvvKh3cYV(eF$v&LZLsul?0a_5`X$k`V=Lnp zzO3Q1Gnl{lX1t1LVVWSv4?YWOPxF{w@vNBYslM6}k<7{%9N}SajJ-2yo{+xL2my7y zmDDe0R)B7qtdPt?Kx!Ix(mL@yU-k7L!Cw>+^skLX9MO2C(gb%P5Lk^;C#)-LS+96jLk{FH|wTn#``YH4x`A zlp*&*bsStyMHKCOa+g&hTY9x`%uiYc%&c~Dt&%6Oq-vB+1I~YA`82Mfvs@P4y1U>7 zCP81x2SKw@@okrxib%T2RV5v#*ENrC*dXb(iM_$3)cMOsscggvE^XmFI=Kg#u47P zq7mk!yHg$AjV!&8j^xvqaD1~3ZuwFSE{^O-_an=Pb+0z8i?AnY!gl`B3~RO#J+;Oo zii0`S)nKx5#2hh@<7-RcH5=^!4)HQaap}^G;;_srF}~^Ke%qt2|2?ve*!Bo*bVY9h#UrVvg{oQY7RE4~IR;GSq9XNZ2~owIX3@ zga#j?m8L;dwDI`mr}3BS<8YO0t1Fv9$s~xo=8(@OGv2s%7)NnY$O>vrQfzE_&6YW0 zmq66A*Ji=%T8diMi*7_MJ)ww!%cTKeKZbd7!wm9uEj0k8E8S=S;bY2hULJK-c4Zi~ zu>vq8`&|gafT((|zV}Q%lqC%(3YxmC5ThR2`36&mL z=CcrNE$!Z2ceoq7cejlzWV~F5!*tnYN*J4sifef|%*uM*&EYUBdIQmu8w*|+dJU?F ziq5iyLFG(5Crmzag-j0q3$P3m+gLxj<}0q@m3*~4^+Z2L7C%&;6+@rlb$)E^)@p0i z^@YxbtP8-k>4AjQWjQ(Kw*Y={a?EG&M_l&s--Mg6R;sFX!GQ2tOR+tBcn{Ulo#SMF4o04illgV{rIRFVtDl4#kZJrk;0NMV!|Qg2Z3C~94`0E` zG1d6*Bs{mtz&URY1IK?K9kbaBeK=~AbiV|tEjV|RN8xfJ zp6;^ai5KsP!xO&`3CcAX^5Vie#MA%keLUSK@MQ5Zm2feEbT!21o>;|*I| zJB)U+V$2q8m74?XKAdxFOtXT|80L`JpF0NFD8syQK=nKnWjf zM2r$TrAjLB@WqUQc9~XCj^dS&Q z{JL8!%)5}z(Q!&kyAIjel@~vQtuzOGr z2*ts;wHl3xmgQ7?g1uOO>{+)dqFQz+0#tua^d#TUvlsyl{pk%7iQpArSHIH*l z6F`sJ1<+F|0_dP!0P*6-*0BJ(r}qWW_RIn(1zPJ*3XQCq6f*b_g3rWe3voG@x<)N7 z%?2G+V4>K>f0_t{6w`Wx1>*&=l5UqScg7-;&XhZQV&u;AkV0C_8WW34H4hU`(_tlP zLTAD*bl#jIbe^#b9bSB39Sfb^y)SegyE3cRlZ2~PkT@l-6M-(WruvWh9+e>z^q@Vf zCV^7)ASP6}Sm{<>8X1I)NKCgBqs`ry(r!BaJf;q$!mLRjJIC zy~AG3KZC2#*~VBS2;hvK=TM1G6USd=7svG!aeT@yj(IWcDUq#xJ=%<(=zVegRmnPF z8lW=D;RHFich%%vo_L$-c4d!bZ<+oIm$j|nyz{aYNhp7{iei;pIJL2MUh+9(nkhK5t3XnL?-QcBB#c+8YPfpRO!feJdpu}BcvKSx!&7u0CBu8j z-iI4r6W9HCq)-7i4~8D^cIBylPB>DniraErK3ZoX1unDVl_42tnfm3i)XQ*(+wRKp z3qonfpEnGG%2KF66m&m}&-z~|X^VdUxa^vNLnj91630hF1wU{g$3cw!YZ=$3)S2rjVu zQ#&uoN311gGIQ>^7w4WQ-KJlnsfKglk#@a=N2|gatF7~C)6T!MPdne6GVOHi(+)3w za2-!OZ|ePN=WFdcavG3UI^!TnX{H(42fDG8D%s`_l;8L-u68TjDp5sNnqXzp(b#EW zZ<6m)1#C>)pi%}t$oDewpHOryGhrz9*oW}l8RG6i)J&CTV%`N`x)k#wWf064!Hh@K zgz_}BANf+fKYC?Zn|dogj&y7>(MMWL(F*YWR$)ftINQK2?}_pxd@;-@lMqJE(#)p` zvk%yX*^j3Pv+uJDGhV!&g_+;`!t6Kf!YmC)7ca~nQWpAS+-4}0T*ZSTVo&5dBngkK zY+u^68penbVq#NLC5l6OqFY0%*hMybh06oO9GXeK$V!T22Th5{+|}2Jht}|ebm_`P z>xvDc_gf4Mn89}j7HXj4y_9KZ>L=`y@slZ%@xyk>$cu;8v6^?b_a)<}GFcZaP!h7y z=6M5a!CBDfpbD&dg}CE}3b5hT@eE#AZ7z8MEMmY9g&+zC#NcAM>?s9jVVBi%pN zQ~@H+cyPjtzkptDY-PzD{j4>!xb5LV#6wa$wK?P|zI$Qii*Vo@J2rXz+3^$yz80L$ zk>V(I!Y@GKJTRyaraD~!k%e^eoK}O*qLYN-wr^|=-gKhn*P6v+ zPd+m~HhXGv^!V}d>EhYIZ+WkGYi)0|$!Zrw-)y=k>8IFS#rdrz`dh-A%>Gjr|2M)YEkoAeve!UD2ym^3mWe&B;C-lt&)PNif7^=e|tQ|bw zNZvCjpyS^@bYM6x)5y_h51KSCQqK3tI0+?RWw1ykJ`Y7q)QTp7Gf!j!91C zpR&%1liPVTPOMk?Z`!9tUMzwW)}zt=>wAA%eD8HuqkAHZ#EjVMZmCP0SCm;VT)Hco zb@oMq6n3yihr`zv!MqM4tO+y2?p^SBqVDo|rzSYiB}S8BH!0Zcn*{1O;N z`QK1W+-ept11|u2$8k8FtPs9Y5h%2@I@~y0f0p(XL4F6Y=D_VmrZDLD3%lbV zEjQ|3!jszDI*oOK77coN8!gob;yUE5h#1`!N@)-DokKqMx_n=Gf{@e{c!AY>J;N$Y_{hX z{TAOJd0BoF*1>;KqdhBkz{^;Sa?w5OLKDiUiaPjHengdhL=`^5fX1UvHTo558&-{v zZ)dDbCsbR<-;1asVaNuf&=8%nBS!B*XMhT-A!*F##owg3WQ+a~kBFZ|rgy|ggFJt1 z`sl>eQwZ-2B6Bel%{pknf-U>qyTHr^=81As^O*=0 zs!F?6Hf8{&C$b)OWY!B7VQRtM`@%kZmxMYjJP4Y+ebJwN*6zSK9jn8Hg?=(jrID&= zIMa$IVet%R9})Q}XBg;|I_}|wF+3vaxYN|-#H8dw059-utI+~o)H}BXKD=NR6pRf` zgYmFd3nzP2S4B0!=uiuzI&g8JDE>HI?QkM54)t4Y$2G3a*x->t`OO0C12mGUqiC6W z2Vv}6@}tGE*#sPH+%#|8P74*-0aXS-c%^e^LVKE#$ve3P!^q+>xi7`CAs@U=515zb zd%4NKvaR}g@GE>_8M9ASSwaXTAoVygA;jceybqg=SBUa)c!e#NUp{zm%!6V(qOuK= zjJyX86}_KiA237nBjA}cDDc?!poJ@FD!?Nced7K zBx*9soEVgea@^IoqD)k943vrTost9A56XVH_fd9_K-rl_4d&09hkHxtju-;Q#L^bL zMG9pylYMVKXTz7)fkoxiA#3CxP{oy$_|kBT!*5*!$4>n0{&=Dt-K{PoCFC;)v&x z;BaDEv}xFtlP8#~z=H?Flt=63r@;SSu_fU<=)loX5TYJV=FLz?)@q4GHZ7#XI;MJh z9c=xfK2ADcifi8F&KSfGrXgNDc$0y6QGS;|{3hoB8W5M)&$RsM-Us{yhc_F`LsSBo|uNZ^AU1{hO58h&+Pn6#y(6zH!NwC>DzQ`uil#X3s z(-bB>1rVa;6`4$=!BVu6uq2MJ%OH!Qki$_{?P(o0j6Yg6%_Pz!w>+$Tu8FkBh_O=n zL6p0&-1gRiLG(|0K1=S&cRjda1CtfPt{*+g0AvACO|Y=NrWU}=CJgZaV9;}6FKL0G za}<18!`xdu2(O=7b!}3sGRhr7H+iBVE$z`$RS;2;`Lkk|Es|dqx6~SE`E|p&KQ|H~*@Q_y!5o?~@gGfkEH-cjuFMwaMik%FsA5@7 zsYZmu&S?GznZp1hPcoCOJwKgl2~&Egy;*)jd=vDA6v!bvZ^#+Nd4VePZCj(3bj6bI zR60e&){Xm>?2Gm}nFlhRg}4lj%`%r!v~snd(Fq1dNdnzZ3I=f2vT4EiF#cH|Nx@RZ zG{@K&Xyw2Z&tdw)Ff-Un0j8$y>F08SAs3FOQ$LqeGt8<#sLQO? z&E*s;GWvOVQ1^j(B#ER~RqXQk5?YK1HzR6mCb_-v>+(^T9pG=4dk5tj(y8#$U}C53ZDIusMii5X$U zL;2025{qeLrSc3U7Z9lqnA^QHr1Z02@I)6F28l@+SkN#pv}Mak3s_8wDN2bUibfE|vGVWR!q1(ew(=SfilnRB9_yFle^{M}ragkwcIO3d;^EcHE1QwIhC zvsLl8w!H7>F2G^A#MxN-xeFkPCwQVjX2yQ*0v}E->gO&XE@X>S;eOb6iEhu;z$d&H z!ulBHkch7~m*}lqFD!rCg$-A1z<&^4@pIr@3|`WU)t|&)AhSiK(Yb}DZOukJ3;P$# zl@}{#DrKy54lgeq-B5Y?=!SOXE%X;e_`;u8wA+=pRYj0jVc`|H&0$gih8Eqr3+WSv zLgiw()Cj!cW>6Z&-IZZ_#Bbo^+LQdjVe>)zM;5V}kY&#vgECW#_u4R|7P`R5dNy ztKJAV&AJ=f)oqqyY+_T{gAB~IWk^=Q)2j3+mT!O$tn{%gmvBsSBng0a#qaD{g3Q(+ zbDZojW@!>YnqC1TPAOcjfnQ<;YV|s}d-SU6_3#66sp0cphGhevlLJ?-+m8FD}_zrb=DQD1LGVLA)@MVkD_a8K&&CVOXhvX?^g zC5@Smuwu+AZSCuW?EVeLW4!o2-d8QN!vJ=^x7o3XpU`vcSiK9n4SaEO=O#6qG*_Tx z#D@hs!f66`p84s87^QQraL}*g!%q%c2iE!J+fU?vXtfQ*d0y+o8St9ux9YdfEd|LYxhqY{kV#vbnNH%DATmdHDjYf;9!)OMQ zg{|=2^_cO+4J7q2gXmjg&NG;(103ol@7O#6nbksPJW!hXfg(g}7~vU~l~K8c--DRe z27o}WR4Q}SQ~NujEr-0KdE|&=EbwVO`2dee(EFv{i2#Rto0+X(bY5IL>|WNE#sFiW zV8(Y#sgSkJ84@v&6B$_)&Rn;DuUQLPmk<)@0~U=!K-P-h(#S$bi7+Hy2DmA2PXrw> zvgI2Erz=TJNWFL+^7JH}Uki#=R~p9Cb`8Tz(J&_L8U`=MbY!Rvtv3zhU~g*}Kai+l z*wM2xC4;vmIgSY*5=>n=7I;`9=n zu9+xh&GNs8VzeHa(^`(6ka}XyT1z#Bk8wn#r)VmCI1-czUZX30g;9f!PSOaH@EUx? zSvNWhr$+Z`3x814TzHu_7m6~-gUo;JUWMTgq85WXOH^gBREXfqP-v7#Y&uQkb{FlT z^e}Y~r9F1Li}p~qG*PMXUrK6MuLkW$*xNec%UZcmYQ#8E(g|IC%c9rt$GcN)D2N7u z#T3I6ba-0S$dqPES+FZQucRnC6}zItiyvC2iq12=t?2yu)v2ZwT8hMDX=Q4U6{V7c z!@C)bEun{dxPS~BP&zJkDq;!l6k@HJq+hindXGC-vgti>%1qgtw^hfLUb`7@lM%-W zwNg&1l!JIFbs-wj$V`x;39&C$&UlP5-e~nMUMm7}Wz&k#Muh*8>_n6-4Ad^9O04yX zWfYsQxeJ<>AqW(jNP5XY6q#eXSG}yfrwx&{hNhOWZU~N+HrRGkVf^kDHEJ(jO;cW~ zR*Eoyp{mJNZHk)FW;Pp?Y*Dc>>B@%DtWqy?Z&?#@#wgpz0 zE}radb@974r>TqSXj-|#XlYwY0<>ct#FgX0Cmw&?S%P2i$RfLSYZiC1pa#Jn&a48X zQyn5K6j3GK6xL$)H&M>bUZM>$P=WSB4U0Mu?#K#YelG<+?KDCebqx+BYS(JY3u72D zR%I10QJA(nrS;tTmuy;SoYLApbJkW}3_wBrVlfn6UMn5Vkjg+ODRZY`Pl!xt;%m}% zQ={g^Yo|b$)zePN6ezn0R@8*1MYZ)~5{%&~+nh@%N!(-_jn0x0P1RCI(^bWNdK*Y5 z(pE*G!pWR zkCXB|x*(d?Q*qn&K2{&zBm?l^uORYwLYYdotV0gn|=nKlcLLv1kn8IZSA_a~$@d#Sx*;isd(stLi&w{Nt zJn^MF|3BfHMdPH*v9{3yCq+o1H(XfB!&z(2ZF(AiYs03|LV1#LAaJ)&8(kkT` zr#g%NLZ#)*0rKEJVU6!QpRM>XCccJF5N1>l=K-G`VW-FmdZXzyw-;5ZX&Jsi>MLfx z*0K);D~6n)m_4G^$?KdRc69VL%#7`qqLM+H7u$iOzh4 z-?BZ`WZTPn59M~5SA721^wEi_r)-{nV&`Svtu?)@20k7|VGjCP5E>R|T{+5TeW4qe zMU*mzc+5QRtrL~hNgd>=5Lrxe8tutmU6fj5G^$L>Ny-gUe}Y*fZ6B4O z^x%`+h$kktq!)RZ{UV_`y6?>UUM+L*gtViwaZk6R;;hBqgO8$JP4D>;L|y^4N{V!W zh>m7q;LQW1-dVa{Z?wp&kC7i#C+xm#7-)Yu3~iwK&>)noYLsj9uX!(g6JbI{1N~Etv(8zmq(Fl-4ooOIEm)F(E>Svtw;Tz5x<_*#EG9wa2ild#5l0{c zRqe#m6RM^jGRnSHXr+2{5g-^rDiL`s>`i(zcFmTDtVG&~m~|sH!jERR#HUMfYIq;$ zy)nVf_2FhHqfpz$X@uD;<5w3FJTP2IaBF z;u2^vbAHWlolntZCU4*>i;@3h^6nH>X7WZ}!N>q2%h#(T;gL*0#&q70XGY!S3Z7DUm#A=W*1I+?{_$IXPWyc{qe z;kQ;|K@8(?EUy#`HH?$6+_w4%Lb^7xM~FTeqYhnd@(oWp#D*w=31bN|0zwJ}X`Zm2 zQz71<4cwq35|cE;rMV%T`TPZO(vZ>vj95+BIK=Hf`rWcKD?6$zLXAEaQx~4L2Vx|2 zc6*67PB@`)Lk$=DTyV?N%zHEs*}c6j_1^Kwh7H3zcW!ldg5SX6Qs7m*dec7xYyztr zTIMu)vKCtY4~O$Qi!5r)J8rFJvA;Xg)nq3*9cWrt=!t?G+wjJA;PIUkW+bG|6WsR) zWnX|3(%u!JpTk>udM0`S6OLkaLwzFk=4EEQIfQ=@i}Z6kIsKeYxNb~X2hy_AzF%Np z1@?0~ai@S#Ebw_T(Lk=5Oi+i>O#D>9U75T0TgInxvWa@;s&^s_55t8-)Wr=6(|tt0 zm$juanTW)QNE6K|6|%Nz+Q0QUJ<+gP3tE>-gD`COh(EB{j8Wm1)%h2dsOWB!Fw8`b zmp11M?n|W2GzEFeTz6>zd0UbS{BLRb6Mr*{AO9<9!@r-Q=Mp6nBTnt_ zFe_(!i!i_&>shsJSp`~@!b`|F>og~8MVD%tW~QwA076-US)vUx@JBvYnb56Jw(*S8 zo$fg2B3Gr@K}%HYs!jKnwk3u7`7x4~&pc|d)-W?|I4c-^9( zALID(@o6}{Xm!&3TSSIUVy0^umWCpFhNX@^O>kIgx`ySU?(NJe+J<;Xq*`RU=|cXgm*|a1XPfINV;W1IATd*W_xwzC!>iJi+;j( zjjT>9`7n8y-BH*v02tBbvGrhzh=!WFOa(= zF4H;3nuMY=t4?bF=6tU9QZ9{SBLDYs9#GHZginKb%+%;xZ;0Re9)# z6JsJCVR3jBgcEnUj(^e~THO8WT?7moA&LD!Pzbdw_5(rJb0Fwe2nAiNUIBrWZ4Z9= z$;bIGKZ?J&Q1boam+uh2yj%S8b>f$o#4k(YmwEi9`ZQdj)eFmoN~5!})Cj!g%8S+U zuh_7m^3_;4*Lh*yuXzWChle3LIA{)^cIW(HxY=k2B@fL55L+=kIx*~yxRAxH>Xlmf z>rmA_;|{~Y!hzcyhTPX}Z)nl2gO7AD6i5|&n7yrxuQ`X8myT|zynJ*+yYd$GuXe@j zynd}-zRlM!{s>-v3NIhW%ct@3M|k;5y!u09Bt zZFso{FT3&b1YRcb@+Q2L@p1+)Z^KImFW(6luljyy;g#iAmOJA>fip0M5N*ELs1G-T z((oeWdl{zD_8U0z!>r0MS!|law3K`f2~fmJL%8Bu?l`-4ySsKh^2nZDd%V)F^2na@ zes|Zyd)@NG`}XbG`^c`+@(as_>f3IH9(+4qK7g11gqM%t<(KjDS-gA>FJHjRzu;x_ zHn?oV%RP7*#LIrX9Kp*hUgq%9!pl4G@-2AzUcCG$UOs}CU%|`o;N^edAvo4cf4Sd%9WJi4L!3;5%XcDr+3 z)2o3*g2?D;(O&hx!cDU-$iZK<6k`*c${wUtsx4QxRecybeDXB>>|9u}R9ua5U#>1f zC#pXSovzlgJ7>BVkgL;=C4BqgRcWhTZ9!uoX3xNnsWuMvN8kfH*M&k1fwx@!FHm~) zs_Mr$$ux>?XVf<+I@!2_m0tmw@_s4dxoZt1@aArZ$9@$at8D9RE>-+mIq>Qn(yWhL z3Bn|3dH*)s`*$bn2V~9B5XXxL;=0H0-)evCZrME=yXvQ)t2u=0|BKza{BW&)68a&) z7Q*bB$%(0n$^9veNJOvfE|I_^rLBY(`KBQGBA4Lbf^uIaeRultb1jFWGZoelTc$@efT z#$2jaQNLG^R~Ny&_-0PN&ed=#%bA8>X7f>AW$M4>nv8d&N(uW(%@#W+=dd^Lwree# zCujVZ;AD?v3h%d5_^}iUAFxxH7YhdM5ei>3n&(-Q5+NxR9&0ySjm69ae!@=R!zlzl zWG65$F7_4+-K4vHF^C_e~ZYM1-F7y`j?#VT2@8zUDRu8E@={9q!IZIb|Rlj zA@UpTMCQdKy+!0hxhC@cNaXSQi)iH~Q^|2SW650rYkPCHL39UeMH-o1JDD3PWS+B= znHTTvEizB$pUjh>J^M>FFJ$NDv{&*VBPCHD&QwfRs9&^``IQth2X->^;vOUO8*@!& z64@bn4|~Md6ZNvU1ZHkX?OmhP+++idSr@<4PUP=SA@aNIMCQdkCbAcDO=LPKd#u)I zdJ~I_UfG9Dd@i(cO3p5|-*2b%M^h;M0XwC6F&>nS#_k?jAGdN%>Fws^{5)h3CdxzN z%w$>pF*|X8J%zYGYbP!*-q~Bk{hC}8mt-}U&@}&s#2tmhycd_)F$zht`jd7d|8@$I zf5T2>Ufg4{`rWxEasfHmPUI)st*7DKF?1qL)x3V!PUt^NA@uLr3C)Wi>Md@5U#un`Y-In{Xz{lQ!l_q8S$Pc@!*)3=!Z#!lETr4aUucEa-F z`+JLPKb&jAQU)oNS83WM`(-u~|C61>Urr(MKif&ni+A-FiGLy2B)-Mu-7K8@uU4{7 zZsm?ZvAe_nW~VDJzPGpN`l(#g^*W?0#yB2s)YG%Jgt>%%MyO5$MPwt4J@7^@}7Gvlu%6Ie@6Mre!^u0^c zS6U~>YMvW3v+N2_J`_XaqiHmLG=|2ae5ki*eC3mQ_S@MO(zxs`E;U+oXlSBAa&j_; zvThn>r(-B9%J1nd%HEJ`%96HB(mC8kN}YI1F{G`hk#-@5w4%Jn6y`hfNLnw4zWkrg zmES3g7^(hu?9`v)<%@Xv-+0-IQ|+yIxfd_H@$xucj^gDcUOc=6czGLMI(Yd`y!;2e z{AaxU99}+&m*25-GrguO5Q#vuKvLUxMJPlpawS*3{}E zZCS*~C;?d`@X?*u#&u@7x)<(4Xx2VXT8;8IF_f5;|L?3ABeNb$cnl)5G{ErI?t{lh z;W1le7IpR-@}J;b#5=gz-ob6GCtzXF-o%S>32E&hzkj#=G1OXF2U%>^Q_xvmp63kL zGuT0j(#i?0tK-lkfklefBEnwX^jasDaK%ZW+hnKdt#+EejuoR7&`Q(Svio2&(xAYL zw{e=99um2y?WrE2?TxrP87-Hmyt8u6nMCF7b}HYOLghQ`ROZEZ^(vKXJwoOCIF-lG z`OOxbW=*8FxF;u3{sBAXM^Y$Xuv4BF@99;_pYIXM(;D?DIPzYle3~fVYbW(%DWu+K zCp9m|^(juzJtxXv=@C-jhD;8mo`O9dq&C=@GKusj?9?7hq4ps=wR!Oay-MwG?Gb7d zV?!)aSByPwC-rm+sgKx6&5L`Mu|Lowq$b7&Qg_SPvvz8qOriD(JGFUn&ocIZ>Je&_ z{}fr3yJGD(+KKI@5c^GbV)J73pX$ju`y)L-*LjH=K{Ji*) zUM2r8_Xzn(va@L5V*yI|jH~dvo!aM8sNJwrn-}-2WdCfBQ2S0|b~xU!Ax&_CWXTeMh+Enqe=`4dD4gdqt@1}2k@8A2FBHbNK(gdzM1A*XxZdJW*wJepT{^@sGee3S`+;h%7_uO;%;y;(m{mq;#cXWr(!e|StU#{r)|7lD9 zVf&JQF=NSp-M-{1Ik!9fU(W9(k5~$vXE#gJ=jr4fc_RHyF29i7dzfIE;3&as2;M^Q zZi4?r@F9ZVBlsM_UlM$s;CleV0$_lD@iItS0K9}8wxvAtn$CNfZgaTmHX1E>M;1~41J>tE3TuGp@)vwrS{<-ngO9WZF!SPi?>b8|t4MOd`Xg($1~@yb zp{xR)jbX^!)7P)wD&TDOtnoxBxDTAA+B?flR^QWrtAF?$aT3w%2TsQwygmcdb?O=` zj-OtB7Cyq5;A({#A;fvwvOuyZ;@lyVX-x1uTd}L;cNi0d@J%Jt{p!sq7BMCuwu+8k zh4BTOk@pp%idBE?iPbsy~Fsb|1FVQAh?FBg&i zuyBMddV3w?{DraSSv0IGG_YOvTIL`G=05Hep*tGuaV!M8!4uedHpM9I^qa& zD`q)tz;T|EqA7M%7;>Cvq-gn8^h}bC2j2OF4IA#-3#azYO4;)x%P??HqqBe@G{Oa^ zW5*B?peTYVj>p&75^4M>BXfKA%^K zjEF#+Tb)Lu-fSZT#PLZPu1{+x`} zpH#Qee?RQ4a|>cvb*fe8PCZc^UhsLVnapQcu|9xMNSEBg>P8Tio|0QDdDXS=)f?=4 z_4yfl^+x+%RmnHyeXq{tVXyvuS9{fpAR5(7qHb)I-nDO4L>&ZHPs5vvzhPw$Hj90m zx>JetGNfbEVQ&3@8T-xns_DZfv(G^cN4Z-iH0mj;sj%&A$E&s=E5%7q#&}jihuU=X z=THbpp*rzFgo#c-0z7HI*q;~nw=EA_>f=w^u)!*(g(&|;23c>$nA3T|AcIJ{>`wMN zT_ld0$UZKJqrXc}{=n&uD+%Q7c_EO4c_5I_xCG||rS@ux=~m~0(81P;9Lx>L9Fd4S z&6;3?p&~pB`^(qe=Dm0d;XL8zF+PP{B(h%f0+vAiU3yXqt0zfnL&3PtQw2;R4+QoQ z3G75e#yyAg(YaE+;gzqrYkF~K2ae!DdZ}oK76f+{J-t^b>RYNJu5wtpsP85~R>g{4 zq}ms@?&u=bzDTtxENH5YFt%^|_Zy%>!jF1Ux-66lLZLoMTKPf%ae;aj|Dqth9v7$! zh(3g1krRUxb0U1|JSbTx;O6Ma$Y8%a>J5&M;0ASQOBsPIM~C`Hw~PGcVNXCoIY5;RR~qYN!j;BN)cHxwS~?**aXHCf9yO7pP54 z7cNk3wL^K2kGMeXD9i{U&R&UR5yY9f<5HQ<8`P7wa&z+gwGo6<$uB^$SD{$s231`4 ziLOSXEQ%gJ-QL6fvL)P~hEV%Knanr1)()y-PNbMhrsa|BpggU=9-Tcegh|^93M!@c z#c6i*oZRN6^o;vr`L*bo!r{*}n(!oqz`SJ#VSC%SzFVx++^R!Wx?{%IbOIH7QOKF# zmA$^8PqtC4l-1RnP~nHasp~Fi{d-zos|B%j^ReTPKs;qh{ZnKYIxTVU#tzKMKlOjT z;@tk7Gt=|)Q@eU21s5p!7J7Yy1#$f)(D%y8i9M53I!0(3_P)f9y$3U}R~5S{g1su4 zZq9Ovy+54??EPG$SvdkTXbbKd%ibmG#x9}4xOvVn_&Q@K2lwoqKRCC4-@d)G*aeDT zBv515j?Z2OKC5C;d{)T}HIYkv{-r$N^EbP~=P(ki0lO_%lIIDoUq3gsXV*a;5_lEJ ze#nmOhcl2}6^kOfO6I$zTq66g=KEYnIKLp8!kt!yTW3Xnj}xJKv2dt8D-{yfq|&3+^j>% zt819WA4Nn_-!mT(j2mtfkxpo^Fb})CefUc%h8BpT_fJnw%uny# zbMRhR=%r(C(~i9=c?xJ~Jq)VvH%G8nB{Pg{F0uEI@_@Y)>aHMk5}a!{;pxYTEk8@OZXe7r6}~*ht~_Yl@kJ%m;j9&3)OVN!hjb1qnfBqi z#FsDU0bgzosGZarRBCM}SZ@n9Tvn=9VC|cZ2^~8osO0ODFhPBXIeQ2bRPuFs#e{F= z0TZs(vfQLtUq>#p1p%H_!-ZuVZ|KQnEUovullO^MGM*?c2Cfo@+Kb zaAk+b3Z8>IWr+a}(T^zLwChd>M*WL*ye0r*iC8#J!7CmrZgP5f#K9w^9{l%i{_ZWR z8vDfW0Eb<5-=Y@Ov16Ns<~`HQxxbl-lCQF( zmr_x3|Lpx?G+Ya^2^6y6*)6!cSgaiCG(DdR7NqQ@Zt^ln;iGoEdxK2gorHJl`wbBm ztCH!RTi{*fwPtSacL(!;b#F+;I*AvcdlovFiGWAyl`>z6rDB#N^hsXRb~P)=t-Rc= zH@z9Jy;Lu^=GV&mA>2QkC<7T!Ons+Rg3>&Ta=gP%Io_2)Io@ff94c9>8l!%xbHmT}!i`iiBc0}2MtUd@IJHOX ztAs=oTJvskh+Z3#@#t!^PI>CjkhtGt$EIJ(z$R5JicKmx_t>>a0nqq9{HrZ0)F=GEa7~m zc^r4c?nQ!gfH+g^P*d+7JBw09702y%6h)^S?-A2Q6&Oy zb$Xl^IM5#%UR&US!kE63(!RDDT`s#^WnGM5PNm}q3*!>8LKQ#UQ*ZCCcWMw6(;3*W z*h$WZGf2++<@e}uN+oMOdTxcHV|gGs-=mwC`K04i3=h(nn%vs+&5l=4I2}IL^x!gQ zvknU)9JciII@_h&A_L9i^a!M)#`XoqXcZZ^n*!TY@Gxq;)*!RCm))%e0-T#l~VgDaZ zRi{`mA&mmqqz=OZ-PlC&O-^*v8W?lKOVfIO^<|FfvJyCqx_z<1Xe8JwN@-%rze%*| zmktsQ+5*c#~CE(({>W1lC{BWZb{JTJdhy1D@ztZ0umHPhclo<;YSb#V#cY`8Yoe? zcAv5s&5*RnbAc}#BnTp%z=I?X9rVTMz+r>*-~U+4BxYayPGra>iN5HvDN-6wc_BBK*_Ld9|{7!?4FYs{S|E-GSvR)?GVu`4cj>?N{yFRwdJlCfD2c zSMsoJUvpWSxR8z&X+lGm`ogMEv*<4rChAu*>Oxti`tNnrYo7268VUx*HjX;Do3%C< zQZ0(yCI2!vTEIMu#LM|N*TMblB=%){SN&-1excs13MilMhd&ht3}I%XU0~tWG>%4o+*!wqB&bA=x~)KOIz><4YA!?B3XAaD zZK_FkRxt8bXT@u7Q`%V;-6Z{2O}fqU4qBdw45+Ul+}mxEJ_Y%;*V(+op!4h&9rucC zPDQC)>WhvVmh9Jxd~PNFh-!d!4PripI)?KMkAyp%ep_DVkIWscR@()$4OXK%5Yp?g ztp1VhmGq*T^Q^O+l#YqR8f*PAKK-H_ZRq!m<=45TT;>0jRB`u}1{K1mkdMF6r}2!) zS4C8&QBl{@PXb`hTPy2r=a;}t?TNh`z(oc#>nT)IyUDp9F^h}!Mf%?p2m;dvbv@_T zXOIaw!|qYH(k=}2Z*t}YE*%M?G|w;s={ z$>eY5K{omACwD2ESWzQQIVf#NYz&-mWUD{q73a~k!8T3 z)tAT5t0ak`XHSTZVhs2N$4loHf;hLQ>^T7PMUwVt`eqH9@=)WR}R z_Ouqsh4pkHy)av<1j7s#Udt{eNHL#4Fpn+sMwHf`1k6i8KOkm+`3$#i20GM!G({ey!~=Rv0X(38?-x@5FCYw{cq6j5Y^Ooi7p;x?C`)Sug9 z%#2%B_;g9EqhKw=a{RGvBAk-pCC1((U*pwV?BJPV&r-YH*xJ|U9dlP2a6E%xq`^B1 zV>7a;lwm@)dTiD2x>MLP1PTh;m(xY+Sh*?J{#~v%c{@ZWW}b)nyjyRqZsMk zYo_J*B+&AQvuOEr0xj3+xu@lSng?3`j!V*Lc^X<=q~)NutEA;2K9WqCp|@>A=I>Og z_JdgF+j_1SJ=cc=za3DRZu@3sH0WgVa5SR{wLsp$AM#0{S#oJe?`txpe|$14O!S4! ztgfU&Sw)OZwZNY_n826nS$ye-68Mr%*S@cEYXbK-dEiUmNHu{|7@%2@#a1l1!Heip zR;%jOa&58~RsY}|qi>MqwOTj5I7HoK^{++s8&_z9E_AE%iqd-M*mf%u|KR4ZBwUZR zV1ONu#eVK$&0PsUvI*+~CdA28=7%kBq;T15 zm6{ddl_GTb57mMG`%ZX-zaF#ssM;V@vS*;!AL{H8ugO3f$!%G57k%Y=sTHaP39z%N z;Uxp=5{oLLiu=lG|_>D%DE6;_)UwssusWDr@j*_!>&$`KOpq^rl|B2c{qO z=JD)mT8F_A*!fCpYNgS(m5>)F!KhpE6+>&H1O8G&q!hGVo;ucs35r*iv`!7%LKq7X zs{*_Oh6~Kqnmo?DDuG#A$uA@fi<3m$&=fRysT_!m z8oy*=m{utnGGtk2LClT1pvo`HTP+*X4=;}wzD0q^>iHhe{t+-j92U)u3H-i%v=RMNb) zgtBdKZ*$&lgif$%tX%h6yc=?k)|-cM@GbH2xZ^IiJvut|g4SB>3GAwwC@N>>`U*?K zqYa$*!F4L`7-7SqcA=1Nqs=wFmv$=1VpIc)lfH+#j(W6ZYKqC!4ryuVGSc8#f?hyAw^^prMER8)eLou|aHxfr2yJI1t*-ucFj|Xsl)4djq3Z3B$`iZ>Nb6H*Ie~JM0viLXd?3J* zTPs(+0CyJPEl(_2_`%_0Xg$ZpFz37-{yWK%pO$!$tkj?xHQ^4n5R=V;^Z$9XEL6KpYB%NnYKC?q)Uxu~%zR`!-y=3-6I)sqjy;g!bPx${skG!F zV6s|o;e<;?YC)9gP1+ygE9iw-MVU2@n_;U-w$=@nn(szfhLnaF4HgGQ?oph?1=9%_ zsmZ4txb%iX5=5=iTJp+~i3&ZGGK-~f6!Xy(yCdF>$ms%qo|BTQ(g1~;V`lyo+2zSP z&1TwOq0p&;F_+1I74a1>mtvhUcX5;_Vnz(PhOT_c%t@}Cqm}lO(}K?`TDt|L8JM7u zz=lB}Z2sk~&-NF*xZ@7nZ6%(GSf1Yqlu!t0>HdrG_C`3rHpk6@Kx3b@e1>AmEG224 zdd)Lpf6WtG7tG#NO-eoj9a|1u%5nl_SbXjzy+ za1C!QscX=8Io8QeFB#nAL<^_h-RPG2yQmqCSbaT;F1xNP@+*O8Hg+*a*X0zPceR6~x|=TLMYIWesIWmoL}rtA3}w6oWaF~@8tm)Et3ID+ zg5C;!b*OI`$pNofSv>B`j7~=!P~@zI@+L`}deC~?is=N!(tFwvUn$K-(fA>t=dql<$c{ed1>hB4WxN%yB}M1{q7K*~k^?Z^T|dv2VJw zyz9W^*h2V1^Mn)}0&yM`{pjNGC-SLN zpPK!$Q&@4K{f0u?IZ|=MhgNxF)C%;ll)s?7kEkh=GKW@um~x)g<3q%-m`fF^$R@G^ zm8OKrSqT;-@y_}fqgTgQ&!|BuwG5Rg7Fmp?!t$8tP=vQKm)}`a9~n0`v<)F>q#LKS z1&inGUO5=yXCdydmi6-|^|>#%198PuqR>8caHu%yR^f6K z2X=YKQCe~wx>Wm8tq&cPjr(EUYw612C5 zcVNDI$+fjF+qT@UZ=&S~wO95hUoV305bO;PMtCeszh%Es$#PMLCtI^*yqi>psKXPT zzBTV=N!#)u<6V{NjICP~Nt5+fad~dX#?V7YiIxM-_86V-<^^l4kfs&MPg|3VRG`u< ziCvPW*VIa}fLwMxN(9b|wSEpqIhdlojgOMcZXap-gVLJus#PtvA?mDOt+WsDXE!bb z3w=Mij(rUDJ7an9Rs~5-E~Flowe0V+xw33ExjghrYkFEXC?ccad0w$qc_c|Cq4aE8 z>un#l)ldVE$1Q8-w@Hz<-?R$mo(rj0)~U@w9602v2r+uVY}4nx!D-YU>BYdcbTx~8 zg3@t2AQu5O;B3ej(q~X3wx+E{qLLZNFnSjhQMx9I3w_z*0!*{Il^C`qNDL2UNetT) zBnF+nJ?|33y?KxrM$bTE2w}&KL7 zV&Tsm)B#8NcCU^bz@yDdTPoxlP47sh-r-$+U{<((JuuA%AKy~JfX^*`slcClB5VR$ z!ZKCFL4!WR_oE~!KXeRhmQF+RcQ=^GD85#978--zc=1igFJ~@ABZKJdg*e;ytOc9Fp}AVH7d^ ze&>pK+b&Wf61`AJ8fiYoh%5LJbkdlJfYNoN+FxzFx!&}nfp}oa)yvwxm4xlv$=bfl z3ENkvkLCSPu$YJKdrhkUi{$NV;Kw>02XeLVtqj-LKT}(DG|aK+L(twK0(M* zafCzE8m*-(Jl0E@Fhq*6%cBjnUBu+=!vVKiDR-+-IGR8^elCl497~`bI(;JVw4<%Wly9y0`X*n)w(S(uM? zlnpe*U}aXuKJmXs*lUXic75Ez-u90}_Pu7&GF-e{i63oKmYj>lpU?@mOI1*CgY@{j zTamNkIN7S1V8hsN5)34cGj}M7}^C0)8o8Cy?V#j=*kgWK!PSUTv)r+zs34;1r^GyfKUz8Q; zuuYP=3_G0jNnooXSfO_ef)}3uZkCef=1LiFQVj`}%bP5(grt(klL(h$V$`(&W3tlJ z@Zomybbg5}6wOVE!K&#CWRyhHn_`yiz=pcLBl|>x!0@>&f#H)00)tN9mUn^S|I35G z@PX7jvNVAqj3mb8Q2aoBP6)2${>y%UPV&m1aDgx_mN_ImPjAhJvv&2I*QSscvX@G? z!~cB3-u`yh-u|P6y{*%C=6!F!I}dyNeV49P32sA`$JaUFqG??yYMoLKq=sdx#jLZU zrgiNlXw6_lcaCiAd`b8`;XpR|EFrp8vbe9*wf+9!$b|AH=KB%TO+FgZR<6e-^jQ`5 zZHs9%$bs4QdKm>1rSu!g&u82!9tf^@<$LG$?wQ>;IqSi8@LAHR^Cb<5L!F zNOJNk)uNBu65Iyz_d?Sx9rl77E7=WaMeh)`HDEE^bK;?%RIt1|CVJUyr8t;_qLDWR^(SW+^O6`beyii8+4^CuUXYFx}>_7E9x43w*1^Is(XdX5Pi0+)BEyH zbw8X3s{8I#d&5+`cpOn)MOG{3HZVwQPfuvSpU{LBM3ZC|R8|pLCYU6aUnGI$7n~&y zx(CaZ5bsiQc#X_>Ki_eQ(zzh!y*C&vwJF-ZXF8IvO@`=d}8%~}sEFZ`38J*sfcT)9< zJdmoLAXSZKE$3U2|ZBC0MaUlsbq2*hnP(vyal9WJH zg>|Ke}#mhb4#=@oV@aUl|5%VE@@t``aFd9Bbp zTLe}3oVF5Fl}M2|-45b`1Y%mvBBl=}5L2C=d!N(a&I2)BSUoW{P~##Y?N+OI!dH5o z=vi=uPOps{4k*JyBJV{}R-6xf03S)`_A2FUQC{JR#Y%bgLjxE+Yk4J|0@o5K@T;>Z za6N$n>-5}vV%f0!{0~(9z>8N;fh{Or^vS#J&^616iaAJ(>dguJN~aIy z{r2^SJnXB5)P0qS8;`?H<)81w=72$+F$u!Tj-2P7cDtb*Wdzo5b5pYire+V$P3@UK zICTJUF4;aysK$+&Y0IZxw5*1!y)9w)y(???{aC{8)9JbAY8&&g`=(aA`|#i?j2@4p z4Y=BkSL;-($s0^O44uo~zSx71DKmt}cPDJK&tz>gT`+25q|KZp3JXw(T&An;6ZARK)Be&oZ%GT z#3-r4IL6;nT0y5hp9L1+wrS9I0ZsWrLk%S*XFOtuFxKJSvYG%E-Y4U8s0otqXw+*O z)7Vi(fJbC{Z+C}*2Dg#hYU}iEl4ag!^|=gcCqTVOG|3-;ZFrK*8O@#T+~M6avVikvrAf!UMqwA%{vUe5!aM~!}M3> z5>ME<_d1BMMq~F#ewOL$^B5S6;q(|SQ5XPJtKK2EDm>@;?lB8J(y1PthL!OQ&TTn8 z&I^2dx=?FT=S^&;z0`zvu63zBz*bfq*1-Ft7Y_@1i+Ex*<(8HT)e7G3#pX+Nta;or zEBs=|a5eXQg)yra0s-=*qMJr!I(2umzB1SH?nld_dBvPqv-!g{3st6q;n~(oms8IR z!0|#wjYk0qjj;l?1vO>T-34YlI5rAu)$5o&VztPHh&a)}*SbIa9Wu^Sbd4odl_Wsi zXQF=HX5@QHr;}=*tyjgn2usECQ%ap6K<#aJ?r?CC-p1%HRWL_V4C}DiQhkEzvrw1^jXcZBe{W1m5(tNn-lSYn{c!s5e;kwo8@j^SkfkmXm z3g2x(890gOR9;!C8r!|L{S%^eZKEwQ}XrS4I;(#G4PP0l=~XFu3eac*jI@19-yfXLoVv|3)1et%kcm&X5!$7{MSqwBGZ z;>nReQtUv1A!2YRPrq$w2!{;(EVp53{qX4y&D@)l8#bI0PlRkFub>8!AryZ$K`6fL zl8lF$x?t2WR;L&8uKC%W2ch`d%OMnZ7OIs}V0NE|4NeJLt}VR&rpm$Gp=Q<-psrcSJdZ%&=1hhlaGbAJ=g*l+6_0 zfRbNQmJ4Moh;YBglG!@u#}?ip1JXk74Aq6dy{1 zaR@z{berWJ^k^$Gpw5EFj&5@B5>|fgjq?xokQgemnsK1jbjh)1 z{$PEDUT2466}g$8KasT&^0(QRAs(#@mv=mbV@?ou*`N3_i!Lz~a%5GRn?J#(lz&rj zNNHqs4TLKt#)@PJw1V9ZN-$gxhjLluqK3<@D&<^dH#IKajxoGbza zNQ$wME>#e1*5t8-1p}%h|7W=<-I1bF@^wt%M`l?2O%A?c1~TO6rzm+j`qci$EIC>i z+!lGis?!f>IeHNGHLKLBonAhIxO*t$L?f@<6lN&3d(OV!F>AbQ{h3G9uyeznhocBd!RN+JcLUCgK&sJ?P3xz1G)i zmPC4=$Wf^?x60DzHX5xym2YAJ#1$?YfRS^ef_c#0A`6{fUSUV)^B{8n7EH&q9(tAQ zk*3rPahPOu1nm_LcQjgpI;ZKi{aV50CWYuPm|RJ)FaKk35enxf706wn@j+SKr%W%{2rwBx7flw5T0taML@t*nleFq0EW#V#lxPNMpeq&O1EC#O61&*l0YpSj) z-Ec-~B}9t>WID_fAdDADn?}m8sdHi1UfyKOJvE`>s&~?zOeG zBu%4wAUX-BymZn6)l$q&OF(}~XMMb&5?S1`MMOw7e??urR8ym3zobdgZA;;B`qTtq z8FYog;`)WKx;&L?rQl)Pe_$uF+dHWD`>+}6qyte{}Dzeymh7BGrj z4kb}?F{sUjMo>0k>uG09~**!UJ@=@2dW0@BmJvPahOZ*5l3_ z!C%M&!Kdm?6a2Kv&$>;JIOOn7!`Hne>1hLfi?{;uZz^yzu?vdH=*X4SIEu2q{%52CA0bw|Rc+MKnibiwN*n@Xqq3d+m5 zsf3J|x9!u&!S*@z-}K(02QTO;)i$u=g4!Gb_G`rEMI|nEXk1n3p*npl*m8uSshuL~ zD7HEjHX*kcMtY0*&q`EPrVy5e0>-&Ie7?zch2iTpG7b!V33z>H7GCRu*GBMKr}ydu z!xg}5bqSC+#C~}W5c`2i99eKaYKutWdZrPk2?!p!*79*e-lx+j!bDz3NBe6>n=ZX0 z`eG17YX1t(7leX}N^S>A!R9u}--|2>96=n#MTSZZdkdP|`l2(ncemfB z*+D7kQ}Zc>HE0%tJ;RT|zTscix~KR0RXexe?ugEXG^JWj^#{wU^pCZKu%AJwtX83? z#1X*$fq^O_WPw#+hCvOmDvY8<|BYVR>A>Cu-4NwKnCi7y!(9$nFNHs(NzSWDZ-VJj zRSK26oI(qqyv}yNLhtq&BxNFjr0mTiDZ1d*5t5?QZ`UN{$sj3o@02%saw-q>Wg{B${S^UXC5f)OGA{E)65vDNw^@0>vZ`dd5x(;ee&8zJ!u>Bh-tEDNAI4R zw`qz4#bB66oL|Ik`*vrze>f^dD#0ycIp$O8vqvlM_S*7pnpk_5h$WXp4KQ6rnJ-7wh-Z6Q}<8JPo>h~^O+1oZ((`*kRoIH zv~)TnzZLBNL$-<&sncb zpo4GEqJz3%ln(0j&{;ZPcEk|9BkLUJAG`k5i(k zY1~a#JAubFi!dSE-@qlHC(tearipby)hO&H`!ul;zZ0quL`WDkG6Ubd1zk~=(TP2h zdaE<&PppD2dK2h*N!(I|qMrS=!wA?{lMCU{`=PYnn|H`bMQ3`^IbQGJ zi2$xgcohY+z-py^+-cV#EX61+o9=Ozphm=P(+YHHqWab*r&0A>Tu!-VS`fAFc#8w;s(so%{W?!*&DP}Rv)f%VCO>FC9J~TS(d6ak| zWfwABNuHEKW{qcw8^oJJP(bMuZ4NdIrDjFgU1xbhdq)BneSa1g)dim)x$D#EJM?Md zdffHJO!BrJ!0q3TyeX+amj}lB=c#tZV&0uJd@-)QL1&$ku;PW9JBnTlURk_KGnt%x z5H=0_(nGMQRd(_s7x-xKVoYTypl)zTpYSNvVGLP{%}z}kYJ$PUUh&7@1CpWm&u~;zTIUXg`RI0M?s%*1trSb0Cf=X5`80Q?QiU0=;AsOR{r&y%;>tiG zrRX-+9@PQMwsA4W>E+{WG~eX}>!SQkQ9>5q5t?eUW1M7+bu>%y|7AHTeAyKrl+L^W zYI4~AIbgRsGk?RV1U7)hR@L(w1x%PB{OD})f*iyx>`zjE(}WVPQrUqUB)naNmk+EZ zZU!BiY-#dm3JLa3`0rbYl+9+R(Z(Zk|J91MTWX*hRm89nH5AJ!D=>G2ZZvHeM$6Iu z1{0>=##_!%H>M&_`YGzaB+&nlX3>9LF#2v=r~3hQF9ZF@)Z~rUe<=^N{)&`dL%PZQ#cesNZL^sGlwv)gkHh+(9 z?8S1$xZa9Z%i`FYUAt!Z!qeA^NAJF|aRVY~XbULm!M`|Nqayc8oaE*0MV8+7%L$bE zZ?h=#S5<~h~4;9aT|%?GwL)2V&}W%fJ+E-DdI7aza*uBcae1DyIzghx{e& zvO#(^Psl(trul6peRne92~rx}n#)tVu9d_4iXzmtXC-%7x4oxU;u*!|ob zVD|&h$6NO#1eXT|*uIs8x)!0HHv38$r{3_I;#A+t#(x`Lb-fqRtbd~+ktI1 z4Wq1i9GJ#0p3VU%j$N9Hj7^5!zN zzoX8=ZphmAm#YlXxBoibfBXMb2!Z~R=1mS9&coLKF3#u%r}VczFDv98!{hR1+3{%r zJfSt~@Pz@pH3x3cBr>Y=M@RBbI~DNP(Y{@c>mh}};=*3LPu2diY!3ly?uu6sW^$X^ z*B;9ex9s_MvW1=r;VIJ?ovlQtSuH?#^!M9-{B1kMYQJGU1I2z@J>`0-vw|yqK_6L( zHZb;%xhpU+!|VT*K4fay3OAZ`NDEQL??$Lk`{1^rUaH$WR&pEC@T~%$QN~;bsVQ^n zQ!t+x7K9`oBVT?-o=MGXkycg#zHgA^40q@C+C7yPE>PfSo&H{vn^};qi{m+pWpB|% zlz{14qtl*2l!#X0epCssSwxLvZR-V_DzmrKUPYnSVOVAawcC&aHiKeNZdF`O!PL{} zj=vWEw}eIAB%XKbJth&*)*26IL<8louF%t894wC5@JY>HqV)GXlSeC-)SV1?Qvxrz zEsGc2qB2|;Q3&XC|04e?Um+knmN#~=GY{fsGI`=u)Es3jr8~QN7u!uOPfny?NMyR~zBST6kE6={D`K`@oR<|{KvVq$1TI#};9dB>d0=oTLe7n#TRkm?M}ZpS*zrfAx!8q{WdCeP8tE7Gf4$<| z{+%<^^Yc@?B77y-4r)wt_r^ywIf1v*_aB^}n7gmb0ZCs)oAWcSgM0SQADr92Z{OZo zOw<}iiTjYvtxkidHm`ghuO)(o?mDO(m#=BiVolGt(P~dbA>7qY;nir>qN>!S?YJT% zG2mfM&%nSybgRMJv{%qI&_jC>vGw-Mhb2{7HX*2!*!U1i=}ldpT(o=osF)gTL`vF< z9EFAg_D0GX^`na+_28_AkSgNj(Z`rtJ<8fSAW@PwRbaapxC@rjp>}sD8LiTISU=gc zixEm_C76Y9v!(@Z8~g#Ro8GyF1LN(n?wypx);PM}jav+IjNnkJ2;m)k3NpT+N zrnQn#dy?lKqRP-H9hiAGiDt{)7J*Q!BKsiVhUkcR;TEK3wW+v#ukdyy0@V4n zL_g^j0bbNti z=SQOt*RScCK0Q!5`mn*mAoNdt{LvMnui%+Czxg_nLJvf>oFIdfo05DC2hHbq{D% z8q&$Norls*D4~EIVOrM%?!MVO22C1tm_lgp>Gf|agRaF95>wO_0@@mgV1ZnZDHQ?C zu`D4uyV+5}D$K_$t6G1!S@j#+jLGwq^y9Ra>YXZI12dYei`U5hLmg`eT0T!93d)(D zu0eQ%v(~+f;uNMAX^K&BrC)Nypb_+5dS5lBqRZu<6uw})rj$0SV;t&?_Ty_Th)$cF zKyXTbz2y;)UwxW0ts^cQb$3N?EcrKy<@HMkSQt#RikTECY!I5D4kOVRY^*9Vp@RCz zN~d-ORXlctN6xGm|9TUj$XrE)n^hwpFndm)dDBTy1_p*tqQ(-%@zJC?UE*N4b|0sV zW{3&hQJ@aIn5a{}^2i+6lRVbM$m~mC)RIn+_6PmntL_=8_Zj@2L9UTsIPZmY`yloh*`yYE$c5 zz`3Z5(Rv$ope-o9P*{0GO0c>}t%jC1+CP)+_?XHT_!AslgXihu9XsP_-|x1e!4@GF zRqw-}Lg87Xu~yYU*A$vH6mQiMuT%9Vlr0vPb^XJr(zG<@Wk-!uy4G3A2%*XvWXMW# z5I%_q$4UvG8VwxDFrH=`WFx8rAq|0CTnS}+;sJe9aP+em%`_=YswMU&#}+Zd$@@Cp?k?rPLD`52Fx+7mN{#F>?u^%SZy)_|nyFz(l9 zkO?`%GTwPs5K>vJ>j0#xXL>ZVfgkfIYG8b(DVl5tvXRh#sDbI?hR`AN#Up>JhAEse z!da^Y%Q9Wuz`BU=mQ>qn;)qgJy|x&lq1IVV@xDr}T%4bJU>;7E_%|krNuj+GDz*C9 zp{wCUQ#{@RXN8F04KK1utR{Uo7BVh-L~v)d42auobxcTK_ge%VosNDrGV;9$npXt%^x1@w!^QNGP(CV#*3$kd0JZa_vG8M(9 z2x|-4&1P>w-t}T}X*I5f&9R=fv$|L>x@LE=lB;KVtYTGq&TKumA%(5Cx+%(rA|gwd zC;vIG9ep9()oJpKI5_2mXsh6&@I)XKbNrneftuMGsauH=3UTCYilM@taF(mWZ zM=D#^0+4zA$}Wd0uozn7l3!KOh?pzO{@~!jSu(3#_Z`)S6-V^>E)Pv-y20%0ul>^4 zXVaD%SrfYwFQZ_WyZ7#kyO-yb7rke?~e6 zLYF)oOn?yE-D>Ni zTOO*x-RhP;PzjdA+eesj^OD`;9k{+@heD=WbrxXG$IJE_wod56J+#+Xe>-QYA}^3T zIx%CJS}~AH|D9f!nD6HgJ;?>8_~rokzn>{t8S3h%eue-NCYY3xG>`h_3I&t<7 zCMon7u(ueMeql`)oAvY>{5`yek}%;C^BH>gm)QaJ><$K=t*Mnpo6%B&i()zJQb!Vu zT3yGtCOV)mHN3?@%kZuXQ>HS!N8_oeUq|M~m<#SBVAKak=X+LneM#J99kMW#&5AXI z?#EW&i&d+x6Z>J@@x;1u7d;)r&vD_1TkX)F_0T4#1H)bXk6-rVzeUJH`{$?TBx6x_ z>b{L7rA^@#dBIzCkI(3+4zA<2;aD5a=wTxdJ8(hy-QandzC?kKV3@eMaOb?pcSA6n zue4#%P;%jf8zn{9jTyHF+-lw`A4%?Q&by5eA3|g0I-V5O>hK87H%tqj465xe!au3t zaVzr5hVi@6leT1hqH<=&<0q8vknzE(G=)eyPc{5WN1O9sPmzDhYB)yCI}u@|a>NT2 zvIKjwNwQJy9Z~j+>(=|lJ+u^b!ru$prk6r5f1(-VP}O&3cPzeF`lnP)W5JmxBokX%y4mL_cNYNgvuyYge z%10|<4v^Fs+qB>xpO6-Gx~w4JJ7#D3|6pUl^aFMTNmu*KYlP%k?HFTttlkIAY8fSDoE`zc}9`Jf|{Ytgu|8Cf7_zl z`5qBjsEI8t3&$czu(w3?TPiIn9ZXjFY*n59hkj+Z39BD6=WGv+Ri@W;X*=(XPsDeZ`I5)H4{WEV3H1 zgf^wH-Vj@>Ve1@3)>TDx7a59VzQ8MaIAxYY9c0Z$u1J3)1{R4m=qhn7+rZ>OOC{zj z8rmr|{4=t+0Fm3|6bkd|X@0LTPm~WRktDDxgaHAz$Q_@KD=}i`&z=0D@kG|OCM+>3 zip+UvlA;Sr1PE5rsAOF+BvNlOeuVm}*??E{sb*qVWeYSP=1Z+4DiGOOk#6~Qv?0Qv zo|Q(Zo|CJ3>|&c}V_~|yCpW~*J#|xoHbr4F(9;Kj!-9iXSTiHfAYIDY>!ql`F0mb- zC}=`2-G*NQAvtrQadRx*?}&loqe@&7q^V?n`1tLE-ujoe?qQ1zdWPCOIWv8&>Lf1*(OZ1f_v#2AAJRfKy{;*7^ zT*Ju%vx*PsgRaTdt>*U;K}DF!5}$QNHUR~wG$jsDCHQ=dch<)k&G-GoxEho?g!9O3 z;k+y*X^*yG5K|FZf9CQ#Yigh37sR$91dVj_7PwHb)hfFsm~8BsFze?}sx>aR@pgsW9omNuM98CV6%IIXzLgs7(vn-L#b%q$1~V>msu_=~C74+w z&>}PoWpn=niG){cdodOGQZ|6FxI$%q`|Zvxv3imgfQ@qvWfc((VrcMbHL#Xglju6{ zQtUlvi7@?OivH0vCVm^MYp=^*4@b4Sd5Bz}o68{gS_LoN<)-7Rn=e^)$N4+nop4Gb zK1SM?O<5n*M{Yx+Jqg-dgVCP<;(u-J%eG@TXVHr=DlP1WdM|>LZSXk}11z1Rui`?? z-c%@bh-`Df*&f@?JcHXS=;9EbY;78AM=;20lo&SR_`lUy=(c%fFW0 zWST3>R+G!G)%I^t_??0MWU*CwB)V;A}{vwE2cS+5Cx(m7fC=Dm;Ff=>b ztk6e4WOlfbKT@f8TDW2ftU=bV2d1^ho;jaMo{V1mU62$E1PcnWz4Lu z`Xq}3#R-3rbG1cndanp;7UxGJ&Bu$l=ERA~C=#E$(ZrmYSGMGXpDv~7Pw)tR(M42(v_99@4?>hQvBNmU-a#SqaC#Z z2i$6xme|M zJPUB!Q!iCGIb97nd^RoD9 z`<&1h8-?BE*ius%wmM1o`-3Ba$s41Fegx0o4R*>wgbtGHq5d`MUw@kf>$0uGS*^>% z*Nj_*?HnRbsknBt9{d7`%Z1|IfnE4qBb3Wo_$KA~L~VY0EIV%cF(ALpMs5MK~Um_0M?Fnr8L|5qxAc?!o2 zCA^j8?}esYI_w1+B-ssTMbCzs2HdyyoOq~*!4R8`(nCF4orih`i~SElG51i7mA6)e{4GT(yRe5mIn0xJ?x`J-k+SWeDrzu-{MG+@I{*BOWK zR;Ff)Vh<^SF_z|!HyB2;TygooVl=E{*Z`Xi78mN}<1pY>%dQ4$$;StB6wtU8cs$T?kuFO-T3YNW>Vc-(4g zA63lZ8hfdyC$zvP^v=RA;l9GQQoD)?=APmgxuf_6cQgmxgXKzz@2`4rFh$xxkED1K zZ6H(k7`u$5A+!VtCTc8*-UNnX-S(Z-V0}h#LJtl-Kf!^uTO5OGwCCi>bsiQcMd4^H z_+WTmQWnrO3b1WS-m{)%vSnKp3(dHYNLOh2)=0jH9wC~D-6XEfeZps0Dt1fj!c=ZX zGT?h9Ck+{xDGS;{;&uFj7n8_FBR-^R#NzB$am1j!?#8$!y1TKc5*OV>tify7)@*3@X;G)*c@c5Ba z=0=sy-O?XXn*LL2S~KP73vmX0=FF+IIh!UP-P3M2l!0iVWuBXwJuo$U5a9#n4^ABb zoa$+GdrI5g-j=UA<#mxYo!m4`LLze z5&cy??2y+j4&1ZwipV&8=@swm^Q5~U>AseWG+nKFhukV%aKTYL8D6DcEA&X0TyRiS zhQ$CPf%G^p@O9In)}kiJY)Whu@yhayYY%SuSaJ9;Vvg z5*=$Ex6BH^*fCtqJ?}1O^+LeAjy!Wv535smH|r~NE$@D`ESfhi)mXd!;hKdiQ=W8e zYo*JmOF7)phl(0kX$g(7f(T{O-NjCE!0|;a<6g(?5vxTmI3S4nNYwq|?~tNf(KVJ- zRr2V+ide~J&1to-HmIaoF9^^YV=0@p#CpeSyNa!xTFcr3Pe!X0G|Tj!9Ns%Rdw35q z)wj7MM#H|K1jGP=^{GMp38$_r*yyt$pK#vimnnD_tPD4O(#UZV#tWMGApXPgLOZ^J zg_|Xt!|t}#S$BxiwT-sKkbRcJzcOa7>46p1gqch*rR;scxU^TTvfFlN8Pj6Bs#LmI z9kjVv4eFX`wLC;&fVl#$>iJ(`@}cW8x*p3YF6#Y}Vh4&ldNDXH{B+xBm|0JRX4Ce}(Ueq-@dOAFHw%l9E+vVm)NG-O>$-ZE#A_9fHPtY)YM)+Q{k}2v=@;>SSX$z^u{hC``ZZ`P2w_!Fc>@okus#!m&F~~@a0J@Jzc?w(M z9P(#Ig;i3+G9=6B4iH=^V0J7lJEjfIB2`!ipxVIkJq?b`SQ$U%1|#>B}axz zu1$9IMZRtvc1UlL`=LmeK6P*aQs+dc+~HANYzBo8(7EHN820u6SGmTbCfWa{OBF<$ zH9xovsE+)fmW`T~BRKYg?5s545Ta|fLyDFmL<)QzMA(jJ+nr?L!bW(LBb6D3Qo^Qa z@@oY-QhuFDjY@*f_#Xp3qxu4wDM%~gw7;`csS0JbUxw4lpP~+vXcT}~{uYGN;yv^8 z`wk9TY{cRMPq=?-kbYxQnIi_S1vrFV6J1hWRmz%-z5?kA`)8+%QU)#P0FKolec6^7x>U7i9c+t%M-4Oc6&U zB4und`NdTxiM0@nYUK%5P%zrxBoJ0Jlj2S+hjOsZ#h`j6h?^PN#I}Qtt|&`oqc$NG zNB+Sl=`b7|c7&eER|i4;vkRxcsQO39kW@$|te=_`8-n3Nsx0tJ)Cp378S_bsBDZ>k zFTTih&3ABZ(-imz6-i`e5UOBQ!0-LFqfM93G<-w` z1@yHVJ}L(lX$R6m6~YP)rLxUwc64?G*P};q)2;c`-rati(E_B_YLenkuY-wVHf!C} zd;O{%IPmrzUn_*d2}fb7KUh|!f2<|MsTn>rT6lEs)=C~!%6#Y?!2?!-8OHMCqLuD7 z_>cr)5<5`gqZ^_eFtHwYa^B@|^-}mlnv|Z}Q}D>}c<%2CjYvXeVpC>NKO+2r__QLJ z(EypqR^&n8vF65ltqjKIlWZvwoIN(n!%|-x6ZDwyTQ+n?%r=JOfnOy5lbl9*6`=2) zFrVsfV7ppf!OwzACbLkKjY*nXz!giajEKmSOMErcZWZ^>?>;zg-luT?>V`s>E(upW zf(D6umzX+Mw#h4!6&FvB=G-IVNJ~tnc<+^O0R!L*qpi+uh|x#Ji~qGu6L#_mW%Fo0 z>5-Q&X160Uw%$)-+u$(B-@Gs3@40rp>0u_sDT1pxUB0-fVzR5hsro3`s%f%_vv*I; z+f2g)NiIwo&M%bSzTFw_AC5AOnDD|O1YGXy?U1{@w$%EZ#Vx|Ir~#(Se?AeAn+@^W z(5vG7gZriq&Q9%~nw{D+IiImNzQRWgdA5a`4Dwf$X*AhMb0!G`eR2>3qah&gXeRB3P*hZEXq*kXm zfZGaRB$Aj6t&z`KbjsrbyCcHV6!i2$KoN8Ln8{1x{9z3bDsQkn#<)zCmefXRxb3B2 zvyo+rqUM2<{A2=G(2ICMZbU(h-~6*KD2asDxliMm_?=LNz&)W3W(Hb@1YJ@0N{Ky^ z66qQACssihy$Mcm#OV}~x@UjwFg$=DQj-Wa%I6wV_su_f%{N(vrXJ^Mkj0`iz33dT zcOabO#7pghyHKfC+Hf#h2W^aPEH>Ta$arwh3S?x5)web|&{Mn66}V+t%k0$LyaTmR zqh3LX=A!AkArO+dCojB{C|M4%)ke0ovNTH<8(WuR7Oh;Z##nA*TOWPW>UZLCL0eNK zhZmA%QF4$QN}WqrB|0SC=Nzb4$~#gc*c-|!Y$c~8!Utm$gNDj?0dvq?mYS6X501fM z>}1k0X%8_CW=J!% zAy1i}o)QE4RqLE&sO`5qbH`h4Z>3o3H0kA*A>?Ne%%md8*1$-Ae}6!(*#Li(S~I2S zHr5_J%E`jeN^*KRg=oIZ3D!mVo7_Sc-w~Q>Qe4O=#yXm%_5HwZ=QRqLFoVD8Z1I8|#4YSkQh(F<4p*t{z~xiELeS~7GWHS54n zVZUqg0edI>3t(bxv)O4NR!X47w027kRHKR*Hk=M&Ib{Xrj?j&!4Z~IFyPz1{z$L>`xp5S_#CC^ z2sQ(qw>%8p6F{E#t(U;{pOYh9_3{iV{z?$c2FFI9tY z3)$MYCV**?h-#U7I(Xdei?!=?D5CetNfRdofBtNO*+pFs;xC>RT~BschX}gc_lCva z=Y_Qqy4MnsZU~H*1jL5!>wV&Z8+^tVSGfw4Fkj``=(LtXTL98F z*@3UPTE&T~Z{tRJPTA<(?X?jQLmKb+s-vR%+9!TDnwozpbpu2+`9Ba0!4O-H#o#c| zxCq%^7_~XL4zig$srGT}3aPv8soEpf%z^l*&Q`LNl&~0q6APTCB08nNqfgJ{lZ_ z-H*^_$#6LpOj0o~{ffFnoryy@zfR&h-EsZ@X)KwCQG);7 zJf_I0os`={B8mD#7d3Mff-V`ycbQ|SGGmmL=)bl*@~c1NdZSClIot~PowX>}WnHpE z1@jM6d!T0-ZeraUZgiV%l~wh@%p2Mb>U&s)AKx7QlJh_z=N85~m}YvhBRHf+E)fS|-OShW4;uVCBdNccK^Rrs^{L^xw zXF|H<6^kK8XDiWZRzt3J_v3FH!_=0vJp=H@X<62Cy#%kmHCo-(Ypg`GYa_NtA2PLU zg%MX40f|7Jg5?I0_5aY`fvp`YxecjxuE2^?sHNmw2B|4?>SM0I3FI4+7;5_&$%kO# z?=$;WRsk|LiN?@s)NA)38dbfCr+|E_lHAM!cb;{;WpB~NL)Ph9qtl+jL*`b2zPVvX zO4K+8KgaP}5x+~6+1qKaq7XbgSK)VA*KVs93VSgqw%a9_p~Aadcy}kq+!cMrBsYXnj~M7 zT!V=#I%gsOfqY=l0ZCY8p%k>``>Q$4Neu!X!>1S>JbC(Uzxf9n#Lx1PCvMoVr?dQ{ zlN&ZH|0KaL5d2qyj}!d=1YaWf2EjiQJb4$uvk7_#`U!Rr+)wagf+d0@1g|0ZQG%Z$ z_$7jm5d1#D7YY6+!FLFrI0f(wf}0375o{&6hu{D~nV?DVN`ik!@GgR%C-@M-#|b`9 z@MVI(BluT>r|t&0o}fT5O0bJymf(j7Y6LGMcq74&6Z{8)4-kBm;IjmOLGX2ge(=oWWuYH9XEx*P%K=%U+e;;+uBCqYNf49h=+ zDY$RLvddpRozAJJw-BDRL=rfw>s$Uoe2nJZ&{F4 z=+^SX7{+pq9en-y)3XNqbKoqEyR+Oz^$(v`q=gIgASU6|HCEI;z5Ehx6r&{TG4f*x7YbBHt4Z) zqF5}ROrP4TWe2W`bl^6be0fs8Rk5oh#ZzFE)olQGlOO-sC8el=z}7X;?b?CAMO zV3w=snI;Au9}m;V81)+4UUiCKs2}Mm;{`MY-LU_{~A8x zJoF*8-XV0@D^Vwc4l{RLD${u&`fXdeIr;tC2pXy67ogb3P%LsF60*DK>QhZ8SE`3k zxA$imBvHeg`GH`+aowyb#W9D=3JM!uBt)qvu38Pw5%4 zefbmUnZ`^ZoQs9Na&iLit?oZKKQVV-`Z~YFzRm|T)>#$1DYDKgxi_!t{NR!gGt76erSIJ{}UG;C~Y}K!aOYU6?(OYugm_=Ia~H8s})b*+AH=oerd)Ut75lC2)at%nAbJF?4Eov?q@`u zEu}AZ)4td$c?zp$6(iO6ngWJWE%Q_C!g$-h%qscTq-9p$ zp)n81@G6{{VEwTEJYn;#$tK{qQy2KlEw#3hh z+c>5#_YwPYt7KjqS_!xM9$jvgd|O_Zdo*XueZ5uXgJ{!jTzv|?&*gSPe$2k?kH}fdwxlyU@$=TvR(_DOE*Ohf(REkJoAbZ!FUi@u z-vX;MJWauK3*V}>AT2w!`Fc7YJZi^-H^}7ONqC^X-w+}BD!HifAY$#3o87;ZGb|9+ z_jC^<+yTk}jIB8rvAmA+qLRjCg6}6Vfj8Na;O#Owk?)_9-=7gd0+pP5B?*3QjY~i0~EPU3AzuUgzKb5iK zs@O9lTU{mJkav3jBRN~~tIZXc0(Qnue~*3Le<@?#Rk7&0tK{6T`@3?s?$;RWu0uL! z?f74@ul$EIR{s6+dz87WWPPa5Esg(=Ia~SXaOEe3k9Q&Gb;sX7Vqg1DW~}{h*wp!T>P`}FJ9p@a*c{Fvd25B{@3Lp?<-a` zn7RB38F=tjJ07THst9w92md2yc<_7@0Rs=Fj^QmnH0#}q12b2uC6AcR&J1Uu!pu`u zhKMxir~3;3ydL@gHfP8n6n=AFJL(r=B3 za8CjvJe-9H(+P;6({qmqmrUo&H1A5XhpA?>-pn`?%p6Qu^Lo~r|4_o3>vV0;lUvd7 znK@hYXK~H3`Nh+Hy6(za`j;jw{VTGTzL~J}I-M`_b1edgPB() zAjE635aQJd2%*zQ^NtWN$QeRxv?9bB4hfGYpu`)pP~vq7D52B!ON`vIhw+@Dgg7zI zddjc;g0}`_cvAv0d_4;p-kg98Iz9I%#;Kem!=Ac{4qXY3-%LP;=RGl9lQi?K1Z2?Z zxkrZCoFT&|G7EZJj2;`MJ3vOGmj7e8sni>WnH$yOM|DbDvykE@l_9EA(&=0CEPdyc6*}@15JbXLjGD^81`6TYM)0 z3!eO>3|-bgBw&F~*Iw>&D|i04oMFK&#DeMC5lWW(5FTKqwK`0=T&;Lif%V)hOt?a2 zh$^sjx^`5PTTJ+T&M@J+7$y|Dc^flxeF6sDk%a-eVB~p>Ut#WF!u?gwFhFP}6fFo- zftgCH1!Kv3TlHGAQOe>Dy$Kj`R~ANWR2iZ)L8s?lEAh>oVT7=gNW%#ESUL+Kb|xUi zfh>fWOh5>oo_jlqf5{m_h)Z!RLa4;-E3XFdID1D^xR)(Y5qdM^htDQp!ey6aXsC6;s18D> z7xKz}BBLOWoXQ72Iczpydbh^)X^6M4&WX=%arb?o3mREipbJK^K&ShHL0-KF zy+3DIaJ^S+;SBe}j@R*a*PGmU-N}Z{1Qd9A77FNsQ54YWx%Vmgn>j;)=Xtf4a`~r@ z;D&Q=O$N0th`%zJ!7CGR;O$vBpbJKEK&OY8!I65UoHu6hshr`!4en955?pM}brxEs zW~I@t)N5Ino9{?Kg7;@3fiC#`$RSau@6Zy#^;WIZ0iZ?hv`b&i8D887Nl_jfq0LiR zvEbp^R;QNvp6|aTAjU_t5JMM?YOQs;Z&!U85CfsC@}VBY1$6 za$}d8>W?L$#%HroLl=xP4xOI+8_#d&3^yjIOy~|X?71>TjL#)t%9pY*MHh@>icTNa z+$FCkjepA-rd$bDqOQL)*8Ix}YyP)cYyMT0;hKm`UHw$5Zdims#a= zu+(zZh^`&M@GbQqyzWo?7_s3?BY5Vf`<;G{fNG zd#YfR>g)8I^G^Y8&*%Ej!*H@|=cJdZb&gsx+>o{Ym#YlX`zD?4-#0xK9Ki2R-k88p z&QRcaCJB%Z9lEr9x+wt>Zp%W1TU3VYBBFp!_s@`5`J#aASl)2qu6*Evw_JJRh7BiA z7nTp>_o*LQtW>?NeSLlVTVAu(ci3I1H2YfhPP62d>a}*WUhSKh?sEs-Mzg-`mD>F8 z=4JPYD?F36`cUeK*W6sHudLK-eXV9mr1yy&l{#~)EPZaH(dtwAtiiC0EOdH#1>IWy zG@_X-*Vw_=pFcfouxFn@&pOLT@ZUY1<H5ZpjeAQ&dNi{O5O z2MHDlS_H2ocr(E}3EoTa0fOHl_zc0H5&R9oKM*{T54^4-xPhRTV3c5zU?0K51j__R z30_0+7J_#Z{3n7B5&Ry(=Lr6i;Ohk6Be;ymnyU#Mg3Sb53GODCCvXXB1SbeyPw+N^ z_YnLt!AA%_N$>@Nzb5!Qg8xf!1^3Kz2%b+cK(GVAdl>ln0nR4F(|V7de)ROIsW_^w zxRsg>)!Mi`Y4!06Wq+d#k*6t8Yx8`w7lzAYgX4?+LyPX%@bH#}h4J#x z@WPg|*T1FzG#mek%h33LCin=!Ckehl@Ye)?NAQ0Mu6Q!Qa|v!I7$(?Bu#eye391B- z5WJq?#|VCw;6D@mHo<2I{+!@%3BF5k*;4?XP0&NoPq2gFeu5VhECG1S??gYu4Bflh z8KNKBDujVUP4AEk zh2Wh8?f=vWl3GN{{Ku{)V61c)j1P_tjgNUF-eSKyIJ~%JY;n+C zEW7<S6IJTN@!4h_14V*{e&8@8j*&mp*lph)lo1osf^Cs-h;6TFPzjRZeV@E-_1K=4t5 z&l3Cv!Pg1?8NgfqH$4%D;^c^rwrm8u>0!gS+DmS&T%{PY%Ug6onUq2HJu-QxTRMEG z$slVnj9hdF`-jGd2Fu1B+ttFJtfCO7QOqexBgh2tG#ehXj8? z@HK+(5?p!V1eKm!J`DH3El(XEuTOy#VqgXewL+V5aoOEDIH!MTNo;B z85|u4{y;n!D=iL#vJHFS30ubHlzx_7`CkNoPw=kgB3YYA>87$CTlV20o&1Qmj#1g|A{E5T0_ z{4&9B5PXW@PYC`O!9Nf@={A6865LF%ncxQiyyf3U!(x7>SG(X81(TS)w=m!>4lE2W z4ve~`;qu6kH{dRejtne}_4k(tx5#;T5qnW1I7aYVf*&RLNrGP>_%OjI2);n@6@qUQ zY`7iZ=>#_rY$O;b*iA4`ut3ltcsao-f_D!I5;eDW+(gd zAVG@Rlb4*S?fWs*j;`X=t=uS}YHgz44*3k^bTS(#V$4h2c?eXmN0{ zJR}+S2ic21Aoyc~uMqq_!M_q*Spax0!EFRX1QP^%2_7amOmLjwbp&rC_!)xt6Z{sz z9}xT*!T%=sCxT0R0j?&vg`kgMJHdSf4-y^}W^8udK^_L&fx!VxacRUI!8REhDnUSktmJu8D!vhY zSso_XL2w_z0|XvHjo<{q>j~aQ@E(F+Cio44PZ9hH!IufXMeu!s%U=NS9D-X2HWG{z z>?U}KV42_;!J`E4BKUcN4-tHv;PV7uCipvoeW`L&=TuX2(!2rRX1TzFBf)#=z1V2ph7J{E7 z_(g)>CHMn^FA)4S!QT`78^P0x0JjosCKx4{BzPggL4p;6A0~J+!H*IA48i*eev9A_ z2>zPj8wCGI@T5L~XA;~@u$f?zV20oTL5bj{1g{`?Gr`Xg{35{z34WL04+;K~;2Q-0 zM(~t=fNKbDB^V&Mli-B}FD5ud&>{F?g0~R7m*D*bA13%T!5${*~a$0f6fW z?jRT;xSL=f!NUZH362xI4!~P}5!Os#8ntiT>d_8C?Dvj&C7>KsZb~?W&J>C57{>Jv zf}!_A8Y~TM85+d41)DDq4Y*_dLtBQ&M@I$5{VIF(X@V~he1+iK1RDkco=$KB!A62{ zg53o31PcTWf|nDVB6ugk`v^Wr@G*kV5qt^2TmAs57NfQ(EZPYt1pkUac&nzeA~w_! z9NC9UL&M|a?y$EwR32Rz=^yn5$A^c<#>Sw)5JUYwyK(gpz>NeiAlO21H^F{_62VIe zUP16?f}bGxj|9I)@OuQGA^04lI<1w+u@;;iuT2UnBS( zg3l2AIl>#+G;Kc+>1V;e8<)1_mF{=Mp+WiJ=QPpt(M@Wa_ zzgh|w0Zm}#$nH72XZMhxa>W2a35rGt_3WOrz(V&97ao%6SwgzWZDbwU zNH&va$*;&x@)|ir-X=p*Z~+-ZE+vBguFo$(7_fvWP4r ztI5~MedH1HBl2^yle|U_k+;d&lfZdoESX3qlWWNJWHD&~SH6iv|My^U*HP;TIiVAa zwzhH3r8>=Y;tGvEu0roFGd-QmbxiRXdCEt*2eun!o}WiL%<|>Etwb_JeVJO3<)PEs zS*b8lOOd1AQdwZD=G1SQ;sE&@`4<`VaWIlhATjA6v&aJA%ANQ@A9>N~AN|wXY}JoU z>~rZ{8&Q+9E1u^fOTsim=Oqo&Bug>IIofQg_N&a;0Z5jTspLBHSyGZ)fh)&AANh<9 zHhQYrTC_kUMS%?^YWO5@rZ7fEE=in?(Fbf+viCB<7V;CagX|)&lSAb1WWc+?a59=) zLMD;vWInN^o7_&mK^`FA2d>T_nTD@Q}0t;L4nop0aPwOei6L=Y4>&C;EOLo>X1Vz&^jb)6}PVo9B@eNmRtT z2ooy;d`7S<%^n=%8=%-COb0(VXTB zx~}dy-CeekpK@X?!yE^XnG`zC;~=qV=rHNUqL8UDwy3uJ{Rbn^Nb(^PkSSyqaOGLh zhitJq?(LGB#;PR~Ki$b+u=lS{U^ySuu6mK>2Uw2lq>7*Yy0;j$DvuOd}(?5}z^ zgRCVR$R_e6*$!N}0{U>1`~I1z!kw0eTMB`y11~M5jc{m(Ly$BHtVwY^QdJ&%`O1GK z$H_TEm|;&d!_|GyaC%i*8=6cxEfv;aDFoVNTI2W-m$C>F?7bpaZF$Dw)k{q}$Q-hW zl;jq&hO8%>$PdVK);L5Sk4`CbIiFv1P+G^=lhPgx(VU#(gaPi#UDGfn?tU@#d;#uG`dAhXE=vY0f;9b_H(HhGe4 zC%++k$pLbdoHZN_BOf3iBN4fhTuT;`C8R;VOzt5MktfKr?cP^zY$<4c|Z9m zK#76(CzHCi{RZ?}a|@Reg`!^F?CQZsk|iqQ_GUh0R$S6(U1@6!F+8Nu|RM12roe zcUo1m#6K8d__<&dnLr}o%Hz#EZA|RFHJfU=qO8C{kM!H@*aikQM2KY_wS-eq7F5mZ z1q`x++(y>=NY_I@L~ZQ+=QJ`r<8xv{ttBZE^j?w_Jv^?12@WUGKSv3vGu&Retdcy+ z*MH8x{^sWEw=oy`j+=G1bpo8LE0=^0&lxxwV#kWo73aVz!wMW?H2fm>dnS0(C-_}6 zK^w(Jv$fHQ#~^ZN=QcV!`HeuekuX9=SokF$MlwQ$GRM-Ug)NMZLKRoV#-Q_%i=@Rm96K6ERN7cby%9R zJhM4E_9jDmOQQT8A$E~Jl0TE9q~C>L2pL5tkeGA;S3ZrO@rhd;s{fB` z-RvIpdTW{h4NH|cA+0~as zkt`>x$yb3Z1L#Axv!Yd}6Vp!IuUF%%81Y0*?G}kp(qjXF#nxaGi)UUUo8<+Tet}&I ziUvQ6Kg=N8$PS-oQ!`B)i^-?DO((vFvzvcU0Ve8IrWOO$0M4UuaUJ$bE(=h`MH)v5 dqDVC!UNb&MV>A-nMZQHg16Qts9GS+Y{{hf{qQ(FK literal 0 HcmV?d00001 diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 0b1acc7e8..7bf2b8af8 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -48,6 +49,8 @@ import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskState; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; @@ -122,6 +125,13 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws if (task == null) { throw new TaskNotFoundError(); } + + // Check if task is in a non-cancelable state (completed, canceled, failed, rejected) + if (task.getStatus().state().isFinal()) { + throw new TaskNotCancelableError( + "Task cannot be canceled - current state: " + task.getStatus().state().asString()); + } + TaskManager taskManager = new TaskManager( task.getId(), task.getContextId(), @@ -148,11 +158,17 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws EventConsumer consumer = new EventConsumer(queue); EventKind type = resultAggregator.consumeAll(consumer); - if (type instanceof Task tempTask) { - return tempTask; + if (!(type instanceof Task tempTask)) { + throw new InternalError("Agent did not return valid response for cancel"); } - throw new InternalError("Agent did not return a valid response"); + // Verify task was actually canceled (not completed concurrently) + if (tempTask.getStatus().state() != TaskState.CANCELED) { + throw new TaskNotCancelableError( + "Task cannot be canceled - current state: " + tempTask.getStatus().state().asString()); + } + + return tempTask; } @Override @@ -166,32 +182,42 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte EventQueue queue = queueManager.createOrTap(taskId); ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); - boolean interrupted = false; + boolean blocking = true; // Default to blocking behavior + if (params.configuration() != null && Boolean.FALSE.equals(params.configuration().blocking())) { + blocking = false; + } + + boolean interruptedOrNonBlocking = false; EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId, mss.requestContext, queue); ResultAggregator.EventTypeAndInterrupt etai = null; try { + // Create callback for push notifications during background event processing + Runnable pushNotificationCallback = () -> sendPushNotification(taskId, resultAggregator); + EventConsumer consumer = new EventConsumer(queue); // This callback must be added before we start consuming. Otherwise, // any errors thrown by the producerRunnable are not picked up by the consumer producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); - etai = resultAggregator.consumeAndBreakOnInterrupt(consumer); + etai = resultAggregator.consumeAndBreakOnInterrupt(consumer, blocking, pushNotificationCallback); if (etai == null) { LOGGER.debug("No result, throwing InternalError"); throw new InternalError("No result"); } - interrupted = etai.interrupted(); - LOGGER.debug("Was interrupted: {}", interrupted); + interruptedOrNonBlocking = etai.interrupted(); + LOGGER.debug("Was interrupted or non-blocking: {}", interruptedOrNonBlocking); EventKind kind = etai.eventType(); if (kind instanceof Task taskResult && !taskId.equals(taskResult.getId())) { throw new InternalError("Task ID mismatch in agent response"); } + // Send push notification after initial return (for both blocking and non-blocking) + pushNotificationCallback.run(); } finally { - if (interrupted) { + if (interruptedOrNonBlocking) { CompletableFuture cleanupTask = CompletableFuture.runAsync(() -> cleanupProducer(taskId), executor); trackBackgroundTask(cleanupTask); } else { @@ -214,13 +240,15 @@ public Flow.Publisher onMessageSendStream( ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId.get(), mss.requestContext, queue); + + // Move consumer creation and callback registration outside try block + // so consumer is available for background consumption on client disconnect EventConsumer consumer = new EventConsumer(queue); + producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); - try { + AtomicBoolean backgroundConsumeStarted = new AtomicBoolean(false); - // This callback must be added before we start consuming. Otherwise, - // any errors thrown by the producerRunnable are not picked up by the consumer - producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); + try { Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); Flow.Publisher eventPublisher = @@ -258,7 +286,61 @@ public Flow.Publisher onMessageSendStream( return true; })); - return convertingProcessor(eventPublisher, event -> (StreamingEventKind) event); + Flow.Publisher finalPublisher = convertingProcessor(eventPublisher, event -> (StreamingEventKind) event); + + // Wrap publisher to detect client disconnect and continue background consumption + return subscriber -> finalPublisher.subscribe(new Flow.Subscriber() { + private Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + // Wrap subscription to detect cancellation + subscriber.onSubscribe(new Flow.Subscription() { + @Override + public void request(long n) { + subscription.request(n); + } + + @Override + public void cancel() { + LOGGER.debug("Client cancelled subscription for task {}, starting background consumption", taskId.get()); + startBackgroundConsumption(); + subscription.cancel(); + } + }); + } + + @Override + public void onNext(StreamingEventKind item) { + subscriber.onNext(item); + } + + @Override + public void onError(Throwable throwable) { + subscriber.onError(throwable); + } + + @Override + public void onComplete() { + subscriber.onComplete(); + } + + private void startBackgroundConsumption() { + if (backgroundConsumeStarted.compareAndSet(false, true)) { + // Client disconnected: continue consuming and persisting events in background + CompletableFuture bgTask = CompletableFuture.runAsync(() -> { + try { + resultAggregator.consumeAll(consumer); + LOGGER.debug("Background consumption completed for task {}", taskId.get()); + } catch (Exception e) { + LOGGER.error("Error during background consumption for task {}", taskId.get(), e); + } + }, executor); + trackBackgroundTask(bgTask); + } + } + }); } finally { CompletableFuture cleanupTask = CompletableFuture.runAsync(() -> cleanupProducer(taskId.get()), executor); trackBackgroundTask(cleanupTask); @@ -454,5 +536,14 @@ private MessageSendSetup initMessageSend(MessageSendParams params, ServerCallCon return new MessageSendSetup(taskManager, task, requestContext); } + private void sendPushNotification(String taskId, ResultAggregator resultAggregator) { + if (pushSender != null && taskId != null) { + EventKind latest = resultAggregator.getCurrentResult(); + if (latest instanceof Task latestTask) { + pushSender.sendNotification(latestTask); + } + } + } + private record MessageSendSetup(TaskManager taskManager, Task task, RequestContext requestContext) {} } diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index ad9ed9124..a30d60b01 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -45,7 +45,7 @@ public Flow.Publisher consumeAndEmit(EventConsumer consumer) { })); } - public EventKind consumeAll(EventConsumer consumer) { + public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { AtomicReference returnedEvent = new AtomicReference<>(); Flow.Publisher all = consumer.consumeAll(); AtomicReference error = new AtomicReference<>(); @@ -65,13 +65,22 @@ public EventKind consumeAll(EventConsumer consumer) { }, error::set); + Throwable err = error.get(); + if (err != null) { + Utils.rethrow(err); + } + if (returnedEvent.get() != null) { return returnedEvent.get(); } return taskManager.getTask(); } - public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer) throws JSONRPCError { + public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking) throws JSONRPCError { + return consumeAndBreakOnInterrupt(consumer, blocking, null); + } + + public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking, Runnable eventCallback) throws JSONRPCError { Flow.Publisher all = consumer.consumeAll(); AtomicReference message = new AtomicReference<>(); AtomicBoolean interrupted = new AtomicBoolean(false); @@ -92,16 +101,28 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer) callTaskManagerProcess(event); - if ((event instanceof Task task && task.getStatus().state() == TaskState.AUTH_REQUIRED) - || (event instanceof TaskStatusUpdateEvent tsue && tsue.getStatus().state() == TaskState.AUTH_REQUIRED)) { + boolean shouldInterrupt = false; + boolean isAuthRequired = (event instanceof Task task && task.getStatus().state() == TaskState.AUTH_REQUIRED) + || (event instanceof TaskStatusUpdateEvent tsue && tsue.getStatus().state() == TaskState.AUTH_REQUIRED); + + // Always interrupt on auth_required, as it needs external action. + if (isAuthRequired) { // auth-required is a special state: the message should be // escalated back to the caller, but the agent is expected to // continue producing events once the authorization is received // out-of-band. This is in contrast to input-required, where a // new request is expected in order for the agent to make progress, // so the agent should exit. + shouldInterrupt = true; + } + // For non-blocking calls, interrupt as soon as a task is available. + else if (!blocking) { + shouldInterrupt = true; + } - CompletableFuture.runAsync(() -> continueConsuming(all)); + if (shouldInterrupt) { + // Continue consuming the rest of the events in the background. + CompletableFuture.runAsync(() -> continueConsuming(all, eventCallback)); interrupted.set(true); return false; } @@ -118,11 +139,14 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer) message.get() != null ? message.get() : taskManager.getTask(), interrupted.get()); } - private void continueConsuming(Flow.Publisher all) { + private void continueConsuming(Flow.Publisher all, Runnable eventCallback) { consumer(createTubeConfig(), all, event -> { callTaskManagerProcess(event); + if (eventCallback != null) { + eventCallback.run(); + } return true; }, t -> {}); diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java similarity index 97% rename from server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java rename to server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 284465882..9951b7ba7 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerBackgroundTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -28,10 +28,13 @@ import org.junit.jupiter.api.Timeout; /** - * Tests for DefaultRequestHandler background cleanup and task tracking functionality, - * backported from Python PR #440 and #472. + * Comprehensive tests for DefaultRequestHandler, backported from Python's + * test_default_request_handler.py. These tests cover core functionality that + * is transport-agnostic and should work across JSON-RPC, gRPC, and REST. + * + * Background cleanup and task tracking tests are from Python PR #440 and #472. */ -public class DefaultRequestHandlerBackgroundTest { +public class DefaultRequestHandlerTest { private DefaultRequestHandler requestHandler; private InMemoryTaskStore taskStore; diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java index e4a80d71e..55b3b7160 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -10,6 +11,9 @@ import java.util.Collections; +import io.a2a.server.events.EventConsumer; +import io.a2a.server.events.EventQueue; +import io.a2a.server.events.InMemoryQueueManager; import io.a2a.spec.EventKind; import io.a2a.spec.Message; import io.a2a.spec.Task; @@ -183,4 +187,35 @@ void testGetCurrentResultWithMessageTakesPrecedence() { // Task manager should not be called when message is present verifyNoInteractions(mockTaskManager); } + + @Test + void testConsumeAndBreakNonBlocking() throws Exception { + // Test that with blocking=false, the method returns after the first event + Task firstEvent = createSampleTask("non_blocking_task", TaskState.WORKING, "ctx1"); + + // After processing firstEvent, the current result will be that task + when(mockTaskManager.getTask()).thenReturn(firstEvent); + + // Create an event queue using QueueManager (which has access to builder) + InMemoryQueueManager queueManager = + new InMemoryQueueManager(); + + EventQueue queue = queueManager.getEventQueueBuilder("test-task").build(); + queue.enqueueEvent(firstEvent); + + // Create real EventConsumer with the queue + EventConsumer eventConsumer = + new EventConsumer(queue); + + // Close queue after first event to simulate stream ending after processing + queue.close(); + + ResultAggregator.EventTypeAndInterrupt result = + aggregator.consumeAndBreakOnInterrupt(eventConsumer, false); + + assertEquals(firstEvent, result.eventType()); + assertTrue(result.interrupted()); + verify(mockTaskManager).process(firstEvent); + verify(mockTaskManager).getTask(); + } } diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index 54a614fc7..9c11a074b 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -11,7 +11,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record MessageSendConfiguration(List acceptedOutputModes, Integer historyLength, - PushNotificationConfig pushNotificationConfig, boolean blocking) { + PushNotificationConfig pushNotificationConfig, Boolean blocking) { public MessageSendConfiguration { if (historyLength != null && historyLength < 0) { @@ -23,7 +23,7 @@ public static class Builder { List acceptedOutputModes; Integer historyLength; PushNotificationConfig pushNotificationConfig; - boolean blocking; + Boolean blocking = true; public Builder acceptedOutputModes(List acceptedOutputModes) { this.acceptedOutputModes = acceptedOutputModes; @@ -40,7 +40,7 @@ public Builder historyLength(Integer historyLength) { return this; } - public Builder blocking(boolean blocking) { + public Builder blocking(Boolean blocking) { this.blocking = blocking; return this; } diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index eb8668140..50d32c385 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -31,4 +31,8 @@ public TaskNotCancelableError( data); } + public TaskNotCancelableError(@JsonProperty("message") String message) { + this(null, message, null); + } + } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index b37724e56..7da45eb14 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1131,10 +1131,14 @@ public void testOnMessageSendErrorHandling() { try (MockedConstruction mocked = Mockito.mockConstruction( ResultAggregator.class, - (mock, context) -> + (mock, context) -> { Mockito.doThrow( new UnsupportedOperationError()) - .when(mock).consumeAndBreakOnInterrupt(Mockito.any(EventConsumer.class)))){ + .when(mock).consumeAndBreakOnInterrupt( + Mockito.any(EventConsumer.class), + Mockito.anyBoolean(), + Mockito.any()); + })){ response = handler.onMessageSend(request, callContext); } @@ -1470,5 +1474,4 @@ public void onComplete() { Assertions.assertTrue(eventReceived.get(), "Should have received streaming event"); Assertions.assertFalse(mainThreadBlocked.get(), "Main thread should not have been blocked"); } - } From 1af768a2ce12601e96388e624db67b3d7f33f132 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 7 Oct 2025 11:44:07 -0400 Subject: [PATCH 151/493] chore: Release 0.3.0.Beta2 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 30 files changed, 31 insertions(+), 31 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index c7cfa51cc..94dff44e9 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index fe8b6d9e1..cd1503521 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index e9a899ee5..195e92e92 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 3741a8624..f62674049 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index dca7aca3a..b044ae54a 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index b5af50e25..009d4e70b 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 5c90e2868..5f7c0f3b3 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 4349ca9fa..fcbf5340f 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta2-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta2-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta2 +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta2 //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 8a50379f4..bd80caf75 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 97ff6e561..ee8361822 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-examples-server diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 40fbec72c..85e5d01f4 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 5d2742f9f..7b154610b 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index 69c859762..7b391ca10 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index aa17435a9..158e8cb9d 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../pom.xml diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests/pom.xml index f8249dc58..8a3b6f06b 100644 --- a/extras/queue-manager-replicated/tests/pom.xml +++ b/extras/queue-manager-replicated/tests/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index 8745db013..335a3fd72 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index 0e86ac38a..f4a455ee2 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index 13171cfaf..a02ced4dc 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 6e0dcce27..7627d5023 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index aab281760..e0b3dfbee 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index c06bc6830..764d87a97 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 2df2a4037..59d1828e3 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index 0d6c0df3d..91bdbd20b 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 636656536..96e9e5c01 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index 872ad0cda..5b32173bc 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index a13e10125..dbf40e1d1 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index dd094c4a2..f9b853a77 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 97025bed5..755bc5b4e 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 987fcb53c..525e480b8 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 562c1b4bd..43953499d 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2-SNAPSHOT + 0.3.0.Beta2 ../../pom.xml a2a-java-sdk-transport-rest From d6fef7e0952d7916f600ac6e6c5ab3fb5fe1d680 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 7 Oct 2025 11:45:27 -0400 Subject: [PATCH 152/493] chore: Next is 0.3.0.Beta3 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 30 files changed, 31 insertions(+), 31 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 94dff44e9..14264c5f1 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index cd1503521..763740c62 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 195e92e92..6196dc520 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index f62674049..9daf6bcd1 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index b044ae54a..453a66bca 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 009d4e70b..2d8744088 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-common diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 5f7c0f3b3..42c50d78d 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index fcbf5340f..4a8ecd6dc 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta2 -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta2 +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta3-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta3-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index bd80caf75..97505f7ea 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index ee8361822..0929a907e 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-examples-server diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 85e5d01f4..b0fd7e9b5 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 7b154610b..ca50ecafc 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index 7b391ca10..4aceaeb0e 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 158e8cb9d..c6219856c 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests/pom.xml index 8a3b6f06b..16f56c723 100644 --- a/extras/queue-manager-replicated/tests/pom.xml +++ b/extras/queue-manager-replicated/tests/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index 335a3fd72..6887a4582 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index f4a455ee2..4e138b09b 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index a02ced4dc..3079927ad 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 7627d5023..4ccccdc4d 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index e0b3dfbee..4e9a6f335 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 764d87a97..4059447a9 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 59d1828e3..3a7964560 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index 91bdbd20b..5a5a4f368 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 96e9e5c01..64049c46f 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index 5b32173bc..f699612a2 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index dbf40e1d1..c33cf6e8b 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index f9b853a77..bdd5c1700 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 755bc5b4e..bfa98f3d7 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 525e480b8..638eaebca 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 43953499d..ae86cbac5 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta2 + 0.3.0.Beta3-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-rest From 71c56eacc6aa8a1892980183129a1273cf923bc4 Mon Sep 17 00:00:00 2001 From: Siddheya Kulkarni <115717746+Asymtode712@users.noreply.github.com> Date: Tue, 7 Oct 2025 21:56:46 +0530 Subject: [PATCH 153/493] feat: Add message creation methods with contextId and taskId support (#322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## PR Description - Added new message creation methods to A2A.java (createUserMessage, createAgentMessage, createMessage) - Implemented support for configuring contextId, taskId, parts, metadata, and referenceTaskIds - Added comprehensive test cases in A2ATest.java to verify functionality - Methods follow similar pattern to the Python implementation for consistency across SDKs - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Ensure the tests pass Fixes #302 🦕 --- client/base/src/main/java/io/a2a/A2A.java | 61 +++++++- client/base/src/test/java/io/a2a/A2ATest.java | 147 ++++++++++++++++++ 2 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 client/base/src/test/java/io/a2a/A2ATest.java diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index f72945677..063527c23 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -1,7 +1,9 @@ package io.a2a; import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.UUID; import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; @@ -10,6 +12,7 @@ import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; +import io.a2a.spec.Part; import io.a2a.spec.TextPart; @@ -60,11 +63,67 @@ public static Message toAgentMessage(String text, String messageId) { return toMessage(text, Message.Role.AGENT, messageId); } + /** + * Create a user message with text content and optional context and task IDs. + * + * @param text the message text (required) + * @param contextId the context ID to use (optional) + * @param taskId the task ID to use (optional) + * @return the user message + */ + public static Message createUserTextMessage(String text, String contextId, String taskId) { + return toMessage(text, Message.Role.USER, null, contextId, taskId); + } + + /** + * Create an agent message with text content and optional context and task IDs. + * + * @param text the message text (required) + * @param contextId the context ID to use (optional) + * @param taskId the task ID to use (optional) + * @return the agent message + */ + public static Message createAgentTextMessage(String text, String contextId, String taskId) { + return toMessage(text, Message.Role.AGENT, null, contextId, taskId); + } + + /** + * Create an agent message with custom parts and optional context and task IDs. + * + * @param parts the message parts (required) + * @param contextId the context ID to use (optional) + * @param taskId the task ID to use (optional) + * @return the agent message + */ + public static Message createAgentPartsMessage(List> parts, String contextId, String taskId) { + if (parts == null || parts.isEmpty()) { + throw new IllegalArgumentException("Parts cannot be null or empty"); + } + return toMessage(parts, Message.Role.AGENT, null, contextId, taskId); + } private static Message toMessage(String text, Message.Role role, String messageId) { + return toMessage(text, role, messageId, null, null); + } + + private static Message toMessage(String text, Message.Role role, String messageId, String contextId, String taskId) { + Message.Builder messageBuilder = new Message.Builder() + .role(role) + .parts(Collections.singletonList(new TextPart(text))) + .contextId(contextId) + .taskId(taskId); + if (messageId != null) { + messageBuilder.messageId(messageId); + } + return messageBuilder.build(); + } + + private static Message toMessage(List> parts, Message.Role role, String messageId, String contextId, String taskId) { Message.Builder messageBuilder = new Message.Builder() .role(role) - .parts(Collections.singletonList(new TextPart(text))); + .parts(parts) + .contextId(contextId) + .taskId(taskId); if (messageId != null) { messageBuilder.messageId(messageId); } diff --git a/client/base/src/test/java/io/a2a/A2ATest.java b/client/base/src/test/java/io/a2a/A2ATest.java new file mode 100644 index 000000000..f3c2f3a77 --- /dev/null +++ b/client/base/src/test/java/io/a2a/A2ATest.java @@ -0,0 +1,147 @@ +package io.a2a; + +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class A2ATest { + + @Test + public void testToUserMessage() { + String text = "Hello, world!"; + Message message = A2A.toUserMessage(text); + + assertEquals(Message.Role.USER, message.getRole()); + assertEquals(1, message.getParts().size()); + assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); + assertNotNull(message.getMessageId()); + assertNull(message.getContextId()); + assertNull(message.getTaskId()); + } + + @Test + public void testToUserMessageWithId() { + String text = "Hello, world!"; + String messageId = "test-message-id"; + Message message = A2A.toUserMessage(text, messageId); + + assertEquals(Message.Role.USER, message.getRole()); + assertEquals(messageId, message.getMessageId()); + } + + @Test + public void testToAgentMessage() { + String text = "Hello, I'm an agent!"; + Message message = A2A.toAgentMessage(text); + + assertEquals(Message.Role.AGENT, message.getRole()); + assertEquals(1, message.getParts().size()); + assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); + assertNotNull(message.getMessageId()); + } + + @Test + public void testToAgentMessageWithId() { + String text = "Hello, I'm an agent!"; + String messageId = "agent-message-id"; + Message message = A2A.toAgentMessage(text, messageId); + + assertEquals(Message.Role.AGENT, message.getRole()); + assertEquals(messageId, message.getMessageId()); + } + + @Test + public void testCreateUserTextMessage() { + String text = "User message with context"; + String contextId = "context-123"; + String taskId = "task-456"; + + Message message = A2A.createUserTextMessage(text, contextId, taskId); + + assertEquals(Message.Role.USER, message.getRole()); + assertEquals(contextId, message.getContextId()); + assertEquals(taskId, message.getTaskId()); + assertEquals(1, message.getParts().size()); + assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); + assertNotNull(message.getMessageId()); + assertNull(message.getMetadata()); + assertNull(message.getReferenceTaskIds()); + } + + @Test + public void testCreateUserTextMessageWithNullParams() { + String text = "Simple user message"; + + Message message = A2A.createUserTextMessage(text, null, null); + + assertEquals(Message.Role.USER, message.getRole()); + assertNull(message.getContextId()); + assertNull(message.getTaskId()); + assertEquals(1, message.getParts().size()); + assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); + } + + @Test + public void testCreateAgentTextMessage() { + String text = "Agent message with context"; + String contextId = "context-789"; + String taskId = "task-012"; + + Message message = A2A.createAgentTextMessage(text, contextId, taskId); + + assertEquals(Message.Role.AGENT, message.getRole()); + assertEquals(contextId, message.getContextId()); + assertEquals(taskId, message.getTaskId()); + assertEquals(1, message.getParts().size()); + assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); + assertNotNull(message.getMessageId()); + } + + @Test + public void testCreateAgentPartsMessage() { + List> parts = Arrays.asList( + new TextPart("Part 1"), + new TextPart("Part 2") + ); + String contextId = "context-parts"; + String taskId = "task-parts"; + + Message message = A2A.createAgentPartsMessage(parts, contextId, taskId); + + assertEquals(Message.Role.AGENT, message.getRole()); + assertEquals(contextId, message.getContextId()); + assertEquals(taskId, message.getTaskId()); + assertEquals(2, message.getParts().size()); + assertEquals("Part 1", ((TextPart) message.getParts().get(0)).getText()); + assertEquals("Part 2", ((TextPart) message.getParts().get(1)).getText()); + } + + @Test + public void testCreateAgentPartsMessageWithNullParts() { + try { + A2A.createAgentPartsMessage(null, "context", "task"); + org.junit.jupiter.api.Assertions.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Parts cannot be null or empty", e.getMessage()); + } + } + + @Test + public void testCreateAgentPartsMessageWithEmptyParts() { + try { + A2A.createAgentPartsMessage(Collections.emptyList(), "context", "task"); + org.junit.jupiter.api.Assertions.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Parts cannot be null or empty", e.getMessage()); + } + } +} \ No newline at end of file From 59a5f680f433d0d045fae26324ff4e77359dbe2a Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 8 Oct 2025 13:47:34 +0200 Subject: [PATCH 154/493] fix: Fixing build on Java 25. (#329) Tests are failing on Java 25 because of a classloading issue if uwing vertx json codec instead of Jackson object mapper. Signed-off-by: Emmanuel Hugonnet --- .github/workflows/build-and-test.yml | 2 +- .../pom.xml | 27 ------------------- ...otificationConfigStoreIntegrationTest.java | 1 - pom.xml | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 17 +++++------- .../spec/SendStreamingMessageResponse.java | 2 -- spec/src/main/java/io/a2a/util/Utils.java | 9 ++++++- 7 files changed, 17 insertions(+), 43 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 0a5050ed2..6d565ec44 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java-version: ['17', '21'] + java-version: ['17', '21', '25'] steps: - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java-version }} diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index b0fd7e9b5..0cd78769e 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -104,31 +104,4 @@ test - - - - java24-exclude-vertx-incompatible-tests - - [24,) - - - - - maven-surefire-plugin - - --add-opens java.base/java.lang=ALL-UNNAMED - - - **/JpaDatabasePushNotificationConfigStoreIntegrationTest.java - - - - - - - - - diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index c8f577c9a..58681cf44 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -23,7 +23,6 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; diff --git a/pom.xml b/pom.xml index 3079927ad..4097cab8a 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 1.7.1 4.31.1 0.6.1 - 3.28.1 + 3.28.2 5.5.1 2.0.17 1.5.18 diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index af1ec55c2..ae9bb817b 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -64,7 +64,6 @@ import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.json.Json; import io.vertx.ext.web.RoutingContext; @Singleton @@ -111,19 +110,19 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { rc.response() .setStatusCode(200) .putHeader(CONTENT_TYPE, APPLICATION_JSON) - .end(Json.encodeToBuffer(error)); + .end(Utils.toJsonString(error)); } else if (streaming) { final Multi> finalStreamingResponse = streamingResponse; executor.execute(() -> { - MultiSseSupport.subscribeObject( - finalStreamingResponse.map(i -> (Object)i), rc); + MultiSseSupport.subscribeObject( + finalStreamingResponse.map(i -> (Object) i), rc); }); } else { rc.response() .setStatusCode(200) .putHeader(CONTENT_TYPE, APPLICATION_JSON) - .end(Json.encodeToBuffer(nonStreamingResponse)); + .end(Utils.toJsonString(nonStreamingResponse)); } } } @@ -151,7 +150,7 @@ private JSONRPCErrorResponse handleError(JsonProcessingException exception) { } /** - /** + * /** * Handles incoming GET requests to the agent card endpoint. * Returns the agent card in JSON format. * @@ -331,10 +330,9 @@ public Buffer apply(Object o) { ReactiveRoutes.ServerSentEvent ev = (ReactiveRoutes.ServerSentEvent) o; long id = ev.id() != -1 ? ev.id() : count.getAndIncrement(); String e = ev.event() == null ? "" : "event: " + ev.event() + "\n"; - return Buffer.buffer(e + "data: " + Json.encodeToBuffer(ev.data()) + "\nid: " + id + "\n\n"); - } else { - return Buffer.buffer("data: " + Json.encodeToBuffer(o) + "\nid: " + count.getAndIncrement() + "\n\n"); + return Buffer.buffer(e + "data: " + Utils.toJsonString(ev.data()) + "\nid: " + id + "\n\n"); } + return Buffer.buffer("data: " + Utils.toJsonString(o) + "\nid: " + count.getAndIncrement() + "\n\n"); } }), rc); } @@ -350,4 +348,3 @@ private static void endOfStream(HttpServerResponse response) { } } } - diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java index f3bcb9676..0ed6ca80d 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java @@ -1,12 +1,10 @@ package io.a2a.spec; -import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import io.a2a.util.Assert; /** * The response after receiving a request to initiate a task with streaming. diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index ec702130a..07143ab6a 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -94,5 +94,12 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event } - + public static String toJsonString(Object o) { + try { + return OBJECT_MAPPER.writeValueAsString(o); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + } From ea28267c8ac6c8508997f654f02d8559600b4084 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Thu, 9 Oct 2025 11:34:57 -0400 Subject: [PATCH 155/493] fix: Ensure proper errors are reported when authentication fails (#328) I can't reproduce the failures mentioned in https://github.com/a2aproject/a2a-java/pull/325 so attempting to open the PR again to see if it fails on CI. Fixes https://github.com/a2aproject/a2a-java/issues/317 --- client/base/pom.xml | 15 + .../AuthenticationAuthorizationTest.java | 380 ++++++++++++++++++ .../transport/grpc/GrpcErrorMapper.java | 5 + .../java/io/a2a/common/A2AErrorMessages.java | 11 + .../io/a2a/client/http/JdkA2AHttpClient.java | 88 +++- .../transport/grpc/handler/GrpcHandler.java | 47 ++- 6 files changed, 537 insertions(+), 9 deletions(-) create mode 100644 client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java create mode 100644 common/src/main/java/io/a2a/common/A2AErrorMessages.java diff --git a/client/base/pom.xml b/client/base/pom.xml index 14264c5f1..7dcf9eca6 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -48,6 +48,11 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-spec-grpc + test + org.junit.jupiter junit-jupiter-api @@ -64,6 +69,16 @@ slf4j-jdk14 test + + io.grpc + grpc-testing + test + + + io.grpc + grpc-inprocess + test + \ No newline at end of file diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java new file mode 100644 index 000000000..295205748 --- /dev/null +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -0,0 +1,380 @@ +package io.a2a.client; + +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.grpc.GrpcTransport; +import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.client.transport.rest.RestTransport; +import io.a2a.client.transport.rest.RestTransportConfigBuilder; +import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.SendMessageRequest; +import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.AgentSkill; +import io.a2a.spec.Message; +import io.a2a.spec.TextPart; +import io.a2a.spec.TransportProtocol; +import io.grpc.ManagedChannel; +import io.grpc.Server; +import io.grpc.Status; +import io.grpc.inprocess.InProcessChannelBuilder; +import io.grpc.inprocess.InProcessServerBuilder; +import io.grpc.stub.StreamObserver; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +/** + * Tests for handling HTTP 401 (Unauthorized) and 403 (Forbidden) responses + * when the client sends streaming and non-streaming messages. + * + * These tests verify that the client properly fails when the server returns + * authentication or authorization errors. + */ +public class AuthenticationAuthorizationTest { + + private static final String AGENT_URL = "http://localhost:4001"; + private static final String AUTHENTICATION_FAILED_MESSAGE = "Authentication failed"; + private static final String AUTHORIZATION_FAILED_MESSAGE = "Authorization failed"; + + private ClientAndServer server; + private Message MESSAGE; + private AgentCard agentCard; + private Server grpcServer; + private ManagedChannel grpcChannel; + private String grpcServerName; + + @BeforeEach + public void setUp() { + server = new ClientAndServer(4001); + MESSAGE = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("test message"))) + .contextId("context-1234") + .messageId("message-1234") + .build(); + + grpcServerName = InProcessServerBuilder.generateName(); + + agentCard = new AgentCard.Builder() + .name("Test Agent") + .description("Test agent for auth tests") + .url(AGENT_URL) + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) // Support streaming for all tests + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("test_skill") + .name("Test skill") + .description("Test skill") + .tags(Collections.singletonList("test")) + .build())) + .protocolVersion("0.3.0") + .additionalInterfaces(java.util.Arrays.asList( + new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL), + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL), + new AgentInterface(TransportProtocol.GRPC.asString(), grpcServerName))) + .build(); + } + + @AfterEach + public void tearDown() { + server.stop(); + if (grpcChannel != null) { + grpcChannel.shutdownNow(); + } + if (grpcServer != null) { + grpcServer.shutdownNow(); + } + } + + // ========== JSON-RPC Transport Tests ========== + + @Test + public void testJsonRpcNonStreamingUnauthenticated() throws A2AClientException { + // Mock server to return 401 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(401) + ); + + Client client = getJSONRPCClientBuilder(false).build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); + } + + @Test + public void testJsonRpcNonStreamingUnauthorized() throws A2AClientException { + // Mock server to return 403 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(403) + ); + + Client client = getJSONRPCClientBuilder(false).build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); + } + + @Test + public void testJsonRpcStreamingUnauthenticated() throws Exception { + // Mock server to return 401 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(401) + ); + + assertStreamingError( + getJSONRPCClientBuilder(true), + AUTHENTICATION_FAILED_MESSAGE); + } + + @Test + public void testJsonRpcStreamingUnauthorized() throws Exception { + // Mock server to return 403 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/") + ).respond( + response() + .withStatusCode(403) + ); + + assertStreamingError( + getJSONRPCClientBuilder(true), + AUTHORIZATION_FAILED_MESSAGE); + } + + // ========== REST Transport Tests ========== + + @Test + public void testRestNonStreamingUnauthenticated() throws A2AClientException { + // Mock server to return 401 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:send") + ).respond( + response() + .withStatusCode(401) + ); + + Client client = getRestClientBuilder(false).build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); + } + + @Test + public void testRestNonStreamingUnauthorized() throws A2AClientException { + // Mock server to return 403 for non-streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:send") + ).respond( + response() + .withStatusCode(403) + ); + + Client client = getRestClientBuilder(false).build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); + } + + @Test + public void testRestStreamingUnauthenticated() throws Exception { + // Mock server to return 401 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:stream") + ).respond( + response() + .withStatusCode(401) + ); + + assertStreamingError( + getRestClientBuilder(true), + AUTHENTICATION_FAILED_MESSAGE); + } + + @Test + public void testRestStreamingUnauthorized() throws Exception { + // Mock server to return 403 for streaming message + server.when( + request() + .withMethod("POST") + .withPath("/v1/message:stream") + ).respond( + response() + .withStatusCode(403) + ); + + assertStreamingError( + getRestClientBuilder(true), + AUTHORIZATION_FAILED_MESSAGE); + } + + // ========== gRPC Transport Tests ========== + + @Test + public void testGrpcNonStreamingUnauthenticated() throws Exception { + setupGrpcServer(Status.UNAUTHENTICATED); + + Client client = getGrpcClientBuilder(false).build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHENTICATION_FAILED_MESSAGE)); + } + + @Test + public void testGrpcNonStreamingUnauthorized() throws Exception { + setupGrpcServer(Status.PERMISSION_DENIED); + + Client client = getGrpcClientBuilder(false).build(); + + A2AClientException exception = assertThrows(A2AClientException.class, () -> { + client.sendMessage(MESSAGE); + }); + + assertTrue(exception.getMessage().contains(AUTHORIZATION_FAILED_MESSAGE)); + } + + @Test + public void testGrpcStreamingUnauthenticated() throws Exception { + setupGrpcServer(Status.UNAUTHENTICATED); + + assertStreamingError( + getGrpcClientBuilder(true), + AUTHENTICATION_FAILED_MESSAGE); + } + + @Test + public void testGrpcStreamingUnauthorized() throws Exception { + setupGrpcServer(Status.PERMISSION_DENIED); + + assertStreamingError( + getGrpcClientBuilder(true), + AUTHORIZATION_FAILED_MESSAGE); + } + + private ClientBuilder getJSONRPCClientBuilder(boolean streaming) { + return Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(streaming).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()); + } + + private ClientBuilder getRestClientBuilder(boolean streaming) { + return Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(streaming).build()) + .withTransport(RestTransport.class, new RestTransportConfigBuilder()); + } + + private ClientBuilder getGrpcClientBuilder(boolean streaming) { + return Client.builder(agentCard) + .clientConfig(new ClientConfig.Builder().setStreaming(streaming).build()) + .withTransport(GrpcTransport.class, new GrpcTransportConfigBuilder() + .channelFactory(target -> grpcChannel)); + } + + private void assertStreamingError(ClientBuilder clientBuilder, String expectedErrorMessage) throws Exception { + AtomicReference errorRef = new AtomicReference<>(); + CountDownLatch errorLatch = new CountDownLatch(1); + + Consumer errorHandler = error -> { + errorRef.set(error); + errorLatch.countDown(); + }; + + Client client = clientBuilder.streamingErrorHandler(errorHandler).build(); + + try { + client.sendMessage(MESSAGE); + // If no immediate exception, wait for async error + assertTrue(errorLatch.await(5, TimeUnit.SECONDS), "Expected error handler to be called"); + Throwable error = errorRef.get(); + assertTrue(error.getMessage().contains(expectedErrorMessage), + "Expected error message to contain '" + expectedErrorMessage + "' but got: " + error.getMessage()); + } catch (Exception e) { + // Immediate exception is also acceptable + assertTrue(e.getMessage().contains(expectedErrorMessage), + "Expected error message to contain '" + expectedErrorMessage + "' but got: " + e.getMessage()); + } + } + + private void setupGrpcServer(Status status) throws IOException { + grpcServerName = InProcessServerBuilder.generateName(); + grpcServer = InProcessServerBuilder.forName(grpcServerName) + .directExecutor() + .addService(new A2AServiceGrpc.A2AServiceImplBase() { + @Override + public void sendMessage(SendMessageRequest request, StreamObserver responseObserver) { + responseObserver.onError(status.asRuntimeException()); + } + + @Override + public void sendStreamingMessage(SendMessageRequest request, StreamObserver responseObserver) { + responseObserver.onError(status.asRuntimeException()); + } + }) + .build() + .start(); + + grpcChannel = InProcessChannelBuilder.forName(grpcServerName) + .directExecutor() + .build(); + } +} \ No newline at end of file diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java index 7340f7cef..5f0db8f0f 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java @@ -1,5 +1,6 @@ package io.a2a.client.transport.grpc; +import io.a2a.common.A2AErrorMessages; import io.a2a.spec.A2AClientException; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.InvalidAgentResponseError; @@ -64,6 +65,10 @@ public static A2AClientException mapGrpcError(StatusRuntimeException e, String e return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new InvalidParamsError()); case INTERNAL: return new A2AClientException(errorPrefix + (description != null ? description : e.getMessage()), new io.a2a.spec.InternalError(null, e.getMessage(), null)); + case UNAUTHENTICATED: + return new A2AClientException(errorPrefix + A2AErrorMessages.AUTHENTICATION_FAILED); + case PERMISSION_DENIED: + return new A2AClientException(errorPrefix + A2AErrorMessages.AUTHORIZATION_FAILED); default: return new A2AClientException(errorPrefix + e.getMessage(), e); } diff --git a/common/src/main/java/io/a2a/common/A2AErrorMessages.java b/common/src/main/java/io/a2a/common/A2AErrorMessages.java new file mode 100644 index 000000000..22b587d84 --- /dev/null +++ b/common/src/main/java/io/a2a/common/A2AErrorMessages.java @@ -0,0 +1,11 @@ +package io.a2a.common; + +public final class A2AErrorMessages { + + private A2AErrorMessages() { + // prevent instantiation + } + + public static final String AUTHENTICATION_FAILED = "Authentication failed: Client credentials are missing or invalid"; + public static final String AUTHORIZATION_FAILED = "Authorization failed: Client does not have permission for the operation"; +} diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index abcecc8ed..8cd0089d4 100644 --- a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -1,19 +1,31 @@ package io.a2a.client.http; +import static java.net.HttpURLConnection.HTTP_FORBIDDEN; +import static java.net.HttpURLConnection.HTTP_MULT_CHOICE; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + import java.io.IOException; +import java.net.HttpURLConnection; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.BodyHandlers; +import java.net.http.HttpResponse.BodySubscribers; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import java.util.function.Consumer; +import io.a2a.common.A2AErrorMessages; +import io.a2a.spec.A2AClientException; + public class JdkA2AHttpClient implements A2AHttpClient { private final HttpClient httpClient; @@ -88,6 +100,7 @@ protected CompletableFuture asyncRequest( ) { Flow.Subscriber subscriber = new Flow.Subscriber() { private Flow.Subscription subscription; + private volatile boolean errorRaised = false; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -109,24 +122,72 @@ public void onNext(String item) { @Override public void onError(Throwable throwable) { - errorConsumer.accept(throwable); - subscription.cancel(); + if (!errorRaised) { + errorRaised = true; + errorConsumer.accept(throwable); + } + if (subscription != null) { + subscription.cancel(); + } } @Override public void onComplete() { - completeRunnable.run(); - subscription.cancel(); + if (!errorRaised) { + completeRunnable.run(); + } + if (subscription != null) { + subscription.cancel(); + } } }; - BodyHandler bodyHandler = BodyHandlers.fromLineSubscriber(subscriber); + // Create a custom body handler that checks status before processing body + BodyHandler bodyHandler = responseInfo -> { + // Check for authentication/authorization errors only + if (responseInfo.statusCode() == HTTP_UNAUTHORIZED || responseInfo.statusCode() == HTTP_FORBIDDEN) { + final String errorMessage; + if (responseInfo.statusCode() == HTTP_UNAUTHORIZED) { + errorMessage = A2AErrorMessages.AUTHENTICATION_FAILED; + } else { + errorMessage = A2AErrorMessages.AUTHORIZATION_FAILED; + } + // Return a body subscriber that immediately signals error + return BodySubscribers.fromSubscriber(new Flow.Subscriber>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscriber.onError(new IOException(errorMessage)); + } + + @Override + public void onNext(List item) { + // Should not be called + } + + @Override + public void onError(Throwable throwable) { + // Should not be called + } + + @Override + public void onComplete() { + // Should not be called + } + }); + } else { + // For all other status codes (including other errors), proceed with normal line subscriber + return BodyHandlers.fromLineSubscriber(subscriber).apply(responseInfo); + } + }; // Send the response async, and let the subscriber handle the lines. return httpClient.sendAsync(request, bodyHandler) .thenAccept(response -> { - if (!JdkHttpResponse.success(response)) { - subscriber.onError(new IOException("Request failed " + response.statusCode())); + // Handle non-authentication/non-authorization errors here + if (!isSuccessStatus(response.statusCode()) && + response.statusCode() != HTTP_UNAUTHORIZED && + response.statusCode() != HTTP_FORBIDDEN) { + subscriber.onError(new IOException("Request failed with status " + response.statusCode() + ":" + response.body())); } }); } @@ -200,6 +261,13 @@ public A2AHttpResponse post() throws IOException, InterruptedException { .build(); HttpResponse response = httpClient.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); + + if (response.statusCode() == HTTP_UNAUTHORIZED) { + throw new IOException(A2AErrorMessages.AUTHENTICATION_FAILED); + } else if (response.statusCode() == HTTP_FORBIDDEN) { + throw new IOException(A2AErrorMessages.AUTHORIZATION_FAILED); + } + return new JdkHttpResponse(response); } @@ -227,7 +295,7 @@ public boolean success() {// Send the request and get the response } static boolean success(HttpResponse response) { - return response.statusCode() >= 200 && response.statusCode() < 300; + return response.statusCode() >= HTTP_OK && response.statusCode() < HTTP_MULT_CHOICE; } @Override @@ -235,4 +303,8 @@ public String body() { return response.body(); } } + + private static boolean isSuccessStatus(int statusCode) { + return statusCode >= HTTP_OK && statusCode < HTTP_MULT_CHOICE; + } } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index b259e91ab..57dc5ae58 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -18,6 +18,7 @@ import java.util.logging.Logger; import com.google.protobuf.Empty; +import io.a2a.common.A2AErrorMessages; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.StreamResponse; import io.a2a.server.AgentCardValidator; @@ -80,6 +81,8 @@ public void sendMessage(io.a2a.grpc.SendMessageRequest request, responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -100,6 +103,8 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, } } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -120,6 +125,8 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, } } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -141,6 +148,8 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -162,6 +171,8 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -179,7 +190,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC ServerCallContext context = createCallContext(responseObserver); ListTaskPushNotificationConfigParams params = FromProto.listTaskPushNotificationConfigParams(request); List configList = getRequestHandler().onListTaskPushNotificationConfig(params, context); - io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); for (TaskPushNotificationConfig config : configList) { responseBuilder.addConfigs(ToProto.taskPushNotificationConfig(config)); @@ -188,6 +199,8 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -208,6 +221,8 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -228,6 +243,8 @@ public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, convertToStreamResponse(publisher, responseObserver); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -308,6 +325,8 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -413,6 +432,32 @@ private void handleError(StreamObserver responseObserver, JSONRPCError er responseObserver.onError(status.withDescription(description).asRuntimeException()); } + private void handleSecurityException(StreamObserver responseObserver, SecurityException e) { + Status status; + String description; + + String exceptionClassName = e.getClass().getName(); + + // Attempt to detect common authentication and authorization related exceptions + if (exceptionClassName.contains("Unauthorized") || + exceptionClassName.contains("Unauthenticated") || + exceptionClassName.contains("Authentication")) { + status = Status.UNAUTHENTICATED; + description = A2AErrorMessages.AUTHENTICATION_FAILED; + } else if (exceptionClassName.contains("Forbidden") || + exceptionClassName.contains("AccessDenied") || + exceptionClassName.contains("Authorization")) { + status = Status.PERMISSION_DENIED; + description = A2AErrorMessages.AUTHORIZATION_FAILED; + } else { + // If the security exception type cannot be detected, default to PERMISSION_DENIED + status = Status.PERMISSION_DENIED; + description = "Authorization failed: " + (e.getMessage() != null ? e.getMessage() : "Access denied"); + } + + responseObserver.onError(status.withDescription(description).asRuntimeException()); + } + private void handleInternalError(StreamObserver responseObserver, Throwable t) { handleError(responseObserver, new InternalError(t.getMessage())); } From 75a9459d60ced61413bbe5c8e6b7dde4f76ba5b7 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 10 Oct 2025 00:25:37 +0200 Subject: [PATCH 156/493] chore: Using error prone and jspecify to define nullability on the client API. (#330) Setting up error prone Defining the client API as marked to ensure nullability checks Signed-off-by: Emmanuel Hugonnet --- .../java/io/a2a/client/AbstractClient.java | 32 +++++----- .../src/main/java/io/a2a/client/Client.java | 45 +++++++------- .../java/io/a2a/client/ClientBuilder.java | 12 ++-- .../java/io/a2a/client/ClientTaskManager.java | 7 ++- .../io/a2a/client/config/ClientConfig.java | 61 ++++++++++--------- .../io/a2a/client/config/package-info.java | 5 ++ .../main/java/io/a2a/client/package-info.java | 5 ++ .../java/io/a2a/client/ClientBuilderTest.java | 8 +-- .../core/ReplicatedQueueManager.java | 2 +- extras/queue-manager-replicated/tests/pom.xml | 3 - pom.xml | 47 +++++++++++++- .../io/a2a/spec/MessageSendConfiguration.java | 14 +++-- 12 files changed, 154 insertions(+), 87 deletions(-) create mode 100644 client/base/src/main/java/io/a2a/client/config/package-info.java create mode 100644 client/base/src/main/java/io/a2a/client/package-info.java diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index 526b83e01..b6a41a35a 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -19,6 +19,8 @@ import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * Abstract class representing an A2A client. Provides a standard set @@ -29,13 +31,13 @@ public abstract class AbstractClient { private final List> consumers; - private final Consumer streamingErrorHandler; + private final @Nullable Consumer streamingErrorHandler; public AbstractClient(List> consumers) { this(consumers, null); } - public AbstractClient(List> consumers, Consumer streamingErrorHandler) { + public AbstractClient(@NonNull List> consumers, @Nullable Consumer streamingErrorHandler) { checkNotNullParam("consumers", consumers); this.consumers = consumers; this.streamingErrorHandler = streamingErrorHandler; @@ -70,7 +72,7 @@ public void sendMessage(Message request) throws A2AClientException { * @param context optional client call context for the request (may be {@code null}) * @throws A2AClientException if sending the message fails for any reason */ - public abstract void sendMessage(Message request, ClientCallContext context) throws A2AClientException; + public abstract void sendMessage(Message request, @Nullable ClientCallContext context) throws A2AClientException; /** * Send a message to the remote agent. This method will automatically use @@ -110,7 +112,7 @@ public void sendMessage(Message request, public abstract void sendMessage(Message request, List> consumers, Consumer streamingErrorHandler, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Send a message to the remote agent. This method will automatically use @@ -147,7 +149,7 @@ public void sendMessage(Message request, PushNotificationConfig pushNotification * @throws A2AClientException if sending the message fails for any reason */ public abstract void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, - Map metadata, ClientCallContext context) throws A2AClientException; + Map metadata, @Nullable ClientCallContext context) throws A2AClientException; /** * Retrieve the current state and history of a specific task. @@ -168,7 +170,7 @@ public Task getTask(TaskQueryParams request) throws A2AClientException { * @return the task * @throws A2AClientException if retrieving the task fails for any reason */ - public abstract Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException; + public abstract Task getTask(TaskQueryParams request, @Nullable ClientCallContext context) throws A2AClientException; /** * Request the agent to cancel a specific task. @@ -189,7 +191,7 @@ public Task cancelTask(TaskIdParams request) throws A2AClientException { * @return the cancelled task * @throws A2AClientException if cancelling the task fails for any reason */ - public abstract Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException; + public abstract Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException; /** * Set or update the push notification configuration for a specific task. @@ -213,7 +215,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration( */ public abstract TaskPushNotificationConfig setTaskPushNotificationConfiguration( TaskPushNotificationConfig request, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Retrieve the push notification configuration for a specific task. @@ -237,7 +239,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( */ public abstract TaskPushNotificationConfig getTaskPushNotificationConfiguration( GetTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Retrieve the list of push notification configurations for a specific task. @@ -261,7 +263,7 @@ public List listTaskPushNotificationConfigurations( */ public abstract List listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Delete the list of push notification configurations for a specific task. @@ -283,7 +285,7 @@ public void deleteTaskPushNotificationConfigurations( */ public abstract void deleteTaskPushNotificationConfigurations( DeleteTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Resubscribe to a task's event stream. @@ -310,7 +312,7 @@ public void resubscribe(TaskIdParams request) throws A2AClientException { * @param context optional client call context for the request (may be {@code null}) * @throws A2AClientException if resubscribing fails for any reason */ - public abstract void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException; + public abstract void resubscribe(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException; /** * Resubscribe to a task's event stream. @@ -343,7 +345,7 @@ public void resubscribe(TaskIdParams request, List> consumers, - Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException; + Consumer streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException; /** * Retrieve the AgentCard. @@ -362,7 +364,7 @@ public AgentCard getAgentCard() throws A2AClientException { * @return the AgentCard * @throws A2AClientException if retrieving the agent card fails for any reason */ - public abstract AgentCard getAgentCard(ClientCallContext context) throws A2AClientException; + public abstract AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException; /** * Close the transport and release any associated resources. @@ -383,7 +385,7 @@ void consume(ClientEvent clientEventOrMessage, AgentCard agentCard) { * * @return the streaming error handler */ - public Consumer getStreamingErrorHandler() { + public @Nullable Consumer getStreamingErrorHandler() { return streamingErrorHandler; } diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index da5b56cec..ab2222667 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -30,6 +30,9 @@ import static io.a2a.util.Assert.checkNotNullParam; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + public class Client extends AbstractClient { private final ClientConfig clientConfig; @@ -37,7 +40,7 @@ public class Client extends AbstractClient { private AgentCard agentCard; Client(AgentCard agentCard, ClientConfig clientConfig, ClientTransport clientTransport, - List> consumers, Consumer streamingErrorHandler) { + List> consumers, @Nullable Consumer streamingErrorHandler) { super(consumers, streamingErrorHandler); checkNotNullParam("agentCard", agentCard); @@ -51,21 +54,21 @@ public static ClientBuilder builder(AgentCard agentCard) { } @Override - public void sendMessage(Message request, ClientCallContext context) throws A2AClientException { + public void sendMessage(Message request, @Nullable ClientCallContext context) throws A2AClientException { MessageSendParams messageSendParams = getMessageSendParams(request, clientConfig); sendMessage(messageSendParams, null, null, context); } @Override public void sendMessage(Message request, List> consumers, - Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException { + Consumer streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException { MessageSendParams messageSendParams = getMessageSendParams(request, clientConfig); sendMessage(messageSendParams, consumers, streamingErrorHandler, context); } @Override public void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration, - Map metatadata, ClientCallContext context) throws A2AClientException { + Map metatadata, @Nullable ClientCallContext context) throws A2AClientException { MessageSendConfiguration messageSendConfiguration = createMessageSendConfiguration(pushNotificationConfiguration); MessageSendParams messageSendParams = new MessageSendParams.Builder() @@ -78,52 +81,52 @@ public void sendMessage(Message request, PushNotificationConfig pushNotification } @Override - public Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException { + public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.getTask(request, context); } @Override - public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException { + public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.cancelTask(request, context); } @Override public TaskPushNotificationConfig setTaskPushNotificationConfiguration( - TaskPushNotificationConfig request, ClientCallContext context) throws A2AClientException { + TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.setTaskPushNotificationConfiguration(request, context); } @Override public TaskPushNotificationConfig getTaskPushNotificationConfiguration( - GetTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + GetTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.getTaskPushNotificationConfiguration(request, context); } @Override public List listTaskPushNotificationConfigurations( - ListTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.listTaskPushNotificationConfigurations(request, context); } @Override public void deleteTaskPushNotificationConfigurations( - DeleteTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { clientTransport.deleteTaskPushNotificationConfigurations(request, context); } @Override - public void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException { + public void resubscribe(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException { resubscribeToTask(request, null, null, context); } @Override - public void resubscribe(TaskIdParams request, List> consumers, - Consumer streamingErrorHandler, ClientCallContext context) throws A2AClientException { + public void resubscribe(TaskIdParams request, @Nullable List> consumers, + @Nullable Consumer streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException { resubscribeToTask(request, consumers, streamingErrorHandler, context); } @Override - public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException { agentCard = clientTransport.getAgentCard(context); return agentCard; } @@ -150,7 +153,7 @@ private ClientEvent getClientEvent(StreamingEventKind event, ClientTaskManager t } } - private MessageSendConfiguration createMessageSendConfiguration(PushNotificationConfig pushNotificationConfig) { + private MessageSendConfiguration createMessageSendConfiguration(@Nullable PushNotificationConfig pushNotificationConfig) { return new MessageSendConfiguration.Builder() .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) .blocking(!clientConfig.isPolling()) @@ -159,8 +162,8 @@ private MessageSendConfiguration createMessageSendConfiguration(PushNotification .build(); } - private void sendMessage(MessageSendParams messageSendParams, List> consumers, - Consumer errorHandler, ClientCallContext context) throws A2AClientException { + private void sendMessage(MessageSendParams messageSendParams, @Nullable List> consumers, + @Nullable Consumer errorHandler, @Nullable ClientCallContext context) throws A2AClientException { if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { EventKind eventKind = clientTransport.sendMessage(messageSendParams, context); ClientEvent clientEvent; @@ -186,8 +189,8 @@ private void sendMessage(MessageSendParams messageSendParams, List> consumers, - Consumer errorHandler, ClientCallContext context) throws A2AClientException { + private void resubscribeToTask(TaskIdParams request, @Nullable List> consumers, + @Nullable Consumer errorHandler, @Nullable ClientCallContext context) throws A2AClientException { if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) { throw new A2AClientException("Client and/or server does not support resubscription"); } @@ -204,7 +207,7 @@ private void resubscribeToTask(TaskIdParams request, List getOverriddenErrorHandler(Consumer errorHandler) { + private @NonNull Consumer getOverriddenErrorHandler(@Nullable Consumer errorHandler) { return e -> { if (errorHandler != null) { errorHandler.accept(e); @@ -216,7 +219,7 @@ private Consumer getOverriddenErrorHandler(Consumer errorH }; } - private void consume(ClientEvent clientEvent, AgentCard agentCard, List> consumers) { + private void consume(ClientEvent clientEvent, AgentCard agentCard, @Nullable List> consumers) { if (consumers != null) { // use specified consumers for (BiConsumer consumer : consumers) { diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java index a093dfae5..063cd74ac 100644 --- a/client/base/src/main/java/io/a2a/client/ClientBuilder.java +++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java @@ -18,6 +18,8 @@ import java.util.ServiceLoader; import java.util.function.BiConsumer; import java.util.function.Consumer; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; public class ClientBuilder { @@ -35,12 +37,12 @@ public class ClientBuilder { private final AgentCard agentCard; private final List> consumers = new ArrayList<>(); - private Consumer streamErrorHandler; - private ClientConfig clientConfig; + private @Nullable Consumer streamErrorHandler; + private ClientConfig clientConfig = new ClientConfig.Builder().build(); private final Map, ClientTransportConfig> clientTransports = new LinkedHashMap<>(); - ClientBuilder(AgentCard agentCard) { + ClientBuilder(@NonNull AgentCard agentCard) { this.agentCard = agentCard; } @@ -69,7 +71,7 @@ public ClientBuilder streamingErrorHandler(Consumer streamErrorHandle return this; } - public ClientBuilder clientConfig(ClientConfig clientConfig) { + public ClientBuilder clientConfig(@NonNull ClientConfig clientConfig) { this.clientConfig = clientConfig; return this; } @@ -88,10 +90,10 @@ public Client build() throws A2AClientException { private ClientTransport buildClientTransport() throws A2AClientException { // Get the preferred transport AgentInterface agentInterface = findBestClientTransport(); - Class transportProtocolClass = transportProviderRegistry.get(agentInterface.transport()).getTransportProtocolClass(); // Get the transport provider associated to the protocol ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.transport()); + Class transportProtocolClass = clientTransportProvider.getTransportProtocolClass(); // Retrieve the configuration associated to the preferred transport ClientTransportConfig clientTransportConfig = clientTransports.get(transportProtocolClass); diff --git a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java index 591e70ae4..f8737db9d 100644 --- a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java +++ b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java @@ -16,6 +16,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; +import org.jspecify.annotations.Nullable; /** * Helps manage a task's lifecycle during the execution of a request. @@ -24,9 +25,9 @@ */ public class ClientTaskManager { - private Task currentTask; - private String taskId; - private String contextId; + private @Nullable Task currentTask; + private @Nullable String taskId; + private @Nullable String contextId; public ClientTaskManager() { this.currentTask = null; diff --git a/client/base/src/main/java/io/a2a/client/config/ClientConfig.java b/client/base/src/main/java/io/a2a/client/config/ClientConfig.java index fd8a016f4..0f4672aa9 100644 --- a/client/base/src/main/java/io/a2a/client/config/ClientConfig.java +++ b/client/base/src/main/java/io/a2a/client/config/ClientConfig.java @@ -4,6 +4,9 @@ import java.util.Map; import io.a2a.spec.PushNotificationConfig; +import java.util.ArrayList; +import java.util.HashMap; +import org.jspecify.annotations.Nullable; /** * Configuration for the A2A client factory. @@ -14,20 +17,18 @@ public class ClientConfig { private final Boolean polling; private final Boolean useClientPreference; private final List acceptedOutputModes; - private final PushNotificationConfig pushNotificationConfig; - private final Integer historyLength; + private final @Nullable PushNotificationConfig pushNotificationConfig; + private final @Nullable Integer historyLength; private final Map metadata; - public ClientConfig(Boolean streaming, Boolean polling, Boolean useClientPreference, - List acceptedOutputModes, PushNotificationConfig pushNotificationConfig, - Integer historyLength, Map metadata) { - this.streaming = streaming == null ? true : streaming; - this.polling = polling == null ? false : polling; - this.useClientPreference = useClientPreference == null ? false : useClientPreference; - this.acceptedOutputModes = acceptedOutputModes; - this.pushNotificationConfig = pushNotificationConfig; - this.historyLength = historyLength; - this.metadata = metadata; + private ClientConfig(Builder builder) { + this.streaming = builder.streaming == null ? true : builder.streaming; + this.polling = builder.polling == null ? false : builder.polling; + this.useClientPreference = builder.useClientPreference == null ? false : builder.useClientPreference; + this.acceptedOutputModes = builder.acceptedOutputModes; + this.pushNotificationConfig = builder.pushNotificationConfig; + this.historyLength = builder.historyLength; + this.metadata = builder.metadata; } public boolean isStreaming() { @@ -46,11 +47,11 @@ public List getAcceptedOutputModes() { return acceptedOutputModes; } - public PushNotificationConfig getPushNotificationConfig() { + public @Nullable PushNotificationConfig getPushNotificationConfig() { return pushNotificationConfig; } - public Integer getHistoryLength() { + public @Nullable Integer getHistoryLength() { return historyLength; } @@ -58,32 +59,36 @@ public Map getMetadata() { return metadata; } + public static Builder builder() { + return new Builder(); + } + public static class Builder { - private Boolean streaming; - private Boolean polling; - private Boolean useClientPreference; - private List acceptedOutputModes; - private PushNotificationConfig pushNotificationConfig; - private Integer historyLength; - private Map metadata; - - public Builder setStreaming(Boolean streaming) { + private @Nullable Boolean streaming; + private @Nullable Boolean polling; + private @Nullable Boolean useClientPreference; + private List acceptedOutputModes = new ArrayList<>(); + private @Nullable PushNotificationConfig pushNotificationConfig; + private @Nullable Integer historyLength; + private Map metadata = new HashMap<>(); + + public Builder setStreaming(@Nullable Boolean streaming) { this.streaming = streaming; return this; } - public Builder setPolling(Boolean polling) { + public Builder setPolling(@Nullable Boolean polling) { this.polling = polling; return this; } - public Builder setUseClientPreference(Boolean useClientPreference) { + public Builder setUseClientPreference(@Nullable Boolean useClientPreference) { this.useClientPreference = useClientPreference; return this; } public Builder setAcceptedOutputModes(List acceptedOutputModes) { - this.acceptedOutputModes = acceptedOutputModes; + this.acceptedOutputModes = new ArrayList<>(acceptedOutputModes); return this; } @@ -103,9 +108,7 @@ public Builder setMetadata(Map metadata) { } public ClientConfig build() { - return new ClientConfig(streaming, polling, - useClientPreference, acceptedOutputModes, - pushNotificationConfig, historyLength, metadata); + return new ClientConfig(this); } } } \ No newline at end of file diff --git a/client/base/src/main/java/io/a2a/client/config/package-info.java b/client/base/src/main/java/io/a2a/client/config/package-info.java new file mode 100644 index 000000000..ab8c28600 --- /dev/null +++ b/client/base/src/main/java/io/a2a/client/config/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.config; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/base/src/main/java/io/a2a/client/package-info.java b/client/base/src/main/java/io/a2a/client/package-info.java new file mode 100644 index 000000000..73cc3eef8 --- /dev/null +++ b/client/base/src/main/java/io/a2a/client/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index b57ca52ed..1c7ed38a5 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -46,7 +46,7 @@ public class ClientBuilderTest { new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) .build(); - @Test() + @Test public void shouldNotFindCompatibleTransport() throws A2AClientException { A2AClientException exception = Assertions.assertThrows(A2AClientException.class, () -> Client @@ -56,10 +56,10 @@ public void shouldNotFindCompatibleTransport() throws A2AClientException { .channelFactory(s -> null)) .build()); - Assertions.assertTrue(exception.getMessage().contains("No compatible transport found")); + Assertions.assertTrue(exception.getMessage() != null && exception.getMessage().contains("No compatible transport found")); } - @Test() + @Test public void shouldNotFindConfigurationTransport() throws A2AClientException { A2AClientException exception = Assertions.assertThrows(A2AClientException.class, () -> Client @@ -67,7 +67,7 @@ public void shouldNotFindConfigurationTransport() throws A2AClientException { .clientConfig(new ClientConfig.Builder().setUseClientPreference(true).build()) .build()); - Assertions.assertTrue(exception.getMessage().startsWith("Missing required TransportConfig for")); + Assertions.assertTrue(exception.getMessage() != null && exception.getMessage().startsWith("Missing required TransportConfig for")); } @Test diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index 0b31b805d..68de6915f 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -112,7 +112,7 @@ public ReplicationHook(String taskId) { @Override public void onEnqueue(Event event) { // Only replicate if this isn't already a replicated event being processed - if (isHandlingReplicatedEvent.get() != Boolean.TRUE) { + if (!Boolean.TRUE.equals(isHandlingReplicatedEvent.get())) { if (replicationStrategy != null && taskId != null) { replicationStrategy.send(taskId, event); } diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests/pom.xml index 16f56c723..5dd2e059a 100644 --- a/extras/queue-manager-replicated/tests/pom.xml +++ b/extras/queue-manager-replicated/tests/pom.xml @@ -30,20 +30,17 @@ io.github.a2asdk a2a-java-sdk-reference-jsonrpc - ${project.version} io.github.a2asdk a2a-java-sdk-client - ${project.version} test io.github.a2asdk a2a-java-sdk-transport-jsonrpc - ${project.version} test diff --git a/pom.xml b/pom.xml index 4097cab8a..25d188e50 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ 1.73.0 UTF-8 3.5.0 - 3.11.0 + 3.14.1 3.4.2 3.3.1 3.1.2 @@ -64,6 +64,7 @@ 5.5.1 2.0.17 1.5.18 + 2.42.0 true @@ -271,10 +272,20 @@ test ${project.version} + + org.jspecify + jspecify + 1.0.0 + provided + + + org.jspecify + jspecify + @@ -288,9 +299,41 @@ 17 17 - + UTF-8 + true + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + -XDcompilePolicy=simple + --should-stop=ifError=FLOW -parameters + -Xplugin:ErrorProne -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true + + + com.google.errorprone + error_prone_core + ${error-prone.version} + + + com.uber.nullaway + nullaway + 0.12.10 + + + diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index 9c11a074b..cbe72cfb1 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * Defines configuration options for a `message/send` or `message/stream` request. @@ -11,7 +13,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record MessageSendConfiguration(List acceptedOutputModes, Integer historyLength, - PushNotificationConfig pushNotificationConfig, Boolean blocking) { + PushNotificationConfig pushNotificationConfig, Boolean blocking) { public MessageSendConfiguration { if (historyLength != null && historyLength < 0) { @@ -20,6 +22,7 @@ public record MessageSendConfiguration(List acceptedOutputModes, Integer } public static class Builder { + List acceptedOutputModes; Integer historyLength; PushNotificationConfig pushNotificationConfig; @@ -30,17 +33,20 @@ public Builder acceptedOutputModes(List acceptedOutputModes) { return this; } - public Builder pushNotificationConfig(PushNotificationConfig pushNotificationConfig) { + public Builder pushNotificationConfig(@Nullable PushNotificationConfig pushNotificationConfig) { this.pushNotificationConfig = pushNotificationConfig; return this; } - public Builder historyLength(Integer historyLength) { + public Builder historyLength(@Nullable Integer historyLength) { + if (historyLength != null && historyLength < 0) { + throw new IllegalArgumentException("Invalid history length"); + } this.historyLength = historyLength; return this; } - public Builder blocking(Boolean blocking) { + public Builder blocking(@NonNull Boolean blocking) { this.blocking = blocking; return this; } From eee3bb84e79708a2aabb423828e74269fd945822 Mon Sep 17 00:00:00 2001 From: Amith Sourya Sadineni <69379991+amithsourya@users.noreply.github.com> Date: Fri, 10 Oct 2025 14:58:33 +0530 Subject: [PATCH 157/493] test: add test for streaming with multiple events (#326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Implements tests for streaming message scenarios where multiple events are received from the server, addressing Issue #52. This test validates the proper handling of event streams in AI agent-to-agent communication using the A2A Protocol. ## Overview The `testOnMessageStreamNewMessageMultipleEventsSuccess()` method verifies that the JSON-RPC handler correctly processes a sequence of multiple events during streaming communication: 1. **Task Event (WORKING state)** - Initial task state update 2. **TaskArtifactUpdateEvent** - Artifact generation during task execution 3. **TaskStatusUpdateEvent (COMPLETED state)** - Final task completion signal - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #52 🦕 --- --- .../jsonrpc/handler/JSONRPCHandlerTest.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 7da45eb14..9d12824b7 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -335,6 +335,114 @@ public void onComplete() { Assertions.assertSame(message, results.get(0)); } + @Test + public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws InterruptedException { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + + // Create multiple events to be sent during streaming + Task taskEvent = new Task.Builder(MINIMAL_TASK) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + TaskArtifactUpdateEvent artifactEvent = new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("artifact-1") + .parts(new TextPart("Generated artifact content")) + .build()) + .build(); + + TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + // Configure the agent executor to enqueue multiple events + agentExecutorExecute = (context, eventQueue) -> { + // Enqueue the task with WORKING state + eventQueue.enqueueEvent(taskEvent); + // Enqueue an artifact update event + eventQueue.enqueueEvent(artifactEvent); + // Enqueue a status update event to complete the task (this is the "final" event) + eventQueue.enqueueEvent(statusEvent); + }; + + Message message = new Message.Builder(MESSAGE) + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .build(); + + SendStreamingMessageRequest request = new SendStreamingMessageRequest( + "1", new MessageSendParams(message, null, null)); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); + + List results = new ArrayList<>(); + CountDownLatch latch = new CountDownLatch(3); // Expect 3 events + AtomicReference error = new AtomicReference<>(); + + response.subscribe(new Flow.Subscriber<>() { + private Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(SendStreamingMessageResponse item) { + results.add(item.getResult()); + subscription.request(1); + latch.countDown(); + } + + @Override + public void onError(Throwable throwable) { + error.set(throwable); + subscription.cancel(); + // Release latch to prevent timeout + while (latch.getCount() > 0) { + latch.countDown(); + } + } + + @Override + public void onComplete() { + subscription.cancel(); + } + }); + + // Wait for all events to be received + Assertions.assertTrue(latch.await(2, TimeUnit.SECONDS), + "Expected to receive 3 events within timeout"); + + // Assert no error occurred during streaming + Assertions.assertNull(error.get(), "No error should occur during streaming"); + + // Verify that all 3 events were received + assertEquals(3, results.size(), "Should have received exactly 3 events"); + + // Verify the first event is the task + Task receivedTask = assertInstanceOf(Task.class, results.get(0), "First event should be a Task"); + assertEquals(MINIMAL_TASK.getId(), receivedTask.getId()); + assertEquals(MINIMAL_TASK.getContextId(), receivedTask.getContextId()); + assertEquals(TaskState.WORKING, receivedTask.getStatus().state()); + + // Verify the second event is the artifact update + TaskArtifactUpdateEvent receivedArtifact = assertInstanceOf(TaskArtifactUpdateEvent.class, results.get(1), + "Second event should be a TaskArtifactUpdateEvent"); + assertEquals(MINIMAL_TASK.getId(), receivedArtifact.getTaskId()); + assertEquals("artifact-1", receivedArtifact.getArtifact().artifactId()); + + // Verify the third event is the status update + TaskStatusUpdateEvent receivedStatus = assertInstanceOf(TaskStatusUpdateEvent.class, results.get(2), + "Third event should be a TaskStatusUpdateEvent"); + assertEquals(MINIMAL_TASK.getId(), receivedStatus.getTaskId()); + assertEquals(TaskState.COMPLETED, receivedStatus.getStatus().state()); + } + @Test public void testOnMessageStreamNewMessageSuccessMocks() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); From 47c906f1b268283b264911f6c275450ef173a05d Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 10 Oct 2025 16:32:13 +0200 Subject: [PATCH 158/493] chore: Using error prone and jspecify to expend the nullability (#332) definition on the HTTP+REST API. # Description Defining proper contracts on Nullability for the HTTP+REST transport and client Signed-off-by: Emmanuel Hugonnet --- .../java/io/a2a/client/ClientBuilder.java | 3 + .../transport/rest/RestErrorMapper.java | 43 ++--- .../client/transport/rest/RestTransport.java | 40 +++-- .../transport/rest/RestTransportConfig.java | 5 +- .../rest/RestTransportConfigBuilder.java | 4 +- .../client/transport/rest/package-info.java | 5 + .../rest/sse/RestSSEEventListener.java | 30 ++-- .../transport/rest/sse/package-info.java | 5 + .../transport/rest/RestTransportTest.java | 44 ++++- .../client/transport/spi/ClientTransport.java | 21 +-- .../transport/spi/ClientTransportConfig.java | 5 +- .../interceptors/ClientCallInterceptor.java | 5 +- .../spi/interceptors/PayloadAndHeaders.java | 11 +- .../interceptors/auth/AuthInterceptor.java | 5 +- .../interceptors/auth/CredentialService.java | 3 +- .../InMemoryContextCredentialService.java | 8 +- .../spi/interceptors/auth/package-info.java | 5 + .../spi/interceptors/package-info.java | 5 + .../client/transport/spi/package-info.java | 5 + .../io/a2a/client/http/A2ACardResolver.java | 11 +- .../io/a2a/client/http/JdkA2AHttpClient.java | 13 +- .../java/io/a2a/client/http/package-info.java | 5 + pom.xml | 3 +- .../server/rest/quarkus/A2AServerRoutes.java | 157 ++++++++++-------- .../a2a/server/rest/quarkus/package-info.java | 5 + .../java/io/a2a/grpc/utils/ProtoUtils.java | 23 +-- .../java/io/a2a/grpc/utils/package-info.java | 5 + .../java/io/a2a/spec/A2AClientHTTPError.java | 1 + .../io/a2a/spec/APIKeySecurityScheme.java | 12 +- spec/src/main/java/io/a2a/spec/AgentCard.java | 1 - .../GetTaskPushNotificationConfigParams.java | 3 +- .../java/io/a2a/spec/JSONRPCResponse.java | 2 + spec/src/main/java/io/a2a/spec/Message.java | 2 +- spec/src/main/java/io/a2a/spec/Part.java | 2 +- .../StreamingJSONRPCRequestDeserializer.java | 9 +- spec/src/main/java/io/a2a/spec/Task.java | 1 + .../java/io/a2a/spec/TaskQueryParams.java | 7 +- spec/src/main/java/io/a2a/spec/TaskState.java | 34 ++-- .../java/io/a2a/spec/TransportProtocol.java | 16 +- .../transport/rest/handler/RestHandler.java | 46 +++-- .../transport/rest/handler/package-info.java | 5 + 41 files changed, 340 insertions(+), 275 deletions(-) create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/package-info.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/package-info.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/package-info.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/package-info.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/package-info.java create mode 100644 http-client/src/main/java/io/a2a/client/http/package-info.java create mode 100644 reference/rest/src/main/java/io/a2a/server/rest/quarkus/package-info.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/utils/package-info.java create mode 100644 transport/rest/src/main/java/io/a2a/transport/rest/handler/package-info.java diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java index 063cd74ac..da61dfa30 100644 --- a/client/base/src/main/java/io/a2a/client/ClientBuilder.java +++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java @@ -93,6 +93,9 @@ private ClientTransport buildClientTransport() throws A2AClientException { // Get the transport provider associated to the protocol ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.transport()); + if (clientTransportProvider == null) { + throw new A2AClientException("No client available for " + agentInterface.transport()); + } Class transportProtocolClass = clientTransportProvider.getTransportProtocolClass(); // Retrieve the configuration associated to the preferred transport diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java index 103f7bb0c..965cc2962 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java @@ -48,33 +48,20 @@ public static A2AClientException mapRestError(String body, int code) { } public static A2AClientException mapRestError(String className, String errorMessage, int code) { - switch (className) { - case "io.a2a.spec.TaskNotFoundError": - return new A2AClientException(errorMessage, new TaskNotFoundError()); - case "io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError": - return new A2AClientException(errorMessage, new AuthenticatedExtendedCardNotConfiguredError()); - case "io.a2a.spec.ContentTypeNotSupportedError": - return new A2AClientException(errorMessage, new ContentTypeNotSupportedError(null, null, errorMessage)); - case "io.a2a.spec.InternalError": - return new A2AClientException(errorMessage, new InternalError(errorMessage)); - case "io.a2a.spec.InvalidAgentResponseError": - return new A2AClientException(errorMessage, new InvalidAgentResponseError(null, null, errorMessage)); - case "io.a2a.spec.InvalidParamsError": - return new A2AClientException(errorMessage, new InvalidParamsError()); - case "io.a2a.spec.InvalidRequestError": - return new A2AClientException(errorMessage, new InvalidRequestError()); - case "io.a2a.spec.JSONParseError": - return new A2AClientException(errorMessage, new JSONParseError()); - case "io.a2a.spec.MethodNotFoundError": - return new A2AClientException(errorMessage, new MethodNotFoundError()); - case "io.a2a.spec.PushNotificationNotSupportedError": - return new A2AClientException(errorMessage, new PushNotificationNotSupportedError()); - case "io.a2a.spec.TaskNotCancelableError": - return new A2AClientException(errorMessage, new TaskNotCancelableError()); - case "io.a2a.spec.UnsupportedOperationError": - return new A2AClientException(errorMessage, new UnsupportedOperationError()); - default: - return new A2AClientException(errorMessage); - } + return switch (className) { + case "io.a2a.spec.TaskNotFoundError" -> new A2AClientException(errorMessage, new TaskNotFoundError()); + case "io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError" -> new A2AClientException(errorMessage, new AuthenticatedExtendedCardNotConfiguredError()); + case "io.a2a.spec.ContentTypeNotSupportedError" -> new A2AClientException(errorMessage, new ContentTypeNotSupportedError(null, null, errorMessage)); + case "io.a2a.spec.InternalError" -> new A2AClientException(errorMessage, new InternalError(errorMessage)); + case "io.a2a.spec.InvalidAgentResponseError" -> new A2AClientException(errorMessage, new InvalidAgentResponseError(null, null, errorMessage)); + case "io.a2a.spec.InvalidParamsError" -> new A2AClientException(errorMessage, new InvalidParamsError()); + case "io.a2a.spec.InvalidRequestError" -> new A2AClientException(errorMessage, new InvalidRequestError()); + case "io.a2a.spec.JSONParseError" -> new A2AClientException(errorMessage, new JSONParseError()); + case "io.a2a.spec.MethodNotFoundError" -> new A2AClientException(errorMessage, new MethodNotFoundError()); + case "io.a2a.spec.PushNotificationNotSupportedError" -> new A2AClientException(errorMessage, new PushNotificationNotSupportedError()); + case "io.a2a.spec.TaskNotCancelableError" -> new A2AClientException(errorMessage, new TaskNotCancelableError()); + case "io.a2a.spec.UnsupportedOperationError" -> new A2AClientException(errorMessage, new UnsupportedOperationError()); + default -> new A2AClientException(errorMessage); + }; } } diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 5420600d4..f659589b7 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -38,32 +38,30 @@ import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.util.Utils; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.logging.Logger; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; public class RestTransport implements ClientTransport { private static final Logger log = Logger.getLogger(RestTransport.class.getName()); private final A2AHttpClient httpClient; private final String agentUrl; - private final List interceptors; + private @Nullable final List interceptors; private AgentCard agentCard; private boolean needsExtendedCard = false; - public RestTransport(String agentUrl) { - this(null, null, agentUrl, null); - } - public RestTransport(AgentCard agentCard) { this(null, agentCard, agentCard.url(), null); } - public RestTransport(A2AHttpClient httpClient, AgentCard agentCard, - String agentUrl, List interceptors) { + public RestTransport(@Nullable A2AHttpClient httpClient, AgentCard agentCard, + String agentUrl, @Nullable List interceptors) { this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; this.agentCard = agentCard; this.agentUrl = agentUrl.endsWith("/") ? agentUrl.substring(0, agentUrl.length() - 1) : agentUrl; @@ -71,7 +69,7 @@ public RestTransport(A2AHttpClient httpClient, AgentCard agentCard, } @Override - public EventKind sendMessage(MessageSendParams messageSendParams, ClientCallContext context) throws A2AClientException { + public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("messageSendParams", messageSendParams); io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.SendMessageRequest.METHOD, builder, agentCard, context); @@ -94,7 +92,7 @@ public EventKind sendMessage(MessageSendParams messageSendParams, ClientCallCont } @Override - public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer eventConsumer, Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer eventConsumer, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", messageSendParams); checkNotNullParam("eventConsumer", eventConsumer); checkNotNullParam("messageSendParams", messageSendParams); @@ -119,7 +117,7 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + public List listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); ListTaskPushNotificationConfigRequest.Builder builder = ListTaskPushNotificationConfigRequest.newBuilder(); builder.setParent(String.format("/tasks/%1s/pushNotificationConfigs", request.id())); @@ -255,7 +253,7 @@ public List listTaskPushNotificationConfigurations(L } @Override - public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException { + public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.DeleteTaskPushNotificationConfigRequest.METHOD, builder, @@ -281,7 +279,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC @Override public void resubscribe(TaskIdParams request, Consumer eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.TaskSubscriptionRequest.Builder builder = io.a2a.grpc.TaskSubscriptionRequest.newBuilder(); builder.setName("tasks/" + request.id()); @@ -306,7 +304,7 @@ public void resubscribe(TaskIdParams request, Consumer event } @Override - public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException { A2ACardResolver resolver; try { if (agentCard == null) { @@ -346,8 +344,8 @@ public void close() { // no-op } - private PayloadAndHeaders applyInterceptors(String methodName, MessageOrBuilder payload, - AgentCard agentCard, ClientCallContext clientCallContext) { + private PayloadAndHeaders applyInterceptors(String methodName, @Nullable MessageOrBuilder payload, + AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, getHttpHeaders(clientCallContext)); if (interceptors != null && !interceptors.isEmpty()) { for (ClientCallInterceptor interceptor : interceptors) { @@ -383,7 +381,7 @@ private A2AHttpClient.PostBuilder createPostBuilder(String url, PayloadAndHeader return postBuilder; } - private Map getHttpHeaders(ClientCallContext context) { - return context != null ? context.getHeaders() : null; + private Map getHttpHeaders(@Nullable ClientCallContext context) { + return context != null ? context.getHeaders() : Collections.emptyMap(); } } diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java index bbb583a1b..d097b010f 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfig.java @@ -2,10 +2,11 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.transport.spi.ClientTransportConfig; +import org.jspecify.annotations.Nullable; public class RestTransportConfig extends ClientTransportConfig { - private final A2AHttpClient httpClient; + private final @Nullable A2AHttpClient httpClient; public RestTransportConfig() { this.httpClient = null; @@ -15,7 +16,7 @@ public RestTransportConfig(A2AHttpClient httpClient) { this.httpClient = httpClient; } - public A2AHttpClient getHttpClient() { + public @Nullable A2AHttpClient getHttpClient() { return httpClient; } } \ No newline at end of file diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java index 30f8b412a..68150f189 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java @@ -3,14 +3,14 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.client.transport.spi.ClientTransportConfigBuilder; +import org.jspecify.annotations.Nullable; public class RestTransportConfigBuilder extends ClientTransportConfigBuilder { - private A2AHttpClient httpClient; + private @Nullable A2AHttpClient httpClient; public RestTransportConfigBuilder httpClient(A2AHttpClient httpClient) { this.httpClient = httpClient; - return this; } diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/package-info.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/package-info.java new file mode 100644 index 000000000..b7892c239 --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.rest; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java index c34d615cb..d0b130eee 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java @@ -15,6 +15,7 @@ import io.a2a.grpc.StreamResponse; import io.a2a.grpc.utils.ProtoUtils; import io.a2a.spec.StreamingEventKind; +import org.jspecify.annotations.Nullable; public class RestSSEEventListener { @@ -28,43 +29,42 @@ public RestSSEEventListener(Consumer eventHandler, this.errorHandler = errorHandler; } - public void onMessage(String message, Future completableFuture) { + public void onMessage(String message, @Nullable Future completableFuture) { try { - System.out.println("Streaming message received: " + message); + log.fine("Streaming message received: " + message); io.a2a.grpc.StreamResponse.Builder builder = io.a2a.grpc.StreamResponse.newBuilder(); JsonFormat.parser().merge(message, builder); - handleMessage(builder.build(), completableFuture); + handleMessage(builder.build()); } catch (InvalidProtocolBufferException e) { errorHandler.accept(RestErrorMapper.mapRestError(message, 500)); } } - public void onError(Throwable throwable, Future future) { + public void onError(Throwable throwable, @Nullable Future future) { if (errorHandler != null) { errorHandler.accept(throwable); } - future.cancel(true); // close SSE channel + if (future != null) { + future.cancel(true); // close SSE channel + } } - private void handleMessage(StreamResponse response, Future future) { + private void handleMessage(StreamResponse response) { StreamingEventKind event; switch (response.getPayloadCase()) { - case MSG: + case MSG -> event = ProtoUtils.FromProto.message(response.getMsg()); - break; - case TASK: + case TASK -> event = ProtoUtils.FromProto.task(response.getTask()); - break; - case STATUS_UPDATE: + case STATUS_UPDATE -> event = ProtoUtils.FromProto.taskStatusUpdateEvent(response.getStatusUpdate()); - break; - case ARTIFACT_UPDATE: + case ARTIFACT_UPDATE -> event = ProtoUtils.FromProto.taskArtifactUpdateEvent(response.getArtifactUpdate()); - break; - default: + default -> { log.warning("Invalid stream response " + response.getPayloadCase()); errorHandler.accept(new IllegalStateException("Invalid stream response from server: " + response.getPayloadCase())); return; + } } eventHandler.accept(event); } diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/package-info.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/package-info.java new file mode 100644 index 000000000..e1bb6c3b2 --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.rest.sse; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index fc6fc23a6..a296553c3 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -22,6 +22,9 @@ import static org.mockserver.model.HttpResponse.response; import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; @@ -64,6 +67,28 @@ public class RestTransportTest { private static final Logger log = Logger.getLogger(RestTransportTest.class.getName()); private ClientAndServer server; + private static final AgentCard CARD = new AgentCard.Builder() + .name("Hello World Agent") + .description("Just a hello world agent") + .url("http://localhost:4001") + .version("1.0.0") + .documentationUrl("http://example.com/docs") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(true) + .stateTransitionHistory(true) + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("hello_world") + .name("Returns hello world") + .description("just returns hello world") + .tags(Collections.singletonList("hello world")) + .examples(List.of("hi", "hello world")) + .build())) + .protocolVersion("0.3.0") + .build(); @BeforeEach public void setUp() throws IOException { @@ -103,7 +128,8 @@ public void testSendMessage() throws Exception { ); MessageSendParams messageSendParams = new MessageSendParams(message, null, null); ClientCallContext context = null; - RestTransport instance = new RestTransport("http://localhost:4001"); + + RestTransport instance = new RestTransport(CARD); EventKind result = instance.sendMessage(messageSendParams, context); assertEquals("task", result.getKind()); Task task = (Task) result; @@ -144,7 +170,7 @@ public void testCancelTask() throws Exception { .withBody(CANCEL_TASK_TEST_RESPONSE) ); ClientCallContext context = null; - RestTransport instance = new RestTransport("http://localhost:4001"); + RestTransport instance = new RestTransport(CARD); Task task = instance.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64", new HashMap<>()), context); assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); @@ -170,7 +196,7 @@ public void testGetTask() throws Exception { ); ClientCallContext context = null; TaskQueryParams request = new TaskQueryParams("de38c76d-d54c-436c-8b9f-4c2703648d64", 10); - RestTransport instance = new RestTransport("http://localhost:4001"); + RestTransport instance = new RestTransport(CARD); Task task = instance.getTask(request, context); assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); assertEquals(TaskState.COMPLETED, task.getStatus().state()); @@ -222,7 +248,7 @@ public void testSendMessageStreaming() throws Exception { .withBody(SEND_MESSAGE_STREAMING_TEST_RESPONSE) ); - RestTransport client = new RestTransport("http://localhost:4001"); + RestTransport client = new RestTransport(CARD); Message message = new Message.Builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me some jokes"))) @@ -272,7 +298,7 @@ public void testSetTaskPushNotificationConfiguration() throws Exception { .withStatusCode(200) .withBody(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) ); - RestTransport client = new RestTransport("http://localhost:4001"); + RestTransport client = new RestTransport(CARD); TaskPushNotificationConfig pushedConfig = new TaskPushNotificationConfig( "de38c76d-d54c-436c-8b9f-4c2703648d64", new PushNotificationConfig.Builder() @@ -305,7 +331,7 @@ public void testGetTaskPushNotificationConfiguration() throws Exception { .withBody(GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) ); - RestTransport client = new RestTransport("http://localhost:4001"); + RestTransport client = new RestTransport(CARD); TaskPushNotificationConfig taskPushNotificationConfig = client.getTaskPushNotificationConfiguration( new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "10", new HashMap<>()), null); @@ -333,7 +359,7 @@ public void testListTaskPushNotificationConfigurations() throws Exception { .withBody(LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE) ); - RestTransport client = new RestTransport("http://localhost:4001"); + RestTransport client = new RestTransport(CARD); List taskPushNotificationConfigs = client.listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", new HashMap<>()), null); assertEquals(2, taskPushNotificationConfigs.size()); @@ -369,7 +395,7 @@ public void testDeleteTaskPushNotificationConfigurations() throws Exception { .withStatusCode(200) ); ClientCallContext context = null; - RestTransport instance = new RestTransport("http://localhost:4001"); + RestTransport instance = new RestTransport(CARD); instance.deleteTaskPushNotificationConfigurations(new DeleteTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "10"), context); } @@ -392,7 +418,7 @@ public void testResubscribe() throws Exception { .withBody(TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE) ); - RestTransport client = new RestTransport("http://localhost:4001"); + RestTransport client = new RestTransport(CARD); TaskIdParams taskIdParams = new TaskIdParams("task-1234"); AtomicReference receivedEvent = new AtomicReference<>(); diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 8084d8195..56a4067cf 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -16,6 +16,7 @@ import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; +import org.jspecify.annotations.Nullable; /** * Interface for a client transport. @@ -30,7 +31,7 @@ public interface ClientTransport { * @return the response, either a Task or Message * @throws A2AClientException if sending the message fails for any reason */ - EventKind sendMessage(MessageSendParams request, ClientCallContext context) + EventKind sendMessage(MessageSendParams request, @Nullable ClientCallContext context) throws A2AClientException; /** @@ -43,7 +44,7 @@ EventKind sendMessage(MessageSendParams request, ClientCallContext context) * @throws A2AClientException if setting up the streaming connection fails */ void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException; + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException; /** * Retrieve the current state and history of a specific task. @@ -53,7 +54,7 @@ void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Delete the list of push notification configurations for a specific task. @@ -109,7 +110,7 @@ List listTaskPushNotificationConfigurations( */ void deleteTaskPushNotificationConfigurations( DeleteTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException; + @Nullable ClientCallContext context) throws A2AClientException; /** * Reconnect to get task updates for an existing task. @@ -121,7 +122,7 @@ void deleteTaskPushNotificationConfigurations( * @throws A2AClientException if resubscribing to the task fails for any reason */ void resubscribe(TaskIdParams request, Consumer eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException; + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException; /** * Retrieve the AgentCard. @@ -130,7 +131,7 @@ void resubscribe(TaskIdParams request, Consumer eventConsume * @return the AgentCard * @throws A2AClientException if retrieving the agent card fails for any reason */ - AgentCard getAgentCard(ClientCallContext context) throws A2AClientException; + AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException; /** * Close the transport and release any associated resources. diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java index 657971383..8efdb779b 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java @@ -1,6 +1,7 @@ package io.a2a.client.transport.spi; import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; +import java.util.ArrayList; import java.util.List; @@ -9,10 +10,10 @@ */ public abstract class ClientTransportConfig { - protected List interceptors; + protected List interceptors = new ArrayList<>(); public void setInterceptors(List interceptors) { - this.interceptors = interceptors; + this.interceptors = new ArrayList<>(interceptors); } public List getInterceptors() { diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java index 3a44ff70b..41141298d 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java @@ -3,6 +3,7 @@ import java.util.Map; import io.a2a.spec.AgentCard; +import org.jspecify.annotations.Nullable; /** * An abstract base class for client-side call interceptors. @@ -21,6 +22,6 @@ public abstract class ClientCallInterceptor { * @param clientCallContext the {@code ClientCallContext} for this call (may be {@code null}) * @return the potentially modified payload and headers */ - public abstract PayloadAndHeaders intercept(String methodName, Object payload, Map headers, - AgentCard agentCard, ClientCallContext clientCallContext); + public abstract PayloadAndHeaders intercept(String methodName, @Nullable Object payload, Map headers, + AgentCard agentCard, @Nullable ClientCallContext clientCallContext); } diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java index 73983a096..4783cb716 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java @@ -1,18 +1,21 @@ package io.a2a.client.transport.spi.interceptors; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; public class PayloadAndHeaders { - private final Object payload; + private final @Nullable Object payload; private final Map headers; - public PayloadAndHeaders(Object payload, Map headers) { + public PayloadAndHeaders(@Nullable Object payload, Map headers) { this.payload = payload; - this.headers = headers; + this.headers = headers == null ? Collections.emptyMap() : new HashMap<>(headers); } - public Object getPayload() { + public @Nullable Object getPayload() { return payload; } diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java index 1995e4fb0..d2f2a5769 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java @@ -14,6 +14,7 @@ import io.a2a.spec.OAuth2SecurityScheme; import io.a2a.spec.OpenIdConnectSecurityScheme; import io.a2a.spec.SecurityScheme; +import org.jspecify.annotations.Nullable; /** * An interceptor that automatically adds authentication details to requests @@ -31,8 +32,8 @@ public AuthInterceptor(final CredentialService credentialService) { } @Override - public PayloadAndHeaders intercept(String methodName, Object payload, Map headers, - AgentCard agentCard, ClientCallContext clientCallContext) { + public PayloadAndHeaders intercept(String methodName, @Nullable Object payload, Map headers, + AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { Map updatedHeaders = new HashMap<>(headers == null ? new HashMap<>() : headers); if (agentCard == null || agentCard.security() == null || agentCard.securitySchemes() == null) { return new PayloadAndHeaders(payload, updatedHeaders); diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java index 6bccdadd9..949ea8087 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/CredentialService.java @@ -1,6 +1,7 @@ package io.a2a.client.transport.spi.interceptors.auth; import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import org.jspecify.annotations.Nullable; /** * Used to retrieve credentials. @@ -14,5 +15,5 @@ public interface CredentialService { * @param clientCallContext the client call context, which may be {@code null}. * @return the credential or {@code null} if the credential could not be retrieved */ - String getCredential(String securitySchemeName, ClientCallContext clientCallContext); + @Nullable String getCredential(String securitySchemeName, @Nullable ClientCallContext clientCallContext); } diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java index a1974cd13..f6db9662a 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/InMemoryContextCredentialService.java @@ -5,6 +5,7 @@ import java.util.concurrent.ConcurrentMap; import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import org.jspecify.annotations.Nullable; /** * A simple in-memory store for session-keyed credentials. @@ -23,18 +24,17 @@ public InMemoryContextCredentialService() { } @Override - public String getCredential(String securitySchemeName, - ClientCallContext clientCallContext) { + public @Nullable String getCredential(String securitySchemeName, + @Nullable ClientCallContext clientCallContext) { if (clientCallContext == null || !clientCallContext.getState().containsKey(SESSION_ID)) { // no credential to retrieve return null; } Object sessionIdObj = clientCallContext.getState().get(SESSION_ID); - if (! (sessionIdObj instanceof String)) { + if (! (sessionIdObj instanceof String sessionId)) { return null; } - String sessionId = (String) sessionIdObj; Map sessionCredentials = credentialStore.get(sessionId); if (sessionCredentials == null) { return null; diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/package-info.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/package-info.java new file mode 100644 index 000000000..85b061333 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.spi.interceptors.auth; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/package-info.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/package-info.java new file mode 100644 index 000000000..d85534d28 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.spi.interceptors; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/package-info.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/package-info.java new file mode 100644 index 000000000..f2185d0c9 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.spi; + +import org.jspecify.annotations.NullMarked; + diff --git a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java index f510cd2ac..5d94686bc 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java +++ b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java @@ -12,11 +12,12 @@ import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; +import org.jspecify.annotations.Nullable; public class A2ACardResolver { private final A2AHttpClient httpClient; private final String url; - private final Map authHeaders; + private final @Nullable Map authHeaders; private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent-card.json"; @@ -63,12 +64,12 @@ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCar * @param authHeaders the HTTP authentication headers to use. May be {@code null} * @throws A2AClientError if the URL for the agent is invalid */ - public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, String agentCardPath, - Map authHeaders) throws A2AClientError { + public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, @Nullable String agentCardPath, + @Nullable Map authHeaders) throws A2AClientError { this.httpClient = httpClient; - agentCardPath = agentCardPath == null || agentCardPath.isEmpty() ? DEFAULT_AGENT_CARD_PATH : agentCardPath; + String effectiveAgentCardPath = agentCardPath == null || agentCardPath.isEmpty() ? DEFAULT_AGENT_CARD_PATH : agentCardPath; try { - this.url = new URI(baseUrl).resolve(agentCardPath).toString(); + this.url = new URI(baseUrl).resolve(effectiveAgentCardPath).toString(); } catch (URISyntaxException e) { throw new A2AClientError("Invalid agent URL", e); } diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index 8cd0089d4..9b8003741 100644 --- a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -6,7 +6,6 @@ import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -22,9 +21,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Flow; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import io.a2a.common.A2AErrorMessages; -import io.a2a.spec.A2AClientException; public class JdkA2AHttpClient implements A2AHttpClient { @@ -53,7 +52,7 @@ public DeleteBuilder createDelete() { } private abstract class JdkBuilder> implements Builder { - private String url; + private String url = ""; private Map headers = new HashMap<>(); @Override @@ -99,13 +98,13 @@ protected CompletableFuture asyncRequest( Runnable completeRunnable ) { Flow.Subscriber subscriber = new Flow.Subscriber() { - private Flow.Subscription subscription; + private Flow.@Nullable Subscription subscription; private volatile boolean errorRaised = false; @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; - subscription.request(1); + this.subscription.request(1); } @Override @@ -117,7 +116,9 @@ public void onNext(String item) { messageConsumer.accept(item); } } - subscription.request(1); + if (subscription != null) { + subscription.request(1); + } } @Override diff --git a/http-client/src/main/java/io/a2a/client/http/package-info.java b/http-client/src/main/java/io/a2a/client/http/package-info.java new file mode 100644 index 000000000..525333cf1 --- /dev/null +++ b/http-client/src/main/java/io/a2a/client/http/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.http; + +import org.jspecify.annotations.NullMarked; + diff --git a/pom.xml b/pom.xml index 25d188e50..913b0c8cd 100644 --- a/pom.xml +++ b/pom.xml @@ -299,6 +299,7 @@ 17 17 + 17 UTF-8 true @@ -315,7 +316,7 @@ -XDcompilePolicy=simple --should-stop=ifError=FLOW -parameters - -Xplugin:ErrorProne -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true + -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index a08fb400a..a2f3a9348 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -37,12 +37,12 @@ import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import io.a2a.server.extensions.A2AExtensions; +import org.jspecify.annotations.Nullable; @Singleton @Authenticated @@ -53,7 +53,8 @@ public class A2AServerRoutes { // Hook so testing can wait until the MultiSseSupport is subscribed. // Without this we get intermittent failures - private static volatile Runnable streamingMultiSseSupportSubscribedRunnable; + private static volatile @Nullable + Runnable streamingMultiSseSupportSubscribedRunnable; @Inject @Internal @@ -71,10 +72,7 @@ public void sendMessage(@Body String body, RoutingContext rc) { } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } } @@ -85,18 +83,15 @@ public void sendMessageStreaming(@Body String body, RoutingContext rc) { HTTPRestResponse error = null; try { HTTPRestResponse response = jsonRestHandler.sendStreamingMessage(body, context); - if (response instanceof HTTPRestStreamingResponse) { - streamingResponse = (HTTPRestStreamingResponse) response; + if (response instanceof HTTPRestStreamingResponse hTTPRestStreamingResponse) { + streamingResponse = hTTPRestStreamingResponse; } else { error = response; } } finally { if (error != null) { - rc.response() - .setStatusCode(error.getStatusCode()) - .putHeader(CONTENT_TYPE, APPLICATION_JSON) - .end(error.getBody()); - } else { + sendResponse(rc, error); + } else if (streamingResponse != null) { Multi events = Multi.createFrom().publisher(streamingResponse.getPublisher()); executor.execute(() -> { MultiSseSupport.subscribeObject( @@ -112,20 +107,21 @@ public void getTask(RoutingContext rc) { ServerCallContext context = createCallContext(rc); HTTPRestResponse response = null; try { - Integer historyLength = null; - if (rc.request().params().contains("history_length")) { - historyLength = Integer.valueOf(rc.request().params().get("history_length")); + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else { + Integer historyLength = null; + if (rc.request().params().contains("history_length")) { + historyLength = Integer.valueOf(rc.request().params().get("history_length")); + } + response = jsonRestHandler.getTask(taskId, historyLength, context); } - response = jsonRestHandler.getTask(taskId, historyLength, context); } catch (NumberFormatException e) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad history_length")); } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } } @@ -135,7 +131,11 @@ public void cancelTask(RoutingContext rc) { ServerCallContext context = createCallContext(rc); HTTPRestResponse response = null; try { - response = jsonRestHandler.cancelTask(taskId, context); + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else { + response = jsonRestHandler.cancelTask(taskId, context); + } } catch (Throwable t) { if (t instanceof JSONRPCError error) { response = jsonRestHandler.createErrorResponse(error); @@ -143,10 +143,18 @@ public void cancelTask(RoutingContext rc) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } } finally { + sendResponse(rc, response); + } + } + + private void sendResponse(RoutingContext rc, @Nullable HTTPRestResponse response) { + if (response != null) { rc.response() .setStatusCode(response.getStatusCode()) .putHeader(CONTENT_TYPE, response.getContentType()) .end(response.getBody()); + } else { + rc.response().end(); } } @@ -157,19 +165,20 @@ public void resubscribeTask(RoutingContext rc) { HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { - HTTPRestResponse response = jsonRestHandler.resubscribeTask(taskId, context); - if (response instanceof HTTPRestStreamingResponse) { - streamingResponse = (HTTPRestStreamingResponse) response; + if (taskId == null || taskId.isEmpty()) { + error = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - error = response; + HTTPRestResponse response = jsonRestHandler.resubscribeTask(taskId, context); + if (response instanceof HTTPRestStreamingResponse hTTPRestStreamingResponse) { + streamingResponse = hTTPRestStreamingResponse; + } else { + error = response; + } } } finally { if (error != null) { - rc.response() - .setStatusCode(error.getStatusCode()) - .putHeader(CONTENT_TYPE, APPLICATION_JSON) - .end(error.getBody()); - } else { + sendResponse(rc, error); + } else if (streamingResponse != null) { Multi events = Multi.createFrom().publisher(streamingResponse.getPublisher()); executor.execute(() -> { MultiSseSupport.subscribeObject( @@ -185,14 +194,15 @@ public void setTaskPushNotificationConfiguration(@Body String body, RoutingConte ServerCallContext context = createCallContext(rc); HTTPRestResponse response = null; try { - response = jsonRestHandler.setTaskPushNotificationConfiguration(taskId, body, context); + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else { + response = jsonRestHandler.setTaskPushNotificationConfiguration(taskId, body, context); + } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } } @@ -203,14 +213,15 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) { ServerCallContext context = createCallContext(rc); HTTPRestResponse response = null; try { - response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, context); + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else { + response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, context); + } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } } @@ -220,14 +231,15 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { ServerCallContext context = createCallContext(rc); HTTPRestResponse response = null; try { - response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, context); + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else { + response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, context); + } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } } @@ -238,14 +250,17 @@ public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { ServerCallContext context = createCallContext(rc); HTTPRestResponse response = null; try { - response = jsonRestHandler.deleteTaskPushNotificationConfiguration(taskId, configId, context); + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else if (configId == null || configId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad config id")); + } else { + response = jsonRestHandler.deleteTaskPushNotificationConfiguration(taskId, configId, context); + } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } } @@ -254,34 +269,25 @@ public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { * Handles incoming GET requests to the agent card endpoint. * Returns the agent card in JSON format. * - * @param rc + * @param rc the routing context */ @Route(path = "/.well-known/agent-card.json", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) @PermitAll public void getAgentCard(RoutingContext rc) { HTTPRestResponse response = jsonRestHandler.getAgentCard(); - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } @Route(path = "/v1/card", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) public void getAuthenticatedExtendedCard(RoutingContext rc) { HTTPRestResponse response = jsonRestHandler.getAuthenticatedExtendedCard(); - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } @Route(path = "^/v1/.*", order = 100, methods = {Route.HttpMethod.DELETE, Route.HttpMethod.GET, Route.HttpMethod.HEAD, Route.HttpMethod.OPTIONS, Route.HttpMethod.POST, Route.HttpMethod.PUT}, produces = APPLICATION_JSON) public void methodNotFoundMessage(RoutingContext rc) { HTTPRestResponse response = jsonRestHandler.createErrorResponse(new MethodNotFoundError()); - rc.response() - .setStatusCode(response.getStatusCode()) - .putHeader(CONTENT_TYPE, response.getContentType()) - .end(response.getBody()); + sendResponse(rc, response); } static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { @@ -298,12 +304,19 @@ private ServerCallContext createCallContext(RoutingContext rc) { user = new User() { @Override public boolean isAuthenticated() { - return rc.userContext().authenticated(); + if (rc.userContext() != null) { + return rc.userContext().authenticated(); + } + return false; } @Override - public String getUsername() { - return rc.user().subject(); + public @Nullable + String getUsername() { + if (rc.user() != null) { + return rc.user().subject(); + } + return null; } }; } @@ -345,18 +358,18 @@ private static void initialize(HttpServerResponse response) { } } - private static void onWriteDone(Flow.Subscription subscription, AsyncResult ar, RoutingContext rc) { + private static void onWriteDone(Flow.@Nullable Subscription subscription, AsyncResult ar, RoutingContext rc) { if (ar.failed()) { rc.fail(ar.cause()); - } else { + } else if (subscription != null) { subscription.request(1); } } - public static void write(Multi multi, RoutingContext rc) { + private static void write(Multi multi, RoutingContext rc) { HttpServerResponse response = rc.response(); multi.subscribe().withSubscriber(new Flow.Subscriber() { - Flow.Subscription upstream; + Flow.@Nullable Subscription upstream; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -393,7 +406,7 @@ public void onComplete() { }); } - public static void subscribeObject(Multi multi, RoutingContext rc) { + private static void subscribeObject(Multi multi, RoutingContext rc) { AtomicLong count = new AtomicLong(); write(multi.map(new Function() { @Override diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/package-info.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/package-info.java new file mode 100644 index 000000000..00ca87bd6 --- /dev/null +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.server.rest.quarkus; + +import org.jspecify.annotations.NullMarked; + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 6771814f8..701947b42 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -13,7 +13,6 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Struct; import com.google.protobuf.Value; -import io.a2a.grpc.ListTaskPushNotificationConfigResponse; import io.a2a.grpc.StreamResponse; import io.a2a.spec.APIKeySecurityScheme; @@ -62,6 +61,7 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; +import org.jspecify.annotations.Nullable; /** * Utility class to convert between GRPC and Spec objects. @@ -535,7 +535,6 @@ private static io.a2a.grpc.AuthorizationCodeOAuthFlow authorizationCodeOAuthFlow public static io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfigResponse(List configs) { List confs = new ArrayList<>(configs.size()); - ListTaskPushNotificationConfigResponse.Builder response = ListTaskPushNotificationConfigResponse.newBuilder(); for(TaskPushNotificationConfig config: configs) { confs.add(taskPushNotificationConfig(config)); } @@ -811,12 +810,6 @@ public static DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationC return new DeleteTaskPushNotificationConfigParams(taskId, configId); } - private static AgentCapabilities agentCapabilities(io.a2a.grpc.AgentCapabilitiesOrBuilder agentCapabilities) { - return new AgentCapabilities(agentCapabilities.getStreaming(), agentCapabilities.getPushNotifications(), false, - agentCapabilities.getExtensionsList().stream().map(item -> agentExtension(item)).collect(Collectors.toList()) - ); - } - private static AgentExtension agentExtension(io.a2a.grpc.AgentExtensionOrBuilder agentExtension) { return new AgentExtension( agentExtension.getDescription(), @@ -836,7 +829,7 @@ private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.Sen ); } - private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification, String configId) { + private static @Nullable PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification, String configId) { if(pushNotification == null || pushNotification.getDefaultInstanceForType().equals(pushNotification)) { return null; } @@ -848,7 +841,7 @@ private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificat ); } - private static PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification) { + private static @Nullable PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification) { return pushNotification(pushNotification, pushNotification.getId()); } @@ -945,7 +938,7 @@ private static DataPart dataPart(io.a2a.grpc.DataPartOrBuilder dataPart) { return new DataPart(struct(dataPart.getData())); } - private static TaskStatus taskStatus(io.a2a.grpc.TaskStatusOrBuilder taskStatus) { + private static @Nullable TaskStatus taskStatus(io.a2a.grpc.TaskStatusOrBuilder taskStatus) { TaskState state = taskState(taskStatus.getState()); if (state == null) { return null; @@ -957,7 +950,7 @@ private static TaskStatus taskStatus(io.a2a.grpc.TaskStatusOrBuilder taskStatus) ); } - private static Message.Role role(io.a2a.grpc.Role role) { + private static Message.@Nullable Role role(io.a2a.grpc.Role role) { if (role == null) { return null; } @@ -971,7 +964,7 @@ private static Message.Role role(io.a2a.grpc.Role role) { }; } - private static TaskState taskState(io.a2a.grpc.TaskState taskState) { + private static @Nullable TaskState taskState(io.a2a.grpc.TaskState taskState) { if (taskState == null) { return null; } @@ -999,7 +992,7 @@ private static TaskState taskState(io.a2a.grpc.TaskState taskState) { }; } - private static Map struct(Struct struct) { + private static @Nullable Map struct(Struct struct) { if (struct == null || struct.getFieldsCount() == 0) { return null; } @@ -1007,7 +1000,7 @@ private static Map struct(Struct struct) { .collect(Collectors.toMap(Map.Entry::getKey, e -> value(e.getValue()))); } - private static Object value(Value value) { + private static @Nullable Object value(Value value) { switch (value.getKindCase()) { case STRUCT_VALUE: return struct(value.getStructValue()); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/package-info.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/package-info.java new file mode 100644 index 000000000..526496e36 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.grpc.utils; + +import org.jspecify.annotations.NullMarked; + diff --git a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java index 95b59a764..eab7aecf7 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java @@ -27,6 +27,7 @@ public int getCode() { * * @return the error message */ + @Override public String getMessage() { return message; } diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index fd1956b9b..028fea303 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -47,14 +47,16 @@ public String asString() { @JsonCreator public static Location fromString(String location) { switch (location) { - case "cookie": + case "cookie" -> { return COOKIE; - case "header": + } + case "header" -> { return HEADER; - case "query": + } + case "query" -> { return QUERY; - default: - throw new IllegalArgumentException("Invalid API key location: " + location); + } + default -> throw new IllegalArgumentException("Invalid API key location: " + location); } } } diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index a2e806ffc..c0fcbba18 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -22,7 +22,6 @@ public record AgentCard(String name, String description, String url, AgentProvid List>> security, String iconUrl, List additionalInterfaces, String preferredTransport, String protocolVersion, List signatures) { - private static final String TEXT_MODE = "text"; private static final String DEFAULT_PROTOCOL_VERSION = "0.3.0"; private static final TransportProtocol DEFAULT_TRANSPORT = TransportProtocol.JSONRPC; diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index d8952f87d..627feff34 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -6,13 +6,14 @@ import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; +import org.jspecify.annotations.Nullable; /** * Parameters for fetching a pushNotificationConfiguration associated with a Task. */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record GetTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, Map metadata) { +public record GetTaskPushNotificationConfigParams(String id, @Nullable String pushNotificationConfigId, @Nullable Map metadata) { public GetTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 858a4541e..be67a1e24 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -42,10 +42,12 @@ public JSONRPCResponse(String jsonrpc, Object id, T result, JSONRPCError error, this.error = error; } + @Override public String getJsonrpc() { return this.jsonrpc; } + @Override public Object getId() { return this.id; } diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index d3140b684..8278bec7e 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -111,7 +111,7 @@ public enum Role { USER("user"), AGENT("agent"); - private String role; + private final String role; Role(String role) { this.role = role; diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index 993665c7b..2bdfb69d1 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -27,7 +27,7 @@ public enum Kind { FILE("file"), DATA("data"); - private String kind; + private final String kind; Kind(String kind) { this.kind = kind; diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java index 236ad547a..b66533620 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java @@ -27,14 +27,15 @@ public StreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserializa JsonNode paramsNode = treeNode.get("params"); switch (method) { - case TaskResubscriptionRequest.METHOD: + case TaskResubscriptionRequest.METHOD -> { return new TaskResubscriptionRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case SendStreamingMessageRequest.METHOD: + } + case SendStreamingMessageRequest.METHOD -> { return new SendStreamingMessageRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); - default: - throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); + } + default -> throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); } } } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index e964d6b55..873f23431 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -81,6 +81,7 @@ public Map getMetadata() { return metadata; } + @Override public String getKind() { return kind; } diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index eb9892fe2..a6154d67d 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -1,10 +1,11 @@ package io.a2a.spec; -import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; +import java.util.Map; +import org.jspecify.annotations.Nullable; /** * Defines parameters for querying a task, with an option to limit history length. @@ -16,7 +17,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record TaskQueryParams(String id, Integer historyLength, Map metadata) { +public record TaskQueryParams(String id, @Nullable Integer historyLength, @Nullable Map metadata) { public TaskQueryParams { Assert.checkNotNullParam("id", id); @@ -29,7 +30,7 @@ public TaskQueryParams(String id) { this(id, null, null); } - public TaskQueryParams(String id, Integer historyLength) { + public TaskQueryParams(String id, @Nullable Integer historyLength) { this(id, historyLength, null); } } diff --git a/spec/src/main/java/io/a2a/spec/TaskState.java b/spec/src/main/java/io/a2a/spec/TaskState.java index b2d296980..655d20919 100644 --- a/spec/src/main/java/io/a2a/spec/TaskState.java +++ b/spec/src/main/java/io/a2a/spec/TaskState.java @@ -40,27 +40,17 @@ public boolean isFinal(){ @JsonCreator public static TaskState fromString(String state) { - switch (state) { - case "submitted": - return SUBMITTED; - case "working": - return WORKING; - case "input-required": - return INPUT_REQUIRED; - case "auth-required": - return AUTH_REQUIRED; - case "completed": - return COMPLETED; - case "canceled": - return CANCELED; - case "failed": - return FAILED; - case "rejected": - return REJECTED; - case "unknown": - return UNKNOWN; - default: - throw new IllegalArgumentException("Invalid TaskState: " + state); - } + return switch (state) { + case "submitted" -> SUBMITTED; + case "working" -> WORKING; + case "input-required" -> INPUT_REQUIRED; + case "auth-required" -> AUTH_REQUIRED; + case "completed" -> COMPLETED; + case "canceled" -> CANCELED; + case "failed" -> FAILED; + case "rejected" -> REJECTED; + case "unknown" -> UNKNOWN; + default -> throw new IllegalArgumentException("Invalid TaskState: " + state); + }; } } \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/TransportProtocol.java b/spec/src/main/java/io/a2a/spec/TransportProtocol.java index afd11c7a1..d6c2922d2 100644 --- a/spec/src/main/java/io/a2a/spec/TransportProtocol.java +++ b/spec/src/main/java/io/a2a/spec/TransportProtocol.java @@ -24,15 +24,11 @@ public String asString() { @JsonCreator public static TransportProtocol fromString(String transport) { - switch (transport) { - case "JSONRPC": - return JSONRPC; - case "GRPC": - return GRPC; - case "HTTP+JSON": - return HTTP_JSON; - default: - throw new IllegalArgumentException("Invalid transport: " + transport); - } + return switch (transport) { + case "JSONRPC" -> JSONRPC; + case "GRPC" -> GRPC; + case "HTTP+JSON" -> HTTP_JSON; + default -> throw new IllegalArgumentException("Invalid transport: " + transport); + }; } } diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 2ddfdd259..47e3e5ecb 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -46,15 +46,18 @@ import java.util.logging.Level; import java.util.logging.Logger; import mutiny.zero.ZeroPublisher; +import org.jspecify.annotations.Nullable; @ApplicationScoped public class RestHandler { private static final Logger log = Logger.getLogger(RestHandler.class.getName()); private AgentCard agentCard; - private Instance extendedAgentCard; + private @Nullable + Instance extendedAgentCard; private RequestHandler requestHandler; + @SuppressWarnings("NullAway") protected RestHandler() { // For CDI } @@ -106,7 +109,7 @@ public HTTPRestResponse sendStreamingMessage(String body, ServerCallContext cont public HTTPRestResponse cancelTask(String taskId, ServerCallContext context) { try { - if(taskId == null || taskId.isEmpty()) { + if (taskId == null || taskId.isEmpty()) { throw new InvalidParamsError(); } TaskIdParams params = new TaskIdParams(taskId); @@ -153,9 +156,9 @@ public HTTPRestResponse resubscribeTask(String taskId, ServerCallContext context } } - public HTTPRestResponse getTask(String taskId, Integer historyLength, ServerCallContext context) { + public HTTPRestResponse getTask(String taskId, @Nullable Integer historyLength, ServerCallContext context) { try { - TaskQueryParams params = new TaskQueryParams(taskId,historyLength); + TaskQueryParams params = new TaskQueryParams(taskId, historyLength); Task task = requestHandler.onGetTask(params, context); if (task != null) { return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); @@ -168,7 +171,7 @@ public HTTPRestResponse getTask(String taskId, Integer historyLength, ServerCall } } - public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, String configId, ServerCallContext context) { + public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nullable String configId, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); @@ -258,6 +261,7 @@ private HTTPRestStreamingResponse createStreamingResponse(Flow.Publisher convertToSendStreamingMessageResponse( Flow.Publisher publisher) { // We can't use the normal convertingProcessor since that propagates any errors as an error handled @@ -265,7 +269,7 @@ private Flow.Publisher convertToSendStreamingMessageResponse( return ZeroPublisher.create(createTubeConfig(), tube -> { CompletableFuture.runAsync(() -> { publisher.subscribe(new Flow.Subscriber() { - Flow.Subscription subscription; + Flow.@Nullable Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -278,7 +282,9 @@ public void onNext(StreamingEventKind item) { try { String payload = JsonFormat.printer().omittingInsignificantWhitespace().print(ProtoUtils.ToProto.taskOrMessageStream(item)); tube.send(payload); - subscription.request(1); + if (subscription != null) { + subscription.request(1); + } } catch (InvalidProtocolBufferException ex) { onError(ex); } @@ -325,7 +331,7 @@ private int mapErrorToHttpStatus(JSONRPCError error) { if (error instanceof InvalidAgentResponseError) { return 502; } - if (error instanceof InternalError ) { + if (error instanceof InternalError) { return 500; } return 500; @@ -333,7 +339,7 @@ private int mapErrorToHttpStatus(JSONRPCError error) { public HTTPRestResponse getAuthenticatedExtendedCard() { try { - if (!agentCard.supportsAuthenticatedExtendedCard() || !extendedAgentCard.isResolvable()) { + if (!agentCard.supportsAuthenticatedExtendedCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { throw new AuthenticatedExtendedCardNotConfiguredError(); } return new HTTPRestResponse(200, "application/json", Utils.OBJECT_MAPPER.writeValueAsString(extendedAgentCard.get())); @@ -387,7 +393,7 @@ public static class HTTPRestStreamingResponse extends HTTPRestResponse { private final Flow.Publisher publisher; public HTTPRestStreamingResponse(Flow.Publisher publisher) { - super(200, "text/event-stream", null); + super(200, "text/event-stream", ""); this.publisher = publisher; } @@ -399,27 +405,15 @@ public Flow.Publisher getPublisher() { private static class HTTPRestErrorResponse { private final String error; - private final String message; + private final @Nullable + String message; - public HTTPRestErrorResponse(String error, String message) { - this.error = error; - this.message = message; - } - - public HTTPRestErrorResponse(JSONRPCError jsonRpcError) { + private HTTPRestErrorResponse(JSONRPCError jsonRpcError) { this.error = jsonRpcError.getClass().getName(); this.message = jsonRpcError.getMessage(); } - public String getError() { - return error; - } - - public String getMessage() { - return message; - } - - public String toJson() { + private String toJson() { return "{\"error\": \"" + error + "\", \"message\": \"" + message + "\"}"; } diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/package-info.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/package-info.java new file mode 100644 index 000000000..8d4e4063c --- /dev/null +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.transport.rest.handler; + +import org.jspecify.annotations.NullMarked; + From 5522df76c4d3a46ad4b71a009e00ae5aaafa8d3e Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 13 Oct 2025 04:35:42 -0400 Subject: [PATCH 159/493] feat: Add some additional methods to TaskUpdater (#339) # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) --- .../src/main/java/io/a2a/server/tasks/TaskUpdater.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index 1991c837a..204e4adca 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -58,6 +58,14 @@ private void updateStatus(TaskState state, Message message, boolean isFinal) { } } + public String getContextId() { + return this.contextId; + } + + public void addArtifact(List> parts) { + addArtifact(parts, null, null, null); + } + public void addArtifact(List> parts, String artifactId, String name, Map metadata) { addArtifact(parts, artifactId, name, metadata, null, null); } From 75b41508f322f9a83ad49a39534accce06d0cc88 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 13 Oct 2025 09:56:38 +0100 Subject: [PATCH 160/493] test: Disable flaky KafkaReplicationIntegrationTest (#346) #345 is to enable it again --- .../replicated/tests/KafkaReplicationIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 55220aa54..8756820bc 100644 --- a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -41,6 +41,7 @@ import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -48,6 +49,7 @@ * Tests the full A2A message flow with Kafka replication verification. */ @QuarkusTest +@Disabled public class KafkaReplicationIntegrationTest { @Inject From e25328b0e31257194f2f37057b450d1970149734 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 14 Oct 2025 19:52:39 +0100 Subject: [PATCH 161/493] fix: Implement reference counting for EventQueue to prevent premature MainQueue closure (#333) Implements reference counting for EventQueues to fix KafkaReplicationIntegrationTest and prepare for improvements to the replicated QueueManager. Additional fixes resolve TCK timeout issues discovered during testing. ## Changes **EventQueue Reference Counting:** - MainQueue tracks active ChildQueues with reference counting - Prevents premature closure when ChildQueues are still consuming events - Fixes KafkaReplicationIntegrationTest failures - Prepares infrastructure for replicated QueueManager improvements **Fix ForkJoinPool Saturation (TCK fixes):** - Inject @Internal Executor (15 threads) into all transport handlers - Changed CompletableFuture.runAsync() to use injected executor instead of ForkJoinPool.commonPool() - Prevents streaming subscription timeouts under concurrent load on CI (3 threads) - Affects: RestHandler, GrpcHandler, JSONRPCHandler **Improved Queue Lifecycle:** - Removed awaitQueuePollerStart() to eliminate thread blocking bottleneck - EventConsumer now manages queue closing on terminal events - Background cleanup to avoid blocking request threads - Fixed race condition when clients disconnect during streaming - Better terminal event detection in ResultAggregator **Tests & Documentation:** - Updated all transport handler tests to pass executor parameter - Added executor configuration section to README.md Fixes #248 (it became necessary as part of the work) --- .github/workflows/run-tck.yml | 82 ++++ .../java/document_symbols_cache_v23-06-25.pkl | Bin 1473486 -> 0 bytes README.md | 32 ++ .../core/ReplicatedQueueManager.java | 5 + .../core/ReplicatedQueueManagerTest.java | 7 +- .../server/events/EventQueueTestHelper.java | 10 + .../grpc/quarkus/QuarkusGrpcHandler.java | 13 +- .../server/grpc/quarkus/A2ATestResource.java | 8 + .../server/apps/quarkus/A2ATestRoutes.java | 8 + .../src/test/resources/application.properties | 2 +- .../server/rest/quarkus/A2ATestRoutes.java | 8 + server-common/pom.xml | 4 + .../io/a2a/server/events/EventConsumer.java | 11 + .../java/io/a2a/server/events/EventQueue.java | 113 +++++- .../server/events/InMemoryQueueManager.java | 61 ++- .../io/a2a/server/events/QueueManager.java | 9 + .../DefaultRequestHandler.java | 287 +++++++++---- .../io/a2a/server/tasks/ResultAggregator.java | 139 +++++-- .../java/io/a2a/server/tasks/TaskManager.java | 2 +- .../util/async/AsyncExecutorProducer.java | 55 ++- .../io/a2a/server/events/EventQueueTest.java | 39 +- .../events/InMemoryQueueManagerTest.java | 21 +- .../DefaultRequestHandlerTest.java | 59 +++ .../server/tasks/ResultAggregatorTest.java | 15 +- tck/src/main/resources/application.properties | 17 + .../apps/common/AbstractA2AServerTest.java | 382 +++++++++++++++++- .../apps/common/AgentExecutorProducer.java | 23 ++ .../a2a/server/apps/common/TestUtilsBean.java | 4 + .../transport/grpc/handler/GrpcHandler.java | 5 +- .../grpc/handler/GrpcHandlerTest.java | 71 ++-- .../jsonrpc/handler/JSONRPCHandler.java | 15 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 84 ++-- .../transport/rest/handler/RestHandler.java | 12 +- .../rest/handler/RestHandlerTest.java | 34 +- 34 files changed, 1383 insertions(+), 254 deletions(-) delete mode 100644 .serena/cache/java/document_symbols_cache_v23-06-25.pkl create mode 100644 extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueTestHelper.java diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 714953c9f..d88652c91 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -97,6 +97,88 @@ jobs: done - name: Run TCK + id: run-tck + timeout-minutes: 5 run: | ./run_tck.py --sut-url ${{ env.SUT_JSONRPC_URL }} --category all --transports jsonrpc,grpc,rest --compliance-report report.json working-directory: tck/a2a-tck + - name: Capture Thread Dump + if: failure() + run: | + # Find the actual Quarkus JVM (child of Maven process), not the Maven parent + # Look for the dev.jar process which is the actual application + QUARKUS_PID=$(pgrep -f "a2a-tck-server-dev.jar" || echo "") + if [ -n "$QUARKUS_PID" ]; then + echo "📊 Capturing thread dump for Quarkus JVM PID $QUARKUS_PID" + jstack $QUARKUS_PID > tck/target/thread-dump.txt || echo "Failed to capture thread dump" + if [ -f tck/target/thread-dump.txt ]; then + echo "✅ Thread dump captured ($(wc -l < tck/target/thread-dump.txt) lines)" + fi + else + echo "⚠️ No Quarkus JVM process found for thread dump" + echo "Available Java processes:" + ps aux | grep java || true + fi + - name: Capture Heap Dump + if: failure() + run: | + # Find the actual Quarkus JVM (child of Maven process), not the Maven parent + QUARKUS_PID=$(pgrep -f "a2a-tck-server-dev.jar" || echo "") + if [ -n "$QUARKUS_PID" ]; then + echo "📊 Capturing heap dump for Quarkus JVM PID $QUARKUS_PID" + jmap -dump:live,format=b,file=tck/target/heap-dump.hprof $QUARKUS_PID || echo "Failed to capture heap dump" + if [ -f tck/target/heap-dump.hprof ]; then + SIZE=$(du -h tck/target/heap-dump.hprof | cut -f1) + echo "✅ Heap dump captured ($SIZE)" + # Compress to reduce artifact size + gzip tck/target/heap-dump.hprof + COMPRESSED_SIZE=$(du -h tck/target/heap-dump.hprof.gz | cut -f1) + echo "✅ Compressed heap dump ($COMPRESSED_SIZE)" + fi + else + echo "⚠️ No Quarkus JVM process found for heap dump" + echo "Available Java processes:" + ps aux | grep java || true + fi + - name: Stop Quarkus Server + if: always() + run: | + # Find and kill the Quarkus process to ensure logs are flushed + pkill -f "quarkus:dev" || true + sleep 2 + - name: Verify TCK Log + if: failure() + run: | + echo "Checking for log file..." + if [ -f tck/target/tck-test.log ]; then + echo "✅ Log file exists ($(wc -l < tck/target/tck-test.log) lines)" + ls -lh tck/target/tck-test.log + else + echo "❌ Log file not found at tck/target/tck-test.log" + echo "Contents of tck/target/:" + ls -la tck/target/ || echo "tck/target/ does not exist" + fi + - name: Upload TCK Log + if: failure() + uses: actions/upload-artifact@v4 + with: + name: tck-test-log-java-${{ matrix.java-version }} + path: tck/target/tck-test.log + retention-days: 2 + if-no-files-found: warn + - name: Upload Thread Dump + if: failure() + uses: actions/upload-artifact@v4 + with: + name: thread-dump-java-${{ matrix.java-version }} + path: tck/target/thread-dump.txt + retention-days: 2 + if-no-files-found: warn + - name: Upload Heap Dump + if: failure() + uses: actions/upload-artifact@v4 + with: + name: heap-dump-java-${{ matrix.java-version }} + path: tck/target/heap-dump.hprof.gz + retention-days: 2 + if-no-files-found: warn diff --git a/.serena/cache/java/document_symbols_cache_v23-06-25.pkl b/.serena/cache/java/document_symbols_cache_v23-06-25.pkl deleted file mode 100644 index a0c1ae55d5d5ab18d03f277b05ef64b4d5644aef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1473486 zcmeFa378zmbtVXk0Emm=2@t#!O%g1CjcN)MFA#*#02*YoaWuOdVv7<)Wmjc)Rdsb$ zDKiTPL1-jd=1Ou&mewWTk}r)tYp*|j_8dOOV}Blx$M-Sz+T*o7KE~tmr}cVvyz8Cu z=WoB=eJ|ogWJF|SW);v!HdX!MLsdn_@iN{YFJ8QO@#13}zqnz;O&jp2MgqSyvgB49BQ^J;J5p_qxQE;ko@K;c@EW0a*(uv`&?=M`S%=p=Gmvcxu@OIocm1oLbrg8Z)~_r zkpF>dvk2`Jc{@eDoo;92Vzp83PHgOK^4-RQ*Ts^Xg3$Ft_H|>u+VHv)8#-G{71wu5 zq36S|cIRdfNvunukMwQ`YBOUWm}YGL%Xsw`h^8Hm%Y%f*1Mez z-OiSJv*cn~*6Ph|zuN75a=u#k4v&nCoCn&2kwtf|>W>7?wqNo}%|_@q>m#G%*oQ5@ zS%a4G>p|AF%h>H*(J_^+ogMC6(5$yZZ_*7b-OjZ&CTxfA)uGQW5;+sg`PS{umQtl! zFZ*5t@YLDda^YLpJ=;bWZtC2s;j>%0Wnz0}3qB%-Zvsq24BraBZfdtHw;MU`kdVOq z4?&K*E4#FY5y;ToBYa?Zg(>eNZ7&E<0Q+mVRQEzV)*VzXNAR`yngD!Y-0Jvo76<-SU>a{pRmukv8!p?2lr3ths) zP|0nSyn289*`o0We@z?w!L*1!`0KWHz@HcL#2+tr@O?Bd-Lv;>yE0sP-n#(9dt+tD zd%OE~xAXqqrKrz^QN?YP>o`H3^yb}mJtP_RGEe4n_l)1hsrz~Nsps9N4?X+T^M}gb z({s`R!t&!gDmSwNJ(Kl+6Q z#N3VY+A?*2b`_>>qdQW%Kbpg5%sKq+m7K%-q0pGjXI27+-F9c=T(f+o+u7H0ON*ci zI;jYI0rWDP764EX+T|`Q=mj)8cYwZBE!Trqu^zk;UTJwjx3g{f%F*!CYmbvqCVdEM9>A zKZi`1>?A;>GBvTOGL2bC&^!;aND7)WzkCz?M5XZqN|wWrcB_;{_-hDqykS!sCp()@ zpL^-0F~HW;#7?cDBTx?XLXv3s>qAC6nC56XK;~gPm8Hrj^9!!e=L%evO1o0+yx#Jw z7v0cvU`-iTOU`_?;ntng&4mTecQ}Wf7wPvYSYI~%E5!vbyBxx!(YP&>oDzM$vXV?#Cl-ePvnqw1)X)wG(wzlR5oc0ysgy^DqvHYuP!7E zzfp~c`0J>_Lrg159^$VT*8>m#O%8c@Sm)s+Fz0MDtj<@tg;ZgbV=zwR>Nb2h4R%`F zpggWI6o0+PU?`^Tmkh;UPpt=r{<|D9^vQIFavm{W@~;{jchM@6Y@Wy4V3oCT^n}Jy z{55WH6w{uP9K~NRuLq9)Ne($WrgL<<-D=gVo_}OYvriX^Sw|f!4>dO5>bDz>YGZ*Z zOF?`V9n;v0zZMPlV%n%=FaA2a9@zWWIb`pG&R!ULrM8b7AZOgxk?GI}J`Fo3noF%Z z>|o8+y;JS5?R!Vw2U`Y5f!Qi2VR6&&EUw-A?icfW6MCiLIJ_;}s1n?}uNT46IB*+IEQ*{N=0%tlXJP ztjzxH%^Nlx8*iZ7!8Ql}TJ5C>*AQG*vhgp?aZQ`GbCT(FCM-Rv%v9L4O z3q3Ss0yUoesT!JYwCnYvGrd@C!812>=ArFI^U|;rRACF>X+i=2QZ?|LaR=BT3b9}{v)C5keSw{z)vU8~#R-8+I6FUjjCD{&xnN{Bg9u@XG2ahs^ETu;a z5^TfHJUU(N=PbRZ8k`R{8!vg`3^ZN9|6n}&S0<5#&`;CM>+6JDufy0wM-?WpH){g> z1De1NEdu+4kaoKi*!b&S6xhr;1)J>VdNZQ-a6Se1?u{V0&w*2(UoCqKG$gvQOPxff z@-bxNEe>MFV*tP3yc9UEOrJYDHF;tT2aO{B_+Ce`?9H_o3PZ9X=jyegVXD1YEf@9^ z*iiB_l0u|Q5rDWmQeeU&F!RH3XV`UKghdAob5;6ceL4weLAGGs^SI<-V&LkvqBDap zfuaW+2eZ>De9GP>kxZM8sm26f%#z=saoGh()L# z;2AFbMt$GCavB@L1)qF<%y`Z5K)4t*=`|pOwD&It?K!Nipw)zh#Gj=IdwJ)eK$+3s z$x1*jB&rJZnUIT325LuMiyGG9jqMV)*$_jL z6rz0(mh3PgL#kc6pI{zsSPK*IKlo2WCehGY+#)To{O_q{<{`Vp~V?@@yh{ zAsVGqUI0#VqYDeZx8S1U>zfIjWHTR=9rRYie0o!AjU0jb8UWa!t_6yr+hP@n_f#YS zig=bcY1*WRdWFzmQJboGlPwf2vPwuhm~a&LesZ3T$wjLci}e@m(h+M1KYyl`0-pru zkY(uwBHoHJQA$S9830d;SQjM4RN{hAn__P*`bhL&98!!Sz(HB`Tnmp5xb!FW=_7W`E;8pk+ zj$G{_hn502_7t7p9Q20+oSOJAQgx&CI(8DPSt-wyJSg%!_-N{ zOh=C+9YmQrBXJ|v@=WC`3^99_Q?8a_jTi!{Y|?a{i!QkJA>}O~&>JH1v<1>r%?7Df zxNt5g-}{+Jzobp1zh;?8zYJ-+VP0wvfm`!EB!h%U* zY@EqAKC$Xboz@&^v^b;>!xHKRu?o|cQ%hX6FYL$Rk3)k^UP2Alp2AqhMH~|i(FjrA z4J`NVma`NDFk-8XYN$M-bIgy;fuqf z4EtcbtTxKn^C~*!*eAo1?}10&Ie&&GMOIpfU31=z$tQRVT-ccT3ixVBOX3_o*KF23 zw*lP;RxZEYLNm^b+`^)Y)&@A#m%Q>6?4qF{2djLu+%9>(OhA#j0Ba!6ABCkUvx$35 zPK^dv8YMOvjhBZlT|LaZLlLItOB7kx&474?s+lN6IvqTxhOZ5COT?nkzhbQ2SYbd? zAG3m-QLy{bUI@z{rv#QG+*5Lv+Ck{dfrYGGhQ6Bfz*grumzXc5AJVh~YxSaYPFU^q z$r37b8&}AF$Jgi$XYL9!)5W?KPIxgP->R0-ItOT{oMIBvXf=x$fyR@~hKE)!q+L#3 zMUFPcAi+W{qiXTMUZBo~$wku><+Ru+9|!y{o@+?{$hOo$fK}mK$*becGp9z(qmMK& zitvVETnS5vHYJL=4vUJv#Mx8UW|oTVAXtY!AdujqlBBM7F}F_eYc3d+qX9)SK}j+D zB$C~D@)J4ZDzKjHw$i)cNp1L(N_;g^J++02Wg=2Khr2Z(QLr1`EDQ zV#lGdcE(RSOVwb>4NDbxMZQ%K(>bsj{8N;fYuw4WZVpd;6}4k51SM?oIwcfbZKhs9 zkHvBoLD}TOhZ~qF`y@#&DU!J2IxM#e6iLM20=fha*)AT}TmcG7>qd!06H+<_kr3lu zCmL5eQAgPnQ~W^G5WY;zC}fe2Xy)KeY8vA6+H4LprROfiRfyiq>A#`siD$OnWKk4< z6Vf6NMf^3&EG`Zxij%l~7r@R3dUpUB@TPK{9L>4%IG#`C@&DeQpgf+cH!l@aJ6zW) z2^iY!qiA1zHL;?KCZ}LMsGFl5r^Gu2bXV`lANxfdYs#ks%;hAEOH;>Vvra z;@=_!DVpKfQWdN>kD93Q3R{p(HOh-_spPf70*Z@q)GooeMi!x7R5L|li0biEQ7_bFL_NAm;ATJhNf>?% zjiytFoqjO2RY2XveDmN3!1pQV;P~+(I0>M2YN>etcWMr%$k9R;uS2%b^QDFVkR zjFz^~98Kv? zJULCqVI{~b{yM;>Mp@-KRm^J87c`>vcn?`5kT8#fiC?efnr89GIw>na8T>~=kTkHs zqiI2(MM&`DCPE>;px95-LeyN+iZ!;-OC+!MHwyX1C@NKo4KG?I*wLCt< zab-yn=8-Hy`q_dE1ZxQ2Su1QX(o}(o#zq9vg~{2(VC|aQmMWSR@n;{8Ox~(OmUjeD znlxz|G)q@D%pb2vb8jPYiaI7zZKp^$iHS~u2COwxS8LMeHR)xSQ1?q|vT3q+Z_YFK z>0;;!n;5!Z7enmlJ3tJ1`4mG_=%AnE=V^ySvRoyFf`DGDv zkF@vs(S+fSMJ!SYHycouC#htiyk(UhygI%~8i|*}yc>Cv5}%JrLO!jIeV- z$*<0NGfh<5`uB}K^SnOFU$Kqy!&~#B?~L8LkLPo+A52o045NA6nsF4=rZ_CL zv5Y_w{_{7k6Jc$s>nF>skE@5}TRR~I!oM187T>vJl=76V*&=%|=EYwya)q-Qci-Z! z)5574hb{hb(^|C8LHi6!0&mdF$68JF!U6;E3FiL==kA#42jvr3%*#zLXgm(SPH0V* z>|@5jJ~sTs%YyvS>wtIe!!3$;z_M-yi7H9>l`KX)pKmdN^)A_RXA_L?Z#GbJBIE5d zNFn`kKP%1Cy7Y8y((?=^4oe-0{XETdq(?wULRyX_)0%g8hi}fO04*e)E6nO{5TEJj z(3hR*c{!49T*jix(tD`J5wuAIP%WB=N2Zmtj`2lxqF`ru1b+CWXU^R?xP%2UJ(xE;|Me@ zhrJlaEF=2SpwevD%cJEoN;p<&%w5r9{POjI8QKOW%X7C>?AT8h<+ig8q%)nb$ho!a zM{+qfJHISUUE-8V4_qWYOP%{@8ZZDRCSmlHZ)N$zB;-i+7!j3Qpm;3xpbWQ|4Z}JU z)`3Zv>P*38SQYLsVL)GdD5l$_Hsg;Y+rvQ+ztTqE%p@`=PtKvt4$(mGmK z?*uajaP1SWvQMwQ&}{>1bA0PY?Wghy**^t~zR+pRDHSZqDFafc#c+A51?q0 zwm_$A6;T{-R~+i)77g<`PW6)YICuub38H}Axb&BOdQCCQCpwqTgAFG3FgoH0^DR4= ztsG;N^$USlgnNz}REJk6T!g&7pKuNW%iy{batKw{gn_6QKSlHCw#~iluV z4E_>-umi&8aJWaN8Nh*8fHWK&fFsWbVN>@K95KNA+{$?5)I3EQ4;q4XN{XtDi_Jw( zJ=`2vr)Gs(EDMgr>~wShDt;)>ik9ZpY&`IWXi2F0botG&%||!JLGC+j(Q>~THQ309 z_IvQB5k2+{%LDgy@=KHWPmD&)!x8ekGIK4Z|6@L-bW3rvVA^aStYkJ#b*LC|sHIU8 zfFwsKN_As&@@+3V@QxuDECbc94&$(8Zci*wLxN|87qM;Ai5QYLSUr0-ufjs>riSd29 z#Q2|V5`*Q5BnJD*B2Fu}>*iWw{MUR+3|t`zy^cu?I|mu;QGJUT8Q0ImVX)+rceuO_ zm~#~7F@t_csiIF~;Nn2jSbV}XWDjTtN#`RG=h{5|0z18S0w-;D2Ji%4Q2i=~xsh~W z8S$snq>KOYMpF%MZi+ipga6MSn5*Sqw#JL?d4^rbCu!jya zrc-<=SRUbrAdFS!y$12fiIrl9T6QdV@={+Cr43Ra-(=uj6AM+01@zZ}{Uv+@+-U$z z%{Nsia?Fi?#x{gJ>5AkFPc6C21-FC9)+_ z%|`}xtYuPxEC>$pRp$%>^h2yJ=e_*nFyohaVSDii$|g9mT=diu4igE)*17r>13NzM zBmAWZdl@N>!d(~kMhIbeB6b?(Gi#E7$WeyT2w;Aa+d0H7P3gPctR}v-@=N+u!hTkb zoe!3Hms}IFpW^-**jg#@>wmW+M~KAOwN__Xp=b+-{d~dJ&XamOn1|hKRNDqCsQm7% z>_Y1DbmcR@m3IvBN=#9O8NZH%rDPcXU@4|yY9q2afxj>6ygOy%@9}t^^}yeueDZe~ z`FlNV+^I{C1ctt>Gt09v^tE`N^}x`Pd@^)z>Wmr#yrDp&51o+F1;?P2v+Co8 zEKETY42Qr=%X}-nUVrut=o&dZJED#IT;rt5zHx0(P|dS3(}$SEnN5Oz*fOIpTHL3X zw_+?4dMQpJX0RmxYAYnJb?Wn4R9g>%0=_a=a9X+_H0$NN>(TZyJZ3Q*IE=0gV`L02 zl*7xa2N%kLUD1a{a)b`Pnva5a4K9>}9sz5uv(7J+lZlaEn&$@BkuhuA%Wwh&?l}WjsKIq)u*EF= zoaw?Zas69zL_LHa!p-bQsOlYf9a*+);9KN4Dz#JiH!d5Nd!VzOaCU?ZYX&Vex>Qg# z=SP-g%qA4uQ2E}&rUqsgmfkpbtdYtO`FTl~@BGApL2qCOIFOLw%UE0bhCp}XjY zrF4_OC?hQEWY-sbja%^50ugU5+)gh#1K>%p;;Sw6Va=Dt=rXW4q}aku-5VNQW`(B9 zwZF_NMF2CO3Kk+&yTTxTdBKg#VpC63wr1juD5|$6p@2371nc9$y*Og(;-5Ij z4erIMf@A35UYxg2BBwaGu(Qy&aq)2lwI(?!|#+ zlHw)LE#+ydKd!6n4muDO4BSzYpX=&>a=sg12jI+VI0|J))E!)n!tX?f4GhqnX`38- zG46`B@>ipz7_?J>BV7aR0M!BbQW{p5Yekw1B=+g+(DN3koNQUlP`IJR`v_d^7DV8(ft@KD@mDl0wEJ7)wlNh1&|@924Fp--z9L zkk6|UQpBsVa_4q4Sft5)#6b{!>F*Ku@d!aX5?lZLkQ>vpm9wYpqziZ17zELmduw8O zW}xpz>_(!>WeTF7GGy5Fm#miHfWa)uIdsJ&J@0zZOr&Cfugn0yOm3L@RZTK6fRe#J z-(pedpF!C4rmz9g;sf>8bs9E3MOv^xD1!jnBJ#kBFHTypgyotXcLKlR0NR|lLWf@O zF_`tYF{2#93H?Fz#mjK-pm2Q@T7~$u2OGnu>M6IPyG~;7%9(o*eGynFf-J53=!^Ow z9|Z4%b^9QApAcb5K3MqthJyE5P-l8uX+F1u0B{srHE#jnbVe$UTP(28AWRqx6k4(@ zi2!BlDG75GOvi=LGK7y*r%^O2;!0871Q+8JKXr%FL6|T+b;GW$!A^?|Z^R95o^AUq zGAo>A#HB!d??Btl!cLBk3n`wesXTxzGL%*NnIWW_+i?bA!c1w>dzdg?Dy?WZ zlx+|{YN^tIpeRciDXiJKjgE-%0Nj4it$d^sPHg&+QfX6Wh6#L8Oghh|Ghb}p>lO%L%Z^p`l}70Qx;d1qJ_@$*Z)54%HjncoUPf} zNh6S=qhOC;tR%nh3+(ONm5;{N)K~iu zJT&ueXxHW}$ymkaGM;UzcPqO(Tk1`|pUQ~2x$RfGoxI>k^jPfzyhmq;I~O$T?a-Ta z!%BDUq1D;pdl(k|A`!IQIO}YjYnHEcJNsI0X%UY0I;l8&q1)L~!Z^&n*XUm8b~b~S z;5EW-?JV>iUQbYaKX#%iXKEjS)QRo255fm#(tva)2b~F|=Xvr??RKr4QtS=`e^lGU zP^<#Q5PJ&27^ISucu+dJn87xz!I$|PY_`9_U7Xa71}VFEGQ_U5zX287Y80d7JLwHr z{B+cDyS0n(LbXfyUzK`f5k7M>kZMav+Be{T4Ww($#c~a=Tc~{m{%hBKq^ODg2wREd zx1{eG;I;}b~3f=1lRvE$GHB% z%C=c1$QL{a(69Ao?^vica*m2~*%5XxCMp+I=QVAxc^@{hWq!dg1fpV{;yXd_wu~2a3#MSp8eV;xB(KaHYIT6 zg00ngx8%idB0=jdiUE*rtF-|^w&Qo0<(Zqi*Us3;+wVzHQ_MuGW<`p21MDhHo{RxK z(3-kF(-nc#tjnhd^uhmCR@6CV>r}ns#OHjn1UELm56?Uvz?rRf944lAr6K*=HV15z z7@VtWWAI40P0g*m+Z4D&fqz4m@7f1e&|9ioS?KKN@85&(U6*?~u^vRio1IM|%#Y{^ zsC8Xs<-2Z#8t$b7-4*OWLB~R;4;MyGyL1_Yetcvc_TQG^_7VFGz)taHdH)M?RXY#ZeE=Dl zUg)A9F-&*&K*unDrXl#!o1m#Z3Rx#M)gFTngo*~4og7{!WX|wpbpJbOD|R=3eyf2r zN`4ZGy&Hs6=Dru^wi!!hXWu zRy+3g*s*sXPu`RSxqJEZ?FQs1xwyU{_iC;ocg%8~XHSfWJ8ID~6kV@gV-o6daV$#! z*N$0%o6UCIJYdJolNq>qkUt+Xa6`%a2{)a4nYWVdKp4&x`4?gz$}^&7pW6ucA)kZO z9)7j#F^rIn(T)U12MmiDG+rJCcQPA;oOqlyxK>=6k(mj>Uh=4=C}syK*)p19(i4vq zp7T$7^KQEyvZHB8RV-AG5<%78ZjlZII_z#O53fwcO42i1!pSKO;Zf0eKd^XnBG{<| zkq{HI6sy75QY(ZT9@U=1u}lM^u#8cMkJ#VX z#uiZoyDAn|4|!IsQl;tv+DmZ2!?SgasIFLXgR|iL9)EE=Y3L+BUtO?LA?s|g3i-X# z*oXKR#Rp_OxtI0c+)b27ON#vnW6%O{hJN#l@x(s(*!(s+tLFBp>sC69i?=lJv6 z4KPyjeFBVfn|od8D{tl*bhGaR>a4h9CjA)yrGmm?K$8T_m5Le?O#tI%F(a$bQDF1Q z1)o!mk>HL*bt?4ZQWUd8eLZ3wsj#aOydIc++x5C#lfWFwiUc2X>I)6j0aPWTaKW7W z$svN{yj{NeKbhyr9JNC#DOwE&Fv(Ic-xi_6xfV`;wROXWkpl;|IR~8a#=#}FIpS28 zT6J#;4uG-fCQfr64!U^>D+;z}61jI2p(d;h|Jb8|NefNSCecK}QvxXL@P?t_$hKpn ztrmEUu%@R=&6Zc*M(ev$-3Z^Pev8F~bPQr~ENvM{=s#pYi9juB6 ztK$9x=da|f7_Xb@IC)qVr$`T^G10QJ+-sv0ItIG(Kv!le!3l0NyK&Rm*O{)&;*9ZK zYV^*!6|_>^iUX&Z?K5DDrNDt%&M!YGLtk}Jt);ElE34-GqL3<`To@%@$6t9Tmw4+7 zon!qvv&1TCi%4WE>$LLSz%X!H`4WC4IjwvrJVZ|fH7|G|eA$w0cUoCnBzyR5!VwMb6LPw_umGEgoMm9^M`@hKr+aDHezu3e8da$KQhGxz# z#Y=~AaVuzA7}0ASj=V9^lEBFO?Tn;k9EeuYLeEV`Qu1T@WaPig1tW)&7%4dx<0Z0~ zJ2|KF@&P+9DLIjs^xWhnCFhivf08d=av_X5A~&o`j+wmNS#4Cqs#~X%ROy5BK|4b! z8U2>5gOi@$XGmE}F647?{&g-G`j{A;2i}~#npt|n-GfQWjmX9Ldg?im5{~hVLPXb8JwnKCZ{R+p1gC~ z$pxpMjX16FE6!{~P?*DdDfL=9}}LmFdIcvLF72bJkZ)cM!CnzlP(paT2w7bdTK?LDUWF$ zV^a`Xn{@9<^eRcJ(nRT9fqRjK7h(Z1n7t}v5_-Xo*P|JDrD6~{NW&{73l%GhSD&jX z=twT``a=vuafUGsO2&{MJv2JaVhD^~E_p4ygF_KrOnn+J2cwPh`BvEtz47_8UI~|7 z{uKryL=!3^*1OXcrHb$+h+$>vu?ew9MWzU1(_l+&2b=*!O9YihjUSe0A$;@J2!B-T|i%a6>x};fxIhoKh z7|!c>mtF?16WDH`$p9;(6qv+bB)8%B#?MNDMqr+w!k*L0D_x6k6*X2wPaKgwPPUTR$^xqF5(!+Y=L@2$?_ z_yU3uQEkMZsH~kNd{^jUQGq;Jg|*^eAA)dSTo8`>VHM(ig}sZy*X*M3gBhZbikYI2 zl5;K!XLBJ6PlBZbC4kct8`rZiTa3!}x5Nza-Zcv~!AmjEbFb z{T^x{g)ugJq+38fWJmu(2KuQOT=%3YpOh@N<~v(JG%_mkwOX3b1^j<1GW4-Nax}j6 z19zPMnCu4g7)+Oqk_W!?Y-Ay*Owlbyo2p_WK(9GoJ%Ag>k}BhqlOkBj&}eF&igt|E zisslmvNmHCsS`y85n|AoWLVOZ-a8`1R>-#Mq`}9!*lvq*+D!R>tr}KHp(xNNH_T?J z<5l}83^PU{6*Em1l)P71r}AmC2y-zAe>WP0Y``S)5fms`)Gc_NpR_`}D%B@lmn8LU zg%!!`(|U=v-u2GNV4@bK?ofs<${KiMRE4oCh033#*HdK$`rI$O5`_l-6=SNQ5GAJM zqVb6Y*oWtE>M~v5B+D487wtp!6B$F5ikTB8B_C9WD*Bw~YrgzME{5t|5WZ1tZ!^FA z9+=8!QG_E#xga|5>(w&X8mr}@;n)zttT^5%Cy%MhE<2>1-{O4JUvk635Yh64_Iq@`ahtIf)k47@(c zF0)G0ICh_6t>|lDf6_j%zd2)IQ!#U3Q!-v|hfbB7X!P5bFg2%2-Z`swZ>Se zVk&VMUzMY9$9Rt}&%78%vA-V~_kbvv( zNIVKFLrO)&BP~fYVMJr>6=DNLX}GU}@&)@q`HqZ%LdDF1LdiKFC|}OSK&hJpCB{NF zS141Rl%p!blXO+xw?d2!=e;aQU$Hq~&@=2uyQj8KN0^IgfEL{@ip3;HE#eygijwGp z{AQ^E7P@E`7LD;mWnudK@iXHyGh-+BTZ;6la+)J%ryO-SMl-?X_z<5_Ik7Kw1oXRa%R1W%53g5_nb6Eyha=87(=C5A!PV|L7S zB63@aGDosonX{-+l#9lgl7&Abu*#?uYkMT>NAuu3Is0vQN>@4_!~w^x&~%L`3@A>P z<1`B2_Yx3HIPAb=UQ!+j7|vLcE!jm|t1Z>>SIa4B!(P$#B%Y67p`!E|Eh-WxnXhXn zM9z3xV!{~5enV-Xm_rqvOggoh#T3yHFKIx7Hu}CEy>587G2{vfKZe^K%;AQ_Jl)N{ z_Wm2G#Kx@QT=VbEERkvgVJirnZUhu*QDMwL%6!H8GemJP*!73q^}(*cB7#sg7n}C0 zyX&7id@Or7>v;fSFXJB#!e!nm<~>RC1(TP9O-JbLtesCA#*jglbalE%KI)TGQlUt_ zj}`%>EHEKXD!#_P85Gll?JC@pW6ogM+xQpt;QHCFBF2&pbg;R>ISK!Ny>D*lz+gju zU>siwkBY^@=@?P5w&ECPZsA>P>u1sIN)cu*t!hjx&W@UU(S^$nYm5%B(^{Q~SZdn~ouk(i4@;?Hm7-z&Ca?;k zVf_|B z4|WP3c-1#Z)X-#%k30FT3%#*&o1K-E{BRO0>AA^DO1@2HW!}c)rcG-<9`9%4QR5RE zlXw_|UnD4ZSl4oz{nH$v{u0>{s}QKohcj2)d5h^(WHz?SHm>(4jTrtkg$Z1BW;3v% zI^Q8e^kdzOj4T!UA*n*&gV3WX=^w3{3LPU@2A5^@c%92DuSzG;u+TZxuS%Uz)hemh zj{^%qwJzXCl4|`JJVd=r%?tY2eoL}lwax=J9tCPaZFz@aqeqo{7rZo6xgGe@u?+Rv z5IleYw2#0CvRGsFRt^i3)erDwRK1_D72C$2LjYP0A(Z?O6dQqJHr1O^xhVue1h*J7 z%C^`k+rgUvty^n?k|&cWqvu6?v8N_nNBNYCh83lqabH__F$iq7Ltv*J0(bCa46$Yf=WYBs9RAXf zL&+F~3c;x@yz&Nxb2);-Z1P=c#_CMj6K4awD6WbW{(t=VF1b}GPNWPL`dTgx7kAll zu{#47ciVA6$#c=eZK5K!GE4); z9y>7Z&j7}~c3@C4Ml8s^z*x)?FwR?nk-TzIu))QK?Up&yi#HGY=5zV!^w}SS6A-=L z)P`e}uSVs?asfMEB$lX}H!Sy?jb`@i(E#}3H5nH-Hj$*%e#7D@rS2O=G+(eY{oPow z?>4U-o`$Fxo}aa9zg@1{uOTu?wf*F(t=jEW%cVOb%IjQP`K5a*Aq$;}e%J45^{vt( z{XraZP^}UZ1V$--a)p zBxz9l6g-7JwNJwbvRz~UMh+E|{X2OwF6_TxD|QQi4tr`EYAE?}DE37tW-RQnaZYIf zatcBwSLf-zP!Z*kQM%bq=~mt(EcCS|Z{^7t1Kmm~JqL5Xtr+ggA*I?X@4J!oyh+7> zf<)y2>b36xLU8H-d3OCQ`js4TaNY?V*M4Pe=)$qb#sUkO1~->@a1}M`Zw?+9^5EWC zUd6GN>$?zF4WHnxwfKdejj&H{3%3D>4-sKG{T%}^-(@?2ypYnosJse$F@Vx~W zLIh?hQ=ga0)K4p7DTZh zNp<>Vc!<-OniuBIU$-Rds#9l6y~*#!?n3#_&27Kh?W{FQI-7jAvEX%UzXbGmcDQpv zv)&H9NjI!?*A6(H9lnP*TwNqWmJ3CljdRWNm2T&OmRni`r+25?EV_r>A_r-aNw*ie zoh_wGwO)n*+1(4>&SsdE;80w*_LI;!{r8Pv!2a@Qc z=MUM7;q646zS^GwnYBO1|9+R=^Y`F$&Ea4B_ej#;;D3LB^sJevtopuulSesrDi`ya5c zbH?wzg5UB;`57K8PXx`guzH_4S+YeGK|{Trh)Jfc_K|;|9SINeWZXKlB7us*tsa`d zpkxe1vmQqNEt~U)gfEa!P#SYp86X)gkfNTSleYwkkPSS5h7ccoc?HWamX9O{?9kFT zIx`m+GIzpm%n5#n1Sb#1HLhf7DrTTFuJWUkh05k0p%J(EaGn#Mt-A#wI`49~@brktubFBQ!$#hcI)0U#dwssyxGvaU5 zl!}B=3M$?73v`~XJ?8=F=Bo>MCka$F>=X)~?>Bu&Wl*Fj5&NBkN0o7d1E2)i6=iOc z;*7H+%1RUo%qm8R-}aQl%_6p>Wpv!Dyaq#{ASYvFjMzuUOBo}BikZThl9_k)dK1o1 z=5R=0boiKjPU5tLv^oSv!{$=8H08~Eu#zo#TsoncKS~($$_y^#nq_7`Dg-iu@;ZhS z7Q;WY-kFNuyyVW+y`v5rJiRne@9oFKy;_E*9LKzUhYi(OA`cm98LFZbPIqj4s-fY} z2<<#rP`g@`5uak31eGdSh(@t^EIzRbKHP~F%-u!CPDyJ4tI&%E;3)&G8PY^FB|b^I z%)1bG+}Wwg6Jw}gDB8p_N@5abN5#6Y z7fH;e`SE76?zs(+j0A75tQDr`1tw;c@s%|fz+|P>6HQ*)Y_ww2L0oYYQpq|S$f?YM ztH!RzrWjF{i7$8+FgSxVqtYEBgZRuA&XIr;W$@EVT#r+gX#z-_uV`9h?~y*$^ztUw z{>qq5tYz@^<5M-N+st5s;pRj5fdZUj-R@_1qe1i=jG~SiG2JVUo}YR7jj6F$&yP<* z7+h;xqTAnk%c32KnzZ`_oiJ#7j0X7ZmxeT0!3||@JFFb^y^pl3K8(OB?!*TzuLKv? z^G)`{Y)4&aBSEFvu9uxTFY(QwN-qEwKxaTc*e!9wCaVJ3;`zf6#tioGVUr$qQ5qpO z=v{{PFn8EYs})9USZzQ*z@n<$F5zA?)2#vr4i3PcKUN9v()6ou!UhoB6A4xOa2q7- z1(nn4ZMMUM&H00KZlhduW*}@47KC8^*q-mA!$A{%f}6SQrD5k1EY+d8v}*CF0(L99 zET}IDH8*{5wBx(tvLB$4C3lg%9PAzkL0p3ouz?N^c#rZPB$jxWt6?!(fQsDW^F(v0 zRfmh^KxsbJ4&g?@qTgU7HAis-T|`X7n2+%%eGEdMXtmCTQ2 zD4A3Y9Kf_407@PbN+#Z%gpNBo*C|iuP^Y{{iB->hpW>`eR07rc0*W!Kl+An<6MksU ziY4;pZ_Jn_25`n)EwZVf=>j26LC7?+Ysur_!2}}MfXx^IpRkXBPiKq(DrSxVN=6q* zwmY#-M!@U&8v&jFC)yRQmXmC!jKd;esiJ&`a1E{U6VRthTX9n$gSIO6p&7iT&8H0QP+Ta0?&b41n1pq9j0n+PEqa9ZD{y?mN#TrgP|V@{t^lwI>+3*zTWXA?W$>1wIUd_|DvRMUm`@ z@h{prh{v_l7(bR=Y&|gkqd8>!?hGD#`QA{k#t;-#Mt+OIViVgzj1(tY% zmV5GLRjt)Dyvu3^d%>X35B7owd%>Y;M~t>wgweugwBvJxew`2-T;_6349mezAg-V@ zy+Q^%fo;D5HiSesjR>BsKk2{0PT*iCaIh0N*a<{ORWSY!b^`fs;X7w1kQM{leK)kX z9{k=49#zV#nllJ%qVwd%Wg$wm0=tRWt1OF%&F zdx79u14&s6RMcA7@-hIU=3_ssm)E}9_S&%X$jIAb zC+|*92b^`++M(n^5_$9-&OY0U;f^RLkN4e=1F1QK@y{YjYYXGrSAm8$2jZi`p3Y70 zY$9gdaaEnuBX=(%HzWJ-Qd{(1eE`Qo(^euz=X@Uc|MG=~Un7r)WS-)dm&sd2%TtX}>=aU@$)kI}RO|T`8(N`QF0M!L zO?|8#_wA5|@v`bn$+F5xNzYA?iwb~Y!_+tL;rD~%jMWa969=2T1T;6>q4|&cCE4FmRzkhd?B!y8@-*Q%qfTSR&g8F@*?6`j$xb`tI>g~QDC7CRa*s3xw z31f6vs85*f*Q!H-NRU0lLl#J%-VD6$v*Yb>2HvQci8o4KM|dmb3U9L)Fbs&g+hb=l z*TQ0OlT3$Gj7(6e_`~yqa0ISVeG_(!3xQC@)hsya!eNIK6%W)q@GZFHJ&s2w>k!MLsgP|{32zNcd})|ey>|DkNOa!-YtcN)72n6GU@u^k@p@wI_z*; zR`jXfb;1`uM>FK>5xaannjv4Q7~EN=sfd(}ffv__iug>f47MAR2HO z2(~-$gP~)*ER!1Uk_Y0~Z5~P= zYMuR~cGw|njr@{<<70Ll&t%}3ikUd3|3ZO@CbS<59MLUD$&JEK}d4k zRtxSGkbrXP5Rl*^XJD;79oInxw(X23;T;jnvKP=BB z{tEJ&C09(T=tZHhs1e&rJZcA=@g;j^g3;)Gc1b#$AxWv2DM=|=?3=FxqtR5ZBx&av zRg6YFR0NVtOrMeOkojl{o`Hkc?Kt>g1`ep0i33VrM>zO!zHs1LaX_pT>l1Y*v#-pH zC(fOjJUuouc2X{Cl9jm47&IVDUVO-olMiR$go>Fsp=7-Ec%7^u8u`KrTw)CIpGz#- z4Ga1JA(^66iHxD?a^hB&r#BffXXkx6gZEU-7Inc zmY_X{(a$rck$F2rDj5)=VkSf=S!|E419jpv`GUwH6(WLzih!V`bP;hI*{(ItK`Ur>2YgG$6iRbI%{bdhmqgn{0qhHr;Lm;nVUWwF0;wH%GI09c+-x6w=xhw#Y_ZH@;XAmZ|4gEA5vw8&d7cY7NciR zjPqqjUc7Bb%cnBXLd8t9 zQ1UwRLHmb%q2(oYS}_=@i4ArS0kIgR$1o3IlWgzat=HC@EcuKb9i0qxP%#r7l)R46 zu?6m&$z=UZ|Lf7fN17c-fsV zyv!!!h4^W6R@L#sg^O@6q!?`RLmUt5r3jAFDK-N)|Dzo@-3;7NF%vhGypC}5Xufds zjmfw%f19gMa4`^ zQ8Gl)STFY0L;1qgm<>~$j~2y-J?KfV*`;BE;F%$AzS9nk@6Lb*6*Hkh$?FIj$MOY@ z*Ak&&NE>yxUt{}Hk4lXWmfm!lFWbTLy%}JkVkTH9c^!e}Y`(xE0>3lYTMYy~s5FXL z$((E+(kdq;%K0us!hF9SG+)U84HYv%L&@t1G#|_tXuhFW2_yL{l@VofO3-Jts|@>5 zXPyFg<59fbkHBS~fUz8L$bj7s+F|$A4A@bzyNnYrlq{k*bhg1oV?-*&ugYNzrdqC$ zH~S5E!x;BkqprgUYjShtPf4mGNKje{Y8%63g{ZS+eWAr4i{~ zt#TsrrB3H+l&^m(ll*k!H1_7(nOLrW(oQ6K1jgIw&7CZrEsIw9HGP#siM{tBxq z_fE2G@nlt&LbqP`l66xctNI?S$SUcE89A$VgZR{yM1oA}b^A1#l)iV}GO99O%EoQh zWWNkyg?ivevQ@u7!mo!LJsN*CLPUQJ41q>O5sMnqgO-!7Hwm4GoC>z#)hf~53&zzd z_fNKi%GoA_9IC<{^$-RP;+M`>7pB9e?{zC5sa&j-;Vy>CW&H2Wi4E*>mC9TE*CX<( zmAAkIEv{Pm^nA7M9Ud83`O8jHNS9ZyFv`5nC-OkeLT9qyt5`B$ah0xS=>UV_YL?I9 z2W-l9b_QM@eDeV>Q*L9y>*9SKpMi(?spf@?89rx8#ws?KAx=xR-mUD)CvrXwbXVTd z$f;&r+)`P*(5)=h-h_@|cV&DdeCebLjN1PISthpEJ_R2!NNnctF(I*+C*u_^pSKm; z%AZ5g)yBfE+n+ zfG_zEIMZGjI8C3zdIk`B({oxd={XSVQ_gz0odMHc+h=p2GY|P8`Y5c5i=vuiV5@MK zSlW1*Rf3P5@smadwN^ft_R^xPL|g}qL^ea%DX+pEb61G?au(rK%?SInBMM`TD&v7( zXgC3XS|{<>f!O;f7$S*q8^lOgYRni(ciBhM9-jPM(nzA`dySDq$#}sM8%YntNaFDc zt!mBDe2=Gpp2zXj`AcOmO?jbTg_vb4IE>U0kUmTUWt?irPeXvCqm}R<1ND7nP;f#| z!L^9SnSt;7?D&3|C*v(K)>)38Z#D2u$#)5S-vao?&4qm9_!n}C<3Cbxe3Ef@1yRgr z3qGsRe33EfXb5H-NHGXXVNI?DqI!W|FQ00{m+}aQ*`I-+rVJ4WoN-_U^D$d#gAJXrUqR?VZP5%8 z%b>Qzy7-+tnZ;i9ohI+ls3n%BhDGbX<8I)YV{IB@1!vzKJTDDezlS1=)?A-jHG@_% z<7}tLxF;P++i555Ufu>6sE^EvE$@^J5DJ1i6orRa}y_& zd|+MRGdhp5KqQSP_6UZ z!~8j1f~w6ul>FGbz{=x!#L7<|8*gAJG-e|pyTbJ`p7uEyu2ioTcB5$|X6W9MSIUc; zAX0BY5qF>pX8|ap;*^mJYc#P_z@*iw>mz#D(WuA(@1u6`KAr(yDrSP0lGg`#-;+o1 z{`9f|*1&->Eykd%K+y(FbPtY0KEl6<$DzY_cK}ud)mMgH-7Da@*)B`>+u``$3^*R( z&*5COCQB*VAz2zlOwY4oKAJ;V&Q5LIu;JLbi=OXS%bqG&nd#liX=Z*`p_aPO)Pl_` zK8puB1W~_d`$}wo8GpbvgUSVxcxjbZnBQ9Ey!95dK~tS@$ZKq=6<&d&iU&3Vn{S|T z2;=W-+!}~q8Nny`)j{rD-8j%1by0qpH~n+hL_%%LR;Z=N6Kx&Np$Y-v|3(N8u*UI)4i! zHkPRfkI&5)jMswdM}6PD!mfvmmHEJv7gTn{p3CksdJEc*h$(`bL1VPls$VHwRNtOD zHl^pFjx)%DLagD#hilDhL!~YBuNcf1&ZAHZ*gQj^Nq={KJ!%9FWF-P_FVc=BOf+Ez zzD_M(a(%cZb-KA^aiDpYa zfD>Y=;uP49jNfjBURhqy>b&KQUG_?C^g4?YJ(J)YBQ{1WkUn(Sxq5AA*zu5la%C;- zhu_Me6&HT$GsZsA`D~_0a<&b2kU?U?58ca;$WTB#*JS1Wb6oWPjoKZZGTY!s#6rrnWEjTQ> z0A@AI2%0)}4+-pQWoKAf`_eK4$76OJmojin#qKt6Ov&i^gH}@(s)K}Iu0eYthoGIE z2W3d$R?HxY?C&426>!eU1?uzhJo!}``*j)4)YBrXD8KwH6tpf}Fr^Zi=&WxWs`^L<V428t1>2SnXdFv3XKu!;(Sq%AX$o4i9d#LKzaO-2j(au+Y9(#!gH)%MXT(u z@pBRP-r~+HrmB>r6=@xaiwtBcULcKn8>(qIMtjqE@EsBuD|>^Uhj0r$=Q~-1q@z-< zHc9^*?rh~`lT>UV%@%jSIu#cZ>as~@5-K30D zMgI#d?ikG+9i?M5ez_TrJD}@hrP#opbQSrO))%)4-ti$+$n50_S{4@ao=UV3tVrF&8kv(R}@fBi`XrO!7Y za|_$|X}E~a)X)<)}h;ISjfggSsKlZlmQ6;_r57E6<%?pm#U$i7+74fJNYYmFe z1EnC*);TDuoxLr$wCFB)P7)rtZz4=JABQ()UiKaMqKxU<=ioO)hxlgrK$dE(y@kWT zWbHPdj6U_>W-GRpKflvJ1SQ`G#l8!QMKMO!8Vz4TYSs=7weNxU(J%eXu~*w}9pWE& z{x>$&b8(gAdW?dF&dKYN>}Ex)DcgS@ zI0&-+U*kuTZ2viUh=M@P3j*L5EXj7+zV^`gS)dih^E&_<{Ydz)!do*5|I_fLqmJ;} z&p-;CoBCJqfvnY7d^?AO$>RTmC!>u2#gt-jZc0N2)pidQ`xPi=J2%D1+jDa4)ad!s zGjGhCKR))x@$;unjZM8VJ^uPwmlM0wPVAkWDmY1{wMxlHl8B|}588`8Er|so`P)c! zZiL()BQcl~GDa~^k^#0&Ho zJ~!K0ez%?FyLmF6d$8h@ialzuoRTr*2ja7HH+Wh_v{0xa^i|0h4!)Kf9JI-EoL~^^ zJ15Rx#T$}LC$T(97$ScF<;(&D(XLnp0K^O&?XlzNfeai`F%w6Wyxws1-*bbbKkgex zC~M~0^YflRU42vYr65#6Ak+9*w)sI{s)m*0uyAVm;CBPQ*5ITAjz|kv;Am$&vB)Q< zxIajdRU-*1fUrw|>nxmn2--{FkrW#)=wmonuLc#*KQeQ4*g4g1un3MvW`>>PVBTQHA&1A8kX&Li6;f!AK(As8buINhELN`7??WPeX$uxJ+&V?6neB-uj69yUZ0 zB^OOG7|H4#a5n=I*g{JaN$4$hMSO(Q06yQ^ygB#}nX;jp@`7@l$G3q=?-g;B>W0$sf_vU-F8Ml&69T}G5QdH?ih@wWQ<MXHuuC~(l4FM=RUW6q!<3|=HvDAgZdEM7;$8h2If$#J@VlC;h&Nw& z;2`~OV`3DA%yrz(X5*}PIV|w|4>Mr-tR0qzGhq3g9hQ`gQFL++%d@$G<=g$hQemtE z=OLET;CDkHx)V+w;(3UITpVQp>4+Uj@678z6nQA0QdbjWqY3 z5hboqvP&^jDDA-eHqb*q^s;P|qjn^n%s|p{JCZ0F12*LxNwwS{=~_QXir8u_D#ZL0 zo6vef4)f%~9?8wN!GXN+XLbZU+;GXKswS`)I>KlCqp{_k;Dp$1P|H~~Uhc#z3W?8t zFflC_Xod8q(uB=wX-_x)wktO&-#Be6F6VEY#AO#C(sCf6dkvxowo#zuYyW9eZUoBj zZ<1HT(-sui!CClyJY>bK>{_qJOzw0TYgo(02ES^GcYbDfvW4$P2)E2zds_x?xutBP{49 zL(WME#6YGviZFvDW zM#Og_26JPW`p3B$5!bn=xdVbu11SOv(=?8~r^2XaA&kb*1 z64pZ=(&l=nZ*xEWE+$0bW^Py{o*-L8Is{&~Lx7T>NrC`9H-|SRYwqg#8s0yb8weD2 z2ncqet;oVflhgNN+*EK3JqMN=z7r%WNayzl?EI$WlS%xh=Ld`-Psu0pJLG>oH~c=5 z#BUM2?kJ0eYvrlQeK)RS6#9@Ms=F?D^a|uC9&TcPdaK>h>Vh3b9#2kmg{5L}>QZyG zqU1;Oi=zKEFDRNFf!GVMo($ny2Z)+%fq}Nf#ARk5xX@N*x}g|rig02TQE3Hwz__Z4 zV{4-U99|5IrHWTtgjlj?+x7Y+-lbg_QWYGCudM!5R+3l;+xAkQGdpx-4A-~YS#FE$ zg+jJe5&ZML)~AP^klA^!S=vaH-Oy6l^Z*Y9W7-|lcMx7Df6gxywhq{}+Guw;kXH)g=B zVTTzdcMBzAGm7wN(uw8}ZO;p8;*vHt^I>uPW7&DL5RN1)n1>>GF8;sp)HCxK zVq64cD>WeiLZj_PwoWF(>j4F>6;)%%Xq61O5da)ZUgY7U&6y7i#iLq@s;o__a~vK0 z&^l?Dvr3ijans8Qs<#7PAx)y};Z@ExoI~I@Y0)o#c!h^`9w4`}QRL zlwF*DE<>Dt+AdBh`TqQi(|2vp%Q3L8Wy#Vp-8DGbdsM@Y9rB3mWbH6zI3*Sm2u})t zo{ZH#yi`ALN9ng_p!5rNlv46T`A6yPxk2gwl7&)XW{UYkt|=-Tuqrgxd@(@g&sXic zbEE71FdU2)b}8CFCut)RZe@NZZ(t5NfxI@{1f?a&@3Kn zpVt@Zl2!voG=J-4M8zT%G*MnE>g&Kx6IogYb{agbA&>-4HZkjva^GH-ES{Erg6ZSR zRAgf-cUY{b;yW4uI2L2M?d$x=0WOQ|!J?7fmxwM5gGHkh(FisCi{|b4ufd{Go`AOw zLcPa8lE~i88k6ss#YgY2ydE`K#!uD%bT^+$bnoyiH>2<1>m}25^vgUs(RGxH-D_;y zQSvY?v!t7mj9_!#LgV#rQ%cP%V`uU^`Ma3zN?+p1i93^2>^9>SDS1e~BDl0TxMs_Z zyV4W6Swa3C?M2)m)3xoq_?99{I^&Nwh|#GeNRoHaOqB@kzJ$Ohs`(T)6k$<0oKi)k zCXsm#!|CMfG}KUDU@?0ZouvKhMF?L8xBt0ewL}X`3r=ul2^$L#=R2@$ZR1mAWy#$| zEw%I`V%8VhVzaDSK>$7?g)ANUnDVdVB zDLG#}oyrYQ?-zoXo4@eD2?|kUndwpXicONRqfJl(f}P1&iP=Y(2n`9YV_&hO;m3J$ zqJl=nOa+aSMaW0ybDS?Uypb0)%-(^nV|><3V>W|FUmVvaQM-aA*Q2WLrfFT)9EjAz zhXYUu?3!T4#y^@3!n9p>l^SfTSdw8?A|+4Fgx?dlmDP-_HZ&5adK>6A4^duEHMTdB z-QuuGFtg%^QHELaFdcm*;3IlBQegA9MRFuJ!{+Ci$E8JqwGAQ^uRUw9q!p*za3fx& z2=mDJF&N-t6vaLf`CoqnJhoeSU~)9K3SV~r2oQ`6YWS;%v?ZF z^0UjbfcO`7^@5TU7ZCK^TtHB=wt#4$WXg^G=Sp7W>D#&C=`Jzn%z*4X0taZB_Ry^?UQj|I+*RTN#q*<8Li&pEC+!TSSs6eunldK}bKP;pQ(jyXjh(+~+wby<;^CR;MGAzOPO~>A|P)Ukb z77d_?hD1wD=D7NK`?&hI8RLqInd6F*b3CqoG&kew_3Ip0J#jq(UX*>623*vjI^?3% z0nR1Z8NB00+xBmauB882E8 z;u*_`@5-vM=aXr@o=VgDN}g0PIvI)Mv7Pi+(9P`3#Zc``c0|vdKH00I9TE2=FsLW$ zY+l*YqUu-Mi_f01sP6lq*I(GB$zNqilmBFwCX{SF=#?*N^80y_CY^7}5+!5Hc+iU( zv-$p#@On}}Xlg=z9L4&Y9Y=qYfusM;jw4F89>vNRj{Z|_aC9*XM>pcYnz#v)+nvBx zUKlf5*plqZma=vyTgXZunbJ(OaxPykdtYpa5mc9ycNX@Ffmz7&`!WmNSU*Rr;uPbM zO*1p~oMamt3J|-OL2?7nTAB%>KF*S~(y6k~t2bgtr$8T86+&GRjperLi+!9A z8?0Gy1vXf-h-Deru~%fx()&PKMb)==qE<$a5)K#M7r%7_SbO0wKi&erlb6fYXMgx% z>5NF{%!4b9()qAj55TU#`cSAYbZUU_SmG7yC&x~Wo zEr;_{HB7ans)t$?O*ONkbgIdAu!pIX0Vk)#&H?{J|T*IANeJ9AK;M3Em<@R%h8Pm!13NU?x5MYE}#W%;#N zTCR9?wu{XAkfpKn2eP?b-DqO(-gR@$1qBr_w6a08ATwwYpVxXkj;ze9Fs24ko$2;M zunwt*gOr^O6f?|TqCUsfKAgG`?p7A-3Y(Jjdl8_JEj5a=YNgUNH9hVn8!hyXhQ7v)ZML;{KPMNzZewqd4sVNRGB^&( z1Tzn)6O-5YOx4j6&U1sAc5vQX>%8P$Rov6D@5C|%86Sh@i640@A#)kbC)z|IXGafx(&T9ClBlY%Y7 zXC$Iz;LCtcri$}~I-aGl$XZWLze9t@ioFrVcNd^L@V~YU>4E>XAsMf0MbPu6K@Teg zl*&b1B1EEAA#s(KDU67cCGw5X7Qi=jsrFUy9cW{qm`nHtf_zq*on522Yd?0`E49O> z4;pZ}UGn^H|zjQ_nkv7vHxVne&~7XLNGezhwft^EV~c2X>bPt8~B-r-I+vWhc zX=KT*Hb&U9Y7_HOmXS927mT<_^N5x5B9`e^%A7E-^YI)Iv(P!W+%MS11N?X!bZ2A3 zUGln}J=JCrx(QA-U{4mYn~JQPx}A-S)ke8Hv9YrW?!;d3x>#xxcvbiz`?|4SZFt>@ z4V|r}3f#^Iw-WmBtKGR7&PQN|+O{1VHcV`&ZO4zj?RIBp;MIY9)n;Q#)K}XA56!$A z+O-{)WUOLy*$ZLFbm63b9~}OCpY*KVUD0_n>m(pabtkD)Wf)$B?j` zpuDylh-p_4m}Hp|Q_?gDv}(zNm|T2{QsP!HL~gD&s&Eq2o~a7R`M z-oc;WZg82B?-nFjaax9N< zdHL9+@167lP$jR#utJ=*fTb8z+F`kQsgZ<=t)P&i-kXJs`|P-QC<7M{@aGU#K$~MI z8Bb{9WYO6UcghG*qq?43QF1nq*mxgcgE28+$T&9M07bUp)>WK{-bL>x&Tj=R20V44SR9RPB#jP{N!lgZuE zWKzRnOACUJty0I2SSXgJ5=M~Mxw6vTobNA3hChT2S-qG?zv0^Yr-%$-lRs93ktxOngwljIFoyj{gn7oZYzsq1U zC8GrvncUe1>()82xp-~^m#w)Q`d`fxzfbF%{3Hmd`MU*l6!-#5+dspPq^0eDfQLBmsCi+n^XHai z`_gu882-mVAFKk_5yN|1t^NkQ7hA3V0etCfWaF^*hwy!3d+m?l16if9>|LC_Cd)SQ zWL%#9MM|+d3{F#RxWn~VP|UVGWrW%ERz~G!JC!$ca$udR0gI9`B%zf`dJc;dTQR^R zqtet{{|i#SrsC)ykb>1lXYH?{Ra}(bmpHR1>rBQ*Q_oCe;YK?PDf!+c7Si+W1`8>9 zw_u^JXRbX5x7@mx9NcA_$%%PVHC!9P`(M`-+EcX5f+#(nKktG!38{DJB9IJwrX*SW zlPj65zbN}GcaAQ={=f;>FTc;NSIaJVS7vPy{7b1C-G?|LS@h;As77&GrH;f% z@jAgu>PU&rFLXwGSC*1$iD^x@Y8wJK?jU&pxCIK+6Ziq5$+{O%fQL8>t9fDe9kwK6 z74f}*HHN|CKq$;j>ly~t&TgyaUmE@Qz)LbU>Jj+Tv5nx`WAFrQ{`=qq*{QL0Cx?K^ z)}P|Zs8khg#qQwG!RD`_fs)addI*ZylqyE%=F{h1dTDH`%Sqd5C+#lY253@R8-5C<a@CLw{IKWrd@ zl81AOgsFTW;oC+=9B^}K!6C4!7dT-P&UE?>r#V0GT;$3DILbH+_2!&guU{Eh zoy7+DtT`cg?FX%2R+d3zVFQ1$u5` zfs*rzg)8d>3mX5dSn$EcI!i9>rgieLELWnf(#i6dO5kfY~{m0}iPMsG(H6NbkV-RS{!B zYjNC$Behh=U4WySLFmA_%W55C_CzHEXTI5PlpRoRutuiFw%o8%6fJ_v8#tF-zX3bN znAvxhszCrh)H+&GgGMs&XN~anq_c7amYw=P^@2ZT!t_uzC^LkJiyjoDR8iVjD+8M{n8LlXMczQA{OL5%3P(58 zTI2l6sy|L!TK)0dO8p#f&|$EeK2h;Xi%Ank0cR8kx?A>)Q*L@ehl> z9Wh!N68mK%YH!h`6!nCB2Wt-&gAmRkE)}B6kj?m4^6|2G6QY*XyWfVsvEXZwDMVhAz zd?G1=R9JGuf~*yD;@^rSJY-R(u^44?N8JC6lLXUKWNQmp`=!Y-aYYEbrTfKP_v zA<8(>5hDw+ZrI;LA;~a7QdD7I#pC?ec`Igq?A9pIiR>C}9N5(&r@%BPeJ}(quT-5! zOH{jI)7l~;)Ip#$5WTKC5aMrh-2)-suPZkYtnZaP7|71Ty7i0~1R!al6M7#LQRhpr zf>fe?NulA|xN==X1J+~#xO@39$t*Q5GR3;$1rYWjY7r)Pw_K)2knbbyDohN~AalxI z3qpZ-jS?WH*vpu*_Gn)NBhLQ_lQIdrfT44+dVFPoRd zyT4QoUvAHV1{AbC2*?k1`T$Qb;!>dveFm!@8ceXJ9NaPZqvv9HjDs zTb~Z$kl5JelGnoOlIT;WeV`1Ev%>@zR@{(jM4*Z-xo~0c`Ki+mNL_f!7isvLsb{df zYWdzg5UBD_(uE=)kjgiti^y*z#jUebvbZA-%M2$WoM~3MCmSjCFS#R00Y#+(5lHID7jq{Ri#j@10J5sAZQ0TNOq`I1g}g-3t@nLr@(;D#Y^hsWHR5V&N3%fP_gnfvAP7%mrP zfDncY;&XYaGlS^r*Z?X}lh8$bwWEgKZ8cT=JV)Rc`0 zd>H9c3V(|Ao(WL zoKUfnzWJ$I*$aPawfjIDVe-U%ZL)eu|CXD83)n<}l{9WX09X(>fM5Joy@{Ul(AhWB z&F5)U&>JPSrg^h^gkqoloyEsWd}tiP)PZtHtqZY5o_;~U(-=m+1g(_!q*%#B1cqqj zEQ+iCMe#T1EQ*`ry6eW|X62*Wh{5uDfsPUL0lBJvIE=ONs+HE-c<3b%7v)Zy40$Cv z^y)B@Y8Do>!uh0lEtjv+uviPUAp)pVO`8cI^(&+wg}l^?Q%{z*8c_iFLAAV<0+$9a z;a|%4R082U`?83`2G`q@Tz#M|R_lGeksgqr%ML8Nw6TG#w8f(OG1DgR-KJdkQfeeaxuWtGzH)W9qZmOt5T{X2rD$%GTlm@WpCU?)Yiigu zT`&4VNm;o-O?*=Ca0bie2>P>Pi~dXhroJCzA93_dsa-|H9@6Vwrhp&eiuOAbD>?)4 zIb!`={9?VABi7$5uJ3Yuo!tDO^mW>?sR2p!p9w>vpR+d~j*&Y71iJKCDhL`<9cRlF z1kixRo+flvH+L69c@EJ85~vXf)Y4b9;>JRolGk801ab&1MJi>&xBJDc=paToBWF*$ zV4zV?sbbA^W!3jT4y$MCK8k2>+0}=lBcKqMjz0zuOr^~%*u04a!oLTdJ z|E&4$oLMs`uDiw%H@{xany`(bUkqdFeBPNl3AbRmw;#jp_| zckyt2*o?@1%^OIt5|pz^%?Po3W{n1G;&kf-=M<5lMqpe^Um112SOZH79}RwGlT`9f zK$Rd;9_-VXyASN!HvpRIpEmE&q+jV|6O?O3M84Wu`;7d;>>t-Rcow9-8EboH-5I*e z^8}#+bLQpu`sd{*bLQm_iR*Vb+Kij`%X#S}c?YDz|Z$8qBLP1<q2eIOQOiBcoLH0_K4Zb%%am1BQx244> zWJeWz$}gKfn08dQ+owx2uo9p`#{3OI&|Scd znS%#`IL}yYd?X%z@F3I(ksR~%!zXz%PXe6u5V6Z{RMoZ8^@9KC%oM3pmd6 zS~F}p5|A^fAR0mkGV2I+?O$Jx1f0_G{J6Ix%#h_sK(PotB*W8{Ev3KFawMP-ujIlL z|I;A}Iuao-0CPDKP$YY{ZR=e6^YO!acF@2kBZm_$yNvKFy`*a|M*`-C#2Oz?Pw?eP zK)rlfjs#qe1f-HSDRBtt9uSmU%aMR~OyhDSAeA*BKZ%Yip|6rKB@jQR4#dClpVo!+ zSHi0;uG4VF=FD>R7(_Fxa5Q^ayc|8Y96k2hjUFScyW-&3a_A8+I?@%mFOVT8^vM6M zmt$2hcl}xihAmL6O2RX_9Oht@Dlh{S`iw1D4s*cSAa^8-ix%dP0QU0!I0mijNSEdP zaadsZl%TUeE-J%PAxWno>}CuAh+9V>^&IvgI`K=Lyn3*2QuSc}9QV1oxZ*6x`uO1B z;PouXnjlq`gC$DhUTZO)KG}P+-FNBylvlX!#bEydSOvvk{}F$r6@&eIxQNw5&3mD0 z=s$Uy{l#Dx7X$wj=!E*3OBe&Wm0l4uEX358&Qa(-W#&U&1_%-E%+AE3#ElH+5y31YrJ2V^&()#U$3P^0!k(mN zJ7?no2FhK?_-%ABp9=e6R-QgBj^MHiqh#W2pxojI3O8fbS1*wHx(gI;eke?!+#3W? zASJza0r&zt?~oxHA@9WAv*tJP&E@7ws8G<#X66PzGr4(p8Z-GiRNAv9_}SL49V&=ulwfkt$q%3b1H}Vy31j14wS>kL+7xEdoG9Hlvr>SaJ~=t zM7R}Z25DVnORn|XYMcEy*ddy+KB`v*b2m6Z$CBFIjKV%ZsXZP74#v;uqXPXr#cDu) z#zNjO=x&R>R8h&1?WSV|6RwIlAlPoC8^m@TcpK;n4G5Qn(zma9?p=+aRbpzbEZ25Z(WJ-|E=iUY*d<=nW~jVkb4=d zQzF|)B_jKC9pog8;pf7mN@)hH_RoOZ{4-!z&J5sgkUV6~0B(MOXFzWU)K~CWSy+W7 zoJC9=!o=tu;h1U9=fct=%*>?I(7>(>g0;{O#bh1^cKb1KR}Kca83vMNu+=c6+sEW&D%o#kLG;s4+9{E|mH;3ii&0Vf=Gv+g+ z;_G?p41_b?zc~obPcY|AQ#Xy3Nj%uqT+-E$4=sn2#&RB7?)9VP{v5P$Hy173yfo1A zfsoPSWn~^(JQfKXEiPJam$#~gGnc2$u_+3ExICBx7w+cj6K>ukg$wEvtVJD`KKcG2 za8hHbLbbZnxEI0hnDB!<*Q%M;vg}|iqp8}Lb-!N?-BF2U?|Fh{zz>#U4p_LG3l?s^ zCv32MDhRMNOt7T$Z(eW_wPp%^AZ9mI#FjWbELBo}^5uXZGLPgyhP!Qb>{V`7YZj*% zOE`wh&jo>+ON<&_p(K@!smS4Y2PrX3c~h1p+2;u%WdrvIp-24?dVLOrxSIEK|UQOE<&9#^ZSikBQJX{1!$hvyYJJgqP5!vL(N z`*NCD0}(#@bHrHvOz2#PWqj7Nl<}nWus68Sb{TlEDi?v)v#$R_BKf%MZ;D#aTns}d zkAUm021O(#1!6golp85`DSjy~cd;rrgi+Rqpt_ZeH+pda0wi4uDi8x!eF^{f^6?cE zwpo3}{HqHgNcHO(GwBjE*?S{6WFg*kf4Mn5-E0iDTjjxNs5dlNhmu-@wdP>ykB?wdE=tqgaN0J@~8Jy9o-z;SKe_2JS+X>)+sy`?}rU#&(PcP{C|GEyt_>54h;w z3n7tT_B7)Vt1D0vt~NDS-5wrn{yWeNGx#+Kn_BNaKJG8dLX3!ZfIZRw3%&#e#{N0{ zl8_GdzkpVViv3IYfvmS6uvx&xg}`r!W{iscN=7$`inWl#qhb8n{|DWCQL#kgYN})m z;Z#K08b4`U1q~1oYdwRT0iAx*_&NmJ_`2PzNQ3Wo zg%5BV1Br7APjba$De;F9cO&@QDYefJSF-uL&Cg$M-j&8*z7D<=i@)4_d-(jlBRKx< zko*-3)n(@`t(bzp@P-^EyVK8B zZbohC)&G1Qw&*P(%+0us7Wia+Dmb=6y?BviKqodK>kdJfqUNK^DAkuik z7+5FW6+$w|24Cy9!FTzg^E%PIDh)c^4Fa7l@xaYXz()Iq(4aH^Iyh^O_X8XtsQ6>7 zwv<2xTj=&tkd~e2y`)S9`>IkocC&_?B64--t_f*>TK8UP_6I$DF03e;sA(j_I ztrnccp%dOIG+HbN}*p=s% z#FYWOaW*nlHsfAuV=oUYR!7VBXS*d#qY?EiLU%L4I0wkRJ^khoJULZ^}wN1v}niZ zO_Keq{j&cJe%b$~9NEv^?sQ~7H{Z`@LvI^~_zT!*cLK)dh3S;3g{Ba6;J^D3F(6!W z*pQtZJR@eTz>E(Ya{gpv<1s%rp2)!lciZA%gPWz>uxFpr7$zVt1_y{Y%5VX3>Y*Wt zlPEwTJ6!RjU?cQ2#7j01-s}g$;T#}vHx~%pj8zqI{kSB6@U6iC;ZOny20N2jU}cmB z2h0XSp^&F8j`)!8;FpA(c4HxxEkWa!Qnt=3E_7`r69PN1V9I_ z`$fTK=ylrgLF8fJX+H*rb1=Z&b~qT|X58r^3~T@l=#@seNcebgNRU}W`s*PcY|bWe zaYP(!i-ix0-3C-NsZG>M=cZyDrC_dTWC)NhWNVur^>EKZF4Xj7_(;;LOJCkS;hNV2i9M-|2L9Y#g3AjS`OPVb(IA|&3 zB$%Al$*3PO&*mV8ySYwHZhllE<_?V*T%A~yg$toy2o6HCHiQ!25;0|cTwi^|G-QF{ zV&WweUz_kj+P9J?kH-AydM*cD+|6D4ax>OgB6);s--I_TTv&Y}I9QEH3)gjL6P`>q zC*_poVI$v=qqepIR4|@*7!sX2IxQs2Rr0(aE-&Q3g}b?{AZ}h#tDxTx4kLdjF`|~k zE>k0@%vYPPK^&c#&1iT=pAbG>^C|*{Jt=$l3ROlJQf^7?uEvd0Mlf!O_I^XdI}kqw zz{pDX48&fr&sHnd0plh7nssNX(^2D2@fH|t6S=m(xi2K|Gl7aSG|{K6 z_L81^Zun>sf}$(UY5W!TnV`Zm$#vL*Nuw$;9*9zue8UwmGE+9a>@CEs5!|P`zVuzr zX9p`+1kdG}o-g`m)TNvm#ogQ)#m)Pcj^w>*Q()n*;iZMOA(K`Ji}Qt>l5nikuhlF^14Y|KIOcZzWiML3&IQO@WSxJ*QIn*J>C zP^hCsgQ-*43>SkG@pK(+-b18?97GvaAKA46#0C7ToxH;GG^CE-W9S9*8`|iyc#Yz&(>5(1y)EBq zXPorJX9;2VX#XJ^2Jw0vGRDA~0kMy0D&n8TGehHN?1O62x++eWW^naL-3t=@`)H(; zyX8#>4>EW?_m#ySL4o%QnyogNX86j=Fmnx~o1f~|rYbSi#U_tRN?y`;_)-6b9Mp5S z+Z@z$^WAI(RkzpIf!%&~u7LSa{bwY-Pdt5%Gdee;jpYrN=IeX?-3p2*2!448K^|O! z=IcY7den~-d3!22zFy0Wr1$%|%FU_ygM59vBaOM)icJqw8lMf0tJgFm=>vYYax-d6?_}lc zcQ|b2W-PIVp67sta6LG-&Nm}zGi;hUhpA=eC4JD(X>P{7crT~5IW} zTID4rcBP{PPu;wv-QY`t#Rp`8CsUGgmbM@A1K=Z~d2Jd1xSOk&xmoJv9!CAhwMMX} z*Smwmf(+T3eg~ z#NAvpar4qb(}#jXlPnf%po#Zj*u5wAj=T_(+e(R4uD&6(&GK{ig`$u7QS^g3DB^A| zinv)OL@y1Q^h3d+=w;sfHc+JZ8W>1wQ^AWRb=9Bv*Egazuu$qJhT`LXDE(*-l(?G< zC2m$xK}$lteL6TORgzFbfkgaF#7`hB)Ru4}C$w+S+yS65;&0jo@(JT(^PK-b=7-QH zb0EasTnKTq3a?la5c*tj5c-ZJgi^R`<^6i)k{O}nq!eq*sBfS>06Jf{vS2@avKQ-LC})x=$oQm*8uX)5+zV7lnyM9zZeCb_ICsKiT+a{FwXa zIhf;admOJ1H$TX}fb?YJFg<*~7aaPIGx|8ZLT7B_UMH{qU1aHuHKOhOJ54>xWOEO@eaknw;bo&sawrT zaI*@eO(r9UiHJ?XAwniH=tVH%!dgM42nuttl#Y`fFj~?y&ao|i)en~cEe9;z&9y(d z`E^p2q$d1@iJ7~C!%RtP6?cabq2pn;-}uN_(zsUX&F3&mEzctT_kPs;S`KQsn_Hrq zo0YS8Gi+elwcao~Gfc<~1P7T9F=Xs77%^Y{0Kvmf?E5+44kCTYa2?Q9*F@8R<>DDg zuA0sC6tTgD2WNzt%(Oo=YjXSxt5wxQJe#Rjj>J(@awFi9T2{)a@#G2>f?ll!sJzqK zfYR#3x$H4c`EfYr~}B5mTwQ91c@u+I;APID zDFNWccS0t}P09qY8s_P>N(_3%9*>NXG^AZNvNO#ijV>p~Be?``NZ?EvsAGw(r7-`|Hl_82x_20P zNpM=~(B$+sRldB-(oX`Q&LwAZ$*VM-^n8)c#tcb9U*6PDoo{}b?zt&2Z|pZMDDwtY zWZHW=WtK&~3nX~B;=QEa)4rjD|B7w>!TvCEahHlaoL0(#K z<2{V!w%?Mm#~@MUvtW#;`<`vcsw}I8N11nhL*M5{>xI9(<<{nB2u&o*P2^U^^F3h! zi>6tRPs((7RjY=YhpW2wmXAN$X8szf#bkZZYgW03M_n%RBF`|;^{}i$GuOVzOa7Pl z3%#qYu%k@25TBwVBWCo42CmoLC*5bale;k#A}AJ-a)?gvt%;) zYb=|vIcp@pC1DJvjDjr6QkPQ3KsFqcG#TiEZ)E{5<%^jZ%c9P^#z-_;3oC=}wi^wQ z^(fo37JxH&-8u+5;KRFdPxwFLY0FTMrrPXnI6N6QIzunTl-t*5`b)pypP!d-YSAM0GTf*lu=Pu^jw{<^aA>)rsKKZ?yRHQ=Xxv8Lv5zH<12fs zppNBaJcm73ff>{4(61|}YK?gAI2?IXuEKtG8Hy6aU)|nKP<(9nuDSUp>+2`s2eRE_|0)3$m;Gx+^9>N0_STGU zP)5{34UdM&hi`*!)$P4CydhY&Y)45Qs-~ zulCcuS`fI>A%L6Dq|wdS*ZR9{V!C@~LE#dEn}l6PoFwMr+FIXBM32j%cVv+p1N|ttnmYeo5#`s!`EHFaPzH8 z1~8XG1I(x>I_=}?Rp3SKn+IdoltcUQzs_rs8q^JLO_a)UWZ7fvtiRUNSvUi(G0%l% zOv=x(jPDq$P0vilwtC@sxI8=gUS%hLC-RO(|A!XdPR@7-Wi=CtaqoQ#Xf@}PHQD?6 zE4HC?M_*`*`uhO{U{U`7{z$W^zYi{=X=UCECe;smn*A1apy>KucmXgozNXMMU)%eA z@Quj!{wVy?%M?8I?}l5Bud06!{6M%^uvsG@=E7#LXhuu?;~CvHIB4V1unyiQpqtMU zCwkYSQ)djk2l1SsZjGP1wSo+=w=Ga}GnR_*QpeYK`@6whVCrBE^OHzs*uwU+NMdl) zq5erA3vKYdb~qWj^oX$wW=w<;Sn1T=bFu_xYUR;c<$n3Fhd(tJUgLkAmX86 z%}x?-tzU-R;>XObqIp#sX1LpW2Q%D^qGu^#=C{Je%opWOMY{d08LtJ4f>#lU;W-K9r zNbUI(_LhMD`!7LbcYNl?6)T<`h0?OES|uhFF_`UTpl46V*ts&dqyS+0C|cZ5!uT9z zFsW!gIB+Kehj<>3xpm0VMhfLPF8;G`vqP0gF~{HlL(M!~X~RxeO3;!^K4dB$Zna9Y z^oEBDMw%f$bE4Ub?2IhPB{hi6qr#lgI3E5(5=k)d1~yG}oqH5Jj2ncu7orj`b3W6$nY8XVl~s?3=%VWQ7-Hkh!#!qtxKtL(GnqX@5SrsqD^`DWbe)WJ7WD~B;2hVtR3mAk65^23;9=X#2-6SA{M?1 zyhMkYc`rE1Zh}5;GY*lASXfj5{FBhWOBMjN-VSdxf(k$QOL#$Yf&M-G(o4?S`aeKB z_&xs-ejvXrcCM@w>s@wk6wT=OT*YkmbpyYrg#sQ8Pg7b8-F$veB61b15+GzsP`1WT z*(yN-_&lvga5FkDy_E6wyZznnRg{6G+k_;BAft98A&VrV>KowU=)c@!n`2TQA)_K< zm*JiwCf+4)k=mgk8?X;}qX>6v{oK9T&)v168NE?n?s7NqMp>ZXW~>1xa2FDH_l3aS zQC^Ho7I}Fl_-rmK$q7W<2a`;ofGm8&u|rr`2P@1WWIGmPBEGNo^L@Ra?;CRX&fVb1 zK#T9(jORZf-+OparhG$^Fd^aK^&#NkaR&!9y*2Za$%09ytL65hs`V`)e9gb)uPF*3 zNaV@6r54LWvc{>`VSM_`M1>@FzUiR-x-Fo~J$J=*^Y!01S3KiFy%W&X(FA#}RP zubUZVKByL8r*VF;ar5ee@?h|=NwHdp5kYZpc45A~mx#&U(^tX{-&l!-*=^RstY|Rv zs-w!!0V^Q}ri?$}987Oi`8(hu8u#YCVANMU%{WAIjTQt(UI21oe!gZff>yORl3c?> zE#pqXyVEl6?eGgkk(-lfu6`C8VGZ|<@B`Uwv3s3>hRg2#q8Zn46B*rL4QHW*N5i8z z>(I@&h9e5^1*?I#cH)YbP~zAW&W;6RkO*JxC;S#cAuRDM3L?>rivTa-+zl#oTHUxA zk0eCGdmF(R9E&@TGcB0L^CG@bNJ8;4612#GuQz}QSOJdz5U6c7XgGD(_45=PIPV-BBiINOHrv&iJ%zEbab&=>%?uC@M8NDhm`*W2H$>G z&koar+5TWG96do5qNhI33K-yWiNc+ zJ$M7$U#zu8*xDj zqW6U)h`#FvR}k4>Fv{XORPfTQkGidz321~oUN4qQGmvWuv#%E8e*P08)*yQ273%I> zHmo^)uQ@CNoDpk>ENEoYbaq}tO((#Jhbrr_?&I)HRR1EpW?^43ez9AcY8UXG-P;Wc zRWB_z@v0_O|CV?so`1>(5QNp(4hg%Zl?uDUD_|LNAY09~{j!?H>EgrcvYMs!{$iIgXu?$V2yp8*<%5U`jC`@^Y zba4UvMh>CMT|{)my_xIjF7iKnvUjL|FOheMgmdUx@7n(qFbOE9j=HOe`k%e-SBJ9PZbD4xWwM;f*Js1!KB4 zQHxvao;)4H+6@Ds3a6htd1iQIXy3Xg&y-r=5-D6dHav9V=srV|0L?c6Io*9L| zP8~jd7=9l*eR*b*_$gVO&n|#C zPmrnvI#Ck$TGvycr#*eL_hkPfJ!O#VF4X@3SOr4;5AjEuQ2%|nh!VlP7bL-d_B8v2 z`r=~X_kd2A*h?4#xuX5^@QNhbe+Pc)d8Tgtci{pE_x}h#kd+ooHwgB-EZr=cQMmsX zU$;%-ItX_Q3EYflRecG%Y2m)ODEM=vWwB6D{}XsR3iz4pDBu$!#1!#_5J$wXrh+Vq z&2vyc*S$QLd~!b<=jX1c@Ro#3_73zfthtjC&ah*?X{b@f!X}l|-A1EyZYs`O{OnPS zpPRO7ZTU?L_U#845e5kYq4!}cFeIPQTys6nzsTE zL5*(19}tM@-7Vh?7g6?__kzsb?rFv$lDp+0fMXL-3u5#&0*+em&Xlck!iC%>2d-V+ z6u%e08XLqRaFOVF&2aI2eI0(^X~Cvtt=1a6daYL9E5s7v9w)<4<3+5|_e`^SK2NpX z45|&RMyNXahDE}?L$Fe9M9%`@-cQ37sW$IW3#Hng1|EWHdj@}`skR{jmb!T_sJ3BG z^Wv*E@7IKCv`2y3g;H&P8m=Wc`c!T8*TGxU((y_7161@mo-eH-NcIS|u#g%sTF`xk#zPqUopu;&)5p*Bt z_>@BWcH;qkyJPIk)xKPJmU8q|);AXWcQo744e${1MHo!CQ1RW7Qlm5prKNK!ir=o5 ztsVSAjP^?lw`?_uRY)gV;$CL~maLxVPxhXg_i{Bk%tGn&cLU!*pMNXSQ^em+L`}r_(oJFN*BUa8k3Nc5;r2aFQS7$ zgj#6Br~s#8bOz2bgA*4!)n+Av-x|$mstKpww4nA%}-Q)W&zr>5f87@n^r{6_@(9MfcrpYz;&I*s#u-4$@$ zjB=4UuLN9RLhVli;rU5neCDvwcfQOpo_Nvf&Vc+=^$=VGbvM{K8dO|Vus^-G%@2zm zq8W2Zy?T$k?Q<+vZhk_-Vw+{Ls-rBzQhZ+y1S8!_JW=XSbqpK7nBXd!w24N?#${ic zk-MaO6sZzu3QF(_z`yqEh6BXioSRCy9GJ zMy|ML*+LTLm;?RC5??Sw!-{*atz;HsF3L<6{&Di8IsWn6u$C-=cj%>ByVF*RB+pmA zQ~Ano-ljcD-@b6Z@-)Jg-<;@*d#^5l-#mww$==C%`_Qw8Tqu9~PT(#0({IBcY5w%> za1os*=Dpx_z1`F7_os&hnr%QexFlbrpy}7=z6!nq`P?_bFTHdbQQrdBp@_;>_<<0x zpm3vrjSGeCq8a_~cVu*fA}SVwcr>h1c{g*x`=YyITjBL*&=xETY*1-Y>LxgY6f*Lz5qe+20Y1M~HJfEIMg@8fOZbZ)YhWz?@A z{!_hvpF-1%o-7os_M>1;4hp!Nivn(55-50cfGChPHd9f+C0eHAb{jVM&9rwq+@K&Y zSSuQoRD_j>a>j)mG(5=_Bi?g_$2vc3ZpncScXMIG%}W9{Cjx}cWS;P_VPmiviiULC z={OqT>wY-hlM1JPVYJZ?qfI$5;%+XCxOqvy=<5T7(aAg*DYmA;Mxe=$mM2otBG74| zQk_Pne#sF>Tl_fMmV+bi=HiH(mjsR`0>sh3&BIZSI8t1XV#>Z6>(0QwR~yTj=t~z{ zwGJvO@coz1pJ_qKv{Y@#Qf$CCoM#vdzN$`9NCs>nJf@F7tG$GxYEjyBS2c|xxvo>2 zj+F<74ct8NNH+6fPS4q5p zo@R?V1BDll*5-~EZ=LoX=lJbL_T8WycNcNm@OpG1@|R-)rJ~#B-&OZx7{1qL_?YBVq&VhI+|?93T*;ofxA_^# z%{T?UjO6QgIy0A>(W4h2Bfk^`BMXv|7n*R4E>w+*&NZ7DJT9V(@H~JCs0|*w?7W3$ zHBE5bawb!LtzV(;@-ua}XvQsPFH^bOMu(}~d|Q}I{qI3A^~sr5vlEvwW58sS5f+K( z^A(b&lN}R@i!w5uZZ$>$?VQ8!yZrpVFNfdU&E+>Y2g>h%41wR{qj>ItkL8vrWqLvJ z6accM&>oH+e5n;K?({IGoUF*nE^YD8K8g7nFtT~C&Gg{6M?(A z6M>rpoe1l;hu|mppm!E%hT+*Z0AUmB*^)35Q3E`jg(QB0wVk8{=~@+h&MI@YUu6#X zkyFe;4tI0yAa2GpjL{D2r6whW<96B^1e{K@w?PAlGdLo}M?K9j={AYPu#pu*;4f%0DPRCqdfn4IZYlG1gvo$z|DB5k=MrK z>)ZU@z;Uk)_j?c#LAXSVL16PCX}DWD9u#HLs&j4kl~t?&Cv5f^}g<{L!w+O)~Z-CSmJ zGp5RBYm;y|{7euWekZ%I6Cz(*pU6!8muaS;A<~cCjQ6o3!rQf7Y(x-~WZ!|X5?dn% z*rY|9f}}V3k@R>DlDM0TByN^sA>A$s2T5-Y0!bHBk>q76Nf0lX@ODrcBw^Ijt8WP1 zOjeL`|D<5%kRLNgb1=i*b~&nqo8^9rrzA``V5tNFmM7Ry`$Ay#_<0?T$14{R zl$X>vn)A}^ur!a~j8rU;ef*}1h($vVCy*zXPnPv^y%b&_bRb+4I`Eo_%M1dR_!J~0 zTsC&V^P!gBmQ>;)0v+koM?mD7gS&#_C|<_%kkq zARl^<@r1;=ZZpgf&ul%aDeCuR?ete;$*~`g`#I0eC(~9UeEoKZ^V}@sW~>5$;jsSQ zL9qTU%=*%~HoC?;QAXDPKH>atN5h`dE0Mbt-PNv5HcC@8`;EdpaV1X-jQGL8&HK~9 zz}Ml-B1;T#vs#v#1$n{&!-s+Z!#*1Y=?rvvy)_m`2$13ziZf@%kNTN>Hb>TTw@nU{ zxp}vg^#L;ZBP&+GZXEoBa`ZnG0=LKCudPCw^hq9a5pP2Y(0S)rfUV-L0(gJzVyV?A z@CG2%fD^$TMdA2{oi?Z(*J#p)AU~~A#4R{u$1TP%dB?~Xs>kqID2o%0bv}aS=sHc2 z!vKpHQA`exM~CuI%~m_Q7)O<6xaB1Ng0t2^_lj~> z=FoV*-f(qOWD0wqql_qc`-thH_fpj21^&)-fxa#5ZN?_`>a|I;(*e;g516-=_*{3g zuy;tz^@>1;Vjpz^3kyCdlniW6=&EnRg!KH(uN$j&SIj$>e6&Cr)ks~koaCFl*uq*5d@cm0j{oK=h#L{7%o4XEmvtaRYn>60=C!f7 z+-y|Z$~4p{I6`VdMTHm=RV$I`{`Dl&>@&hWFt=99Z0$;eo>&uZuRsf#pA25m0xvFNsCu5w;^*9QdpmB z)H=nH;S(o@$A*rKo;*6{A#tg&6yFf$)Y4+<{OmF0u#IcOj+2s4%xwM=eo2Dk%-6QV zK8(!<7^7*>yc|&m1lX>Zl(mLkGZtv-J^^sov|87J!goiTja;AHyZl=C-8ovAySeK$ zZkDlXfqG_sEd(XpyTBTl)@GVr1_VIdpBX(m8l8j{RlD1YQBjCe+tF;?DMm7nf|O=a zXqUlar*s~;4zPm?;jvy8v_*Mt7brK=3NFtI|298LxEW9V@>=11-SyvbGaBy{Nf6*_ z9v1Px7X(V~^!o3ZX|4&rQM7X;f6mJ4J${~Y^CM|Ig>AVgWSWIkKORT>Mg2!)U9xHw- zrWpdBO#_;>6=58>@_B8gN!-q3_WS+J{$LKXxtpuUxp`;U68gp+A;g})&eY?ITLzb9 z8|72+TES)WCV5kTvin1RZht(7+uY6NHa7>(?M)$YdwiqgRCo9mX}N76pgr3tLt0G( z_Q)$y0ps&rF9@y|g&1(J=p{tj>H+Vt7dR*}IQgZZ2zuhu<$WseMYTF>S3v`KEbvek z{Xnk1gik_Bb#LP(@gi8P!%}gHy@7-9BQCX~8Ft>#D!Z*#FW5;qN9Zux~JlIYO{GAUX|1Q?b}D5aU)_5a1Mnf2rG|?F97CcPFc` zuhE)@6L-KJ*6xBs3cOGB7Vb=5tKy@wBl>^DKZU=NGljXE>;B|sJdj4Dye1<0*U*VR zuqBZl0RE%m`EEczH;<-Sg?!!3q37m7MHU2S?8bQeN4eq*|M* z(6a83IEe}P29pxQ?~XKQW=9*(Lp{9saI00CrTY)T#Bp-yx8iybguob{mJ?S_;1*Uk z>p$UVJvTp_#(KVfpJOj_^Bcot{i8v!epm+7i2@i$@D#42&5I3UR^(xz!hm^W;o$`H zH`;x(`Tk>mzH@VGa1LL;*Wo)iCg zNjfurJ#Pg+1Hp~i9RG1Y$GJH*Ym2YDLY|ug<@m`UIIgm`#7w6h2i|8#cFA3Q{M{Ag z>y<&F)_^Gm7e47{>raa2)O??mZrr>%%!&N=AlUk>!5haE7WGCQ^O`bwjA@Fq+u9d! zt;vo00T4hUJ$fnp|Foa~pUL5WN;hr}l>gNr_&;f}M=VS*653tF2{6$5f-dWo6)u40 zjfy+;V_iv-1Cf8>hse+7KqRFbHwOxlZV(WeNavL|;hhAb zDN27hM$Fi3yAw|rA)2)a>zGDo3Ic-PuHbt%I$VLnp!tD&qlfsnhoZe0u8Iw)@8HK? zaD_HI=u$&^z{7EnuFAiPSgZ#&vf;*kW<4RKQEr@IM@_ve1m_>M{;PgW|5^^Fxtm+g zf}3UWVJjLU95jD_2x#to)!ap43z^U@vJFHCn%Aruf<} z7w8njmL9F>m?bDnZ3JS`_GD-{B2{R5t>dvxi%tB?hT|9|CE~^h%&Za{(h~0qpYxd) zW$Bf7Hz%D}Xu~VuuBj#CUBvGpUBvsdg(J!kQ%A7Kg;`2bjsNTi{-22ER2Lt2bAivz zw@aftOyK{!Ab{V1jeml93I~N1tN7>-1VFqhYeYn77U=JNkJ0A&X&tnQJMoBOx@_rz zJo{S=;__;%ng(ul%Ipia#K@K!*rqxTlf$GTbi-s z4KPTdBXSj?aH-%zC894w%1oYf4<*Q>3$;=-ReBlU0cs<6XZWe*GUS~&#AXIYAmho+ zC*wub2e8D2DcfAp(O;zQfAb57KhF^me?)4QE-#eF%hej z74wiX9C>co)k6Lm!wK`4uE(UTiG(2TciY9;uJsLV`S0DSeYHD*z6T~=9-aIC2Rjqi zv-603^>WJy{M6shdcg(SpwRD4Z!6$WdFamK@`zef38^JRrd0E5%;&8?jHzfC{`9TB zMm(tcLu4Cb%Z@l)WhgnC{8-D*oC=&k;dAEbueR(7Pz%^)Pr!Be1ZdnPXR9go=e5S1 z-=NED7Lu4|?aOuA)ZlR2l*1eTr&wA$buUNmzd^L87QWJGp9^Cvu+8zpDcTcnBX6rQDzs40+5SbqT6vEoBcelN$QU*02C*ubGZrAuK zsC(u9KuQScKlqvYPog<>e}KEWC04mvI)AF$VZj~_Q`hedA^r1N7QjAcd5#@I#|W*u z#1w_qiTK4>ayeoB^NwN}S;2zEnc5>aaeEpJMsDC695=9P_eQdW1Bly#0L1xh9o_HB zOu!;LxW)Ao-vKN0xaG#P&>~RE4gbF4$f^{`+?)p)*>StQr>+L{MvsI8nfrr)Oi3yR zT2`bp@rt9MT%hz3-#M~21roR9L1G=>;DmUN$o7mF&v1b8Xb`}7IVnqW++VtyC?DSl zKGjaFs`SChh;Tk*6L)37c?OyYPH@C@XV`u1#%7eIiycepq3|)WIY$-HOyzad$TwY%f>S9Q{rF{kViX}M$8=t2?FWH_aIxY2^HC`C+ZN{ zAY(1Q9D+Y?d;^rhwqh-`4=0*Ou!Zu?DOZkS#WnX0_3O01rd|g9(CfZV)sITr8Vj}Z z$)aJA?B^_!uAd2(^;Gjikn~MN)QHwZXyg0_l@eR+HoBs7SxKPsx@sjs5(_rB;xP7W zz8j7sug1OB;B)P`)gC-wI#+8Awwv8n8IL3Aw3<_chldACkAUN>S&z#d{B?i5bfGi| z_d^!e{&I7Ay4e_Px5|U)-x#E8wI<%jw+zOZDcBy=Ql*GD%~dDsABLxQtF`g}`yO>n zf;t7mJy;PU*=MHWX&fp#-0g^BouzYAcX7IG!(uVA6Scz|LbC_}l%{Ow8 zVeJm@i4O^{+u_z^7eQe--bBb6CN)RXU9edp_h1rCRdCpYAq!5q1D5-Zz98C#WSi=z z^Z5iwi8fQ|9`#w7H|m3H+Tk=FJ3)zar|5Vja1A0km$eMz# zSdb67r1&UjmW484RT8S2ud)x(GGt^Qiy%!swsDpV!Aw40;pTcCFs?N^8wH6^Mt!C^``D-lOQm zlu!`XB8v|STy4+__8Evz6WSGdAX z^4%g=#@ZWZ_fgmTJ#54xifEA=9i}NE+e}}cy$Z6rDk^;Gt;#@lF99SYm1!Uh2r0Ms8Mx$|j$nk#YGXfGsw4cpjVl6B;=J{H*`>E0;E zMS@-?r;yicwicQGYQpR^Lrsj(2bwL*g{*L@@ISf9OoqBqkpsY^OmU7uS}tV8-^_?W z(zweOZM+V0W~gX2*B}Eo{FMBB z;?A?4YT99oDXgSET8@lWha{5#o@Zn%i$sW!ovwE=<_8OsJ+u{mxja{>HhZ`0$#;Ze zh1FKuT=m83g=z(FdkO#d^6?ebOUEH-=oRtTD*CHieP{h$7?aI8@b8&`@Royvg9{qf zMC?Mrk^p>uswwixB(;gWC=*n50M{loa<&ThB25hB1LJP8g5JuSzpe-JLZ_pa#H=RD0g#Og}iDb=RtAlp7f z+_rSNZnb)Xb}34f$1gigeAx9awuKHB>%**tZka1{lA&I!ua_$k{cNPx;z)+4z2o;1q%`knr6dlh9M5`Pm( z3`LCn0TQ!_kgfkZ5P;gosRX6h86vjYs01Zg3Fpc*TiL9<)z3<9-kruuzP`g@B{yS@ zF#)pBSox=+u<|w;qz%EA&5M-zLELhAxOV9D>Cw|KjEp_aeB9`tlAHZ})TW0U~14Vb?F zVV9>npM7rVxglcf3nRlPP7L#5S=l_^>gO>xKbFR0zV7mvn;#99$G3*U<2R)8IE9VC z^VdPzoUX-3idc9A<*LF+&x7)6WC5=A3-#@Ow(k_pSOC;(0dlwXj!@_3+k$2L?oinN z7GBj`YjA^Y!rU@r9d)`LAb>1rk!WPp!y+WyQWh)v(FUAR%FJKwXa4Pe=HH#eeC`I- zUoG35o26yeTLbwG^m^#U4$9iz7YGW*Z-Kg2?_ZzQ*EH&mB89bUJOA?!-jg!qn!%Bd>eC z0bqvh+u~MJy;!kK$eXp38U(O=O&RQO0q=cq9HZiNHa{{1eyg|KrOPCtwe#Kr2b(L)iDpd|3ZT zde)o0VC(q#IO?|Js1$W-({Zy4N8#Z~dYt4!=gC4gx{yzFC?1wsB28FSfymB?XYfF8 z_z#1hYAyK8=i+h+pHahlOn^sGt7)p)gp!j@2*-@e=i%d}4fhT#qf~q$mM->pMAcFo z#sn(iT!!cz)UE`Q-T_-Qjm`_0!SO;Fo^lRA_>NQ(p9h%YyJAm%?}3O2nyyV&JMaWp z=D}WI4C}sHCx-f%z}!Yte1~>aYT5ObN(t<7`D=IZA^epjL8>>X%0I*ymQjeO+flax-?;*m)6J)5;Vq$* zK=F~{!;r{0>HWf_mc)zsk>j5al0ep63<*osrP$IL0M1o~@~QGCOm`?zkTrY!xX1qb zVYLRV@}Xu{b0X4i8{kgbM3+z3GZSz2e?57x5L)T?q|-@HX~Pz?_t0=T{^h7#>XfUI zVa~c%N~++(G3F_cL@Pq`2J&!)zCH(>3ZRE6i=Z*Ri>i> z;StchAZN0^rt`8__M9{D${hT-;Jk--ZFHgzsEm!^7zTo=TBx6 zip2KK0=(HT9E?aoyh;8;P?)1C!d5rlcd=#mqm3UP2UKia;0-CtaMCjf?MSb1+vQN{UtAPh8ke5mE(%!Wzqb6Q7*F$VDid> ztTZ`Vavg!NwQfPdHbe{VeOabIneVXXNb74G2O8Cv#RZ13s>!zKTmJtckj0cPm1X2OkvQ1Yu7cB1$Krua>DdnU(|t_1RY;*tiJcS%3h*M|4Z`aH{?7iA|% zBU@_mWzP#P>X$t)!sE2;dFj}yU(TCl&x`yL?oY!VuHS@yi}hftmu%OL(*S*K$L$aI|o#GUMqZNrU`cp8d&)5>$C z)BsmNZW74bR1(PZ=y~Dj>2F!MBoH6bDi?%kihHjtKrToI9h1Gc%sV9{eYl0n44DLe zLS{%6f23uG#BdQ)2+VsSb)e>H_GgBK0+b3+4AJ(lF`)FDF7h0_J*A7h1AgiGNB!LnD$+ofUO*ZH6MXMNnie0=^)}FfNhD*^n<)H};O8rM zTTtbyGg6v)9KvuQaNTsm@yQKs4(`#nKT~Q~u?rsDpyOQ$4L47SBPp`su+9$$ZXQU3 z17E+_fde;VAecaEpm6v=U~nkdaM1p4K2`*d(V*i+TpsW6sYTooBjL2xF9B}xvwWjy z#(-w8aN=$d&}<0~ZoWHomVYcTmVX29hxs{ZY;%!&pZU-8|7bi>>P~eMFp>MM#vUq( zf(N`0Np#P_%_cu?w&dW3yScdG=HPMj$-r>)jaj%!uv7{T(`V!WN{SZRxpI-Svs9@R zlKMy@Wwl?zZ1W>!R}NCPi|d;l3B%1>S;APyxQ1wJ{A^%|`I22^jqyWOm^61OHfXqF z8Xd`XaydloCXcuR?o#BbY=OFg2LjgoM7#XC7BNBZss%J{vE$vE*7-k4V zqP6!BKCG>R`=yve|Ap9icVW98>p%+F=0J6~AF6lfK=n>>9a3*CWysBV)-2jTy$v$K zgI)t24Iu*dR|5m=SubD>@M-QcY)$dTVIg8Iguxz+#$BjX~nyoLW?D0e8z8t9R z^+Sc5ml#xjD=?^hEDtK0yB??*kRrH%d6fnmlr<)@q9;W9PxNNW0)lx?J&RQ6&k6xT z=KvvXgNWSg3u_Pfv9>=4YXx!L)u7zGU5cfffn1dTp$eq`8W_rc#fvggh?k-<1y8dP zru3cySqr?&7h+pHA=BH`+SWulO7EL-H*- zkUSu+yPA@lrB7>#Xv)6`3z*}l;S_8yHVw&^Wb}M-YUxlFunRFUCrU5lVeN6L(VaO6 z;t}C2YAIJ(kVce+1gD3pZWg%N1jMj=IXxiuUZg@K!_CKL+nsp22vL4T@Ru|?Q;ovj zw-fMrbLOm%BCG*Hl9y?sEGdj|kM>3n@uz@a_D1`uLDFGZtvKIUFQiRbyhZlJMLHyg z>L?K-^xNPIWa$y@iQBC&lrsI{^pZ(y4eMA?U?yF)YnN8Dp?K(fLu+05i<~vZye=IdjOj43V@tJnaKd zM<`Jds)fL3(q)q;AZsukL3)K_*a(u@aG)KpoUANg5vOp;dbd8vI~{l1h;KP&-!#e& z#uxFUbQ(@z287D{O|vKYN*eM8&Z#6l41pt=<`IISuUG7JTMc|kqu4dhTrn(foGX9)I0r{faqD%oBdaIhvz5N5pm4mgDC8mWnO+4`Hdy z-bcOqQg}l!kCtTespixaWHVzeSu^xH#yl~)K^+%KDf}(xFi$;?b&sMSMJcP&+tOUT z?AlM+cYM|lwdZr7#@%jrycyj5Fhi}nz5Y&M=2|Xi=`9(u~`U2Rfi!LCd2)-QS*x%e4te*C?BH4J@t5 z@>&z4%nnnRiM`NLpbocnVX=SwAuO`es$GC2&xrG7L~2f&6w;h*8;7kQ6PuLA-qmXj zR)oaMb5LwPu=s|T!qT$i7on!flZR(!rfQJr4$F_Ra&soGtYe`fz~$SNG=W8mmREM( zv@jh8aTQS$#8L@FFCl`;>XRl z9Ncg>7dPA-JZ|0-7;b*)D#VTAG?Wa-P`I(~3>D3Xtu1}&Vyo7niybIueg=*l&XrOF zzTrH>Sa4m8%}3wFP}b*L!eaFJn{IR}O!}yYkhTt#>260*IuyeSRqV9fY80D%zOLGW zW%pFZt@AW{<~yJvmC^(r8&-~9%zuzlifIE|k~Duj`%5GU*VAY*XP9uG(Sq4=pKaEA zOuQ?c=R3Ovd}pvWhJ)n42PX(G8DCwvQZNaM)r=*A;@<26)PYGOVzT%2yo@PqW`;76d+i49Eq4!fOT}{d&s23*MVNWj_qR z^gL6({-bc=`1<-M;0LnVV)u;#8ZNuni)M6_{ZvLbI4sOU36F*+@_q)oIc_q1I1!nQ z!6Xt_`$@b>&;sr;>sjkW^BdDhUbX*LFN^<1na0NpqJ7ClBDLM~=Qv^=+ zF;4K|7$!Qzhq4-EijEjEJ^WIQC<%OWrqn7;bGRoFL&*@sM=25?&R&8uB-Ft}c(%~F zn38ZrE`=I}BRRdVDdRuk^(eX@GF9vs=1~Uer`8L72dQ!;l~Pcs{MR9&@?-ksJ;=3+ zTY#U~03;hhY%MRtmm)6YEWQ5l6^1J5D`lw0-7YDwTegDo+T<2+oU3nycG%*%6@H-l z*vvn-V)N?yHv9p)YrENew`fM)wUgU@-5|Tu>c*qp58dv7ZjSCM;M0l7RaCK`DO=;G z>}Ej%s4VLd+>CiJUds6T-TrQPw6dTKc65Hn&`#g9fabY-5=I>|?dD*&^1li(@X|t9 z=YAR{>vsd&a3^PvjJ>$foPtPRI8{%t3&n8@y6hOSbVJ2BoCRvp5$IA_yT;F2Za$sH zTE2dt!&+{BBxKh9zX7rKq-3p#dOiVpn2(!>u^fslR$`tiL@*y|&diQBo(ES~e7Myr z%~ChJ{2bw69fZ{miDOn61grh?dz~Kyw}@ukvGvYx?zY_l0XM_iispCk7Dx+Hh{bfE z&?fue2ZV?>v4>QBFpQ06CK_2CY`BT(PX?H=l}pUu3THiverhOik&`G$LBU2p3O41S zfV)AGq@^6V8Bf_pDQ}gG3Jm~jcF|HK2x&je0HCs^A;s4rO3&AAvk&$M5N7Po3t_?Z zSU|9UkaVY)fA(2|vS2eNDTij0JQC0c9cMYBolxeqHDpNtAD*RwPU8&$#J?NPLSXzS z^bUIIT)P9I3>~PURBA)Ex8pD{6L!@hoR{bW%jIRN=fJw_+AKn?ZD_Uj=zAGsb-#Ke zI62&PG@OzT3q$EzbY1gFEc~E*(OZRn(64L}8+g?wI6$#<#Zco#tc>?evw1#mzy4;m zU;peP={JX9wOvp2#JvSsBKm2V>>azZ-FoBui`Rk8`uZXC30us%#Z30kW7O>`Dd}wn zqb@C+=hh@vF^Oo2dz}UF+IpTp*?Vf<3`!1z%(;|c&TVI4bC7NxvI{ZiF-m8Tl?_IH z`@%6Qjc~;@qATvbx&Tbep=Gjna^4Kg9uk?kahp5S*6GdC9QKXuxj{{|DT9Br3&%fA ztKt|@68BmQz_G;BCwouMn@8>-3l*&N3UCa9l`i9tv|y!|;Uc=|%zI%6@SUD!f3VWx zLSYu@gdmP3426E9!KbF0Z#;aaW5%_SrM{Ha{b|8KI$lnWsk?QzHkA7QHycJ#8}O*ZLLeE zcFy7VU4DMwm&0%F=JK1H1LgN$g~0FeQ7VG$VYy{WnV|=GP=~UyqQ{?z4wzRlFriaz zH7`=wjJ8-H5x6wG@W_qq$IBus~?g+u{beg>l8bC5jM)A?msT^C3k>-gGsz|m@IUqn7d73;l+fpfR z(F1;94CMfWyWQs4Qrx^x*;4fn13u2qQI?AUtP<1NhWo>U00`i+T%z2JCtY}LJiflo z-wh+-m5p~BA|eQv=siJT^C7tcvvfQt%A{52+U_f>7*Zf?_7o@BBYu8zbLxtkuS2q} z1p{ujR@C9}^FR>%yi4+vWQP%Z5Fxi*7H(AYvS1kGDca;wKQrGTn(_1+@8sleE;G6L zL7trH+9VtfKM@3n-^niQgmTc{PGqM3%QRDzO{^FXQ(gV8?Pk1>6%krC`htOWx#1(&ITu;%+XIxLJyYrGTX4K_Ka3Dw4cRB?;mM6W$IuA_+qwjR+t7 z*B69tCMzh?2+D8X@Yb}s=U z<#c-k7uK1Q=3*TC8;f!5a@lu4Wd2z0C<>k|$S`Cwob}4C5Li8aUdKLL_2!cr=aTxI z((JG_kKc?;^zoZ2B5D>noIsvjJ}Jd@Jr!ObbRb+4I`Eo_%M1dR_!J~0z+qx4f?^{? zTRbcG*Yh;O(?ayS@iW7sIdwyTyWQ$&1a7`V+M}x>eoZjl*Z-N}d=P@q>Yz_osn@uWxa{z|Cq| zdNYUte8;flz_$khhJ7{)(i!OTdTT6lQb_R&#hJ5W9Q8B#Y>uqwZjjJy&3JC!EoFU} zO#Vm+Odfx~HU?>~Cs`N8m@#863~^gf#A%qVXv*;>$i-5tA@}{DI-0n*DE!^9!8SD& zHJY>!$WN;jap%q0XcLdrZV4P)EH-tU-YJ9wc#l9GI4JxkO0dG31rCjeVqTbZO*I?Z z(Zv{(sNpPL*m<0qnvE`&K)YcLw}~$F1UtBVo;CxO_1bC88tM)SQ2UIqp?zV8JN&HpVR|J(SM5#G6-mmvr-D zW-NQhbkTb$YViVpXSzV&7Pd6yCMFKAza=Sjs$JmnfO%Vq&vhpYdxyjnuLyJ~_E9G= ztl)zJ1DX@MG)?qoacj}w(m~NAwpUa3R9E5Ak~!kz1;6;<=DX5#1YdXeJGfbyiW@*j z4B;FgmBVNN<}Fp94m(|vt7rAvO!zM7*LxEoX)wG;z4 z%N?WM8p+wPnEIIzm^%KOt5lJ3;#TCc4rFMpGXm1QHWouYzzS5YATed0n0;ib*^VnJ z7{rjMT8TvW-!FXB#aer4dZsg5*q5SLSoY97#Il=0u?#>VQIJE}vlxv*$#ab40698c zYlvgmxj;B784l%e+(ufuYWzBsn*v!&%cPRP*K)s$H*yBWuK2Do$`p% z=*gpFo_AR)EX6m3IkmJ{IzM|1Ic($Fu;ZlU6EmB?gkO^2IKrCi-?qa(jLilZqiN8* z98ux}*shn9k&0b2)@ABG0dRM7Z(Rq{p0Uw z9bh-zyb$YUL0goEuLya5D!-F4e>GaBy{juz%3{x^a^$(@W6 zWrh=6m}#yFzEQMu0DsQP>OFp*ax<3P^753gyFBIQz`bY4p>mSE9dtqxYa8P1!&PzcN^u(ph`&7D%YIWAGf(GzdU{Z}< zu3XlU3`nW&?Yst9ti$pH2p7&l_z{;{(F{9pXqDYos~3C|@)|;62Dr`jj<~M{x4;*q z4zvTH2$2u-xq2Epcl=rj;b#bUoej9Y1yYK;i|OWhW2C!hzZJ6VN& zjn;Gt`ZOEuE;yv%^hA1#guotw?qrc?2lD@je+vI~&J^ZuuKSalcd@{(ZchaAuPIj{ z^8~gevg5vgR6O5}!WyQ%ZsJgCTmn2gOhkAsNg-yMQP={c$MbSA+OMFtp9|c_1yey8teHwoM>S=y`prlFq_#J#Pg+12K)+9RG1Y$GJH* zS&Of`LY|ug<@k{xIIfbl#7w6h2i|8#cFA3Q{M}veuU7_zS_7ukS@@)%tv@N6Q`3A> zx^XiWQ4eq;p9+Gl&l zw4eW<$>D!WH*OA;|7U~X|D?qposQ)$;sh9IePPrXKt$lbd86VE{a9C$!3cMAw->m12U{DSw7zf_5$_HH zF+CFfG>Vhl&4+A5EXn zK@)d#>ql_2toCcgA%ugY4+VjvubTT8Y?~6aM0mdl{_+|$(vlT-#}rGhF=4?8!nKD= z{WuK^%oriI8MkDrLmT8J2ai+2El?Xq_{t_6fV1^UCs? zzTk)N7jxkI>*Bf#Uv8FVSptRc4}}2V@q3{3*^{FeAhy0%iPgS>iRmlJak`{NLKxfI z5YLO4Iog0AjZ2<7s6!3W_z)c5>O{>(YCNj#-62O`kq9h2e831V^hXxjbO_Op1H!ZE z!LT=y@@N<3SDdr;TYjMbC(*nf)VDWk+{j0Qe&64UNtu2Zhn9_~_q)M~o@sR>VdY*uJH>`qMgG68FFn#dKM3eB%hP zfjruPFkRXv*K2ItzSmpPqo-l7SUn7OmnS-wHnDclqgP&0x27ja;Iqj~B*P8V)B3h+ z`U~{@BR|UjGzaB>EUvqD3^xak@?Q%9<-OzCMhqb?QL#m|nt?@#6j$`kD=hTS{0R7~ z90dG7egtrH;0XA=5D+kazgNMVmaJg63MrJ4HtymqPe@n-$nA^(5^*x+c)yTyn1Bl+ z6XIHwScEtdOQvBjC0^xXj~Z~jN58<~#z!z^%!nRJ6<*eGiO3+vcUT30wg!Qr|8wn% z6|fZO)XJcv@XS-?R*d48IuL_pX7Cxtp}iq}#W5%f6ZUvl5!jG2qO~9H7>`+7<|)IG z`+@xh2H~wt;v}HC3jRXY2`=82$e>?}O7HBHIdDcI9B;uM%R(kG1R$sHC_9 z^jBM6OU`>OsXty4a;E4K2CAz&L~CM|GQUBWtzMe;X3f2IN7P;($NQxejsLP(oH_OO zM((;nw5Jx_<9=?iIXBBxFi)`gYH1nk$*FyQgS!vvrE*pQ243 zM5y^+hp;;6-EEuY%ADn6EZoS$?HXSNb+2@jro^EAou8?HFPc-`q}(DU9Tdd!vL)q8SSN2LsuZq8h3=*!5h zDR|nFho?<^gA)WWBHO*;FX2d|p&;-ymBiEhoL!8U^jMj6$n>>$KC&$ZA3O8#v7K*l z<&A8Y%O5KaFdTf01%Z!1_RmLdPeH+*c_`SGf&$qdI0{NZpy2aKDPd|I;fc(3C8mYi z;M$r0!4^5~W<%-dsR=0G*J|5AJ}hl$=NoT?opXw}AJBn9`yQj6cBt-Fo0#QQxN_|W zxySuyA$YOI-?Gx#cmJu3aWy07W_TtD=>ukiGvlJjq&)(lxR zm@**6V1(reoOhja zdsJy~RdP~95*hQjHa#;H<5BSK=y10q!n#W5reX*vqK_)&4M!r0ng=JElar9BAaWSM z?JWL2Rw{Rztyv617Y!oohypaACKQA)LClF5w0mv}!kVlxF_a{E+${|L@PPvdWWQGJ z0(c=riCsJxN=MfSi(gVJ$h3;8K29%vNc7;iq7${o`M4r)qZ-+dACeL3_E_q;dVRoz z{m2W=DTvjA4<}6$nv)fH6780%H)wX^G8FMK&ML4XkZ?8?lHY-&bvR(adosH9IZRQ2 zF@ofm!F+XZmGgwN=WuJXi=Z(4JtEGgjYCX#+jK6-B%F=`eqs=};0rond3}`rCF!7_ z?mg#}?os8!nK$Z#Yue!i3Mge}1TJwB4IK#r9w{UT!y2Kc`NdkN%B)6`tyA3Rpn+ar&J3NU9V zer6U5%f~NuXFw4u6$3{d8KMk7)S2bumq5IGk6#i~LP1!IPCn4bnlf}%9*;nOkUVvd z#8^HUMo3ooyLe3|#HaYK@$$hi_ys&Ch8dO`elGb>S$fL{!(7G5ck*lEPu3`W@REDh z&+@@A;xw}*e=v+C)XDr=xU)vAXDhu!M6mNou5g8&=etF&jLnQ!)3#FA`z^3<5k<7f z#5B{CkZoqvvTc-FT@@9+oV?}o->>2F-vUm&;$F_VGb-82GPPJ%h{nC#WEtUX&a8LHgTS9+>5vJA_X(-^DrhA}Gta1l=iLJ}AD~wamqT$-=(B+tu^a77U0%k73QP$9o zu}5OzHU4+qqxx8$Ky=@9y$%5%T`Xerl`Zhg)o!AdjyN+=$($a>WkG2)e7GB z68`Vy<14C{j<4ueUlD(;qQAP;ch)arBqnFQzGniW3l0tr4nEh8TkXN~rE|5`V7u9E zmGNBnPOCXJczAfQ^avyYHR}*#fWPjqmoAhB;eIHpxxd_;o^Cb<+pY58G~{Xw(zRL> z@8eqr$(P+8Or^YtedekaL0H`T<^YJ9?2TUQ7wlHg*WbGnKE1`z4fI)#(4k>*E02jy z#l%+)Ccd)3OqA3r#t|`bue(r;a|oF19l0`Q+2bIC{+9%UwhcE%p!Q#Dmej>asZoL` zr93{JQG9x@!KZIoI6nD^R_r30;@&F@z^)8BCVOw0H`mgK1LmEbD}bo$t!$L0kv>yK|;gJ`eLa@?k_&IRGjB?eO-fcIzL4UwVEy zP@jWq$5+)q0zdFpi}g1Pn7FJzEt+qD0v6xz>$XN*hiotlG2D!Ysec^0Rk!!_k&R~w z`hw8a6yVgJ6Qr&2leSho11f%4&*0|g(@5j%kNUeERHVVz`w65mFg*WDq-pW-T>k{n z(XG~cV{#EBJl>#=%H0;Y1v?|9na9UEEr>g0Uo0sfjx%j@Fr&WxnNquoT`qwz$k zJJm_RM8=vMbrnbyJm7suqI(W*Hu-V0B?mX$&BYBj2alUS2n;vhn1!1JOQqm2cSs#T zNzo!45RMYrS*lbDNqr=dvf3|Uw)v5=D+ejt#dSD@-cl;uyp<)4mFpa$t?{RUA?8c= zE`jBJRNRF?&-VG@ifMF+ga;Ej8eWi2S!+{v1gBuMi!u`S{Xy!E(oCSaQb)!lxm-kk&0JH_=K4pg}r z6SC1#_ncK0B4GbEFuJRTOG`4&ObLhgXf1iNY3RvvJcAy|d=0s^Oxc#Btuy_Kzd=7FkLEz~ zfVl2zN^X`uttFxIOdm?!#T3@X>r|E-`tGF;AmA5y814LB_(?wO;_J}d=^1SBqLNl z<1mISwVZ5I9oHIT`Wp2R$9K}Nr|2wB2qtR5nTme%@U}Fs9(!|BwwjLnq4sPJ)VSO2 z&H|R3A7-dQ0r#JQ#o<}n03}!Atx~}BWyYQmR6zM&%cFBjG-LH%ud8`PTpw_{ar4*7 zd9V$vZapQr$NguZB z|D*n2bvWLRYRz>zm8#QTZ*KIv;zmo%F>6l-qotfFOys^kEU5$X>2fXq*$+bODW?;H zAd+Ljvba#PlIB5SC!}A>a97-Duwq34;dmuuC_hpztKHcU2h2S;yS4AAV&O1ooPIUtMs2$%77ppI{H!qdynZYkITR!rKb7-H zZBY#XMS45C=Nn)-HYJEBS}0o$)DGL++(<3-4v`%qqEnshkh^_efTCFScj;L5PhOu5 zvv1HkR$Ywg+JtQ8jAJ*k>e<~ZQuRitT z3YH3~AD^cmI3~)Q`tcerQXYVLFXI2d!P87#q*FhxF9u%4I1wN}#u#W0-sw&5&{-Yl z@rlC4a{<2$JfypM2^UVjY4b9E&{A4UdQdLEyQKHZW=iS!Cf{SX$?G^M)j|R{PvNm| z#bdsd4l&AiVyCK6f4bZ0b!wd!1$c|0-s&If9rB$>@vz?c7THYi^bVD;BT(1(7zOIm zPzSfe>Mo7{WNIJK=-D!MeG&ZKG?MFu;LR`M?UdN@w`U%GR48k~qrr02>v!9+@SOIP zpg;%1GgJAf$8(JmLH{_8S8>g;1M!R}5PkW2@9d4G{q6qM(nQauK}H?F%2jda~JS z_7LSE-+C^(+-cW?O)A+gOuC4AXaLd?!hUO!>Nf>l)LEAUHX)2x>%mRSvi3m-DDPfu z#vEbrsKj+$<~t~m1Qv1)--DtQyw38mYTNzw6`UQ9nd{f)V9mqb{ZRlsp5^)j@T-R9V{H>a+coE?6{Nk%05lfa1(TTuP2Bm=l%JU!xQt<;Lv(v z4$A5zHhxz3V`_$*m&HAu7n+p!=1dCD$S+zmLeX_DSWfNnj0`s!FYMK1&^)};KM!&W zsI&x_$?ec_M0K3loq$j5@dV5mZyJW7EbrA+m@nQK;AA3iou6cMf|oc3d=)wR*>^xO4SsUYN)xm^5?o3v~7_y>{= z|My(`rJ5PyYqaxKj5QkUT*HMMVSF0{6m)R60Yf5o_}6GCs1knfvmCfM*vTe^tFGm@ z3V^_&dxm~IoZ--2#zm+Co94aH(qHy8Qx|E6ZrPFdaeN8vQLl64H3tuS-Nr8Hv>W*t zrcJn!7xBwrD9*Kc3b&$G;A#9INLkQ&pM;?Ut;KJ@Nw$+``K<4``{i{M3$!4~&5z-+ zPvJ42XITvVE+#7GwL5Z*xB18TfP5D`%GSHMnX<*aL*(mI{>PFbVhdcOv9cez?1a;y zv6h!`Ha~;$kuUnN3a#|YBjm&iuF3!~+`|GNw^Mf33QpjT@}cpd9~#_T$$|!7f4>6_ zZhl0gcg&#ixiNvpg9;id-(*#-w#few>+T}rPQaxfsXEBH{Nf=$D7d+r1q!}?zXJ+x zK0L0V_~Mv=;!zhAA2$|U)-vGLccJlSl+>l;-J@C`93!7$JnY8?H&ew?uPou~F2mqv zsyHge-Iy_q?;I0s=nAE@6`M6zJFwQ?MFyLG=fYMCRcm|ErCvTd9`U1tn@MHxqJyuy z=-}p=aYe@uj0rlX73WwHn?+-_*^6YfxCnV;9QDp@Qv{mOl5h2Epojdp_=s$#2sAG) z9+lT`cO(io-^IAFN=lBCCi>&!fseEAMg+!l$09@-Me|{;AwqIQ`Vp_a5yPd`PRdwA z`fN%;QF*hJ=w@mp&i$tBG9R(H6+e^WY4m^$V#7;pJ((Zzs?-(z3=PhD)3kL-f27@P zevW;GsuN99&7kdm+yEm*r39g)S-n~}tssai>RQ*8e2%M_*g332R9|hN3|Reihk}ar zz2?VD>yJUc{QW6HubgRzeMER6b`bgBQJs#JsV*$7tj;dY%}?X7&h2Vvy>e(eSc!V# zH%@Ods^p1%N0qEgiREujR+l36bRkBFSM7XMpKaHvxf|7sxW-5QT%%vWHD>)>gPWfm zcdqgCW5P9l{x&P{m8cYvfFTM~X~VFpJ|=c8h$lroD+DNRM_oj|BA$`Zf&y1{aU1ZW z0#pM67=>F?$SYMSJAE0Iwh(8CS3D*DyOz9TjVqk35!J3W<2CMh4IPVX=vdMYhpRqE zBNZ*rt)f^K!Xqn39+TuHnBHgz$0v6?7g2QM5*PO~n59*B5R^G3hIW_+dicmxQXGr# zLx~mAz!vJst+n1Iai=DJ@R&JK8ZZdOq{n0rq=54lv0!wZoZkjefQqc{d1N3^T_DZh$avp$S(UZ_{5w&IP$Voj!;k>Gr- zsG|B=#vj!)EAz|FO*q=E$DfI1J59x-rU*Q0I8*NT(bwR$@`uxzp3KZ?P0tPwkYowo z9&W{`awmRR^N?Jw@TX6Ua(eQ==5zvFVE;jXTM9>t{Y71mX54@zZ`RI(XycA1X~dz& z8X0qN1NDw~F1B9^d$opq9P2J9SPsU%qjH4h3jxNy6kspXxj0LEm=p5lnH}iYTe2J{ z3-MkzBEd+UqM{~sTIwW+g2T@mbV`E|UGR{rHT5~BYC?DQx|f~j#Iw$bWo5C-lhXk) zm^1BWuevfncWUYQit*L-;1xvo(|lFDMav?yehA%8|D;M#PHo8EL3ciB;kU6`y6flYr1gfrTP{8;I=rCJJ%Joh_tW03YHr z6M4q65?_q04_8RGArbpYwN3^@+=`-Y2(iZ=b3W%-B8kNUs!xchYCs>vahYC1AEJdM z<`yGd>hz+gf>&@YqbP+QAxe*}o0i%;bD`7Xr>h}NvEUodSye_C% zM(GixL#RPfk3c#tI$_AwZ9h!RTjCUV&9$1Cq~nzP&~D@FSEYE3lQ+#t<+n)7z3A6+ zf2=^uz2et$x%p$`uI2uZW1{7LK1<8334F-p7y%HafW;=?RA3}67piZ)UxS85vcB0y z1j`5tZ>PP+(t9liJ6)4}8wxI=bWNn_h*3cO6>4DYQ;|s}R-;(cqmZm=1nqb?G%}K& zOrJBHnk?>2X~`^ilp@BPVs0HqFKpH)8X>VruZ|2iOz zlK##1I$O=!F_=nF1X)V(IV}LB25H?=Sr1!rWXemSM<^pqX_Uw#j}>Z>^!Z}&`h#io z6_SiB>F5snS0;T^04@3)M6?J8>wPYzEN&u#amC z^`FKew|h{Tnp;~@y@@eH>4W8EDiJUSW3<%ICx!MYahBH4HC;7vP!LZOnH#50E1{^> za%WuOI_fzGF;8LLn7pBscRc~_w$k3@>D#beV($?K=XeLgdZbw)-uiG??>3ZEhbpPA zA$_y85DgeM3|*B$)M;mFsUJzS)Z>YA3T{l>uyb*(+dLO7N3{rhjQB(!B@QA{JSQ$v zQ-9p6rY_!bQ-PlP(|$dbo1YtZJ@xm;L{EL<%^B8MB!o#=h@(e>i=8euYm%r^>}Uji zl=aVqFRYP9u%_R3F2e5;g=^~qhHGNi$(y(wZSzTKX3(}+scnvk^^w#~3=|x5IE9n% zO{tvV8*G?`(W=)3ucjThW-GKu#xV`ziuhfsB=l>RKDvJf*jG+1pIlga-Zf9A4UF^& z!jUD-ug3UI#p$ZGZCGLDv>LL71W9#bh9z|Yrl4pG!bsAOux5xYBxa`tQ4Jds+Y@M0g`*zoO8VTMjd>o*+UNsCAyEbiLVyyBoS zM!Rj{+)<|Jg?eI6r90?f6-Lfn*NLBU#iN>fMNj-6;j*qqZShllN+tO!lZNSjrTS2{ z5%}n~nsH2pKotC9fdkKEIg>7n?H;%LJ23H9n$Tw zrLTo3=_DPURNo1ewXxGjVW$hp!C(Yc(baOM?~bnUc5+pmVOS9l$SLtV@QAt4KYaee zdT=o!0t!$uEYcpBNJU9ZyI(?MSAiPn!ayy1c=UMWm&bj$q9PoA=2`)SSH^Ubi3w(= zWVz&MsICvFLc8l^uW=gLrA-`ejK{+k;bXNU^x=Zd^x~boYj#p{7Qen=H@*0FzQGBJ z)a@!(c5GuJ|J9i6ryspDV?RyogPg}O=YhDOClNqM4F`9Tmk_S92OI1}#;oy6;ITRndW`d$Eb+&L_J_;%p>?M7)ED$y3g7 zW%?1(2lc2GUe=<}-ql$FF}vIMu{(nH5Z5_`M}H$vjM)2;#394C+;dsC5T_TGGF%tG z7g}qmEu}xr46j6z_F-jSZbvM^*52OcRD2~GWBEAEm#rwD2$#LNY>-{da97&u% zX~xdxh+TQuFkv%9abY4*4%~-=)F?VDMCo2K^jTXRXM3Sml}-Xqc2CKqP-vP#!j>AW z@510jWSTE14f`#M{30Yk(P2R=wo{U(h?0`+h2 z9`deBIPhp=>6-~6Ed!Ez)YMM@tWnTuX22_101pXMV1(1_aJ)5r6tzcH%>shYeeP=e zVq0PopC?BY1XJ`r^>sj?9-}lpE4c|Pp@N-=rtBdOZ=-_%{i)!C$urcK@4Kwg8%vXz zYB2XQ3=tF~)RnMXYXA#1@@X>Fh?UuDZZvRasW=ilc`;YC*6%@g31yJ|91eH6m28SL zPxVo#U>34AvWVrbcWV7DSm9-qVsr_H0@C)+MOgM`rz<^X?k@6etJp;f?MwN+IepGw zWMc7C8QaA#7Ht>T_y!ylY?gjlRPB$l`xPezpU1}Q(>qi_X;WnKyhVDNb&Paw;^*RL z_(eD?*m{v_&r1E&4+a?zsts-99;62Ud)ge1NtucNZ#*`#1DzTyp{UoJ!&UU(ZsO$&0a6AoLX8wJvZO&cDjswsslVu(&;^8vP`cgCqy_b#mTeUtL4=C;FTSt zOqeHS%o$Dr!`R4Ram^wYryg_)g)ORfI);Zzr&tVQoRIj$m>_XeAgmY&LbA|K!rd#9 zC$1_br&g*UKZym=?aA)b-7Vm#CkZy}u8SN?=QD7&RfIG3;9DKRq}t!BaONEMG)_=E zHYQN}n9{SXg?AVppOz#61!QF7=K%)Y?$w=62CBZf2vysB1I`e$)E(8XQT0X~B1Qy8 zCy0#`q)v|qq|W{Um2f$QjLI%ff6_8;yMM0Ltci(N+dT&)UTsLk!+o5pi|Iq9O_j(2 zszxZ79r@}Lpprq!A1FiyI7Y3?(GD_9aLOv>++G!`9eY)#WmK9Rvi*K~+==R9+~#NpD7 z^bdg`PRJlV!+V*W$H)|$SAId;5VPLvjU=QICAhFd3a!~N{Rs5X6=@em9?5H|xiq08 z_nFo$i7n6-nN!j#?ts};=?nC=Z^__z-&Vx&zBPm6srGx7935AV_g{<$#~b{7KD+Y* zWDmF<7zaY%h+W7xAX5r|kcC(rn`hxS2i^>5Iu&i4Dw_R=@i2Ox{SUWT(L20i!k{m_ zf-DW`SVgK>Btpb+)Fv1%1=W+6i`S0gpN;r>jdZ-lf0Dd8|3Tt--%394-8?mOB{yRZdY9;4E{nvy}zG^ir%F8ZMh3JJ8-G7dDvxj99b-a0t!SeBjRPrN!K{K8sP_zipMsFQl!2hLW7#M-WAZ1emy|%I#ZEq>7x1t)hr8k%C!ut!@+> zQ7*H(G=^9#`fiNlMs+b_m4?vz+C_BsrYprYL+gGeA zt=MeeK&4zI85n0-3+kAZ>}(tQIFikYMG@)x>3vWzHc9OGTQnIs0s`Pa)*_ormq>>V_wXG+dN2EwEu<29)2 zJwyQ$S}L)VpHmemCj}Yx{s0KRM#a6A4@XvfHU|KI(4-)vJPoeeG=tA31sUr>VL`?m z9YmDKpc;&mVtZ0vA&=BCs7#9O(ad;E1CHTjQf$vVOUC|`tMkYsCEy{Y17}i~cX8&4 zsMRB4o#IKcJxY`i*7A|gOwq!YunvhY3NHAnQ_=NR@(v|s>3AinHOJ>W9>o@tuX4lU zOf?7zwv%Fe8YbQYtz}JBZTwFc&NS7+MjubOK@`Zj`Df zhY_`Deh2`M9Cqu(A*-eYVp}K0_A-to%Gs|>itPbdv-Yt~lVW?~45soG+soNGk@h<& zt7bT!x$4GnR#$cN-Wy&~o0~hIlvN|?P26TuR_$WjI;N8+W>Qv7REsmU9-5gP^^Fb0 z$TljK3P+=!lvTqd46VJpDNroT+euk9I`4_9eraRMq^z1GD*4|@ zSv60&IE@vWBM2fjVacW(3e4D)RZHy`z%N9mF2$k<59=h6OIDXKLRe|Z_r6++TngRy zHK~)4ymOMsC2ELFa^w|1RSxpg6~`ly>kgzyZ*3k!jubMwwl+UZzo5KZ zqceEl?75hdiF&g~XSu=~r|nQe!%pL~jSG!B-Fk`s^Xkb1jn_^d=r=wm|N0a0SHJQ3 z%^qdCbJ?{oucQ9mQ!_I&XK=23Jaaxg*X+*3oqo4QCv#9%@XYMOOn5Xz%-|+UfYPsr zH^U3z4DLr%CvVyOM*JY?SP(ia;pRf0dG8gLAXX$9Lqw?(z?8MvegI*H~}h=B2EG@%4xNj~&tj z!zBKfG{VwT#rM%D%SaWQe;p(1H=2VNA(5ykHi}Y{wE*QPWF2y0rJf7B^?+6zzVaGM z%=aSQEY1hVG192mB>#KJDDo;`a_8d0(!%2GOKYpMD<|{ea>NgpCuK7gJ@w+4A46?u z3uW9qJ$`Wc(bB`^f69Q12*NQJyg0goLU5+YZ+G~}(Z^ly5NS>FyvwPW4i4zF4_~_c zs(Tk9Hm_FI%9&$}3#&PKhdZPtB)s^Ii}Nchv(L}xqxD06w0^Vzt^C*n4qCZ6;kskP z+kuW7RF-5JDcM)25CbIpRVwebz8=w6vNWPDoqMDuNAr zafKocSAlLsAof54WR2p^r<|f{+JWEz^&O{|=TFQpFV7!eTV0-AS~-1cc{LxjPy0d3 z%`auKSibJESZ=1`>{3-28_@nr=|TH~DhFZ7V-AG&tUJ&{e9dNEWOJv-eKWmP8?O3505!NJh++G*~>Hdx(g3( zescWaad+wAadZTD$dz}|ai>{kEgu%g{jlI>I$h2S3%>4}WVo5mmK!sD;{MXZVmg6E zak)cBoZQjq^|t3)O~{mdIL!Ouz|EB`IPi5>v*6}O#t#nF(!=4w1P^k8@?0fTfxsRhQl#PGKpD-H{V zx)xVqPMr&Dji@dITu<50!JW`IaoGyeO~ZY$VGM z#m#p!RFWgOHopfN|EERNfbf*PhYZ4&4uykzU#nKDS9rJ??%$L0z3*_|`w7`h$0B;A z9zXUj=P_toK7Yf z*67oO_R{-@KUsE~JbT+szLSB-nQ=`{2cj3zX+PFRL6Uw{6wc^-rrK;@=$wz#F*=1O z|Iqu@KjRb`K@LNmB9liAD}HJ?TR;t~ern+6_m3|%G)qqnw+uxM`xPNcKui&X+&hg# z3-XaYAO(u7JVA?AdPcikdT70XBiOupfKI}o3-ZmGGKGPSrez%8RuO{y#AXk1+%Gg^9NBRh22L%dIJQv-F^Zchh7ka7b;9~-72T*WoalD8 zPU}E4Q)KATt1#X`oJS0)Aw@;%dc_XWCCOQ(j4>IQd$MgXl`@O{u)>g!5&vv`v<(Gihr=RtmvaOyv)$fh;SQ_Vuui^Mz zjd0H(SwbY&Ou3S- z!bL3=!7(r=kcUw0(F5L7MTc$K2DXuiIYoNO8>=*pD8XrrjBYYEemEw)S8L!sr{*u! zB3TwbGG!;--)nR`7e)Q*Y z%%}8b`=2a3iyWL1wQ{`czMB)gIc#Bk%(p*W`W5;D7#e3dCkNSkIEf*w_IOQ&(gpjD zHtXDF+tl)-Ja+s%?2ig~*muh7?{!!SHz&FDsVI+I$gCCfm@-m$zRS<2xcO`rpW^Fo zDi}9Y=8ulI{H3wrPrqJz^q*!=g$$??n;EhcKP!xvF~t2`r^R(j;%a=kU4t&sZoV4T z=~mA!$c?MIr9}!#K3cxpj}~sGP&%(vjZPl)k;ZBL7(;cv#0xkLb{4n}^ z1u*)4dEJ$W+^nR};4Z+3Mlp6c`oBt#qYIfhGA5jXB!Q5DBvC-Y3>UAGMrCwjZrxp^ z-6fHB9QC<+Q4!XD(2unrD!|&`m)G6sac)-H*Wj(dnjFX2q3!0WvL~p0eJ0whDQCPX zIAkEs^0T>!dW(a;e1-mp{gC>@0!Z;=cRA`1H@{CS&nboe;JHrya=wQ4Bl6vDTsAio zT)i3^Uw_aU5;s$xjn?PiLU1X)z2V-{gZh2S9($p~DjM7JXpvpb>`rQ|$Que|>VNA; zz(0`9nUVQFDzCd9TyEB?nMbCM2Ou6PJs`fA=5nNSv3_QVMELYI7{u?c8JQmIHJWji zZp6+|AWA!o=qmkVWsYu4yh(JyWs0|M524EagdcXlS^&GBl-J$FO>S11dV{w^yuy7z zS!Hl1Y{M&n>~kmi=Di~5*1l=D^qZfOgK+i9UzW|8RqOaMmt?qE1wAzGN%8U9$l-}{ zX9bk})AIeU@%m?F^Lw&*`B&t1cS^ZgYb>{7N?}H$-PJQ=!O~wSJq2I(Q!vq#`p49a zi5FEG2?UOwqV7~bnM{v9_Lxnj>N@>&DfafbU#p?YSrNj1&X2I4FF@E=<#pGd!_5l# zgFApQjx-q`l)YSflzqd{DAQp+3Jt?SjY@$IkF{HcG%8?azvu_r|4;z5|6X2y*ilQl zSyfsb*f9g+!OiNW$Jy}Q%urm1WoJ`Ug$SdZq{qiXGJUMF=~7N`J1x4(@#-8#6(BbJ5hwi?#!l{0YP#MM*BUYws>U0a+zjS8PQ^B}Ch9JcyV zg@o_4I61w#`iax?C<}^evhz!G^M{P+97RIu&?j|88TP4s!GuMqS+Rbq?Q*_yG(IdM z#W}9Xg7K}(9yroW2$vxOo!O6Q8s^nRCGl?5Vg^b)LnJXvZt0bk`3mBxV#GanpO)f@{;S1gU7yFb{;$UOTTRA{c^?v2u7QUs(@>8D{OgD7JwUf zy2&jHbBOt(!2VvO8V+3zJ6HVGaz02Ju?yF~31O$h*?Iq|AN9Xkfck$Xue(MoZl;2* z0`+5S=l!35bMW3LX4F&7yDrA#S!1Q$rjyejB@K))caEQZ@G@zo2SEdb!} z`2qL`1pxegKLEK|o507G$^BsI0eEF}0DAEeGz8n)75gIt(s%@py{+u)dGC-G7aW30B0van~>lw_}2yNz7Ww{_>xC=y0!!?@z(Sy$r{wfA+o!*Vvz z6Jvnd9V%D$#9lbOA=7~Lo|x7x>QW1XswC=H^>goy0V7b*jl0_$VG>OCx%-X4E?=mX zHr_8>wN`kw~O!Gx_ldIkR=B1A?VIx3DhaVUc`F z2P?}`3|z|~$>ySP7@Sv`=ob{{BejT3^};YxvW;lZ5@R$oIWyaG)!&DRE{^SXoXqXcX zGtG?&EDVr|h6#CV^4N)nxj!1F&?SVPS^AWTF9#*h13LTV_n?YR|6EJ!in$eNp#O;3 zPwAObQRHG-qnwML+aD!k2jh2ecJ4RvLCRxvyfUwN^8zZ0i3n?8MXonnQasgfkRjne zcek)JC}IT%@lxMIoxtj@gH_ieIi}M=G=a`N$D#LRx8#;UCTOmMy|-DUbLfQhn|xluj&K> zInU}XxoVpz=kofh0H4ebg#=1kmjy5t@n{}kMr4^p1gukPl)B#}HuI55y=Yxq^xD>o zW=3;8$_UF>CtQ;)afxmu4NCfN#G7`gkTpc>q;l+}ax7zDQaSd6ljmv@Kb52_-2mA#QVgqg_M?DTe&XGNIYY;B?WcF^4wN6++U&x8 zB*Lja`~m)@O242~a-%cgQpAnVZqC0UDIeCLm>?XYGIIvC{^ObRIL)LxBM%Fxq2hYC z)0&xGAnQ2{6q`}4Ck*ME;e~Jp_v2`l!^qLv>a=I#Zf&NAu;dwWt=Xab_?8*6n#DVf zI*sEJ{pZz_2O6)PJkW1^&it$2hz8*`7{|t7PI-j>vDmR+B&D8*`PG3dQY_ zL`3EErq^q5kaB5u2JdKgsz78_zM?7-IXHN}*{<)LJUF-oCn{}3bcp=m7TDjqJ@M?BTAn?+ZZqi^o9aSb8z2wSUV4QO5nkVEC2|$ z`YZT!QE&b?@Jj~$Z~k*!J9*3IZ{Y_mx3%o=k`Qs1{byzKO-T6s1K(pGkk@e%g@qPw zz8{bMF&=B&Gq|nRK$+%l)ZY2D;8L4npl{jg)XtM5gKgRpz%W&$U^|C41 zObLVDG4gd3Nb@~L#pC1{F^zvlgDn^5`*$>?>kjJ8KgBTmjpm^G+#;5Y{kqcXXRKe~ ztfx_+QmpqKp%zK`OZ}jKsku3Gsrfp}@LEgF&1GNe0}qsHsUK7cSgvFal2vpU_>FPY zJF`s%`e&?`pxB9<-h5ME(q^A zFQXA0PGsNgb!OY`4#nZ45QjMTikjmJ<;)qD-UJ`)RCWM&hCDdh=adk5?=2PZ&VC%l z#%5-M7orx6&VCQIMtt-ncq9D|LT&CS~GG=uPl{2B1Ez~QbZ_fda)PF_1L>t4EO`^ID z4L(MN8{;q*d@(ACblUnPmQ@@kb{h9pXq$^%nL~#>FF8lYU#I{Lmvla8&Jf9IO*iz% z3LU7aW~hL8p7ayXM+=CDAA6faJlw4GL&36XL?xx1L*=5Jw}Nt%Y^GV`iuRI08mlbM z{2|oq#pY_V*21e}%NFWpHnc$t^mNV( zsiERe*(%05GVt#sL(d$96snZgU;ufg*=sOO7bz#t`jNg+fb>~;-BnJwnJS~xE~D(q z$%o2?^he(yWW=#BrU2c3TNo}voMn7joWLBjzm?2e0((Mt$y<6;p^^L>F`qaZIczaH zps<)cQ(*=tJ&L=grh_T4`y=?tmxL&(o>`e+HWVON-!U|11BpW`NA?i$9!+Qvp!D%5 zIu+7?B2~;PXvTc7CknOE0o+kNICnXyH6mznkgOX~Z?50PcAU;#g{e??C{IzH|7<_R z(Zlw8r~O|+hqqdN;Skg$Q=A3}=D_>=AZ~Q}t$F|*GB_9Euv3V?dT_B3watM;`PAcq z{kFM&d8s1`BEr5!il?hRX6TI%5)j~awgzWR|B5`oDsQ85($5Rd6!3zV{Jemh={(VK z;05#L;st*OsrOu((u)?@L-A6rEFky>&fB;?)NT#WBzAGE>`B;zk)=yS3;vPpvCDqM7qm1Tp3Ya{9(&&fzqiS?pj;#cDB}fTcBlX6zXJk<;=0gh1J#h z;}GYy-lZc`2INT^xe_J;{-G+QY6*nIp6QT;sMN|i&DtpA^kn3!a zmZef+*QT~mi9X3{Ax@2rh0d7>A+5HdlUGQ;Yd_KpLUG85xKr&~*P9Wvz$pEnU>sA-zgV`|Bhd5<}I#4)&*Nq0~14UOWVMaAvlMlQF>?%Tj(^MzH(~$ zx`x=;ckJF+>goTiU>Uf>yhz>HjlBb+g|K+$uKL3+P9VE>uDm+mYSk3 zbv{A%CRyK+?puGz)yZVz+ott_31UFzrWwkY?^;uV>8nr#!}C#m8YZC#g<)$^k@4||{e0_Z3i#HK`1uw$(|Jzg zz_)&=Tzu=Xo4v+IS00g8m9^MeOiVb3&vl?;@jgbVSUvEbAnZl~@ldb8(++ACAi z-?s+G{o1)`O}-?mTXxoLO>_j83Jt*gZkO9)4pZ~$RrvI(Tbz-fRqWQ_wxrDysMJ&r z<=_O!VhBzBuF<0vqLQT>{P|RwV$jdN0ks@w9)0wV;8Ah9UN?f}D{fwhWcZJW7In!f zI2Xo{Vx4wy`Z8il+tQ=39rlnLg`j2XY8Bzi6d^-lC?ORoYSs{sf;5}$PPZ3y)&*f1 zPfG_yvuBnWr(hd+&*eyHd#Bz&F9_=)9o;F$_aa!-BQfZbqE|3-ye&M`6^l=zQ4dFdbbQJALJ8-JV zy~HK=?P5+aXnK`U6{Yx>`_yI}_tCl7)>8Lih8xXx*h;=$#DCLJXYQ~HZMsXbioFT= zx*(6y@!G9+aSJ~ucIP3Gmt_&)( z*cRF8IHO?_j*|rcnQY#(VNBwaM%XEuQ1@&X{noIw4J?!P3?z_Qgy>B%OOxz!RA1+@ zp{)xPfnjWXha+ryXcD%e!k;;?GIMcVDLQfJ$0%#k!>(4ym97Y1H!x_~X!e`(+{Ga} z@_O|>Tv%+1C%#_T8WZKk3W>H{s+K3yWYQ376JIZ7SXncq#EkOmWUCPqhoBDpH(}+< z{)t176Zw2B^*T5NhnAPbl35!{c?CUOS(-3m8V)9oBCytpqev#lZAk+~;(!}L{pV*D zOEz>B-osxzN0BU?C@Or>IkaDehlcAVKB5lmINgsMP_E!5s0rIVHW%y74Y9){8+<|O zzr|N?(zXA7mK@7m0qe`xtoGlK-=WI^icv;`FYSLhz|lswG5FM|6#_?k!8Ixx_)h== z6b<|u{m3dB_>Xass>qr5qI%q~d7Ax21Ivr9U%@w^TD)|VY^Mr@zn=5ltq%0KyTf?wH}IIR0-+fEn_yB? zta3*V@HYPd-!BJ(>Vp=>x%ug=@$vP?{EvN9j}M&vcWI!d=e>VQqbwtJZ2mSzMl}jQ zto+79@Ky?aRO@dPvlG~OJIwJ~q^{6;<=uRUe83M8Zhk5YB7FT32O`}3k@17bzb!pP zzD_{|{?f?Rp5#(fbcnP1Eepoyto4+m#7JU6-2Nv8+>3L%Y zi#I)3cCqqw0W7Ms2>tDvYBe=dLi*adHt;pB)It=hJ;n7 zZamb|he{8Pzi@*TId*an3ho+8EW3e+P<>U=Pzp>*>pI*|wthqv9hK&cAahF-Vsn|1 ztR@ud`w+L@-xeWii+sN#0?PKtgZmLb+&@$RcYX}@N3Bg8H&Z1x;(%ia_s7c)_p?8W zTz92qD~M+TZn7 z)qb~C3EP*sEN)ety@oQoSJC6SPW|#8ce(BVOqvcwKECfXuUF%~sKVpS7GjB_3|f6o zM!1MMM{*x|WDrok+BuKvSnh^tPH+NQV9Tf{oJY?e@ze8(0($1hTzcl_F{bC)($n+D zZg%K-WPq@(MdCz+XDhHIrJV?0hw`|=Yyf+(+OFE)pLN zYR-CA@`Pkq@JhWbGdhJcRG;V-P-aIvLN_YClPau-eX3&DV{Z}C#xUzn5|hYEGlq*2 z{nQIF5Z!r&w#88hyI5R3yM{m3mQJlMoLHEfU0pb}w6=QcpFBXhy1R9K)Dz2N7fO9fn% zA9J}VH&a!1trbypAXQ}_k4?a-vUAbFzf~%c2)oPxKt}3H)?o$I)zuehr$U%dJo$A~ zBh_b*Mybp$_5jRQSH#f{q)15RK_5&YiAD*A0{ox$5RbxVaIZ9o%42eS zlGbVY71sAV5Ar+^qjL3EalnFNP21aWkjJZ?`Ae86q~Feoi6}DbZTgwi7Ydlvc|Vil zW~v~q6<9JojgPp#TzV$;Zw{KJUb2`}78(lKReu|&eMQ9_i)&Czlw~t6%|Hmh^iOdz ze%{B;*7zEj8#6WeXkZby%d@NjnT*5FGf=*TKD55;58&s$AHdf}`v7Y|<_BCi&UZB7 zVw&^m1%=Vz1RaGT3t*7Ei0Oozh2sJ04AiJ<1NhBcZgrQ7f$M>8SN8*^uQYa`Ab4NxO zaJ%2I=88PeR#t6CsanBOM{rae$fc<@gA)y<*jsd)McicOr5(KNQ7}<#Ggwd(f0t*7 z)uZ*0@;p%UOgnKmQzhZ1u1zmkuO{m7PQqA3+M#cnNObfhze*m#Y z`^7y21&nZMQznr zToP%aj9o8E+3}UML6?Z4XvR6VBBlT3aRqs8Xg&_>Znp_PCGV?tX_0Yz$>K&Z&A7LY zWTQzvj7b~KSd-O5j@9P<2De!1v<1T;fBI1NnjiNwjGroC7(d}>7~HG^QOD4z`~#(D z7Q)>ds{M_h!||tr4FKQ*EoB#fE1^6 z#}Kn?&HXmI2pxc_Fd;p-9QC0=yHfx}N$g~z$)rcGniFbqv1Ru+{3Q2>1tj;I;`*0w zI)L@Zzmsm*g}i>UI=-~`>!qi?%^`JxG$e*5Ju03kF*}vGJ&JUKt2gioITJZ*!?4D^ zY1p+uQz$gg_EoQJcHN-zE6x2nc#I?~X(uDU7E47$sjw}#;3e`qECqnNG2b$bGrAWdiLF#pIB_3x9RJ?%PTTas*Zm8_93=Kt8wbN=rFp7X!T z>n^L|<}uEp_@7J9YL;%v51zkxRU|GJglGJ}UV*y!-kS;}>VNi2RBl!^sK(H| z{pY2ZsNZ+%C=%5`!Y*a%MPO-71oDrESe@#(o5-i*AscdBDyDu{yjO{z2-s!|A%(^i zYGuMmlQfbeQ@X%3Y%6=2YA$m?Qz{FoHi)8V8S zE);J!t9RsNUdbA&Vpt+ULMiN7{kw_EdO+sKzMfPR;b|m=*j%io&fGBm_r>?ID&kb3 zTzt4dMOl15-{9=}b^9258~?KOO3Ej4t^3(n*rkrr?xc;4SF!Wg6uY-lz${9jUtw^O zWQT5)9+1`rlD8_W-*x13Qj$$xr1#BlZTBt&m2y;#X0vZF;rb|{0$RQnhwCf)K- z$;4w;8d z5=MA6_@RmZSSxIwAByOo$RPS>i-`Wo45C-E0N>OZviZug*h~N_2dp(?wizB)l5=Ccnk!GRXdP5!ugXkiBXjW0(E?r6>Ep z$aUH0p`p-azlBVd=5}j{6l5uz=!~fXpBEfMM^I&IYlu+zT<_Y^p=_o|~wF0j_M zi>3b&!+cbHHyvB1JKqO?JQshE1Ia9xP?R*x{v~lx6n7Pb-3U2j5!_Qf{Q)vVM^p;q z+jK5)6=uA}uvcp&$-hUY-27Q%>PGA8XjWG)=X*Gb5?sMqs1!XgCyWRux}7aW14VnC z1o(EX|aj|NUYv|C8Z1d6+2Vf->S}EoNL9zK&cDHDQJ-G}&T? zFm-YWRHxk%MGaA|;D}~5lD}leI?D<>rC_c@*P@{@lgv@9lM|bA(vn10z2skFCKlSg zXamWV2$L@2Vf73icABO~UR{HIr;@x1S`LWQcE1J?vy)>Q7M%Dg+~VlD;74Qm%rOnpT{uHMs`+ zqp{^qj|eUmQiH0Yk5*U=v9T5Fc~(YQgN(|5$k^|f{g9xuU-`&^1EO4fFvL*{f)~6d z*@G$oJH%tAfmT{r)x+7xGI)-1V7`=jGKv3GBRSfFhjP!nDd<7PDCn~zRD&Atu?V%Q zqxrDb5Y%Rh2=Kcz+ZD5}Cxxdpd!5-f4A4Srh^vS!Xj5}sp+t0JLuSw{D2w9khgb~d za3ac08@aik>~$IDxC|R0lS30yp)E$Bjj^2GIGlYCjv*ZI3GIq1#fnK6rq`R1A=f1w zCuQ+vd{uQ)7N4LMQ28Y`R^8w+yf)htkqV|oRhaRuGzX(VK6Fr2Au$C3R63EN1FlX_ z=9P1XKGwr6-jdBA%3gGd&H~u4!szyAXYmGql@*>2SeA9AYsp2%y7VonpiNSaEnja9GTsD3b8$5+xtivdQtrf-CMA%s zYYF6GAv!65TSNv{)w686)7?m+m{joP=Y7Pu#@7<3mwH%|mEUG!4alUt+QwEMkIg=Ol1Eq2+CM+S|j=GIWS(Z!Gq=VPw^=#F{BH6mA!+l5K0NwpF> z_YtGZu83GfPBMgn5Hm7GNype;6P4`sI?$B_-TvtOz7Pw&2QsNwe!W4!ieuj}Rq=)g z25^04TQq4KIRysv3!(1jY54L#qnx2M0+ zOCPYF{MVU7HG7Za@>G)KoFutRu6hwYj8#V5q%4lyPJ3f#1oiE0tr{GK^Ma^Wk}i=q zDT{*;VJa1hifDu*7p^$~DB;YyW^s&n#6x!-0iSQIlDd}yJ2w$p1 z_#$R^TpYqQNq|P2YB{PwK!cZSf^$$O3D83-z>;eV+q{(woyWN+DX{t&1If7!Ma&mB z01BbVIV($3v=uxzNr4^L6xciNKo;!Q=2wtl+K=$dSEumHm7PkXGkD7?_PmQ>JKTu6 zl!LI-_-x}sqfQqu(SKe&d7$yy$pihy=fq!&Kl29hXTS0J%~OpLqi#HNK0Md#&cvO5w-y1Dy>6#9GrKSo9t~mS*^FvE`t|T;cp;p@{ZRc6Bg=fN z)1HaDwHeBKm=V{S9lDQinW5czJmZYFN;mE_>T)#E;KKD9!N%bDo_tEbasJag70l}3 zU>o(kb_VZgcB+^K_)KV`s$v#)1_#eK+x4B32M4!w!}dnBLl4{n%R{#(em&S~wxgYs z2L`v-8euoA;RHVX)gRo98oYGF<{B37*4>ZGIXT-Fpx8 zH$zV|b#ZGwf*GZ?)3~QRsQ47d+PEQ5(Hz{j9oEi+P6H1hKDtA&0ckJdgNd{^&*GOf zb#Hz>nor)c`APhsyR0RCpM-<6#EU=u2HAcSsvFgEo_n(c4ek!m*xbNlje7>S)f&im z>_+XKPYa04LEgSPyK-{v#g$V_J92n;`G@y@ISiD6wh;KBY<^qT==ia~d5oLs;C4AW zOkSIYSsIS^DvjoPVt4aAM$m6G2me|n`;uw1Sqs*i?XVTF?NzTHjIUEuQ`$63hMm`} zzo=yjB}AT*k?@DZbG51I^c^HHL>A&yfIH(Tj=_}*axH3WOKS1^1@NC^eKX$zs!TVadD4#V>{KRh4y!;_ou$$}?ezukc+ zH{V@ecz&TY@O&FBPGBsQyNI`w&=i}B{3U$Ezl7ZU?yM!`>vuUz$j$F6?-K5mW(n`6 zB@`ZAT;oRQ79WG&+d<#l%%xmj^k>tw2O3H~1|%W|H*k4jDF zl%$cXnCCV#xH9yEGdQw1uJ+qoVK;7sEh^es(fWg|a8Oo7e~2Cv}T4P;h~Q7M1#v?)NS5vC|Eh5%!UemBl#Kujo)%BW5%)a`p{ z>-v1uqgSbXfg+F}@q_$B1t34_2RS!Cz`Ux&ApdGvKtA}R?4YsKS{fQHEpiZDLbXt6 zn&OXVOb8{>0~$NkIJ^*5%ts7#iym}V3ouKMG4~`m(sdKy(y~V-zpWlAcal?=GC=xn z1EfX{K@mYPT1vq`mk5g`bxcoT%T4`|lt%W$XmeYl*#>KGMqCAHr&ng2=_Y#^` zfNR3%`~}3}EJYV3R`Z4X$NYGjFTm4temrq=rTlpM-Ll~6?A!9sk?^jm67@KEZAI84 z=P%WwZ896Wa3fwNcA)uB;$j@Gc`595ke!)fVVtb)g1PUFgVES z3q?Hhc|XQC3NZeHALHCiN2`*>s>HJWUzP^r|Kea?!Ci}Q*$X>7&^eW!a1FQ{7|hfF zj3)p)Z2+SlRi+__TV9BM5zMTL~|J1dp`pT*0lM74Fr)^%8MYae#8jQy|rNLx( zK~ROaCaS3Wm+?n+_WAjx)pXLBeqtMbhqyA=K`^iIA;c(1+umqJy-u4Ffh#nCRoL`S z<37|DBh9+qn?hV<>T7hDTXMWqi%kDIm2>~OK1|E-Yo>ZM^mXc7O;H$iQiUM6i5jB84t%_C{eH-kT-;gk0r$oLh#1wspF@D-sSBmsCS~6%AW>M!vno}Mcld`^vsO>Hvg+v(Fu9n z`J;t4FPe-O8gqkHmBep7A)EB^qwd!8{*)dz+wC>i;rWvwE)rEuzo`1A0#UW;7ggLm zQ+`qP?@J@9zHN9>3MxxQ69sXLi!@LZ}H0%ZXN@Z(HkBvi{t*6vQ0+5Xqar8*gS%=g?EDBWlE=2>hIva=#=yifxBdVPbLSerQ&PDVDqZigv z04Ge&{J=|I!Ra7$LP!fWf04gMS`0lwXNU;q<3jOKx+598Vez5^5J%XXf6))l?=1l5 zxBJ1#%^xnm!t>U$fb;BIauuH8)_5q*hp-IP5R8#ZiBoAL9(mJ;#J^tH?lnUo3$O*l zl+dL^Ycvei&e7xAq$Dy_fLtJB7d=ekf^VzP-cx#vvRp{2V-vc5EhKfv5IV>d26Pw{ zciyL3C920%+}Vl{f+-wczFRRvl-sC2PY$I*UrN75)s-{H78h1m=Z|}K^wnmJnyq20 zA~vykN{X#*(6H^TtDb4$NTIijnX0&ZSZgmPE{ZJ|?w9HT^~@`jbjdCjp{BXgoplY} zm7xj9b@laT+yvole;X!}hM)C$JeIV(#~lcDBEN}yEK+y;pI|&}k6Njz){N3ion@!f z>ctn)`xzZN5jLfo2wMR^Am@*7ahbh1bjmW)Ey>p`b<~&VI~l&u&*c9>0h9lJKa=O? z$I8#--(MOg|GT-THs-JGP|Tfxuy0&hN`i#1=ds)-HoO3}V7D0?Ugof%*sJEaZ0q&$ zTfOPCd5&;%WN{q2NHaN4c2JQ{lA>DXJw4o|xJ)+lQ{-9Hi@|QPJ2P%VfA&{tKTg({ zW%5$Nh3ISc%2#AYe+>kxaIqg46n<)AMWk)z84E}=nXT>QFl=>>FBk)#V-B`5c{~14?|Ca(h|C}Gs z+`LeJf%{Zh@O<{b8xI`7hG({3Z<1RcJ9Ap>7=`k?a84qYo_6pZWD-#(NnDHCVYk_d zC0ETRgapD@EGoXZ-4CXGEtery1^4|HP^fDSFOCDS1pker-xue zhQQ5IT6!3`Asme-ymE213-3uMkPMQ3Ly`C7+=Gx+r=SwUSlz0_F%vX5+Bj;sp72pM zO%q{hhJvYCF~D;p3rmlgC__vtElvPBTgE~H&>dvV)`FE(eu={d3{QY+LMH)sC^a|) zg`gKTLK&y}VN0MrrUe?k#1*HOOmoG;(}iN;1clx0W*ht;y+UE}zMBf9!hiBh1#W(^ z{8HiN(ny8>=GMH(UjmDVu%%SUS@x@z36l*iL{TNJP5NSi{TrMcZ9}^pIY*0Of<)kE zP?1dV8OL#|a667-x+_0+i=wU7Mq>D-S6^<3;u~*o>#VktvI#M0K_~?8HIdhr$h{#p z!pTP{P*sQA2>T#B{C*WNcBG_Odtp~GDK^iOm46p{$>RH20dZ>REIwDDm@IyPZ*Y?8 zb^92Y=0crb$B88#s^kr609wfU7 z`Ip-pw8W%`9B;0#N2u$EDA*Gy0SQk=Zd~5tu?#)rbder1m!XHK_M_!Dk#tL=heWxt zc||L11jDd%7AgqER|{m=PW+&*01`Co!8*$R;9hy2%a*J?`v{~5z=SC3bpnvJ$H|DZ z@kU%vQ7^Kxpi?_g^e+tRRt+E8Us-QU#wr+C!maaIGX?+YZyYuR(5P&f7~%DPFTR?X zYsH@vZY;NOtBr2wVq8g=Z>ZK7SVMn6)<&29+x3Pq~Kxwo!5rb}dB zp_xrv~tj4mWE*?VA+>0pFjM&1}!6p?~R@Vtv3O zu$s2cF3q8oe~sLKDkvbAo6N{4VY-16X@Z)_2%tge&ghSn*1R`*HSDs(YSbl6nD%&r z#dh6VR%h$(@_m}zq=Kv+E$!(nBerW!lzF@}0gQZ_N%RhVfJ99A zQI*DihjZB=8~N#WeH-qG9-@ML{_fCvi9^GjuCrM0tvD^BvsT=Ts zkE3)N77^6HV8O6RKlttp{oo%K=?8x+LqAaMA1%Lr@FS(s4}KZBWcfK^#1jga8gx%q z#~^KWEZSfRJC3yNgbKGwIX=jCA4}0ZgaLJ z4FJB?;yOxARc6W3SfLt|Vq4Wv_rFjoScwNCHKe?SpaGG#c^#A@E(IW=!`>Q?mOZ-h zk*3L4{?4P?*cB1>M^9Q6`y9^5Xm>7BRz|4w1Q8z@LZ=Yru&C0O2?4$GK-@9pnFv7j zyX1paC=Wy(2PU~gfsy#p7Ppf!9$|J-*c3__b-+4+?)w@kVV1&Epr&Lfc2X}R^{7Z| zp&gO_KujLiKu;J?LyhNAOpq~eL=illx`&)8lGKKzzAXvlY=u=gE!8L-z0CUHbeF?? zvg>~nMjUlcy0Q<(L<3Y4Lp6}nn(xK`g$yO_e=AbbelbHyQ|)75#QB-hC~5yO*N8LJ zGK2VsSI`=;(BO2@4H8>h2>WVox4^a7pczYLjhIOcEa~=zq*k)v!OYCW?-@z^L~TRm zzrn1J6A8PpFR4*z>Qq?PSB|hEqAuhKK-C)wLG^ovy=C7j*#?Mc94eKbW>S4rDm-3& zb;eD3lR&G03Ai@7-)feRc!Yh*q(viRRn=$IID&n!#sT;|p(Limi0c{*5~~hQb%rFz zc?-cP;FSzYO|FBRoxC1rI@hAqtIIH=LJj5 ztV5vAfB+C}GnCzl1h+6fQFUUPqTI|0adx;@V;8?f0m)&+|36fXPrQ} z_^&eb8`b_y`HfJ&QX2i{`P=f0P$Mk%o@+Te3`ffWfJ)08!4Sq+&+#WyOf(%56jdQd zucFFL$old%)pUG#)#?tJ9_n>(IS4gaCGQmr&C?szGmi3n*sH@hpz_``(Q^=74-=jbnl*8XzTZLVJq&UL61oZe$n#rW-R6vNo1Z>Dpmo>s_w z*0?!OKXMB#QQ1R1jX1=r2lw+<#W&~e%Zp*J)~JLOE}iH>%{tg-HTrBHl_M&lUK+7- ziP=dlo%;Fk!+}D4yM#h1V;=57SGvK%0;*r-+grW?>4_T zd2DaK_h|O_x^}Slk;8JKeIaZ$#R0Ah^_S__km@ED*}mS%I-r|L`+*|e@Lha^n@OwM zKUjX<@V82%8y?M#IL}<#EVrZd$iTEkd`d;hT!dXzWVWfGa*9i8wK^AZyk+I|WfcEM z7%2T62RMzwC`)H{(n~rdJ1Ye>c+*#8LZxH|mwMP6(S!b^6}CK6c!aliv01{?g7?_A zkR%u@+S=}2Mp#Z)co}eauyC9LuY{40KJdlxLMS}0?MHfOMX?^7U)*Zen!P~KjUbkw z)jS^saxM~Xl=J=b^jhx(OyQeH_51R3$_!2?K&}WXo(@i(+r%lH&r+7L0y3K?jk6*X&BwT{ zy4^=@9{RYo^U>usq6;EN(Ew`iN3zK^fu+BFFC5SNYI2SXlIyM_1Wu7cznIFku znX3Kq@@r53XK7gGBfDjpa*0Q0o21>q%`Js11sxFR(%gg2TP1ZPL{fPTp$)i66A3-B zcWl%D{t5T$g(BGd`m-&V0Ju=3*t>S2e|8WWPUlGpTp(>+`%sD#6Db&}$*lmJSs&c|bO5|b&M{E>v#H9?5sM^b) z!TjZW58QNs{%!u}(y+$Q=hhS+apgy6lp50#m;s&FAn}ycx>xsT*67m#6|pQTrYnO1 zy^5fFuha%rD`|wJ4C=pyLTH+j^gslU?tzwQtLwa3kk~Ogt3B$PQ3VNEY7I$!U`4%B zb>+F&8y#RokGP{Tl_}r685+_^x-*_r6Ggx3IkM&^E%7E2ha}F8p)V7DbyOhRCtL)N zu62u}@Z|X@L_$Pa3syjAk$~CE5HR0fBw)^G2pH8q2FKyu`AA6)zg?4u12ua|$`yyUD(zbRc>`xhT3lJ#duf*V zcPYQ(n2t(HqUibJ(CLuYTlRV?~Cv=Wq9H$q~}H1cwx+k z346QS*^auc1m~JLi*0NS!*8A5jqa4{A@5MOWymF+70Tz=)BUtjVB5O?IqOhe=fOF7 z)^6VO^bSIcak{S4iNq2D%wxJu7;N+>Boo3RC@Q?1tozt`=ZE_1NuiH?VTgQ7{{tiB zTV`~C*JtNjs#F1$sk6%zOVy2BO zwU2?t?B3Fd@88L_m=!MZND^EyGQq?sN9-aLJ~~gvp=M3*#4R!;7#_1_xp8ln5F<;S z$zK&S>c^&wV=uNXH?HVP=6xc)=xq31NiUQJVFt_x!@!Y-=%0(ICV)#>)F1`8h-%3Y zEX`Orh%>z$?=-y}3pj+YOGnrjxHWz-gGv8X5tIJ=8BAKWKUjXZMx``N`t7+)dW9YR z$(oMDm7RSRp-b$m>^+s}vcJ&~{$Jvq^K`EEkj&Z}7eNeJX2RyhgjjDN2Qa{v}N~`w?7aEm+Q&M?t{kS z^0%n*nJYV$#<{_j^=2!2YG!5zr8&Cs%=z$KvpW-a`rR7kNcB+ZXl8a{COjHq`$XXc z^y}eG1Ov_BepsUq*E(BUo%T%Jt%;M0swogvaLw>98Zw8AZFgZ&=^?QZ$i}TAQzei=7s2Z6%E`4CS57Sf zn^RL$8bq`+e9iicTBcAJ{HeewRh+&lGt zxZ5y7g*tz!7HyMx%9uGL7z8tXriT*I)qZ;`?8c3-bprLI zD|!W{#qw2GKXH0~ZF&C0{PO(L+`O^5@9FVNlg>hWG1@}uA^lx+QXQ=l{iB~*LDJBmzD(x7UA7fnhrx+d) z^AY2NMRz*q+~&0OIdt8eAju^KOEP;jDll>9!+s=UD8+X|UIG#c?d)z~!o;ILXV{@A z9YIL#B`HM{eCC$15yKSB*_k_Pql=A5E)8w;i9gEjJ9P0Naq z?{YUd%G|KZEN!?nf~q0)0{^q-{F+@D?M6>ftXEGR zKNa*YZ%0XeF3dong>=iP!#Oh}zs>*ZRdhlH?)amH)Dca_3yrx+Fk?Q!TN8zHm3yIdLuhyX0Hy&m68}rWBcJM;=O@Urkv#ihy;D{18 zY-&T-OMWL$zcAuJh||NyZ3Qk8lk&~a^0T_s9YZiW?IH2;c)^iHBKnl&Z}u=N%uudH zZsp9e#RW3d@1bX(rr6et^h{?sMbvy+R3_9e#RVk~@-3cuM;5W`t_*gQZ9_9|LWefH z22KbjS)XU(Reb{aW>(TXlGx{yG%Vhikkga*qG$obVPAGiBBu5&u zdU+ZlYB=M#afhH5^@JyzccsMyRe5f4r3a4V0jdU=%^{4vwk>=z)LH$srO%GIMsZTsM~z1psfnAf%6L>Y+t$}lX&O^3$d0&lzEbxpKgcY7 zldxs`1t#2w@B4_a!r-1rGi)vAS@Nj4+5>@xUNx6W_U*)sRbZ6(JwuQ=4AW%W#!g4` z%GE9m03CJMH-2lwA1xLVJVg(~EGO37Xv5)KPiRYh`hl!}%$C$Z*mGKS zS+WL^f}zqoIE+w^UBnSGPW8hUD|sx}s%P=J!i_HtQ%$F_+ENQSfr?=h%~!!3Zq>c_ zUZlj%@HLQcG|hK8+HQq4D&{23%bGrdilmHZgby_#dWG9@(l~}+diCW7 z#oj<3Eau_PYAZ5Ke0rKY3J!ctn7GAGgcCd{1ojCT5b|e`oxEY`iT(n;;9GLo#$#CI$=D1P({d4G zP8PK+Ff+evp7PmJawK&}9snirTyqfS_|QkMUa^l56*79Sp{dj$xtARhoG5S2#a>AiYncrD!_Q&uK}ji6g>sBs9B1dwY0`g ziSN&f3ALBS;NJSBSRY^qt7%Joxg(<}Rz%v$1EFbnw39_u?~RS9E02p7F~Pbsx+NuZ z_eLti^E0ec*x8DZ4UAA@99ZC40QsTWiQu_f;pie+Oq<6(DfB>VSu;HL3MZnYoR}(` z{IlFpb0Xkc6pbgk?9rk&P`tt*QU!LB0bh=l6Hl3c7ln2rZ?8e}}Mi98T{`_u-~VjO!Z z57fL9V^F_eg%HljJY_dScF3IwMgRjp4V?{TK!{6iNhw<>g6Za6$zK+Qt9U*w#0GPaC2+0B6&g z8X3n#C*c@-m#o1N)N--dPUMloV=qWog(4iwsE&5$BC?l&1m^t0ts6o>i0mXvd@>=R zS00EvhL|Yevv|ljLJTsQZYL!s!t8da0-I>LPEvXlIhHH9NhTAu z8_8+ZWF4ReiRw08C>UxK`d#WH^%Zrryt8oO17jf7s9xAPagZMtTyU#oy>bvwX4LiL}Ov5PIYWq(kXYTr zRW>o0BxVgI22Cu#JUMSY?Dh@?lk?USTR>V9G|!jMJ8!+nDn2=&n+TK@LX!*=>;CKV zfbJsPteCYl)v0h&jEtyuPEK>C6Z|yv2#(TjmJ-T_(<61q^|p7!1(l7iBIZjULI@9u zLRy+3COV{^%3i(_>CU>j=fqT-PZK9bXX&cDT%yewSt?mk4O!b$3paQjymsZrI+4cIbgyU`*)t#IFZi&33eN^1$HsS|jWt?ltP-um0d>l!T@mHebYYo;FxRi${jhnyZocOLk{Dh^P0EH z=C@^yjvou0$GG{B9vx=z6Ew_{vA&4Lbp0W{`7*}QZ!`!0`gL@gtDvleq~1!>o)pg6 zcKvzOh{mDjf|_pi^-;#ybcgX8+b@0l~?`OOY-iYIE=?qw+{4(OUTQ1z5 zed4AA2c9EMNbJqcLv-C$Rn8{gak?pajsz#vYG!+7>Z<8_7Q4|NSr4xA9A5E8v3C`@ zSJ?Y1)nB|q=knk^@>cjJ#GT}%@M^O;hkHQHgmg)0;BJ~NfZsTls)!EW>!*XDfDZ2S z(*ZXtsZx46_{MV4!Qh`|6M?&|`$-6Xlun^Gx+;xM@ihwA>htSp<28)cUv0wn3FLwm@2^DI zEw|58*NbLuJaxRHubOBFetjZOZys(=OCnG1fJ9*nr{nH;3sI2_y6+OREO2xze*aC{ z)A3^@4cOdw<(hdqobhTOhZs#XxUl~|4*v)?2FFMBZg@MoMt+T-$Kt`S@eA}L%dhcO zTqGm6c`uCHzvyZ9`!%jVDt-=Qh57deK*f+=j331Z3v<&~@XH`Y-J3s)X814uJN%%f zwU+q35)RH1FaB3I$#!yJ{8G+ya9~)_;O+p8&0oP|J_m*vY(XcD?y4 zdC0$#-27nHO7iu0J1fb}50rN$?=8(r28yVSZL-KuXKRJKrngcO0r@P&7Uxz!VIKA` z=aaITf=axES(VojRATKrxcP2gPRp5Iu8qVaWm(pj_E9r5%?~+^LP&1 zJfLSA*1X?-wz=OLwz=%`toW-u>Sj54B$!p*7$p^1VS>U-HG-H)P^Ur$aN?QnF>~6$4-ztQdj5Tz!JAz^Nuc8kSF--rQ$LC zls109*aaB+!^+adI5gAq_Rw!ohsmda9zQ7z5_M@vFY3Ya6S>kTRfnE@mZfJgNJT*v|7F+BiT z^WF9X@D0`g@Ry4LP|&k)^f7tJY@E^}wka%XpQ@trRfx)k?o!K-l1aOHusorM<(qO~ z`9?h~S#!mHSe~*5mY*@hQcuvLxC{v#`jd&|ICEJjic`}vg(8WSj7KY2D0_4M7CrV( zg(BD!)a(;@TET@XV7@raf}!@Ysyw`z$U{u~d^+fm zBQ=Ywimo1(UJfjm^sr>j-05sFEYDd3%g<)O(nQn3;N%b3q+n#VJ~PE4U6uI@abXs` zlr0%o^bkCq1HpzKf~1R69Bc3RAnHwC8ANeZz;I0$K&}N zJU*|-BWoT3TjHm!!DGgrdB#A}qM)pi`!^>EXQq$L%;w2LYDknuEBGf{8oo=9zAw)~ z-@EnbW6dLANc<^l(3i1mmeD8Z*(db0;%hL87z0uJP*o-pz3{lV3I&h)(+l6L$K?BR zF!_~wOtR(?&QVX4IjH=Q9+j+l!hV;_|Fi~`zn3L0B|s&t#e+doE9Z&{&s>0NpRLMJ zM+?=*{I=U%gy4%j-}Hy|VEsrASie;dR@Pj#AFNmIu_e(WW2ci^8YQw023IuaY$|A? z4`2qVey4xCz{&oKXHA&#X(3%v05XL!d z^d?_mh=@!`*y(Q6=x^WtF+F-enS4`esM9AF zpD3oGlHd!zKJd{OR8T*qN93n-5cz$2M6%`)2)@{74I+Q1I3fj22ZEzkzzwLLD&W*U zTGdW(m306P)4~h?59)#XBRN3*Aw5u8^Q8R?|Ld&*>dzJjszlX+pt|YBUBPEYeEA+7 zDhE(Ze@u_hpUlDMkL&TtnqO-_KIg5$=U)`ZrOP63$*B=zy~80FTfK;jFP{-C6P+mtw?eb(@h7$zD$6x4clK^hI$pe|3w3 z-1YC`k(`^&UK$O}@}z7|DF^mhBkCTjRsGL&)uJ62dPbu>(lhra6A1 zuS{}&vA;>dC0tHyD3Al+yDNSxJg26e3C>gRPFFc#wz;7a&fYsYYrNP4MSp`tb3f z2_|vL0-ETbOgRQ|7{UQZgJ&H}56DV9KEl$nrS5D-vQmqa(;(f8)pnn%Dpd)68ry(e3}NHSKEw3+$_4G!^JEZ|rC8$I*?TMqMoQP2FWc?3*1 z@3n^c5BF2uL?&z&>&u*)QP_mn#$Xjq7D2&N>zUAPvsl1UFyIfVT~gLIp_9QYc?_+# zNs=y_Y9hQtmgX_8l2h}XA(b@IP6OR#keB))1+LIx@CK>+3G4Kf3nfh&-8Ma}R#`ZR zUetG)6jG2^k&fau1q9^$jvZPFIe#5%H`Ps>zl?QI3a5$oy_VY#KQMU{Y#rdIQD6NA zYoz7VnW|DYp|?w5qA4R~ZM-xD<~SEjbVbof9Ns}Ixd@NvNem`s6XHd%G?6evksNp9 zwn_mMm0}PwA-ylt05X5QL44elCq70Uq=t^j+a)?Es7b`3 zWc;`^WJiLbjrs!PeioFub&7*pIr4F@?RC&LKUaUc;kAjR%J$pL-(ukX$vob_)xi6_ zeFS_rpR$JcuPwU2(o%Tyj8C{MjAya4j*imq z8hATA;z93%uvl^Zwlm2+=~1H-oOO#vPO)&X<#uzX`CZMuu~E7u{`WbJObzT z%8giLO8Q~K|9z2(jY^)_pqcP_g5$73a6FwSIG!>H4&MG6`vu4ESR*)=1{NHg$~mHg zb}J%0I$Ke$VFU!orKfo0N)TF$CQpLA$sj?zJPGnvlR2+sgL0X#a>JueF9@df8r{6@%dTnmobL zFbEFbZo88hygXmBMsR$7XI7McHhptM#$v+{Nq(^t`VfgvV)Q$1&BNog=&l+H1p3 z()HoIrHkJ&~3N{2oiH&7Ay2P=e({KvT?8;N%Df z3Wu?c1P+>JCLqFW=FEh=Cu=P%E@gx?W69>Q1#9NX(>yH_35! zD0V# zpNim|@~P>$DR;lS)(J3e0sov_bc*~wo__hT0g2h3J+ zw7_2Smnetbvq2;}#RFD4)3X+dN^LscT!0l7$tobutB@=-3}5&RSTdL6t|*mPgUbX*imosUhWU3>ZO_#|O-SmO7TUDE^t{ z1h#He{2|lk?}2FiD2hl_hm2>K_A_|4Y{3>1_V5t>0k#ny7IIFkATsBQk!x#%Z2CL2 zPd%UaMe$*T^1*LKKWU4qO5j}dG4j4C!G^7!v0`*c)qR!Df7CuU|8samkXo6rFfI54 z`s0k$m#9@YiV=gqoMs@Ctk&Y30lFfKhSJX6fFuzgqJ3yCK`;y@eePFLYN$5J{za7T zn5aB%j>p_LPmlp?XR6rrs}hzEU;D`1$)|3(!KZ&qoO6XZK^}SEL}0_#&e>pO$UE62 z4^cFtVo;g1B~ccI$PkaaZz8Zk50MCYM=InKK`D4X$XdM(g^~(VM){Zl2qZnBvI#n+ z);_fWl=j)V!i$%56{lfrqO2l$Mg@iIru1F)!=wz8{y&EiY=o;r7iBnJ?Q}Ezwq*sv z`rw$$08&N}8LEbHnK-jf`dt+-ZTy0BKCL@f)kB?^_H;kADkxkQjG zFFDU2mbx>p$$BMQvsejA>E7WKV#~ve;trxE4HijJwxT8`$l06b*~`hVQ7Wxy^UJ9g zP}c4`1QX$IDv*QSodr=Qa}~Pq_X;ZRse&EUUxw(uEaaxhsd>(jd_Y7*!v7L02etH( zmu_S2u09z`6ye!kp28(JrC%41i%QNn7i>JRDS36S4Q6vVmi_2*1;r}|krYFh85pJM zPZ$Bb-lpdnVlamT?ju;*HV;(nRS5(NN)Hy9gp-=PRnbc8WT~g^mD)s6mQ?FY17Zof zZ+{h_K15JiF9yIVHiriJG@!iUVo|f~8J17jCeD=gVrX$0rJBqp>&4`;L{`BE9to1Y z0Y=)DHAUjGBz#eaS-bRVifjgM=o`<48g_z}JhyZW@s>;LZ@vkSu3abzX#^7YkE=!+ zz`Lb2Yef8#@$7|BuRXCDcu|LQF2HEFA`oM4=uLqvc07*`%;I365@#5zs8(EKY3Et{ z4O}xxw<0*(l293;`q?Yac|fh;2Fa zlwK%vo|)B-Ykr9=*MX7|iA6If6)^6To4_m%c(XU>nd)UcvrIpe`5Jhc@~WM=Y^$!E zb_a&-GUZi1nuHkUNh)5o?vbQ^Vu!8lO%&iwr@USbNw2NdPe7h&*MmQPd>sD3?AM_8 zs$<=-0R}FA*@w~`P#$Cf&T8^Hm{qpXc(!q-QOADI;s2gLJk~gOc&yua2l?0hy*tQ1 zyN!3Q?#5JgmQVXt+m=sj9=b@aB?I$%@7em~-rT{g^p5tMFr4?yZsYVb8x^2NZzuZS zHhM4hgDRvQSLusLCTqCS+j-h=*EbID?Ct8f?GfM@Ve8d%X zIQq?RZZvjV37c^kcH<(3jo-Uz&8?k=gDM>jVjkQ;T!EZ;DNF;(iPc-+k4#aq>Ohaf zyH;<5UwA_T4mVQhC~%nHw}ZCtfZ7~;GVZ%pff0M$-Ei9^+}7CLyQOL4ux8e8h63SPCh$OQ)rYS{g4AB=tX8y9jvJeFwFPq-C=;_UxA2QLKUEizF zn>9Z;67-(8k=_q*de61zy;jg!SJytU9&pNHoiqNoihwIZqQ7Jl|5iQmS##Aye0KeA zh4`!)&zYq1$Yy4LvyH^RhZFx%H}q7(Q-#!_bXCcH9V)7^JYZxutV6fy$^I_djA!R* zm!Ui8^&1qjv*vgiT73xSR$hR1Mc{l1v!Qq=efU+%!&&niOk-l#)iJT=TVyDDNO2q2 zoi!VwI7h45kUA>3d!?r+gb<`O6EvCD8&<>hb`5gy+y3)j9lKVryvuZ_#7dInr|HmES$Fy3r|W|phx-~bdZXh2g8*OsI&>y zrm|(j-FiH*<|Pvz*!7AcQdl#dg)WvUYh}Y%+lYrZOL&0u%EfW!3>ya>M%B&ZV;2;4 ztu?aIQPHD=HQ!}I2fOYl=wQuvjs!YBXd^mqITHZYpo9ag6mYW7q2O%$DN^i~&{ACv zUPg;^E@CAYQO6~l;Cu80XU$mEU8}yZ>#C~2niE>9wc_AAY$SM95S$zObu~4XM1?^-+64;= z82(`IVl4{3-$oQHK5`U_Kz97PCqfWdfx~!K9f{UTnQLaN$7q?jyc8=@DJ*Z-(EE=C zplRlSWv?DA_vV0QAHA;XkgS=n3cc$9EI5i$K+8|sh?W&2TBK=ZaG~Tj-~}tR!`4^2 zOLm2;0J@F>=n&ZLu?BUvyt!Wwp$Bpx^cs5oW<}nxW^M}YT@MhV;}``H{fdnsdb1Hk ziHT(};zTz9DO2#gNWmBbj={ciKo6XE<^YG?cAWwo)_k+jNK^Keol8M|JzHmbkiPpG z<=LzmL8;Z5*!4T~w^hWDV72@mM2L;%mH%cVbZ_RCKA}6YvvQG?s)}?uY__w>`;eZz ztQqN~B`>?Kl9x5d7CoEE`?ofd_a+m0fs!h9ufu8)XuU0B3VB#hXx98D6QSAls=~*t z8NEEh6k;tO|ErCJp67(7CJ~J_1dfK4$qU;cw2@-T^J*7%$<-&^uz|OMtyD$EH7E=! zR50Y|DzDe$Vum(fYr@4N^!lv|E?BeJ8)?*CYhm)jS6k@CeP3Er!RSP`L6VeLMr!q&cV@1dR_J4vt|zO-qnC3X4A78O9yPk(z^;{NtjJJ&5HpF6B~;o>I$;5 zaDUgyGV! zuyrlD-9n0;{Hq;fV+OUwxLgz#8N*(FXrhO_BJsZq4{2+p*boLbY=+QM@BD)sUDswi z$qhF0$Y=3z*p%N0p~uuLWniFzqv&UzT%*<+r zgij_xd3dT36n_d&8#-Z_59tGc7`m`v57NtI+DO_!{NQX6#HlR%<*q zMRphBW&V@q+GNZ5jX0+9VH8W#7!0|vFk68L?XcVOIxH^g-~L;x08kXguC+ zxAD&$mApZ9Yt?genl>9Pdp^DXYDFby&9CJu`Kv%BXH|Z1ERS`;Zsa}mMmRg|AnNGW zTKV2);cC|VN4?Ntg2ph-WJxn(hnFAb2-0GSEUCPv!tc0@WROj zal%oG+@jhpUI=SNF{^b;kL_o3u-&HDRpT6M9_QE|C98GQMm)ba6VI~MI;J=V2J56R zv01}4P-Ec#>Fs7=i}eklu~1hT!`K9~Ddfn}P>;2T` z&B7BfN;2@PY{ca!`^Tjg1w1Cc7&ND$Brn@nX?9(CvJ!1|)&{TsR}0_Dc^Ll1Vo<;UNpRfx|<;klAzLgwV-gW1xR>2OMjNIshL zX$8$nt56?O?BX>D^f3wYG78Hc+tUqy7dhaNglf+Rw(XmZ%?|tmYNLXY!B&boskN0~ zR&3Nszwer{v8j9Rp_6!z0}dz9L`mJ3SScly%A8<1shfg(%#ECyyN1Tx(0O8U@fiH8 z2?^WFLC1kCEm7o*J$#D%=1L|;u=zD7)`#P|67icAbyzaYOUc2TD;yx;BR!F>H3O%g z>mI4uvTr7XS-e81|JpRzbT{GkA?NBP6Jo(?Mw2IFX^>u?nd-8Mbh`pim*usbjVA#> zTIbOpsHD!w{uXxp3*KP-oK2_l#2*zSTKmb2EEu=|P zMn8a(=TdA!WI8` z>nQ8}wYY92>sz4OrThR|1d-iqE|yr4UIaDN6N6-aH8ptUKJ1017Uf#tc&PMT8yv-c zeK9!gwfl=a_$ocdKazuScH4^;jI-wZM+)Pgun^-XU%qQ>jN3d2uKOUp6Cn;n)B)ak z1fmJz0ITd^WTsGI;O$%;V9f82R|YhlkPzKNZsbA_y4PNC(FlasNYL(Fod*0a;BiG& zah`;ZZRFofD@t?NbFR zt+HPYp%y4P5SXjX1j+b7@(0DbL2HPfuBMq>KzqjI35l_L*=Mz}K0ac5|Z|o6|PmC}<89XXJH60pr zBl#Q}(}p!Q6`1lT>8FUm24|PuU-iS;)>^cVvowAr;9)2HYnD7p+p{nc>^uDsa)rs- z#u1{fiMOYO50zh%@W}~s`gZ*i__3TNklm&(fvov9c?m=`Sgq#$sD(>l?{E3~htro{ zV-m}hL~WA!oQSexrU;_;Azw0+VkPDmE)J?bO*omWYp#KyioEBL$;WHabNAik{mE+}H9S_}{4V=W^t+`56vSoC!u^NB^5PLjH}IYPkq>C0LNR^O?j3y%6#r!@TTDj(m{)WJ5EKI@>AGOX{as zC5!Qq+1s!adpWBJ-YK^><<{ncZfz1<4rDOCDVh;#ld?CGE=XG><8zoQwWmsyDRrZz zUS&^IRjRvm@&qkti?}QnTt$9azl!`>&MNXN`c;HA-#1dL$bYkO6{&;GtoSODruycW z6Etg_B3jMptSIB%whu)B*{~%}EY}vg%9ducl%0s|$S=Iv42_s4Cm|eXQg(<;5?RiS zOAc6&aG=EENbHEUpwoeaF{yz`U_7zxSY$DSJrtb5*$xkT>lKVUqIFoSCr`mYP8~VA zIQQ7x%=F^i(Icl8j~<>qB3G|Sd=lM@Rf%lkYbSpg8t?jewz{U?qFYms1hBk=H9ybC z`rq|y>EGw9rGKGcOIh>8NUf!RXX9FW4vY}R*V35UlHWCB+tm(EQ9urVnG_riV*60g zz8fkbg3HHkuNU$6;GWRy6!xYt?*v%yg{LEQ&Vn<_X~D@+Vk8Mh+*`yop|aQ=Iu=6( zhM3cdyvt*>FXX=H9y8WD28Iue(y+$%bP5O=E(=H=tnO} z(Y+u%^{PvYp@``&;qa9gy#t16F8lfkiD98IJS0Xj8)v>}f1mH!&q8nTxAGpL0>$QO zK8Zch+zG4FneQ$+_Z&Xr291Z*f~Zw)}7%r?Q`At9j3@!7kF19K|Sj zq}Y*+2p$}UczvHbXO@{egiWo8u@Ntz>gQ%dcwie%a=*m$`;FmQ3Ze$B>qu2(vMbLH zC&&ELw_dL=r1es7+V;24wy-p&vE?2Uvw`5qgUgnzlLA03NEBz}{%9{CS4xqTF6AlGOyW4G;i&(*xO zC?hylS0Wi>6G^U_EL;~Mp@pwo7%Q26g)>PDRElmOeH(7O-t_9uO2@5v%TQ8sT|BeT z#X2?+5t+*tJ{qe+9_*}j0z66s-d69ztl%tobVTBviN&oS^O0xe*($HGv7W&hu)(sh z6Pk#vYRC)3GPsC{Jx6sn9-@V9C_-rIEoH5x$|>(!Ad!Tr#YBls-fYe!K?etzEtyVP zmSsRCqAbe*0rFEI97s$pFo#}LmD zPZxoll?qN%yswT=noQQhmAB3Da~wx#M{=VS%8dSOSd{speVMi2^l9?)gBkG4gKZ2Y z1vGQTOsMo=FKJ&TcjbRGx|N0Zpdvw8mZ2=mpzy`TU6z6BT9-^pe{%C!rgk?y+-g(9 zkjAQBrgr1zUJh?8Q@hb_7+YM*)NXk)xQ3nbc8x-6cTzSIFP5AFiw(TgE!tBP6(&YF z?Kk<^s^yW2#!D;Zlj#I#S|uC3G9%Dj?c~CHby!W6{OAd>X;&n8MyNzt>||t%oh1BL zD6e@$L7y=J#^|kTvc;{%rPd4qTT&2*5FY)+ zYl)jmiNlQwVoG2hFOl*}WuDjt$rI~Wiz~B;;=>Szl10=n0WNTY$sohMu(=2Qj<}3R zXHl}srp2`9CHg@bk8Um>S;nK6@#tkdI#ke;HVUZd8uyHS?maV~HPHSOpUC3T`&~WI z=FMW$C5O5`R|&lary6CTGS(o-$_SEhBX5kT(X|Xz#@OVo3{<}Q{Kf~qZVYM-zz@{H ze<{QUlhfFKe4PDJy%qjAzfoxfy`A02Z*DZ6U7dySejeJqvg|iKCU2b2@A>VSe*Gv`dQS{O679nXnJO^Dy`6YE z=0@+8eozJ8W=vE?-mX$y_I953+x3mZJA1o2ZhOT;p6TrhBexTgKX*3$wzqM3toMRi z!-dq4$m_try1mO`sl^VfZwG1~9$R(s7u0|5T^D*y5IK;@a-2VJ^=)ub?K{?8UD7n; zBd(}>kc0?QZ?|1Ac?QM{^6nyniQl_z&8?kwS0MY+fRG0_P?Ewjbp*a17p2uV!5=-9 z{a4=tt%rB5z7>99rv%AgN#UW8eEtV7qwPDOLO?y^zH1ezu*cm9w>99l#_rx#wFZ=w zhV;;lXEqS`Y>c?Jfl?E8TOLVmBxAfnKgL(lu|Q^H0+xGeGgd#)4)I2M9f}_$Zez_i zu^}eX8Uq}&Rr2o~j_5)Id$kFp=r;V5znhuJ7}Jw`q-J^MoU}i{jOB;uCgC6?m|-Fl zq=tw*=4nh2d=&pT!%OVr7Ui*KaJG7^yVUeU2*DEf6GuF@z$4!5bpp|&y4(p`b08Ak zrKSgko$)oGTML~!F$$=^=J{tJ=Z&6kG2Q97>!ia$UJ4zDkUaLn$#4?iE5B(v3R-^c zxJPoNYu-bPSR)umb&%%O@s}#y60Zi;k#Y(RKUwT9vC<(>I!$~MjuC&voTM*>&g4Of z6tqA6Kc9mt#=?ivHbm}3zPVZpy6xytaJDVaj@*sKPt*KI`2zlZK0PeRT@5-14nScn zoH+48^lkJ+4@jaO_;gBD>04;OBalK_A@3wdY4E+el#fm1=*x(V za@#ep86$gxN>hWR+OL;XPv%Ifd-alvHII*mq@6wUh`Q%L=slR?h>wM70A`z4^=PtKnInJFGaaX?0!YX;!}FDSkDoQ z^Lnw!n(;&%)G;kr;67-TSbSHeSX78wO2tNH6jiyckcv_&X|w6AxJ`66ctXE7X@VE4 zsGMtq9R@VRqsx^!-F#F@p7sRQ@H4f{7ojvgMyqbnDnD6U;bpxf-N=!oD|$)Fnr}`? z($%vdwTO|HZr5Lq$*~LFrLflVmvF=GoU2x==b18WFfE*+FTGRQf4`qLzW`pU-L|va z_9(Zp<_FkV8@t&UW1GS?u+HHCNvRe#w5#+Mg|l=F*C_8`%~%#uJ2ZCvF8ys-=#v^f zz$EiY6#G_L55+IY%ZVHF3qZts4S36obLABHsn4jb-u8x}Mfq1??kn`X0;w$${Uy^zi$N9QeIk4?os?%V@yw zH>`r+4;F!+L|Dg65nxM_fI=~TK*$d~_=#lev zImqehk;9s`7ESvW&M#Vp9Ips+VzO$?{0Whji4+es*l)+Lw@QQkluh(F`3K+&!)=kQ zp}^jVyQWeVehMnQvHJrTk0LZ*5DKw8l(a6b6CPWoEcO2t)~&hyf5OnkcK<`zH2(|d z+xa~YZggFc1NQmEKWdd6_{Gdq{v^xJkj$x)*`DybaCvjd6FY#n!6}~bqsPT#x5ANb z`dVJGR!oHRus|`&Q2b#d0|T4JgC0-VghTzp+_3=}ynE6vbMcv4S5J;Euk`={v&FnF z9xpzKZ)cZ0Ql(B7^W};|3Cz-{=|76Co8^krOl=fm6yi)4(LRQ{;>53~Tr~XHk6G#j zrU5|{TrR+|8FW*Aej~OulK2m@rXMS6p8rO=?NKAuet|X{Bh@}gud9)2tU0|~8j5-T zWAvq}dH!>>*=U|;x2fiN);t{Z{P)l?sOEXrY&6fa>#BL4H7CsT*G}5Dxp^gy$d*9A z?KaPULQnATqs>P1{3q#k)jZFdwdQ&I2!E$lguf)s^FN@6-w)@&@6&qtv1YA#-ah!< zYZd%1P4oPZ=<)XxIr#fgJ^om;);w86+ltZBTKls!TGvy7Vk;P2gYu zqPbiKJ;_VSeKJM%IQ`G{P?y2@^~L=UTa}CzM8-VT*hg^_W)GZb*WB((gXUxv_Er26 z-QB6aihrfeMqkA*)9b3Qf;HchiU}Nwui_8rOI2URKhb8RuY%pC`YKrSaC{YiNXMZ1 zDp<48SHZ5Uz6#cy@Kv;|+rvF)lUBCfzKTEA6Z}tUv(Z=aC-l1Nt6&-L(Q&01fDeenARtKfHO`YQfLkH5dm!QU74_+!mlUxj`6`-oNe zyEuIn|Dea$Kjz@;OL}~a}sb`h6IG5?GFqw=>pM`_&;NesNyf}RpeO7`geH7gOjlau$ zJ5%k*0TMWf!!^!6NFB|f4b|-_j4SuuZ|f#@$`?Iw4{{EJuz#a|)XwGIpWi2tWn z5&u$jSj@l70KpsbAh_EALEf%)TG$7{zqbm4m!#8T{zd}^Z^^^pO$H3|cCF*WJ`C=t z+LT;4Vl|NF9Rqr9&qL2`2K4ZDty{uA^t{L_^lZ0V;&aw%v|q4kMJekbm2L@IKt)vV z?8`1Q0#Tv1CP#eUDmidZW*sC5a!=cTa=i_6?Z{j4I+}BW_Q6>O6Ao5q=jK%$Rpv7&2EnW}~n9L3VA?5VcgHC78c=Cx`Rkb_s!hpxzVhHfMF@rDbhIo35| zGR!H_I)4w_WvF)YMb>V#lfRmEQ0?TreQIm$${lHM{ z&$n8U*YZU@V%|`x|0X;|a&@*p94{z7n$n8TTjGPOD_VcW2wjv+O@3U?(S%Oq_K!)Y3o5R`|2MR&x5z)LZFyYAomb z3!3INw@x_t)Yxn&oduxjy+6lBU#=+wgp!wQisVT{=npW57rYWru6;hqX2q=Yx1e>N ztk@dce4c6kjcjwHn&!WpwHrjDtnY~suVZV#D8%^{4Mm2<;w+| zhSEg8Jtq41S|tbmBGW`~HUe$lK0o2yCmG-rOSk2r?EB@Ia%Jd44Q2P!NAYqiJyzTI zorfQWi~z-%%6s7NK1`BU_R1`@N7WlpTV9SF%LSbi2-~)bvxY z=DlpIq#FA_!`h9;{;y;mRAWDHzi(@d{U2nntt#R4-^bdG#(sX|e&t5qK0TB;{135V zsK$QYZZ!7u4yv)Awe=BP@8vDPQbx@7{yj^STw~zR{ zrfe$Ia7h~bzs&%_kLE$}BL)cacCE4BJ_x?lDhOVh#{TazVDNkLF!`{?A-?gOX)zhEz8$gr z>9-!^6=^hK^=qQeO$~}~MBZX|$pd;V`zsYN^GUUDDqg1(bl_6hZF!vu$KR9oAh3sd z^{QL1r_A^HXX+}HW5#+1*oxOX%J>qgm-r?zATb4*MSQqXTzV9LVd+;Wi}^5kBs1tP zHt)7b7TlCu0YnFvd6Hxv-2X5k(xKSnjiO0ch*SzL2!TLEhB7TT%&r}D)pR#tN zgW-o+2i3vA+tbF;VVv4K|8wlM%CWukf5h624hDXsa&WJB;80HPo&R|@4AsHF+l>we z-a&OR@b-j*VIDW5R>fYOv}$|2&4qq`(LnTHW$i`>!!NN8s)K>Ir+KK+TD6b(o>j!Z zBpnRDW`N*-$%Ejp8z9KrwGIaRAQ)N&!AsM@@LL89{%#%yf7^gT-mY~p*oVRQScSn+ zb1?j#0X=`1ho0Xzpoh0>9SruN=lxcpXS*E?Gx+sZX|zXeqDSdqC>;!?gW-ZW7-p@K z1HYD;O)#U{escW@+_W483tk(|@@TNH2FsQ4>G26?{89Yh4F2!XxMaUF*r$b+9G>%L zuM-Gkb9K2BwB{nOg9m9==)2%?!o)_r1biqRujct@w#P@n-zkiAV#vdE5ri5S0ktaD zOedFO?m<}fp6$BLumTj=qf0?xj)Z0C29P23M6F`}gjZM5ETUUpQyr9<<=kci~9ebrhh)rvj$@HFw30{Gh$yw*6>* z5&Gm63Hmd(DN@b)Ph3XNzcrfm|D4^Zn)P`*H8e&%=(q{8bmdZ9n>O5y!|y|cwpN7q;>OlGyiY4+7 zol1pIiyD5&)gOA-!-@NnsutdXKk}1}=lo#D^BaxR&unysxa_NH(3cF5{NH(y{2vBL z@^-D2%szSa8#Y1mIl=>d0yMD7m66y&u3JBU7v3OK8 zFjd9uH9_*M8*^vf<$?P2f*hsbd?Pvh-c`;2|W ze}z@VFYPm6#@=m!taRj%W z_#l(!p`LU{)@DU`P-l0x5rBG4*}^;>Nc%G+(6m^2W5A8R)n%&M$|niR_0wYs@| z#P_Wt{w0|ddanV3ug!zteFg~fc5PCqeGoir6$HOtZ;VLBE)`K_I$qdqMj@>&qmR)j zq;;L&_`v!YM6ke5G9sz(AdS}0NigqRCFq$|3fu1w+KEU zdwXQ^06qLxZB9RGRXEkjYao&s6MM+i$vR>2j36oM&5)o;t8<#NGh2{p=whzt68;au zj-?Du!vk5~V9-fDlU7lz=!z+#87^Hjk&cbW-9rdh8C^!qV};}(_EcmcHt7WpUXc+N zw2R0@yz3&1O;3tE{J^3U)|ZsV2NTotP{W%CAG}d+<3RC;47QdPCo2RzcE_X0@HhV8 z-|QcGB4hvNWrh$xrP!d;AOGJbu~GV61YI^Eq_>G}9Z~|~p`%NyUM)HZI2a??`x#B3s3`d^@BBaI<-tB_DoAgW&ylf&z4Ih4n_ zSz}U+K%BE8Lp5?yoDC-}q2X_E zlHmsD{8EOSKqj#q3ZXiX@IT4qp(?|lVeLlY&u@H_B1n0AZR>>po7mV?;r~|FZWR7+ zW*t=F&)ZGH|Cg=f>ff@7t9xIyW!wl=;m!%TBj4lCXbR$AwTfiFW49FUlkNf{L?YVh zOZk{h^gnr9<{?~JyB~=m4c5yo^`HnViq>sHUp+xoy;8@Z;!^xS-o5iCvm}){DmEY( z2rg)X=4$Z|ErG;U+iA2331plvxrFP%c%trwS(8o?6Q>f1x!ONL?2QKw1k18Pkvy)D z4U*G)Itp5T?YOt>bv!VYNGiAE;fZbBf~j;%+A(3Mw`+4I?1SKqRzdJHnbsF9KMnOSi$pJR9fq98yNR3$)ID-a_p1@^eP>gAjRAsR zmj}U~0fM|;n@wRK1ov14!BIO$>)#vD^9_0E`FaC-c)K=B!anr8)++RD_c>aBX`Mp* zk5<_Rf6I_ek@mus5lPlWBo$e7W5X2oE}}M&;FpRB&xqahI=@UJa>|g|*8dQU!ASTG zgUEt|D1yA7kMkfDNgTSX)yU`%PZ&VEeynQ$Zfe*4_s0IPvZZrt+4yr+b zwVLtJbq^K3V%e7>NF1)^0Qid=u-S8U%Q|)*xUX@lRPr{7cdx@E;8j z{Ej>bewzV;yj^P$un&SQs~~tu8U()60Kt#tLGZf_5ajJzgMfVyJZBXIN6jGcaRYk3 zHxE6ZFrbIGYYhVSq33;8p{Fzm%-KYblchmm1nVl`jyKN=-~Vp>;xh=?87=tNR#^nY zD+^X$FocC<9kFeQDa#Kc4B8&nV2I8Jm|xD%_ zE&FY^N%K4T$=A#=1~<QjgjU;J?NL?ZNS;_%T##xx`) zdv@8`pT!DQwgldt{P~5kF_vRba=V>4Z2_x9L81Z)IX0J!;;Dy_T}U!SLq{A4D`h#5 zFl(*~M8N_a`wE(X2<4qd?c-7W-wdiBeDyM@HDtVD!%G)~TJ8j`xyb8a5^ROOM%Et^ zqv&`w&p+egI5ZU}_z;p>PGJRTGeP3~)&0eYcbA$z&Ph55Jj@CLr*l9`yoS=u^D*G> zoDC4~YRrSc4(HRuBC*m34nTA{l1R{eK>nx1Mj=4INct8kCysQR&59;II~U*N;Mbv} z9H4%vggVF0HbA`~iZLTT6h~r%G}1Zg`LG$}m)&OQ#Zrr3#oy_4A#CL!&1AdH@e{rV ze<19@bL-Wx>3M6F`}gim@x|=3T^K$SmRM8=g=A&#gtMPyWDJ>`i0Pwbrgn5FINN>- z_teRa^N6e)Tfs~N)R#bjU#uLqiIk(fB@J?pYF-!y7_W0N>TC%$k0sA-k)CJLQ0WYN z^8N4x$!+L|UIN36R$1A_|>D)fO%_CmcwtoIEHg*V4nhx^_*;;aBZalSuCq zvnW2OtiN`lEE9=%JyXGI#6&{PFjQ*GLB;@ffs4(%vGrS%gbFsn<$_mdx*DT_c?(Wv zeE33rtG6h_&~*}`p5uL}J9;6utr7J(EL&&?LpH*EMX9>Anzu%MFNWG)(zXF5OH%17 zmk%F2Su_fI4QmDh0OfzCw}$f7?KW%|diRJd9|XpcCx1Rymis(>r_7Yybf)aZBzqIza`CA3 ziLe$bk*Z+?Uc6RiPqpQ)RVd@eyjG0@;vAneGY!{odv%T23=8-Mgz&pya`UNR z%hYUxz~5r2X?QFBPK_jmVv$17@_VIjLR3p_TnIxwM3}u8M%f}e@4OWLvQex*z zY@9h`f&PSy8*yDZ@hz8-yiT&&CZshnt7v5PFhuo)bKl5nx%*{xb1Su6KkawrLYJJR z;XM2>Y!(#HEboE8`?PZKTa(rf-pb7GbVaH%&AZ}DWaK>)u3XJhQ0n8QFPCA3?CEL_ zt9VBw&P7zZCf=1&Cz^ZK^`i=G@fZD;ccSe_)rHxaqel)cNK7=iK8|da5lxq`or|6M zbi2+B8W*`~gZOjBzCuKu4ArB{rJ}M@q~9uHT{KEI8ydt==}sP&EF)_0j-MO6qe}36 z_p0(GL)fFF;+HZ_JbXsWrG?J~`<+mEP3rpqF*ZdKm|-e9py9$_g|tr}b=gG||>a0&!v+q^(%2wq)7R z#e5DU5Qr@e@rPKoc-fdLMX1M!0Gdc~mVV#n>6u)ZuS zI4CmmW!G<3h!N2c>X;h7UUS86o1k`P>OVf447L`u)|y@fr_rG8lT6+}HUF0i@EYQO`yJmpl^|c(z7GK!741fXzS{O`ZBu`7XgRK#0o2aEG~{j8eDRA5y%Ia(PeMy55r3iF zM10e*9fy^2qaFo75uUwx5kFvvfU(9Yv7%fJ=fSpCk3e-%sCe31J%oR_@W%oENA*@{ zIlob91ikCr6*zm|d(NwYUnppAG@f0Zg#!zDq10!W{ib(dYHI34=yk%W)9#YrnWAh8 zAwE$jXiiPfO}YDBP|wlGgMUu0x@X)e=nn~alVJL71?{P@Q=7sw@2AK$Kfr#h%al5T zI*#l){NMA3#~SAjk98aGkpI<%nyIVXIiQu^0|S3ew{iNJ4La2?Iqp5%g*kw@ZJ6)~ z9c?h(jWZ472hU%(gM5Ft@y^vx;u#j~n7H?CJ5{FAW3M79HyU+1GOzb#+eZ@fXjXd1 z1|^m8p@f7LpkZ%k+iiIpy_@`?3it)@5kkDG;J3H)wBN389NyX6)p6S^9#X%zD~#Mu zME=~_ghZ>2!(+V{)EaKbtwE7m_*b`gIryTm!|G1}r4Ns-{wMr(XSdtCF7#NH{Nwz2 zt3L)8)xKlh)t}Tf<0G!9dl5K{HX6I_gwT(|xPhxLVhH)YJJ;OWX~2q;g`NjDP+own z{y5ACis;oJf;c>W{!L?3jSW4HP~KB<8Q#LECq9yQ@Eyao@EHl-T3& z@UG9pZH?W%t7;9uS?_r5jb}EHH|RKDfiiv$!U&%Bqejq;jsd`1kU+RW2YZEnus6~{ z?NnZRJ8j-&8tYB;`t{0ftQlZW#@f5mIY5HgaaMmB2XCbi{cRkXEgZ1=3ox2)!#~;D zk#$muhR~92GPz&$DBXOis)AcISD35!f@p*OZ4Qx^M!`aqav@UK0#SPd$ z=KC9Dexl5DB$%t`a@3uAjyjOTQFrM%iZ$OoS{(KNTFFsIcNXEO1gRySI^%YnR!}?5 zz|NS7{j2(D7eulLwOp&9mwOf7d>sB!J+Ux*d{1=^l#sB(4UlM0S_Gbiw?&n4C~&=} zx`dCA!!-CC>4=gq#M=F|U3ztk@u4(K>9NAgd*a00Atxf72oJ@7Rk370_32i|_p+Z! z{H^?tUD_uXO(&0Id^U%L{0DjB{zRBs)LC~b^Dx<7eIbGXx!Obc%!6e9y*hc}!{i_I zYK6R!oScxV6pnL2Z>gSbcx}@4zyZ(>>p=@|+asohOD_CfjiB#wFY0#M6*^P-{lpZ= zhD3Z=Qs8DwV;nA}YpzUA3_M}wjvrFxkbtrTk|<>G6tWLTi9d;_(C>y!Sq6;cBT$Sg z(mC}6-8gW);P*~26iV-q)|%Ag!~{$I6Q4Ch*SSH5CEYF_J6D%pCJm1I+SE1UW%8QA zzcO0^dq}@rJeIRuJgi?XSTmm0iOS-LC}96!<#MqTY%yjx3Rl25m8F$}@=GjX$0OjC z$$k6p9ghR-xNK*fm=x}kNFR7L!FK{9&|lIjfLw(?d;r5wu{0TjqzafXs=k#pOnn!G(b=iI!_gS|UVsnYmX-=>4 zbqCmZoc&|+zWZOZo%}Awz_&be^{wZ6DoQr1;fSH9x!=h5(mm`?Wc?XVh=gX7;C|^w zlK@CRMU?@&<#qniAZirY7cS@}z^NPwu&9>+toioQTIjF0QUd&3AqgPJoaTSJb|LfQ zU&>0)v;l#;3n(M#IE79ihBQhdgX;-m@O=mk<=!n&7EmCX#0?wqcFL)j$Z~2Cu$gBL zcJ)GPHAhG->4g+)c1BA`-D0JXdSxLYMQJQaD6Zyj**?Q)wZfsWZ4Le@?P>gaB^H!> zs)Ca%3EgQA)LV3;Zb%NJ#wVby2mZqbWcy35Ps{-&P$*K)YOrRRRuTp2CypRkep zdp~D3MCb`EiGY3(E2C8Zw`o^B4w(`XKn3=Qe+gqNacNYMO7_yzQRLPdl6NK#rX4*@ z-;)E=P!ChqJYsw412)3+Z6IxpnPcQ?~qpw680ptHnhZ{H#RhA>ceKPt-Ab|XjhSKt!woXYE(wY)XL_bE$VNP) z8E{DP1!Q*u|Ks0=sK1cA#JM(vVz+jESloP+2ZG8&=Z&=pJaofuanP z2Zf;K--mI5Nu8(?s~k|y?Q91Qst`&dHy~mcoDu$SG~FaNWo%3s5le=NDvJmLBNkXV zf+nntFS&IRx8i^XYBJhXv|>nzucDIZyY-Uj2XZ9Q$MlkjHNR%GBvHpoN%S-QNg@s9 zFR(DGam`xwD3nraTci{hM<``LGlalVCbt-2P)VI2@w-D4TI|VLa_bh9Tc~EsMzCX@ z;EZ4QV367ITc6QOtN)xMt$t81tyuHKXi2MgTPdyX%9K`SdTV6Vg;pv^B?=q_)@zHC zRhXVLEf=eZ%|mieQaqk>pJdL^_K_{J&{wD;N%e!9SlJ+w7mdk7@(Za_BEKPFlu34F ze2kLgxHJXktyOU&u)3WZM|r@MAP@syh3IdxO>>Dc6;~gscDe38<(?V7mj$kly@b+6O48i6S`G*{<@{8Q$QKFM zA^ku(q>o#^#b<1NQi2#|KPz}F4AKHbZZWE{{6py!B;*Kf5@G}=FCmEdtzwQ6cZ*i> z096j%!wqt%z0#M#QFUA((T4dV&VfJD%cH-{kw<^5mq)Dmfzi?uK5L~s`p!b~D4X_@ zT-s_u!R3PNAz&g4>_JMH?^Y`pTv+js##v%268Kg79a(vL z%H%TpY}aju#Gi_DTU?0AwwFg~p%)(T8KQq9dzbgmTA4L}*^V5c^}p!#7brEhSo2+@ zCA9u;D}~mtB#bZQxNXVIrX%%s3a71BjJA5C#1$`Q@C+hi^F)^CrYR5y>`!E)6vI9S z?CKGI)!&d?!%H^tGipL&O30mlxx72SlQF4MA8-DJIm|o1i*-=ASG4aLE%yBrE7|uG zMc7v*_y8Q-qIIFE{tQ^VGkNx^#S5&$v0_B}n`fv3e2|IQCkb({~_BBasH${k4RWG#(%I&6)Y-LxL}mz33DF_K8#JUamdh?%N+W4QE0~cjSL-cnjOSSCbmimAoAO! zMQxGDKh2uPbR~X^j+bN%v*j!I&_BMSODkU*3_^sU50U+&tWRdBhaFRf<3UUxZ-|v2 zSzacfA>G6NENGpQZj46~%Uk_Z3d5uNyVihf@`A)9@Cn-JtlE!HiN2r$-i^MzI-pmVid- zTy#ZzTwC}^S`_>FDEUM5f3snwc&1oAoIFLZESUmP*-UjROQsNZ=ZrBDB~$Y7UDnTl zb5jOTK!z%zRl#PI&OA_UuAaANPw2w(Z6=)-WxTZS&sAs@;LPwSNeqGGk#C|gp(H5r zl}bD&8c z&a;-9!&)?t* zH~m0)spnV3XKej)FGkr9)5jo@A(j3+ceg^PAb~I1WcKx?<|uKum^tx)!uX^wmO|)h z1IwV0a9XN^4?EgO9JpSeDweG=)ClT;>AMBg5H5GgKy5-oZ7v zBioS+pz7uZl9ut|a5DVXkh$OZ@T(zvw6*$ih`;W7@W)5+4;TKZ-U@%5->5W#-pi*~ zymmC>cIt1$`kQVPbTIUJqw#FxOrwr{p2Pn=e|W5M?(kT*@ecB@`I~o;e|8)1T>S@3 zWq3O_FZJHJ>^HpwQ&UrTTuC@}+FkNHQ(@5U)I6LKsFyM|JvZgs#lBf&&gHy zj5`JW&v>25TF`0*?WwR+o5IAQDRRvZupjF(r5|t=du}x9bU0q`>;)UbN^f>JAJfI4 z{t8TNZ)e+Wc^kcJ{h$gHQKgUH=B1PS>*(a9pO zw8!pvQMc13+sxE6Dtm-#ElevY8!O`^Ux)M`*Yv4-VXfn@p=e`W55+CI8@iolWxNqZ zYp|F#gPPlH1YvaGb?~F>4J%hj9=)%+x4Ku?qvo!`TG;d>-=hccu}_Oz`82_CMJy^# z`yI<}w;7Q`=;j0Xs>)2zWHLL1V~*M@O)m=CSoac+1O%5e6rE1bSluH}IogfjEW5!U z6Q1^)kp3-?z-z5V>*Vb``BbW3K?Kt6xU4f(1EvhEgA0CZt?3C`=kfZup4TUHc>Q*I z9r8yLB7!yF!+HHm;B{1NEoAgbdl>z1^@*Z6bd{KVYRZ`*(%x~MkkCIC7*3@NImHe` zsq^H*(Idx?%{UP#IpJE+iDEf4u>y$)!~5fjbV8_~ z+N?Y6;HjR-mvcp+^5NOAlMsem8PqfqFpXQ*oLiIKn_f7xH@!UY(j5=fG&wS$1o%(Bi6w+??vpZ$rB{`3`YYq4z^AcY~fWvAI`04nk9s zFxvO*-K*19bUMSJQPX#MayPYBsyYyfid*>u&RcLT zdj|IKjFjqTnou#Rr=4hvO^aAA@|5sr^b5sm&O-4v{X)T-M`59O+Mb2t%e`)C->V9KqAl3m(>d>j-G5 zo;`x+a=qZu5mOR03+rLzwZIb@30jM#E>>q24$mDsb?o@jW3$H>-vk@b2+m6!XAnYg zW~ds2TlDb3+=MO(18mM!8}nrfK4#lZZ}?J^ro<>wYkF>{FfAk+oF_s_W#}7_p^=rJ z41;!OttOtFW6wLM=jiw4aP&L$9L<`Y5#Z?19**wnIa*82!C0ED2N#C5hi2zPt{PZS z826JI<|lmN=*kY2=(#fFgfaQuQ5}=%RixX$L|D0Es3?Ufwm9ugBq+WjgRF3@mUcLNc&94~&z30RB$gj8P zoppWCcrd}1SRS|4OfuZP)E0|LMKLo6G?)V?P$pi2B6%xK*f^zIZ^p*A48@d9y|AO zdzOq3@6yJ1WRvyMUM14eAjp9Erg>&@({9Iws~fh9rVv~Gs*t`@U1kctP^ZKvZUupm z_&0=a3^6_g=WsC*|Iu%Z2tJ{oI1%ihcHm&E#uGHGcoBTONmk>v{^CZ+(~?wK3Igz? zw4G&)uw#-Kf_KoA2?S%2D1F3-;47-xF(PrehhcHTXGn{mJTw(leE2J1a-E07PKaXl z>RUX1WEkNY#8$q`~c zj*xJ?GhQ3Q&4TVq!-4cS#Fhi#a@GRB9feL5@c7L5o;c1$2+P4e-c>O?v6^W@&&9|k zD^olS%C#t~F0J5qy;gv;G30%s1(EjsBqSAxtk-qXf=p6;kk`s!*}l@Rwl-K6bw(O0 z8#O#zf@SXxQGPzG&5MZxX=?#w`#?ruyN)6jhmS%xSSEi8kr>O|^z$Bo{FkaZy!N`J zLd`PS^hxvcSFojD$w8lgRgOw9eMjpbW}~}G zn;<`b6?@55%1e0rJiJ7g(=Tqk-Ed=7j1Ds6KaaYBJ(=;pW{s-So8@WoB;4J>WHOOF zDiIMhbzF9^mPxf6`OWHdr{j`@>W2-^OQ z6kN;NUtmH39M4Pi+LtLP;O#du6!hebOUuyk`}Uxr`^Zr!8PM_Tp0Eh8qew~McZ#e~ z<(5!aQd&_%k$^)=-_l-*=w5lZrJ(1^-5eo7{|+py@zfLkN0( zt=sXV^*B8t&Zbgva6L^x<7z`5AYNer#0`0X;5Xi+EE&9ARNQzGRNRo)L#(`E+0t?4 zeYTX?=^bMWg*;=9N>WJ-;5xx7>PX9lw5On}0+xrftgg||S;LJdZ-Bri*tt&gQYA2x zPJ?A)E(bIl6B72wV$KyksKXZdHHV%pp4>=4B2ol3*g}VylO=NTyEmmK#un{tDY8X+ zM$y-6)~uqp_9Oq|XAYZn2))m5Ky5{P_B^}o;hHO74b{N9OHIKA!qh1;goyRw9HkJ4 zzUaj(Vr3Tek$9&p9L8L$8mlfVrZh^0!ze0p3)7^khe?RYw(OTxrf69x@DW;6WvU?A z(?NNOL+N753voiNoMk;CETNVaCQ;TSLY-OqF{XN@W1)2}5hb2P_SgeTc!m%maOm$GgR>=g_(1BE@1<8MpBSGwXNi(GNj7jNB~f{S`t-$X7STGlt|z`4kx zAd84?33(L~l#x#i)`(J+Lk%YVE|d*v68y_)=3eXkS5uyhe%aE~C~R zBM{1R3OM`9&+zjNGqh)XH)a1(gdE#Eh=kTYp;sp2Q|JvJap_&1yk#40(B%Y6X9~MA zg_{?HE*M;^OHeFRxS^W9UXvTjLY|YtZ3KAQoM_DBmh2mSR6$E{UYX-dimGe)NLlW6 zEc7~3wA>uGjS_muvD>3icA?DiC5MBAP_ELDMl}=xli=e%bv9PZ2C6}*iN2S%Oer%$ zsV)oF^)e&0*qJnO025A~iL>z<#OlDLRtr`VCr_hV0{)3VJf%`jyWHZ8$}%HV5{O8H zzKl78is)lS(n6(y#Xe@;8CGT9;6;6Cj#>X4vhG>t4Wa-*UMuqkEpk?td4p2kAXOLu zqh*9}SR@lER5xO%ced$Gzyd_iPvb^6Ie^!(=@&;4Ab4ekmOp zlpsjp^&oH8gyQ_)9P z{csk~bWfd7O-4r#oTuq*xXg+%_BSq4a#|_6T2p%RGdTTD27(E9n(gAx7Z^YoLT5>zZ2G09tjlUMOTJ9tt zZatplkj77kAOz#`0h^uh31>S8A9V3x=%J2(sKakz{cb1$*lD>j-)(k^$0^k(mfYw2 zLJ7GIZpi`*F*Z_JL2>Osb}B-q1Exuw=-~lHYe``#Qo(Iugn36Us44yM3&F zQt}GVBi5bX3uQAZRF@E%0OAaPs&y}{b$oJQXk~m6%8NY)l0#?{>V3lwa zL)<^u5XtgZV2F>poq|IY4~r8dYi<;4E#H5^4fSv^*GVC$U}5flAJ^=Hb=TT zhvLcA-3H~6P@$}h@2l#LJgIg&&B{2d4;R5x;S|%B?m6LUA4FTlr1^?T-~Q zN{P~nP>cWpQ)^AvZ^J5Xy#Hu7TI)vh0epU?zX#OZHFpV?y$BBtkw_7@W{^aDhJdO1 zdbqG}0wJmG+0wIHGLf8gtD$5M*h2bR?J+CdRx)=dJohr2a4K-(JuIxuD@Hb{F~Untkn=YD9xZvs)1YEv z@=xJcq$1o@W817S5>>CP@pwI98%MwG#baZ%09--`B|9co^-80Glpu~DBT0GNhqug%{^eu?@aIN z6XoKF<||A&s3Di8h7DE(GtRO+Ym{@Tm*nhfi zGQ;8uJe)B~Wmovy5!KJ$TqLt%X-lK}*+5o$d;198r#FfCd`PKyb`0Hjvdc}9bMU1nT`$ByE_h_2&5$W^_n?UnX z7&XM3U!R{^ERYV1sUu$kYOPN*#;fpRr&$$rXzvWiUd|!GoFH|9U!}Y zoBlR*meK*jqV>Z#QVT`n=WtwhpnUZ+FcwIx=-tB)3(*O7N2aQDW+EZ^m7y1%Si@Ky z0>>-#)P9AY+OML`m`J0g_ABZ2>lJFV=G~0ii2zYsss3vgQvGd@JnBJEzBlXE8e~_+ zPM0BvCNjE8duAC1EUZxn7c66oO1u!RiRHw zMc6N3q%L_SIws_Y7{Grgo1NL`AVmPCg1m5%oHi&98O&pdyY&pQKZhYIdWK-l_l^=n z{FQ|a@nv}okxlX;FbTR=Vo>Y2gxgf&UKHsVJRi5|?i5^;&0qUu2BQH46x@|X;Bf+W zG7X^sdzHj-OgP{+nmi~?0uD9%UHc_&Ja1sPmm|AD+(%;W^Z4sNJ%2r%!(aF7`HMB* zF-rXP&ld964VnCvrm=)23sxNs=ZQ?O1k4kQjo>=UO3BrRCFtUlRTb+72KysH+rx#1 zFQ$3Of4v_0GdakAM2~#deD^3J|El}#+mwAMj%8EzEP#EQ#uD;lq0Tb8LC~k*Hr|e5 zB6KlHxJqG-6th{vcF|qyOW4lpA$L3na*ydD$C^h<*uLC8$esMxOkta*u>?6gg>B@n ztr^sNxD>1Rio`tWe+NlM9y+2XgNX0U-8>+ zQ@*p}M|X#g>yTn4>;X_n;Ym1{$MIWhO|J#F!&%2cJNEf7RtzF}DUb#SxnG6>xzdpQ zHXNedu6cB95oBQU@m0qSyiHaVcywCYWE*@I_3V8zhrLhe*_$YyOR%$O zRY~Yi^uTms9g^ky8_)+LEeW(zHk3cX1))WqyrO?DTP5}dd^b^4GrI@(17wC(`Q4}K zX+_Uj%^c2Z=sAluKQKz1^@x3()%zLq2B8?yB}Su=Vn&-Ifn>I+d8A}RVS^22jl8A6 zWVs;<$4tj_BkyPj7o~#Ug0>!^=W-Al=n)!x;M?v6u?0W$`bcRWN9=>@$(JQ^G1cv? zmfjLL^Hzc4;Kqa9PQ3_V z%@d=vNWIlU&VOl9&R1yN2m5o(OUe}A*ivO|^Ind`^CTOY^hyeEl{qDWF_6Gnkl2zk zAh09~n?2NYK*f!`mzO2ZQD%SO0tbALkShheyhZYcm?si*RnR~BD#G|dyVmWX_2lT9 zN6)PZ^Gw%0J=6bK4%2_Fp6OY0Wt5nH)k3DfE>Wm8wcKmz>_o&J?#$t_ke%)tNm^#g z=Y%u+oLB3jQA~IcB>@3zD^($!eS0N)7cPp<+@ZKuW1?6eT{5vuAG{GX49{&<;kfV> zud;Uno)fQgT)b3U?%HB-bZNEXqt9|2A8=~id9~wf`bGi1EODeWj1t0MNDxHm)WjS# z*9FIt@y!Hi1dLob*zvJ0XcrzYFL=;#>vh_Mqh{l;hGgivVr(#8CUbDSspnS1?%G;& zeL7rk*D5+cEdOvx$URFow|VlE-zfhkcd{5~ZGeW{ng31IEB2{zmCso9-v?J+skOw^BsPGL? z9nC;NXPFSxIUrGTkA!1~}t4j((%p@t4=h$005tX&tQ4^jQL3 zbc@I8{tEQ?&wCX}uT8u*B#qKC$99LEu2)HP1QbW0>4wu#7K}X+$0%M2kOx=Q0FaL% zckrMCHju4_eFB@3#brQv!4nF>OZrHQp6v;j20WMw( zjDZU^891@5I!|UOZ=-V@Y6`u z59(51s!7bdAg8G~F=$lxYm}sRKB zx~J?zSi?dr1{Yg&AkbqnA*@xo%l57Z_`T{ll9HG9Hi}*-?F+Ite!KUaTZ@|O4n&(s zCNP66j%S&R={GQyGeLSu*2bbXIZ7tw$?b|xGX2z3{7a>A#x}fAn7xoeWP4Z);=!hH zQ`WMy+K1$phaFFPox~Z}B3SdN(?8w?H3JC6=GSL}E(9w)%%?-_CaSyxq@dQYkF4b; z)_RUny0}jypA3Ns{ftowLl7m=Y|2vm5etJPzvWe9V{DgA>!axsGZw)a2%8O}j}*`M zq5ZcuQ-r+(2S`WPU26I@hvcE4VQ5;;8jGp(MN`A?OqAGpJ-A z;<`x1i;~MjLqn*cP{A36ZVa&PjGD^B!srCklJpks%zF zvQvH&MoVCB3bxW&i@;?+=v*$HwKy4okJ4G2tioP8Yo!>4#O6k_0}p8WhbNTIS`2Kc zasw`zVE#{OiB&D$q#wjG(Eiyj1JXx&mfgN+5!! zQYiAGbmoDTkS5}m&OE<8vd%nOR+iK?#ImwIaG+s?{``ZA|h;NycZT&Vnb(cq(KMboc>?#xw?cAFR;2+WV+IBgPiKZpamz(8L8P8bB%F1>}vG7vfT z1`;h4(@v^aO2~RGxx`nTY|7rDrz~s6BoQs}*!5Q_lx5ABN=kueBV~WuCd%HCpe(1E zlc1nV-)rXQj?B$ZKXqzxdf_l5?{#{{y<1P-Ds8^rL|%5AqmY+1V;HTyR?AQzKbjfkj=x-b! z{=iqLczx0Q?83tIqtl17A-h)(+1KVkmffa8mNidV4cXtb2(l-yhN1wO zs7(^H7;rm5lVA)^e`rTKK(4u+2=VxqbK?SO0OoNBu0&81L&ncPqroV< zNI78NzJ32cd+!1#*KySe%UG5r;b&~waV*={<5x?b>5)c~ZOO8&@n~d^_U=p?oWElt$AmOze2>bDUEa8!CSRNs~HiVGySU%t*AIl>@ zmOyv}ShBF^oO;}(=?7Q>RXybISH2(Yk0zf$xOGlFAP}xAS-kjWj$kdTbMCaJ&*uoqo#iBtPDV&Y$cFk< z>J~1ObdEkt!&4YEH;yW`n8yoYquem81j&%D#&Ur$#$=xzDN*gxh8WB)bYKazt$r>fXgmhP$w0+IL(Q-Gxt5XRmtw(E;-(5$$%Bam6C zpehW5;I`I0U7UTx@$pmB<44A)$EU`|mE8xgPWus~^>Ww`hAhHl3>>$kcX9Z@B$Kw8 z5IJr~f)Vu1T9IX|9H47qj!X|-|XrGKqT_G5Bc;l;$%{z2#;_>J~ z&(?)_OL zkHNOtMNgD2QgzQd*M$b!yLDOeOtVp+UMgjmC(qgC$%zzsGHsV9yjUz!^R0dr^8~q% zW`UfAy4}IL%1cGNRxx&t zH`{^t-W1@yUH?YU+t@aOQTq%3OHS@zQHhFbM4tawYDTE!nT zt%|%@2<*x>-p7K}{H_6ODt3_aigJxFW^?Tv>%%oh*LX1=US!qy`2G9skBz7r-!!sG zo;1D}^2E0f2y5q&TH|by1B>Y9s6;fa+P5|85`+cysTAwrOLlU!T(hhGimp|Ty1gOeGe|0v!yZtM_(+rb7?VuCQlrGbaP@p z#aO$HupFUf^LWrGp|e+g!Dc?9^u44WKF&7rFrgn778~;gNiHE0R<`LJ^~6D`h1=7k zLCc?q%y=i3%5IBUm5NWcA+tN0Z6a?My@2BrFb#2me9eZ?+by9k7`d3&TVX);c}sW# zJdLPR1=>;Ie`M&E?4D=7xV7XgNM%m?z)aKERp7oX0%rAYbHGec~nN*P|@Vl~_-jD34=yw_?57R$8#Ws)`bG6jixbU+Y0L48Hah-Q;FE+G;HNrD=~&Yb<#O67-p{~pJx?#DtTgA z5igGCeQ0(Ud(Lkkl8*YoW4j)O9|yUB;SuOi^x%W0uwiecjC1^b*e4${*BkF+#hUm* z)Ll?^6IH*$6Ls(<<4!qgEAj7SOKLOH=d-Lh)_3*0*!>#}>ohNp`W3Fqp3YCOmzsMz zpJc_cdpi6vb5DmCug;#%hgl!YJsn;gyQjnNn|nIESliS2@w_daKb9lKx^s#5bbiWC z?vJwK*gc(}X7|lK9bRnR)5#s#e=SdBe>Lvue9Vr$Ur52;&)Tuai>-S)xx?P?x0PC?monN)X=D(%D=GW}7;lJA%YBg7| z&9u(ENTK2g8S8_&Rl|#8w`%x(bE}3IYg;w1 zc{pb);ybWIdC6^eiMMM0!cOl0$%WAE=$ zu=m$??D1mjR!#1(_eh?ww{Ev;{=p8ge@X$^m+S!J#n!Ew+yU%Jo&eTkTQ&d74x9g- z0-Jxa!-f}Iw`y_+o7p_U=2CCfY{rkz(^k#RIif~?tERtI!@?YmU@J7)FqhH9Xvok~ zfe&Qy2!1te)ojfZhyUSK8NzQ2qfF9X4X-Ma)PnD?HK3;Je0fH@BiwPH7t7FJkKaVGE&gQ@->+UvJD#Cl@tkH0&Wu}h z{H8;PV3!?tz6AkKEx0xB2avp*3oN;%p@mrNPPfo20TNPX8CHD^jo2l^IMjfMB4zxe zL+wT}3HfxpUdJQ`hZ}Wo%&pbt+|ub8uhnE!OwLy@xrrOuP2>BmDaQB7t9b>(`Yy{4 zMp9jv=J)Lw_BCFwU}$|=d_<-kSd*Li%L{sx#^klUJkAU+9~?9u6b*`A%RE>z4c1jb zyPgtqGP#An-mt(=Uc<}dY_gMEc?H8LE6Z<>3?`%;2=cb(xsW4`sPk3nf{rL^QHena zOg2iV9e)uL$Sneg!GRZzy5x0~q&)Kj#)Vh%Mut|N$nfqsCf9~@ZD}Q61qpET8OmSH z5y?(AAX!I30?5o0kHT4|ly??^zl6x=YXGxOBsIt-14+2vmCCb&b1qG&ytmN#mcQsp zKexibEFS7XtLXtHo>Bc!%sIp+W!aj#J~0QNs7~lkiO=ltek1BSnILK?wrHbR234V; zc1`UE*MaC$oMxrbu9clR)QK8_S9bh5sX^2Shv(%CB!_8wko?L)2b7s{^*9MO3^wg% z5T9a4&c|vuQw{!nVlJl1+xV<%>NY!hd7NXxkk^VgfY&0V}aR=2r>S1@%OQEt_3-j}yY^*`iDLSH)F<~?~M!}sKg z41L|k3x=5uZdjAYKhaRJFpxgJAyHp zPqiVRi>fOWaWbb|nmB*U+Id!A0&md;#Raq5%l;}}m)W(G`$#*H*{r5rA5t~VkY*IB zf+;4KK|UdxYRL&+9;=oN@d~C|BFfRjPtPuDswFE)I6nC>f34y2I61}3V-*tdpyBi= z8%*$eTwzM;$wzpfOfBV6ULLEZ?B^9sEk%@D{o+2Jx9RayIg+)VSJD@37nhbg*3(?r zx)n=}wBvP=WP0+k7&MKhq3Q7$G>LMnr(5pO^oMyu({dIxWynMY6W76Y)S(g1ci24JGx>gkp{fc<5j z0QQ4f0LJtl#VjNtG;1KmFpKp^aFp`oM)!#vk?rIMGiR~RLf?xC9n7ZrgljXDFIoC@sprq` zdw978aQ8jFbbKsznEd9f{x5mr=daJ=GzY=ZF{^yxKQ{qsn}eXe)PmDX&XqitQvPL0 z3>4L1U~%i*+LFA1f?GtZMKXK6_$3#T?d+skIsdt;IBGosNN)D7Ttg za|f+md4g6w3$!vU73Iu<3T-p)Oh5CmwXTh*jTq=O)1b2y107LrHKOJYI*;cGIzNyF zI$f>dvs$`J4fNJUN8K)s_FdRe+c8KxpN6zEF-Q~TRvT*WkoIhzkoGNEkd~eA(G+{Y9{p_;6rb?Yx8r-6>KGAnyIk@^t&E@QlBN zIW?bbG-{q(e_YI2VE5rit3{4Qn|EtXb+)sbWsRvX!8Xh2!B3pWqv5(fVJ(X!U3JrJ zU(`FvJ}?FX5*%Sd)<7&|q2}ch^!I6-+a&D7Ttvt9e`Pv~#32b=H}g_Pb-C_5L(yeNPOuM7h;W zn>%QIZJwajV`kd#i-FD$r9tQWW1u6-t!CQXLFYYrg3kIj)BeX8q*6P_vZ;|IX2UNHI7rB%(S%}5w35hl`AS-FyX(9+nM146CcX56z-d8lPn83iufG=j+P+}n@H@)tR7C`Y{f5Cz!XxeNvkKBbXfwo@Z>Er z2g`?7E1M-V4|{d`Nv|mIY;LNt*kWs*^T6*9LeGdA;FKq2P2b=gZsGS0&OzXpJ&6qz zg5>;Ma?Uh-aO$l0_ZD$X;9r(mR#;mjd8M#{&{PTz_=VQR`T|Ir!K4-H1HEH#uDEAB zsqc_L<#G+e7|P*b$zCD6VBLR#$5y_eS8aUtl;CkRnyb)R@j!I zw}@uhLLdd3AB(}}C(^L_v%JDB1~x_cg94k^I0qmLZqT5d@ymGx=r867peO(IiVYh; zv_I|DmLNy)qSvZ4%1x)$aNJS}@|ZgUzaBlD4^68!NUBJeC%`3^&qN17{`M9Kc zN~C!!IIsMLsI9tZ++xiIFKym@@d&+;Ue12y{uKgr;NAeZ#Vx|a9{dV9{-|1EfF4nE z8DjfkJLokxN?+(h@?qPY8F;OBPe78bB+<^_X|G08u%DB#ay!Rr}MXk!6w)#rt7ywNC+b|Q zvmHPk1=D5YJ{W4&111`iSkKFqx%NY-w&f%o=;ZeSJMqOK$#dO14ioZ$Xe2Qva88;B z=|N1lpvPNqS&(QWuK*G9Pb7oorMjB2|OifIVK6`3*bmk~%Py+)4 zQn|vZBn*+pzwqCHUmiH%4D24+Gcc%CrJJP&1X`bKO;8<3yVe9>|Dki|U$WI@M|wkJ zFAw0a9ySX}6BhjzL}?FPu(W>KuT#SQNS^u-o2o6mY366>%j)l`DGS^IY=O;Q`0PN-OfP{nGD6QBpyGjSBqLtHLYFV4Q<`1q;m@gw8Y<5Od5 z&|LgthQ77?kC606AvToE`-avRUh+b znvMGOQfcKKeP-s^)b#N&YM=nJ54d@zvFJHe0KAP^E0x)K)jxn3W_)p}bv}eX4Bm_v zobdu14{&P_>ekIdv?g0n3_8%-(Bt~7p{<*{gf~QkPr_Q-hbS+?0~{+Z7!^ryJZRu* z3+5eLmlIEoicB>t?Zkx3>lN&*Cw9L|+zoe^px4G526U^#+7 zA#IidpZ-u@g}u+Rp>Z+s5Uq;e6hRuSGmklXcf=TqpXyz(Exp)3QnQDT)C_UW=5k8( zLd=jHDnKj?!0}%KyLWQf0OE$^i(hX!KJe+TN8!goE?{^QQ1sw~rm!)s=wzEL_|f;F z1<_ZN(QrFmMy3A*oZwR98gr;i^tFd@VVyA7ipjsPJz#L6oF;^{6hGHkhwEz(^=5k! z;tfIl#b5f`0|BqEJqZ4TpSr>rmxTr&h*fbk_EvOUd??FUv!%V%JK3@okL)z3K5URp z!(bIJkQs`qh2{&av4S2XI=^V&UBcryH65J260i7A^JwW#djQLZK@2U>5L>rt40|rC zmY|x{6qAEZP2g39`8Cc!*tK2FXt#f8wg%7s5)m0k{L>u-XV?mJ&MPFZK5cTSzeE&c z+86R(z;c1wmx$o}lFvi4Qx}5CM`zB2hiEICmF78D{j*H@$*8Ki%p9aw(OIS!3A+9v zb94wD+M$@*{3w>*u=T$Bpn?#lmFQmTj5@^)S3e=EABP6F{3ZleP8(=*iJu zUzv!&T#*BxPpo<+wtNxV^vd0uvgqL#V}9<>VuD8aGwXsXl5dz;f*hPiV|M{L`ghkV9p&7%u?D(CYq zLY%d?s`1wBdjK{4*jzQ76-Rr$#pYU|+mGSAWD(SvVmP;6SpK#vHbA5;{DY(d?}YHl zHXfJK*$cm1ST0l=o$E*lkg&jz#pTM2l{1wx-aChvmyT|zynJ*+yYd$LYjXJt`mhi;enlGbjyXJl!ze^BY)?R~aVh)g0FQTf~aXl``v%*Ew^!da%$r zyegm4uAF{hxd4OR*;t3H7R#NR{YDW+0nDU0P(?Nh%bkrFFt~hlW9O;>QV)2`_{3G< zoEWs|*Nrv5?kykP(AiR|xB&#hcme#??p*1?Q&6}1cR;)YgKkjI^JJ&Tmo*H3V zv{(Hi+%)TMXjlKiQjAS(Dtj%6@L#TM%MUF62KovS!|MPRe&>!Qw{#jJRUI259$iM@ z0B?T|z8HDC`aj{92zyt*0HsH-s{S?nz)Bj4w=*~xB%b{DSFrLcAj`o&COmho0S(^V z?eN&Yz+;tdoz0~RL=?d;((((0an{GJglQ5%%dCH!?ftux^#f7_X%OVam@3xVJ%0aI z`(t;@?!n-F#r{0WwCk`pmlM3ze*?O;D}Lurp`nJ-SBX$!*b6~-jT(l|NqU`~q>i1W z53u4J;z-ILgAD%~5_s`oeo1h*>JEg7sk>|ouM0#^Q* z307W=r%^ERm^=AX$O~9cZdJ0ySn;acLBR}{A@xPiCVwzMyosfH;Mm<{7qolq*nOB4 zV|G33+__g^TNkvv_-2mXj+*x4gDiJSsw`6c?#5*lhWbKg%~Goo z9QNj2h~+0OYleaelku|Oj+e($@N&S87hWu6U5@cGmK(hMV)B+)EL~N{Ss6s$qyttTu8TMRmF!?8$Fc~4M0!fTbE)m&L3c*CZZ`ZCc)=F7k zyqM&zLpMo`g>6~RtJGi|*6a$irXZcDpK)t`IWuUF*g-p)0@|nSpykCqAV^Q;1+*t; zL3zxC+bC%j;KH5;8c3a&yr99QDaO*tZ8L^7@{Mg4M7GRe0YYT!ExAdfC+Z2&t{l1= zLVK4)w6Qf${is7a&Xx~JqZy;DabJdZYrVn!3{&HH9SZkZcWI15Jep%@>LD({g9*v( z6r-9RvvbJvDI9X#&LOUk6O5_kbO%>J%aqK|8A(87DWP5l+0XWZ!V-@;j zA0*Jg(@I+q)W4)12h77@8!gJC5k98uZptc@9a?E@&`GGDP3H+4$fo$*%X#BieN3_i z6BSZ4q;bg`>|Amxg-hOO=Mr8#l7B9FIX7H_8)@8>RH^r=Y! zII&)gLVrKvdR#e;1#-eIxeKsK(VT4%)Il7iA=|YhyODzIIXkj>@!tF+`(3#~_ETSx zgzR+sW^}+;Wbm3!4m9ns{Rw!_%2Er4)05I`yE~0k0YOD%uWnWvZAdvk=Q*%D?bk4& zgyYwpxkjtPXEk=J2hIgde{0G{SPSf>?00=8EPJHz(XGPJZe_w_wTW}Zg9;l*pOdd- zGo^Zng7SLh1<`up%aR0yNFqrBfoAvSv{&*#d8K|rj)!Cg@kP6UcqK(Z1a<+zi+eyo zyf-%j;*~3t1VlQ0FSUT!ZC4+0*@FKFc1+C-IRuk@sJw2foQ$VDG^t_p7b{kj=J3td z=JZ{SgnYn~mDLa6j)e3gmZ({k7Rfb7fS+ZfAlD%BiVG@-3pXCb$AlVuLDHepNWC>MAQzUWA18c?T2em&Uq-zrrW!4<> zX>V0LuFFbg($=++XxE}rB(-a-6{Bm%$*8lUt}#-GuBn7E<#AHilvem$VamZBxDcg~ zrHREwuk6FhmM=x|w5RNA(f8Yh;YU-1;Ro!(kQYCk|F!6U&W$iUxiLxA%tYc#EfG@` zM^vBSBwde%Uc)J6)^(u4(^UJC%0ecrj=+wuS0G_T3UbkJ(wJi&_>e`TXD_j*ry>Sf ziwcCHRWzxAe1lWlgm{w!Adey=u|d6HU68gaI4Vprr=dmM?s#MK5H83{lY6I({?Tr%r^m4#92rk(3 zB58@glcZ!F)Adt@(b=P#IFQZaGz4dH!KpLI$y{6s_5)L;zfgx0(#uYv4tLL$Ja$L{ z@nt~e$`_rT)&Q@N2NZsM+9 z8gAj^EEBgfK*0N=iHFj%@=hw`*y`R1)O;8L;GaA^|ae0ZcW`6 z_+2~m{8+CN|7Dr-be6?A`zeUJ z^yb?&zHS&2>py8eUxX!w$#SUJv!-I_@ENwZ$R<(p=zplNtP0sH{h70 z$bM{_of|#rudka;Mt1E|j4$atiM=r`q^~vS+^Gg;c1Z*N-`Iiwr4-$fAxX9#GL6Gs}*&VTX_bC*f7n1v%`)T-r`h3AYa zZ{mr+hZ}XUu_n$0lh<(OGt!PszAl9+C$HrdZqTz-$nx#^XUltX!Tha-(PZ2jQD7I0j%wc;}~$EK!_kFh$tcY|;2 zK(JI|XP^uWI`wv~rX9eu>PN<7uL=Vz_Q^8rKzLxK5Ps$p3WwL~gk5Q^0k@J9jdG?{sE?n%vMm=YiLv z51uoSp%R>NfTCmqu7C$f&1*UfnpwOk8h8>?+buPM7VJOM2Ji9ntqNqzN0c0(>nSoP zpTb5@VWX#@y;Ho=Q$FawLEWvLBIEO^My-5mi5lR|57W>1^<)eXlLQ1~AQBC$;H5?F zkaZ#@#7L<~15%hre=X)EAUKH;f_=JVRq`29p4Rpkh))d;y-RYa^r##|XP_o^GmT3h zis90uXDL3p=tR_A?#&q0mGVcVL{S@8JepQo=am_iY2 zT_XwQl-w33Sf*~17$U)4&gK^ig0kVbC3I|}J<}m`t5S})U1ix83dAyOXf|3fc4=a8 zGDZx#X<~3XMhuGbd-5*^-;^6M_ztigm}0P1afQZ$Zo`tIS;U~Gk%W(SB^2cg3t3b7 zv-w`p!x_sYPV5rWz^Y?z4U$4HFbT#zoH>)$8!#4b8+;7?X0z>uE_8~*aIvo^74fVO z5zyug1R~+ASF6dshU~2@<}pSxypfbnB<%ADccNr+T()l=w-MdZT~=O|)@=F}?GKRr zB42nCXQ?KKQj9z~nwZNWK83X3p4B@di4*+DRyd}pT}P)N(Q>8*k%r4ueT@LD#Ki%>hP)&4_aGzSKZOOu z*aYWp*dsyfhq8%abEM*5$cJsN4I_i&j^h+~=vC!3Ao$h7!P2K%-c$OabT+a!3cH*b z9zz-tjT#Z2I#_mK87mjPYS*>U6GOpth6EkK`cv|T__qRrP0z!`HCn=aYee7ltTvP9V?_0P(nR%JV??zm zpUA)JcQ!Yo`k+NrN5$d+M?wIi6oBL;K~TJcZzZ7*g$oLFI7tIAzzw@Q8RnXXh$q=z zb5UZ`e88ADtLT+I=?;t;J*p+!wMHQ!JR3QH(??OUZ>wDMhx>IPI3@ z?9WhI%Ny{393HV=mX5F}^c@ztIKt@^E-{(tVbVM7P$t2%Ta!7mFKUa_;W!Oa|H={B z0Dp+kq2;!N2hfo=@};Oqu$h2jbm)1+WzCy!nO{nBv&#=PJ^&{@E-tg#EkJu%(a5%7n2e-N0NOqu{nOxnZ>*Kkls#5-NlFY z^A{r&726O&lhTCS@)xI#EM)YwTownCP8|&WYkOpQ$PEnTRQ^Dy9(;5bAt}LgcfJtaD4pK^!SnS>G7$taYaZffW|dy zKl>uC_99+g{p^dl#|!)3961-#cySC3J~WT>5EUa64hmWp%X`W@t+GyCGn+1vwkMt`_wdy;71Sq?gN`kbW+( zI;0|G!Mo_nbUAZ@rJ;`NDg>kub&#NfBGK0feL7quiWC9EYG|TGRs(sVYgjqR9JR7x z*0V@u)Eq7cqtmhrE{rUma*__q*sp4LI&u3shVqePC=H-|_UUJ)skg%Q0Kz_gIiQM8 zk*y<@hc9~06|CsV-C4VFQq&{KE@q?D5IW6yp2=f%)x;+?*33%ADVpCTeZ;hv!g5FX za(;`xvAZ-FY5brPx1T{lfXQ*fz-l=kEA+6=4C3MLkhro83Tb5RN8n4c39^Wv{Rn)_ zV@$)7YCz?)r8OEqNuc%Pwb`nO3G7vf*Jk`~M$M4@LPi1hLwvh1bf*b(DL#~n%v`j3 zA-;B^hVIfjd40s?kMn4 zKNmtiqAazQ*Tk;3`w^kN6cK9YpMEGI#@zHn31Q#uIdniX{HUd92<3quG9xQkWrxuk zDwSM|GAHML2pV~!(%vW$LSNGI4LqThD}${((GNjuVg3O;ZloWAW`&5(k1Gj5v(F;^ zs3Z`bz_H_@`HehgJxHV#+C~KThP9aQY(FZAjSPdV5~Zrcl|c`y;@)r=d(~r6Nn0T< zX|eh_I4Y~{!7rV?@XLkeLZ#8UE(8Yp^etCjtemNo@!mPSymWL!<>jLr+LgDkzqZg{ z?aEuL@4}G9ug223&V_l1`#Uf^Jba?*1Xlmf>rfROQ-`7c87~+rH5L~e_2Fhv8pfcCVY=rxupX~6Ov*rWSnqBTD=t^ctT$fg z%;oCALg(lk~lqxPH5Q8i_@K?KYC7f}L6{_C})H}MN`d#?rj&{3q z9Yn|jVc_V`Y0+Nwy>Qd4yP;iupQRX^*i`mfE+S}KeqizK(AUbkfQ8?=V+m5exeJhT z#Da)NDH$2C_BX&6BWqXxJ^T{k?&>m>9=)piZSVstX(av%1_zVGSF_?PAd32X5+1wO zKn8E^c6jXj;IYcK&Sp{@!P{>61;QZp@D@mg6!P?wZEy$032d45bd$ZOSF$caf?*Ae zn^-X>{kHaWBfCFrJjRR1cuzYwI|r0Wm3Il{=LhjSawGD70()~gv0eQk=s~;UcYY4$ z#u>j}@|0l<<9P6B8v$LA|1A(R9-Kqx!r7s&v*udq_sH!aPrn;+W#NN#BRKVl+wQCZ+)-0Re5$`J`psT|=i z#k-`Iv~7m;6wC+(0iT#V9s@$tl%-dsvh;~dEIF**mC6$JW{o9BQV+UPmJ%9@C`;FB zQ{%;JNxuVR11;%y@y9JOTGD6XCQhYlU6?|D&r*y{TplecVGu!i{tWbWt!I*?9;_Z7 z{LU?6s?nez)uWHYr;>W~Dfp!m!`9VL!%fhVehYpeX*CjG&){H^`0H3PYDvGJ@Yp5; z8N4;rlKu!DGqfaJ2vn)3{4lC{w_Q_1&QVdK&Tg`I_D0q#P?R)0ZfC`)C|NuE8g_q+ z@fa_@i+5H#b@@-R_m@yNO2SjQ`bF%`<&<#M{~vk)y3xtcXxY|f+JM7el$46fg6n7p z(hCM)-=SRTFxz06a0@_##H71)aHbW2TkoKAYz~*zk7F(#Fh{ay;8f5G)_GigNGe5< z@q?6SG_mogBlio~9XOH@-vCM1y|WI!0Fsd73%Iuec1ym2Syp)B0`@R}MG;gs_BG0L zT6O*wrTz@QS9_6aY-C->cBgS$zX=-V_&LZo+JprylnNX_B%_hMu6F`tUXC)*E{X(x-*4c@3ONiFCNZ6yZ&2l*mbXVYKmmn zETp{@?2GKic>Zxn-4{VR@GjKA6Bh>N8x1%cVPMV;1{BOo=8Uj+ckFL@x9u$&Pp_E> z;3CG-r$O;mv64aQwOyX9OdjktQp0HS06d$mttQKcLAz`?kRls~?6QFuKbn8paP6ac znacm~?qow8nR`J_TpIBJhJ#7B-ljCKV59cWpz;E1Yd&u~O|mILw$}5gb_6X4vWU0N zOBp6+ZSkm_@tO2fSX(~Qg93?5H&w3h(Bzs`mhg`^^_1ICF3EA?PlAq=wvISx7e22~ z5k8OEg%2-w@-KXD&yDaouyMnNLuhm&EK9}2O3Pl5Fp?!NnW&kl`z@bjO}(K?8JVRN zwSL(xCMo#7FkfE4DnbY+$#h)SD8eTDlcby}Ps*2rU=%Navw*_R$_m*8>%_3VR?M3KXuE9R&~nv`WdiI&KOlpQ0e$E(Zh%wwQ_ia8CCHA(hP zE>zQIfs^)Gpp-HTyum&T@Zuh@Chf@0Ebu3GYm$wW{Ye0=QTR_v&1BvQ*?<<$*_lpx zP-QxNbqb$rPrfAi++^25O=}x#)VQlgb2RY4kcjFsq*-q)!D$9*EbQ4?_)H25&)Zp; z7w^fxBKL4^SokJLu%4vIrI5GJ*2q`*PqH;GBO7IZ{yb*5V4MALDq<5)Q&fEHT{y0` z)Pk59aMxw}oNPe1s-d%hSC*^^HZ`Gb#hI6x7XkK3yj?zB1UH8QSmA)V!&)no3feb| zE(w)H&Hxcr!}f7Pxh(4+CyQlkAWy<>i;t7l#K&nS#*~kf596r7gS>rHm1a8}3HGIoihKWAJnuST3ZA z+n3mjHXARxz>2rPi>&)^FSGl%8jtbf`-QmO3XV#1jRqVtSx5JzF_dw+*@BzOi^T0b zf9M$am?VE4huz_J<#^#>~JWIo8 zQMdd47rYFgetvu}1 zca5C>KCbEvrK|_#?AqVQjk=sE7v%gz{Ljxc(~?I4F^ciFd&(h1$)V31zyffrHCm3dp8 zJNg?8!tqTp7EV3$8NwRQMkrXi{!y)mPdoeE3g z7S{P&{H6L?xWZo$#RQoqu7wp~nCk&zC#&P&P%F$@M^-0=1muFiw^?&nuuK+hhzok+ zBGW`HR~F3Ap;$!vY_~1{RlctB?#lZrAFO=1@(YzuRsNvz`O257SAy-cdIMhWz{`Vp z*^if}@Nxn#CA_?dm$%{NoAB~Jy! zc0*mAqUZj5Ww->{=7qQzHAME)JU>%2G&v)Vv@My56#Q1u+3HYk2bphw^ zvJ_(z(O}OXUop&83S3g!VS1IXgAWA0`B3+Ka~Zq6&^dqk-i~<@_876K8DJ~|Rhh`V z#m-V`apx~V+&KiILXw{EK)UgbgC2z*eL^34QnoT7Z3hO6@MmH1)wgk>^mh2!i9=HL z9dH*yoxhe>)&$)3Oc)d90=Wb?ifzW2{ExdSFT<7@x2kehwtR(9k=rA9^UiLQg{3w1x&oNK+TcO)1 zKnKNs@4R1kYtBS{(pzi<=T-Fr&XZthVUm+#`w2$3`OT-@dKnU7EriTMIbf1;watzz zUi^FXh^^fs<$bX?6=0?#c!+bFrUgHJMls>i`6CF88 z>)CpO*=KqiW*4}BV;n-1{lQknZx+QI%0-)Po-JU;578dXsbMy?BQ9pVI4_+^6u(HC zh#wSB88{Wi3dB>Od#4?`11ZqG%l;TIe%(5Q?jQFybpHY@v_~2N`2+&YNKgu-qyxw% zH`P>ttB-axr&6myRAK`=SxVU^pvfh-VxRzX793R4C<>0)WEbZ5+M)eG3P;?>?%!Yt zb6$KC=Ljvo(>hoT{Z(&s#%bNhl#6(Z1P`&}wA|+D2}rLQ%4c>|rpg6E+*AC9cz}$n z(P#`3;a>@^&>Wm8fZSmRkDW>$1=Uk9-Km)?fuDJv`$ z_=VWv;8dZ=bF)fBVr7QNI4I(&6iiO0K(T0t;_ehE4%?x~i`O?4w;b$gb5SZ3L+oUL zB34d>;r}q(>1{B7uNBN}A(Vn5<;3nC;`q!7j4%w^OO*4q zQIgQr)iA)NZmWbkx?wyaQR< zSWWMo-)uD@=OIlo4jKpDC8tq`z~P2d!98C14bpmq-C%64y%^gD)8Z_cyPXo`@2a>< zOJ3bM3%MLv?WW^Gj({_sE;!^lP%{x7&kzB47@U#EuVeeHHPc?Z9fpy<^*Qu)6z3LYS_{6> zF3`U!MWDZl-7gq6GG2U8=qjjs6z;dp z_7XVZTG7h&bd7fq@x@A`aa!N-Yt)~{GX6x2sWz?`{9H`QB#OFY648yur1zM5fijW#Ln7NSU%3SHU4^0i+H><)iEe;3NXG zmJcUNfNK(FLs6WRG={ZU(8JD&d5QCFR>sEu@d*#(Jl4U?@j_1n@#HKRqJ+B;OL&qb zYSlzIAr`owhN3;bn$b->1bKBZnuARVazxo&mporpm~1z_kI%82Z13Sz@XW|hw=wo$Ik^^C0N9!Vj6nyvV540S&hHm}m;gn%n zqs&?YGaf8a9XO4+GD*jZ|wFYMd3V=L!Ko%MA`cU1zYrd{BE zBD>D|w%`r9CB-HQ{Q!CGc2(niJR{OSa*ur7dF z@u~qjNMeUkVwJz!jB?bt+GIVQ8hotlNB@YB8KzjGbanfap6XK(|0?Ar z7wHI^@Ff@R=9kd6@&T9JUn`+ox$(SVeN-{r_n&CviJE=?3D~1gT4yPnF@66DW#PDr z{u2oTB$R7Cw*$^a4mb{hFE%&^L7qsdyx4ZZRb4Lp?Ecn_;(x~dZ4yVezcdE4^{riK z(h&b*tzB$s%#s;?J1j3TKOsx6wUH9X8HotLJ)arcvL!bB=A0#H!$gTMtZ5RqWtQ!V z5%i|%Xb^TMz5O2qrf&{F<2D#EP$gt=CT_Ja(f2W!@S(seA?=GqMBX%UL&R=ZkGqb9)qQEK@f( z*}Hia>koM9XspAFcgA&--``??>{{MUZDaZOvEMyBfBYHtzqgpL`n%9)%oKP(8KD$# z=r&8SN6M14xxGxKKj>j93AYn@s9(5N4zuL(P0efsWokW%GThJpF@ysM3rIJ9y-<~S za7vmU7)U+vpKydZAAcz4dZ38Bsk&!eOw>$$_c}kAR~=CA??NZIH2R%u&{)lzKN{dx_ySHYLT)YFHpuJakNy*PdEOI$WquYkdhipT1e zPz;%%H*%0^7~E1NyqXw%krkt^cTK`$kO^9YE^lo+Jaz*-X6Sn4^h|w}PS4b}&GxQs zWqknsPJ0J0#_WF9uJQXf*&o9+KdfsosoaWv?9ut)9_)MXFjnw`@WG35nX)eMv8zY%as1FT%|?BqUaGar zUP1Z2$jgdT$EHqAop|=y9h#2}z{ztL#m>x8zs@~EqsR(^Mv#oG>+Q(8ffe5shb(^o zHUn9_7#-!;1+pINQDi-{LS#KNb8Kq*_}Dn&#({()jljgzjmC-uGd0u9w#7cPy~d8O zn^`f&7+EFPP3%5oYSt!1UW^msy1>^nJ&Lbc@HOD4k3)(Ei0=&Am_ON>hyM{n;Hw7- zK76<4zXbk*jXD!wrdu5C9ZP>WCH8{mMJ44jt0)X+QSq08!CUMYygdbjx7snti`NYX zpYKr&{xM)sIVhd5M;>Ju6)_()7M;zhfKWKfglH%lX=w5ib!$$>?5 zm!1JHPx={#Igf_W}$LV<3LG&y>8ZlY_)?LTIOO!X0)_7)arSJMEmni`NY& zlzWsDKAVXXw7I=|9?%{q)nJ9_$H1W&@H35r`8pWrzcUW~G&iKc{$4xmccsAoK0EAr z@w$Qii@gc^lamm21jv8Vo-4{!OZ4PW-Q%Bd#SGcNw_frf35I1h zX=*Dvm9fw>NSi$Lsv#qV6(chV-n!s;HAt>y?U2UWRalZvFO_Hskk}EOk0Y;F+X;Kg zyYVvdI6i~_T9u%2*1@wmCLn9(f?FF67TSwo6d6CqQyCTp*zhm5n=OcYbUYvOeFe6W zCs|Ws-ipE1!**QlPr(&`46N7c7Q#W0xo@%=(@ZDWGVuvPZD5c{qkh1-*%5Hl!( z0*%8ZD$huLv5T|q09y7ONHe3&6v_649e7Ws0FOUrN;Y2nS^+#YA8;>Awh#6w)V|q} zY;hooQKt!VS?na3e{QVix!hLG65#?UTWA8JR;+7D5|5}Qr-{eIc9b4TK`DRiwT5`) z#g0Jf^&lQq)w|aP<4^V|S`Tuxsx)X*U?@@TY%M^wYc(9!|b+N6PgcYk8TP?$3mqJ9+B3u!Jln$HnflD!nud|B5 zo5ZxQWV%jX=T6#z`g{sdr`dh*me$rIym&y!!_6QMX{P9PLHJv}iPV!PA*I#u&YjFQ zv(tHs=U4|zTFdq8p2$0uoNb-;Fo zT}e|x-e70BQz0=sW|hVf!)TG|RMH^&YO?{%j8@Jw(=sI1*zv%P zcH~1iM;F9tHc)tPJdkos95S)$TM%Ge}w>jWx$??It|-0B&Hh-{F@B-u%D=w#qz<=3{cVE2a{{n^hv9M_>EEg$Fd)%Y=Nr7LsvW zZ=&j?_m%PpEj=9z52oO7viq0wBsGdZ@oM-MUsS8AROWuAtUWN`t-2qfYc#)ycif{UhTLT$};LH8JlvvKh3cYV(eF$v&LZLsul?0a_5`X$k`V=Lnp zzO3Q1Gnl{lX1t1LVVWSv4?YWOPxF{w@vNBYslM6}k<7{%9N}SajJ-2yo{+xL2my7y zmDDe0R)B7qtdPt?Kx!Ix(mL@yU-k7L!Cw>+^skLX9MO2C(gb%P5Lk^;C#)-LS+96jLk{FH|wTn#``YH4x`A zlp*&*bsStyMHKCOa+g&hTY9x`%uiYc%&c~Dt&%6Oq-vB+1I~YA`82Mfvs@P4y1U>7 zCP81x2SKw@@okrxib%T2RV5v#*ENrC*dXb(iM_$3)cMOsscggvE^XmFI=Kg#u47P zq7mk!yHg$AjV!&8j^xvqaD1~3ZuwFSE{^O-_an=Pb+0z8i?AnY!gl`B3~RO#J+;Oo zii0`S)nKx5#2hh@<7-RcH5=^!4)HQaap}^G;;_srF}~^Ke%qt2|2?ve*!Bo*bVY9h#UrVvg{oQY7RE4~IR;GSq9XNZ2~owIX3@ zga#j?m8L;dwDI`mr}3BS<8YO0t1Fv9$s~xo=8(@OGv2s%7)NnY$O>vrQfzE_&6YW0 zmq66A*Ji=%T8diMi*7_MJ)ww!%cTKeKZbd7!wm9uEj0k8E8S=S;bY2hULJK-c4Zi~ zu>vq8`&|gafT((|zV}Q%lqC%(3YxmC5ThR2`36&mL z=CcrNE$!Z2ceoq7cejlzWV~F5!*tnYN*J4sifef|%*uM*&EYUBdIQmu8w*|+dJU?F ziq5iyLFG(5Crmzag-j0q3$P3m+gLxj<}0q@m3*~4^+Z2L7C%&;6+@rlb$)E^)@p0i z^@YxbtP8-k>4AjQWjQ(Kw*Y={a?EG&M_l&s--Mg6R;sFX!GQ2tOR+tBcn{Ulo#SMF4o04illgV{rIRFVtDl4#kZJrk;0NMV!|Qg2Z3C~94`0E` zG1d6*Bs{mtz&URY1IK?K9kbaBeK=~AbiV|tEjV|RN8xfJ zp6;^ai5KsP!xO&`3CcAX^5Vie#MA%keLUSK@MQ5Zm2feEbT!21o>;|*I| zJB)U+V$2q8m74?XKAdxFOtXT|80L`JpF0NFD8syQK=nKnWjf zM2r$TrAjLB@WqUQc9~XCj^dS&Q z{JL8!%)5}z(Q!&kyAIjel@~vQtuzOGr z2*ts;wHl3xmgQ7?g1uOO>{+)dqFQz+0#tua^d#TUvlsyl{pk%7iQpArSHIH*l z6F`sJ1<+F|0_dP!0P*6-*0BJ(r}qWW_RIn(1zPJ*3XQCq6f*b_g3rWe3voG@x<)N7 z%?2G+V4>K>f0_t{6w`Wx1>*&=l5UqScg7-;&XhZQV&u;AkV0C_8WW34H4hU`(_tlP zLTAD*bl#jIbe^#b9bSB39Sfb^y)SegyE3cRlZ2~PkT@l-6M-(WruvWh9+e>z^q@Vf zCV^7)ASP6}Sm{<>8X1I)NKCgBqs`ry(r!BaJf;q$!mLRjJIC zy~AG3KZC2#*~VBS2;hvK=TM1G6USd=7svG!aeT@yj(IWcDUq#xJ=%<(=zVegRmnPF z8lW=D;RHFich%%vo_L$-c4d!bZ<+oIm$j|nyz{aYNhp7{iei;pIJL2MUh+9(nkhK5t3XnL?-QcBB#c+8YPfpRO!feJdpu}BcvKSx!&7u0CBu8j z-iI4r6W9HCq)-7i4~8D^cIBylPB>DniraErK3ZoX1unDVl_42tnfm3i)XQ*(+wRKp z3qonfpEnGG%2KF66m&m}&-z~|X^VdUxa^vNLnj91630hF1wU{g$3cw!YZ=$3)S2rjVu zQ#&uoN311gGIQ>^7w4WQ-KJlnsfKglk#@a=N2|gatF7~C)6T!MPdne6GVOHi(+)3w za2-!OZ|ePN=WFdcavG3UI^!TnX{H(42fDG8D%s`_l;8L-u68TjDp5sNnqXzp(b#EW zZ<6m)1#C>)pi%}t$oDewpHOryGhrz9*oW}l8RG6i)J&CTV%`N`x)k#wWf064!Hh@K zgz_}BANf+fKYC?Zn|dogj&y7>(MMWL(F*YWR$)ftINQK2?}_pxd@;-@lMqJE(#)p` zvk%yX*^j3Pv+uJDGhV!&g_+;`!t6Kf!YmC)7ca~nQWpAS+-4}0T*ZSTVo&5dBngkK zY+u^68penbVq#NLC5l6OqFY0%*hMybh06oO9GXeK$V!T22Th5{+|}2Jht}|ebm_`P z>xvDc_gf4Mn89}j7HXj4y_9KZ>L=`y@slZ%@xyk>$cu;8v6^?b_a)<}GFcZaP!h7y z=6M5a!CBDfpbD&dg}CE}3b5hT@eE#AZ7z8MEMmY9g&+zC#NcAM>?s9jVVBi%pN zQ~@H+cyPjtzkptDY-PzD{j4>!xb5LV#6wa$wK?P|zI$Qii*Vo@J2rXz+3^$yz80L$ zk>V(I!Y@GKJTRyaraD~!k%e^eoK}O*qLYN-wr^|=-gKhn*P6v+ zPd+m~HhXGv^!V}d>EhYIZ+WkGYi)0|$!Zrw-)y=k>8IFS#rdrz`dh-A%>Gjr|2M)YEkoAeve!UD2ym^3mWe&B;C-lt&)PNif7^=e|tQ|bw zNZvCjpyS^@bYM6x)5y_h51KSCQqK3tI0+?RWw1ykJ`Y7q)QTp7Gf!j!91C zpR&%1liPVTPOMk?Z`!9tUMzwW)}zt=>wAA%eD8HuqkAHZ#EjVMZmCP0SCm;VT)Hco zb@oMq6n3yihr`zv!MqM4tO+y2?p^SBqVDo|rzSYiB}S8BH!0Zcn*{1O;N z`QK1W+-ept11|u2$8k8FtPs9Y5h%2@I@~y0f0p(XL4F6Y=D_VmrZDLD3%lbV zEjQ|3!jszDI*oOK77coN8!gob;yUE5h#1`!N@)-DokKqMx_n=Gf{@e{c!AY>J;N$Y_{hX z{TAOJd0BoF*1>;KqdhBkz{^;Sa?w5OLKDiUiaPjHengdhL=`^5fX1UvHTo558&-{v zZ)dDbCsbR<-;1asVaNuf&=8%nBS!B*XMhT-A!*F##owg3WQ+a~kBFZ|rgy|ggFJt1 z`sl>eQwZ-2B6Bel%{pknf-U>qyTHr^=81As^O*=0 zs!F?6Hf8{&C$b)OWY!B7VQRtM`@%kZmxMYjJP4Y+ebJwN*6zSK9jn8Hg?=(jrID&= zIMa$IVet%R9})Q}XBg;|I_}|wF+3vaxYN|-#H8dw059-utI+~o)H}BXKD=NR6pRf` zgYmFd3nzP2S4B0!=uiuzI&g8JDE>HI?QkM54)t4Y$2G3a*x->t`OO0C12mGUqiC6W z2Vv}6@}tGE*#sPH+%#|8P74*-0aXS-c%^e^LVKE#$ve3P!^q+>xi7`CAs@U=515zb zd%4NKvaR}g@GE>_8M9ASSwaXTAoVygA;jceybqg=SBUa)c!e#NUp{zm%!6V(qOuK= zjJyX86}_KiA237nBjA}cDDc?!poJ@FD!?Nced7K zBx*9soEVgea@^IoqD)k943vrTost9A56XVH_fd9_K-rl_4d&09hkHxtju-;Q#L^bL zMG9pylYMVKXTz7)fkoxiA#3CxP{oy$_|kBT!*5*!$4>n0{&=Dt-K{PoCFC;)v&x z;BaDEv}xFtlP8#~z=H?Flt=63r@;SSu_fU<=)loX5TYJV=FLz?)@q4GHZ7#XI;MJh z9c=xfK2ADcifi8F&KSfGrXgNDc$0y6QGS;|{3hoB8W5M)&$RsM-Us{yhc_F`LsSBo|uNZ^AU1{hO58h&+Pn6#y(6zH!NwC>DzQ`uil#X3s z(-bB>1rVa;6`4$=!BVu6uq2MJ%OH!Qki$_{?P(o0j6Yg6%_Pz!w>+$Tu8FkBh_O=n zL6p0&-1gRiLG(|0K1=S&cRjda1CtfPt{*+g0AvACO|Y=NrWU}=CJgZaV9;}6FKL0G za}<18!`xdu2(O=7b!}3sGRhr7H+iBVE$z`$RS;2;`Lkk|Es|dqx6~SE`E|p&KQ|H~*@Q_y!5o?~@gGfkEH-cjuFMwaMik%FsA5@7 zsYZmu&S?GznZp1hPcoCOJwKgl2~&Egy;*)jd=vDA6v!bvZ^#+Nd4VePZCj(3bj6bI zR60e&){Xm>?2Gm}nFlhRg}4lj%`%r!v~snd(Fq1dNdnzZ3I=f2vT4EiF#cH|Nx@RZ zG{@K&Xyw2Z&tdw)Ff-Un0j8$y>F08SAs3FOQ$LqeGt8<#sLQO? z&E*s;GWvOVQ1^j(B#ER~RqXQk5?YK1HzR6mCb_-v>+(^T9pG=4dk5tj(y8#$U}C53ZDIusMii5X$U zL;2025{qeLrSc3U7Z9lqnA^QHr1Z02@I)6F28l@+SkN#pv}Mak3s_8wDN2bUibfE|vGVWR!q1(ew(=SfilnRB9_yFle^{M}ragkwcIO3d;^EcHE1QwIhC zvsLl8w!H7>F2G^A#MxN-xeFkPCwQVjX2yQ*0v}E->gO&XE@X>S;eOb6iEhu;z$d&H z!ulBHkch7~m*}lqFD!rCg$-A1z<&^4@pIr@3|`WU)t|&)AhSiK(Yb}DZOukJ3;P$# zl@}{#DrKy54lgeq-B5Y?=!SOXE%X;e_`;u8wA+=pRYj0jVc`|H&0$gih8Eqr3+WSv zLgiw()Cj!cW>6Z&-IZZ_#Bbo^+LQdjVe>)zM;5V}kY&#vgECW#_u4R|7P`R5dNy ztKJAV&AJ=f)oqqyY+_T{gAB~IWk^=Q)2j3+mT!O$tn{%gmvBsSBng0a#qaD{g3Q(+ zbDZojW@!>YnqC1TPAOcjfnQ<;YV|s}d-SU6_3#66sp0cphGhevlLJ?-+m8FD}_zrb=DQD1LGVLA)@MVkD_a8K&&CVOXhvX?^g zC5@Smuwu+AZSCuW?EVeLW4!o2-d8QN!vJ=^x7o3XpU`vcSiK9n4SaEO=O#6qG*_Tx z#D@hs!f66`p84s87^QQraL}*g!%q%c2iE!J+fU?vXtfQ*d0y+o8St9ux9YdfEd|LYxhqY{kV#vbnNH%DATmdHDjYf;9!)OMQ zg{|=2^_cO+4J7q2gXmjg&NG;(103ol@7O#6nbksPJW!hXfg(g}7~vU~l~K8c--DRe z27o}WR4Q}SQ~NujEr-0KdE|&=EbwVO`2dee(EFv{i2#Rto0+X(bY5IL>|WNE#sFiW zV8(Y#sgSkJ84@v&6B$_)&Rn;DuUQLPmk<)@0~U=!K-P-h(#S$bi7+Hy2DmA2PXrw> zvgI2Erz=TJNWFL+^7JH}Uki#=R~p9Cb`8Tz(J&_L8U`=MbY!Rvtv3zhU~g*}Kai+l z*wM2xC4;vmIgSY*5=>n=7I;`9=n zu9+xh&GNs8VzeHa(^`(6ka}XyT1z#Bk8wn#r)VmCI1-czUZX30g;9f!PSOaH@EUx? zSvNWhr$+Z`3x814TzHu_7m6~-gUo;JUWMTgq85WXOH^gBREXfqP-v7#Y&uQkb{FlT z^e}Y~r9F1Li}p~qG*PMXUrK6MuLkW$*xNec%UZcmYQ#8E(g|IC%c9rt$GcN)D2N7u z#T3I6ba-0S$dqPES+FZQucRnC6}zItiyvC2iq12=t?2yu)v2ZwT8hMDX=Q4U6{V7c z!@C)bEun{dxPS~BP&zJkDq;!l6k@HJq+hindXGC-vgti>%1qgtw^hfLUb`7@lM%-W zwNg&1l!JIFbs-wj$V`x;39&C$&UlP5-e~nMUMm7}Wz&k#Muh*8>_n6-4Ad^9O04yX zWfYsQxeJ<>AqW(jNP5XY6q#eXSG}yfrwx&{hNhOWZU~N+HrRGkVf^kDHEJ(jO;cW~ zR*Eoyp{mJNZHk)FW;Pp?Y*Dc>>B@%DtWqy?Z&?#@#wgpz0 zE}radb@974r>TqSXj-|#XlYwY0<>ct#FgX0Cmw&?S%P2i$RfLSYZiC1pa#Jn&a48X zQyn5K6j3GK6xL$)H&M>bUZM>$P=WSB4U0Mu?#K#YelG<+?KDCebqx+BYS(JY3u72D zR%I10QJA(nrS;tTmuy;SoYLApbJkW}3_wBrVlfn6UMn5Vkjg+ODRZY`Pl!xt;%m}% zQ={g^Yo|b$)zePN6ezn0R@8*1MYZ)~5{%&~+nh@%N!(-_jn0x0P1RCI(^bWNdK*Y5 z(pE*G!pWR zkCXB|x*(d?Q*qn&K2{&zBm?l^uORYwLYYdotV0gn|=nKlcLLv1kn8IZSA_a~$@d#Sx*;isd(stLi&w{Nt zJn^MF|3BfHMdPH*v9{3yCq+o1H(XfB!&z(2ZF(AiYs03|LV1#LAaJ)&8(kkT` zr#g%NLZ#)*0rKEJVU6!QpRM>XCccJF5N1>l=K-G`VW-FmdZXzyw-;5ZX&Jsi>MLfx z*0K);D~6n)m_4G^$?KdRc69VL%#7`qqLM+H7u$iOzh4 z-?BZ`WZTPn59M~5SA721^wEi_r)-{nV&`Svtu?)@20k7|VGjCP5E>R|T{+5TeW4qe zMU*mzc+5QRtrL~hNgd>=5Lrxe8tutmU6fj5G^$L>Ny-gUe}Y*fZ6B4O z^x%`+h$kktq!)RZ{UV_`y6?>UUM+L*gtViwaZk6R;;hBqgO8$JP4D>;L|y^4N{V!W zh>m7q;LQW1-dVa{Z?wp&kC7i#C+xm#7-)Yu3~iwK&>)noYLsj9uX!(g6JbI{1N~Etv(8zmq(Fl-4ooOIEm)F(E>Svtw;Tz5x<_*#EG9wa2ild#5l0{c zRqe#m6RM^jGRnSHXr+2{5g-^rDiL`s>`i(zcFmTDtVG&~m~|sH!jERR#HUMfYIq;$ zy)nVf_2FhHqfpz$X@uD;<5w3FJTP2IaBF z;u2^vbAHWlolntZCU4*>i;@3h^6nH>X7WZ}!N>q2%h#(T;gL*0#&q70XGY!S3Z7DUm#A=W*1I+?{_$IXPWyc{qe z;kQ;|K@8(?EUy#`HH?$6+_w4%Lb^7xM~FTeqYhnd@(oWp#D*w=31bN|0zwJ}X`Zm2 zQz71<4cwq35|cE;rMV%T`TPZO(vZ>vj95+BIK=Hf`rWcKD?6$zLXAEaQx~4L2Vx|2 zc6*67PB@`)Lk$=DTyV?N%zHEs*}c6j_1^Kwh7H3zcW!ldg5SX6Qs7m*dec7xYyztr zTIMu)vKCtY4~O$Qi!5r)J8rFJvA;Xg)nq3*9cWrt=!t?G+wjJA;PIUkW+bG|6WsR) zWnX|3(%u!JpTk>udM0`S6OLkaLwzFk=4EEQIfQ=@i}Z6kIsKeYxNb~X2hy_AzF%Np z1@?0~ai@S#Ebw_T(Lk=5Oi+i>O#D>9U75T0TgInxvWa@;s&^s_55t8-)Wr=6(|tt0 zm$juanTW)QNE6K|6|%Nz+Q0QUJ<+gP3tE>-gD`COh(EB{j8Wm1)%h2dsOWB!Fw8`b zmp11M?n|W2GzEFeTz6>zd0UbS{BLRb6Mr*{AO9<9!@r-Q=Mp6nBTnt_ zFe_(!i!i_&>shsJSp`~@!b`|F>og~8MVD%tW~QwA076-US)vUx@JBvYnb56Jw(*S8 zo$fg2B3Gr@K}%HYs!jKnwk3u7`7x4~&pc|d)-W?|I4c-^9( zALID(@o6}{Xm!&3TSSIUVy0^umWCpFhNX@^O>kIgx`ySU?(NJe+J<;Xq*`RU=|cXgm*|a1XPfINV;W1IATd*W_xwzC!>iJi+;j( zjjT>9`7n8y-BH*v02tBbvGrhzh=!WFOa(= zF4H;3nuMY=t4?bF=6tU9QZ9{SBLDYs9#GHZginKb%+%;xZ;0Re9)# z6JsJCVR3jBgcEnUj(^e~THO8WT?7moA&LD!Pzbdw_5(rJb0Fwe2nAiNUIBrWZ4Z9= z$;bIGKZ?J&Q1boam+uh2yj%S8b>f$o#4k(YmwEi9`ZQdj)eFmoN~5!})Cj!g%8S+U zuh_7m^3_;4*Lh*yuXzWChle3LIA{)^cIW(HxY=k2B@fL55L+=kIx*~yxRAxH>Xlmf z>rmA_;|{~Y!hzcyhTPX}Z)nl2gO7AD6i5|&n7yrxuQ`X8myT|zynJ*+yYd$GuXe@j zynd}-zRlM!{s>-v3NIhW%ct@3M|k;5y!u09Bt zZFso{FT3&b1YRcb@+Q2L@p1+)Z^KImFW(6luljyy;g#iAmOJA>fip0M5N*ELs1G-T z((oeWdl{zD_8U0z!>r0MS!|law3K`f2~fmJL%8Bu?l`-4ySsKh^2nZDd%V)F^2na@ zes|Zyd)@NG`}XbG`^c`+@(as_>f3IH9(+4qK7g11gqM%t<(KjDS-gA>FJHjRzu;x_ zHn?oV%RP7*#LIrX9Kp*hUgq%9!pl4G@-2AzUcCG$UOs}CU%|`o;N^edAvo4cf4Sd%9WJi4L!3;5%XcDr+3 z)2o3*g2?D;(O&hx!cDU-$iZK<6k`*c${wUtsx4QxRecybeDXB>>|9u}R9ua5U#>1f zC#pXSovzlgJ7>BVkgL;=C4BqgRcWhTZ9!uoX3xNnsWuMvN8kfH*M&k1fwx@!FHm~) zs_Mr$$ux>?XVf<+I@!2_m0tmw@_s4dxoZt1@aArZ$9@$at8D9RE>-+mIq>Qn(yWhL z3Bn|3dH*)s`*$bn2V~9B5XXxL;=0H0-)evCZrME=yXvQ)t2u=0|BKza{BW&)68a&) z7Q*bB$%(0n$^9veNJOvfE|I_^rLBY(`KBQGBA4Lbf^uIaeRultb1jFWGZoelTc$@efT z#$2jaQNLG^R~Ny&_-0PN&ed=#%bA8>X7f>AW$M4>nv8d&N(uW(%@#W+=dd^Lwree# zCujVZ;AD?v3h%d5_^}iUAFxxH7YhdM5ei>3n&(-Q5+NxR9&0ySjm69ae!@=R!zlzl zWG65$F7_4+-K4vHF^C_e~ZYM1-F7y`j?#VT2@8zUDRu8E@={9q!IZIb|Rlj zA@UpTMCQdKy+!0hxhC@cNaXSQi)iH~Q^|2SW650rYkPCHL39UeMH-o1JDD3PWS+B= znHTTvEizB$pUjh>J^M>FFJ$NDv{&*VBPCHD&QwfRs9&^``IQth2X->^;vOUO8*@!& z64@bn4|~Md6ZNvU1ZHkX?OmhP+++idSr@<4PUP=SA@aNIMCQdkCbAcDO=LPKd#u)I zdJ~I_UfG9Dd@i(cO3p5|-*2b%M^h;M0XwC6F&>nS#_k?jAGdN%>Fws^{5)h3CdxzN z%w$>pF*|X8J%zYGYbP!*-q~Bk{hC}8mt-}U&@}&s#2tmhycd_)F$zht`jd7d|8@$I zf5T2>Ufg4{`rWxEasfHmPUI)st*7DKF?1qL)x3V!PUt^NA@uLr3C)Wi>Md@5U#un`Y-In{Xz{lQ!l_q8S$Pc@!*)3=!Z#!lETr4aUucEa-F z`+JLPKb&jAQU)oNS83WM`(-u~|C61>Urr(MKif&ni+A-FiGLy2B)-Mu-7K8@uU4{7 zZsm?ZvAe_nW~VDJzPGpN`l(#g^*W?0#yB2s)YG%Jgt>%%MyO5$MPwt4J@7^@}7Gvlu%6Ie@6Mre!^u0^c zS6U~>YMvW3v+N2_J`_XaqiHmLG=|2ae5ki*eC3mQ_S@MO(zxs`E;U+oXlSBAa&j_; zvThn>r(-B9%J1nd%HEJ`%96HB(mC8kN}YI1F{G`hk#-@5w4%Jn6y`hfNLnw4zWkrg zmES3g7^(hu?9`v)<%@Xv-+0-IQ|+yIxfd_H@$xucj^gDcUOc=6czGLMI(Yd`y!;2e z{AaxU99}+&m*25-GrguO5Q#vuKvLUxMJPlpawS*3{}E zZCS*~C;?d`@X?*u#&u@7x)<(4Xx2VXT8;8IF_f5;|L?3ABeNb$cnl)5G{ErI?t{lh z;W1le7IpR-@}J;b#5=gz-ob6GCtzXF-o%S>32E&hzkj#=G1OXF2U%>^Q_xvmp63kL zGuT0j(#i?0tK-lkfklefBEnwX^jasDaK%ZW+hnKdt#+EejuoR7&`Q(Svio2&(xAYL zw{e=99um2y?WrE2?TxrP87-Hmyt8u6nMCF7b}HYOLghQ`ROZEZ^(vKXJwoOCIF-lG z`OOxbW=*8FxF;u3{sBAXM^Y$Xuv4BF@99;_pYIXM(;D?DIPzYle3~fVYbW(%DWu+K zCp9m|^(juzJtxXv=@C-jhD;8mo`O9dq&C=@GKusj?9?7hq4ps=wR!Oay-MwG?Gb7d zV?!)aSByPwC-rm+sgKx6&5L`Mu|Lowq$b7&Qg_SPvvz8qOriD(JGFUn&ocIZ>Je&_ z{}fr3yJGD(+KKI@5c^GbV)J73pX$ju`y)L-*LjH=K{Ji*) zUM2r8_Xzn(va@L5V*yI|jH~dvo!aM8sNJwrn-}-2WdCfBQ2S0|b~xU!Ax&_CWXTeMh+Enqe=`4dD4gdqt@1}2k@8A2FBHbNK(gdzM1A*XxZdJW*wJepT{^@sGee3S`+;h%7_uO;%;y;(m{mq;#cXWr(!e|StU#{r)|7lD9 zVf&JQF=NSp-M-{1Ik!9fU(W9(k5~$vXE#gJ=jr4fc_RHyF29i7dzfIE;3&as2;M^Q zZi4?r@F9ZVBlsM_UlM$s;CleV0$_lD@iItS0K9}8wxvAtn$CNfZgaTmHX1E>M;1~41J>tE3TuGp@)vwrS{<-ngO9WZF!SPi?>b8|t4MOd`Xg($1~@yb zp{xR)jbX^!)7P)wD&TDOtnoxBxDTAA+B?flR^QWrtAF?$aT3w%2TsQwygmcdb?O=` zj-OtB7Cyq5;A({#A;fvwvOuyZ;@lyVX-x1uTd}L;cNi0d@J%Jt{p!sq7BMCuwu+8k zh4BTOk@pp%idBE?iPbsy~Fsb|1FVQAh?FBg&i zuyBMddV3w?{DraSSv0IGG_YOvTIL`G=05Hep*tGuaV!M8!4uedHpM9I^qa& zD`q)tz;T|EqA7M%7;>Cvq-gn8^h}bC2j2OF4IA#-3#azYO4;)x%P??HqqBe@G{Oa^ zW5*B?peTYVj>p&75^4M>BXfKA%^K zjEF#+Tb)Lu-fSZT#PLZPu1{+x`} zpH#Qee?RQ4a|>cvb*fe8PCZc^UhsLVnapQcu|9xMNSEBg>P8Tio|0QDdDXS=)f?=4 z_4yfl^+x+%RmnHyeXq{tVXyvuS9{fpAR5(7qHb)I-nDO4L>&ZHPs5vvzhPw$Hj90m zx>JetGNfbEVQ&3@8T-xns_DZfv(G^cN4Z-iH0mj;sj%&A$E&s=E5%7q#&}jihuU=X z=THbpp*rzFgo#c-0z7HI*q;~nw=EA_>f=w^u)!*(g(&|;23c>$nA3T|AcIJ{>`wMN zT_ld0$UZKJqrXc}{=n&uD+%Q7c_EO4c_5I_xCG||rS@ux=~m~0(81P;9Lx>L9Fd4S z&6;3?p&~pB`^(qe=Dm0d;XL8zF+PP{B(h%f0+vAiU3yXqt0zfnL&3PtQw2;R4+QoQ z3G75e#yyAg(YaE+;gzqrYkF~K2ae!DdZ}oK76f+{J-t^b>RYNJu5wtpsP85~R>g{4 zq}ms@?&u=bzDTtxENH5YFt%^|_Zy%>!jF1Ux-66lLZLoMTKPf%ae;aj|Dqth9v7$! zh(3g1krRUxb0U1|JSbTx;O6Ma$Y8%a>J5&M;0ASQOBsPIM~C`Hw~PGcVNXCoIY5;RR~qYN!j;BN)cHxwS~?**aXHCf9yO7pP54 z7cNk3wL^K2kGMeXD9i{U&R&UR5yY9f<5HQ<8`P7wa&z+gwGo6<$uB^$SD{$s231`4 ziLOSXEQ%gJ-QL6fvL)P~hEV%Knanr1)()y-PNbMhrsa|BpggU=9-Tcegh|^93M!@c z#c6i*oZRN6^o;vr`L*bo!r{*}n(!oqz`SJ#VSC%SzFVx++^R!Wx?{%IbOIH7QOKF# zmA$^8PqtC4l-1RnP~nHasp~Fi{d-zos|B%j^ReTPKs;qh{ZnKYIxTVU#tzKMKlOjT z;@tk7Gt=|)Q@eU21s5p!7J7Yy1#$f)(D%y8i9M53I!0(3_P)f9y$3U}R~5S{g1su4 zZq9Ovy+54??EPG$SvdkTXbbKd%ibmG#x9}4xOvVn_&Q@K2lwoqKRCC4-@d)G*aeDT zBv515j?Z2OKC5C;d{)T}HIYkv{-r$N^EbP~=P(ki0lO_%lIIDoUq3gsXV*a;5_lEJ ze#nmOhcl2}6^kOfO6I$zTq66g=KEYnIKLp8!kt!yTW3Xnj}xJKv2dt8D-{yfq|&3+^j>% zt819WA4Nn_-!mT(j2mtfkxpo^Fb})CefUc%h8BpT_fJnw%uny# zbMRhR=%r(C(~i9=c?xJ~Jq)VvH%G8nB{Pg{F0uEI@_@Y)>aHMk5}a!{;pxYTEk8@OZXe7r6}~*ht~_Yl@kJ%m;j9&3)OVN!hjb1qnfBqi z#FsDU0bgzosGZarRBCM}SZ@n9Tvn=9VC|cZ2^~8osO0ODFhPBXIeQ2bRPuFs#e{F= z0TZs(vfQLtUq>#p1p%H_!-ZuVZ|KQnEUovullO^MGM*?c2Cfo@+Kb zaAk+b3Z8>IWr+a}(T^zLwChd>M*WL*ye0r*iC8#J!7CmrZgP5f#K9w^9{l%i{_ZWR z8vDfW0Eb<5-=Y@Ov16Ns<~`HQxxbl-lCQF( zmr_x3|Lpx?G+Ya^2^6y6*)6!cSgaiCG(DdR7NqQ@Zt^ln;iGoEdxK2gorHJl`wbBm ztCH!RTi{*fwPtSacL(!;b#F+;I*AvcdlovFiGWAyl`>z6rDB#N^hsXRb~P)=t-Rc= zH@z9Jy;Lu^=GV&mA>2QkC<7T!Ons+Rg3>&Ta=gP%Io_2)Io@ff94c9>8l!%xbHmT}!i`iiBc0}2MtUd@IJHOX ztAs=oTJvskh+Z3#@#t!^PI>CjkhtGt$EIJ(z$R5JicKmx_t>>a0nqq9{HrZ0)F=GEa7~m zc^r4c?nQ!gfH+g^P*d+7JBw09702y%6h)^S?-A2Q6&Oy zb$Xl^IM5#%UR&US!kE63(!RDDT`s#^WnGM5PNm}q3*!>8LKQ#UQ*ZCCcWMw6(;3*W z*h$WZGf2++<@e}uN+oMOdTxcHV|gGs-=mwC`K04i3=h(nn%vs+&5l=4I2}IL^x!gQ zvknU)9JciII@_h&A_L9i^a!M)#`XoqXcZZ^n*!TY@Gxq;)*!RCm))%e0-T#l~VgDaZ zRi{`mA&mmqqz=OZ-PlC&O-^*v8W?lKOVfIO^<|FfvJyCqx_z<1Xe8JwN@-%rze%*| zmktsQ+5*c#~CE(({>W1lC{BWZb{JTJdhy1D@ztZ0umHPhclo<;YSb#V#cY`8Yoe? zcAv5s&5*RnbAc}#BnTp%z=I?X9rVTMz+r>*-~U+4BxYayPGra>iN5HvDN-6wc_BBK*_Ld9|{7!?4FYs{S|E-GSvR)?GVu`4cj>?N{yFRwdJlCfD2c zSMsoJUvpWSxR8z&X+lGm`ogMEv*<4rChAu*>Oxti`tNnrYo7268VUx*HjX;Do3%C< zQZ0(yCI2!vTEIMu#LM|N*TMblB=%){SN&-1excs13MilMhd&ht3}I%XU0~tWG>%4o+*!wqB&bA=x~)KOIz><4YA!?B3XAaD zZK_FkRxt8bXT@u7Q`%V;-6Z{2O}fqU4qBdw45+Ul+}mxEJ_Y%;*V(+op!4h&9rucC zPDQC)>WhvVmh9Jxd~PNFh-!d!4PripI)?KMkAyp%ep_DVkIWscR@()$4OXK%5Yp?g ztp1VhmGq*T^Q^O+l#YqR8f*PAKK-H_ZRq!m<=45TT;>0jRB`u}1{K1mkdMF6r}2!) zS4C8&QBl{@PXb`hTPy2r=a;}t?TNh`z(oc#>nT)IyUDp9F^h}!Mf%?p2m;dvbv@_T zXOIaw!|qYH(k=}2Z*t}YE*%M?G|w;s={ z$>eY5K{omACwD2ESWzQQIVf#NYz&-mWUD{q73a~k!8T3 z)tAT5t0ak`XHSTZVhs2N$4loHf;hLQ>^T7PMUwVt`eqH9@=)WR}R z_Ouqsh4pkHy)av<1j7s#Udt{eNHL#4Fpn+sMwHf`1k6i8KOkm+`3$#i20GM!G({ey!~=Rv0X(38?-x@5FCYw{cq6j5Y^Ooi7p;x?C`)Sug9 z%#2%B_;g9EqhKw=a{RGvBAk-pCC1((U*pwV?BJPV&r-YH*xJ|U9dlP2a6E%xq`^B1 zV>7a;lwm@)dTiD2x>MLP1PTh;m(xY+Sh*?J{#~v%c{@ZWW}b)nyjyRqZsMk zYo_J*B+&AQvuOEr0xj3+xu@lSng?3`j!V*Lc^X<=q~)NutEA;2K9WqCp|@>A=I>Og z_JdgF+j_1SJ=cc=za3DRZu@3sH0WgVa5SR{wLsp$AM#0{S#oJe?`txpe|$14O!S4! ztgfU&Sw)OZwZNY_n826nS$ye-68Mr%*S@cEYXbK-dEiUmNHu{|7@%2@#a1l1!Heip zR;%jOa&58~RsY}|qi>MqwOTj5I7HoK^{++s8&_z9E_AE%iqd-M*mf%u|KR4ZBwUZR zV1ONu#eVK$&0PsUvI*+~CdA28=7%kBq;T15 zm6{ddl_GTb57mMG`%ZX-zaF#ssM;V@vS*;!AL{H8ugO3f$!%G57k%Y=sTHaP39z%N z;Uxp=5{oLLiu=lG|_>D%DE6;_)UwssusWDr@j*_!>&$`KOpq^rl|B2c{qO z=JD)mT8F_A*!fCpYNgS(m5>)F!KhpE6+>&H1O8G&q!hGVo;ucs35r*iv`!7%LKq7X zs{*_Oh6~Kqnmo?DDuG#A$uA@fi<3m$&=fRysT_!m z8oy*=m{utnGGtk2LClT1pvo`HTP+*X4=;}wzD0q^>iHhe{t+-j92U)u3H-i%v=RMNb) zgtBdKZ*$&lgif$%tX%h6yc=?k)|-cM@GbH2xZ^IiJvut|g4SB>3GAwwC@N>>`U*?K zqYa$*!F4L`7-7SqcA=1Nqs=wFmv$=1VpIc)lfH+#j(W6ZYKqC!4ryuVGSc8#f?hyAw^^prMER8)eLou|aHxfr2yJI1t*-ucFj|Xsl)4djq3Z3B$`iZ>Nb6H*Ie~JM0viLXd?3J* zTPs(+0CyJPEl(_2_`%_0Xg$ZpFz37-{yWK%pO$!$tkj?xHQ^4n5R=V;^Z$9XEL6KpYB%NnYKC?q)Uxu~%zR`!-y=3-6I)sqjy;g!bPx${skG!F zV6s|o;e<;?YC)9gP1+ygE9iw-MVU2@n_;U-w$=@nn(szfhLnaF4HgGQ?oph?1=9%_ zsmZ4txb%iX5=5=iTJp+~i3&ZGGK-~f6!Xy(yCdF>$ms%qo|BTQ(g1~;V`lyo+2zSP z&1TwOq0p&;F_+1I74a1>mtvhUcX5;_Vnz(PhOT_c%t@}Cqm}lO(}K?`TDt|L8JM7u zz=lB}Z2sk~&-NF*xZ@7nZ6%(GSf1Yqlu!t0>HdrG_C`3rHpk6@Kx3b@e1>AmEG224 zdd)Lpf6WtG7tG#NO-eoj9a|1u%5nl_SbXjzy+ za1C!QscX=8Io8QeFB#nAL<^_h-RPG2yQmqCSbaT;F1xNP@+*O8Hg+*a*X0zPceR6~x|=TLMYIWesIWmoL}rtA3}w6oWaF~@8tm)Et3ID+ zg5C;!b*OI`$pNofSv>B`j7~=!P~@zI@+L`}deC~?is=N!(tFwvUn$K-(fA>t=dql<$c{ed1>hB4WxN%yB}M1{q7K*~k^?Z^T|dv2VJw zyz9W^*h2V1^Mn)}0&yM`{pjNGC-SLN zpPK!$Q&@4K{f0u?IZ|=MhgNxF)C%;ll)s?7kEkh=GKW@um~x)g<3q%-m`fF^$R@G^ zm8OKrSqT;-@y_}fqgTgQ&!|BuwG5Rg7Fmp?!t$8tP=vQKm)}`a9~n0`v<)F>q#LKS z1&inGUO5=yXCdydmi6-|^|>#%198PuqR>8caHu%yR^f6K z2X=YKQCe~wx>Wm8tq&cPjr(EUYw612C5 zcVNDI$+fjF+qT@UZ=&S~wO95hUoV305bO;PMtCeszh%Es$#PMLCtI^*yqi>psKXPT zzBTV=N!#)u<6V{NjICP~Nt5+fad~dX#?V7YiIxM-_86V-<^^l4kfs&MPg|3VRG`u< ziCvPW*VIa}fLwMxN(9b|wSEpqIhdlojgOMcZXap-gVLJus#PtvA?mDOt+WsDXE!bb z3w=Mij(rUDJ7an9Rs~5-E~Flowe0V+xw33ExjghrYkFEXC?ccad0w$qc_c|Cq4aE8 z>un#l)ldVE$1Q8-w@Hz<-?R$mo(rj0)~U@w9602v2r+uVY}4nx!D-YU>BYdcbTx~8 zg3@t2AQu5O;B3ej(q~X3wx+E{qLLZNFnSjhQMx9I3w_z*0!*{Il^C`qNDL2UNetT) zBnF+nJ?|33y?KxrM$bTE2w}&KL7 zV&Tsm)B#8NcCU^bz@yDdTPoxlP47sh-r-$+U{<((JuuA%AKy~JfX^*`slcClB5VR$ z!ZKCFL4!WR_oE~!KXeRhmQF+RcQ=^GD85#978--zc=1igFJ~@ABZKJdg*e;ytOc9Fp}AVH7d^ ze&>pK+b&Wf61`AJ8fiYoh%5LJbkdlJfYNoN+FxzFx!&}nfp}oa)yvwxm4xlv$=bfl z3ENkvkLCSPu$YJKdrhkUi{$NV;Kw>02XeLVtqj-LKT}(DG|aK+L(twK0(M* zafCzE8m*-(Jl0E@Fhq*6%cBjnUBu+=!vVKiDR-+-IGR8^elCl497~`bI(;JVw4<%Wly9y0`X*n)w(S(uM? zlnpe*U}aXuKJmXs*lUXic75Ez-u90}_Pu7&GF-e{i63oKmYj>lpU?@mOI1*CgY@{j zTamNkIN7S1V8hsN5)34cGj}M7}^C0)8o8Cy?V#j=*kgWK!PSUTv)r+zs34;1r^GyfKUz8Q; zuuYP=3_G0jNnooXSfO_ef)}3uZkCef=1LiFQVj`}%bP5(grt(klL(h$V$`(&W3tlJ z@Zomybbg5}6wOVE!K&#CWRyhHn_`yiz=pcLBl|>x!0@>&f#H)00)tN9mUn^S|I35G z@PX7jvNVAqj3mb8Q2aoBP6)2${>y%UPV&m1aDgx_mN_ImPjAhJvv&2I*QSscvX@G? z!~cB3-u`yh-u|P6y{*%C=6!F!I}dyNeV49P32sA`$JaUFqG??yYMoLKq=sdx#jLZU zrgiNlXw6_lcaCiAd`b8`;XpR|EFrp8vbe9*wf+9!$b|AH=KB%TO+FgZR<6e-^jQ`5 zZHs9%$bs4QdKm>1rSu!g&u82!9tf^@<$LG$?wQ>;IqSi8@LAHR^Cb<5L!F zNOJNk)uNBu65Iyz_d?Sx9rl77E7=WaMeh)`HDEE^bK;?%RIt1|CVJUyr8t;_qLDWR^(SW+^O6`beyii8+4^CuUXYFx}>_7E9x43w*1^Is(XdX5Pi0+)BEyH zbw8X3s{8I#d&5+`cpOn)MOG{3HZVwQPfuvSpU{LBM3ZC|R8|pLCYU6aUnGI$7n~&y zx(CaZ5bsiQc#X_>Ki_eQ(zzh!y*C&vwJF-ZXF8IvO@`=d}8%~}sEFZ`38J*sfcT)9< zJdmoLAXSZKE$3U2|ZBC0MaUlsbq2*hnP(vyal9WJH zg>|Ke}#mhb4#=@oV@aUl|5%VE@@t``aFd9Bbp zTLe}3oVF5Fl}M2|-45b`1Y%mvBBl=}5L2C=d!N(a&I2)BSUoW{P~##Y?N+OI!dH5o z=vi=uPOps{4k*JyBJV{}R-6xf03S)`_A2FUQC{JR#Y%bgLjxE+Yk4J|0@o5K@T;>Z za6N$n>-5}vV%f0!{0~(9z>8N;fh{Or^vS#J&^616iaAJ(>dguJN~aIy z{r2^SJnXB5)P0qS8;`?H<)81w=72$+F$u!Tj-2P7cDtb*Wdzo5b5pYire+V$P3@UK zICTJUF4;aysK$+&Y0IZxw5*1!y)9w)y(???{aC{8)9JbAY8&&g`=(aA`|#i?j2@4p z4Y=BkSL;-($s0^O44uo~zSx71DKmt}cPDJK&tz>gT`+25q|KZp3JXw(T&An;6ZARK)Be&oZ%GT z#3-r4IL6;nT0y5hp9L1+wrS9I0ZsWrLk%S*XFOtuFxKJSvYG%E-Y4U8s0otqXw+*O z)7Vi(fJbC{Z+C}*2Dg#hYU}iEl4ag!^|=gcCqTVOG|3-;ZFrK*8O@#T+~M6avVikvrAf!UMqwA%{vUe5!aM~!}M3> z5>ME<_d1BMMq~F#ewOL$^B5S6;q(|SQ5XPJtKK2EDm>@;?lB8J(y1PthL!OQ&TTn8 z&I^2dx=?FT=S^&;z0`zvu63zBz*bfq*1-Ft7Y_@1i+Ex*<(8HT)e7G3#pX+Nta;or zEBs=|a5eXQg)yra0s-=*qMJr!I(2umzB1SH?nld_dBvPqv-!g{3st6q;n~(oms8IR z!0|#wjYk0qjj;l?1vO>T-34YlI5rAu)$5o&VztPHh&a)}*SbIa9Wu^Sbd4odl_Wsi zXQF=HX5@QHr;}=*tyjgn2usECQ%ap6K<#aJ?r?CC-p1%HRWL_V4C}DiQhkEzvrw1^jXcZBe{W1m5(tNn-lSYn{c!s5e;kwo8@j^SkfkmXm z3g2x(890gOR9;!C8r!|L{S%^eZKEwQ}XrS4I;(#G4PP0l=~XFu3eac*jI@19-yfXLoVv|3)1et%kcm&X5!$7{MSqwBGZ z;>nReQtUv1A!2YRPrq$w2!{;(EVp53{qX4y&D@)l8#bI0PlRkFub>8!AryZ$K`6fL zl8lF$x?t2WR;L&8uKC%W2ch`d%OMnZ7OIs}V0NE|4NeJLt}VR&rpm$Gp=Q<-psrcSJdZ%&=1hhlaGbAJ=g*l+6_0 zfRbNQmJ4Moh;YBglG!@u#}?ip1JXk74Aq6dy{1 zaR@z{berWJ^k^$Gpw5EFj&5@B5>|fgjq?xokQgemnsK1jbjh)1 z{$PEDUT2466}g$8KasT&^0(QRAs(#@mv=mbV@?ou*`N3_i!Lz~a%5GRn?J#(lz&rj zNNHqs4TLKt#)@PJw1V9ZN-$gxhjLluqK3<@D&<^dH#IKajxoGbza zNQ$wME>#e1*5t8-1p}%h|7W=<-I1bF@^wt%M`l?2O%A?c1~TO6rzm+j`qci$EIC>i z+!lGis?!f>IeHNGHLKLBonAhIxO*t$L?f@<6lN&3d(OV!F>AbQ{h3G9uyeznhocBd!RN+JcLUCgK&sJ?P3xz1G)i zmPC4=$Wf^?x60DzHX5xym2YAJ#1$?YfRS^ef_c#0A`6{fUSUV)^B{8n7EH&q9(tAQ zk*3rPahPOu1nm_LcQjgpI;ZKi{aV50CWYuPm|RJ)FaKk35enxf706wn@j+SKr%W%{2rwBx7flw5T0taML@t*nleFq0EW#V#lxPNMpeq&O1EC#O61&*l0YpSj) z-Ec-~B}9t>WID_fAdDADn?}m8sdHi1UfyKOJvE`>s&~?zOeG zBu%4wAUX-BymZn6)l$q&OF(}~XMMb&5?S1`MMOw7e??urR8ym3zobdgZA;;B`qTtq z8FYog;`)WKx;&L?rQl)Pe_$uF+dHWD`>+}6qyte{}Dzeymh7BGrj z4kb}?F{sUjMo>0k>uG09~**!UJ@=@2dW0@BmJvPahOZ*5l3_ z!C%M&!Kdm?6a2Kv&$>;JIOOn7!`Hne>1hLfi?{;uZz^yzu?vdH=*X4SIEu2q{%52CA0bw|Rc+MKnibiwN*n@Xqq3d+m5 zsf3J|x9!u&!S*@z-}K(02QTO;)i$u=g4!Gb_G`rEMI|nEXk1n3p*npl*m8uSshuL~ zD7HEjHX*kcMtY0*&q`EPrVy5e0>-&Ie7?zch2iTpG7b!V33z>H7GCRu*GBMKr}ydu z!xg}5bqSC+#C~}W5c`2i99eKaYKutWdZrPk2?!p!*79*e-lx+j!bDz3NBe6>n=ZX0 z`eG17YX1t(7leX}N^S>A!R9u}--|2>96=n#MTSZZdkdP|`l2(ncemfB z*+D7kQ}Zc>HE0%tJ;RT|zTscix~KR0RXexe?ugEXG^JWj^#{wU^pCZKu%AJwtX83? z#1X*$fq^O_WPw#+hCvOmDvY8<|BYVR>A>Cu-4NwKnCi7y!(9$nFNHs(NzSWDZ-VJj zRSK26oI(qqyv}yNLhtq&BxNFjr0mTiDZ1d*5t5?QZ`UN{$sj3o@02%saw-q>Wg{B${S^UXC5f)OGA{E)65vDNw^@0>vZ`dd5x(;ee&8zJ!u>Bh-tEDNAI4R zw`qz4#bB66oL|Ik`*vrze>f^dD#0ycIp$O8vqvlM_S*7pnpk_5h$WXp4KQ6rnJ-7wh-Z6Q}<8JPo>h~^O+1oZ((`*kRoIH zv~)TnzZLBNL$-<&sncb zpo4GEqJz3%ln(0j&{;ZPcEk|9BkLUJAG`k5i(k zY1~a#JAubFi!dSE-@qlHC(tearipby)hO&H`!ul;zZ0quL`WDkG6Ubd1zk~=(TP2h zdaE<&PppD2dK2h*N!(I|qMrS=!wA?{lMCU{`=PYnn|H`bMQ3`^IbQGJ zi2$xgcohY+z-py^+-cV#EX61+o9=Ozphm=P(+YHHqWab*r&0A>Tu!-VS`fAFc#8w;s(so%{W?!*&DP}Rv)f%VCO>FC9J~TS(d6ak| zWfwABNuHEKW{qcw8^oJJP(bMuZ4NdIrDjFgU1xbhdq)BneSa1g)dim)x$D#EJM?Md zdffHJO!BrJ!0q3TyeX+amj}lB=c#tZV&0uJd@-)QL1&$ku;PW9JBnTlURk_KGnt%x z5H=0_(nGMQRd(_s7x-xKVoYTypl)zTpYSNvVGLP{%}z}kYJ$PUUh&7@1CpWm&u~;zTIUXg`RI0M?s%*1trSb0Cf=X5`80Q?QiU0=;AsOR{r&y%;>tiG zrRX-+9@PQMwsA4W>E+{WG~eX}>!SQkQ9>5q5t?eUW1M7+bu>%y|7AHTeAyKrl+L^W zYI4~AIbgRsGk?RV1U7)hR@L(w1x%PB{OD})f*iyx>`zjE(}WVPQrUqUB)naNmk+EZ zZU!BiY-#dm3JLa3`0rbYl+9+R(Z(Zk|J91MTWX*hRm89nH5AJ!D=>G2ZZvHeM$6Iu z1{0>=##_!%H>M&_`YGzaB+&nlX3>9LF#2v=r~3hQF9ZF@)Z~rUe<=^N{)&`dL%PZQ#cesNZL^sGlwv)gkHh+(9 z?8S1$xZa9Z%i`FYUAt!Z!qeA^NAJF|aRVY~XbULm!M`|Nqayc8oaE*0MV8+7%L$bE zZ?h=#S5<~h~4;9aT|%?GwL)2V&}W%fJ+E-DdI7aza*uBcae1DyIzghx{e& zvO#(^Psl(trul6peRne92~rx}n#)tVu9d_4iXzmtXC-%7x4oxU;u*!|ob zVD|&h$6NO#1eXT|*uIs8x)!0HHv38$r{3_I;#A+t#(x`Lb-fqRtbd~+ktI1 z4Wq1i9GJ#0p3VU%j$N9Hj7^5!zN zzoX8=ZphmAm#YlXxBoibfBXMb2!Z~R=1mS9&coLKF3#u%r}VczFDv98!{hR1+3{%r zJfSt~@Pz@pH3x3cBr>Y=M@RBbI~DNP(Y{@c>mh}};=*3LPu2diY!3ly?uu6sW^$X^ z*B;9ex9s_MvW1=r;VIJ?ovlQtSuH?#^!M9-{B1kMYQJGU1I2z@J>`0-vw|yqK_6L( zHZb;%xhpU+!|VT*K4fay3OAZ`NDEQL??$Lk`{1^rUaH$WR&pEC@T~%$QN~;bsVQ^n zQ!t+x7K9`oBVT?-o=MGXkycg#zHgA^40q@C+C7yPE>PfSo&H{vn^};qi{m+pWpB|% zlz{14qtl*2l!#X0epCssSwxLvZR-V_DzmrKUPYnSVOVAawcC&aHiKeNZdF`O!PL{} zj=vWEw}eIAB%XKbJth&*)*26IL<8louF%t894wC5@JY>HqV)GXlSeC-)SV1?Qvxrz zEsGc2qB2|;Q3&XC|04e?Um+knmN#~=GY{fsGI`=u)Es3jr8~QN7u!uOPfny?NMyR~zBST6kE6={D`K`@oR<|{KvVq$1TI#};9dB>d0=oTLe7n#TRkm?M}ZpS*zrfAx!8q{WdCeP8tE7Gf4$<| z{+%<^^Yc@?B77y-4r)wt_r^ywIf1v*_aB^}n7gmb0ZCs)oAWcSgM0SQADr92Z{OZo zOw<}iiTjYvtxkidHm`ghuO)(o?mDO(m#=BiVolGt(P~dbA>7qY;nir>qN>!S?YJT% zG2mfM&%nSybgRMJv{%qI&_jC>vGw-Mhb2{7HX*2!*!U1i=}ldpT(o=osF)gTL`vF< z9EFAg_D0GX^`na+_28_AkSgNj(Z`rtJ<8fSAW@PwRbaapxC@rjp>}sD8LiTISU=gc zixEm_C76Y9v!(@Z8~g#Ro8GyF1LN(n?wypx);PM}jav+IjNnkJ2;m)k3NpT+N zrnQn#dy?lKqRP-H9hiAGiDt{)7J*Q!BKsiVhUkcR;TEK3wW+v#ukdyy0@V4n zL_g^j0bbNti z=SQOt*RScCK0Q!5`mn*mAoNdt{LvMnui%+Czxg_nLJvf>oFIdfo05DC2hHbq{D% z8q&$Norls*D4~EIVOrM%?!MVO22C1tm_lgp>Gf|agRaF95>wO_0@@mgV1ZnZDHQ?C zu`D4uyV+5}D$K_$t6G1!S@j#+jLGwq^y9Ra>YXZI12dYei`U5hLmg`eT0T!93d)(D zu0eQ%v(~+f;uNMAX^K&BrC)Nypb_+5dS5lBqRZu<6uw})rj$0SV;t&?_Ty_Th)$cF zKyXTbz2y;)UwxW0ts^cQb$3N?EcrKy<@HMkSQt#RikTECY!I5D4kOVRY^*9Vp@RCz zN~d-ORXlctN6xGm|9TUj$XrE)n^hwpFndm)dDBTy1_p*tqQ(-%@zJC?UE*N4b|0sV zW{3&hQJ@aIn5a{}^2i+6lRVbM$m~mC)RIn+_6PmntL_=8_Zj@2L9UTsIPZmY`yloh*`yYE$c5 zz`3Z5(Rv$ope-o9P*{0GO0c>}t%jC1+CP)+_?XHT_!AslgXihu9XsP_-|x1e!4@GF zRqw-}Lg87Xu~yYU*A$vH6mQiMuT%9Vlr0vPb^XJr(zG<@Wk-!uy4G3A2%*XvWXMW# z5I%_q$4UvG8VwxDFrH=`WFx8rAq|0CTnS}+;sJe9aP+em%`_=YswMU&#}+Zd$@@Cp?k?rPLD`52Fx+7mN{#F>?u^%SZy)_|nyFz(l9 zkO?`%GTwPs5K>vJ>j0#xXL>ZVfgkfIYG8b(DVl5tvXRh#sDbI?hR`AN#Up>JhAEse z!da^Y%Q9Wuz`BU=mQ>qn;)qgJy|x&lq1IVV@xDr}T%4bJU>;7E_%|krNuj+GDz*C9 zp{wCUQ#{@RXN8F04KK1utR{Uo7BVh-L~v)d42auobxcTK_ge%VosNDrGV;9$npXt%^x1@w!^QNGP(CV#*3$kd0JZa_vG8M(9 z2x|-4&1P>w-t}T}X*I5f&9R=fv$|L>x@LE=lB;KVtYTGq&TKumA%(5Cx+%(rA|gwd zC;vIG9ep9()oJpKI5_2mXsh6&@I)XKbNrneftuMGsauH=3UTCYilM@taF(mWZ zM=D#^0+4zA$}Wd0uozn7l3!KOh?pzO{@~!jSu(3#_Z`)S6-V^>E)Pv-y20%0ul>^4 zXVaD%SrfYwFQZ_WyZ7#kyO-yb7rke?~e6 zLYF)oOn?yE-D>Ni zTOO*x-RhP;PzjdA+eesj^OD`;9k{+@heD=WbrxXG$IJE_wod56J+#+Xe>-QYA}^3T zIx%CJS}~AH|D9f!nD6HgJ;?>8_~rokzn>{t8S3h%eue-NCYY3xG>`h_3I&t<7 zCMon7u(ueMeql`)oAvY>{5`yek}%;C^BH>gm)QaJ><$K=t*Mnpo6%B&i()zJQb!Vu zT3yGtCOV)mHN3?@%kZuXQ>HS!N8_oeUq|M~m<#SBVAKak=X+LneM#J99kMW#&5AXI z?#EW&i&d+x6Z>J@@x;1u7d;)r&vD_1TkX)F_0T4#1H)bXk6-rVzeUJH`{$?TBx6x_ z>b{L7rA^@#dBIzCkI(3+4zA<2;aD5a=wTxdJ8(hy-QandzC?kKV3@eMaOb?pcSA6n zue4#%P;%jf8zn{9jTyHF+-lw`A4%?Q&by5eA3|g0I-V5O>hK87H%tqj465xe!au3t zaVzr5hVi@6leT1hqH<=&<0q8vknzE(G=)eyPc{5WN1O9sPmzDhYB)yCI}u@|a>NT2 zvIKjwNwQJy9Z~j+>(=|lJ+u^b!ru$prk6r5f1(-VP}O&3cPzeF`lnP)W5JmxBokX%y4mL_cNYNgvuyYge z%10|<4v^Fs+qB>xpO6-Gx~w4JJ7#D3|6pUl^aFMTNmu*KYlP%k?HFTttlkIAY8fSDoE`zc}9`Jf|{Ytgu|8Cf7_zl z`5qBjsEI8t3&$czu(w3?TPiIn9ZXjFY*n59hkj+Z39BD6=WGv+Ri@W;X*=(XPsDeZ`I5)H4{WEV3H1 zgf^wH-Vj@>Ve1@3)>TDx7a59VzQ8MaIAxYY9c0Z$u1J3)1{R4m=qhn7+rZ>OOC{zj z8rmr|{4=t+0Fm3|6bkd|X@0LTPm~WRktDDxgaHAz$Q_@KD=}i`&z=0D@kG|OCM+>3 zip+UvlA;Sr1PE5rsAOF+BvNlOeuVm}*??E{sb*qVWeYSP=1Z+4DiGOOk#6~Qv?0Qv zo|Q(Zo|CJ3>|&c}V_~|yCpW~*J#|xoHbr4F(9;Kj!-9iXSTiHfAYIDY>!ql`F0mb- zC}=`2-G*NQAvtrQadRx*?}&loqe@&7q^V?n`1tLE-ujoe?qQ1zdWPCOIWv8&>Lf1*(OZ1f_v#2AAJRfKy{;*7^ zT*Ju%vx*PsgRaTdt>*U;K}DF!5}$QNHUR~wG$jsDCHQ=dch<)k&G-GoxEho?g!9O3 z;k+y*X^*yG5K|FZf9CQ#Yigh37sR$91dVj_7PwHb)hfFsm~8BsFze?}sx>aR@pgsW9omNuM98CV6%IIXzLgs7(vn-L#b%q$1~V>msu_=~C74+w z&>}PoWpn=niG){cdodOGQZ|6FxI$%q`|Zvxv3imgfQ@qvWfc((VrcMbHL#Xglju6{ zQtUlvi7@?OivH0vCVm^MYp=^*4@b4Sd5Bz}o68{gS_LoN<)-7Rn=e^)$N4+nop4Gb zK1SM?O<5n*M{Yx+Jqg-dgVCP<;(u-J%eG@TXVHr=DlP1WdM|>LZSXk}11z1Rui`?? z-c%@bh-`Df*&f@?JcHXS=;9EbY;78AM=;20lo&SR_`lUy=(c%fFW0 zWST3>R+G!G)%I^t_??0MWU*CwB)V;A}{vwE2cS+5Cx(m7fC=Dm;Ff=>b ztk6e4WOlfbKT@f8TDW2ftU=bV2d1^ho;jaMo{V1mU62$E1PcnWz4Lu z`Xq}3#R-3rbG1cndanp;7UxGJ&Bu$l=ERA~C=#E$(ZrmYSGMGXpDv~7Pw)tR(M42(v_99@4?>hQvBNmU-a#SqaC#Z z2i$6xme|M zJPUB!Q!iCGIb97nd^RoD9 z`<&1h8-?BE*ius%wmM1o`-3Ba$s41Fegx0o4R*>wgbtGHq5d`MUw@kf>$0uGS*^>% z*Nj_*?HnRbsknBt9{d7`%Z1|IfnE4qBb3Wo_$KA~L~VY0EIV%cF(ALpMs5MK~Um_0M?Fnr8L|5qxAc?!o2 zCA^j8?}esYI_w1+B-ssTMbCzs2HdyyoOq~*!4R8`(nCF4orih`i~SElG51i7mA6)e{4GT(yRe5mIn0xJ?x`J-k+SWeDrzu-{MG+@I{*BOWK zR;Ff)Vh<^SF_z|!HyB2;TygooVl=E{*Z`Xi78mN}<1pY>%dQ4$$;StB6wtU8cs$T?kuFO-T3YNW>Vc-(4g zA63lZ8hfdyC$zvP^v=RA;l9GQQoD)?=APmgxuf_6cQgmxgXKzz@2`4rFh$xxkED1K zZ6H(k7`u$5A+!VtCTc8*-UNnX-S(Z-V0}h#LJtl-Kf!^uTO5OGwCCi>bsiQcMd4^H z_+WTmQWnrO3b1WS-m{)%vSnKp3(dHYNLOh2)=0jH9wC~D-6XEfeZps0Dt1fj!c=ZX zGT?h9Ck+{xDGS;{;&uFj7n8_FBR-^R#NzB$am1j!?#8$!y1TKc5*OV>tify7)@*3@X;G)*c@c5Ba z=0=sy-O?XXn*LL2S~KP73vmX0=FF+IIh!UP-P3M2l!0iVWuBXwJuo$U5a9#n4^ABb zoa$+GdrI5g-j=UA<#mxYo!m4`LLze z5&cy??2y+j4&1ZwipV&8=@swm^Q5~U>AseWG+nKFhukV%aKTYL8D6DcEA&X0TyRiS zhQ$CPf%G^p@O9In)}kiJY)Whu@yhayYY%SuSaJ9;Vvg z5*=$Ex6BH^*fCtqJ?}1O^+LeAjy!Wv535smH|r~NE$@D`ESfhi)mXd!;hKdiQ=W8e zYo*JmOF7)phl(0kX$g(7f(T{O-NjCE!0|;a<6g(?5vxTmI3S4nNYwq|?~tNf(KVJ- zRr2V+ide~J&1to-HmIaoF9^^YV=0@p#CpeSyNa!xTFcr3Pe!X0G|Tj!9Ns%Rdw35q z)wj7MM#H|K1jGP=^{GMp38$_r*yyt$pK#vimnnD_tPD4O(#UZV#tWMGApXPgLOZ^J zg_|Xt!|t}#S$BxiwT-sKkbRcJzcOa7>46p1gqch*rR;scxU^TTvfFlN8Pj6Bs#LmI z9kjVv4eFX`wLC;&fVl#$>iJ(`@}cW8x*p3YF6#Y}Vh4&ldNDXH{B+xBm|0JRX4Ce}(Ueq-@dOAFHw%l9E+vVm)NG-O>$-ZE#A_9fHPtY)YM)+Q{k}2v=@;>SSX$z^u{hC``ZZ`P2w_!Fc>@okus#!m&F~~@a0J@Jzc?w(M z9P(#Ig;i3+G9=6B4iH=^V0J7lJEjfIB2`!ipxVIkJq?b`SQ$U%1|#>B}axz zu1$9IMZRtvc1UlL`=LmeK6P*aQs+dc+~HANYzBo8(7EHN820u6SGmTbCfWa{OBF<$ zH9xovsE+)fmW`T~BRKYg?5s545Ta|fLyDFmL<)QzMA(jJ+nr?L!bW(LBb6D3Qo^Qa z@@oY-QhuFDjY@*f_#Xp3qxu4wDM%~gw7;`csS0JbUxw4lpP~+vXcT}~{uYGN;yv^8 z`wk9TY{cRMPq=?-kbYxQnIi_S1vrFV6J1hWRmz%-z5?kA`)8+%QU)#P0FKolec6^7x>U7i9c+t%M-4Oc6&U zB4und`NdTxiM0@nYUK%5P%zrxBoJ0Jlj2S+hjOsZ#h`j6h?^PN#I}Qtt|&`oqc$NG zNB+Sl=`b7|c7&eER|i4;vkRxcsQO39kW@$|te=_`8-n3Nsx0tJ)Cp378S_bsBDZ>k zFTTih&3ABZ(-imz6-i`e5UOBQ!0-LFqfM93G<-w` z1@yHVJ}L(lX$R6m6~YP)rLxUwc64?G*P};q)2;c`-rati(E_B_YLenkuY-wVHf!C} zd;O{%IPmrzUn_*d2}fb7KUh|!f2<|MsTn>rT6lEs)=C~!%6#Y?!2?!-8OHMCqLuD7 z_>cr)5<5`gqZ^_eFtHwYa^B@|^-}mlnv|Z}Q}D>}c<%2CjYvXeVpC>NKO+2r__QLJ z(EypqR^&n8vF65ltqjKIlWZvwoIN(n!%|-x6ZDwyTQ+n?%r=JOfnOy5lbl9*6`=2) zFrVsfV7ppf!OwzACbLkKjY*nXz!giajEKmSOMErcZWZ^>?>;zg-luT?>V`s>E(upW zf(D6umzX+Mw#h4!6&FvB=G-IVNJ~tnc<+^O0R!L*qpi+uh|x#Ji~qGu6L#_mW%Fo0 z>5-Q&X160Uw%$)-+u$(B-@Gs3@40rp>0u_sDT1pxUB0-fVzR5hsro3`s%f%_vv*I; z+f2g)NiIwo&M%bSzTFw_AC5AOnDD|O1YGXy?U1{@w$%EZ#Vx|Ir~#(Se?AeAn+@^W z(5vG7gZriq&Q9%~nw{D+IiImNzQRWgdA5a`4Dwf$X*AhMb0!G`eR2>3qah&gXeRB3P*hZEXq*kXm zfZGaRB$Aj6t&z`KbjsrbyCcHV6!i2$KoN8Ln8{1x{9z3bDsQkn#<)zCmefXRxb3B2 zvyo+rqUM2<{A2=G(2ICMZbU(h-~6*KD2asDxliMm_?=LNz&)W3W(Hb@1YJ@0N{Ky^ z66qQACssihy$Mcm#OV}~x@UjwFg$=DQj-Wa%I6wV_su_f%{N(vrXJ^Mkj0`iz33dT zcOabO#7pghyHKfC+Hf#h2W^aPEH>Ta$arwh3S?x5)web|&{Mn66}V+t%k0$LyaTmR zqh3LX=A!AkArO+dCojB{C|M4%)ke0ovNTH<8(WuR7Oh;Z##nA*TOWPW>UZLCL0eNK zhZmA%QF4$QN}WqrB|0SC=Nzb4$~#gc*c-|!Y$c~8!Utm$gNDj?0dvq?mYS6X501fM z>}1k0X%8_CW=J!% zAy1i}o)QE4RqLE&sO`5qbH`h4Z>3o3H0kA*A>?Ne%%md8*1$-Ae}6!(*#Li(S~I2S zHr5_J%E`jeN^*KRg=oIZ3D!mVo7_Sc-w~Q>Qe4O=#yXm%_5HwZ=QRqLFoVD8Z1I8|#4YSkQh(F<4p*t{z~xiELeS~7GWHS54n zVZUqg0edI>3t(bxv)O4NR!X47w027kRHKR*Hk=M&Ib{Xrj?j&!4Z~IFyPz1{z$L>`xp5S_#CC^ z2sQ(qw>%8p6F{E#t(U;{pOYh9_3{iV{z?$c2FFI9tY z3)$MYCV**?h-#U7I(Xdei?!=?D5CetNfRdofBtNO*+pFs;xC>RT~BschX}gc_lCva z=Y_Qqy4MnsZU~H*1jL5!>wV&Z8+^tVSGfw4Fkj``=(LtXTL98F z*@3UPTE&T~Z{tRJPTA<(?X?jQLmKb+s-vR%+9!TDnwozpbpu2+`9Ba0!4O-H#o#c| zxCq%^7_~XL4zig$srGT}3aPv8soEpf%z^l*&Q`LNl&~0q6APTCB08nNqfgJ{lZ_ z-H*^_$#6LpOj0o~{ffFnoryy@zfR&h-EsZ@X)KwCQG);7 zJf_I0os`={B8mD#7d3Mff-V`ycbQ|SGGmmL=)bl*@~c1NdZSClIot~PowX>}WnHpE z1@jM6d!T0-ZeraUZgiV%l~wh@%p2Mb>U&s)AKx7QlJh_z=N85~m}YvhBRHf+E)fS|-OShW4;uVCBdNccK^Rrs^{L^xw zXF|H<6^kK8XDiWZRzt3J_v3FH!_=0vJp=H@X<62Cy#%kmHCo-(Ypg`GYa_NtA2PLU zg%MX40f|7Jg5?I0_5aY`fvp`YxecjxuE2^?sHNmw2B|4?>SM0I3FI4+7;5_&$%kO# z?=$;WRsk|LiN?@s)NA)38dbfCr+|E_lHAM!cb;{;WpB~NL)Ph9qtl+jL*`b2zPVvX zO4K+8KgaP}5x+~6+1qKaq7XbgSK)VA*KVs93VSgqw%a9_p~Aadcy}kq+!cMrBsYXnj~M7 zT!V=#I%gsOfqY=l0ZCY8p%k>``>Q$4Neu!X!>1S>JbC(Uzxf9n#Lx1PCvMoVr?dQ{ zlN&ZH|0KaL5d2qyj}!d=1YaWf2EjiQJb4$uvk7_#`U!Rr+)wagf+d0@1g|0ZQG%Z$ z_$7jm5d1#D7YY6+!FLFrI0f(wf}0375o{&6hu{D~nV?DVN`ik!@GgR%C-@M-#|b`9 z@MVI(BluT>r|t&0o}fT5O0bJymf(j7Y6LGMcq74&6Z{8)4-kBm;IjmOLGX2ge(=oWWuYH9XEx*P%K=%U+e;;+uBCqYNf49h=+ zDY$RLvddpRozAJJw-BDRL=rfw>s$Uoe2nJZ&{F4 z=+^SX7{+pq9en-y)3XNqbKoqEyR+Oz^$(v`q=gIgASU6|HCEI;z5Ehx6r&{TG4f*x7YbBHt4Z) zqF5}ROrP4TWe2W`bl^6be0fs8Rk5oh#ZzFE)olQGlOO-sC8el=z}7X;?b?CAMO zV3w=snI;Au9}m;V81)+4UUiCKs2}Mm;{`MY-LU_{~A8x zJoF*8-XV0@D^Vwc4l{RLD${u&`fXdeIr;tC2pXy67ogb3P%LsF60*DK>QhZ8SE`3k zxA$imBvHeg`GH`+aowyb#W9D=3JM!uBt)qvu38Pw5%4 zefbmUnZ`^ZoQs9Na&iLit?oZKKQVV-`Z~YFzRm|T)>#$1DYDKgxi_!t{NR!gGt76erSIJ{}UG;C~Y}K!aOYU6?(OYugm_=Ia~H8s})b*+AH=oerd)Ut75lC2)at%nAbJF?4Eov?q@`u zEu}AZ)4td$c?zp$6(iO6ngWJWE%Q_C!g$-h%qscTq-9p$ zp)n81@G6{{VEwTEJYn;#$tK{qQy2KlEw#3hh z+c>5#_YwPYt7KjqS_!xM9$jvgd|O_Zdo*XueZ5uXgJ{!jTzv|?&*gSPe$2k?kH}fdwxlyU@$=TvR(_DOE*Ohf(REkJoAbZ!FUi@u z-vX;MJWauK3*V}>AT2w!`Fc7YJZi^-H^}7ONqC^X-w+}BD!HifAY$#3o87;ZGb|9+ z_jC^<+yTk}jIB8rvAmA+qLRjCg6}6Vfj8Na;O#Owk?)_9-=7gd0+pP5B?*3QjY~i0~EPU3AzuUgzKb5iK zs@O9lTU{mJkav3jBRN~~tIZXc0(Qnue~*3Le<@?#Rk7&0tK{6T`@3?s?$;RWu0uL! z?f74@ul$EIR{s6+dz87WWPPa5Esg(=Ia~SXaOEe3k9Q&Gb;sX7Vqg1DW~}{h*wp!T>P`}FJ9p@a*c{Fvd25B{@3Lp?<-a` zn7RB38F=tjJ07THst9w92md2yc<_7@0Rs=Fj^QmnH0#}q12b2uC6AcR&J1Uu!pu`u zhKMxir~3;3ydL@gHfP8n6n=AFJL(r=B3 za8CjvJe-9H(+P;6({qmqmrUo&H1A5XhpA?>-pn`?%p6Qu^Lo~r|4_o3>vV0;lUvd7 znK@hYXK~H3`Nh+Hy6(za`j;jw{VTGTzL~J}I-M`_b1edgPB() zAjE635aQJd2%*zQ^NtWN$QeRxv?9bB4hfGYpu`)pP~vq7D52B!ON`vIhw+@Dgg7zI zddjc;g0}`_cvAv0d_4;p-kg98Iz9I%#;Kem!=Ac{4qXY3-%LP;=RGl9lQi?K1Z2?Z zxkrZCoFT&|G7EZJj2;`MJ3vOGmj7e8sni>WnH$yOM|DbDvykE@l_9EA(&=0CEPdyc6*}@15JbXLjGD^81`6TYM)0 z3!eO>3|-bgBw&F~*Iw>&D|i04oMFK&#DeMC5lWW(5FTKqwK`0=T&;Lif%V)hOt?a2 zh$^sjx^`5PTTJ+T&M@J+7$y|Dc^flxeF6sDk%a-eVB~p>Ut#WF!u?gwFhFP}6fFo- zftgCH1!Kv3TlHGAQOe>Dy$Kj`R~ANWR2iZ)L8s?lEAh>oVT7=gNW%#ESUL+Kb|xUi zfh>fWOh5>oo_jlqf5{m_h)Z!RLa4;-E3XFdID1D^xR)(Y5qdM^htDQp!ey6aXsC6;s18D> z7xKz}BBLOWoXQ72Iczpydbh^)X^6M4&WX=%arb?o3mREipbJK^K&ShHL0-KF zy+3DIaJ^S+;SBe}j@R*a*PGmU-N}Z{1Qd9A77FNsQ54YWx%Vmgn>j;)=Xtf4a`~r@ z;D&Q=O$N0th`%zJ!7CGR;O$vBpbJKEK&OY8!I65UoHu6hshr`!4en955?pM}brxEs zW~I@t)N5Ino9{?Kg7;@3fiC#`$RSau@6Zy#^;WIZ0iZ?hv`b&i8D887Nl_jfq0LiR zvEbp^R;QNvp6|aTAjU_t5JMM?YOQs;Z&!U85CfsC@}VBY1$6 za$}d8>W?L$#%HroLl=xP4xOI+8_#d&3^yjIOy~|X?71>TjL#)t%9pY*MHh@>icTNa z+$FCkjepA-rd$bDqOQL)*8Ix}YyP)cYyMT0;hKm`UHw$5Zdims#a= zu+(zZh^`&M@GbQqyzWo?7_s3?BY5Vf`<;G{fNG zd#YfR>g)8I^G^Y8&*%Ej!*H@|=cJdZb&gsx+>o{Ym#YlX`zD?4-#0xK9Ki2R-k88p z&QRcaCJB%Z9lEr9x+wt>Zp%W1TU3VYBBFp!_s@`5`J#aASl)2qu6*Evw_JJRh7BiA z7nTp>_o*LQtW>?NeSLlVTVAu(ci3I1H2YfhPP62d>a}*WUhSKh?sEs-Mzg-`mD>F8 z=4JPYD?F36`cUeK*W6sHudLK-eXV9mr1yy&l{#~)EPZaH(dtwAtiiC0EOdH#1>IWy zG@_X-*Vw_=pFcfouxFn@&pOLT@ZUY1<H5ZpjeAQ&dNi{O5O z2MHDlS_H2ocr(E}3EoTa0fOHl_zc0H5&R9oKM*{T54^4-xPhRTV3c5zU?0K51j__R z30_0+7J_#Z{3n7B5&Ry(=Lr6i;Ohk6Be;ymnyU#Mg3Sb53GODCCvXXB1SbeyPw+N^ z_YnLt!AA%_N$>@Nzb5!Qg8xf!1^3Kz2%b+cK(GVAdl>ln0nR4F(|V7de)ROIsW_^w zxRsg>)!Mi`Y4!06Wq+d#k*6t8Yx8`w7lzAYgX4?+LyPX%@bH#}h4J#x z@WPg|*T1FzG#mek%h33LCin=!Ckehl@Ye)?NAQ0Mu6Q!Qa|v!I7$(?Bu#eye391B- z5WJq?#|VCw;6D@mHo<2I{+!@%3BF5k*;4?XP0&NoPq2gFeu5VhECG1S??gYu4Bflh z8KNKBDujVUP4AEk zh2Wh8?f=vWl3GN{{Ku{)V61c)j1P_tjgNUF-eSKyIJ~%JY;n+C zEW7<S6IJTN@!4h_14V*{e&8@8j*&mp*lph)lo1osf^Cs-h;6TFPzjRZeV@E-_1K=4t5 z&l3Cv!Pg1?8NgfqH$4%D;^c^rwrm8u>0!gS+DmS&T%{PY%Ug6onUq2HJu-QxTRMEG z$slVnj9hdF`-jGd2Fu1B+ttFJtfCO7QOqexBgh2tG#ehXj8? z@HK+(5?p!V1eKm!J`DH3El(XEuTOy#VqgXewL+V5aoOEDIH!MTNo;B z85|u4{y;n!D=iL#vJHFS30ubHlzx_7`CkNoPw=kgB3YYA>87$CTlV20o&1Qmj#1g|A{E5T0_ z{4&9B5PXW@PYC`O!9Nf@={A6865LF%ncxQiyyf3U!(x7>SG(X81(TS)w=m!>4lE2W z4ve~`;qu6kH{dRejtne}_4k(tx5#;T5qnW1I7aYVf*&RLNrGP>_%OjI2);n@6@qUQ zY`7iZ=>#_rY$O;b*iA4`ut3ltcsao-f_D!I5;eDW+(gd zAVG@Rlb4*S?fWs*j;`X=t=uS}YHgz44*3k^bTS(#V$4h2c?eXmN0{ zJR}+S2ic21Aoyc~uMqq_!M_q*Spax0!EFRX1QP^%2_7amOmLjwbp&rC_!)xt6Z{sz z9}xT*!T%=sCxT0R0j?&vg`kgMJHdSf4-y^}W^8udK^_L&fx!VxacRUI!8REhDnUSktmJu8D!vhY zSso_XL2w_z0|XvHjo<{q>j~aQ@E(F+Cio44PZ9hH!IufXMeu!s%U=NS9D-X2HWG{z z>?U}KV42_;!J`E4BKUcN4-tHv;PV7uCipvoeW`L&=TuX2(!2rRX1TzFBf)#=z1V2ph7J{E7 z_(g)>CHMn^FA)4S!QT`78^P0x0JjosCKx4{BzPggL4p;6A0~J+!H*IA48i*eev9A_ z2>zPj8wCGI@T5L~XA;~@u$f?zV20oTL5bj{1g{`?Gr`Xg{35{z34WL04+;K~;2Q-0 zM(~t=fNKbDB^V&Mli-B}FD5ud&>{F?g0~R7m*D*bA13%T!5${*~a$0f6fW z?jRT;xSL=f!NUZH362xI4!~P}5!Os#8ntiT>d_8C?Dvj&C7>KsZb~?W&J>C57{>Jv zf}!_A8Y~TM85+d41)DDq4Y*_dLtBQ&M@I$5{VIF(X@V~he1+iK1RDkco=$KB!A62{ zg53o31PcTWf|nDVB6ugk`v^Wr@G*kV5qt^2TmAs57NfQ(EZPYt1pkUac&nzeA~w_! z9NC9UL&M|a?y$EwR32Rz=^yn5$A^c<#>Sw)5JUYwyK(gpz>NeiAlO21H^F{_62VIe zUP16?f}bGxj|9I)@OuQGA^04lI<1w+u@;;iuT2UnBS( zg3l2AIl>#+G;Kc+>1V;e8<)1_mF{=Mp+WiJ=QPpt(M@Wa_ zzgh|w0Zm}#$nH72XZMhxa>W2a35rGt_3WOrz(V&97ao%6SwgzWZDbwU zNH&va$*;&x@)|ir-X=p*Z~+-ZE+vBguFo$(7_fvWP4r ztI5~MedH1HBl2^yle|U_k+;d&lfZdoESX3qlWWNJWHD&~SH6iv|My^U*HP;TIiVAa zwzhH3r8>=Y;tGvEu0roFGd-QmbxiRXdCEt*2eun!o}WiL%<|>Etwb_JeVJO3<)PEs zS*b8lOOd1AQdwZD=G1SQ;sE&@`4<`VaWIlhATjA6v&aJA%ANQ@A9>N~AN|wXY}JoU z>~rZ{8&Q+9E1u^fOTsim=Oqo&Bug>IIofQg_N&a;0Z5jTspLBHSyGZ)fh)&AANh<9 zHhQYrTC_kUMS%?^YWO5@rZ7fEE=in?(Fbf+viCB<7V;CagX|)&lSAb1WWc+?a59=) zLMD;vWInN^o7_&mK^`FA2d>T_nTD@Q}0t;L4nop0aPwOei6L=Y4>&C;EOLo>X1Vz&^jb)6}PVo9B@eNmRtT z2ooy;d`7S<%^n=%8=%-COb0(VXTB zx~}dy-CeekpK@X?!yE^XnG`zC;~=qV=rHNUqL8UDwy3uJ{Rbn^Nb(^PkSSyqaOGLh zhitJq?(LGB#;PR~Ki$b+u=lS{U^ySuu6mK>2Uw2lq>7*Yy0;j$DvuOd}(?5}z^ zgRCVR$R_e6*$!N}0{U>1`~I1z!kw0eTMB`y11~M5jc{m(Ly$BHtVwY^QdJ&%`O1GK z$H_TEm|;&d!_|GyaC%i*8=6cxEfv;aDFoVNTI2W-m$C>F?7bpaZF$Dw)k{q}$Q-hW zl;jq&hO8%>$PdVK);L5Sk4`CbIiFv1P+G^=lhPgx(VU#(gaPi#UDGfn?tU@#d;#uG`dAhXE=vY0f;9b_H(HhGe4 zC%++k$pLbdoHZN_BOf3iBN4fhTuT;`C8R;VOzt5MktfKr?cP^zY$<4c|Z9m zK#76(CzHCi{RZ?}a|@Reg`!^F?CQZsk|iqQ_GUh0R$S6(U1@6!F+8Nu|RM12roe zcUo1m#6K8d__<&dnLr}o%Hz#EZA|RFHJfU=qO8C{kM!H@*aikQM2KY_wS-eq7F5mZ z1q`x++(y>=NY_I@L~ZQ+=QJ`r<8xv{ttBZE^j?w_Jv^?12@WUGKSv3vGu&Retdcy+ z*MH8x{^sWEw=oy`j+=G1bpo8LE0=^0&lxxwV#kWo73aVz!wMW?H2fm>dnS0(C-_}6 zK^w(Jv$fHQ#~^ZN=QcV!`HeuekuX9=SokF$MlwQ$GRM-Ug)NMZLKRoV#-Q_%i=@Rm96K6ERN7cby%9R zJhM4E_9jDmOQQT8A$E~Jl0TE9q~C>L2pL5tkeGA;S3ZrO@rhd;s{fB` z-RvIpdTW{h4NH|cA+0~as zkt`>x$yb3Z1L#Axv!Yd}6Vp!IuUF%%81Y0*?G}kp(qjXF#nxaGi)UUUo8<+Tet}&I ziUvQ6Kg=N8$PS-oQ!`B)i^-?DO((vFvzvcU0Ve8IrWOO$0M4UuaUJ$bE(=h`MH)v5 dqDVC!UNb&MV>A-nMZQHg16Qts9GS+Y{{hf{qQ(FK diff --git a/README.md b/README.md index 52bdea529..a42a49b24 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,38 @@ public class WeatherAgentExecutorProducer { } ``` +### 4. Configure Executor Settings (Optional) + +The A2A Java SDK uses a dedicated executor for handling asynchronous operations like streaming subscriptions. By default, this executor is configured with a core pool size of 5 threads and a maximum pool size of 50 threads, optimized for I/O-bound operations. + +You can customize the executor settings in your `application.properties`: + +```properties +# Core thread pool size for the @Internal executor (default: 5) +a2a.executor.core-pool-size=5 + +# Maximum thread pool size (default: 50) +a2a.executor.max-pool-size=50 + +# Thread keep-alive time in seconds (default: 60) +a2a.executor.keep-alive-seconds=60 +``` + +**Why this matters:** +- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load. +- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool used by other async tasks. +- **Concurrency**: In production environments with high concurrent streaming requests, increase the pool sizes accordingly. + +**Default Configuration:** +```properties +# These are the defaults - no need to set unless you want different values +a2a.executor.core-pool-size=5 +a2a.executor.max-pool-size=50 +a2a.executor.keep-alive-seconds=60 +``` + +**Note:** The reference server implementations automatically configure this executor. If you're creating a custom server integration, ensure you provide an `@Internal Executor` bean for optimal streaming performance. + ## A2A Client The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. The Java client implementation supports the following transports: diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index 68de6915f..c5016440a 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -94,6 +94,11 @@ public EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { .hook(new ReplicationHook(taskId)); } + @Override + public int getActiveChildQueueCount(String taskId) { + return delegate.getActiveChildQueueCount(taskId); + } + private class ReplicatingEventQueueFactory implements EventQueueFactory { @Override public EventQueue.EventQueueBuilder builder(String taskId) { diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 5f6a6ebd4..3fb52617d 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -17,6 +18,7 @@ import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueClosedException; +import io.a2a.server.events.EventQueueTestHelper; import io.a2a.spec.Event; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskState; @@ -147,8 +149,11 @@ void testBasicQueueManagerFunctionality() throws InterruptedException { EventQueue queue = queueManager.createOrTap(taskId); assertNotNull(queue); + // createOrTap now returns ChildQueue, get returns MainQueue EventQueue retrievedQueue = queueManager.get(taskId); - assertEquals(queue, retrievedQueue); + assertNotNull(retrievedQueue); + // queue should be a ChildQueue (cannot be tapped) + assertThrows(IllegalStateException.class, () -> EventQueueTestHelper.tapQueue(queue)); EventQueue tappedQueue = queueManager.tap(taskId); assertNotNull(tappedQueue); diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueTestHelper.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueTestHelper.java new file mode 100644 index 000000000..1b095889b --- /dev/null +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueTestHelper.java @@ -0,0 +1,10 @@ +package io.a2a.server.events; + +/** + * Utils to access package private methods in the io.a2a.server.events package + */ +public class EventQueueTestHelper { + public static EventQueue tapQueue(EventQueue queue) { + return queue.tap(); + } +} diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 74fd4a217..6e51eae85 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -3,8 +3,11 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; +import java.util.concurrent.Executor; + import io.a2a.server.PublicAgentCard; import io.a2a.server.requesthandlers.RequestHandler; +import io.a2a.server.util.async.Internal; import io.a2a.spec.AgentCard; import io.a2a.transport.grpc.handler.CallContextFactory; import io.a2a.transport.grpc.handler.GrpcHandler; @@ -20,14 +23,17 @@ public class QuarkusGrpcHandler extends GrpcHandler { private final AgentCard agentCard; private final RequestHandler requestHandler; private final Instance callContextFactoryInstance; + private final Executor executor; @Inject public QuarkusGrpcHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler, - Instance callContextFactoryInstance) { + Instance callContextFactoryInstance, + @Internal Executor executor) { this.agentCard = agentCard; this.requestHandler = requestHandler; this.callContextFactoryInstance = callContextFactoryInstance; + this.executor = executor; } @Override @@ -44,4 +50,9 @@ protected AgentCard getAgentCard() { protected CallContextFactory getCallContextFactory() { return callContextFactoryInstance.isUnsatisfied() ? null : callContextFactoryInstance.get(); } + + @Override + protected Executor getExecutor() { + return executor; + } } diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java index bf9acfb1e..6c24c5166 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -105,6 +105,14 @@ public Response getStreamingSubscribedCount() { return Response.ok(String.valueOf(streamingSubscribedCount.get()), TEXT_PLAIN).build(); } + @GET + @Path("/queue/childCount/{taskId}") + @Produces(TEXT_PLAIN) + public Response getChildQueueCount(@PathParam("taskId") String taskId) { + int count = testUtilsBean.getChildQueueCount(taskId); + return Response.ok(String.valueOf(count), TEXT_PLAIN).build(); + } + @DELETE @Path("/task/{taskId}/config/{configId}") public Response deleteTaskPushNotificationConfig(@PathParam("taskId") String taskId, @PathParam("configId") String configId) { diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java index 5af126bf5..3e6be1a31 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java @@ -139,6 +139,14 @@ public void getStreamingSubscribedCount(RoutingContext rc) { .end(String.valueOf(streamingSubscribedCount.get())); } + @Route(path = "/test/queue/childCount/:taskId", methods = {Route.HttpMethod.GET}, produces = {TEXT_PLAIN}) + public void getChildQueueCount(@Param String taskId, RoutingContext rc) { + int count = testUtilsBean.getChildQueueCount(taskId); + rc.response() + .setStatusCode(200) + .end(String.valueOf(count)); + } + @Route(path = "/test/task/:taskId/config/:configId", methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) public void deleteTaskPushNotificationConfig(@Param String taskId, @Param String configId, RoutingContext rc) { try { diff --git a/reference/jsonrpc/src/test/resources/application.properties b/reference/jsonrpc/src/test/resources/application.properties index d3366bece..7b9cea9cc 100644 --- a/reference/jsonrpc/src/test/resources/application.properties +++ b/reference/jsonrpc/src/test/resources/application.properties @@ -1 +1 @@ -quarkus.arc.selected-alternatives=io.a2a.server.apps.common.TestHttpClient \ No newline at end of file +quarkus.arc.selected-alternatives=io.a2a.server.apps.common.TestHttpClient diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java index 24d49c9a0..6cda8dc8f 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java @@ -141,6 +141,14 @@ public void getStreamingSubscribedCount(RoutingContext rc) { .end(String.valueOf(streamingSubscribedCount.get())); } + @Route(path = "/test/queue/childCount/:taskId", methods = {Route.HttpMethod.GET}, produces = {TEXT_PLAIN}) + public void getChildQueueCount(@Param String taskId, RoutingContext rc) { + int count = testUtilsBean.getChildQueueCount(taskId); + rc.response() + .setStatusCode(200) + .end(String.valueOf(count)); + } + @Route(path = "/test/task/:taskId/config/:configId", methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) public void deleteTaskPushNotificationConfig(@Param String taskId, @Param String configId, RoutingContext rc) { try { diff --git a/server-common/pom.xml b/server-common/pom.xml index 5a5a4f368..ca78345e4 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -91,6 +91,10 @@ logback-classic test + + org.eclipse.microprofile.config + microprofile-config-api + diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index f43aec9cc..0be198cbf 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -10,8 +10,11 @@ import mutiny.zero.BackpressureStrategy; import mutiny.zero.TubeConfiguration; import mutiny.zero.ZeroPublisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class EventConsumer { + private static final Logger LOGGER = LoggerFactory.getLogger(EventConsumer.class); private final EventQueue queue; private Throwable error; @@ -21,6 +24,7 @@ public class EventConsumer { public EventConsumer(EventQueue queue) { this.queue = queue; + LOGGER.debug("EventConsumer created with queue {}", System.identityHashCode(queue)); } public Event consumeOne() throws A2AServerException, EventQueueClosedException { @@ -107,4 +111,11 @@ public EnhancedRunnable.DoneCallback createAgentRunnableDoneCallback() { } }; } + + public void close() { + // Close the queue to stop the polling loop in consumeAll() + // This will cause EventQueueClosedException and exit the while(true) loop + LOGGER.debug("EventConsumer closing queue {}", System.identityHashCode(queue)); + queue.close(); + } } diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 3e7ede598..6f848af6a 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -49,6 +49,8 @@ static EventQueueBuilder builder() { public static class EventQueueBuilder { private int queueSize = DEFAULT_QUEUE_SIZE; private EventEnqueueHook hook; + private String taskId; + private Runnable onCloseCallback; public EventQueueBuilder queueSize(int queueSize) { this.queueSize = queueSize; @@ -60,9 +62,19 @@ public EventQueueBuilder hook(EventEnqueueHook hook) { return this; } + public EventQueueBuilder taskId(String taskId) { + this.taskId = taskId; + return this; + } + + public EventQueueBuilder onClose(Runnable onCloseCallback) { + this.onCloseCallback = onCloseCallback; + return this; + } + public EventQueue build() { - if (hook != null) { - return new MainQueue(queueSize, hook); + if (hook != null || onCloseCallback != null) { + return new MainQueue(queueSize, hook, taskId, onCloseCallback); } else { return new MainQueue(queueSize); } @@ -111,11 +123,14 @@ public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException return event; } try { + LOGGER.trace("Polling queue {} (wait={}ms)", System.identityHashCode(this), waitMilliSeconds); Event event = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); if (event != null) { // Call toString() since for errors we don't really want the full stacktrace LOGGER.debug("Dequeued event (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); semaphore.release(); + } else { + LOGGER.trace("Dequeue timeout (null) from queue {}", System.identityHashCode(this)); } return event; } catch (InterruptedException e) { @@ -136,15 +151,26 @@ public void taskDone() { public abstract void close(boolean immediate); + /** + * Close this queue with control over parent notification (ChildQueue only). + * + * @param immediate If true, clear all pending events immediately + * @param notifyParent If true, notify parent (standard behavior). If false, close this queue + * without decrementing parent's reference count (used for non-blocking + * non-final tasks to keep MainQueue alive for resubscription) + * @throws UnsupportedOperationException if called on MainQueue + */ + public abstract void close(boolean immediate, boolean notifyParent); + public boolean isClosed() { return closed; } - public void doClose() { + protected void doClose() { doClose(false); } - public void doClose(boolean immediate) { + protected void doClose(boolean immediate) { synchronized (this) { if (closed) { return; @@ -166,25 +192,43 @@ static class MainQueue extends EventQueue { private final CountDownLatch pollingStartedLatch = new CountDownLatch(1); private final AtomicBoolean pollingStarted = new AtomicBoolean(false); private final EventEnqueueHook enqueueHook; + private final String taskId; + private final Runnable onCloseCallback; MainQueue() { super(); this.enqueueHook = null; + this.taskId = null; + this.onCloseCallback = null; } MainQueue(int queueSize) { super(queueSize); this.enqueueHook = null; + this.taskId = null; + this.onCloseCallback = null; } MainQueue(EventEnqueueHook hook) { super(); this.enqueueHook = hook; + this.taskId = null; + this.onCloseCallback = null; } MainQueue(int queueSize, EventEnqueueHook hook) { super(queueSize); this.enqueueHook = hook; + this.taskId = null; + this.onCloseCallback = null; + } + + MainQueue(int queueSize, EventEnqueueHook hook, String taskId, Runnable onCloseCallback) { + super(queueSize); + this.enqueueHook = hook; + this.taskId = taskId; + this.onCloseCallback = onCloseCallback; + LOGGER.debug("Created MainQueue for task {} with onClose callback: {}", taskId, onCloseCallback != null); } EventQueue tap() { @@ -218,6 +262,42 @@ void signalQueuePollerStarted() { pollingStarted.set(true); } + void childClosing(ChildQueue child, boolean immediate) { + children.remove(child); // Remove the closing child + + // Only close MainQueue if immediate OR no children left + if (immediate || children.isEmpty()) { + LOGGER.debug("MainQueue closing: immediate={}, children.isEmpty()={}", immediate, children.isEmpty()); + this.doClose(immediate); + } else { + LOGGER.debug("MainQueue staying open: {} children remaining", children.size()); + } + } + + /** + * Get the count of active child queues. + * Used for testing to verify reference counting mechanism. + * + * @return number of active child queues + */ + public int getActiveChildCount() { + return children.size(); + } + + @Override + protected void doClose(boolean immediate) { + super.doClose(immediate); + // Invoke callback after closing to notify QueueManager + if (onCloseCallback != null) { + LOGGER.debug("Invoking onClose callback for task {}", taskId); + try { + onCloseCallback.run(); + } catch (Exception e) { + LOGGER.error("Error in onClose callback for task {}", taskId, e); + } + } + } + @Override public void close() { close(false); @@ -226,7 +306,16 @@ public void close() { @Override public void close(boolean immediate) { doClose(immediate); - children.forEach(child -> child.doClose(immediate)); + if (immediate) { + // Force-close all remaining children + children.forEach(child -> child.doClose(immediate)); + } + children.clear(); + } + + @Override + public void close(boolean immediate, boolean notifyParent) { + throw new UnsupportedOperationException("MainQueue does not support notifyParent parameter - use close(boolean) instead"); } } @@ -263,12 +352,22 @@ void signalQueuePollerStarted() { @Override public void close() { - parent.close(); + close(false); } @Override public void close(boolean immediate) { - parent.close(immediate); + close(immediate, true); + } + + @Override + public void close(boolean immediate, boolean notifyParent) { + this.doClose(immediate); // Close self first + if (notifyParent) { + parent.childClosing(this, immediate); // Notify parent + } else { + LOGGER.debug("Closing {} without notifying parent (keeping MainQueue alive)", this); + } } } } diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index 41dfac222..d096527d4 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -4,9 +4,13 @@ import java.util.concurrent.ConcurrentMap; import jakarta.enterprise.context.ApplicationScoped; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @ApplicationScoped public class InMemoryQueueManager implements QueueManager { + private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryQueueManager.class); + private final ConcurrentMap queues = new ConcurrentHashMap<>(); private final EventQueueFactory factory; @@ -43,12 +47,23 @@ public void close(String taskId) { if (existing == null) { throw new NoTaskQueueException(); } + // Close the queue to stop EventConsumer polling loop + LOGGER.debug("Closing queue {} for task {}", System.identityHashCode(existing), taskId); + existing.close(); } @Override public EventQueue createOrTap(String taskId) { - + LOGGER.info("createOrTap called for task {}, current map size: {}", taskId, queues.size()); EventQueue existing = queues.get(taskId); + + // Lazy cleanup: remove closed queues from map + if (existing != null && existing.isClosed()) { + LOGGER.debug("Removing closed queue {} for task {}", System.identityHashCode(existing), taskId); + queues.remove(taskId); + existing = null; + } + EventQueue newQueue = null; if (existing == null) { // Use builder pattern for cleaner queue creation @@ -57,7 +72,18 @@ public EventQueue createOrTap(String taskId) { // Make sure an existing queue has not been added in the meantime existing = queues.putIfAbsent(taskId, newQueue); } - return existing == null ? newQueue : existing.tap(); + + EventQueue main = existing == null ? newQueue : existing; + EventQueue result = main.tap(); // Always return ChildQueue + + if (existing == null) { + LOGGER.info("Created new MainQueue {} for task {}, returning ChildQueue {} (map size: {})", + System.identityHashCode(main), taskId, System.identityHashCode(result), queues.size()); + } else { + LOGGER.debug("Tapped existing MainQueue {} -> ChildQueue {} for task {}", + System.identityHashCode(main), System.identityHashCode(result), taskId); + } + return result; } @Override @@ -65,11 +91,36 @@ public void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedExcep eventQueue.awaitQueuePollerStart(); } - private static class DefaultEventQueueFactory implements EventQueueFactory { + @Override + public int getActiveChildQueueCount(String taskId) { + EventQueue queue = queues.get(taskId); + if (queue == null || queue.isClosed()) { + return -1; // Queue doesn't exist or is closed + } + // Cast to MainQueue to access getActiveChildCount() + if (queue instanceof EventQueue.MainQueue mainQueue) { + return mainQueue.getActiveChildCount(); + } + // This should not happen in normal operation since we only store MainQueues + return -1; + } + + private class DefaultEventQueueFactory implements EventQueueFactory { @Override public EventQueue.EventQueueBuilder builder(String taskId) { - // Default implementation doesn't need task-specific configuration - return EventQueue.builder(); + // Return builder with callback that removes queue from map when closed + return EventQueue.builder() + .taskId(taskId) + .onClose(() -> { + LOGGER.debug("Queue close callback invoked for task {}, removing from map", taskId); + EventQueue removed = queues.remove(taskId); + if (removed != null) { + LOGGER.info("Removed closed queue for task {} from QueueManager (map size: {})", + taskId, queues.size()); + } else { + LOGGER.debug("Queue for task {} was already removed from map", taskId); + } + }); } } } diff --git a/server-common/src/main/java/io/a2a/server/events/QueueManager.java b/server-common/src/main/java/io/a2a/server/events/QueueManager.java index ebdf67e74..331afef09 100644 --- a/server-common/src/main/java/io/a2a/server/events/QueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/QueueManager.java @@ -16,4 +16,13 @@ public interface QueueManager { default EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { return EventQueue.builder(); } + + /** + * Get the count of active child queues for a given task. + * Used for testing to verify reference counting mechanism. + * + * @param taskId the task ID + * @return number of active child queues, or -1 if queue doesn't exist + */ + int getActiveChildQueueCount(String taskId); } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 7bf2b8af8..23d02b5a3 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -138,7 +138,7 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws taskStore, null); - ResultAggregator resultAggregator = new ResultAggregator(taskManager, null); + ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); EventQueue queue = queueManager.tap(task.getId()); if (queue == null) { @@ -180,7 +180,7 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte LOGGER.debug("Request context taskId: {}", taskId); EventQueue queue = queueManager.createOrTap(taskId); - ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); + ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor); boolean blocking = true; // Default to blocking behavior if (params.configuration() != null && Boolean.FALSE.equals(params.configuration().blocking())) { @@ -201,7 +201,7 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte // any errors thrown by the producerRunnable are not picked up by the consumer producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); etai = resultAggregator.consumeAndBreakOnInterrupt(consumer, blocking, pushNotificationCallback); - + if (etai == null) { LOGGER.debug("No result, throwing InternalError"); throw new InternalError("No result"); @@ -217,12 +217,12 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte // Send push notification after initial return (for both blocking and non-blocking) pushNotificationCallback.run(); } finally { - if (interruptedOrNonBlocking) { - CompletableFuture cleanupTask = CompletableFuture.runAsync(() -> cleanupProducer(taskId), executor); - trackBackgroundTask(cleanupTask); - } else { - cleanupProducer(taskId); - } + // Remove agent from map immediately to prevent accumulation + CompletableFuture agentFuture = runningAgents.remove(taskId); + LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId, runningAgents.size()); + + // Track cleanup as background task to avoid blocking Vert.x threads + trackBackgroundTask(cleanupProducer(agentFuture, taskId, queue, false)); } LOGGER.debug("Returning: {}", etai.eventType()); @@ -232,12 +232,14 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte @Override public Flow.Publisher onMessageSendStream( MessageSendParams params, ServerCallContext context) throws JSONRPCError { - LOGGER.debug("onMessageSendStream - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); + LOGGER.debug("onMessageSendStream START - task: {}; context: {}; runningAgents: {}; backgroundTasks: {}", + params.message().getTaskId(), params.message().getContextId(), runningAgents.size(), backgroundTasks.size()); MessageSendSetup mss = initMessageSend(params, context); AtomicReference taskId = new AtomicReference<>(mss.requestContext.getTaskId()); EventQueue queue = queueManager.createOrTap(taskId.get()); - ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null); + LOGGER.debug("Created/tapped queue for task {}: {}", taskId.get(), queue); + ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor); EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId.get(), mss.requestContext, queue); @@ -289,61 +291,90 @@ public Flow.Publisher onMessageSendStream( Flow.Publisher finalPublisher = convertingProcessor(eventPublisher, event -> (StreamingEventKind) event); // Wrap publisher to detect client disconnect and continue background consumption - return subscriber -> finalPublisher.subscribe(new Flow.Subscriber() { - private Flow.Subscription subscription; - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - // Wrap subscription to detect cancellation - subscriber.onSubscribe(new Flow.Subscription() { - @Override - public void request(long n) { - subscription.request(n); - } - - @Override - public void cancel() { - LOGGER.debug("Client cancelled subscription for task {}, starting background consumption", taskId.get()); - startBackgroundConsumption(); - subscription.cancel(); - } - }); - } + return subscriber -> { + LOGGER.debug("Creating subscription wrapper for task {}", taskId.get()); + finalPublisher.subscribe(new Flow.Subscriber() { + private Flow.Subscription subscription; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + LOGGER.debug("onSubscribe called for task {}", taskId.get()); + this.subscription = subscription; + // Wrap subscription to detect cancellation + subscriber.onSubscribe(new Flow.Subscription() { + @Override + public void request(long n) { + LOGGER.debug("Subscription.request({}) for task {}", n, taskId.get()); + subscription.request(n); + } - @Override - public void onNext(StreamingEventKind item) { - subscriber.onNext(item); - } + @Override + public void cancel() { + LOGGER.debug("Client cancelled subscription for task {}, starting background consumption", taskId.get()); + startBackgroundConsumption(); + subscription.cancel(); + } + }); + } - @Override - public void onError(Throwable throwable) { - subscriber.onError(throwable); - } + @Override + public void onNext(StreamingEventKind item) { + LOGGER.debug("onNext: {} for task {}", item.getClass().getSimpleName(), taskId.get()); + subscriber.onNext(item); + } - @Override - public void onComplete() { - subscriber.onComplete(); - } + @Override + public void onError(Throwable throwable) { + LOGGER.error("onError for task {}", taskId.get(), throwable); + subscriber.onError(throwable); + } - private void startBackgroundConsumption() { - if (backgroundConsumeStarted.compareAndSet(false, true)) { - // Client disconnected: continue consuming and persisting events in background - CompletableFuture bgTask = CompletableFuture.runAsync(() -> { - try { - resultAggregator.consumeAll(consumer); - LOGGER.debug("Background consumption completed for task {}", taskId.get()); - } catch (Exception e) { - LOGGER.error("Error during background consumption for task {}", taskId.get(), e); + @Override + public void onComplete() { + LOGGER.debug("onComplete for task {}", taskId.get()); + try { + subscriber.onComplete(); + } catch (IllegalStateException e) { + // Client already disconnected and response closed - this is expected + // for streaming responses where client disconnect triggers background + // consumption. Log and ignore. + if (e.getMessage() != null && e.getMessage().contains("Response has already been written")) { + LOGGER.debug("Client disconnected before onComplete, response already closed for task {}", taskId.get()); + } else { + throw e; } - }, executor); - trackBackgroundTask(bgTask); + } } - } - }); + + private void startBackgroundConsumption() { + if (backgroundConsumeStarted.compareAndSet(false, true)) { + LOGGER.debug("Starting background consumption for task {}", taskId.get()); + // Client disconnected: continue consuming and persisting events in background + CompletableFuture bgTask = CompletableFuture.runAsync(() -> { + try { + LOGGER.debug("Background consumption thread started for task {}", taskId.get()); + resultAggregator.consumeAll(consumer); + LOGGER.debug("Background consumption completed for task {}", taskId.get()); + } catch (Exception e) { + LOGGER.error("Error during background consumption for task {}", taskId.get(), e); + } + }, executor); + trackBackgroundTask(bgTask); + } else { + LOGGER.debug("Background consumption already started for task {}", taskId.get()); + } + } + }); + }; } finally { - CompletableFuture cleanupTask = CompletableFuture.runAsync(() -> cleanupProducer(taskId.get()), executor); - trackBackgroundTask(cleanupTask); + LOGGER.debug("onMessageSendStream FINALLY - task: {}; runningAgents: {}; backgroundTasks: {}", + taskId.get(), runningAgents.size(), backgroundTasks.size()); + + // Remove agent from map immediately to prevent accumulation + CompletableFuture agentFuture = runningAgents.remove(taskId.get()); + LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId.get(), runningAgents.size()); + + trackBackgroundTask(cleanupProducer(agentFuture, taskId.get(), queue, true)); } } @@ -402,11 +433,18 @@ public Flow.Publisher onResubscribeToTask( } TaskManager taskManager = new TaskManager(task.getId(), task.getContextId(), taskStore, null); - ResultAggregator resultAggregator = new ResultAggregator(taskManager, null); + ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); EventQueue queue = queueManager.tap(task.getId()); if (queue == null) { - throw new TaskNotFoundError(); + // If task is in final state, queue legitimately doesn't exist anymore + if (task.getStatus().state().isFinal()) { + throw new TaskNotFoundError(); + } + // For non-final tasks, recreate the queue so client can receive future events + // (Note: historical events from before queue closed are not available) + LOGGER.debug("Queue not found for active task {}, creating new queue for future events", task.getId()); + queue = queueManager.createOrTap(task.getId()); } EventConsumer consumer = new EventConsumer(queue); @@ -456,38 +494,64 @@ private boolean shouldAddPushInfo(MessageSendParams params) { return pushConfigStore != null && params.configuration() != null && params.configuration().pushNotificationConfig() != null; } + /** + * Register and execute the agent asynchronously in the agent-executor thread pool. + * + * Queue Lifecycle Architecture: + * - Agent-executor thread: Executes agent and enqueues events, returns immediately + * - Vert.x worker thread (consumer): Polls queue, processes events, closes queue on final event + * - Background cleanup: Manages ChildQueue/MainQueue lifecycle after agent completes + * + * This design avoids blocking agent-executor threads waiting for consumer polling to start, + * eliminating cascading delays when Vert.x worker threads are busy. + */ private EnhancedRunnable registerAndExecuteAgentAsync(String taskId, RequestContext requestContext, EventQueue queue) { + LOGGER.debug("Registering agent execution for task {}, runningAgents.size() before: {}", taskId, runningAgents.size()); + logThreadStats("AGENT START"); EnhancedRunnable runnable = new EnhancedRunnable() { @Override public void run() { + LOGGER.debug("Agent execution starting for task {}", taskId); agentExecutor.execute(requestContext, queue); - try { - queueManager.awaitQueuePollerStart(queue); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + LOGGER.debug("Agent execution completed for task {}", taskId); + // No longer wait for queue poller to start - the consumer (which is guaranteed + // to be running on the Vert.x worker thread) will handle queue lifecycle. + // This avoids blocking agent-executor threads waiting for worker threads. } }; CompletableFuture cf = CompletableFuture.runAsync(runnable, executor) .whenComplete((v, err) -> { if (err != null) { + LOGGER.error("Agent execution failed for task {}", taskId, err); runnable.setError(err); + // Don't close queue here - let the consumer handle it via error callback + // This ensures the consumer (which may not have started polling yet) gets the error } - queue.close(); + // Queue lifecycle is now managed entirely by EventConsumer.consumeAll() + // which closes the queue on final events. No need to close here. + logThreadStats("AGENT COMPLETE END"); runnable.invokeDoneCallbacks(); }); runningAgents.put(taskId, cf); + LOGGER.debug("Registered agent for task {}, runningAgents.size() after: {}", taskId, runningAgents.size()); return runnable; } private void trackBackgroundTask(CompletableFuture task) { backgroundTasks.add(task); + LOGGER.debug("Tracking background task (total: {}): {}", backgroundTasks.size(), task); task.whenComplete((result, throwable) -> { try { if (throwable != null) { - if (throwable instanceof java.util.concurrent.CancellationException) { + // Unwrap CompletionException to check for CancellationException + Throwable cause = throwable; + if (throwable instanceof java.util.concurrent.CompletionException && throwable.getCause() != null) { + cause = throwable.getCause(); + } + + if (cause instanceof java.util.concurrent.CancellationException) { LOGGER.debug("Background task cancelled: {}", task); } else { LOGGER.error("Background task failed", throwable); @@ -495,17 +559,48 @@ private void trackBackgroundTask(CompletableFuture task) { } } finally { backgroundTasks.remove(task); + LOGGER.debug("Removed background task (remaining: {}): {}", backgroundTasks.size(), task); } }); } - private void cleanupProducer(String taskId) { - // TODO the Python implementation waits for the producerRunnable - runningAgents.get(taskId) - .whenComplete((v, t) -> { - queueManager.close(taskId); - runningAgents.remove(taskId); - }); + private CompletableFuture cleanupProducer(CompletableFuture agentFuture, String taskId, EventQueue queue, boolean isStreaming) { + LOGGER.debug("Starting cleanup for task {} (streaming={})", taskId, isStreaming); + logThreadStats("CLEANUP START"); + + if (agentFuture == null) { + LOGGER.debug("No running agent found for task {}, cleanup complete", taskId); + return CompletableFuture.completedFuture(null); // Return completed future + } + + return agentFuture.whenComplete((v, t) -> { + if (t != null) { + LOGGER.debug("Agent completed with error for task {}", taskId, t); + } else { + LOGGER.debug("Agent completed successfully for task {}", taskId); + } + + // Determine if we should keep the MainQueue alive + // For non-streaming, non-blocking requests with non-final tasks, we close the ChildQueue + // but keep the MainQueue alive to support resubscription + boolean keepMainQueueAlive = false; + if (!isStreaming) { + Task task = taskStore.get(taskId); + if (task != null && !task.getStatus().state().isFinal()) { + keepMainQueueAlive = true; + LOGGER.debug("Non-streaming call with non-final task {} (state={}), closing ChildQueue but keeping MainQueue alive for resubscription", + taskId, task.getStatus().state()); + } + } + + LOGGER.debug("{} call, closing queue for task {} (immediate=false, notifyParent={})", + isStreaming ? "Streaming" : "Non-streaming", taskId, !keepMainQueueAlive); + + // Close the ChildQueue, optionally keeping MainQueue alive + queue.close(false, !keepMainQueueAlive); + + logThreadStats("CLEANUP END"); + }); } private MessageSendSetup initMessageSend(MessageSendParams params, ServerCallContext context) { @@ -545,5 +640,47 @@ private void sendPushNotification(String taskId, ResultAggregator resultAggregat } } + /** + * Log current thread and resource statistics for debugging. + * Only logs when DEBUG level is enabled. Call this from debugger or add strategic + * calls during investigation. In production with INFO logging, this is a no-op. + */ + @SuppressWarnings("unused") // Used for debugging + private void logThreadStats(String label) { + // Early return if debug logging is not enabled to avoid overhead + if (!LOGGER.isDebugEnabled()) { + return; + } + + ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + int activeThreads = rootGroup.activeCount(); + + LOGGER.debug("=== THREAD STATS: {} ===", label); + LOGGER.debug("Active threads: {}", activeThreads); + LOGGER.debug("Running agents: {}", runningAgents.size()); + LOGGER.debug("Background tasks: {}", backgroundTasks.size()); + LOGGER.debug("Queue manager active queues: {}", queueManager.getClass().getSimpleName()); + + // List running agents + if (!runningAgents.isEmpty()) { + LOGGER.debug("Running agent tasks:"); + runningAgents.forEach((taskId, future) -> + LOGGER.debug(" - Task {}: {}", taskId, future.isDone() ? "DONE" : "RUNNING") + ); + } + + // List background tasks + if (!backgroundTasks.isEmpty()) { + LOGGER.debug("Background tasks:"); + backgroundTasks.forEach(task -> + LOGGER.debug(" - {}: {}", task, task.isDone() ? "DONE" : "RUNNING") + ); + } + LOGGER.debug("=== END THREAD STATS ==="); + } + private record MessageSendSetup(TaskManager taskManager, Task task, RequestContext requestContext) {} } diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index a30d60b01..b19427ab0 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -5,6 +5,8 @@ import static io.a2a.server.util.async.AsyncUtils.processor; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -22,11 +24,13 @@ public class ResultAggregator { private final TaskManager taskManager; + private final Executor executor; private volatile Message message; - public ResultAggregator(TaskManager taskManager, Message message) { + public ResultAggregator(TaskManager taskManager, Message message, Executor executor) { this.taskManager = taskManager; this.message = message; + this.executor = executor; } public EventKind getCurrentResult() { @@ -40,7 +44,12 @@ public Flow.Publisher consumeAndEmit(EventConsumer consumer) { Flow.Publisher all = consumer.consumeAll(); return processor(createTubeConfig(), all, ((errorConsumer, event) -> { - callTaskManagerProcess(event); + try { + callTaskManagerProcess(event); + } catch (A2AServerException e) { + errorConsumer.accept(e); + return false; + } return true; })); } @@ -60,7 +69,12 @@ public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { return false; } } - callTaskManagerProcess(event); + try { + callTaskManagerProcess(event); + } catch (A2AServerException e) { + error.set(e); + return false; + } return true; }, error::set); @@ -84,24 +98,47 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, Flow.Publisher all = consumer.consumeAll(); AtomicReference message = new AtomicReference<>(); AtomicBoolean interrupted = new AtomicBoolean(false); + AtomicBoolean shouldCloseConsumer = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); - consumer( + CompletableFuture completionFuture = new CompletableFuture<>(); + + // CRITICAL: The subscription itself must run on a background thread to avoid blocking + // the Vert.x worker thread. EventConsumer.consumeAll() starts a polling loop that + // blocks in dequeueEvent(), so we must subscribe from a background thread. + // Use the @Internal executor (not ForkJoinPool.commonPool) to avoid saturation + // during concurrent request bursts. + CompletableFuture.runAsync(() -> { + consumer( createTubeConfig(), all, - (event -> { + (event) -> { + // Handle Throwable events if (event instanceof Throwable t) { errorRef.set(t); + completionFuture.completeExceptionally(t); return false; } + + // Handle Message events if (event instanceof Message msg) { - this.message = msg; + ResultAggregator.this.message = msg; message.set(msg); + completionFuture.complete(null); return false; } - callTaskManagerProcess(event); + // Process event through TaskManager + try { + callTaskManagerProcess(event); + } catch (A2AServerException e) { + errorRef.set(e); + completionFuture.completeExceptionally(e); + return false; + } + // Determine interrupt behavior boolean shouldInterrupt = false; + boolean continueInBackground = false; boolean isAuthRequired = (event instanceof Task task && task.getStatus().state() == TaskState.AUTH_REQUIRED) || (event instanceof TaskStatusUpdateEvent tsue && tsue.getStatus().state() == TaskState.AUTH_REQUIRED); @@ -114,21 +151,75 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, // new request is expected in order for the agent to make progress, // so the agent should exit. shouldInterrupt = true; + continueInBackground = true; } - // For non-blocking calls, interrupt as soon as a task is available. else if (!blocking) { + // For non-blocking calls, interrupt as soon as a task is available. shouldInterrupt = true; + continueInBackground = true; + } + else { + // For blocking calls, interrupt when we get any task-related event (EventKind except Message) + // Cancel subscription to free resources (client can resubscribe if needed) + shouldInterrupt = true; + continueInBackground = false; } if (shouldInterrupt) { - // Continue consuming the rest of the events in the background. - CompletableFuture.runAsync(() -> continueConsuming(all, eventCallback)); + // Complete the future to unblock the main thread interrupted.set(true); - return false; + completionFuture.complete(null); + + if (continueInBackground) { + // Continue consuming in background - keep requesting events + return true; + } else { + // Blocking call - cancel subscription AND close consumer to stop polling loop + // We need to close the consumer after the consumer() call completes + shouldCloseConsumer.set(true); + return false; + } } + + // Continue processing return true; - }), - errorRef::set); + }, + throwable -> { + // Handle onError and onComplete + if (throwable != null) { + errorRef.set(throwable); + completionFuture.completeExceptionally(throwable); + } else { + // onComplete + completionFuture.complete(null); + } + } + ); + }, executor); + + // Wait for completion or interruption + try { + completionFuture.join(); + } catch (CompletionException e) { + // CompletionException wraps the actual exception + Throwable cause = e.getCause(); + if (cause != null) { + Utils.rethrow(cause); + } else { + throw e; + } + } + + // Close consumer if blocking interrupt occurred + if (shouldCloseConsumer.get()) { + // Close the EventConsumer's queue to stop the infinite polling loop + // This will cause EventQueueClosedException and exit the while(true) loop + consumer.close(); + } + + // Background consumption continues automatically via the subscription + // No need to create a new publisher - returning true in nextFunction + // keeps the subscription alive Throwable error = errorRef.get(); if (error != null) { @@ -139,26 +230,8 @@ else if (!blocking) { message.get() != null ? message.get() : taskManager.getTask(), interrupted.get()); } - private void continueConsuming(Flow.Publisher all, Runnable eventCallback) { - consumer(createTubeConfig(), - all, - event -> { - callTaskManagerProcess(event); - if (eventCallback != null) { - eventCallback.run(); - } - return true; - }, - t -> {}); - } - - private void callTaskManagerProcess(Event event) { - try { - taskManager.process(event); - } catch (A2AServerException e) { - // TODO Decide what to do in case of failure - e.printStackTrace(); - } + private void callTaskManagerProcess(Event event) throws A2AServerException { + taskManager.process(event); } public record EventTypeAndInterrupt(EventKind eventType, boolean interrupted) { diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 067aa2832..523b9346f 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -94,7 +94,7 @@ Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { public Event process(Event event) throws A2AServerException { if (event instanceof Task task) { - saveTask(task); + saveTaskEvent(task); } else if (event instanceof TaskStatusUpdateEvent taskStatusUpdateEvent) { saveTaskEvent(taskStatusUpdateEvent); } else if (event instanceof TaskArtifactUpdateEvent taskArtifactUpdateEvent) { diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index 27b4ac12b..d6f0e996e 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -2,26 +2,65 @@ import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @ApplicationScoped public class AsyncExecutorProducer { + private static final Logger LOGGER = LoggerFactory.getLogger(AsyncExecutorProducer.class); + + @ConfigProperty(name = "a2a.executor.core-pool-size", defaultValue = "5") + int corePoolSize; + + @ConfigProperty(name = "a2a.executor.max-pool-size", defaultValue = "50") + int maxPoolSize; + + @ConfigProperty(name = "a2a.executor.keep-alive-seconds", defaultValue = "60") + long keepAliveSeconds; + private ExecutorService executor; @PostConstruct public void init() { - executor = Executors.newCachedThreadPool(); + LOGGER.info("Initializing async executor: corePoolSize={}, maxPoolSize={}, keepAliveSeconds={}", + corePoolSize, maxPoolSize, keepAliveSeconds); + + executor = new ThreadPoolExecutor( + corePoolSize, + maxPoolSize, + keepAliveSeconds, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + new A2AThreadFactory() + ); } @PreDestroy public void close() { + LOGGER.info("Shutting down async executor"); executor.shutdown(); + try { + if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { + LOGGER.warn("Executor did not terminate in 10 seconds, forcing shutdown"); + executor.shutdownNow(); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted while waiting for executor shutdown", e); + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } } @Produces @@ -30,4 +69,16 @@ public Executor produce() { return executor; } + private static class A2AThreadFactory implements ThreadFactory { + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix = "a2a-agent-executor-"; + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement()); + t.setDaemon(false); + return t; + } + } + } diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 67757cc69..ff79d62c2 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -354,15 +354,50 @@ public void testCloseIdempotent() throws Exception { } /** - * Test that child queues are closed when parent closes. + * Test that child queues are NOT automatically closed when parent closes gracefully. + * Children must close themselves, which then notifies parent via reference counting. */ @Test public void testCloseChildQueues() throws Exception { EventQueue childQueue = eventQueue.tap(); assertTrue(childQueue != null); + // Graceful close - parent closes but children remain open eventQueue.close(); assertTrue(eventQueue.isClosed()); - assertTrue(childQueue.isClosed()); + assertFalse(childQueue.isClosed()); // Child NOT closed on graceful parent close + + // Immediate close - parent force-closes all children + EventQueue parentQueue2 = EventQueue.builder().build(); + EventQueue childQueue2 = parentQueue2.tap(); + parentQueue2.close(true); // immediate=true + assertTrue(parentQueue2.isClosed()); + assertTrue(childQueue2.isClosed()); // Child IS closed on immediate parent close + } + + /** + * Test reference counting: MainQueue stays open while children are active, + * closes automatically when last child closes. + */ + @Test + public void testMainQueueReferenceCountingStaysOpenWithActiveChildren() throws Exception { + EventQueue mainQueue = EventQueue.builder().build(); + EventQueue child1 = mainQueue.tap(); + EventQueue child2 = mainQueue.tap(); + + // Close child1 + child1.close(); + + // MainQueue should still be open (child2 active) + assertFalse(mainQueue.isClosed()); + assertTrue(child1.isClosed()); + assertFalse(child2.isClosed()); + + // Close child2 + child2.close(); + + // Now MainQueue should auto-close (no children left) + assertTrue(mainQueue.isClosed()); + assertTrue(child2.isClosed()); } } diff --git a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java index a1f4e197a..9bd418403 100644 --- a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java @@ -115,9 +115,12 @@ public void testCreateOrTapNewQueue() { EventQueue result = queueManager.createOrTap(taskId); assertNotNull(result); - // Should be added to manager + // createOrTap now returns ChildQueue, not MainQueue + // MainQueue should be stored in manager EventQueue retrievedQueue = queueManager.get(taskId); - assertSame(result, retrievedQueue); + assertNotNull(retrievedQueue); + // Result should be a ChildQueue (cannot be tapped) + assertThrows(IllegalStateException.class, () -> result.tap()); } @Test @@ -204,15 +207,17 @@ public void testCreateOrTapRaceCondition() throws InterruptedException, Executio assertNotNull(result); } - // There should be exactly one queue in the manager + // There should be exactly one MainQueue in the manager EventQueue managerQueue = queueManager.get(taskId); assertNotNull(managerQueue); - // One of the results should be the main queue instance, the others should be tapped child queues. - long mainQueueCount = results.stream().filter(q -> q == managerQueue).count(); - assertEquals(1, mainQueueCount, "Exactly one thread should have created the main queue."); + // ALL results should be ChildQueues (cannot tap a ChildQueue) + for (EventQueue result : results) { + assertThrows(IllegalStateException.class, () -> result.tap()); + } - long childQueueCount = results.stream().filter(q -> q != managerQueue).count(); - assertEquals(results.size() - 1, childQueueCount, "All other threads should have received a tapped queue."); + // All ChildQueues should be distinct instances + long distinctCount = results.stream().distinct().count(); + assertEquals(results.size(), distinctCount, "All ChildQueues should be distinct instances"); } } diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 9951b7ba7..2fc55ea26 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -60,6 +60,65 @@ void setUp() { serverCallContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of(), Set.of()); } + /** + * Test that multiple blocking messages to the same task work correctly + * when agent doesn't emit final events (fire-and-forget pattern). + * This replicates TCK test: test_message_send_continue_task + */ + @Test + @Timeout(10) + void testBlockingMessageContinueTask() throws Exception { + String taskId = "continue-task-1"; + String contextId = "continue-ctx-1"; + + // Configure agent to NOT complete tasks (like TCK fire-and-forget agent) + agentExecutor.setExecuteCallback((context, queue) -> { + Task task = context.getTask(); + if (task == null) { + // First message: create SUBMITTED task + task = new Task.Builder() + .id(context.getTaskId()) + .contextId(context.getContextId()) + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + queue.enqueueEvent(task); + } + // Don't complete - just return (fire-and-forget) + }); + + // First blocking message - should return SUBMITTED task + Message message1 = new Message.Builder() + .messageId("msg-1") + .role(Message.Role.USER) + .parts(new TextPart("first message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params1 = new MessageSendParams(message1, null, null); + Object result1 = requestHandler.onMessageSend(params1, serverCallContext); + + assertTrue(result1 instanceof Task); + Task task1 = (Task) result1; + assertTrue(task1.getId().equals(taskId)); + assertTrue(task1.getStatus().state() == TaskState.SUBMITTED); + + // Second blocking message to SAME taskId - should not hang + Message message2 = new Message.Builder() + .messageId("msg-2") + .role(Message.Role.USER) + .parts(new TextPart("second message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params2 = new MessageSendParams(message2, null, null); + Object result2 = requestHandler.onMessageSend(params2, serverCallContext); + + // Should complete successfully (not timeout) + assertTrue(result2 instanceof Task); + } + /** * Test that background cleanup tasks are properly tracked and cleared. * Backported from Python test: test_background_cleanup_task_is_tracked_and_cleared diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java index 55b3b7160..a91ad2713 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -10,6 +10,8 @@ import static org.mockito.Mockito.when; import java.util.Collections; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; @@ -39,11 +41,14 @@ public class ResultAggregatorTest { private TaskManager mockTaskManager; private ResultAggregator aggregator; + // Use a real thread pool executor instead of direct executor + // to avoid blocking the calling thread during async operations + private final Executor testExecutor = Executors.newCachedThreadPool(); @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); - aggregator = new ResultAggregator(mockTaskManager, null); + aggregator = new ResultAggregator(mockTaskManager, null, testExecutor); } // Helper methods for creating sample data @@ -69,7 +74,7 @@ private Task createSampleTask(String taskId, TaskState statusState, String conte @Test void testConstructorWithMessage() { Message initialMessage = createSampleMessage("initial", "msg1", Message.Role.USER); - ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, initialMessage); + ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, initialMessage, testExecutor); // Test that the message is properly stored by checking getCurrentResult assertEquals(initialMessage, aggregatorWithMessage.getCurrentResult()); @@ -80,7 +85,7 @@ void testConstructorWithMessage() { @Test void testGetCurrentResultWithMessageSet() { Message sampleMessage = createSampleMessage("hola", "msg1", Message.Role.USER); - ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, sampleMessage); + ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, sampleMessage, testExecutor); EventKind result = aggregatorWithMessage.getCurrentResult(); @@ -115,7 +120,7 @@ void testConstructorStoresTaskManagerCorrectly() { @Test void testConstructorWithNullMessage() { - ResultAggregator aggregatorWithNullMessage = new ResultAggregator(mockTaskManager, null); + ResultAggregator aggregatorWithNullMessage = new ResultAggregator(mockTaskManager, null, testExecutor); Task expectedTask = createSampleTask("null_msg_task", TaskState.WORKING, "ctx1"); when(mockTaskManager.getTask()).thenReturn(expectedTask); @@ -175,7 +180,7 @@ void testMultipleGetCurrentResultCalls() { void testGetCurrentResultWithMessageTakesPrecedence() { // Test that when both message and task are available, message takes precedence Message message = createSampleMessage("priority message", "pri1", Message.Role.USER); - ResultAggregator messageAggregator = new ResultAggregator(mockTaskManager, message); + ResultAggregator messageAggregator = new ResultAggregator(mockTaskManager, message, testExecutor); // Even if we set up the task manager to return something, message should take precedence Task task = createSampleTask("should_not_be_returned", TaskState.WORKING, "ctx1"); diff --git a/tck/src/main/resources/application.properties b/tck/src/main/resources/application.properties index 63b5095da..6034e67b4 100644 --- a/tck/src/main/resources/application.properties +++ b/tck/src/main/resources/application.properties @@ -1,3 +1,20 @@ # Use the new gRPC implementation which uses the main HTTP port quarkus.grpc.server.use-separate-server=false %dev.quarkus.http.port=9999 + +# Thread pool configuration for TCK testing +# Limit max threads to prevent resource exhaustion in CI environments +a2a.executor.core-pool-size=5 +a2a.executor.max-pool-size=15 +a2a.executor.keep-alive-seconds=60 + +# Enable debug logging for troubleshooting SSE streaming test timeouts +#quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +#quarkus.log.category."io.a2a.server.events".level=DEBUG +#quarkus.log.category."io.a2a.server.tasks".level=DEBUG + +# Log to file for analysis +quarkus.log.file.enable=true +quarkus.log.file.path=target/tck-test.log +quarkus.log.file.level=DEBUG +quarkus.log.console.level=INFO diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 0abba1f77..9467ab297 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -4,19 +4,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.wildfly.common.Assert.assertNotNull; -import static org.wildfly.common.Assert.assertTrue; - -import io.a2a.client.Client; -import io.a2a.client.ClientBuilder; -import io.a2a.client.config.ClientConfig; -import io.a2a.client.ClientEvent; -import io.a2a.client.MessageEvent; -import io.a2a.client.TaskUpdateEvent; -import jakarta.ws.rs.core.MediaType; import java.io.EOFException; import java.io.IOException; @@ -27,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -36,11 +29,19 @@ import java.util.function.Consumer; import java.util.stream.Stream; -import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.ws.rs.core.MediaType; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.client.Client; +import io.a2a.client.ClientBuilder; +import io.a2a.client.ClientEvent; +import io.a2a.client.MessageEvent; +import io.a2a.client.TaskEvent; +import io.a2a.client.TaskUpdateEvent; +import io.a2a.client.config.ClientConfig; import io.a2a.spec.A2AClientException; -import io.a2a.spec.AgentCard; import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.Artifact; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -72,7 +73,6 @@ import io.a2a.spec.TransportProtocol; import io.a2a.spec.UnsupportedOperationError; import io.a2a.util.Utils; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -117,6 +117,7 @@ public abstract class AbstractA2AServerTest { protected final int serverPort; private Client client; private Client nonStreamingClient; + private Client pollingClient; protected AbstractA2AServerTest(int serverPort) { this.serverPort = serverPort; @@ -539,6 +540,173 @@ public void testResubscribeNoExistingTaskError() throws Exception { } } + /** + * Regression test for race condition where MainQueue closed when first ChildQueue closed, + * preventing resubscription. With reference counting, MainQueue stays alive while any + * ChildQueue exists, allowing successful concurrent operations. + * + * This test verifies that: + * 1. Multiple consumers can be active simultaneously + * 2. All consumers receive events while the MainQueue is alive + * 3. MainQueue doesn't close prematurely when earlier operations complete + */ + @Test + @Timeout(value = 1, unit = TimeUnit.MINUTES) + public void testMainQueueReferenceCountingWithMultipleConsumers() throws Exception { + saveTaskInTaskStore(MINIMAL_TASK); + try { + // 1. Ensure queue exists for the task + ensureQueueForTask(MINIMAL_TASK.getId()); + + // 2. First consumer subscribes and receives initial event + CountDownLatch firstConsumerLatch = new CountDownLatch(1); + AtomicReference firstConsumerEvent = new AtomicReference<>(); + AtomicBoolean firstUnexpectedEvent = new AtomicBoolean(false); + AtomicReference firstErrorRef = new AtomicReference<>(); + + BiConsumer firstConsumer = (event, agentCard) -> { + if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifact) { + firstConsumerEvent.set(artifact); + firstConsumerLatch.countDown(); + } else if (!(event instanceof TaskUpdateEvent)) { + firstUnexpectedEvent.set(true); + } + }; + + Consumer firstErrorHandler = error -> { + if (!isStreamClosedError(error)) { + firstErrorRef.set(error); + } + firstConsumerLatch.countDown(); + }; + + // Wait for first subscription to be established + CountDownLatch firstSubscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> firstSubscriptionLatch.countDown()); + + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), + List.of(firstConsumer), + firstErrorHandler); + + assertTrue(firstSubscriptionLatch.await(15, TimeUnit.SECONDS), "First subscription should be established"); + + // Enqueue first event + TaskArtifactUpdateEvent event1 = new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("artifact-1") + .parts(new TextPart("First artifact")) + .build()) + .build(); + enqueueEventOnServer(event1); + + // Wait for first consumer to receive event + assertTrue(firstConsumerLatch.await(15, TimeUnit.SECONDS), "First consumer should receive event"); + assertFalse(firstUnexpectedEvent.get()); + assertNull(firstErrorRef.get()); + assertNotNull(firstConsumerEvent.get()); + + // Verify we have multiple child queues (ensureQueue + first resubscribe) + int childCountBeforeSecond = getChildQueueCount(MINIMAL_TASK.getId()); + assertTrue(childCountBeforeSecond >= 2, "Should have at least 2 child queues"); + + // 3. Second consumer resubscribes while first is still active + // This simulates the Kafka replication race condition where resubscription happens + // while other consumers are still active. Without reference counting, the MainQueue + // might close when the ensureQueue ChildQueue closes, preventing this resubscription. + CountDownLatch secondConsumerLatch = new CountDownLatch(1); + AtomicReference secondConsumerEvent = new AtomicReference<>(); + AtomicBoolean secondUnexpectedEvent = new AtomicBoolean(false); + AtomicReference secondErrorRef = new AtomicReference<>(); + + BiConsumer secondConsumer = (event, agentCard) -> { + if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifact) { + secondConsumerEvent.set(artifact); + secondConsumerLatch.countDown(); + } else if (!(event instanceof TaskUpdateEvent)) { + secondUnexpectedEvent.set(true); + } + }; + + Consumer secondErrorHandler = error -> { + if (!isStreamClosedError(error)) { + secondErrorRef.set(error); + } + secondConsumerLatch.countDown(); + }; + + // Wait for second subscription to be established + CountDownLatch secondSubscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> secondSubscriptionLatch.countDown()); + + // This should succeed with reference counting because MainQueue stays alive + // while first consumer's ChildQueue exists + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), + List.of(secondConsumer), + secondErrorHandler); + + assertTrue(secondSubscriptionLatch.await(15, TimeUnit.SECONDS), "Second subscription should be established"); + + // Verify child queue count increased (now ensureQueue + first + second) + int childCountAfterSecond = getChildQueueCount(MINIMAL_TASK.getId()); + assertTrue(childCountAfterSecond > childCountBeforeSecond, + "Child queue count should increase after second resubscription"); + + // 4. Enqueue second event - both consumers should receive it + TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder() + .taskId(MINIMAL_TASK.getId()) + .contextId(MINIMAL_TASK.getContextId()) + .artifact(new Artifact.Builder() + .artifactId("artifact-2") + .parts(new TextPart("Second artifact")) + .build()) + .build(); + enqueueEventOnServer(event2); + + // Both consumers should receive the event + assertTrue(secondConsumerLatch.await(15, TimeUnit.SECONDS), "Second consumer should receive event"); + assertFalse(secondUnexpectedEvent.get()); + assertNull(secondErrorRef.get(), + "Resubscription should succeed with reference counting (MainQueue stays alive)"); + + TaskArtifactUpdateEvent receivedEvent = secondConsumerEvent.get(); + assertNotNull(receivedEvent); + assertEquals("artifact-2", receivedEvent.getArtifact().artifactId()); + assertEquals("Second artifact", ((TextPart) receivedEvent.getArtifact().parts().get(0)).getText()); + + } finally { + deleteTaskInTaskStore(MINIMAL_TASK.getId()); + } + } + + /** + * Wait for the child queue count to reach a specific value. + * Uses polling with sleep intervals, similar to awaitStreamingSubscription(). + * + * @param taskId The task ID + * @param expectedCount The expected child queue count + * @param timeoutMs Timeout in milliseconds + * @return true if count reached expected value within timeout, false otherwise + */ + private boolean waitForChildQueueCountToBe(String taskId, int expectedCount, long timeoutMs) { + long endTime = System.currentTimeMillis() + timeoutMs; + while (System.currentTimeMillis() < endTime) { + if (getChildQueueCount(taskId) == expectedCount) { + return true; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + return false; + } + @Test public void testListPushNotificationConfigWithConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); @@ -754,6 +922,148 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio } } + @Test + @Timeout(value = 1, unit = TimeUnit.MINUTES) + public void testNonBlockingWithMultipleMessages() throws Exception { + // 1. Send first non-blocking message to create task in WORKING state + Message message1 = new Message.Builder(MESSAGE) + .taskId("multi-event-test") + .contextId("test-context") + .parts(new TextPart("First request")) + .build(); + + AtomicReference taskIdRef = new AtomicReference<>(); + CountDownLatch firstTaskLatch = new CountDownLatch(1); + + BiConsumer firstMessageConsumer = (event, agentCard) -> { + if (event instanceof TaskEvent te) { + taskIdRef.set(te.getTask().getId()); + firstTaskLatch.countDown(); + } else if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { + taskIdRef.set(status.getTaskId()); + firstTaskLatch.countDown(); + } + }; + + // Non-blocking message creates task in WORKING state and returns immediately + // Queue stays open because task is not in final state + getPollingClient().sendMessage(message1, List.of(firstMessageConsumer), null); + + assertTrue(firstTaskLatch.await(10, TimeUnit.SECONDS)); + String taskId = taskIdRef.get(); + assertNotNull(taskId); + assertEquals("multi-event-test", taskId); + + // 2. Resubscribe to task (queue should still be open) + CountDownLatch resubEventLatch = new CountDownLatch(2); // artifact-2 + completion + List resubReceivedEvents = new CopyOnWriteArrayList<>(); + AtomicBoolean resubUnexpectedEvent = new AtomicBoolean(false); + AtomicReference resubErrorRef = new AtomicReference<>(); + + BiConsumer resubConsumer = (event, agentCard) -> { + if (event instanceof TaskUpdateEvent tue) { + resubReceivedEvents.add(tue.getUpdateEvent()); + resubEventLatch.countDown(); + } else { + resubUnexpectedEvent.set(true); + } + }; + + Consumer resubErrorHandler = error -> { + if (!isStreamClosedError(error)) { + resubErrorRef.set(error); + } + }; + + // Wait for subscription to be active + CountDownLatch subscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); + + getClient().resubscribe(new TaskIdParams(taskId), + List.of(resubConsumer), + resubErrorHandler); + + assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); + + // 3. Send second streaming message to same taskId + Message message2 = new Message.Builder(MESSAGE) + .taskId("multi-event-test") // Same taskId + .contextId("test-context") + .parts(new TextPart("Second request")) + .build(); + + CountDownLatch streamEventLatch = new CountDownLatch(2); // artifact-2 + completion + List streamReceivedEvents = new CopyOnWriteArrayList<>(); + AtomicBoolean streamUnexpectedEvent = new AtomicBoolean(false); + + BiConsumer streamConsumer = (event, agentCard) -> { + if (event instanceof TaskUpdateEvent tue) { + streamReceivedEvents.add(tue.getUpdateEvent()); + streamEventLatch.countDown(); + } else { + streamUnexpectedEvent.set(true); + } + }; + + // Streaming message adds artifact-2 and completes task + getClient().sendMessage(message2, List.of(streamConsumer), null); + + // 4. Verify both consumers received artifact-2 and completion + assertTrue(resubEventLatch.await(10, TimeUnit.SECONDS)); + assertTrue(streamEventLatch.await(10, TimeUnit.SECONDS)); + + assertFalse(resubUnexpectedEvent.get()); + assertFalse(streamUnexpectedEvent.get()); + assertNull(resubErrorRef.get()); + + // Both should have received 2 events: artifact-2 and completion + assertEquals(2, resubReceivedEvents.size()); + assertEquals(2, streamReceivedEvents.size()); + + // Verify resubscription events + long resubArtifactCount = resubReceivedEvents.stream() + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .count(); + assertEquals(1, resubArtifactCount); + + long resubCompletionCount = resubReceivedEvents.stream() + .filter(e -> e instanceof TaskStatusUpdateEvent) + .filter(e -> ((TaskStatusUpdateEvent) e).isFinal()) + .count(); + assertEquals(1, resubCompletionCount); + + // Verify streaming events + long streamArtifactCount = streamReceivedEvents.stream() + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .count(); + assertEquals(1, streamArtifactCount); + + long streamCompletionCount = streamReceivedEvents.stream() + .filter(e -> e instanceof TaskStatusUpdateEvent) + .filter(e -> ((TaskStatusUpdateEvent) e).isFinal()) + .count(); + assertEquals(1, streamCompletionCount); + + // Verify artifact-2 details from resubscription + TaskArtifactUpdateEvent resubArtifact = (TaskArtifactUpdateEvent) resubReceivedEvents.stream() + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .findFirst() + .orElseThrow(); + assertEquals("artifact-2", resubArtifact.getArtifact().artifactId()); + assertEquals("Second message artifact", + ((TextPart) resubArtifact.getArtifact().parts().get(0)).getText()); + + // Verify artifact-2 details from streaming + TaskArtifactUpdateEvent streamArtifact = (TaskArtifactUpdateEvent) streamReceivedEvents.stream() + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .findFirst() + .orElseThrow(); + assertEquals("artifact-2", streamArtifact.getArtifact().artifactId()); + assertEquals("Second message artifact", + ((TextPart) streamArtifact.getArtifact().parts().get(0)).getText()); + } + @Test public void testMalformedJSONRPCRequest() { // skip this test for non-JSONRPC transports @@ -1218,6 +1528,23 @@ private int getStreamingSubscribedCount() { } } + protected int getChildQueueCount(String taskId) { + HttpClient client = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + serverPort + "/test/queue/childCount/" + taskId)) + .GET() + .build(); + try { + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + String body = response.body().trim(); + return Integer.parseInt(body); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + protected void deletePushNotificationConfigInStore(String taskId, String configId) throws Exception { HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1268,6 +1595,16 @@ protected Client getNonStreamingClient() throws A2AClientException { return nonStreamingClient; } + /** + * Get a client configured for polling (non-blocking) operations. + */ + protected Client getPollingClient() throws A2AClientException { + if (pollingClient == null) { + pollingClient = createPollingClient(); + } + return pollingClient; + } + /** * Create a client with the specified streaming configuration. */ @@ -1317,4 +1654,23 @@ private ClientConfig createClientConfig(boolean streaming) { .build(); } + /** + * Create a client configured for polling (non-blocking) operations. + */ + private Client createPollingClient() throws A2AClientException { + AgentCard agentCard = createTestAgentCard(); + ClientConfig clientConfig = new ClientConfig.Builder() + .setStreaming(false) // Non-streaming + .setPolling(true) // Polling mode (translates to blocking=false on server) + .build(); + + ClientBuilder clientBuilder = Client + .builder(agentCard) + .clientConfig(clientConfig); + + configureTransport(clientBuilder); + + return clientBuilder.build(); + } + } \ No newline at end of file diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java index 9576da4a2..f472a37ab 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java @@ -3,11 +3,14 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import java.util.List; + import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import io.quarkus.arc.profile.IfBuildProfile; @@ -20,6 +23,26 @@ public AgentExecutor agentExecutor() { return new AgentExecutor() { @Override public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater updater = new TaskUpdater(context, eventQueue); + String taskId = context.getTaskId(); + + // Special handling for multi-event test + if ("multi-event-test".equals(taskId)) { + // First call: context.getTask() == null (new task) + if (context.getTask() == null) { + updater.startWork(); + // Return immediately - queue stays open because task is in WORKING state + return; + } else { + // Second call: context.getTask() != null (existing task) + updater.addArtifact( + List.of(new TextPart("Second message artifact", null)), + "artifact-2", "Second Artifact", null); + updater.complete(); + return; + } + } + if (context.getTaskId().equals("task-not-supported-123")) { eventQueue.enqueueEvent(new UnsupportedOperationError()); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java index c65766dd8..9df23c565 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java @@ -50,6 +50,10 @@ public void enqueueEvent(String taskId, Event event) { queueManager.get(taskId).enqueueEvent(event); } + public int getChildQueueCount(String taskId) { + return queueManager.getActiveChildQueueCount(taskId); + } + public void deleteTaskPushNotificationConfig(String taskId, String configId) { pushNotificationConfigStore.deleteInfo(taskId, configId); } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 57dc5ae58..81701c3c1 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import io.grpc.Context; import java.util.concurrent.Flow; @@ -294,7 +295,7 @@ public void onComplete() { responseObserver.onCompleted(); } }); - }); + }, getExecutor()); } @Override @@ -504,6 +505,8 @@ public static void setStreamingSubscribedRunnable(Runnable runnable) { protected abstract CallContextFactory getCallContextFactory(); + protected abstract Executor getExecutor(); + /** * Attempts to extract the X-A2A-Extensions header from the current gRPC context. * This will only work if a server interceptor has been configured to capture diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 8f7edea23..12bbdb577 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -70,7 +70,7 @@ public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testOnGetTaskSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); GetTaskRequest request = GetTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) @@ -92,7 +92,7 @@ public void testOnGetTaskSuccess() throws Exception { @Test public void testOnGetTaskNotFound() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); GetTaskRequest request = GetTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -106,7 +106,7 @@ public void testOnGetTaskNotFound() throws Exception { @Test public void testOnCancelTaskSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -137,7 +137,7 @@ public void testOnCancelTaskSuccess() throws Exception { @Test public void testOnCancelTaskNotSupported() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -156,7 +156,7 @@ public void testOnCancelTaskNotSupported() throws Exception { @Test public void testOnCancelTaskNotFound() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); CancelTaskRequest request = CancelTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -169,7 +169,7 @@ public void testOnCancelTaskNotFound() throws Exception { @Test public void testOnMessageNewMessageSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -185,7 +185,7 @@ public void testOnMessageNewMessageSuccess() throws Exception { @Test public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); @@ -201,7 +201,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { @Test public void testOnMessageError() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(new UnsupportedOperationError()); }; @@ -211,7 +211,7 @@ public void testOnMessageError() throws Exception { @Test public void testSetPushNotificationConfigSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); @@ -230,7 +230,7 @@ public void testSetPushNotificationConfigSuccess() throws Exception { @Test public void testGetPushNotificationConfigSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -259,7 +259,7 @@ public void testGetPushNotificationConfigSuccess() throws Exception { @Test public void testPushNotificationsNotSupportedError() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); @@ -271,7 +271,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); @@ -283,7 +283,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); @@ -291,7 +291,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { @Test public void testOnMessageStreamNewMessageSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -309,7 +309,7 @@ public void testOnMessageStreamNewMessageSuccess() throws Exception { @Test public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -361,7 +361,7 @@ public void onCompleted() { @Test public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK) .history(new ArrayList<>()) @@ -411,7 +411,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep @Test public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); List events = List.of( AbstractA2ARequestHandlerTest.MINIMAL_TASK, new TaskArtifactUpdateEvent.Builder() @@ -491,7 +491,7 @@ public void onCompleted() { @Test public void testOnResubscribeNoExistingTaskError() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -503,7 +503,7 @@ public void testOnResubscribeNoExistingTaskError() throws Exception { @Test public void testOnResubscribeExistingTaskSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); @@ -538,7 +538,7 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); @@ -583,7 +583,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { @Test public void testStreamingNotSupportedError() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); } @@ -592,7 +592,7 @@ public void testStreamingNotSupportedError() throws Exception { public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception { // This test does not exist in the Python implementation AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); @@ -606,14 +606,14 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception public void testOnMessageStreamInternalError() throws Exception { DefaultRequestHandler mocked = Mockito.mock(DefaultRequestHandler.class); Mockito.doThrow(new InternalError("Internal Error")).when(mocked).onMessageSendStream(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, mocked); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, mocked, internalExecutor); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INTERNAL); } @Test public void testListPushNotificationConfig() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -639,7 +639,7 @@ public void testListPushNotificationConfig() throws Exception { @Test public void testListPushNotificationConfigNotSupported() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -657,7 +657,7 @@ public void testListPushNotificationConfigNotSupported() throws Exception { public void testListPushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -673,7 +673,7 @@ public void testListPushNotificationConfigNoPushConfigStore() { @Test public void testListPushNotificationConfigTaskNotFound() { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -688,7 +688,7 @@ public void testListPushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfig() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -711,7 +711,7 @@ public void testDeletePushNotificationConfig() throws Exception { @Test public void testDeletePushNotificationConfigNotSupported() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); - GrpcHandler handler = new TestGrpcHandler(card, requestHandler); + GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -730,7 +730,7 @@ public void testDeletePushNotificationConfigNotSupported() throws Exception { public void testDeletePushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) @@ -747,7 +747,7 @@ public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { @Test public void testStreamingDoesNotBlockMainThread() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler); + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); // Track if the main thread gets blocked during streaming AtomicBoolean eventReceived = new AtomicBoolean(false); @@ -874,10 +874,12 @@ private void assertGrpcError(StreamRecorder streamRecorder, Status.Code e private static class TestGrpcHandler extends GrpcHandler { private final AgentCard card; private final RequestHandler handler; + private final java.util.concurrent.Executor executor; - TestGrpcHandler(AgentCard card, RequestHandler handler) { + TestGrpcHandler(AgentCard card, RequestHandler handler, java.util.concurrent.Executor executor) { this.card = card; this.handler = handler; + this.executor = executor; } @Override @@ -894,5 +896,10 @@ protected AgentCard getAgentCard() { protected CallContextFactory getCallContextFactory() { return null; } + + @Override + protected java.util.concurrent.Executor getExecutor() { + return executor; + } } } diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 62bb422b4..9a9d5eef1 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.concurrent.Flow; import io.a2a.server.AgentCardValidator; @@ -44,6 +45,7 @@ import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskResubscriptionRequest; +import io.a2a.server.util.async.Internal; import mutiny.zero.ZeroPublisher; @ApplicationScoped @@ -52,23 +54,26 @@ public class JSONRPCHandler { private AgentCard agentCard; private Instance extendedAgentCard; private RequestHandler requestHandler; + private final Executor executor; protected JSONRPCHandler() { + this.executor = null; } @Inject public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard Instance extendedAgentCard, - RequestHandler requestHandler) { + RequestHandler requestHandler, @Internal Executor executor) { this.agentCard = agentCard; this.extendedAgentCard = extendedAgentCard; this.requestHandler = requestHandler; - + this.executor = executor; + // Validate transport configuration AgentCardValidator.validateTransportConfiguration(agentCard); } - public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) { - this(agentCard, null, requestHandler); + public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler, Executor executor) { + this(agentCard, null, requestHandler, executor); } public SendMessageResponse onMessageSend(SendMessageRequest request, ServerCallContext context) { @@ -278,7 +283,7 @@ public void onComplete() { tube.complete(); } }); - }); + }, executor); }); } } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 9d12824b7..467760ef9 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -6,8 +6,8 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -81,7 +81,7 @@ public class JSONRPCHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testOnGetTaskSuccess() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); GetTaskResponse response = handler.onGetTask(request, callContext); @@ -92,7 +92,7 @@ public void testOnGetTaskSuccess() throws Exception { @Test public void testOnGetTaskNotFound() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); GetTaskResponse response = handler.onGetTask(request, callContext); assertEquals(request.getId(), response.getId()); @@ -102,7 +102,7 @@ public void testOnGetTaskNotFound() throws Exception { @Test public void testOnCancelTaskSuccess() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -127,7 +127,7 @@ public void testOnCancelTaskSuccess() throws Exception { @Test public void testOnCancelTaskNotSupported() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -143,7 +143,7 @@ public void testOnCancelTaskNotSupported() { @Test public void testOnCancelTaskNotFound() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); CancelTaskResponse response = handler.onCancelTask(request, callContext); assertEquals(request.getId(), response.getId()); @@ -153,7 +153,7 @@ public void testOnCancelTaskNotFound() { @Test public void testOnMessageNewMessageSuccess() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -175,7 +175,7 @@ public void testOnMessageNewMessageSuccess() { @Test public void testOnMessageNewMessageSuccessMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) @@ -195,7 +195,7 @@ public void testOnMessageNewMessageSuccessMocks() { @Test public void testOnMessageNewMessageWithExistingTaskSuccess() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); @@ -218,7 +218,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() { @Test public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); Message message = new Message.Builder(MESSAGE) @@ -242,7 +242,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { public void testOnMessageError() { // See testMessageOnErrorMocks() for a test more similar to the Python implementation, using mocks for // EventConsumer.consumeAll() - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(new UnsupportedOperationError()); }; @@ -259,7 +259,7 @@ public void testOnMessageError() { @Test public void testOnMessageErrorMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); Message message = new Message.Builder(MESSAGE) .taskId(MINIMAL_TASK.getId()) .contextId(MINIMAL_TASK.getContextId()) @@ -280,7 +280,7 @@ public void testOnMessageErrorMocks() { @Test public void testOnMessageStreamNewMessageSuccess() throws InterruptedException { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -337,7 +337,7 @@ public void onComplete() { @Test public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws InterruptedException { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); // Create multiple events to be sent during streaming Task taskEvent = new Task.Builder(MINIMAL_TASK) @@ -445,7 +445,7 @@ public void onComplete() { @Test public void testOnMessageStreamNewMessageSuccessMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); // This is used to send events from a mock List events = List.of( @@ -514,7 +514,7 @@ public void onComplete() { @Test public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -588,7 +588,7 @@ public void onComplete() { @Test public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); Task task = new Task.Builder(MINIMAL_TASK) .history(new ArrayList<>()) @@ -666,7 +666,7 @@ public void onComplete() { @Test public void testSetPushNotificationConfigSuccess() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); TaskPushNotificationConfig taskPushConfig = @@ -682,7 +682,7 @@ public void testSetPushNotificationConfigSuccess() { @Test public void testGetPushNotificationConfigSuccess() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -707,7 +707,7 @@ public void testGetPushNotificationConfigSuccess() { @Test public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); List events = List.of( @@ -812,7 +812,7 @@ public void onComplete() { @Test public void testOnResubscribeExistingTaskSuccess() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); queueManager.createOrTap(MINIMAL_TASK.getId()); @@ -880,7 +880,7 @@ public void onComplete() { @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); queueManager.createOrTap(MINIMAL_TASK.getId()); @@ -952,7 +952,7 @@ public void onComplete() { @Test public void testOnResubscribeNoExistingTaskError() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); Flow.Publisher response = handler.onResubscribeToTask(request, callContext); @@ -995,7 +995,7 @@ public void onComplete() { @Test public void testStreamingNotSupportedError() { AgentCard card = createAgentCard(false, true, true); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); SendStreamingMessageRequest request = new SendStreamingMessageRequest.Builder() .id("1") @@ -1046,7 +1046,7 @@ public void onComplete() { public void testStreamingNotSupportedErrorOnResubscribeToTask() { // This test does not exist in the Python implementation AgentCard card = createAgentCard(false, true, true); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); Flow.Publisher response = handler.onResubscribeToTask(request, callContext); @@ -1092,7 +1092,7 @@ public void onComplete() { @Test public void testPushNotificationsNotSupportedError() { AgentCard card = createAgentCard(true, false, true); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); TaskPushNotificationConfig config = @@ -1115,7 +1115,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); @@ -1134,7 +1134,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); @@ -1160,7 +1160,7 @@ public void testOnMessageSendInternalError() { Mockito.doThrow(new InternalError("Internal Error")).when(mocked) .onMessageSend(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); - JSONRPCHandler handler = new JSONRPCHandler(CARD, mocked); + JSONRPCHandler handler = new JSONRPCHandler(CARD, mocked, internalExecutor); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); @@ -1174,7 +1174,7 @@ public void testOnMessageStreamInternalError() { Mockito.doThrow(new InternalError("Internal Error")).when(mocked) .onMessageSendStream(Mockito.any(MessageSendParams.class), Mockito.any(ServerCallContext.class)); - JSONRPCHandler handler = new JSONRPCHandler(CARD, mocked); + JSONRPCHandler handler = new JSONRPCHandler(CARD, mocked, internalExecutor); SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); Flow.Publisher response = handler.onMessageSendStream(request, callContext); @@ -1225,7 +1225,7 @@ public void testOnMessageSendErrorHandling() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); @@ -1256,7 +1256,7 @@ public void testOnMessageSendErrorHandling() { @Test public void testOnMessageSendTaskIdMismatch() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = ((context, eventQueue) -> { @@ -1271,7 +1271,7 @@ public void testOnMessageSendTaskIdMismatch() { @Test public void testOnMessageStreamTaskIdMismatch() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = ((context, eventQueue) -> { @@ -1322,7 +1322,7 @@ public void onComplete() { @Test public void testListPushNotificationConfig() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -1349,7 +1349,7 @@ public void testListPushNotificationConfig() { @Test public void testListPushNotificationConfigNotSupported() { AgentCard card = createAgentCard(true, false, true); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -1378,7 +1378,7 @@ public void testListPushNotificationConfigNotSupported() { public void testListPushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -1396,7 +1396,7 @@ public void testListPushNotificationConfigNoPushConfigStore() { @Test public void testListPushNotificationConfigTaskNotFound() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1413,7 +1413,7 @@ public void testListPushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfig() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -1441,7 +1441,7 @@ public void testDeletePushNotificationConfig() { @Test public void testDeletePushNotificationConfigNotSupported() { AgentCard card = createAgentCard(true, false, true); - JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -1470,7 +1470,7 @@ public void testDeletePushNotificationConfigNotSupported() { public void testDeletePushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); @@ -1497,7 +1497,7 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { @Test public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); GetAuthenticatedExtendedCardResponse response = handler.onGetAuthenticatedExtendedCardRequest(request, callContext); assertEquals(request.getId(), response.getId()); @@ -1507,7 +1507,7 @@ public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { @Test public void testStreamingDoesNotBlockMainThread() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler); + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); // Track if the main thread gets blocked during streaming AtomicBoolean mainThreadBlocked = new AtomicBoolean(true); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 47e3e5ecb..586149948 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -40,9 +40,11 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.server.util.async.Internal; import io.a2a.util.Utils; import jakarta.enterprise.inject.Instance; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; import mutiny.zero.ZeroPublisher; @@ -56,26 +58,30 @@ public class RestHandler { private @Nullable Instance extendedAgentCard; private RequestHandler requestHandler; + private final Executor executor; @SuppressWarnings("NullAway") protected RestHandler() { // For CDI + this.executor = null; } @Inject public RestHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard Instance extendedAgentCard, - RequestHandler requestHandler) { + RequestHandler requestHandler, @Internal Executor executor) { this.agentCard = agentCard; this.extendedAgentCard = extendedAgentCard; this.requestHandler = requestHandler; + this.executor = executor; // Validate transport configuration AgentCardValidator.validateTransportConfiguration(agentCard); } - public RestHandler(AgentCard agentCard, RequestHandler requestHandler) { + public RestHandler(AgentCard agentCard, RequestHandler requestHandler, Executor executor) { this.agentCard = agentCard; this.requestHandler = requestHandler; + this.executor = executor; } public HTTPRestResponse sendMessage(String body, ServerCallContext context) { @@ -305,7 +311,7 @@ public void onComplete() { tube.complete(); } }); - }); + }, executor); }); } diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 409998f6b..58e74f85e 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -23,7 +23,7 @@ public class RestHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testGetTaskSuccess() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(),null, callContext); @@ -41,7 +41,7 @@ public void testGetTaskSuccess() { @Test public void testGetTaskNotFound() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); RestHandler.HTTPRestResponse response = handler.getTask("nonexistent", null, callContext); @@ -52,7 +52,7 @@ public void testGetTaskNotFound() { @Test public void testSendMessage() throws InvalidProtocolBufferException { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -83,7 +83,7 @@ public void testSendMessage() throws InvalidProtocolBufferException { @Test public void testSendMessageInvalidBody() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); String invalidBody = "invalid json"; RestHandler.HTTPRestResponse response = handler.sendMessage(invalidBody, callContext); @@ -95,7 +95,7 @@ public void testSendMessageInvalidBody() { @Test public void testSendMessageWrongValueBody() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); String requestBody = """ { "message": @@ -119,7 +119,7 @@ public void testSendMessageWrongValueBody() { @Test public void testSendMessageEmptyBody() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); RestHandler.HTTPRestResponse response = handler.sendMessage("", callContext); @@ -130,7 +130,7 @@ public void testSendMessageEmptyBody() { @Test public void testCancelTaskSuccess() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorCancel = (context, eventQueue) -> { @@ -151,7 +151,7 @@ public void testCancelTaskSuccess() { @Test public void testCancelTaskNotFound() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); RestHandler.HTTPRestResponse response = handler.cancelTask("nonexistent", callContext); @@ -162,7 +162,7 @@ public void testCancelTaskNotFound() { @Test public void testSendStreamingMessageSuccess() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -194,7 +194,7 @@ public void testSendStreamingMessageSuccess() { @Test public void testSendStreamingMessageNotSupported() { AgentCard card = createAgentCard(false, true, true); - RestHandler handler = new RestHandler(card, requestHandler); + RestHandler handler = new RestHandler(card, requestHandler, internalExecutor); String requestBody = """ { @@ -214,7 +214,7 @@ public void testSendStreamingMessageNotSupported() { @Test public void testPushNotificationConfigSuccess() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); String requestBody = """ @@ -241,7 +241,7 @@ public void testPushNotificationConfigSuccess() { @Test public void testPushNotificationConfigNotSupported() { AgentCard card = createAgentCard(true, false, true); - RestHandler handler = new RestHandler(card, requestHandler); + RestHandler handler = new RestHandler(card, requestHandler, internalExecutor); String requestBody = """ { @@ -260,7 +260,7 @@ public void testPushNotificationConfigNotSupported() { @Test public void testGetPushNotificationConfig() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); // First, create a push notification config @@ -288,7 +288,7 @@ public void testGetPushNotificationConfig() { @Test public void testDeletePushNotificationConfig() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", callContext); Assertions.assertEquals(204, response.getStatusCode()); @@ -296,7 +296,7 @@ public void testDeletePushNotificationConfig() { @Test public void testListPushNotificationConfigs() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.getId(), callContext); @@ -308,7 +308,7 @@ public void testListPushNotificationConfigs() { @Test public void testHttpStatusCodeMapping() { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); // Test 400 for invalid request RestHandler.HTTPRestResponse response = handler.sendMessage("", callContext); @@ -321,7 +321,7 @@ public void testHttpStatusCodeMapping() { @Test public void testStreamingDoesNotBlockMainThread() throws Exception { - RestHandler handler = new RestHandler(CARD, requestHandler); + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); // Track if the main thread gets blocked during streaming AtomicBoolean eventReceived = new AtomicBoolean(false); From eb732ad89b920d4fab5719b4a5074145f1917a3b Mon Sep 17 00:00:00 2001 From: Sebastien Dionne Date: Wed, 15 Oct 2025 12:52:40 -0400 Subject: [PATCH 162/493] docs: Fix typos and linguistic errors in documentation / hacktoberfest (#343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Fix typos and linguistic errors in documentation. It's not much but I'm happy to help Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X ] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X ] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [ X] Ensure the tests pass - [X ] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --------- Signed-off-by: Sebastien Dionne Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- README.md | 13 ++++++------- client/base/src/main/java/io/a2a/A2A.java | 8 ++++---- .../src/main/java/io/a2a/client/ClientBuilder.java | 4 ++-- .../main/java/io/a2a/common/A2AErrorMessages.java | 2 +- .../java/io/a2a/client/http/A2ACardResolver.java | 4 ++-- .../a2a/server/agentexecution/RequestContext.java | 2 +- .../a2a/transport/grpc/handler/GrpcHandlerTest.java | 2 +- 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 479228199..0b92171d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ This can be done by looking in the top-right corner of the repository page and c The next step is to clone your newly forked repository onto your local workspace. This can be done by going to your newly forked repository, which should be at `https://github.com/USERNAME/a2a-java`. -Then, there will be a green button that says "Code". Click on that and copy the URL. +Then, there will be a green button that says "Code". Click on it and copy the URL. Then, in your terminal, paste the following command: ```bash diff --git a/README.md b/README.md index a42a49b24..f5228e0c0 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) - [Add a class that creates an A2A Agent Card](#2-add-a-class-that-creates-an-a2a-agent-card) - [Add a class that creates an A2A Agent Executor](#3-add-a-class-that-creates-an-a2a-agent-executor) -### 1. Add an A2A Java SDK Reference Server dependency to your project +### 1. Add an A2A Java SDK Server Maven dependency to your project -Adding a dependency on an A2A Java SDK Reference Server will provide access to the core classes +Adding a dependency on an A2A Java SDK Server will provide access to the core classes that make up the A2A specification and allow you to run your agentic Java application as an A2A server agent. The A2A Java SDK provides [reference A2A server implementations](reference) based on [Quarkus](https://quarkus.io) for use with our tests and examples. However, the project is designed in such a way that it is trivial to integrate with various Java runtimes. @@ -128,7 +128,7 @@ public class WeatherAgentCardProducer { .skills(Collections.singletonList(new AgentSkill.Builder() .id("weather_search") .name("Search weather") - .description("Helps with weather in city, or states") + .description("Helps with weather in cities or states") .tags(Collections.singletonList("weather")) .examples(List.of("weather in LA, CA")) .build())) @@ -294,8 +294,7 @@ that you can use to create your A2A `Client`. ### 2. Add one or more dependencies on the A2A Java SDK Client Transport(s) you'd like to use -By default, the sdk-client is coming with the JSONRPC transport dependency. Despite the fact that the JSONRPC transport -dependency is included by default, you still need to add the transport to the Client as described in [JSON-RPC Transport section](#json-rpc-transport-configuration). +By default, the `sdk-client` artifact includes the JSONRPC transport dependency. However, you must still explicitly configure this transport when building the `Client` as described in the [JSON-RPC Transport section](#json-rpc-transport-configuration). If you want to use the gRPC transport, you'll need to add a relevant dependency: @@ -505,8 +504,8 @@ client.sendMessage(message, customConsumers, customErrorHandler); // Retrieve the task with id "task-1234" Task task = client.getTask(new TaskQueryParams("task-1234")); -// You can also specify the maximum number of items of history for the task -// to include in the response and +// You can also specify the maximum number of history items for the task +// to include in the response Task task = client.getTask(new TaskQueryParams("task-1234", 10)); // You can also optionally specify a ClientCallContext with call-specific config to use diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index 063527c23..d64cdaa27 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -136,7 +136,7 @@ private static Message toMessage(List> parts, Message.Role role, String * @param agentUrl the base URL for the agent whose agent card we want to retrieve * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema + * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema */ public static AgentCard getAgentCard(String agentUrl) throws A2AClientError, A2AClientJSONError { return getAgentCard(new JdkA2AHttpClient(), agentUrl); @@ -149,7 +149,7 @@ public static AgentCard getAgentCard(String agentUrl) throws A2AClientError, A2A * @param agentUrl the base URL for the agent whose agent card we want to retrieve * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema + * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema */ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl) throws A2AClientError, A2AClientJSONError { return getAgentCard(httpClient, agentUrl, null, null); @@ -164,7 +164,7 @@ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl) * @param authHeaders the HTTP authentication headers to use * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema + * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema */ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, Map authHeaders) throws A2AClientError, A2AClientJSONError { return getAgentCard(new JdkA2AHttpClient(), agentUrl, relativeCardPath, authHeaders); @@ -180,7 +180,7 @@ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, M * @param authHeaders the HTTP authentication headers to use * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema + * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema */ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl, String relativeCardPath, Map authHeaders) throws A2AClientError, A2AClientJSONError { A2ACardResolver resolver = new A2ACardResolver(httpClient, agentUrl, relativeCardPath, authHeaders); diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java index da61dfa30..5765da204 100644 --- a/client/base/src/main/java/io/a2a/client/ClientBuilder.java +++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java @@ -91,14 +91,14 @@ private ClientTransport buildClientTransport() throws A2AClientException { // Get the preferred transport AgentInterface agentInterface = findBestClientTransport(); - // Get the transport provider associated to the protocol + // Get the transport provider associated with the protocol ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.transport()); if (clientTransportProvider == null) { throw new A2AClientException("No client available for " + agentInterface.transport()); } Class transportProtocolClass = clientTransportProvider.getTransportProtocolClass(); - // Retrieve the configuration associated to the preferred transport + // Retrieve the configuration associated with the preferred transport ClientTransportConfig clientTransportConfig = clientTransports.get(transportProtocolClass); if (clientTransportConfig == null) { diff --git a/common/src/main/java/io/a2a/common/A2AErrorMessages.java b/common/src/main/java/io/a2a/common/A2AErrorMessages.java index 22b587d84..2801a2b4e 100644 --- a/common/src/main/java/io/a2a/common/A2AErrorMessages.java +++ b/common/src/main/java/io/a2a/common/A2AErrorMessages.java @@ -3,7 +3,7 @@ public final class A2AErrorMessages { private A2AErrorMessages() { - // prevent instantiation + // Prevent instantiation } public static final String AUTHENTICATION_FAILED = "Authentication failed: Client credentials are missing or invalid"; diff --git a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java index 5d94686bc..539f3a741 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java +++ b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java @@ -35,7 +35,7 @@ public A2ACardResolver(String baseUrl) throws A2AClientError { } /** - /**Get the agent card for an A2A agent. + * Constructs an A2ACardResolver with a specific HTTP client and base URL. * * @param httpClient the http client to use * @param baseUrl the base URL for the agent whose agent card we want to retrieve @@ -81,7 +81,7 @@ public A2ACardResolver(A2AHttpClient httpClient, String baseUrl, @Nullable Strin * * @return the agent card * @throws A2AClientError If an HTTP error occurs fetching the card - * @throws A2AClientJSONError f the response body cannot be decoded as JSON or validated against the AgentCard schema + * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema */ public AgentCard getAgentCard() throws A2AClientError, A2AClientJSONError { A2AHttpClient.GetBuilder builder = httpClient.createGet() diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index 585b4fce4..0078738dd 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -38,7 +38,7 @@ public RequestContext( this.relatedTasks = relatedTasks == null ? new ArrayList<>() : relatedTasks; this.callContext = callContext; - // if the taskId and contextId were specified, they must match the params + // If the taskId and contextId were specified, they must match the params if (params != null) { if (taskId != null && !taskId.equals(params.message().getTaskId())) { throw new InvalidParamsError("bad task id"); diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 12bbdb577..90af21f1e 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -742,7 +742,7 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { @Disabled public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { - // TODO - getting the authenticated extended agent card isn't support for gRPC right now + // TODO - getting the authenticated extended agent card isn't supported for gRPC right now } @Test From 2a75e6bbc4192e6f643e69781ec50b7cf22cf740 Mon Sep 17 00:00:00 2001 From: Ethan <72623865+ethanthopkins@users.noreply.github.com> Date: Thu, 16 Oct 2025 08:53:58 -0600 Subject: [PATCH 163/493] chore: Add Jspecify NullCheck in tck module #336 (#340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …y @Nullable annotation to AgentCardProducer, getEnvOrDefault Method and AgentExecutorProducer execute Method. Fixes #336 # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #336 🦕 --- .../java/io/a2a/tck/server/AgentExecutorProducer.java | 1 - tck/src/main/java/io/a2a/tck/server/package-info.java | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tck/src/main/java/io/a2a/tck/server/package-info.java diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index 859790bcb..5c085f981 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -11,7 +11,6 @@ import io.a2a.spec.JSONRPCError; import io.a2a.spec.Task; import io.a2a.spec.TaskNotCancelableError; -import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; diff --git a/tck/src/main/java/io/a2a/tck/server/package-info.java b/tck/src/main/java/io/a2a/tck/server/package-info.java new file mode 100644 index 000000000..f2b9319f2 --- /dev/null +++ b/tck/src/main/java/io/a2a/tck/server/package-info.java @@ -0,0 +1,10 @@ +@NullMarked +package io.a2a.tck.server; + +import org.jspecify.annotations.NullMarked; + +//The following had @Nullable annotation applied from JSpecify +//AgentCardProducer.java getEnvOrDefault method, +//AgentExecutorProducer.java execute method +// + From 8e83576f834a2fa7c649f5a22095658bdea3f63a Mon Sep 17 00:00:00 2001 From: MOUAD ELBOUCHRAOUI <126575832+devcom33@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:10:00 +0100 Subject: [PATCH 164/493] chore: add Jspecify NullCheck in grpc transport (Fixes #334) (#342) This PR adds Jspecify nullness annotations to the gRPC transport module: - Added @NullMarked in package-info.java for the package. - Annotated nullable parameters with @Nullable in GrpcTransport.java and related classes. - Ensured all fields and method signatures match nullability expectations. - Fixed NullAway errors by initializing non-null fields and updating constructor annotations. - All tests pass successfully. --------- Co-authored-by: devcom33 --- .../client/transport/grpc/GrpcTransport.java | 46 +++++++++++-------- .../grpc/GrpcTransportConfigBuilder.java | 7 ++- .../client/transport/grpc/package-info.java | 4 ++ .../spi/interceptors/PayloadAndHeaders.java | 2 +- 4 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/package-info.java diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 2023339d2..5a8437679 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -18,6 +18,8 @@ import io.a2a.client.transport.spi.interceptors.auth.AuthInterceptor; import io.a2a.common.A2AHeaders; import io.a2a.grpc.A2AServiceGrpc; +import io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub; +import io.a2a.grpc.A2AServiceGrpc.A2AServiceStub; import io.a2a.grpc.CancelTaskRequest; import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; @@ -28,7 +30,8 @@ import io.a2a.grpc.SendMessageResponse; import io.a2a.grpc.StreamResponse; import io.a2a.grpc.TaskSubscriptionRequest; - +import io.a2a.grpc.utils.ProtoUtils.FromProto; +import io.a2a.grpc.utils.ProtoUtils.ToProto; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -49,6 +52,7 @@ import io.grpc.StatusRuntimeException; import io.grpc.stub.MetadataUtils; import io.grpc.stub.StreamObserver; +import org.jspecify.annotations.Nullable; public class GrpcTransport implements ClientTransport { @@ -60,15 +64,16 @@ public class GrpcTransport implements ClientTransport { Metadata.ASCII_STRING_MARSHALLER); private final A2AServiceBlockingV2Stub blockingStub; private final A2AServiceStub asyncStub; - private final List interceptors; + private final @Nullable List interceptors; private AgentCard agentCard; public GrpcTransport(Channel channel, AgentCard agentCard) { this(channel, agentCard, null); } - public GrpcTransport(Channel channel, AgentCard agentCard, List interceptors) { + public GrpcTransport(Channel channel, AgentCard agentCard, @Nullable List interceptors) { checkNotNullParam("channel", channel); + checkNotNullParam("agentCard", agentCard); this.asyncStub = A2AServiceGrpc.newStub(channel); this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); this.agentCard = agentCard; @@ -76,7 +81,7 @@ public GrpcTransport(Channel channel, AgentCard agentCard, List eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); SendMessageRequest grpcRequest = createGrpcSendMessageRequest(request, context); @@ -117,7 +122,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException { + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); ListTaskPushNotificationConfigRequest grpcRequest = ListTaskPushNotificationConfigRequest.newBuilder() @@ -221,7 +226,7 @@ public List listTaskPushNotificationConfigurations( @Override public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException { + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); DeleteTaskPushNotificationConfigRequest grpcRequest = DeleteTaskPushNotificationConfigRequest.newBuilder() @@ -240,7 +245,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC @Override public void resubscribe(TaskIdParams request, Consumer eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); @@ -261,7 +266,7 @@ public void resubscribe(TaskIdParams request, Consumer event } @Override - public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException { // TODO: Determine how to handle retrieving the authenticated extended agent card return agentCard; } @@ -270,7 +275,7 @@ public AgentCard getAgentCard(ClientCallContext context) throws A2AClientExcepti public void close() { } - private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messageSendParams, ClientCallContext context) { + private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messageSendParams, @Nullable ClientCallContext context) { SendMessageRequest.Builder builder = SendMessageRequest.newBuilder(); builder.setRequest(ToProto.message(messageSendParams.message())); if (messageSendParams.configuration() != null) { @@ -285,8 +290,11 @@ private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messag /** * Creates gRPC metadata from ClientCallContext headers. * Extracts headers like X-A2A-Extensions and sets them as gRPC metadata. + * @param context the client call context containing headers, may be null + * @param payloadAndHeaders the payload and headers wrapper, may be null + * @return the gRPC metadata */ - private Metadata createGrpcMetadata(ClientCallContext context, PayloadAndHeaders payloadAndHeaders) { + private Metadata createGrpcMetadata(@Nullable ClientCallContext context, @Nullable PayloadAndHeaders payloadAndHeaders) { Metadata metadata = new Metadata(); if (context != null && context.getHeaders() != null) { @@ -328,7 +336,7 @@ private Metadata createGrpcMetadata(ClientCallContext context, PayloadAndHeaders * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors * @return blocking stub with metadata interceptor */ - private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(ClientCallContext context, + private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(@Nullable ClientCallContext context, PayloadAndHeaders payloadAndHeaders) { Metadata metadata = createGrpcMetadata(context, payloadAndHeaders); return blockingStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); @@ -341,7 +349,7 @@ private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(ClientCallContex * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors * @return async stub with metadata interceptor */ - private A2AServiceStub createAsyncStubWithMetadata(ClientCallContext context, + private A2AServiceStub createAsyncStubWithMetadata(@Nullable ClientCallContext context, PayloadAndHeaders payloadAndHeaders) { Metadata metadata = createGrpcMetadata(context, payloadAndHeaders); return asyncStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); @@ -351,7 +359,7 @@ private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigPa return getTaskPushNotificationConfigName(params.id(), params.pushNotificationConfigId()); } - private String getTaskPushNotificationConfigName(String taskId, String pushNotificationConfigId) { + private String getTaskPushNotificationConfigName(String taskId, @Nullable String pushNotificationConfigId) { StringBuilder name = new StringBuilder(); name.append("tasks/"); name.append(taskId); @@ -366,7 +374,7 @@ private String getTaskPushNotificationConfigName(String taskId, String pushNotif } private PayloadAndHeaders applyInterceptors(String methodName, Object payload, - AgentCard agentCard, ClientCallContext clientCallContext) { + AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, clientCallContext != null ? clientCallContext.getHeaders() : null); if (interceptors != null && ! interceptors.isEmpty()) { diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java index 1f1d0a2b8..6878ce7d4 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java @@ -6,9 +6,11 @@ import java.util.function.Function; +import org.jspecify.annotations.Nullable; + public class GrpcTransportConfigBuilder extends ClientTransportConfigBuilder { - private Function channelFactory; + private @Nullable Function channelFactory; public GrpcTransportConfigBuilder channelFactory(Function channelFactory) { Assert.checkNotNullParam("channelFactory", channelFactory); @@ -20,6 +22,9 @@ public GrpcTransportConfigBuilder channelFactory(Function chann @Override public GrpcTransportConfig build() { + if (channelFactory == null) { + throw new IllegalStateException("channelFactory must be set"); + } GrpcTransportConfig config = new GrpcTransportConfig(channelFactory); config.setInterceptors(interceptors); return config; diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/package-info.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/package-info.java new file mode 100644 index 000000000..c0c12ccb5 --- /dev/null +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.client.transport.grpc; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java index 4783cb716..816ad3e5f 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java @@ -10,7 +10,7 @@ public class PayloadAndHeaders { private final @Nullable Object payload; private final Map headers; - public PayloadAndHeaders(@Nullable Object payload, Map headers) { + public PayloadAndHeaders(@Nullable Object payload, @Nullable Map headers) { this.payload = payload; this.headers = headers == null ? Collections.emptyMap() : new HashMap<>(headers); } From c9fe654cd2468dd46750828941245d54ed303b7d Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 20 Oct 2025 20:48:54 +0100 Subject: [PATCH 165/493] feat: Implement robust replication and task-aware queue lifecycle (#351) This commit introduces a comprehensive overhaul of the event replication and queue lifecycle management system. It ensures data consistency for all request types, enhances robustness in distributed environments, and adds support for new operational patterns. Previously, the system suffered from several issues: blocking requests could lead to data loss, the replication mechanism was susceptible to race conditions, and the queue lifecycle was not fully aligned with the task's state, preventing patterns like fire-and-forget. This change implements three major architectural improvements: 1. Fix Data Loss in Blocking Requests: * Corrects a critical bug where blocking onMessageSend calls would stop processing events after returning the first one. * The ResultAggregator now ensures all subsequent events from the agent are consumed in the background, guaranteeing the final task state is correctly persisted in the TaskStore. 2. Authoritative State & Transaction-Aware Replication: * Introduces a TaskStateProvider interface to decouple queue management from the persistence layer, allowing the ReplicatedQueueManager to check the authoritative state of a task from a shared database. * Replaces time-based cleanup delays with a robust, transaction-aware "poison pill" (QueueClosedEvent). This event is now broadcast via a CDI observer only after the final task state is successfully committed to the database, eliminating race conditions in distributed cleanup. 3. Task-Aware Queue Lifecycle & `ThreadLocal` Removal: * Refactors the EventQueue to use a polymorphic EventQueueItem, which cleanly distinguishes between local and replicated events. This allows for the complete removal of the isHandlingReplicatedEvent ThreadLocal, simplifying the concurrency model. * The MainQueue lifecycle is now strictly tied to the task's finalization state. As a core guarantee, a `MainQueue` will remain open for a task as long as the task is not in a final state. This holds true even if all consumers disconnect, enabling reliable fire-and-forget operations and late resubscriptions, while queues for finalized tasks are correctly garbage collected. --- .gitignore | 1 + .../transport/jsonrpc/JSONRPCTransport.java | 6 +- .../jsonrpc/sse/SSEEventListener.java | 19 ++ extras/common/pom.xml | 21 ++ .../common/events/TaskFinalizedEvent.java | 26 ++ extras/queue-manager-replicated/README.md | 180 ++++++++++++ extras/queue-manager-replicated/core/pom.xml | 5 + .../replicated/core/ReplicatedEvent.java | 114 -------- .../core/ReplicatedEventQueueItem.java | 178 ++++++++++++ .../core/ReplicatedQueueManager.java | 107 ++++--- .../core/EventSerializationTest.java | 83 ++++-- .../core/ReplicatedQueueManagerTest.java | 259 +++++++++++++++-- .../ReactiveMessagingReplicationStrategy.java | 10 +- ...ctiveMessagingReplicationStrategyTest.java | 10 +- extras/queue-manager-replicated/tests/pom.xml | 18 ++ .../KafkaReplicationIntegrationTest.java | 189 ++++++++++++- .../tests/ReplicationTestAgentExecutor.java | 12 + .../tests/TestKafkaEventConsumer.java | 61 +++- .../src/test/resources/application.properties | 16 +- extras/task-store-database-jpa/pom.xml | 4 + .../database/jpa/JpaDatabaseTaskStore.java | 131 ++++++++- .../taskstore/database/jpa/JpaTask.java | 40 +++ .../jpa/JpaDatabaseTaskStoreTest.java | 94 ++++++ pom.xml | 6 + .../io/a2a/server/events/EventConsumer.java | 71 +++-- .../a2a/server/events/EventEnqueueHook.java | 4 +- .../java/io/a2a/server/events/EventQueue.java | 165 ++++++++--- .../io/a2a/server/events/EventQueueItem.java | 37 +++ .../server/events/InMemoryQueueManager.java | 82 ++++-- .../server/events/LocalEventQueueItem.java | 31 ++ .../a2a/server/events/QueueClosedEvent.java | 39 +++ .../io/a2a/server/events/QueueManager.java | 1 + .../DefaultRequestHandler.java | 87 ++++-- .../a2a/server/tasks/InMemoryTaskStore.java | 24 +- .../io/a2a/server/tasks/ResultAggregator.java | 138 +++++---- .../a2a/server/tasks/TaskStateProvider.java | 62 ++++ .../a2a/server/events/EventConsumerTest.java | 84 ++++-- .../io/a2a/server/events/EventQueueTest.java | 64 +++-- .../events/InMemoryQueueManagerTest.java | 5 +- .../AbstractA2ARequestHandlerTest.java | 25 +- .../DefaultRequestHandlerTest.java | 267 +++++++++++++++++- .../server/tasks/MockTaskStateProvider.java | 40 +++ .../server/tasks/ResultAggregatorTest.java | 10 +- .../io/a2a/server/tasks/TaskUpdaterTest.java | 36 +-- tck/src/main/resources/application.properties | 8 +- .../apps/common/AbstractA2AServerTest.java | 239 ++++++++++++++++ .../grpc/handler/GrpcHandlerTest.java | 4 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 12 +- 48 files changed, 2637 insertions(+), 488 deletions(-) create mode 100644 extras/common/pom.xml create mode 100644 extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java delete mode 100644 extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java create mode 100644 extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java create mode 100644 server-common/src/main/java/io/a2a/server/events/EventQueueItem.java create mode 100644 server-common/src/main/java/io/a2a/server/events/LocalEventQueueItem.java create mode 100644 server-common/src/main/java/io/a2a/server/events/QueueClosedEvent.java create mode 100644 server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java create mode 100644 server-common/src/test/java/io/a2a/server/tasks/MockTaskStateProvider.java diff --git a/.gitignore b/.gitignore index 742dfd180..d4f021407 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ nbproject/ # Private Claude config .claude/ +.serena/ diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 8464911f5..108c80558 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -140,7 +140,8 @@ public void sendMessageStreaming(MessageSendParams request, Consumer sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), () -> { - // We don't need to do anything special on completion + // Signal normal stream completion to error handler (null error means success) + sseEventListener.onComplete(); })); } catch (IOException e) { throw new A2AClientException("Failed to send streaming message request: " + e, e); @@ -318,7 +319,8 @@ public void resubscribe(TaskIdParams request, Consumer event msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), () -> { - // We don't need to do anything special on completion + // Signal normal stream completion to error handler (null error means success) + sseEventListener.onComplete(); })); } catch (IOException e) { throw new A2AClientException("Failed to send task resubscription request: " + e, e); diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index 99ca546c4..ff4b93f3c 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -16,6 +16,7 @@ public class SSEEventListener { private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); private final Consumer eventHandler; private final Consumer errorHandler; + private volatile boolean completed = false; public SSEEventListener(Consumer eventHandler, Consumer errorHandler) { @@ -38,6 +39,24 @@ public void onError(Throwable throwable, Future future) { future.cancel(true); // close SSE channel } + public void onComplete() { + // Idempotent: only signal completion once, even if called multiple times + if (completed) { + log.fine("SSEEventListener.onComplete() called again - ignoring (already completed)"); + return; + } + completed = true; + + // Signal normal stream completion (null error means successful completion) + log.fine("SSEEventListener.onComplete() called - signaling successful stream completion"); + if (errorHandler != null) { + log.fine("Calling errorHandler.accept(null) to signal successful completion"); + errorHandler.accept(null); + } else { + log.warning("errorHandler is null, cannot signal completion"); + } + } + private void handleMessage(JsonNode jsonNode, Future future) { try { if (jsonNode.has("error")) { diff --git a/extras/common/pom.xml b/extras/common/pom.xml new file mode 100644 index 000000000..632b33e03 --- /dev/null +++ b/extras/common/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta3-SNAPSHOT + ../../pom.xml + + + a2a-java-extras-common + A2A Java SDK :: Extras :: Common + Common classes shared across extras modules + + + + + diff --git a/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java b/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java new file mode 100644 index 000000000..8c5f59348 --- /dev/null +++ b/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java @@ -0,0 +1,26 @@ +package io.a2a.extras.common.events; + +/** + * CDI event fired when a task reaches a final state and is successfully persisted to the database. + * This event is fired AFTER the database transaction commits, making it safe for downstream + * components to assume the task is durably stored. + * + *

Used by the replicated queue manager to send poison pill events after ensuring + * the final task state is committed to the database, eliminating race conditions. + */ +public class TaskFinalizedEvent { + private final String taskId; + + public TaskFinalizedEvent(String taskId) { + this.taskId = taskId; + } + + public String getTaskId() { + return taskId; + } + + @Override + public String toString() { + return "TaskFinalizedEvent{taskId='" + taskId + "'}"; + } +} diff --git a/extras/queue-manager-replicated/README.md b/extras/queue-manager-replicated/README.md index c832001fd..8756be0a2 100644 --- a/extras/queue-manager-replicated/README.md +++ b/extras/queue-manager-replicated/README.md @@ -177,6 +177,186 @@ Events are serialized using Jackson with polymorphic type information to ensure } ``` +## Production Considerations + +### Kafka Partitioning Strategy + +**Critical for scalability and correctness**: How you partition your Kafka topic significantly impacts system performance and behavior. + +#### Simple Approach: Single Partition + +The simplest configuration uses a single partition for the replicated events topic: + +```bash +kafka-topics.sh --create --topic replicated-events --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1 +``` + +**Advantages**: +- Guarantees global event ordering +- Simpler to reason about and debug +- Suitable for development, testing, and low-throughput production systems + +**Disadvantages**: +- Limited scalability (single partition bottleneck) +- Cannot parallelize consumption across multiple consumer instances +- All events processed sequentially + +**When to use**: Development environments, integration tests, production systems with low event volumes (<1000 events/sec), or when strict global ordering is required. + +#### Recommended Approach: Partition by Task ID + +For production systems with higher throughput, partition events by `taskId`: + +```properties +# Configure the producer to use taskId as the partition key +mp.messaging.outgoing.replicated-events-out.key.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.replicated-events-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer +``` + +```bash +# Create topic with multiple partitions +kafka-topics.sh --create --topic replicated-events --bootstrap-server localhost:9092 --partitions 10 --replication-factor 3 +``` + +The `ReactiveMessagingReplicationStrategy` already sends the `taskId` as the Kafka message key, so Kafka will automatically partition by task ID using its default partitioner. + +**Advantages**: +- **Horizontal scalability**: Different tasks can be processed in parallel across partitions +- **Per-task ordering guarantee**: All events for a single task go to the same partition and maintain order +- **Consumer parallelism**: Multiple consumer instances can process different partitions concurrently + +**Disadvantages**: +- No global ordering across all tasks +- More complex to debug (events spread across partitions) +- Requires proper consumer group configuration + +**When to use**: Production systems with medium to high throughput, systems that need to scale horizontally, distributed deployments with multiple A2A instances. + +#### Consumer Group Configuration + +When using multiple partitions, ensure all A2A instances belong to the same consumer group: + +```properties +mp.messaging.incoming.replicated-events-in.group.id=a2a-instance-group +``` + +This ensures that: +- Each partition is consumed by exactly one instance +- Events for the same task always go to the same instance (partition affinity) +- System can scale horizontally by adding more instances (up to the number of partitions) + +**Rule of thumb**: Number of partitions ≥ number of A2A instances for optimal distribution. + +### Transaction-Aware Queue Cleanup ("Poison Pill") + +When a task reaches a final state (COMPLETED, FAILED, CANCELED), all nodes in the cluster must terminate their event consumers for that task. This is achieved through a special "poison pill" event (`QueueClosedEvent`) that is replicated to all nodes. + +#### How It Works + +The poison pill mechanism uses **transaction-aware CDI events** to ensure the poison pill is only sent AFTER the final task state is durably committed to the database: + +1. **Task Finalization**: When `JpaDatabaseTaskStore.save()` persists a task with a final state, it fires a `TaskFinalizedEvent` CDI event +2. **Transaction Coordination**: The CDI observer is configured with `@Observes(during = TransactionPhase.AFTER_SUCCESS)`, which delays event delivery until AFTER the JPA transaction commits +3. **Poison Pill Delivery**: `ReplicatedQueueManager.onTaskFinalized()` receives the event and sends `QueueClosedEvent` via the replication strategy +4. **Cluster-Wide Termination**: All nodes receive the `QueueClosedEvent`, recognize it as final, and gracefully terminate their event consumers + +**Key Architecture Decision**: We use JPA transaction lifecycle hooks instead of time-based delays for poison pill delivery because: +- **Eliminates race conditions**: No time window where the poison pill might arrive before the database commit +- **Deterministic cleanup**: Queue termination happens immediately after transaction commit, without delay-based tuning +- **Simplicity**: No need to monitor consumer lag or configure delays for cleanup timing +- **Reliability**: Works correctly regardless of network latency or database performance + +**Note**: While the poison pill mechanism eliminates delays for cleanup, the system still uses a configurable grace period (`a2a.replication.grace-period-seconds`, default 15s) in `JpaDatabaseTaskStore.isTaskActive()` to handle late-arriving replicated events. This grace period prevents queue recreation for tasks that were recently finalized, accommodating Kafka consumer lag and network delays. See the Grace Period Configuration section below for details. + +#### Code Flow + +**JpaDatabaseTaskStore** (fires CDI event): +```java +@Inject +Event taskFinalizedEvent; + +public void save(Task task) { + // ... persist task to database ... + + // Fire CDI event if task reached final state + if (task.getStatus().state().isFinal()) { + taskFinalizedEvent.fire(new TaskFinalizedEvent(task.getId())); + } + // Transaction commits here (end of method) +} +``` + +**ReplicatedQueueManager** (observes and sends poison pill): +```java +public void onTaskFinalized(@Observes(during = TransactionPhase.AFTER_SUCCESS) TaskFinalizedEvent event) { + String taskId = event.getTaskId(); + LOGGER.debug("Task {} finalized - sending poison pill after transaction commit", taskId); + + // Send QueueClosedEvent to all nodes via replication + QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); + replicationStrategy.send(taskId, closedEvent); +} +``` + +#### Configuration + +No configuration is required for the poison pill mechanism - it works automatically when: +1. Using `JpaDatabaseTaskStore` for task persistence +2. Using `ReplicatedQueueManager` for event replication +3. Both modules are present in your application + +#### Monitoring + +Enable debug logging to monitor poison pill delivery: + +```properties +quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +quarkus.log.category."io.a2a.extras.taskstore.database.jpa".level=DEBUG +``` + +You should see log entries like: +``` +Task abc-123 is in final state, firing TaskFinalizedEvent +Task abc-123 finalized - sending poison pill (QueueClosedEvent) after transaction commit +``` + +#### Grace Period Configuration + +While the poison pill mechanism provides deterministic cleanup timing, the system uses a configurable **grace period** to handle late-arriving replicated events. This is separate from the poison pill mechanism and serves a different purpose. + +**Purpose**: The grace period prevents queue recreation for tasks that were recently finalized. When a replicated event arrives after a task is finalized, the system checks if the task is still within the grace period before creating a new queue. + +**Configuration**: +```properties +# Grace period for handling late-arriving events (default: 15 seconds) +a2a.replication.grace-period-seconds=15 +``` + +**How It Works**: +1. When a task is finalized, `JpaDatabaseTaskStore` records the `finalizedAt` timestamp +2. When a replicated event arrives, `ReplicatedQueueManager.onReplicatedEvent()` calls `taskStateProvider.isTaskActive(taskId)` +3. `JpaDatabaseTaskStore.isTaskActive()` returns `true` if: + - Task is not in a final state, OR + - Task is final but within the grace period (`now < finalizedAt + gracePeriodSeconds`) +4. If `isTaskActive()` returns `false`, the replicated event is skipped (no queue created) + +**When to Adjust**: +- **Increase** the grace period if you observe warnings about skipped events for inactive tasks in high-latency networks +- **Decrease** the grace period to reduce memory usage in systems with very low latency and high task turnover +- **Default (15s)** is suitable for most deployments with typical Kafka consumer lag + +**Monitoring**: +```properties +quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +``` + +Watch for: +``` +Skipping replicated event for inactive task abc-123 # Event arrived too late +``` + +**Important**: This grace period is for **late event handling**, not cleanup timing. The poison pill mechanism handles cleanup deterministically without delays. + ## Advanced Topics ### Custom Replication Strategies diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index ca50ecafc..48717409b 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -20,6 +20,11 @@ a2a-java-sdk-server-common ${project.version} + + io.github.a2asdk + a2a-java-extras-common + ${project.version} + io.quarkus quarkus-core diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java deleted file mode 100644 index 77eeb0406..000000000 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEvent.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.a2a.extras.queuemanager.replicated.core; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; - -import io.a2a.spec.Event; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.StreamingEventKind; - -public class ReplicatedEvent { - private String taskId; - - @JsonInclude(JsonInclude.Include.NON_NULL) - private StreamingEventKind event; - - @JsonInclude(JsonInclude.Include.NON_NULL) - private JSONRPCError error; - - // Default constructor for JSON deserialization - public ReplicatedEvent() { - } - - // Constructor for creating from A2A StreamingEventKind objects - public ReplicatedEvent(String taskId, StreamingEventKind event) { - this.taskId = taskId; - this.event = event; - this.error = null; - } - - // Constructor for creating from A2A JSONRPCError objects - public ReplicatedEvent(String taskId, JSONRPCError error) { - this.taskId = taskId; - this.event = null; - this.error = error; - } - - // Backward compatibility constructor for generic Event objects - public ReplicatedEvent(String taskId, Event event) { - this.taskId = taskId; - if (event instanceof StreamingEventKind streamingEvent) { - this.event = streamingEvent; - this.error = null; - } else if (event instanceof JSONRPCError jsonRpcError) { - this.event = null; - this.error = jsonRpcError; - } else { - throw new IllegalArgumentException("Event must be either StreamingEventKind or JSONRPCError, got: " + event.getClass()); - } - } - - - public String getTaskId() { - return taskId; - } - - public void setTaskId(String taskId) { - this.taskId = taskId; - } - - public StreamingEventKind getEvent() { - return event; - } - - public void setEvent(StreamingEventKind event) { - this.event = event; - this.error = null; // Clear error when setting event - } - - public JSONRPCError getError() { - return error; - } - - public void setError(JSONRPCError error) { - this.error = error; - this.event = null; // Clear event when setting error - } - - /** - * Get the contained event as the generic Event interface for backward compatibility. - * @return the event (either StreamingEventKind or JSONRPCError) or null if neither is set - */ - @JsonIgnore - public Event getEventAsGeneric() { - if (event != null) { - return event; - } - return error; - } - - /** - * Check if this ReplicatedEvent contains an event (vs an error). - * @return true if it contains a StreamingEventKind event - */ - public boolean hasEvent() { - return event != null; - } - - /** - * Check if this ReplicatedEvent contains an error. - * @return true if it contains a JSONRPCError - */ - public boolean hasError() { - return error != null; - } - - @Override - public String toString() { - return "ReplicatedEvent{" + - "taskId='" + taskId + '\'' + - ", event=" + event + - ", error=" + error + - '}'; - } -} \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java new file mode 100644 index 000000000..5eac92d4f --- /dev/null +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java @@ -0,0 +1,178 @@ +package io.a2a.extras.queuemanager.replicated.core; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSetter; + +import io.a2a.server.events.EventQueueItem; +import io.a2a.spec.Event; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.StreamingEventKind; + +public class ReplicatedEventQueueItem implements EventQueueItem { + private String taskId; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private StreamingEventKind event; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private JSONRPCError error; + + private boolean closedEvent; + + // Default constructor for JSON deserialization + public ReplicatedEventQueueItem() { + } + + // Constructor for creating from A2A StreamingEventKind objects + public ReplicatedEventQueueItem(String taskId, StreamingEventKind event) { + this.taskId = taskId; + this.event = event; + this.error = null; + } + + // Constructor for creating from A2A JSONRPCError objects + public ReplicatedEventQueueItem(String taskId, JSONRPCError error) { + this.taskId = taskId; + this.event = null; + this.error = error; + } + + // Backward compatibility constructor for generic Event objects + public ReplicatedEventQueueItem(String taskId, Event event) { + this.taskId = taskId; + if (event instanceof io.a2a.server.events.QueueClosedEvent) { + this.event = null; + this.error = null; + this.closedEvent = true; + } else if (event instanceof StreamingEventKind streamingEvent) { + this.event = streamingEvent; + this.error = null; + this.closedEvent = false; + } else if (event instanceof JSONRPCError jsonRpcError) { + this.event = null; + this.error = jsonRpcError; + this.closedEvent = false; + } else { + throw new IllegalArgumentException("Event must be StreamingEventKind, JSONRPCError, or QueueClosedEvent, got: " + event.getClass()); + } + } + + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + /** + * Get the StreamingEventKind event field (for JSON serialization). + * @return the StreamingEventKind event or null + */ + @JsonGetter("event") + @JsonInclude(JsonInclude.Include.NON_NULL) + public StreamingEventKind getStreamingEvent() { + return event; + } + + @JsonSetter("event") + public void setEvent(StreamingEventKind event) { + this.event = event; + this.error = null; // Clear error when setting event + } + + /** + * Get the JSONRPCError field (for JSON serialization). + * @return the JSONRPCError or null + */ + @JsonGetter("error") + @JsonInclude(JsonInclude.Include.NON_NULL) + public JSONRPCError getErrorObject() { + return error; + } + + @JsonSetter("error") + public void setError(JSONRPCError error) { + this.error = error; + this.event = null; // Clear event when setting error + } + + /** + * Get the contained event as the generic Event interface (implements EventQueueItem). + * This is the method required by the EventQueueItem interface. + * @return the event (StreamingEventKind, JSONRPCError, or QueueClosedEvent) or null if none is set + */ + @JsonIgnore + @Override + public Event getEvent() { + if (closedEvent) { + return new io.a2a.server.events.QueueClosedEvent(taskId); + } + if (event != null) { + return event; + } + return error; + } + + /** + * Indicates this is a replicated event (implements EventQueueItem). + * @return always true for replicated events + */ + @JsonIgnore + @Override + public boolean isReplicated() { + return true; + } + + /** + * Check if this ReplicatedEvent contains an event (vs an error). + * @return true if it contains a StreamingEventKind event + */ + public boolean hasEvent() { + return event != null; + } + + /** + * Check if this ReplicatedEvent contains an error. + * @return true if it contains a JSONRPCError + */ + public boolean hasError() { + return error != null; + } + + /** + * Check if this is a QueueClosedEvent (poison pill). + * For JSON serialization. + * @return true if this is a queue closed event + */ + @JsonGetter("closedEvent") + public boolean isClosedEvent() { + return closedEvent; + } + + /** + * Set the closed event flag (for JSON deserialization). + * @param closedEvent true if this is a queue closed event + */ + @JsonSetter("closedEvent") + public void setClosedEvent(boolean closedEvent) { + this.closedEvent = closedEvent; + if (closedEvent) { + this.event = null; + this.error = null; + } + } + + @Override + public String toString() { + return "ReplicatedEventQueueItem{" + + "taskId='" + taskId + '\'' + + ", event=" + event + + ", error=" + error + + ", closedEvent=" + closedEvent + + '}'; + } +} \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index c5016440a..5e90ed00d 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -3,37 +3,42 @@ import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; +import jakarta.enterprise.event.TransactionPhase; import jakarta.enterprise.inject.Alternative; import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.a2a.extras.common.events.TaskFinalizedEvent; +import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.events.EventEnqueueHook; import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueFactory; +import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.events.QueueManager; -import io.a2a.spec.Event; @ApplicationScoped @Alternative @Priority(50) public class ReplicatedQueueManager implements QueueManager { + private static final Logger LOGGER = LoggerFactory.getLogger(ReplicatedQueueManager.class); private final InMemoryQueueManager delegate; - private final ThreadLocal isHandlingReplicatedEvent = new ThreadLocal<>(); - @Inject private ReplicationStrategy replicationStrategy; - public ReplicatedQueueManager() { - this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory()); - } + private TaskStateProvider taskStateProvider; - // For testing - ReplicatedQueueManager(ReplicationStrategy replicationStrategy) { - this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory()); + @Inject + public ReplicatedQueueManager(ReplicationStrategy replicationStrategy, TaskStateProvider taskStateProvider) { this.replicationStrategy = replicationStrategy; + this.taskStateProvider = taskStateProvider; + this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory(), taskStateProvider); } + @Override public void add(String taskId, EventQueue queue) { delegate.add(taskId, queue); @@ -51,6 +56,9 @@ public EventQueue tap(String taskId) { @Override public void close(String taskId) { + // Close the local queue - this will trigger onClose callbacks + // The poison pill callback will check isTaskFinalized() and send if needed + // The cleanup callback will remove the queue from the map delegate.close(taskId); } @@ -65,29 +73,54 @@ public void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedExcep delegate.awaitQueuePollerStart(eventQueue); } - public void onReplicatedEvent(@Observes ReplicatedEvent replicatedEvent) { - isHandlingReplicatedEvent.set(true); - try { - EventQueue queue = delegate.get(replicatedEvent.getTaskId()); + public void onReplicatedEvent(@Observes ReplicatedEventQueueItem replicatedEvent) { + // Check if task is still active before processing replicated event (unless it's a QueueClosedEvent) + // QueueClosedEvent should always be processed to terminate streams, even for inactive tasks + if (!replicatedEvent.isClosedEvent() + && !taskStateProvider.isTaskActive(replicatedEvent.getTaskId())) { + // Task is no longer active - skip processing this replicated event + // This prevents creating queues for tasks that have been finalized beyond the grace period + LOGGER.debug("Skipping replicated event for inactive task {}", replicatedEvent.getTaskId()); + return; + } - if (queue == null) { - // If no queue exists, create or tap one to handle the replicated event - // This can happen when events arrive after the original queue has been closed - queue = delegate.createOrTap(replicatedEvent.getTaskId()); - } + // Get or create a ChildQueue for this task (creates MainQueue if it doesn't exist) + EventQueue childQueue = delegate.createOrTap(replicatedEvent.getTaskId()); - if (queue != null) { - // Use the backward compatibility method to get the event as the generic Event interface - Event event = replicatedEvent.getEventAsGeneric(); - if (event != null) { - queue.enqueueEvent(event); - } + try { + // Get the MainQueue to enqueue the replicated event item + // We must use enqueueItem (not enqueueEvent) to preserve the isReplicated() flag + // and avoid triggering the replication hook again (which would cause a replication loop) + EventQueue mainQueue = delegate.get(replicatedEvent.getTaskId()); + if (mainQueue != null) { + mainQueue.enqueueItem(replicatedEvent); + } else { + LOGGER.warn("MainQueue not found for task {}, cannot enqueue replicated event. This may happen if the queue was already cleaned up.", + replicatedEvent.getTaskId()); } } finally { - isHandlingReplicatedEvent.remove(); + // Close the temporary ChildQueue to prevent leaks + // The MainQueue remains open for other consumers + childQueue.close(); } } + /** + * Observes task finalization events fired AFTER database transaction commits. + * This guarantees the task's final state is durably stored before sending the poison pill. + * + * @param event the task finalized event containing the task ID + */ + public void onTaskFinalized(@Observes(during = TransactionPhase.AFTER_SUCCESS) TaskFinalizedEvent event) { + String taskId = event.getTaskId(); + LOGGER.debug("Task {} finalized - sending poison pill (QueueClosedEvent) after transaction commit", taskId); + + // Send poison pill directly via replication strategy + // The transaction has committed, so the final state is guaranteed to be in the database + io.a2a.server.events.QueueClosedEvent closedEvent = new io.a2a.server.events.QueueClosedEvent(taskId); + replicationStrategy.send(taskId, closedEvent); + } + @Override public EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { return QueueManager.super.getEventQueueBuilder(taskId) @@ -102,11 +135,20 @@ public int getActiveChildQueueCount(String taskId) { private class ReplicatingEventQueueFactory implements EventQueueFactory { @Override public EventQueue.EventQueueBuilder builder(String taskId) { - // Use the taskId parameter directly instead of ThreadLocal - return delegate.getEventQueueBuilder(taskId).hook(new ReplicationHook(taskId)); + // Poison pill sending is handled by the TaskFinalizedEvent observer (onTaskFinalized) + // which sends the QueueClosedEvent after the database transaction commits. + // This ensures proper ordering and transactional guarantees. + + // Return the builder with callbacks + return delegate.getEventQueueBuilder(taskId) + .taskId(taskId) + .hook(new ReplicationHook(taskId)) + .addOnCloseCallback(delegate.getCleanupCallback(taskId)) + .taskStateProvider(taskStateProvider); } } + private class ReplicationHook implements EventEnqueueHook { private final String taskId; @@ -115,11 +157,12 @@ public ReplicationHook(String taskId) { } @Override - public void onEnqueue(Event event) { - // Only replicate if this isn't already a replicated event being processed - if (!Boolean.TRUE.equals(isHandlingReplicatedEvent.get())) { - if (replicationStrategy != null && taskId != null) { - replicationStrategy.send(taskId, event); + public void onEnqueue(EventQueueItem item) { + if (!item.isReplicated()) { + // Only replicate if this isn't already a replicated event + // This prevents replication loops + if (taskId != null) { + replicationStrategy.send(taskId, item.getEvent()); } } } diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index 749f0a308..f05201fb7 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -10,6 +10,7 @@ import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.server.events.QueueClosedEvent; import io.a2a.spec.Artifact; import io.a2a.spec.Event; import io.a2a.spec.InternalError; @@ -200,7 +201,7 @@ public void testJSONRPCErrorSubclassesSerialization() throws JsonProcessingExcep @Test public void testReplicatedEventWithStreamingEventSerialization() throws JsonProcessingException { - // Test that ReplicatedEvent can properly handle StreamingEventKind + // Test that ReplicatedEventQueueItem can properly handle StreamingEventKind TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() .taskId("replicated-test-task") .contextId("replicated-test-context") @@ -208,26 +209,26 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc .isFinal(false) .build(); - // Create ReplicatedEvent with StreamingEventKind - ReplicatedEvent originalReplicatedEvent = new ReplicatedEvent("replicated-test-task", statusEvent); + // Create ReplicatedEventQueueItem with StreamingEventKind + ReplicatedEventQueueItem originalReplicatedEvent = new ReplicatedEventQueueItem("replicated-test-task", statusEvent); - // Serialize the ReplicatedEvent + // Serialize the ReplicatedEventQueueItem String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); assertTrue(json.contains("\"taskId\":\"replicated-test-task\""), "JSON should contain task ID"); assertTrue(json.contains("\"event\""), "JSON should contain event field"); assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain the event kind"); assertFalse(json.contains("\"error\""), "JSON should not contain error field"); - // Deserialize the ReplicatedEvent - ReplicatedEvent deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEvent.class); + // Deserialize the ReplicatedEventQueueItem + ReplicatedEventQueueItem deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); // Now we should get the proper type back! - StreamingEventKind retrievedEvent = deserializedReplicatedEvent.getEvent(); - assertNotNull(retrievedEvent); - assertInstanceOf(TaskStatusUpdateEvent.class, retrievedEvent, "Should deserialize to TaskStatusUpdateEvent"); + Event retrievedEventAsEvent = deserializedReplicatedEvent.getEvent(); + assertNotNull(retrievedEventAsEvent); + assertInstanceOf(TaskStatusUpdateEvent.class, retrievedEventAsEvent, "Should deserialize to TaskStatusUpdateEvent"); - TaskStatusUpdateEvent retrievedStatusEvent = (TaskStatusUpdateEvent) retrievedEvent; + TaskStatusUpdateEvent retrievedStatusEvent = (TaskStatusUpdateEvent) retrievedEventAsEvent; assertEquals(statusEvent.getTaskId(), retrievedStatusEvent.getTaskId()); assertEquals(statusEvent.getContextId(), retrievedStatusEvent.getContextId()); assertEquals(statusEvent.getStatus().state(), retrievedStatusEvent.getStatus().state()); @@ -236,30 +237,31 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc // Test helper methods assertTrue(deserializedReplicatedEvent.hasEvent()); assertFalse(deserializedReplicatedEvent.hasError()); - assertNull(deserializedReplicatedEvent.getError()); + assertFalse(deserializedReplicatedEvent.isClosedEvent()); + assertNull(deserializedReplicatedEvent.getErrorObject()); } @Test public void testReplicatedEventWithErrorSerialization() throws JsonProcessingException { - // Test that ReplicatedEvent can properly handle JSONRPCError + // Test that ReplicatedEventQueueItem can properly handle JSONRPCError InvalidRequestError error = new InvalidRequestError("Invalid request for testing"); - // Create ReplicatedEvent with JSONRPCError - ReplicatedEvent originalReplicatedEvent = new ReplicatedEvent("error-test-task", error); + // Create ReplicatedEventQueueItem with JSONRPCError + ReplicatedEventQueueItem originalReplicatedEvent = new ReplicatedEventQueueItem("error-test-task", error); - // Serialize the ReplicatedEvent + // Serialize the ReplicatedEventQueueItemQueueItem String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); assertTrue(json.contains("\"taskId\":\"error-test-task\""), "JSON should contain task ID"); assertTrue(json.contains("\"error\""), "JSON should contain error field"); assertTrue(json.contains("\"message\""), "JSON should contain error message"); assertFalse(json.contains("\"event\""), "JSON should not contain event field"); - // Deserialize the ReplicatedEvent - ReplicatedEvent deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEvent.class); + // Deserialize the ReplicatedEventQueueItem + ReplicatedEventQueueItem deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); // Should get the error back - JSONRPCError retrievedError = deserializedReplicatedEvent.getError(); + JSONRPCError retrievedError = deserializedReplicatedEvent.getErrorObject(); assertNotNull(retrievedError); assertEquals(error.getMessage(), retrievedError.getMessage()); assertEquals(error.getCode(), retrievedError.getCode()); @@ -267,7 +269,8 @@ public void testReplicatedEventWithErrorSerialization() throws JsonProcessingExc // Test helper methods assertFalse(deserializedReplicatedEvent.hasEvent()); assertTrue(deserializedReplicatedEvent.hasError()); - assertNull(deserializedReplicatedEvent.getEvent()); + assertFalse(deserializedReplicatedEvent.isClosedEvent()); + assertNull(deserializedReplicatedEvent.getStreamingEvent()); } @Test @@ -281,11 +284,51 @@ public void testReplicatedEventBackwardCompatibility() throws JsonProcessingExce .build(); // Use the backward compatibility constructor - ReplicatedEvent replicatedEvent = new ReplicatedEvent("backward-compat-task", (Event) statusEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem("backward-compat-task", (Event) statusEvent); // Should work the same as the specific constructor assertTrue(replicatedEvent.hasEvent()); assertFalse(replicatedEvent.hasError()); + assertFalse(replicatedEvent.isClosedEvent()); assertInstanceOf(TaskStatusUpdateEvent.class, replicatedEvent.getEvent()); } + + @Test + public void testQueueClosedEventSerialization() throws JsonProcessingException { + // Test that QueueClosedEvent can be properly serialized and deserialized via ReplicatedEventQueueItem + String taskId = "queue-closed-serialization-test"; + QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); + + // Create ReplicatedEventQueueItem with QueueClosedEvent + ReplicatedEventQueueItem originalReplicatedEvent = new ReplicatedEventQueueItem(taskId, closedEvent); + + // Verify the item is marked as a closed event + assertTrue(originalReplicatedEvent.isClosedEvent(), "Should be marked as closed event"); + assertFalse(originalReplicatedEvent.hasEvent(), "Should not have regular event"); + assertFalse(originalReplicatedEvent.hasError(), "Should not have error"); + + // Serialize the ReplicatedEventQueueItem + String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); + assertTrue(json.contains("\"taskId\":\"" + taskId + "\""), "JSON should contain task ID"); + assertTrue(json.contains("\"closedEvent\":true"), "JSON should contain closedEvent flag set to true"); + assertFalse(json.contains("\"event\""), "JSON should not contain event field"); + assertFalse(json.contains("\"error\""), "JSON should not contain error field"); + + // Deserialize the ReplicatedEventQueueItem + ReplicatedEventQueueItem deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + assertEquals(taskId, deserializedReplicatedEvent.getTaskId()); + + // Verify the deserialized item is marked as a closed event + assertTrue(deserializedReplicatedEvent.isClosedEvent(), "Deserialized should be marked as closed event"); + assertFalse(deserializedReplicatedEvent.hasEvent(), "Deserialized should not have regular event"); + assertFalse(deserializedReplicatedEvent.hasError(), "Deserialized should not have error"); + + // Verify getEvent() reconstructs the QueueClosedEvent + Event retrievedEvent = deserializedReplicatedEvent.getEvent(); + assertNotNull(retrievedEvent, "getEvent() should return a reconstructed QueueClosedEvent"); + assertInstanceOf(QueueClosedEvent.class, retrievedEvent, "Should deserialize to QueueClosedEvent"); + + QueueClosedEvent retrievedClosedEvent = (QueueClosedEvent) retrievedEvent; + assertEquals(taskId, retrievedClosedEvent.getTaskId(), "Reconstructed event should have correct task ID"); + } } \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 3fb52617d..3e2da2f51 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -16,9 +16,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import io.a2a.extras.common.events.TaskFinalizedEvent; import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueClosedException; +import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.EventQueueTestHelper; +import io.a2a.server.events.QueueClosedEvent; import io.a2a.spec.Event; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskState; @@ -35,7 +38,7 @@ class ReplicatedQueueManagerTest { @BeforeEach void setUp() { - queueManager = new ReplicatedQueueManager(); + queueManager = new ReplicatedQueueManager(new NoOpReplicationStrategy(), new MockTaskStateProvider(true)); testEvent = new TaskStatusUpdateEvent.Builder() .taskId("test-task") .contextId("test-context") @@ -47,7 +50,7 @@ void setUp() { @Test void testReplicationStrategyTriggeredOnNormalEnqueue() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); String taskId = "test-task-1"; EventQueue queue = queueManager.createOrTap(taskId); @@ -62,12 +65,12 @@ void testReplicationStrategyTriggeredOnNormalEnqueue() throws InterruptedExcepti @Test void testReplicationStrategyNotTriggeredOnReplicatedEvent() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); String taskId = "test-task-2"; EventQueue queue = queueManager.createOrTap(taskId); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); queueManager.onReplicatedEvent(replicatedEvent); assertEquals(0, strategy.getCallCount()); @@ -76,7 +79,7 @@ void testReplicationStrategyNotTriggeredOnReplicatedEvent() throws InterruptedEx @Test void testReplicationStrategyWithCountingImplementation() throws InterruptedException { CountingReplicationStrategy countingStrategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(countingStrategy); + queueManager = new ReplicatedQueueManager(countingStrategy, new MockTaskStateProvider(true)); String taskId = "test-task-3"; EventQueue queue = queueManager.createOrTap(taskId); @@ -88,7 +91,7 @@ void testReplicationStrategyWithCountingImplementation() throws InterruptedExcep assertEquals(taskId, countingStrategy.getLastTaskId()); assertEquals(testEvent, countingStrategy.getLastEvent()); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); queueManager.onReplicatedEvent(replicatedEvent); assertEquals(2, countingStrategy.getCallCount()); @@ -99,12 +102,12 @@ void testReplicatedEventDeliveredToCorrectQueue() throws InterruptedException { String taskId = "test-task-4"; EventQueue queue = queueManager.createOrTap(taskId); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); queueManager.onReplicatedEvent(replicatedEvent); Event dequeuedEvent; try { - dequeuedEvent = queue.dequeueEvent(100); + dequeuedEvent = queue.dequeueEventItem(100).getEvent(); } catch (EventQueueClosedException e) { fail("Queue should not be closed"); return; @@ -119,7 +122,7 @@ void testReplicatedEventCreatesQueueIfNeeded() throws InterruptedException { // Verify no queue exists initially assertNull(queueManager.get(taskId)); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, testEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); // Process the replicated event assertDoesNotThrow(() -> queueManager.onReplicatedEvent(replicatedEvent)); @@ -131,7 +134,7 @@ void testReplicatedEventCreatesQueueIfNeeded() throws InterruptedException { // Verify the event was enqueued by dequeuing it Event dequeuedEvent; try { - dequeuedEvent = queue.dequeueEvent(100); + dequeuedEvent = queue.dequeueEventItem(100).getEvent(); } catch (EventQueueClosedException e) { fail("Queue should not be closed"); return; @@ -167,32 +170,33 @@ void testBasicQueueManagerFunctionality() throws InterruptedException { void testQueueToTaskIdMappingMaintained() throws InterruptedException { String taskId = "test-task-6"; CountingReplicationStrategy countingStrategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(countingStrategy); + queueManager = new ReplicatedQueueManager(countingStrategy, new MockTaskStateProvider(true)); EventQueue queue = queueManager.createOrTap(taskId); queue.enqueueEvent(testEvent); assertEquals(taskId, countingStrategy.getLastTaskId()); - queueManager.close(taskId); + queueManager.close(taskId); // Task is active, so NO poison pill is sent EventQueue newQueue = queueManager.createOrTap(taskId); newQueue.enqueueEvent(testEvent); assertEquals(taskId, countingStrategy.getLastTaskId()); + // 2 replication calls: 1 testEvent, 1 testEvent (no QueueClosedEvent because task is active) assertEquals(2, countingStrategy.getCallCount()); } @Test void testReplicatedEventJsonSerialization() throws Exception { - // Test that ReplicatedEvent can be properly serialized and deserialized with StreamingEventKind + // Test that ReplicatedEventQueueItem can be properly serialized and deserialized with StreamingEventKind TaskStatusUpdateEvent originalEvent = new TaskStatusUpdateEvent.Builder() .taskId("json-test-task") .contextId("json-test-context") .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build(); - ReplicatedEvent original = new ReplicatedEvent("json-test-task", originalEvent); + ReplicatedEventQueueItem original = new ReplicatedEventQueueItem("json-test-task", originalEvent); // Serialize to JSON String json = Utils.OBJECT_MAPPER.writeValueAsString(original); @@ -202,7 +206,7 @@ void testReplicatedEventJsonSerialization() throws Exception { assertTrue(json.contains("\"kind\":\"status-update\"")); // Deserialize back - ReplicatedEvent deserialized = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEvent.class); + ReplicatedEventQueueItem deserialized = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); assertNotNull(deserialized); assertEquals("json-test-task", deserialized.getTaskId()); assertNotNull(deserialized.getEvent()); @@ -213,7 +217,7 @@ void testReplicatedEventJsonSerialization() throws Exception { @Test void testParallelReplicationBehavior() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); String taskId = "parallel-test-task"; EventQueue queue = queueManager.createOrTap(taskId); @@ -261,7 +265,7 @@ void testParallelReplicationBehavior() throws InterruptedException { .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build(); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, event); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); queueManager.onReplicatedEvent(replicatedEvent); Thread.sleep(1); // Small delay to interleave operations } @@ -289,6 +293,205 @@ void testParallelReplicationBehavior() throws InterruptedException { "Only normal enqueue operations should trigger replication, not replicated events"); } + @Test + void testReplicatedEventSkippedWhenTaskInactive() throws InterruptedException { + // Create a task state provider that returns false (task is inactive) + MockTaskStateProvider stateProvider = new MockTaskStateProvider(false); + queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider); + + String taskId = "inactive-task"; + + // Verify no queue exists initially + assertNull(queueManager.get(taskId)); + + // Process a replicated event for an inactive task + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); + queueManager.onReplicatedEvent(replicatedEvent); + + // Queue should NOT be created because task is inactive + assertNull(queueManager.get(taskId), "Queue should not be created for inactive task"); + } + + @Test + void testReplicatedEventProcessedWhenTaskActive() throws InterruptedException { + // Create a task state provider that returns true (task is active) + MockTaskStateProvider stateProvider = new MockTaskStateProvider(true); + queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider); + + String taskId = "active-task"; + + // Verify no queue exists initially + assertNull(queueManager.get(taskId)); + + // Process a replicated event for an active task + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); + queueManager.onReplicatedEvent(replicatedEvent); + + // Queue should be created and event should be enqueued + EventQueue queue = queueManager.get(taskId); + assertNotNull(queue, "Queue should be created for active task"); + + // Verify the event was enqueued + Event dequeuedEvent; + try { + dequeuedEvent = queue.dequeueEventItem(100).getEvent(); + } catch (EventQueueClosedException e) { + fail("Queue should not be closed"); + return; + } + assertEquals(testEvent, dequeuedEvent, "Event should be enqueued for active task"); + } + + + @Test + void testReplicatedEventToExistingQueueWhenTaskBecomesInactive() throws InterruptedException { + // Create a task state provider that returns true initially + MockTaskStateProvider stateProvider = new MockTaskStateProvider(true); + queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider); + + String taskId = "task-becomes-inactive"; + + // Create queue and enqueue an event + EventQueue queue = queueManager.createOrTap(taskId); + queue.enqueueEvent(testEvent); + + // Dequeue to clear the queue + try { + queue.dequeueEventItem(100); + } catch (EventQueueClosedException e) { + fail("Queue should not be closed"); + } + + // Now mark task as inactive + stateProvider.setActive(false); + + // Process a replicated event - should be skipped + TaskStatusUpdateEvent newEvent = new TaskStatusUpdateEvent.Builder() + .taskId(taskId) + .contextId("test-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .isFinal(true) + .build(); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, newEvent); + queueManager.onReplicatedEvent(replicatedEvent); + + // Try to dequeue with a short timeout - should timeout (no new event) + try { + EventQueueItem item = queue.dequeueEventItem(100); + assertNull(item, "No event should be enqueued for inactive task"); + } catch (EventQueueClosedException e) { + fail("Queue should not be closed"); + } + } + + @Test + void testPoisonPillSentViaTransactionAwareEvent() throws InterruptedException { + CountingReplicationStrategy strategy = new CountingReplicationStrategy(); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); + + String taskId = "poison-pill-test"; + EventQueue queue = queueManager.createOrTap(taskId); + + // Enqueue a normal event first + queue.enqueueEvent(testEvent); + + // In the new architecture, QueueClosedEvent (poison pill) is sent via CDI events + // when JpaDatabaseTaskStore.save() persists a final task and the transaction commits + // ReplicatedQueueManager.onTaskFinalized() observes AFTER_SUCCESS and sends the poison pill + + // Simulate the CDI event observer being called (what happens in real execution) + TaskFinalizedEvent taskFinalizedEvent = new TaskFinalizedEvent(taskId); + + // Call the observer method directly (simulating CDI event delivery) + queueManager.onTaskFinalized(taskFinalizedEvent); + + // Verify that QueueClosedEvent was replicated + // strategy.getCallCount() should be 2: one for testEvent, one for QueueClosedEvent + assertEquals(2, strategy.getCallCount(), "Should have replicated both normal event and QueueClosedEvent"); + + Event lastEvent = strategy.getLastEvent(); + assertTrue(lastEvent instanceof QueueClosedEvent, "Last replicated event should be QueueClosedEvent"); + assertEquals(taskId, ((QueueClosedEvent) lastEvent).getTaskId()); + } + + @Test + void testQueueClosedEventJsonSerialization() throws Exception { + // Test that ReplicatedEventQueueItem can serialize/deserialize QueueClosedEvent + String taskId = "closed-event-json-test"; + QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); + ReplicatedEventQueueItem original = new ReplicatedEventQueueItem(taskId, closedEvent); + + // Verify the item is marked as closed event + assertTrue(original.isClosedEvent(), "Should be marked as closed event"); + assertFalse(original.hasEvent(), "Should not have regular event"); + assertFalse(original.hasError(), "Should not have error"); + + // Serialize to JSON + String json = Utils.OBJECT_MAPPER.writeValueAsString(original); + assertNotNull(json); + assertTrue(json.contains(taskId), "JSON should contain taskId"); + assertTrue(json.contains("\"closedEvent\":true"), "JSON should contain closedEvent flag"); + + // Deserialize back + ReplicatedEventQueueItem deserialized = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + assertNotNull(deserialized); + assertEquals(taskId, deserialized.getTaskId()); + assertTrue(deserialized.isClosedEvent(), "Deserialized should be marked as closed event"); + assertFalse(deserialized.hasEvent(), "Deserialized should not have regular event"); + assertFalse(deserialized.hasError(), "Deserialized should not have error"); + + // Verify getEvent() returns QueueClosedEvent + Event reconstructedEvent = deserialized.getEvent(); + assertNotNull(reconstructedEvent); + assertTrue(reconstructedEvent instanceof QueueClosedEvent, + "getEvent() should return QueueClosedEvent"); + assertEquals(taskId, ((QueueClosedEvent) reconstructedEvent).getTaskId()); + } + + @Test + void testReplicatedQueueClosedEventTerminatesConsumer() throws InterruptedException { + String taskId = "remote-close-test"; + EventQueue queue = queueManager.createOrTap(taskId); + + // Enqueue a normal event + queue.enqueueEvent(testEvent); + + // Simulate receiving QueueClosedEvent from remote node + QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); + ReplicatedEventQueueItem replicatedClosedEvent = new ReplicatedEventQueueItem(taskId, closedEvent); + queueManager.onReplicatedEvent(replicatedClosedEvent); + + // Dequeue the normal event first + EventQueueItem item1; + try { + item1 = queue.dequeueEventItem(100); + } catch (EventQueueClosedException e) { + fail("Should not throw on first dequeue"); + return; + } + assertNotNull(item1); + assertEquals(testEvent, item1.getEvent()); + + // Next dequeue should get the QueueClosedEvent + EventQueueItem item2; + try { + item2 = queue.dequeueEventItem(100); + } catch (EventQueueClosedException e) { + fail("Should not throw on second dequeue, should return the event"); + return; + } + assertNotNull(item2); + assertTrue(item2.getEvent() instanceof QueueClosedEvent, + "Second event should be QueueClosedEvent"); + } + + private static class NoOpReplicationStrategy implements ReplicationStrategy { + @Override + public void send(String taskId, Event event) { + // No-op for tests that don't care about replication + } + } + private static class CountingReplicationStrategy implements ReplicationStrategy { private final AtomicInteger callCount = new AtomicInteger(0); private volatile String lastTaskId; @@ -314,4 +517,26 @@ public Event getLastEvent() { } } + + private static class MockTaskStateProvider implements io.a2a.server.tasks.TaskStateProvider { + private volatile boolean active; + + public MockTaskStateProvider(boolean active) { + this.active = active; + } + + @Override + public boolean isTaskActive(String taskId) { + return active; + } + + @Override + public boolean isTaskFinalized(String taskId) { + return !active; // If task is inactive, it's finalized; if active, not finalized + } + + public void setActive(boolean active) { + this.active = active; + } + } } \ No newline at end of file diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java index 9836095bd..04238c27d 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java @@ -5,7 +5,7 @@ import jakarta.inject.Inject; import com.fasterxml.jackson.core.type.TypeReference; -import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.extras.queuemanager.replicated.core.ReplicationStrategy; import io.a2a.util.Utils; import org.eclipse.microprofile.reactive.messaging.Channel; @@ -18,21 +18,21 @@ public class ReactiveMessagingReplicationStrategy implements ReplicationStrategy { private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveMessagingReplicationStrategy.class); - private static final TypeReference REPLICATED_EVENT_TYPE_REF = new TypeReference() {}; + private static final TypeReference REPLICATED_EVENT_TYPE_REF = new TypeReference() {}; @Inject @Channel("replicated-events-out") private Emitter emitter; @Inject - private Event cdiEvent; + private Event cdiEvent; @Override public void send(String taskId, io.a2a.spec.Event event) { LOGGER.debug("Sending replicated event for task: {}, event: {}", taskId, event); try { - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, event); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); String json = Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); emitter.send(json); LOGGER.debug("Successfully sent replicated event for task: {}", taskId); @@ -47,7 +47,7 @@ public void onReplicatedEvent(String jsonMessage) { LOGGER.debug("Received replicated event JSON: {}", jsonMessage); try { - ReplicatedEvent replicatedEvent = Utils.unmarshalFrom(jsonMessage, REPLICATED_EVENT_TYPE_REF); + ReplicatedEventQueueItem replicatedEvent = Utils.unmarshalFrom(jsonMessage, REPLICATED_EVENT_TYPE_REF); LOGGER.debug("Deserialized replicated event for task: {}, event: {}", replicatedEvent.getTaskId(), replicatedEvent.getEvent()); diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java index 32ae1d21a..a10b019db 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java @@ -14,7 +14,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskState; @@ -28,7 +28,7 @@ class ReactiveMessagingReplicationStrategyTest { private Emitter emitter; @Mock - private Event cdiEvent; + private Event cdiEvent; @InjectMocks private ReactiveMessagingReplicationStrategy strategy; @@ -46,14 +46,14 @@ public void setUp() { } private String createValidJsonMessage(String taskId, String contextId) throws Exception { - // Create a proper ReplicatedEvent JSON with StreamingEventKind + // Create a proper ReplicatedEventQueueItem JSON with StreamingEventKind TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() .taskId(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) .isFinal(false) .build(); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, event); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); return Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); } @@ -103,7 +103,7 @@ public void testOnReplicatedEventHandlesInvalidJson() { assertDoesNotThrow(() -> strategy.onReplicatedEvent(invalidJsonMessage)); // CDI event should not be fired for invalid JSON - verify(cdiEvent, never()).fire(any(ReplicatedEvent.class)); + verify(cdiEvent, never()).fire(any(ReplicatedEventQueueItem.class)); } diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests/pom.xml index 5dd2e059a..390d0bc3f 100644 --- a/extras/queue-manager-replicated/tests/pom.xml +++ b/extras/queue-manager-replicated/tests/pom.xml @@ -32,6 +32,24 @@ a2a-java-sdk-reference-jsonrpc + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + ${project.version} + test + + + io.quarkus + quarkus-jdbc-h2 + test + + + io.quarkus + quarkus-hibernate-orm + test + + io.github.a2asdk diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 8756820bc..fee44ca93 100644 --- a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -24,14 +24,14 @@ import io.a2a.client.config.ClientConfig; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; -import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.server.PublicAgentCard; +import io.a2a.server.events.QueueClosedEvent; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; -import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; @@ -40,8 +40,8 @@ import io.quarkus.test.junit.QuarkusTest; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -49,7 +49,6 @@ * Tests the full A2A message flow with Kafka replication verification. */ @QuarkusTest -@Disabled public class KafkaReplicationIntegrationTest { @Inject @@ -65,6 +64,7 @@ public class KafkaReplicationIntegrationTest { private Client streamingClient; private Client nonStreamingClient; + private Client pollingClient; @BeforeEach public void setup() throws A2AClientException { @@ -87,6 +87,31 @@ public void setup() throws A2AClientException { .clientConfig(streamingConfig) .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) .build(); + + // Create polling client for non-blocking operations + ClientConfig pollingConfig = new ClientConfig.Builder() + .setStreaming(false) + .setPolling(true) + .build(); + + pollingClient = Client.builder(agentCard) + .clientConfig(pollingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + } + + @AfterEach + public void tearDown() throws Exception { + // Close all clients to release resources and prevent connection leaks + if (nonStreamingClient != null) { + nonStreamingClient.close(); + } + if (streamingClient != null) { + streamingClient.close(); + } + if (pollingClient != null) { + pollingClient.close(); + } } @Test @@ -131,7 +156,7 @@ public void testA2AMessageReplicatedToKafka() throws Exception { assertEquals(TaskState.SUBMITTED, task.getStatus().state()); // Wait for the event to be replicated to Kafka - ReplicatedEvent replicatedEvent = testConsumer.waitForEvent(taskId, 30); + ReplicatedEventQueueItem replicatedEvent = testConsumer.waitForEvent(taskId, 30); assertNotNull(replicatedEvent, "Event should be replicated to Kafka within 30 seconds"); // Verify the replicated event content @@ -228,7 +253,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { .isFinal(true) .build(); - ReplicatedEvent replicatedEvent = new ReplicatedEvent(taskId, statusEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, statusEvent); String eventJson = Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); // Send to Kafka using reactive messaging @@ -250,14 +275,152 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { assertEquals(taskId, completedEvent.getTaskId(), "Event should have correct task ID"); assertEquals(contextId, completedEvent.getContextId(), "Event should have correct context ID"); - // Also verify via client API that the task state was updated - Task updatedTask = nonStreamingClient.getTask(new TaskQueryParams(taskId, null)); - assertNotNull(updatedTask, "Task should still exist"); - assertEquals(TaskState.COMPLETED, updatedTask.getStatus().state(), "Task should now show COMPLETED state after Kafka replication"); + // Note: We do NOT verify TaskStore state here because replicated events intentionally + // skip TaskStore updates to avoid duplicates. The TaskStore is updated on the originating + // node that produced the event. This test verifies the replication flow: + // Kafka -> EventQueue -> Streaming Clients, which is working correctly. + } + + @Test + public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { + String taskId = "queue-closed-test-" + System.currentTimeMillis(); + String contextId = "test-context-" + System.currentTimeMillis(); + + // Clear any previous events + testConsumer.clear(); + + // Use polling (non-blocking) client with "working" command + // This creates task in WORKING state (non-final) and keeps queue alive + Message workingMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("working"))) + .taskId(taskId) + .messageId("working-msg-" + System.currentTimeMillis()) + .contextId(contextId) + .build(); + + CountDownLatch workingLatch = new CountDownLatch(1); + AtomicReference taskIdRef = new AtomicReference<>(); + + pollingClient.sendMessage(workingMessage, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof TaskEvent taskEvent) { + taskIdRef.set(taskEvent.getTask().getId()); + workingLatch.countDown(); + } else if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { + if (status.getStatus().state() == TaskState.WORKING) { + taskIdRef.set(status.getTaskId()); + workingLatch.countDown(); + } + } + }), (Throwable error) -> { + workingLatch.countDown(); + }); + + assertTrue(workingLatch.await(15, TimeUnit.SECONDS), "Task creation timed out"); + String createdTaskId = taskIdRef.get(); + assertNotNull(createdTaskId, "Task should be created"); + assertEquals(taskId, createdTaskId); + + // Set up streaming resubscription to listen for the QueueClosedEvent + CountDownLatch streamCompletedLatch = new CountDownLatch(1); + AtomicBoolean streamCompleted = new AtomicBoolean(false); + AtomicBoolean streamErrored = new AtomicBoolean(false); + AtomicReference errorRef = new AtomicReference<>(); + + // Create consumer - we expect the stream to complete when QueueClosedEvent arrives + BiConsumer consumer = (event, agentCard) -> { + // We might receive some events before the stream completes, that's fine + // The important thing is that the stream eventually completes + }; + + // Create error handler that captures completion + Consumer errorHandler = error -> { + if (error == null) { + // null error means stream completed normally + streamCompleted.set(true); + } else { + streamErrored.set(true); + errorRef.set(error); + } + streamCompletedLatch.countDown(); + }; + + // Resubscribe to the task - this creates a streaming subscription + streamingClient.resubscribe(new TaskIdParams(taskId), List.of(consumer), errorHandler); + + // Wait a moment to ensure the streaming subscription is fully established + Thread.sleep(2000); + + // Now manually send a QueueClosedEvent to Kafka to simulate queue closure on another node + QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); + ReplicatedEventQueueItem replicatedClosedEvent = new ReplicatedEventQueueItem(taskId, closedEvent); + String eventJson = Utils.OBJECT_MAPPER.writeValueAsString(replicatedClosedEvent); + + // Send to Kafka using reactive messaging + testEmitter.send(eventJson); + + // Wait for the stream to complete - should happen when QueueClosedEvent is received + // Allow extra time for Kafka message processing and CDI event propagation + assertTrue(streamCompletedLatch.await(30, TimeUnit.SECONDS), + "Streaming subscription should complete when QueueClosedEvent is received"); + + // Verify the stream completed normally (not with an error) + assertTrue(streamCompleted.get(), "Stream should complete normally when QueueClosedEvent is received"); + assertFalse(streamErrored.get(), "Stream should not error on QueueClosedEvent"); + assertNull(errorRef.get(), "Should not receive error when stream completes gracefully"); + } + + @Test + public void testPoisonPillGenerationOnTaskFinalization() throws Exception { + String taskId = "poison-pill-gen-test-" + System.currentTimeMillis(); + String contextId = "test-context-" + System.currentTimeMillis(); + + // Clear any previous events + testConsumer.clear(); + + // Create a task that will be completed (finalized) + Message completeMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("complete"))) + .taskId(taskId) + .messageId("complete-msg-" + System.currentTimeMillis()) + .contextId(contextId) + .build(); + + CountDownLatch completeLatch = new CountDownLatch(1); + AtomicReference finalTask = new AtomicReference<>(); + + nonStreamingClient.sendMessage(completeMessage, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof TaskEvent taskEvent) { + finalTask.set(taskEvent.getTask()); + // Count down for any task event - we just need the task to be created + completeLatch.countDown(); + } + }), (Throwable error) -> { + completeLatch.countDown(); + }); - // Note: The replicated event goes to the local queue, but since we created the task - // and immediately sent a completion event, the task lifecycle might not reflect this - // in the client API. The important thing is that the replication system works. + assertTrue(completeLatch.await(15, TimeUnit.SECONDS), "Task creation timed out"); + Task createdTask = finalTask.get(); + assertNotNull(createdTask, "Task should be created"); + + // The task should complete very quickly since it's a simple operation + // Wait a moment to ensure all events have been enqueued + Thread.sleep(2000); + + // Wait for the QueueClosedEvent to be published to Kafka + // The event should be published automatically because isTaskFinalized() returns true + ReplicatedEventQueueItem poisonPill = testConsumer.waitForClosedEvent(taskId, 30); + assertNotNull(poisonPill, "QueueClosedEvent should be published to Kafka when task is finalized"); + assertTrue(poisonPill.isClosedEvent(), "Event should be marked as closedEvent"); + assertEquals(taskId, poisonPill.getTaskId(), "QueueClosedEvent should have correct task ID"); + + // Verify the event is actually a QueueClosedEvent + assertInstanceOf(QueueClosedEvent.class, poisonPill.getEvent(), + "Event should be a QueueClosedEvent"); + QueueClosedEvent closedEvent = + (QueueClosedEvent) poisonPill.getEvent(); + assertEquals(taskId, closedEvent.getTaskId(), "QueueClosedEvent task ID should match"); } } \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java index 556ceab49..4db27cb14 100644 --- a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java @@ -38,6 +38,18 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP // Submit task - this should trigger TaskStatusUpdateEvent taskUpdater.submit(); break; + case "working": + // Move task to WORKING state without completing - keeps queue alive + taskUpdater.submit(); + taskUpdater.startWork(); + break; + case "complete": + // Complete the task - should trigger poison pill generation + taskUpdater.submit(); + taskUpdater.startWork(); + taskUpdater.addArtifact(List.of(new TextPart("Task completed"))); + taskUpdater.complete(); + break; default: throw new InvalidRequestError("Unknown command: " + lastText); } diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java index f82719733..7571a6ba9 100644 --- a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java +++ b/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java @@ -8,7 +8,7 @@ import org.eclipse.microprofile.reactive.messaging.Incoming; -import io.a2a.extras.queuemanager.replicated.core.ReplicatedEvent; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.util.Utils; import io.quarkus.arc.profile.IfBuildProfile; @@ -20,13 +20,13 @@ @ApplicationScoped public class TestKafkaEventConsumer { - private final ConcurrentLinkedQueue receivedEvents = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue receivedEvents = new ConcurrentLinkedQueue<>(); private volatile CountDownLatch eventLatch; @Incoming("test-replicated-events-in") public void onTestReplicatedEvent(String jsonMessage) { try { - ReplicatedEvent event = Utils.OBJECT_MAPPER.readValue(jsonMessage, ReplicatedEvent.class); + ReplicatedEventQueueItem event = Utils.OBJECT_MAPPER.readValue(jsonMessage, ReplicatedEventQueueItem.class); receivedEvents.offer(event); // Signal any waiting threads @@ -44,11 +44,11 @@ public void onTestReplicatedEvent(String jsonMessage) { * Wait for an event matching the given task ID. * @param taskId the task ID to wait for * @param timeoutSeconds maximum time to wait - * @return the matching ReplicatedEvent, or null if timeout + * @return the matching ReplicatedEventQueueItem, or null if timeout */ - public ReplicatedEvent waitForEvent(String taskId, int timeoutSeconds) throws InterruptedException { + public ReplicatedEventQueueItem waitForEvent(String taskId, int timeoutSeconds) throws InterruptedException { // Check if we already have the event - ReplicatedEvent existing = findEventByTaskId(taskId); + ReplicatedEventQueueItem existing = findEventByTaskId(taskId); if (existing != null) { return existing; } @@ -74,11 +74,11 @@ public ReplicatedEvent waitForEvent(String taskId, int timeoutSeconds) throws In * @param taskId the task ID to wait for * @param contentMatch a string that must be present in the event * @param timeoutSeconds maximum time to wait - * @return the matching ReplicatedEvent, or null if timeout + * @return the matching ReplicatedEventQueueItem, or null if timeout */ - public ReplicatedEvent waitForEventWithContent(String taskId, String contentMatch, int timeoutSeconds) throws InterruptedException { + public ReplicatedEventQueueItem waitForEventWithContent(String taskId, String contentMatch, int timeoutSeconds) throws InterruptedException { // Check if we already have the event - ReplicatedEvent existing = findEventByTaskIdWithContent(taskId, contentMatch); + ReplicatedEventQueueItem existing = findEventByTaskIdWithContent(taskId, contentMatch); if (existing != null) { return existing; } @@ -99,20 +99,59 @@ public ReplicatedEvent waitForEventWithContent(String taskId, String contentMatc } } + /** + * Wait for a QueueClosedEvent matching the given task ID. + * @param taskId the task ID to wait for + * @param timeoutSeconds maximum time to wait + * @return the matching ReplicatedEventQueueItem with QueueClosedEvent, or null if timeout + */ + public ReplicatedEventQueueItem waitForClosedEvent(String taskId, int timeoutSeconds) throws InterruptedException { + // Check if we already have the event + ReplicatedEventQueueItem existing = findClosedEventByTaskId(taskId); + if (existing != null) { + return existing; + } + + // Set up latch to wait for new events + eventLatch = new CountDownLatch(1); + + try { + // Wait for new events + boolean received = eventLatch.await(timeoutSeconds, TimeUnit.SECONDS); + if (received) { + // Check again for the event + return findClosedEventByTaskId(taskId); + } + return null; + } finally { + eventLatch = null; + } + } + /** * Find an event by task ID in the received events. */ - private ReplicatedEvent findEventByTaskId(String taskId) { + private ReplicatedEventQueueItem findEventByTaskId(String taskId) { return receivedEvents.stream() .filter(event -> taskId.equals(event.getTaskId())) .findFirst() .orElse(null); } + /** + * Find a QueueClosedEvent by task ID in the received events. + */ + private ReplicatedEventQueueItem findClosedEventByTaskId(String taskId) { + return receivedEvents.stream() + .filter(event -> taskId.equals(event.getTaskId()) && event.isClosedEvent()) + .findFirst() + .orElse(null); + } + /** * Find an event by task ID and content match in the received events. */ - private ReplicatedEvent findEventByTaskIdWithContent(String taskId, String contentMatch) { + private ReplicatedEventQueueItem findEventByTaskIdWithContent(String taskId, String contentMatch) { return receivedEvents.stream() .filter(event -> taskId.equals(event.getTaskId()) && event.getEvent().toString().contains(contentMatch)) diff --git a/extras/queue-manager-replicated/tests/src/test/resources/application.properties b/extras/queue-manager-replicated/tests/src/test/resources/application.properties index 69110ff3a..1e02c4559 100644 --- a/extras/queue-manager-replicated/tests/src/test/resources/application.properties +++ b/extras/queue-manager-replicated/tests/src/test/resources/application.properties @@ -1,5 +1,12 @@ # Select our ReplicatedQueueManager as the active implementation -quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager +quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager,io.a2a.extras.taskstore.database.jpa.JpaDatabaseTaskStore + +# Configure in-memory H2 database for testing +quarkus.datasource."a2a-java".db-kind=h2 +quarkus.datasource."a2a-java".jdbc.url=jdbc:h2:mem:test +quarkus.hibernate-orm."a2a-java".datasource=a2a-java +quarkus.hibernate-orm."a2a-java".database.generation=drop-and-create +quarkus.hibernate-orm."a2a-java".packages=io.a2a.extras.taskstore.database.jpa # Configure the outgoing channel (QueueManager -> Kafka) mp.messaging.outgoing.replicated-events-out.connector=smallrye-kafka @@ -27,3 +34,10 @@ mp.messaging.incoming.test-replicated-events-in.group.id=test-consumer-group # Reduce timeouts for faster tests quarkus.messaging.kafka.health.timeout=5s +# Enable debug logging for queue lifecycle and Kafka +quarkus.log.category."io.a2a.server.events".level=DEBUG +quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +quarkus.log.category."io.a2a.client".level=DEBUG +quarkus.log.category."io.smallrye.reactive.messaging".level=DEBUG + diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index 6887a4582..ad7ca99ad 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -22,6 +22,10 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-extras-common + jakarta.annotation jakarta.annotation-api diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 5eecc20b5..c4d814b78 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -1,28 +1,42 @@ package io.a2a.extras.taskstore.database.jpa; +import java.time.Duration; +import java.time.Instant; + import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; import jakarta.enterprise.inject.Alternative; +import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.transaction.Transactional; import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.extras.common.events.TaskFinalizedEvent; +import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.tasks.TaskStore; import io.a2a.spec.Task; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ApplicationScoped @Alternative @Priority(50) -public class JpaDatabaseTaskStore implements TaskStore { - +public class JpaDatabaseTaskStore implements TaskStore, TaskStateProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(JpaDatabaseTaskStore.class); - + @PersistenceContext(unitName = "a2a-java") EntityManager em; + @Inject + Event taskFinalizedEvent; + + @ConfigProperty(name = "a2a.replication.grace-period-seconds", defaultValue = "15") + long gracePeriodSeconds; + @Transactional @Override public void save(Task task) { @@ -31,6 +45,14 @@ public void save(Task task) { JpaTask jpaTask = JpaTask.createFromTask(task); em.merge(jpaTask); LOGGER.debug("Persisted/updated task with ID: {}", task.getId()); + + if (task.getStatus() != null && task.getStatus().state() != null && task.getStatus().state().isFinal()) { + // Fire CDI event if task reached final state + // IMPORTANT: The event will be delivered AFTER transaction commits (AFTER_SUCCESS observers) + // This ensures the task's final state is durably stored before the QueueClosedEvent poison pill is sent + LOGGER.debug("Task {} is in final state, firing TaskFinalizedEvent", task.getId()); + taskFinalizedEvent.fire(new TaskFinalizedEvent(task.getId())); + } } catch (JsonProcessingException e) { LOGGER.error("Failed to serialize task with ID: {}", task.getId(), e); throw new RuntimeException("Failed to serialize task with ID: " + task.getId(), e); @@ -69,4 +91,107 @@ public void delete(String taskId) { LOGGER.debug("Task not found for deletion with ID: {}", taskId); } } + + /** + * Determines if a task is considered active for queue management purposes. + *

+ * A task is active if: + *

    + *
  • Its state is not final, OR
  • + *
  • Its state is final but it was finalized within the grace period
  • + *
+ * The grace period handles the race condition where events are published to Kafka + * while a task is active, but consumed on a replica node after the task is finalized. + *

+ * + * @param taskId the task ID to check + * @return true if the task is active (or recently finalized within grace period), false otherwise + */ + @Transactional + @Override + public boolean isTaskActive(String taskId) { + LOGGER.debug("Checking if task is active: {}", taskId); + + JpaTask jpaTask = em.find(JpaTask.class, taskId); + if (jpaTask == null) { + LOGGER.debug("Task not found, considering inactive: {}", taskId); + return false; + } + + try { + Task task = jpaTask.getTask(); + + // Task is active if not in final state + if (task.getStatus() == null || task.getStatus().state() == null || !task.getStatus().state().isFinal()) { + LOGGER.debug("Task is not in final state, considering active: {}", taskId); + return true; + } + + // Task is in final state - check grace period + Instant finalizedAt = jpaTask.getFinalizedAt(); + if (finalizedAt == null) { + // Should not happen, but defensive: if final state but no timestamp, consider inactive + LOGGER.warn("Task {} is in final state but has no finalizedAt timestamp, considering inactive", taskId); + return false; + } + + Instant gracePeriodEnd = finalizedAt.plus(Duration.ofSeconds(gracePeriodSeconds)); + Instant now = Instant.now(); + + boolean withinGracePeriod = now.isBefore(gracePeriodEnd); + LOGGER.debug("Task {} is final. FinalizedAt: {}, GracePeriodEnd: {}, Now: {}, Active: {}", + taskId, finalizedAt, gracePeriodEnd, now, withinGracePeriod); + + return withinGracePeriod; + + } catch (JsonProcessingException e) { + LOGGER.error("Failed to deserialize task with ID: {}, considering inactive", taskId, e); + return false; + } + } + + /** + * Determines if a task is in a final state, ignoring the grace period. + *

+ * This method performs an immediate check: returns true only if the task + * is in a final state (COMPLETED, CANCELED, FAILED, etc.), regardless of when + * it was finalized. + *

+ *

+ * This method is used by the MainQueue.onClose callback to decide whether + * to publish the QueueClosedEvent "poison pill". By ignoring the grace period, + * it ensures that subscribers are terminated immediately when the task is done, + * providing responsive UX. + *

+ * + * @param taskId the task ID to check + * @return true if the task is in a final state (ignoring grace period), false otherwise + */ + @Transactional + @Override + public boolean isTaskFinalized(String taskId) { + LOGGER.debug("Checking if task is finalized: {}", taskId); + + JpaTask jpaTask = em.find(JpaTask.class, taskId); + if (jpaTask == null) { + LOGGER.debug("Task not found, considering not finalized: {}", taskId); + return false; + } + + try { + Task task = jpaTask.getTask(); + + // Task is finalized if in final state (ignore grace period) + boolean isFinalized = task.getStatus() != null + && task.getStatus().state() != null + && task.getStatus().state().isFinal(); + + LOGGER.debug("Task {} finalization check: {}", taskId, isFinalized); + return isFinalized; + + } catch (JsonProcessingException e) { + LOGGER.error("Failed to deserialize task with ID: {}, considering not finalized", taskId, e); + return false; + } + } } diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java index a3bae673f..2b3fc56f1 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -1,5 +1,7 @@ package io.a2a.extras.taskstore.database.jpa; +import java.time.Instant; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -20,6 +22,9 @@ public class JpaTask { @Column(name = "task_data", columnDefinition = "TEXT", nullable = false) private String taskJson; + @Column(name = "finalized_at") + private Instant finalizedAt; + @Transient private Task task; @@ -48,6 +53,27 @@ public void setTaskJson(String taskJson) { this.taskJson = taskJson; } + public Instant getFinalizedAt() { + return finalizedAt; + } + + /** + * Sets the finalized timestamp for this task. + *

+ * This method is idempotent - it only sets the timestamp on the first transition + * to a final state, as a defense-in-depth measure complementing the existing + * application logic that prevents modifications to final tasks. + *

+ * + * @param finalizedAt the timestamp when the task was finalized + * @param isFinalState whether the current task state is final + */ + public void setFinalizedAt(Instant finalizedAt, boolean isFinalState) { + if (this.finalizedAt == null && isFinalState) { + this.finalizedAt = finalizedAt; + } + } + public Task getTask() throws JsonProcessingException { if (task == null) { this.task = Utils.unmarshalFrom(taskJson, Task.TYPE_REFERENCE); @@ -61,12 +87,26 @@ public void setTask(Task task) throws JsonProcessingException { id = task.getId(); } this.task = task; + updateFinalizedTimestamp(task); } static JpaTask createFromTask(Task task) throws JsonProcessingException { String json = Utils.OBJECT_MAPPER.writeValueAsString(task); JpaTask jpaTask = new JpaTask(task.getId(), json); jpaTask.task = task; + jpaTask.updateFinalizedTimestamp(task); return jpaTask; } + + /** + * Updates the finalizedAt timestamp if the task is in a final state. + * This method is idempotent and only sets the timestamp on first finalization. + * + * @param task the task to check for finalization + */ + private void updateFinalizedTimestamp(Task task) { + if (task.getStatus() != null && task.getStatus().state() != null) { + setFinalizedAt(Instant.now(), task.getStatus().state().isFinal()); + } + } } diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index 4ac03f948..8c091f87f 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -26,6 +26,10 @@ public class JpaDatabaseTaskStoreTest { @Inject TaskStore taskStore; + + @Inject + jakarta.persistence.EntityManager entityManager; + @Test public void testIsJpaDatabaseTaskStore() { assertInstanceOf(JpaDatabaseTaskStore.class, taskStore); @@ -179,4 +183,94 @@ public void testTaskWithComplexMetadata() { assertEquals(42, retrieved.getMetadata().get("key2")); assertEquals(true, retrieved.getMetadata().get("key3")); } + + @Test + @Transactional + public void testIsTaskActiveForNonFinalTask() { + // Create a task in non-final state + Task task = new Task.Builder() + .id("test-task-active-1") + .contextId("test-context") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + taskStore.save(task); + + // Task should be active (not in final state) + JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; + boolean isActive = jpaDatabaseTaskStore.isTaskActive("test-task-active-1"); + + assertEquals(true, isActive, "Non-final task should be active"); + } + + @Test + @Transactional + public void testIsTaskActiveForFinalTaskWithinGracePeriod() { + // Create a task and update it to final state + Task task = new Task.Builder() + .id("test-task-active-2") + .contextId("test-context") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + taskStore.save(task); + + // Update to final state + Task finalTask = new Task.Builder() + .id("test-task-active-2") + .contextId("test-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + taskStore.save(finalTask); + + // Task should be active (within grace period - default 15 seconds) + JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; + boolean isActive = jpaDatabaseTaskStore.isTaskActive("test-task-active-2"); + + assertEquals(true, isActive, "Final task within grace period should be active"); + } + + @Test + @Transactional + public void testIsTaskActiveForFinalTaskBeyondGracePeriod() { + // Create and save a task in final state + Task task = new Task.Builder() + .id("test-task-active-3") + .contextId("test-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + taskStore.save(task); + + // Directly update the finalizedAt timestamp to 20 seconds in the past + // (beyond the default 15-second grace period) + JpaTask jpaTask = entityManager.find(JpaTask.class, "test-task-active-3"); + assertNotNull(jpaTask); + + // Manually set finalizedAt to 20 seconds in the past + java.time.Instant pastTime = java.time.Instant.now().minusSeconds(20); + entityManager.createQuery("UPDATE JpaTask j SET j.finalizedAt = :finalizedAt WHERE j.id = :id") + .setParameter("finalizedAt", pastTime) + .setParameter("id", "test-task-active-3") + .executeUpdate(); + + entityManager.flush(); + entityManager.clear(); // Clear persistence context to force fresh read + + // Task should be inactive (beyond grace period) + JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; + boolean isActive = jpaDatabaseTaskStore.isTaskActive("test-task-active-3"); + + assertEquals(false, isActive, "Final task beyond grace period should be inactive"); + } + + @Test + @Transactional + public void testIsTaskActiveForNonExistentTask() { + JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; + boolean isActive = jpaDatabaseTaskStore.isTaskActive("non-existent-task"); + + assertEquals(false, isActive, "Non-existent task should be inactive"); + } } diff --git a/pom.xml b/pom.xml index 913b0c8cd..05301c45b 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,11 @@ a2a-java-sdk-server-common ${project.version} + + ${project.groupId} + a2a-java-extras-common + ${project.version} + ${project.groupId} a2a-java-sdk-transport-grpc @@ -434,6 +439,7 @@ client/transport/spi common examples/helloworld + extras/common extras/task-store-database-jpa extras/push-notification-config-store-database-jpa extras/queue-manager-replicated diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index 0be198cbf..ccf9f0ce9 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -28,14 +28,14 @@ public EventConsumer(EventQueue queue) { } public Event consumeOne() throws A2AServerException, EventQueueClosedException { - Event event = queue.dequeueEvent(NO_WAIT); - if (event == null) { + EventQueueItem item = queue.dequeueEventItem(NO_WAIT); + if (item == null) { throw new A2AServerException(ERROR_MSG, new InternalError(ERROR_MSG)); } - return event; + return item.getEvent(); } - public Flow.Publisher consumeAll() { + public Flow.Publisher consumeAll() { TubeConfiguration conf = new TubeConfiguration() .withBackpressureStrategy(BackpressureStrategy.BUFFER) .withBufferSize(256); @@ -55,17 +55,49 @@ public Flow.Publisher consumeAll() { // enqueued by the agent and the agent simply threw an exception // TODO the callback mentioned above seems unused in the Python 0.2.1 tag + EventQueueItem item; Event event; try { - event = queue.dequeueEvent(QUEUE_WAIT_MILLISECONDS); - if (event == null) { + item = queue.dequeueEventItem(QUEUE_WAIT_MILLISECONDS); + if (item == null) { continue; } + event = item.getEvent(); + if (event instanceof Throwable thr) { tube.fail(thr); return; } - tube.send(event); + + // Check for QueueClosedEvent BEFORE sending to avoid delivering it to subscribers + boolean isFinalEvent = false; + if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) { + isFinalEvent = true; + } else if (event instanceof Message) { + isFinalEvent = true; + } else if (event instanceof Task task) { + isFinalEvent = task.getStatus().state().isFinal(); + } else if (event instanceof QueueClosedEvent) { + // Poison pill event - signals queue closure from remote node + // Do NOT send to subscribers - just close the queue + LOGGER.debug("Received QueueClosedEvent for task {}, treating as final event", + ((QueueClosedEvent) event).getTaskId()); + isFinalEvent = true; + } + + // Only send event if it's not a QueueClosedEvent + // QueueClosedEvent is an internal coordination event used for replication + // and should not be exposed to API consumers + if (!(event instanceof QueueClosedEvent)) { + tube.send(item); + } + + if (isFinalEvent) { + LOGGER.debug("Final event detected, closing queue and breaking loop for queue {}", System.identityHashCode(queue)); + queue.close(); + LOGGER.debug("Queue closed, breaking loop for queue {}", System.identityHashCode(queue)); + break; + } } catch (EventQueueClosedException e) { completed = true; tube.complete(); @@ -74,31 +106,14 @@ public Flow.Publisher consumeAll() { tube.fail(t); return; } - - boolean isFinalEvent = false; - if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) { - isFinalEvent = true; - } else if (event instanceof Message) { - isFinalEvent = true; - } else if (event instanceof Task task) { - switch (task.getStatus().state()) { - case COMPLETED: - case CANCELED: - case FAILED: - case REJECTED: - case UNKNOWN: - isFinalEvent = true; - } - } - - if (isFinalEvent) { - queue.close(); - break; - } } } finally { if (!completed) { + LOGGER.debug("EventConsumer finally block: calling tube.complete() for queue {}", System.identityHashCode(queue)); tube.complete(); + LOGGER.debug("EventConsumer finally block: tube.complete() returned for queue {}", System.identityHashCode(queue)); + } else { + LOGGER.debug("EventConsumer finally block: completed=true, skipping tube.complete() for queue {}", System.identityHashCode(queue)); } } }); diff --git a/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java b/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java index 81f860ce7..7bc19bf71 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java +++ b/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java @@ -1,7 +1,5 @@ package io.a2a.server.events; -import io.a2a.spec.Event; - public interface EventEnqueueHook { - void onEnqueue(Event event); + void onEnqueue(EventQueueItem item); } \ No newline at end of file diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 6f848af6a..d590d8890 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import io.a2a.server.tasks.TaskStateProvider; import io.a2a.spec.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,8 +21,8 @@ public abstract class EventQueue implements AutoCloseable { public static final int DEFAULT_QUEUE_SIZE = 1000; private final int queueSize; - private final BlockingQueue queue = new LinkedBlockingDeque<>(); - private final Semaphore semaphore; + protected final BlockingQueue queue = new LinkedBlockingDeque<>(); + protected final Semaphore semaphore; private volatile boolean closed = false; protected EventQueue() { @@ -50,7 +51,8 @@ public static class EventQueueBuilder { private int queueSize = DEFAULT_QUEUE_SIZE; private EventEnqueueHook hook; private String taskId; - private Runnable onCloseCallback; + private List onCloseCallbacks = new java.util.ArrayList<>(); + private TaskStateProvider taskStateProvider; public EventQueueBuilder queueSize(int queueSize) { this.queueSize = queueSize; @@ -67,14 +69,21 @@ public EventQueueBuilder taskId(String taskId) { return this; } - public EventQueueBuilder onClose(Runnable onCloseCallback) { - this.onCloseCallback = onCloseCallback; + public EventQueueBuilder addOnCloseCallback(Runnable onCloseCallback) { + if (onCloseCallback != null) { + this.onCloseCallbacks.add(onCloseCallback); + } + return this; + } + + public EventQueueBuilder taskStateProvider(TaskStateProvider taskStateProvider) { + this.taskStateProvider = taskStateProvider; return this; } public EventQueue build() { - if (hook != null || onCloseCallback != null) { - return new MainQueue(queueSize, hook, taskId, onCloseCallback); + if (hook != null || !onCloseCallbacks.isEmpty() || taskStateProvider != null) { + return new MainQueue(queueSize, hook, taskId, onCloseCallbacks, taskStateProvider); } else { return new MainQueue(queueSize); } @@ -90,6 +99,11 @@ public int getQueueSize() { abstract void signalQueuePollerStarted(); public void enqueueEvent(Event event) { + enqueueItem(new LocalEventQueueItem(event)); + } + + public void enqueueItem(EventQueueItem item) { + Event event = item.getEvent(); if (closed) { LOGGER.warn("Queue is closed. Event will not be enqueued. {} {}", this, event); return; @@ -101,38 +115,51 @@ public void enqueueEvent(Event event) { Thread.currentThread().interrupt(); throw new RuntimeException("Unable to acquire the semaphore to enqueue the event", e); } - queue.add(event); + queue.add(item); LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); } abstract EventQueue tap(); - public Event dequeueEvent(int waitMilliSeconds) throws EventQueueClosedException { + /** + * Dequeues an EventQueueItem from the queue. + *

+ * This method returns the full EventQueueItem wrapper, allowing callers to check + * metadata like whether the event is replicated via {@link EventQueueItem#isReplicated()}. + *

+ * + * @param waitMilliSeconds the maximum time to wait in milliseconds + * @return the EventQueueItem, or null if timeout occurs + * @throws EventQueueClosedException if the queue is closed and empty + */ + public EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { if (closed && queue.isEmpty()) { LOGGER.debug("Queue is closed, and empty. Sending termination message. {}", this); throw new EventQueueClosedException(); } try { if (waitMilliSeconds <= 0) { - Event event = queue.poll(); - if (event != null) { + EventQueueItem item = queue.poll(); + if (item != null) { + Event event = item.getEvent(); // Call toString() since for errors we don't really want the full stacktrace - LOGGER.debug("Dequeued event (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); + LOGGER.debug("Dequeued event item (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); semaphore.release(); } - return event; + return item; } try { LOGGER.trace("Polling queue {} (wait={}ms)", System.identityHashCode(this), waitMilliSeconds); - Event event = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); - if (event != null) { + EventQueueItem item = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); + if (item != null) { + Event event = item.getEvent(); // Call toString() since for errors we don't really want the full stacktrace - LOGGER.debug("Dequeued event (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); + LOGGER.debug("Dequeued event item (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); semaphore.release(); } else { LOGGER.trace("Dequeue timeout (null) from queue {}", System.identityHashCode(this)); } - return event; + return item; } catch (InterruptedException e) { LOGGER.debug("Interrupted dequeue (waiting) {}", this); Thread.currentThread().interrupt(); @@ -193,42 +220,49 @@ static class MainQueue extends EventQueue { private final AtomicBoolean pollingStarted = new AtomicBoolean(false); private final EventEnqueueHook enqueueHook; private final String taskId; - private final Runnable onCloseCallback; + private final List onCloseCallbacks; + private final TaskStateProvider taskStateProvider; MainQueue() { super(); this.enqueueHook = null; this.taskId = null; - this.onCloseCallback = null; + this.onCloseCallbacks = List.of(); + this.taskStateProvider = null; } MainQueue(int queueSize) { super(queueSize); this.enqueueHook = null; this.taskId = null; - this.onCloseCallback = null; + this.onCloseCallbacks = List.of(); + this.taskStateProvider = null; } MainQueue(EventEnqueueHook hook) { super(); this.enqueueHook = hook; this.taskId = null; - this.onCloseCallback = null; + this.onCloseCallbacks = List.of(); + this.taskStateProvider = null; } MainQueue(int queueSize, EventEnqueueHook hook) { super(queueSize); this.enqueueHook = hook; this.taskId = null; - this.onCloseCallback = null; + this.onCloseCallbacks = List.of(); + this.taskStateProvider = null; } - MainQueue(int queueSize, EventEnqueueHook hook, String taskId, Runnable onCloseCallback) { + MainQueue(int queueSize, EventEnqueueHook hook, String taskId, List onCloseCallbacks, TaskStateProvider taskStateProvider) { super(queueSize); this.enqueueHook = hook; this.taskId = taskId; - this.onCloseCallback = onCloseCallback; - LOGGER.debug("Created MainQueue for task {} with onClose callback: {}", taskId, onCloseCallback != null); + this.onCloseCallbacks = List.copyOf(onCloseCallbacks); // Defensive copy + this.taskStateProvider = taskStateProvider; + LOGGER.debug("Created MainQueue for task {} with {} onClose callbacks and TaskStateProvider: {}", + taskId, onCloseCallbacks.size(), taskStateProvider != null); } EventQueue tap() { @@ -237,11 +271,34 @@ EventQueue tap() { return child; } - public void enqueueEvent(Event event) { - super.enqueueEvent(event); - children.forEach(eq -> eq.internalEnqueueEvent(event)); + @Override + public void enqueueItem(EventQueueItem item) { + // MainQueue must accept events even when closed to support: + // 1. Late-arriving replicated events for non-finalized tasks + // 2. Events enqueued during onClose callbacks (before super.doClose()) + // 3. QueueClosedEvent termination for remote subscribers + // + // We bypass the parent's closed check and enqueue directly + Event event = item.getEvent(); + + // Acquire semaphore for backpressure + try { + semaphore.acquire(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unable to acquire the semaphore to enqueue the event", e); + } + + // Add to this MainQueue's internal queue + queue.add(item); + LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); + + // Distribute to all ChildQueues (they will receive the event even if MainQueue is closed) + children.forEach(eq -> eq.internalEnqueueItem(item)); + + // Trigger replication hook if configured if (enqueueHook != null) { - enqueueHook.onEnqueue(event); + enqueueHook.onEnqueue(item); } } @@ -265,13 +322,32 @@ void signalQueuePollerStarted() { void childClosing(ChildQueue child, boolean immediate) { children.remove(child); // Remove the closing child - // Only close MainQueue if immediate OR no children left - if (immediate || children.isEmpty()) { - LOGGER.debug("MainQueue closing: immediate={}, children.isEmpty()={}", immediate, children.isEmpty()); + // Close immediately if requested + if (immediate) { + LOGGER.debug("MainQueue closing immediately (immediate=true)"); this.doClose(immediate); - } else { + return; + } + + // If there are still children, keep queue open + if (!children.isEmpty()) { LOGGER.debug("MainQueue staying open: {} children remaining", children.size()); + return; } + + // No children left - check if task is finalized before auto-closing + if (taskStateProvider != null && taskId != null) { + boolean isFinalized = taskStateProvider.isTaskFinalized(taskId); + if (!isFinalized) { + LOGGER.debug("MainQueue for task {} has no children, but task is not finalized - keeping queue open for potential resubscriptions", taskId); + return; // Don't close - keep queue open for fire-and-forget or late resubscribes + } + LOGGER.debug("MainQueue for task {} has no children and task is finalized - closing queue", taskId); + } else { + LOGGER.debug("MainQueue has no children and no TaskStateProvider - closing queue (legacy behavior)"); + } + + this.doClose(immediate); } /** @@ -286,16 +362,19 @@ public int getActiveChildCount() { @Override protected void doClose(boolean immediate) { - super.doClose(immediate); - // Invoke callback after closing to notify QueueManager - if (onCloseCallback != null) { - LOGGER.debug("Invoking onClose callback for task {}", taskId); - try { - onCloseCallback.run(); - } catch (Exception e) { - LOGGER.error("Error in onClose callback for task {}", taskId, e); + // Invoke all callbacks BEFORE closing, so they can still enqueue events + if (!onCloseCallbacks.isEmpty()) { + LOGGER.debug("Invoking {} onClose callbacks for task {} BEFORE closing", onCloseCallbacks.size(), taskId); + for (Runnable callback : onCloseCallbacks) { + try { + callback.run(); + } catch (Exception e) { + LOGGER.error("Error in onClose callback for task {}", taskId, e); + } } } + // Now close the queue + super.doClose(immediate); } @Override @@ -331,8 +410,8 @@ public void enqueueEvent(Event event) { parent.enqueueEvent(event); } - private void internalEnqueueEvent(Event event) { - super.enqueueEvent(event); + private void internalEnqueueItem(EventQueueItem item) { + super.enqueueItem(item); } @Override diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueueItem.java b/server-common/src/main/java/io/a2a/server/events/EventQueueItem.java new file mode 100644 index 000000000..e2bfc8f3c --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/EventQueueItem.java @@ -0,0 +1,37 @@ +package io.a2a.server.events; + +import io.a2a.spec.Event; + +/** + * Represents an item that can be enqueued/dequeued in an EventQueue. + *

+ * This abstraction allows the queue to track whether events originated locally + * or were received from replication, enabling proper handling of the replication loop + * prevention mechanism. + *

+ */ +public interface EventQueueItem { + + /** + * Gets the event contained in this queue item. + * + * @return the event + */ + Event getEvent(); + + /** + * Checks if this item represents a replicated event. + *

+ * Replicated events are those received from other nodes in the cluster via + * the replication mechanism (e.g., Kafka). Local events are those enqueued + * directly by the local agent execution. + *

+ *

+ * This distinction is important to prevent replication loops - replicated events + * should not be re-replicated to other nodes. + *

+ * + * @return true if this event was replicated from another node, false if it's a local event + */ + boolean isReplicated(); +} diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index d096527d4..1383d058e 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -4,6 +4,9 @@ import java.util.concurrent.ConcurrentMap; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import io.a2a.server.tasks.TaskStateProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,13 +16,18 @@ public class InMemoryQueueManager implements QueueManager { private final ConcurrentMap queues = new ConcurrentHashMap<>(); private final EventQueueFactory factory; + private final TaskStateProvider taskStateProvider; - public InMemoryQueueManager() { + @Inject + public InMemoryQueueManager(TaskStateProvider taskStateProvider) { this.factory = new DefaultEventQueueFactory(); + this.taskStateProvider = taskStateProvider; } - public InMemoryQueueManager(EventQueueFactory factory) { + // For testing with custom factory + public InMemoryQueueManager(EventQueueFactory factory, TaskStateProvider taskStateProvider) { this.factory = factory; + this.taskStateProvider = taskStateProvider; } @Override @@ -54,14 +62,24 @@ public void close(String taskId) { @Override public EventQueue createOrTap(String taskId) { - LOGGER.info("createOrTap called for task {}, current map size: {}", taskId, queues.size()); + LOGGER.debug("createOrTap called for task {}, current map size: {}", taskId, queues.size()); EventQueue existing = queues.get(taskId); - // Lazy cleanup: remove closed queues from map + // Lazy cleanup: only remove closed queues if task is finalized + // Don't remove queues for non-finalized tasks - they must stay for late-arriving events if (existing != null && existing.isClosed()) { - LOGGER.debug("Removing closed queue {} for task {}", System.identityHashCode(existing), taskId); - queues.remove(taskId); - existing = null; + boolean isFinalized = (taskStateProvider != null) && taskStateProvider.isTaskFinalized(taskId); + if (isFinalized) { + LOGGER.debug("Removing closed queue {} for finalized task {}", System.identityHashCode(existing), taskId); + queues.remove(taskId); + existing = null; + } else { + LOGGER.debug("Queue {} for task {} is closed but task not finalized - keeping for late-arriving events", + System.identityHashCode(existing), taskId); + // Don't remove or recreate - existing closed queue stays in map + // This is critical for replication where events may arrive after MainQueue closes + // The closed MainQueue can still receive enqueued events via enqueueEvent() + } } EventQueue newQueue = null; @@ -77,7 +95,7 @@ public EventQueue createOrTap(String taskId) { EventQueue result = main.tap(); // Always return ChildQueue if (existing == null) { - LOGGER.info("Created new MainQueue {} for task {}, returning ChildQueue {} (map size: {})", + LOGGER.debug("Created new MainQueue {} for task {}, returning ChildQueue {} (map size: {})", System.identityHashCode(main), taskId, System.identityHashCode(result), queues.size()); } else { LOGGER.debug("Tapped existing MainQueue {} -> ChildQueue {} for task {}", @@ -105,22 +123,50 @@ public int getActiveChildQueueCount(String taskId) { return -1; } + /** + * Get the cleanup callback that removes a queue from the map when it closes. + * This is exposed so that subclasses (like ReplicatedQueueManager) can reuse + * this cleanup logic while adding their own callbacks in the correct order. + *

+ * The cleanup callback checks if the task is finalized before removing the queue. + * If the task is not finalized, the queue remains in the map to handle late-arriving events. + *

+ * + * @param taskId the task ID for the queue + * @return a Runnable that removes the queue from the map if appropriate + */ + public Runnable getCleanupCallback(String taskId) { + return () -> { + LOGGER.debug("Queue close callback invoked for task {}", taskId); + + // Check if task is finalized before removing queue + boolean isFinalized = (taskStateProvider != null) && taskStateProvider.isTaskFinalized(taskId); + + if (!isFinalized) { + LOGGER.debug("Task {} is not finalized, keeping queue in map for late-arriving events", taskId); + return; // Don't remove from map - task is still active + } + + LOGGER.debug("Task {} is finalized, removing queue from map", taskId); + + EventQueue removed = queues.remove(taskId); + if (removed != null) { + LOGGER.debug("Removed closed queue for task {} from QueueManager (map size: {})", + taskId, queues.size()); + } else { + LOGGER.debug("Queue for task {} was already removed from map", taskId); + } + }; + } + private class DefaultEventQueueFactory implements EventQueueFactory { @Override public EventQueue.EventQueueBuilder builder(String taskId) { // Return builder with callback that removes queue from map when closed return EventQueue.builder() .taskId(taskId) - .onClose(() -> { - LOGGER.debug("Queue close callback invoked for task {}, removing from map", taskId); - EventQueue removed = queues.remove(taskId); - if (removed != null) { - LOGGER.info("Removed closed queue for task {} from QueueManager (map size: {})", - taskId, queues.size()); - } else { - LOGGER.debug("Queue for task {} was already removed from map", taskId); - } - }); + .addOnCloseCallback(getCleanupCallback(taskId)) + .taskStateProvider(taskStateProvider); } } } diff --git a/server-common/src/main/java/io/a2a/server/events/LocalEventQueueItem.java b/server-common/src/main/java/io/a2a/server/events/LocalEventQueueItem.java new file mode 100644 index 000000000..88c32024d --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/LocalEventQueueItem.java @@ -0,0 +1,31 @@ +package io.a2a.server.events; + +import io.a2a.spec.Event; +import io.a2a.util.Assert; + +/** + * Represents a locally-generated event in the queue. + *

+ * Local events are those enqueued directly by the agent executor on this node, + * as opposed to events received via replication from other nodes. + *

+ */ +class LocalEventQueueItem implements EventQueueItem { + + private final Event event; + + LocalEventQueueItem(Event event) { + Assert.checkNotNullParam("event", event); + this.event = event; + } + + @Override + public Event getEvent() { + return event; + } + + @Override + public boolean isReplicated() { + return false; + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/QueueClosedEvent.java b/server-common/src/main/java/io/a2a/server/events/QueueClosedEvent.java new file mode 100644 index 000000000..b18c1a47a --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/QueueClosedEvent.java @@ -0,0 +1,39 @@ +package io.a2a.server.events; + +import io.a2a.spec.Event; + +/** + * Poison pill event used to signal that a queue has been closed. + *

+ * When a MainQueue is closed on one node, this event is published to Kafka + * to notify all other nodes consuming replicated events that they should + * gracefully terminate their event streams for this task. + *

+ *

+ * This event implements the "poison pill" pattern - when EventConsumer + * encounters this event, it throws EventQueueClosedException to terminate + * the consumption loop and close the stream. + *

+ *

+ * Note: This is an internal event that is never sent to end clients. + * The EventConsumer intercepts this event and terminates the stream + * before it reaches the client. + *

+ */ +public class QueueClosedEvent implements Event { + + private final String taskId; + + public QueueClosedEvent(String taskId) { + this.taskId = taskId; + } + + public String getTaskId() { + return taskId; + } + + @Override + public String toString() { + return "QueueClosedEvent{taskId='" + taskId + "'}"; + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/QueueManager.java b/server-common/src/main/java/io/a2a/server/events/QueueManager.java index 331afef09..b0eb517ae 100644 --- a/server-common/src/main/java/io/a2a/server/events/QueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/QueueManager.java @@ -1,6 +1,7 @@ package io.a2a.server.events; public interface QueueManager { + void add(String taskId, EventQueue queue); EventQueue get(String taskId); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 23d02b5a3..c05dcfd24 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -19,6 +19,7 @@ import java.util.function.Supplier; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import io.a2a.server.ServerCallContext; @@ -28,10 +29,12 @@ import io.a2a.server.events.EnhancedRunnable; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.QueueManager; import io.a2a.server.events.TaskQueueExistsException; import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.server.tasks.PushNotificationSender; +import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; @@ -222,7 +225,8 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId, runningAgents.size()); // Track cleanup as background task to avoid blocking Vert.x threads - trackBackgroundTask(cleanupProducer(agentFuture, taskId, queue, false)); + // Pass the consumption future to ensure cleanup waits for background consumption to complete + trackBackgroundTask(cleanupProducer(agentFuture, etai != null ? etai.consumptionFuture() : null, taskId, queue, false)); } LOGGER.debug("Returning: {}", etai.eventType()); @@ -251,10 +255,12 @@ public Flow.Publisher onMessageSendStream( AtomicBoolean backgroundConsumeStarted = new AtomicBoolean(false); try { - Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); + Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); - Flow.Publisher eventPublisher = - processor(createTubeConfig(), results, ((errorConsumer, event) -> { + // First process the items then convert to Event + Flow.Publisher processed = + processor(createTubeConfig(), results, ((errorConsumer, item) -> { + Event event = item.getEvent(); if (event instanceof Task createdTask) { if (!Objects.equals(taskId.get(), createdTask.getId())) { errorConsumer.accept(new InternalError("Task ID mismatch in agent response")); @@ -288,6 +294,9 @@ public Flow.Publisher onMessageSendStream( return true; })); + // Then convert EventQueueItem -> Event + Flow.Publisher eventPublisher = convertingProcessor(processed, EventQueueItem::getEvent); + Flow.Publisher finalPublisher = convertingProcessor(eventPublisher, event -> (StreamingEventKind) event); // Wrap publisher to detect client disconnect and continue background consumption @@ -374,7 +383,7 @@ private void startBackgroundConsumption() { CompletableFuture agentFuture = runningAgents.remove(taskId.get()); LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId.get(), runningAgents.size()); - trackBackgroundTask(cleanupProducer(agentFuture, taskId.get(), queue, true)); + trackBackgroundTask(cleanupProducer(agentFuture, null, taskId.get(), queue, true)); } } @@ -427,6 +436,7 @@ private PushNotificationConfig getPushNotificationConfig(List onResubscribeToTask( TaskIdParams params, ServerCallContext context) throws JSONRPCError { + LOGGER.debug("onResubscribeToTask - taskId: {}", params.id()); Task task = taskStore.get(params.id()); if (task == null) { throw new TaskNotFoundError(); @@ -435,6 +445,7 @@ public Flow.Publisher onResubscribeToTask( TaskManager taskManager = new TaskManager(task.getId(), task.getContextId(), taskStore, null); ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); EventQueue queue = queueManager.tap(task.getId()); + LOGGER.debug("onResubscribeToTask - tapped queue: {}", queue != null ? System.identityHashCode(queue) : "null"); if (queue == null) { // If task is in final state, queue legitimately doesn't exist anymore @@ -448,8 +459,9 @@ public Flow.Publisher onResubscribeToTask( } EventConsumer consumer = new EventConsumer(queue); - Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); - return convertingProcessor(results, e -> (StreamingEventKind) e); + Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); + LOGGER.debug("onResubscribeToTask - returning publisher for taskId: {}", params.id()); + return convertingProcessor(results, item -> (StreamingEventKind) item.getEvent()); } @Override @@ -564,40 +576,59 @@ private void trackBackgroundTask(CompletableFuture task) { }); } - private CompletableFuture cleanupProducer(CompletableFuture agentFuture, String taskId, EventQueue queue, boolean isStreaming) { + /** + * Wait for all background tasks to complete. + * Useful for testing to ensure cleanup completes before assertions. + * + * @return CompletableFuture that completes when all background tasks finish + */ + public CompletableFuture waitForBackgroundTasks() { + CompletableFuture[] tasks = backgroundTasks.toArray(new CompletableFuture[0]); + if (tasks.length == 0) { + return CompletableFuture.completedFuture(null); + } + LOGGER.debug("Waiting for {} background tasks to complete", tasks.length); + return CompletableFuture.allOf(tasks); + } + + private CompletableFuture cleanupProducer(CompletableFuture agentFuture, CompletableFuture consumptionFuture, String taskId, EventQueue queue, boolean isStreaming) { LOGGER.debug("Starting cleanup for task {} (streaming={})", taskId, isStreaming); logThreadStats("CLEANUP START"); if (agentFuture == null) { LOGGER.debug("No running agent found for task {}, cleanup complete", taskId); - return CompletableFuture.completedFuture(null); // Return completed future + return CompletableFuture.completedFuture(null); + } + + // Wait for BOTH agent AND consumption to complete before cleanup + // This ensures TaskStore is fully updated before we check task finalization + CompletableFuture bothComplete = agentFuture; + if (consumptionFuture != null) { + bothComplete = CompletableFuture.allOf(agentFuture, consumptionFuture); + LOGGER.debug("Cleanup will wait for both agent and consumption to complete for task {}", taskId); } - return agentFuture.whenComplete((v, t) -> { + return bothComplete.whenComplete((v, t) -> { if (t != null) { - LOGGER.debug("Agent completed with error for task {}", taskId, t); + LOGGER.debug("Agent/consumption completed with error for task {}", taskId, t); } else { - LOGGER.debug("Agent completed successfully for task {}", taskId); + LOGGER.debug("Agent and consumption both completed successfully for task {}", taskId); } - // Determine if we should keep the MainQueue alive - // For non-streaming, non-blocking requests with non-final tasks, we close the ChildQueue - // but keep the MainQueue alive to support resubscription - boolean keepMainQueueAlive = false; - if (!isStreaming) { - Task task = taskStore.get(taskId); - if (task != null && !task.getStatus().state().isFinal()) { - keepMainQueueAlive = true; - LOGGER.debug("Non-streaming call with non-final task {} (state={}), closing ChildQueue but keeping MainQueue alive for resubscription", - taskId, task.getStatus().state()); - } - } + // Always close the ChildQueue and notify the parent MainQueue + // The parent will close itself when all children are closed (childClosing logic) + // This ensures proper cleanup and removal from QueueManager map + LOGGER.debug("{} call, closing ChildQueue for task {} (immediate=false, notifyParent=true)", + isStreaming ? "Streaming" : "Non-streaming", taskId); - LOGGER.debug("{} call, closing queue for task {} (immediate=false, notifyParent={})", - isStreaming ? "Streaming" : "Non-streaming", taskId, !keepMainQueueAlive); + // Always notify parent so MainQueue can clean up when last child closes + queue.close(false, true); - // Close the ChildQueue, optionally keeping MainQueue alive - queue.close(false, !keepMainQueueAlive); + // For replicated environments, the poison pill is now sent via CDI events + // When JpaDatabaseTaskStore.save() persists a final task, it fires TaskFinalizedEvent + // ReplicatedQueueManager.onTaskFinalized() observes AFTER_SUCCESS and sends poison pill + // This guarantees the transaction is committed before the poison pill is sent + LOGGER.debug("Queue cleanup completed for task {}", taskId); logThreadStats("CLEANUP END"); }); diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 78d0567fb..0320f8422 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -8,7 +8,7 @@ import io.a2a.spec.Task; @ApplicationScoped -public class InMemoryTaskStore implements TaskStore { +public class InMemoryTaskStore implements TaskStore, TaskStateProvider { private final ConcurrentMap tasks = new ConcurrentHashMap<>(); @@ -26,4 +26,26 @@ public Task get(String taskId) { public void delete(String taskId) { tasks.remove(taskId); } + + @Override + public boolean isTaskActive(String taskId) { + Task task = tasks.get(taskId); + if (task == null) { + return false; + } + // Task is active if not in final state + return task.getStatus() == null || task.getStatus().state() == null || !task.getStatus().state().isFinal(); + } + + @Override + public boolean isTaskFinalized(String taskId) { + Task task = tasks.get(taskId); + if (task == null) { + return false; + } + // Task is finalized if in final state (ignores grace period) + return task.getStatus() != null + && task.getStatus().state() != null + && task.getStatus().state().isFinal(); + } } diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index b19427ab0..f73242491 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -11,7 +11,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.a2a.server.events.EventConsumer; +import io.a2a.server.events.EventQueueItem; import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.EventKind; @@ -23,6 +27,8 @@ import io.a2a.util.Utils; public class ResultAggregator { + private static final Logger LOGGER = LoggerFactory.getLogger(ResultAggregator.class); + private final TaskManager taskManager; private final Executor executor; private volatile Message message; @@ -40,28 +46,34 @@ public EventKind getCurrentResult() { return taskManager.getTask(); } - public Flow.Publisher consumeAndEmit(EventConsumer consumer) { - Flow.Publisher all = consumer.consumeAll(); + public Flow.Publisher consumeAndEmit(EventConsumer consumer) { + Flow.Publisher allItems = consumer.consumeAll(); - return processor(createTubeConfig(), all, ((errorConsumer, event) -> { - try { - callTaskManagerProcess(event); - } catch (A2AServerException e) { - errorConsumer.accept(e); - return false; + // Process items conditionally - only save non-replicated events to database + return processor(createTubeConfig(), allItems, (errorConsumer, item) -> { + // Only process non-replicated events to avoid duplicate database writes + if (!item.isReplicated()) { + try { + callTaskManagerProcess(item.getEvent()); + } catch (A2AServerException e) { + errorConsumer.accept(e); + return false; + } } + // Continue processing and emit (both replicated and non-replicated) return true; - })); + }); } public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { AtomicReference returnedEvent = new AtomicReference<>(); - Flow.Publisher all = consumer.consumeAll(); + Flow.Publisher allItems = consumer.consumeAll(); AtomicReference error = new AtomicReference<>(); consumer( createTubeConfig(), - all, - (event) -> { + allItems, + (item) -> { + Event event = item.getEvent(); if (event instanceof Message msg) { message = msg; if (returnedEvent.get() == null) { @@ -69,11 +81,14 @@ public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { return false; } } - try { - callTaskManagerProcess(event); - } catch (A2AServerException e) { - error.set(e); - return false; + // Only process non-replicated events to avoid duplicate database writes + if (!item.isReplicated()) { + try { + callTaskManagerProcess(event); + } catch (A2AServerException e) { + error.set(e); + return false; + } } return true; }, @@ -95,23 +110,26 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, } public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking, Runnable eventCallback) throws JSONRPCError { - Flow.Publisher all = consumer.consumeAll(); + Flow.Publisher allItems = consumer.consumeAll(); AtomicReference message = new AtomicReference<>(); AtomicBoolean interrupted = new AtomicBoolean(false); - AtomicBoolean shouldCloseConsumer = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); CompletableFuture completionFuture = new CompletableFuture<>(); + // Separate future for tracking background consumption completion + CompletableFuture consumptionCompletionFuture = new CompletableFuture<>(); // CRITICAL: The subscription itself must run on a background thread to avoid blocking // the Vert.x worker thread. EventConsumer.consumeAll() starts a polling loop that - // blocks in dequeueEvent(), so we must subscribe from a background thread. + // blocks in dequeueEventItem(), so we must subscribe from a background thread. // Use the @Internal executor (not ForkJoinPool.commonPool) to avoid saturation // during concurrent request bursts. CompletableFuture.runAsync(() -> { consumer( createTubeConfig(), - all, - (event) -> { + allItems, + (item) -> { + Event event = item.getEvent(); + // Handle Throwable events if (event instanceof Throwable t) { errorRef.set(t); @@ -127,18 +145,22 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, return false; } - // Process event through TaskManager - try { - callTaskManagerProcess(event); - } catch (A2AServerException e) { - errorRef.set(e); - completionFuture.completeExceptionally(e); - return false; + // Process event through TaskManager - only for non-replicated events + if (!item.isReplicated()) { + try { + callTaskManagerProcess(event); + } catch (A2AServerException e) { + errorRef.set(e); + completionFuture.completeExceptionally(e); + return false; + } } // Determine interrupt behavior boolean shouldInterrupt = false; boolean continueInBackground = false; + boolean isFinalEvent = (event instanceof Task task && task.getStatus().state().isFinal()) + || (event instanceof TaskStatusUpdateEvent tsue && tsue.isFinal()); boolean isAuthRequired = (event instanceof Task task && task.getStatus().state() == TaskState.AUTH_REQUIRED) || (event instanceof TaskStatusUpdateEvent tsue && tsue.getStatus().state() == TaskState.AUTH_REQUIRED); @@ -159,10 +181,16 @@ else if (!blocking) { continueInBackground = true; } else { - // For blocking calls, interrupt when we get any task-related event (EventKind except Message) - // Cancel subscription to free resources (client can resubscribe if needed) + // For ALL blocking calls (both final and non-final events), use background consumption + // This ensures all events are processed and persisted to TaskStore in background + // Queue lifecycle is now managed by DefaultRequestHandler.cleanupProducer() + // which waits for BOTH agent and consumption futures before closing queues shouldInterrupt = true; - continueInBackground = false; + continueInBackground = true; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Blocking call for task {}: {} event, returning with background consumption", + taskIdForLogging(), isFinalEvent ? "final" : "non-final"); + } } if (shouldInterrupt) { @@ -170,15 +198,19 @@ else if (!blocking) { interrupted.set(true); completionFuture.complete(null); - if (continueInBackground) { - // Continue consuming in background - keep requesting events - return true; - } else { - // Blocking call - cancel subscription AND close consumer to stop polling loop - // We need to close the consumer after the consumer() call completes - shouldCloseConsumer.set(true); - return false; + // Signal that cleanup can proceed while consumption continues in background. + // This prevents infinite hangs for fire-and-forget agents that never emit final events. + // Processing continues (return true below) and all events are still persisted to TaskStore. + consumptionCompletionFuture.complete(null); + + // Continue consuming in background - keep requesting events + // Note: continueInBackground is always true when shouldInterrupt is true + // (auth-required, non-blocking, or blocking all set it to true) + if (LOGGER.isDebugEnabled()) { + String reason = isAuthRequired ? "auth-required" : (blocking ? "blocking" : "non-blocking"); + LOGGER.debug("Task {}: Continuing background consumption (reason: {})", taskIdForLogging(), reason); } + return true; } // Continue processing @@ -189,9 +221,11 @@ else if (!blocking) { if (throwable != null) { errorRef.set(throwable); completionFuture.completeExceptionally(throwable); + consumptionCompletionFuture.completeExceptionally(throwable); } else { - // onComplete + // onComplete - subscription finished normally completionFuture.complete(null); + consumptionCompletionFuture.complete(null); } } ); @@ -210,16 +244,9 @@ else if (!blocking) { } } - // Close consumer if blocking interrupt occurred - if (shouldCloseConsumer.get()) { - // Close the EventConsumer's queue to stop the infinite polling loop - // This will cause EventQueueClosedException and exit the while(true) loop - consumer.close(); - } - // Background consumption continues automatically via the subscription - // No need to create a new publisher - returning true in nextFunction - // keeps the subscription alive + // returning true in the consumer function keeps the subscription alive + // Queue lifecycle is managed by DefaultRequestHandler.cleanupProducer() Throwable error = errorRef.get(); if (error != null) { @@ -227,14 +254,21 @@ else if (!blocking) { } return new EventTypeAndInterrupt( - message.get() != null ? message.get() : taskManager.getTask(), interrupted.get()); + message.get() != null ? message.get() : taskManager.getTask(), + interrupted.get(), + consumptionCompletionFuture); } private void callTaskManagerProcess(Event event) throws A2AServerException { taskManager.process(event); } - public record EventTypeAndInterrupt(EventKind eventType, boolean interrupted) { + private String taskIdForLogging() { + Task task = taskManager.getTask(); + return task != null ? task.getId() : "unknown"; + } + + public record EventTypeAndInterrupt(EventKind eventType, boolean interrupted, CompletableFuture consumptionFuture) { } } diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java new file mode 100644 index 000000000..f467ee516 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java @@ -0,0 +1,62 @@ +package io.a2a.server.tasks; + +/** + * Provider interface for determining the active state of a task. + *

+ * This interface decouples the queue lifecycle management from the task state persistence layer. + * Implementations should check the authoritative task state to determine whether a task is + * considered "active" for queue management purposes. + *

+ *

+ * A task is typically considered active if: + *

+ *
    + *
  • Its state is not final (e.g., not COMPLETED, CANCELED, FAILED, etc.), OR
  • + *
  • Its state is final but it was finalized recently (within a configurable grace period)
  • + *
+ *

+ * The grace period mechanism handles race conditions in both replicated and non-replicated environments + * where events may be in-flight when a task finalizes. + *

+ */ +public interface TaskStateProvider { + + /** + * Determines whether a task is considered active for queue management purposes. + *

+ * This method includes the grace period in its check. A task is considered active if: + *

+ *
    + *
  • Its state is not final, OR
  • + *
  • Its state is final but finalized within the grace period (now < finalizedAt + gracePeriod)
  • + *
+ *

+ * This method is used to decide whether to process late-arriving events. + *

+ * + * @param taskId the ID of the task to check + * @return {@code true} if the task is active (or recently finalized within grace period), + * {@code false} otherwise + */ + boolean isTaskActive(String taskId); + + /** + * Determines whether a task is in a final state, ignoring the grace period. + *

+ * This method performs an immediate check: returns {@code true} only if the task + * is in a final state (COMPLETED, CANCELED, FAILED, etc.), regardless of when + * it was finalized. + *

+ *

+ * This method is used by cleanup callbacks and MainQueue closing logic to decide whether + * a queue can be closed and removed. By ignoring the grace period, it ensures responsive + * cleanup while late in-flight events are still handled by the grace period mechanism + * for {@code isTaskActive}. + *

+ * + * @param taskId the ID of the task to check + * @return {@code true} if the task is in a final state (ignoring grace period), + * {@code false} otherwise + */ + boolean isTaskFinalized(String taskId); +} diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index c6a12d737..0cf633fff 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -113,7 +113,7 @@ public void testConsumeAllMultipleEvents() throws JsonProcessingException { eventQueue.enqueueEvent(event); } - Flow.Publisher publisher = eventConsumer.consumeAll(); + Flow.Publisher publisher = eventConsumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference error = new AtomicReference<>(); @@ -127,8 +127,8 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Event item) { - receivedEvents.add(item); + public void onNext(EventQueueItem item) { + receivedEvents.add(item.getEvent()); subscription.request(1); } @@ -174,7 +174,7 @@ public void testConsumeUntilMessage() throws Exception { eventQueue.enqueueEvent(event); } - Flow.Publisher publisher = eventConsumer.consumeAll(); + Flow.Publisher publisher = eventConsumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference error = new AtomicReference<>(); @@ -188,8 +188,8 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Event item) { - receivedEvents.add(item); + public void onNext(EventQueueItem item) { + receivedEvents.add(item.getEvent()); subscription.request(1); } @@ -223,7 +223,7 @@ public void testConsumeMessageEvents() throws Exception { eventQueue.enqueueEvent(event); } - Flow.Publisher publisher = eventConsumer.consumeAll(); + Flow.Publisher publisher = eventConsumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference error = new AtomicReference<>(); @@ -237,8 +237,8 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Event item) { - receivedEvents.add(item); + public void onNext(EventQueueItem item) { + receivedEvents.add(item.getEvent()); subscription.request(1); } @@ -303,7 +303,7 @@ public void testConsumeAllRaisesStoredException() throws InterruptedException { // Set an error in the event consumer setEventConsumerError(new RuntimeException("Stored error")); - Flow.Publisher publisher = eventConsumer.consumeAll(); + Flow.Publisher publisher = eventConsumer.consumeAll(); final AtomicReference receivedError = new AtomicReference<>(); final CountDownLatch errorLatch = new CountDownLatch(1); @@ -316,7 +316,7 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Event item) { + public void onNext(EventQueueItem item) { // Should not be called errorLatch.countDown(); } @@ -349,7 +349,7 @@ public void testConsumeAllStopsOnQueueClosed() throws Exception { // Close the queue immediately queue.close(); - Flow.Publisher publisher = consumer.consumeAll(); + Flow.Publisher publisher = consumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference completed = new AtomicReference<>(false); final CountDownLatch completionLatch = new CountDownLatch(1); @@ -361,8 +361,8 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Event item) { - receivedEvents.add(item); + public void onNext(EventQueueItem item) { + receivedEvents.add(item.getEvent()); } @Override @@ -399,7 +399,7 @@ public void testConsumeAllHandlesQueueClosedException() throws Exception { // Close the queue before consuming queue.close(); - Flow.Publisher publisher = consumer.consumeAll(); + Flow.Publisher publisher = consumer.consumeAll(); final List receivedEvents = new ArrayList<>(); final AtomicReference completed = new AtomicReference<>(false); final CountDownLatch completionLatch = new CountDownLatch(1); @@ -411,8 +411,8 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Event item) { - receivedEvents.add(item); + public void onNext(EventQueueItem item) { + receivedEvents.add(item.getEvent()); } @Override @@ -437,6 +437,56 @@ public void onComplete() { assertSame(message, receivedEvents.get(0)); } + @Test + public void testConsumeAllTerminatesOnQueueClosedEvent() throws Exception { + EventQueue queue = EventQueue.builder().build(); + EventConsumer consumer = new EventConsumer(queue); + + // Enqueue a QueueClosedEvent (poison pill) + QueueClosedEvent queueClosedEvent = new QueueClosedEvent("task-123"); + queue.enqueueEvent(queueClosedEvent); + + Flow.Publisher publisher = consumer.consumeAll(); + final List receivedEvents = new ArrayList<>(); + final AtomicReference completed = new AtomicReference<>(false); + final AtomicReference error = new AtomicReference<>(); + final CountDownLatch completionLatch = new CountDownLatch(1); + + publisher.subscribe(new Flow.Subscriber<>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscription.request(Long.MAX_VALUE); + } + + @Override + public void onNext(EventQueueItem item) { + receivedEvents.add(item.getEvent()); + } + + @Override + public void onError(Throwable throwable) { + error.set(throwable); + completionLatch.countDown(); + } + + @Override + public void onComplete() { + completed.set(true); + completionLatch.countDown(); + } + }); + + // Wait for completion with timeout + assertTrue(completionLatch.await(5, TimeUnit.SECONDS), "Test timed out waiting for completion callback."); + + // Should complete gracefully without error + assertTrue(completed.get(), "Stream should complete normally"); + assertNull(error.get(), "Stream should not error"); + + // The poison pill should not be delivered to subscribers + assertEquals(0, receivedEvents.size(), "QueueClosedEvent should be intercepted, not delivered"); + } + private void enqueueAndConsumeOneEvent(Event event) throws Exception { eventQueue.enqueueEvent(event); Event result = eventConsumer.consumeOne(); diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index ff79d62c2..389c21d88 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -104,8 +104,8 @@ public void testEnqueueEventPropagagesToChildren() throws Exception { parentQueue.enqueueEvent(event); // Event should be available in both parent and child queues - Event parentEvent = parentQueue.dequeueEvent(-1); - Event childEvent = childQueue.dequeueEvent(-1); + Event parentEvent = parentQueue.dequeueEventItem(-1).getEvent(); + Event childEvent = childQueue.dequeueEventItem(-1).getEvent(); assertSame(event, parentEvent); assertSame(event, childEvent); @@ -124,14 +124,14 @@ public void testMultipleChildQueuesReceiveEvents() throws Exception { parentQueue.enqueueEvent(event2); // All queues should receive both events - assertSame(event1, parentQueue.dequeueEvent(-1)); - assertSame(event2, parentQueue.dequeueEvent(-1)); + assertSame(event1, parentQueue.dequeueEventItem(-1).getEvent()); + assertSame(event2, parentQueue.dequeueEventItem(-1).getEvent()); - assertSame(event1, childQueue1.dequeueEvent(-1)); - assertSame(event2, childQueue1.dequeueEvent(-1)); + assertSame(event1, childQueue1.dequeueEventItem(-1).getEvent()); + assertSame(event2, childQueue1.dequeueEventItem(-1).getEvent()); - assertSame(event1, childQueue2.dequeueEvent(-1)); - assertSame(event2, childQueue2.dequeueEvent(-1)); + assertSame(event1, childQueue2.dequeueEventItem(-1).getEvent()); + assertSame(event2, childQueue2.dequeueEventItem(-1).getEvent()); } @Test @@ -144,15 +144,15 @@ public void testChildQueueDequeueIndependently() throws Exception { parentQueue.enqueueEvent(event); // Dequeue from child1 first - Event child1Event = childQueue1.dequeueEvent(-1); + Event child1Event = childQueue1.dequeueEventItem(-1).getEvent(); assertSame(event, child1Event); // child2 should still have the event available - Event child2Event = childQueue2.dequeueEvent(-1); + Event child2Event = childQueue2.dequeueEventItem(-1).getEvent(); assertSame(event, child2Event); // Parent should still have the event available - Event parentEvent = parentQueue.dequeueEvent(-1); + Event parentEvent = parentQueue.dequeueEventItem(-1).getEvent(); assertSame(event, parentEvent); } @@ -168,7 +168,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { assertFalse(childQueue.isClosed()); try { - assertNotNull(childQueue.dequeueEvent(-1)); // Child has the event + assertNotNull(childQueue.dequeueEventItem(-1)); // Child has the event } catch (EventQueueClosedException e) { // This is fine if queue closed before dequeue } @@ -184,7 +184,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { // Child queue should be cleared due to immediate close // Child queue should be cleared and closed, so dequeueing should throw - assertThrows(EventQueueClosedException.class, () -> childQueue.dequeueEvent(-1)); + assertThrows(EventQueueClosedException.class, () -> childQueue.dequeueEventItem(-1)); } @Test @@ -195,12 +195,16 @@ public void testEnqueueEventWhenClosed() throws Exception { queue.close(); // Close the queue first assertTrue(queue.isClosed()); - // Attempt to enqueue should be ignored (no exception thrown) + // MainQueue accepts events even when closed (for replication support) + // This ensures late-arriving replicated events can be enqueued to closed queues queue.enqueueEvent(event); - // Verify the queue is still empty - // Verify the queue is still empty and dequeueing from a closed, empty queue throws - assertThrows(EventQueueClosedException.class, () -> queue.dequeueEvent(-1)); + // Event should be available for dequeuing + Event dequeuedEvent = queue.dequeueEventItem(-1).getEvent(); + assertSame(event, dequeuedEvent); + + // Now queue is closed and empty, should throw exception + assertThrows(EventQueueClosedException.class, () -> queue.dequeueEventItem(-1)); } @Test @@ -210,7 +214,7 @@ public void testDequeueEventWhenClosedAndEmpty() throws Exception { assertTrue(queue.isClosed()); // Dequeue from closed empty queue should throw exception - assertThrows(EventQueueClosedException.class, () -> queue.dequeueEvent(-1)); + assertThrows(EventQueueClosedException.class, () -> queue.dequeueEventItem(-1)); } @Test @@ -223,18 +227,18 @@ public void testDequeueEventWhenClosedButHasEvents() throws Exception { assertTrue(queue.isClosed()); // Should still be able to dequeue existing events - Event dequeuedEvent = queue.dequeueEvent(-1); + Event dequeuedEvent = queue.dequeueEventItem(-1).getEvent(); assertSame(event, dequeuedEvent); // Now queue is closed and empty, should throw exception - assertThrows(EventQueueClosedException.class, () -> queue.dequeueEvent(-1)); + assertThrows(EventQueueClosedException.class, () -> queue.dequeueEventItem(-1)); } @Test public void testEnqueueAndDequeueEvent() throws Exception { Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); eventQueue.enqueueEvent(event); - Event dequeuedEvent = eventQueue.dequeueEvent(200); + Event dequeuedEvent = eventQueue.dequeueEventItem(200).getEvent(); assertSame(event, dequeuedEvent); } @@ -242,14 +246,14 @@ public void testEnqueueAndDequeueEvent() throws Exception { public void testDequeueEventNoWait() throws Exception { Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); eventQueue.enqueueEvent(event); - Event dequeuedEvent = eventQueue.dequeueEvent(-1); + Event dequeuedEvent = eventQueue.dequeueEventItem(-1).getEvent(); assertSame(event, dequeuedEvent); } @Test public void testDequeueEventEmptyQueueNoWait() throws Exception { - Event dequeuedEvent = eventQueue.dequeueEvent(-1); - assertNull(dequeuedEvent); + EventQueueItem item = eventQueue.dequeueEventItem(-1); + assertNull(item); } @Test @@ -262,7 +266,7 @@ public void testDequeueEventWait() throws Exception { .build(); eventQueue.enqueueEvent(event); - Event dequeuedEvent = eventQueue.dequeueEvent(1000); + Event dequeuedEvent = eventQueue.dequeueEventItem(1000).getEvent(); assertSame(event, dequeuedEvent); } @@ -277,7 +281,7 @@ public void testTaskDone() throws Exception { .build()) .build(); eventQueue.enqueueEvent(event); - Event dequeuedEvent = eventQueue.dequeueEvent(1000); + Event dequeuedEvent = eventQueue.dequeueEventItem(1000).getEvent(); assertSame(event, dequeuedEvent); eventQueue.taskDone(); } @@ -290,7 +294,7 @@ public void testEnqueueDifferentEventTypes() throws Exception { for (Event event : events) { eventQueue.enqueueEvent(event); - Event dequeuedEvent = eventQueue.dequeueEvent(100); + Event dequeuedEvent = eventQueue.dequeueEventItem(100).getEvent(); assertSame(event, dequeuedEvent); } } @@ -323,9 +327,9 @@ public void testCloseImmediateClearsQueue() throws Exception { // After immediate close, queue should be cleared // Attempting to dequeue should return null or throw exception try { - Event dequeuedEvent = eventQueue.dequeueEvent(-1); - // If we get here, the event should be null (queue was cleared) - assertNull(dequeuedEvent); + EventQueueItem item = eventQueue.dequeueEventItem(-1); + // If we get here, the item should be null (queue was cleared) + assertNull(item); } catch (EventQueueClosedException e) { // This is also acceptable - queue is closed } diff --git a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java index 9bd418403..1eca1b739 100644 --- a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java @@ -14,16 +14,19 @@ import java.util.concurrent.ExecutionException; import java.util.stream.IntStream; +import io.a2a.server.tasks.MockTaskStateProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class InMemoryQueueManagerTest { private InMemoryQueueManager queueManager; + private MockTaskStateProvider taskStateProvider; @BeforeEach public void setUp() { - queueManager = new InMemoryQueueManager(); + taskStateProvider = new MockTaskStateProvider(); + queueManager = new InMemoryQueueManager(taskStateProvider); } @Test diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 9f12ee792..9447203e1 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -20,6 +20,7 @@ import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.tasks.BasePushNotificationSender; import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; @@ -34,6 +35,7 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; +import io.a2a.spec.Event; import io.a2a.spec.TextPart; import io.a2a.util.Utils; import io.quarkus.arc.profile.IfBuildProfile; @@ -89,8 +91,9 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC } }; - taskStore = new InMemoryTaskStore(); - queueManager = new InMemoryQueueManager(); + InMemoryTaskStore inMemoryTaskStore = new InMemoryTaskStore(); + taskStore = inMemoryTaskStore; + queueManager = new InMemoryQueueManager(inMemoryTaskStore); httpClient = new TestHttpClient(); PushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); PushNotificationSender pushSender = new BasePushNotificationSender(pushConfigStore, httpClient); @@ -146,6 +149,24 @@ protected interface AgentExecutorMethod { void invoke(RequestContext context, EventQueue eventQueue) throws JSONRPCError; } + /** + * Helper method to wrap events in EventQueueItem for tests. + * Creates a simple wrapper that marks events as non-replicated (local events). + */ + protected static EventQueueItem wrapEvent(Event event) { + return new EventQueueItem() { + @Override + public Event getEvent() { + return event; + } + + @Override + public boolean isReplicated() { + return false; + } + }; + } + @Dependent @IfBuildProfile("test") protected static class TestHttpClient implements A2AHttpClient { diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 2fc55ea26..968c7812d 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -18,6 +18,7 @@ import io.a2a.server.tasks.InMemoryTaskStore; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; +import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; import io.a2a.spec.Task; import io.a2a.spec.TaskState; @@ -45,7 +46,8 @@ public class DefaultRequestHandlerTest { @BeforeEach void setUp() { taskStore = new InMemoryTaskStore(); - queueManager = new InMemoryQueueManager(); + // Pass taskStore as TaskStateProvider to queueManager for task-aware queue management + queueManager = new InMemoryQueueManager(taskStore); agentExecutor = new TestAgentExecutor(); requestHandler = new DefaultRequestHandler( @@ -81,8 +83,15 @@ void testBlockingMessageContinueTask() throws Exception { .contextId(context.getContextId()) .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - queue.enqueueEvent(task); + } else { + // Subsequent messages: emit WORKING task (non-final) + task = new Task.Builder() + .id(context.getTaskId()) + .contextId(context.getContextId()) + .status(new TaskStatus(TaskState.WORKING)) + .build(); } + queue.enqueueEvent(task); // Don't complete - just return (fire-and-forget) }); @@ -408,6 +417,243 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception { // background consumption isn't implemented. This test documents the expected behavior. } + /** + * Test that blocking message calls persist all events in background. + * This test proves the bug where blocking calls stop consuming events after + * the first event is returned, causing subsequent events to be lost. + * + * Expected behavior: + * 1. Blocking call returns immediately with first event (WORKING state) + * 2. Agent continues running in background and produces more events + * 3. Background consumption continues and persists all events to TaskStore + * 4. Final task state (COMPLETED) is persisted despite blocking call having returned + * + * This test will FAIL before the fix is applied, demonstrating the bug. + */ + @Test + @Timeout(15) + void testBlockingMessagePersistsAllEventsInBackground() throws Exception { + String taskId = "blocking-persist-task"; + String contextId = "blocking-persist-ctx"; + + Message message = new Message.Builder() + .messageId("msg-blocking-persist") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + // Explicitly set blocking=true (though it's the default) + MessageSendConfiguration config = new MessageSendConfiguration.Builder() + .blocking(true) + .build(); + + MessageSendParams params = new MessageSendParams(message, config, null); + + // Agent that produces multiple events with delays + CountDownLatch agentStarted = new CountDownLatch(1); + CountDownLatch firstEventEmitted = new CountDownLatch(1); + CountDownLatch allowCompletion = new CountDownLatch(1); + + agentExecutor.setExecuteCallback((context, queue) -> { + agentStarted.countDown(); + + // Emit first event (WORKING state) + Task workingTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + queue.enqueueEvent(workingTask); + firstEventEmitted.countDown(); + + // Sleep to ensure the blocking call has returned before we emit more events + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + + // Wait for permission to complete + try { + allowCompletion.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + + // Emit final event (COMPLETED state) + // This event will be LOST with the current bug, because the consumer + // has already stopped after returning the first event + Task completedTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + queue.enqueueEvent(completedTask); + }); + + // Call blocking onMessageSend + Object result = requestHandler.onMessageSend(params, serverCallContext); + + // Assertion 1: The immediate result should be the first event (WORKING) + assertTrue(result instanceof Task, "Result should be a Task"); + Task immediateTask = (Task) result; + assertTrue(immediateTask.getStatus().state() == TaskState.WORKING, + "Immediate return should show WORKING state, got: " + immediateTask.getStatus().state()); + + // At this point, the blocking call has returned, but the agent is still running + + // Allow the agent to emit the final COMPLETED event + allowCompletion.countDown(); + + // Assertion 2: Poll for the final task state to be persisted + // Use polling loop instead of fixed sleep for faster and more reliable test + long timeoutMs = 5000; + long startTime = System.currentTimeMillis(); + Task persistedTask = null; + boolean completedStateFound = false; + + while (System.currentTimeMillis() - startTime < timeoutMs) { + persistedTask = taskStore.get(taskId); + if (persistedTask != null && persistedTask.getStatus().state() == TaskState.COMPLETED) { + completedStateFound = true; + break; + } + Thread.sleep(100); // Poll every 100ms + } + + assertTrue(persistedTask != null, "Task should be persisted to store"); + assertTrue( + completedStateFound, + "Final task state should be COMPLETED (background consumption should have processed it), got: " + + (persistedTask != null ? persistedTask.getStatus().state() : "null") + + " after " + (System.currentTimeMillis() - startTime) + "ms. " + + "This failure proves the bug - events after the first are lost." + ); + } + + /** + * Test the BIG idea: MainQueue stays open for non-final tasks even when all children close. + * This enables fire-and-forget tasks and late resubscriptions. + */ + @Test + @Timeout(15) + void testMainQueueStaysOpenForNonFinalTasks() throws Exception { + String taskId = "fire-and-forget-task"; + String contextId = "fire-ctx"; + + // Create initial task in WORKING state (non-final) + Task initialTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + taskStore.save(initialTask); + + Message message = new Message.Builder() + .messageId("msg-fire") + .role(Message.Role.USER) + .parts(new TextPart("fire and forget")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendParams params = new MessageSendParams(message, null, null); + + // Agent that emits WORKING status but never completes (fire-and-forget pattern) + CountDownLatch agentStarted = new CountDownLatch(1); + CountDownLatch allowAgentFinish = new CountDownLatch(1); + + agentExecutor.setExecuteCallback((context, queue) -> { + agentStarted.countDown(); + + // Emit WORKING status (non-final) + Task workingTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + queue.enqueueEvent(workingTask); + + // Don't emit final state - just wait and finish + try { + allowAgentFinish.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + // Agent finishes WITHOUT emitting final task state + }); + + // Start streaming + var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); + + // Wait for agent to start and emit WORKING event + assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); + + // Give time for WORKING event to be processed + Thread.sleep(500); + + // Simulate client disconnect - this closes the ChildQueue + // but MainQueue should stay open because task is non-final + + // Allow agent to finish + allowAgentFinish.countDown(); + + // Give time for agent to finish and cleanup to run + Thread.sleep(2000); + + // THE BIG IDEA TEST: Resubscription should work because MainQueue is still open + // Even though: + // 1. The original ChildQueue closed (client disconnected) + // 2. The agent finished executing + // 3. Task is still in non-final WORKING state + // Therefore: MainQueue should still be open for resubscriptions + + io.a2a.spec.TaskIdParams resubParams = new io.a2a.spec.TaskIdParams(taskId); + var resubResult = requestHandler.onResubscribeToTask(resubParams, serverCallContext); + + // If we get here without exception, the BIG idea works! + assertTrue(true, "Resubscription succeeded - MainQueue stayed open for non-final task"); + } + + /** + * Test that MainQueue DOES close when task is finalized. + * This ensures Level 2 protection doesn't prevent cleanup of completed tasks. + */ + @Test + @Timeout(15) + void testMainQueueClosesForFinalizedTasks() throws Exception { + String taskId = "completed-task"; + String contextId = "completed-ctx"; + + // Create initial task in COMPLETED state (already finalized) + Task completedTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + taskStore.save(completedTask); + + // Create a queue for this task + EventQueue mainQueue = queueManager.createOrTap(taskId); + assertTrue(mainQueue != null, "Queue should be created"); + + // Close the child queue (simulating client disconnect) + mainQueue.close(); + + // Give time for cleanup callback to run + Thread.sleep(1000); + + // Since the task is finalized (COMPLETED), the MainQueue should be removed from the map + // This tests that Level 2 protection (childClosing check) allows cleanup for finalized tasks + EventQueue queue = queueManager.get(taskId); + assertTrue(queue == null || queue.isClosed(), + "Queue for finalized task should be null or closed"); + } + /** * Simple test agent executor that allows controlling execution timing */ @@ -432,17 +678,18 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP executing = true; try { if (executeCallback != null) { + // Custom callback is responsible for emitting events executeCallback.call(context, eventQueue); + } else { + // No custom callback - emit default completion event + Task completedTask = new Task.Builder() + .id(context.getTaskId()) + .contextId(context.getContextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + eventQueue.enqueueEvent(completedTask); } - // Enqueue a simple task completion event - Task completedTask = new Task.Builder() - .id(context.getTaskId()) - .contextId(context.getContextId()) - .status(new TaskStatus(TaskState.COMPLETED)) - .build(); - eventQueue.enqueueEvent(completedTask); - } finally { executing = false; } diff --git a/server-common/src/test/java/io/a2a/server/tasks/MockTaskStateProvider.java b/server-common/src/test/java/io/a2a/server/tasks/MockTaskStateProvider.java new file mode 100644 index 000000000..c14ad1628 --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/tasks/MockTaskStateProvider.java @@ -0,0 +1,40 @@ +package io.a2a.server.tasks; + +import java.util.HashSet; +import java.util.Set; + +/** + * Mock implementation of TaskStateProvider for testing. + * Allows tests to explicitly control which tasks are considered finalized. + */ +public class MockTaskStateProvider implements TaskStateProvider { + private final Set finalizedTasks = new HashSet<>(); + + /** + * Mark a task as finalized for testing purposes. + * + * @param taskId the task ID to mark as finalized + */ + public void markFinalized(String taskId) { + finalizedTasks.add(taskId); + } + + /** + * Unmark a task as finalized (return to active state). + * + * @param taskId the task ID to unmark + */ + public void unmarkFinalized(String taskId) { + finalizedTasks.remove(taskId); + } + + @Override + public boolean isTaskActive(String taskId) { + return !finalizedTasks.contains(taskId); + } + + @Override + public boolean isTaskFinalized(String taskId) { + return finalizedTasks.contains(taskId); + } +} diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java index a91ad2713..06e12e425 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -203,7 +205,7 @@ void testConsumeAndBreakNonBlocking() throws Exception { // Create an event queue using QueueManager (which has access to builder) InMemoryQueueManager queueManager = - new InMemoryQueueManager(); + new InMemoryQueueManager(new MockTaskStateProvider()); EventQueue queue = queueManager.getEventQueueBuilder("test-task").build(); queue.enqueueEvent(firstEvent); @@ -221,6 +223,10 @@ void testConsumeAndBreakNonBlocking() throws Exception { assertEquals(firstEvent, result.eventType()); assertTrue(result.interrupted()); verify(mockTaskManager).process(firstEvent); - verify(mockTaskManager).getTask(); + // getTask() is called at least once for the return value (line 255) + // May be called once more if debug logging executes in time (line 209) + // The async consumer may or may not execute before verification, so we accept 1-2 calls + verify(mockTaskManager, atLeast(1)).getTask(); + verify(mockTaskManager, atMost(2)).getTask(); } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index c18dc415b..0e99f57b5 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -56,7 +56,7 @@ public void init() { @Test public void testAddArtifactWithCustomIdAndName() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "custom-artifact-id", "Custom Artifact", null); - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -68,7 +68,7 @@ public void testAddArtifactWithCustomIdAndName() throws Exception { assertSame(SAMPLE_PARTS, taue.getArtifact().parts()); - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test @@ -239,7 +239,7 @@ public void testNewAgentMessageWithMetadata() throws Exception { @Test public void testAddArtifactWithAppendTrue() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, true, null); - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -252,13 +252,13 @@ public void testAddArtifactWithAppendTrue() throws Exception { assertEquals(true, taue.isAppend()); assertNull(taue.isLastChunk()); - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test public void testAddArtifactWithLastChunkTrue() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, null, true); - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -267,13 +267,13 @@ public void testAddArtifactWithLastChunkTrue() throws Exception { assertNull(taue.isAppend()); assertEquals(true, taue.isLastChunk()); - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test public void testAddArtifactWithAppendAndLastChunk() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, true, false); - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -281,13 +281,13 @@ public void testAddArtifactWithAppendAndLastChunk() throws Exception { assertEquals(true, taue.isAppend()); assertEquals(false, taue.isLastChunk()); - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test public void testAddArtifactGeneratesIdWhenNull() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, null, "Test Artifact", null); - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -298,7 +298,7 @@ public void testAddArtifactGeneratesIdWhenNull() throws Exception { assertEquals(36, artifactId.length()); // Standard UUID length assertTrue(artifactId.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")); - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test @@ -312,7 +312,7 @@ public void testTerminalStateProtectionAfterComplete() throws Exception { assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); // Verify no additional events were queued - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test @@ -326,7 +326,7 @@ public void testTerminalStateProtectionAfterFail() throws Exception { assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); // Verify no additional events were queued - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test @@ -340,7 +340,7 @@ public void testTerminalStateProtectionAfterReject() throws Exception { assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); // Verify no additional events were queued - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test @@ -354,7 +354,7 @@ public void testTerminalStateProtectionAfterCancel() throws Exception { assertEquals("Cannot update task status - terminal state already reached", exception.getMessage()); // Verify no additional events were queued - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } @Test @@ -383,7 +383,7 @@ public void testConcurrentCompletionAttempts() throws Exception { thread2.join(); // Exactly one event should have been queued - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskStatusUpdateEvent.class, event); @@ -392,11 +392,11 @@ public void testConcurrentCompletionAttempts() throws Exception { assertTrue(tsue.getStatus().state() == TaskState.COMPLETED || tsue.getStatus().state() == TaskState.FAILED); // No additional events should be queued - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); } private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, TaskState state, Message statusMessage) throws Exception { - Event event = eventQueue.dequeueEvent(0); + Event event = eventQueue.dequeueEventItem(0).getEvent(); assertNotNull(event); assertInstanceOf(TaskStatusUpdateEvent.class, event); @@ -408,7 +408,7 @@ private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, assertEquals(state, tsue.getStatus().state()); assertEquals(statusMessage, tsue.getStatus().message()); - assertNull(eventQueue.dequeueEvent(0)); + assertNull(eventQueue.dequeueEventItem(0)); return tsue; } diff --git a/tck/src/main/resources/application.properties b/tck/src/main/resources/application.properties index 6034e67b4..c68793be4 100644 --- a/tck/src/main/resources/application.properties +++ b/tck/src/main/resources/application.properties @@ -8,10 +8,10 @@ a2a.executor.core-pool-size=5 a2a.executor.max-pool-size=15 a2a.executor.keep-alive-seconds=60 -# Enable debug logging for troubleshooting SSE streaming test timeouts -#quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG -#quarkus.log.category."io.a2a.server.events".level=DEBUG -#quarkus.log.category."io.a2a.server.tasks".level=DEBUG +# Enable debug logging for troubleshooting TCK failures +quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.server.events".level=DEBUG +quarkus.log.category."io.a2a.server.tasks".level=DEBUG # Log to file for analysis quarkus.log.file.enable=true diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 9467ab297..7fcf32cb4 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -500,6 +500,10 @@ public void testResubscribeNoExistingTaskError() throws Exception { // Create error handler to capture the TaskNotFoundError Consumer errorHandler = error -> { + if (error == null) { + // Stream completed successfully - ignore, we're waiting for an error + return; + } if (!isStreamClosedError(error)) { errorRef.set(error); } @@ -1673,4 +1677,239 @@ private Client createPollingClient() throws A2AClientException { return clientBuilder.build(); } + /** + * Integration test for THE BIG IDEA: MainQueue stays open for non-final tasks, + * enabling fire-and-forget patterns and late resubscription. + * + * Flow: + * 1. Agent emits WORKING state (non-final) and finishes without completing + * 2. Client disconnects (ChildQueue closes) + * 3. MainQueue should stay OPEN because task is non-final + * 4. Late resubscription should succeed + */ + @Test + @Timeout(value = 2, unit = TimeUnit.MINUTES) + public void testMainQueueStaysOpenForNonFinalTasks() throws Exception { + String taskId = "fire-and-forget-task-integration"; + String contextId = "fire-ctx"; + + // Create task in WORKING state (non-final) + Task workingTask = new Task.Builder() + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); + saveTaskInTaskStore(workingTask); + + try { + // Ensure queue exists for the task + ensureQueueForTask(taskId); + + // Send a message that will leave task in WORKING state (fire-and-forget pattern) + Message message = new Message.Builder(MESSAGE) + .taskId(taskId) + .contextId(contextId) + .parts(new TextPart("fire and forget")) + .build(); + + CountDownLatch firstEventLatch = new CountDownLatch(1); + AtomicReference errorRef = new AtomicReference<>(); + + BiConsumer consumer = (event, agentCard) -> { + // Receive any event (Message) to know agent processed the request + if (event instanceof MessageEvent) { + firstEventLatch.countDown(); + } + }; + + Consumer errorHandler = error -> { + if (!isStreamClosedError(error)) { + errorRef.set(error); + } + firstEventLatch.countDown(); + }; + + // Start streaming subscription + CountDownLatch subscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); + + getClient().sendMessage(message, List.of(consumer), errorHandler); + + // Wait for subscription to be established + assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS), + "Subscription should be established"); + + // Wait for agent to respond (test agent sends Message, not WORKING status) + assertTrue(firstEventLatch.await(15, TimeUnit.SECONDS), + "Should receive agent response"); + assertNull(errorRef.get()); + + // Give agent time to finish (task remains in WORKING state - non-final) + Thread.sleep(2000); + + // THE BIG IDEA TEST: Resubscribe to the task + // Even though the agent finished and original ChildQueue closed, + // MainQueue should still be open because task is in non-final WORKING state + + CountDownLatch resubLatch = new CountDownLatch(1); + AtomicReference resubErrorRef = new AtomicReference<>(); + + BiConsumer resubConsumer = (event, agentCard) -> { + // We might not receive events immediately, but subscription should succeed + resubLatch.countDown(); + }; + + Consumer resubErrorHandler = error -> { + if (!isStreamClosedError(error)) { + resubErrorRef.set(error); + } + resubLatch.countDown(); + }; + + // This should succeed - MainQueue is still open for non-final task + CountDownLatch resubSubscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> resubSubscriptionLatch.countDown()); + + getClient().resubscribe(new TaskIdParams(taskId), + List.of(resubConsumer), + resubErrorHandler); + + // Wait for resubscription to be established + assertTrue(resubSubscriptionLatch.await(15, TimeUnit.SECONDS), + "Resubscription should succeed - MainQueue stayed open for non-final task"); + + // Verify no errors during resubscription + assertNull(resubErrorRef.get(), + "Resubscription should not error - validates THE BIG IDEA works end-to-end"); + + } finally { + deleteTaskInTaskStore(taskId); + } + } + + /** + * Integration test verifying MainQueue DOES close when task is finalized. + * This ensures Level 2 protection doesn't prevent cleanup of completed tasks. + * + * Flow: + * 1. Send message to new task (creates task in WORKING, then completes it) + * 2. Task reaches COMPLETED state (final) + * 3. ChildQueue closes after receiving final event + * 4. MainQueue should close because task is finalized + * 5. Resubscription should fail with TaskNotFoundError + */ + @Test + @Timeout(value = 2, unit = TimeUnit.MINUTES) + public void testMainQueueClosesForFinalizedTasks() throws Exception { + String taskId = "completed-task-integration"; + String contextId = "completed-ctx"; + + // Send a message that will create and complete the task + Message message = new Message.Builder(MESSAGE) + .taskId(taskId) + .contextId(contextId) + .parts(new TextPart("complete task")) + .build(); + + CountDownLatch completionLatch = new CountDownLatch(1); + AtomicReference errorRef = new AtomicReference<>(); + + BiConsumer consumer = (event, agentCard) -> { + if (event instanceof TaskEvent te) { + // Might get Task with final state + if (te.getTask().getStatus().state().isFinal()) { + completionLatch.countDown(); + } + } else if (event instanceof MessageEvent me) { + // Message is considered a final event + completionLatch.countDown(); + } else if (event instanceof TaskUpdateEvent tue && + tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { + if (status.isFinal()) { + completionLatch.countDown(); + } + } + }; + + Consumer errorHandler = error -> { + if (!isStreamClosedError(error)) { + errorRef.set(error); + } + completionLatch.countDown(); + }; + + try { + // Send message and wait for completion + getClient().sendMessage(message, List.of(consumer), errorHandler); + + assertTrue(completionLatch.await(15, TimeUnit.SECONDS), + "Should receive final event"); + assertNull(errorRef.get(), "Should not have errors during message send"); + + // Give cleanup time to run after final event + Thread.sleep(2000); + + // Try to resubscribe to finalized task - should fail + CountDownLatch errorLatch = new CountDownLatch(1); + AtomicReference resubErrorRef = new AtomicReference<>(); + + Consumer resubErrorHandler = error -> { + if (error == null) { + // Stream completed successfully - ignore, we're waiting for an error + return; + } + if (!isStreamClosedError(error)) { + resubErrorRef.set(error); + } + errorLatch.countDown(); + }; + + // Attempt resubscription + try { + getClient().resubscribe(new TaskIdParams(taskId), + List.of(), + resubErrorHandler); + + // Wait for error + assertTrue(errorLatch.await(15, TimeUnit.SECONDS), + "Should receive error for finalized task"); + + Throwable error = resubErrorRef.get(); + assertNotNull(error, "Resubscription should fail for finalized task"); + + // Verify it's a TaskNotFoundError + Throwable cause = error; + boolean foundTaskNotFound = false; + while (cause != null && !foundTaskNotFound) { + if (cause instanceof TaskNotFoundError || + (cause instanceof A2AClientException && + ((A2AClientException) cause).getCause() instanceof TaskNotFoundError)) { + foundTaskNotFound = true; + } + cause = cause.getCause(); + } + assertTrue(foundTaskNotFound, + "Should receive TaskNotFoundError - MainQueue closed for finalized task"); + + } catch (A2AClientException e) { + // Exception might be thrown immediately instead of via error handler + assertInstanceOf(TaskNotFoundError.class, e.getCause(), + "Should fail with TaskNotFoundError - MainQueue cleaned up for finalized task"); + } + + } finally { + // Task might not exist in store if created via message send + try { + Task task = getTaskFromTaskStore(taskId); + if (task != null) { + deleteTaskInTaskStore(taskId); + } + } catch (Exception e) { + // Ignore cleanup errors - task might not have been persisted + } + } + } + } \ No newline at end of file diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 90af21f1e..50fa8979c 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -388,7 +388,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ streamRecorder = sendStreamingMessageRequest(handler); } Assertions.assertNull(streamRecorder.getError()); @@ -564,7 +564,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ handler.taskSubscription(request, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 467760ef9..45109cced 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -186,7 +186,7 @@ public void testOnMessageNewMessageSuccessMocks() { SendMessageResponse response; try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, - (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ + (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); @@ -230,7 +230,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromItems(MINIMAL_TASK)).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); @@ -270,7 +270,7 @@ public void testOnMessageErrorMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromItems(new UnsupportedOperationError())).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(new UnsupportedOperationError()))).when(mock).consumeAll();})){ response = handler.onMessageSend(request, callContext); } @@ -475,7 +475,7 @@ public void testOnMessageStreamNewMessageSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ response = handler.onMessageSendStream(request, callContext); } @@ -622,7 +622,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ response = handler.onMessageSendStream(request, callContext); } @@ -904,7 +904,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events)).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ response = handler.onResubscribeToTask(request, callContext); } From e81b35ade5a3ac333a1b826c56fce75d9997fe7c Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Mon, 20 Oct 2025 15:57:41 -0400 Subject: [PATCH 166/493] fix: Add additional methods in TaskUpdater (#378) Small updates to TaskUpdater that I found useful while working on #338 --- .../src/main/java/io/a2a/server/tasks/TaskUpdater.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index 204e4adca..0b094a5ea 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -32,11 +32,11 @@ private void updateStatus(TaskState taskState) { updateStatus(taskState, null, taskState.isFinal()); } - private void updateStatus(TaskState taskState, Message message) { + public void updateStatus(TaskState taskState, Message message) { updateStatus(taskState, message, taskState.isFinal()); } - private void updateStatus(TaskState state, Message message, boolean isFinal) { + public void updateStatus(TaskState state, Message message, boolean isFinal) { synchronized (stateLock) { // Check if we're already in a terminal state if (terminalStateReached.get()) { @@ -62,6 +62,10 @@ public String getContextId() { return this.contextId; } + public String getTaskId() { + return this.taskId; + } + public void addArtifact(List> parts) { addArtifact(parts, null, null, null); } From 4ddac8b3cd5061b8d3f86536a2e4fc8aaa28740d Mon Sep 17 00:00:00 2001 From: Akanksha Sharan <77259199+Akanksha928@users.noreply.github.com> Date: Tue, 21 Oct 2025 04:49:33 -0700 Subject: [PATCH 167/493] test: add ToProto and FromProto tests (#341) # Description This PR adds new unit tests to improve coverage for `ProtoUtils.ToProto` and `ProtoUtils.FromProto`. ### Tests Added - `testFromProtoPartUnsupportedType`: verifies invalid proto parts throw `InvalidParamsError`. - `testTaskQueryParamsInvalidName`: makes sure invalid task name formats are handled gracefully. - `testToProtoPartUnsupportedType`: checks handling of unsupported `Part` types during conversion. Fixes #304 --- .../java/io/a2a/grpc/utils/FromProtoTest.java | 37 +++++++++++++++++++ .../java/io/a2a/grpc/utils/ToProtoTest.java | 24 ++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java new file mode 100644 index 000000000..284ba172d --- /dev/null +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java @@ -0,0 +1,37 @@ +package io.a2a.grpc.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class FromProtoTest { + @Test + public void testFromProtoPartUnsupportedType() { + io.a2a.grpc.Part emptyPart = io.a2a.grpc.Part.newBuilder().build(); + io.a2a.grpc.Message invalidMessage = io.a2a.grpc.Message.newBuilder() + .addContent(emptyPart) + .build(); + io.a2a.spec.InvalidParamsError exception = assertThrows( + io.a2a.spec.InvalidParamsError.class, + () -> ProtoUtils.FromProto.message(invalidMessage) + ); + + assertEquals("Invalid parameters", exception.getMessage()); + + } + + @Test + public void testTaskQueryParamsInvalidName() { + io.a2a.grpc.GetTaskRequest request = io.a2a.grpc.GetTaskRequest.newBuilder() + .setName("invalid-name-format") + .build(); + + var result = ProtoUtils.FromProto.taskQueryParams(request); + + assertNotNull(result); + assertEquals("invalid-name-format", result.id()); + } + +} diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index 67e8463ed..5b2650d86 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -289,4 +289,28 @@ public void convertTaskTimestampStatus() { assertNotNull(status.timestamp()); assertEquals(expectedTimestamp, status.timestamp()); } + + @Test + public void testToProtoPartUnsupportedType() { + class FakePart extends io.a2a.spec.Part { + @Override + public io.a2a.spec.Part.Kind getKind() { return null; } + @Override + public Map getMetadata() { return Map.of(); } + } + + FakePart fakePart = new FakePart(); + + io.a2a.spec.Message message = new io.a2a.spec.Message.Builder() + .messageId("msg-unsupported") + .contextId("ctx-unsupported") + .role(io.a2a.spec.Message.Role.USER) + .parts(List.of(fakePart)) + .build(); + io.a2a.grpc.Message result = ProtoUtils.ToProto.message(message); + + assertNotNull(result); + assertEquals(1, result.getContentCount()); + } + } From f58d94bf2b6f7f36f2c61006df9b69a267deb39f Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Tue, 21 Oct 2025 13:50:40 +0200 Subject: [PATCH 168/493] Revert "test: add ToProto and FromProto tests" (#381) Reverts a2aproject/a2a-java#341 --- .../java/io/a2a/grpc/utils/FromProtoTest.java | 37 ------------------- .../java/io/a2a/grpc/utils/ToProtoTest.java | 24 ------------ 2 files changed, 61 deletions(-) delete mode 100644 spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java deleted file mode 100644 index 284ba172d..000000000 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/FromProtoTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.a2a.grpc.utils; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class FromProtoTest { - @Test - public void testFromProtoPartUnsupportedType() { - io.a2a.grpc.Part emptyPart = io.a2a.grpc.Part.newBuilder().build(); - io.a2a.grpc.Message invalidMessage = io.a2a.grpc.Message.newBuilder() - .addContent(emptyPart) - .build(); - io.a2a.spec.InvalidParamsError exception = assertThrows( - io.a2a.spec.InvalidParamsError.class, - () -> ProtoUtils.FromProto.message(invalidMessage) - ); - - assertEquals("Invalid parameters", exception.getMessage()); - - } - - @Test - public void testTaskQueryParamsInvalidName() { - io.a2a.grpc.GetTaskRequest request = io.a2a.grpc.GetTaskRequest.newBuilder() - .setName("invalid-name-format") - .build(); - - var result = ProtoUtils.FromProto.taskQueryParams(request); - - assertNotNull(result); - assertEquals("invalid-name-format", result.id()); - } - -} diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index 5b2650d86..67e8463ed 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -289,28 +289,4 @@ public void convertTaskTimestampStatus() { assertNotNull(status.timestamp()); assertEquals(expectedTimestamp, status.timestamp()); } - - @Test - public void testToProtoPartUnsupportedType() { - class FakePart extends io.a2a.spec.Part { - @Override - public io.a2a.spec.Part.Kind getKind() { return null; } - @Override - public Map getMetadata() { return Map.of(); } - } - - FakePart fakePart = new FakePart(); - - io.a2a.spec.Message message = new io.a2a.spec.Message.Builder() - .messageId("msg-unsupported") - .contextId("ctx-unsupported") - .role(io.a2a.spec.Message.Role.USER) - .parts(List.of(fakePart)) - .build(); - io.a2a.grpc.Message result = ProtoUtils.ToProto.message(message); - - assertNotNull(result); - assertEquals(1, result.getContentCount()); - } - } From dcbc1adee34580de7665a33f318d360cea9692e1 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 21 Oct 2025 07:52:47 -0400 Subject: [PATCH 169/493] fix: Update A2AException and A2AClientError so that they extend RuntimeException instead of IOException (#379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #320 🦕 --- spec/src/main/java/io/a2a/spec/A2AClientError.java | 5 ++++- spec/src/main/java/io/a2a/spec/A2AException.java | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/src/main/java/io/a2a/spec/A2AClientError.java b/spec/src/main/java/io/a2a/spec/A2AClientError.java index 2ec8c864e..60065557a 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientError.java @@ -1,6 +1,9 @@ package io.a2a.spec; -public class A2AClientError extends Exception { +/** + * Base exception for A2A Client errors. + */ +public class A2AClientError extends RuntimeException { public A2AClientError() { } diff --git a/spec/src/main/java/io/a2a/spec/A2AException.java b/spec/src/main/java/io/a2a/spec/A2AException.java index 22b8363e2..a97bbd570 100644 --- a/spec/src/main/java/io/a2a/spec/A2AException.java +++ b/spec/src/main/java/io/a2a/spec/A2AException.java @@ -1,11 +1,9 @@ package io.a2a.spec; -import java.io.IOException; - /** * Exception to indicate a general failure related to the A2A protocol. */ -public class A2AException extends IOException { +public class A2AException extends RuntimeException { /** * Constructs a new {@code A2AException} instance. The message is left blank ({@code null}), and no From 5f4a2770ac989d7a4bc30b30c6e7bd6ecc474073 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 21 Oct 2025 17:27:31 +0100 Subject: [PATCH 170/493] feat: Add multi-server test using the replicated queue manager. (#380) --- .gitignore | 1 + extras/queue-manager-replicated/pom.xml | 3 +- .../tests-multi-instance/pom.xml | 25 + .../quarkus-app-1/Dockerfile | 13 + .../quarkus-app-1/pom.xml | 94 ++++ ...tanceReplicationApp1AgentCardProducer.java | 18 + ...eReplicationApp1AgentExecutorProducer.java | 16 + .../src/main/resources/application.properties | 41 ++ .../quarkus-app-2/Dockerfile | 13 + .../quarkus-app-2/pom.xml | 94 ++++ ...tanceReplicationApp2AgentCardProducer.java | 18 + ...eReplicationApp2AgentExecutorProducer.java | 16 + .../src/main/resources/application.properties | 41 ++ .../quarkus-common/pom.xml | 37 ++ .../MultiInstanceReplicationAgentCards.java | 53 +++ ...MultiInstanceReplicationAgentExecutor.java | 48 ++ .../tests-multi-instance/tests/pom.xml | 92 ++++ .../MultiInstanceReplicationTest.java | 441 ++++++++++++++++++ .../{tests => tests-single-instance}/pom.xml | 9 +- .../KafkaReplicationIntegrationTest.java | 0 .../tests/ReplicatedQueueManagerTest.java | 0 .../ReplicationTestAgentCardProducer.java | 0 .../tests/ReplicationTestAgentExecutor.java | 0 .../tests/TestKafkaEventConsumer.java | 0 .../src/test/resources/application.properties | 0 spec/src/main/java/io/a2a/spec/AgentCard.java | 32 ++ 26 files changed, 1097 insertions(+), 8 deletions(-) create mode 100644 extras/queue-manager-replicated/tests-multi-instance/pom.xml create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentCardProducer.java create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentExecutorProducer.java create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentCardProducer.java create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentExecutorProducer.java create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java create mode 100644 extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java create mode 100644 extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml create mode 100644 extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java rename extras/queue-manager-replicated/{tests => tests-single-instance}/pom.xml (93%) rename extras/queue-manager-replicated/{tests => tests-single-instance}/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java (100%) rename extras/queue-manager-replicated/{tests => tests-single-instance}/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java (100%) rename extras/queue-manager-replicated/{tests => tests-single-instance}/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java (100%) rename extras/queue-manager-replicated/{tests => tests-single-instance}/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java (100%) rename extras/queue-manager-replicated/{tests => tests-single-instance}/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java (100%) rename extras/queue-manager-replicated/{tests => tests-single-instance}/src/test/resources/application.properties (100%) diff --git a/.gitignore b/.gitignore index d4f021407..dcc2f1cb8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ nbproject/ # Private Claude config .claude/ .serena/ +claudedocs diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index 4aceaeb0e..a27e91bf8 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -18,7 +18,8 @@ core replication-mp-reactive - tests + tests-single-instance + tests-multi-instance \ No newline at end of file diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml new file mode 100644 index 000000000..bd442cf96 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-parent + 0.3.0.Beta3-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-tests-multi-instance-parent + pom + Java A2A Extras: Replicated Queue Manager Tests - Multi Instance Parent + + + quarkus-common + quarkus-app-1 + quarkus-app-2 + tests + + + diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile new file mode 100644 index 000000000..fb8d091ba --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:17-slim + +WORKDIR /app + +# Copy the built Quarkus application +COPY target/quarkus-app/lib/ /app/lib/ +COPY target/quarkus-app/*.jar /app/ +COPY target/quarkus-app/app/ /app/app/ +COPY target/quarkus-app/quarkus/ /app/quarkus/ + +EXPOSE 8081 + +CMD ["java", "-jar", "/app/quarkus-run.jar"] diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml new file mode 100644 index 000000000..08afad676 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-tests-multi-instance-parent + 0.3.0.Beta3-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-tests-multi-instance-app-1 + Java A2A Extras: Replicated Queue Manager Tests - Multi Instance App 1 + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-tests-multi-instance-common + ${project.version} + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + ${project.version} + + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + ${project.version} + + + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + + + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + ${project.version} + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-hibernate-orm + + + + + io.quarkus + quarkus-messaging-kafka + + + + + io.quarkus + quarkus-smallrye-health + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + + + + + + + diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentCardProducer.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentCardProducer.java new file mode 100644 index 000000000..4594f10b4 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentCardProducer.java @@ -0,0 +1,18 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance.app1; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.extras.queuemanager.replicated.tests.multiinstance.common.MultiInstanceReplicationAgentCards; +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.AgentCard; + +@ApplicationScoped +public class MultiInstanceReplicationApp1AgentCardProducer { + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + return MultiInstanceReplicationAgentCards.createAgentCard(1, 8081); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentExecutorProducer.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentExecutorProducer.java new file mode 100644 index 000000000..f45a58095 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app1/MultiInstanceReplicationApp1AgentExecutorProducer.java @@ -0,0 +1,16 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance.app1; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.extras.queuemanager.replicated.tests.multiinstance.common.MultiInstanceReplicationAgentExecutor; +import io.a2a.server.agentexecution.AgentExecutor; + +@ApplicationScoped +public class MultiInstanceReplicationApp1AgentExecutorProducer { + + @Produces + public AgentExecutor agentExecutor() { + return new MultiInstanceReplicationAgentExecutor(); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties new file mode 100644 index 000000000..f2d484ef3 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties @@ -0,0 +1,41 @@ +# Application HTTP Port +quarkus.http.port=8081 + +# Select our ReplicatedQueueManager and JpaDatabaseTaskStore as the active implementations +quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager,io.a2a.extras.taskstore.database.jpa.JpaDatabaseTaskStore + +# Configure PostgreSQL database (connection details will be provided by Testcontainers) +quarkus.datasource."a2a-java".db-kind=postgresql +quarkus.datasource."a2a-java".jdbc.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/a2adb} +quarkus.datasource."a2a-java".username=${DATABASE_USER:a2auser} +quarkus.datasource."a2a-java".password=${DATABASE_PASSWORD:a2apass} +quarkus.hibernate-orm."a2a-java".datasource=a2a-java +quarkus.hibernate-orm."a2a-java".database.generation=drop-and-create +quarkus.hibernate-orm."a2a-java".packages=io.a2a.extras.taskstore.database.jpa + +# Configure the outgoing channel (QueueManager -> Kafka) +mp.messaging.outgoing.replicated-events-out.connector=smallrye-kafka +mp.messaging.outgoing.replicated-events-out.topic=replicated-events +mp.messaging.outgoing.replicated-events-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.replicated-events-out.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} + +# Configure the incoming channel (Kafka -> QueueManager) +mp.messaging.incoming.replicated-events-in.connector=smallrye-kafka +mp.messaging.incoming.replicated-events-in.topic=replicated-events +mp.messaging.incoming.replicated-events-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.replicated-events-in.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} +mp.messaging.incoming.replicated-events-in.group.id=app-1-consumer-group +mp.messaging.incoming.replicated-events-in.auto.offset.reset=earliest + +# Disable DevServices - Testcontainers will manage infrastructure +quarkus.devservices.enabled=false +quarkus.kafka.devservices.enabled=false + +# Reduce timeouts for faster tests +quarkus.messaging.kafka.health.timeout=5s + +# Enable debug logging +quarkus.log.category."io.a2a.server.events".level=DEBUG +quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +quarkus.log.category."io.a2a.client".level=DEBUG diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile new file mode 100644 index 000000000..920d34747 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:17-slim + +WORKDIR /app + +# Copy the built Quarkus application +COPY target/quarkus-app/lib/ /app/lib/ +COPY target/quarkus-app/*.jar /app/ +COPY target/quarkus-app/app/ /app/app/ +COPY target/quarkus-app/quarkus/ /app/quarkus/ + +EXPOSE 8082 + +CMD ["java", "-jar", "/app/quarkus-run.jar"] diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml new file mode 100644 index 000000000..9a7c9fd42 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-tests-multi-instance-parent + 0.3.0.Beta3-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-tests-multi-instance-app-2 + Java A2A Extras: Replicated Queue Manager Tests - Multi Instance App 2 + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-tests-multi-instance-common + ${project.version} + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + ${project.version} + + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + ${project.version} + + + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + + + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + ${project.version} + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-hibernate-orm + + + + + io.quarkus + quarkus-messaging-kafka + + + + + io.quarkus + quarkus-smallrye-health + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + + + + + + + diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentCardProducer.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentCardProducer.java new file mode 100644 index 000000000..6874601f0 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentCardProducer.java @@ -0,0 +1,18 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance.app2; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.extras.queuemanager.replicated.tests.multiinstance.common.MultiInstanceReplicationAgentCards; +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.AgentCard; + +@ApplicationScoped +public class MultiInstanceReplicationApp2AgentCardProducer { + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + return MultiInstanceReplicationAgentCards.createAgentCard(2, 8082); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentExecutorProducer.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentExecutorProducer.java new file mode 100644 index 000000000..d03388179 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/app2/MultiInstanceReplicationApp2AgentExecutorProducer.java @@ -0,0 +1,16 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance.app2; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import io.a2a.extras.queuemanager.replicated.tests.multiinstance.common.MultiInstanceReplicationAgentExecutor; +import io.a2a.server.agentexecution.AgentExecutor; + +@ApplicationScoped +public class MultiInstanceReplicationApp2AgentExecutorProducer { + + @Produces + public AgentExecutor agentExecutor() { + return new MultiInstanceReplicationAgentExecutor(); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties new file mode 100644 index 000000000..ca4698aa2 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties @@ -0,0 +1,41 @@ +# Application HTTP Port +quarkus.http.port=8082 + +# Select our ReplicatedQueueManager and JpaDatabaseTaskStore as the active implementations +quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager,io.a2a.extras.taskstore.database.jpa.JpaDatabaseTaskStore + +# Configure PostgreSQL database (connection details will be provided by Testcontainers) +quarkus.datasource."a2a-java".db-kind=postgresql +quarkus.datasource."a2a-java".jdbc.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/a2adb} +quarkus.datasource."a2a-java".username=${DATABASE_USER:a2auser} +quarkus.datasource."a2a-java".password=${DATABASE_PASSWORD:a2apass} +quarkus.hibernate-orm."a2a-java".datasource=a2a-java +quarkus.hibernate-orm."a2a-java".database.generation=drop-and-create +quarkus.hibernate-orm."a2a-java".packages=io.a2a.extras.taskstore.database.jpa + +# Configure the outgoing channel (QueueManager -> Kafka) +mp.messaging.outgoing.replicated-events-out.connector=smallrye-kafka +mp.messaging.outgoing.replicated-events-out.topic=replicated-events +mp.messaging.outgoing.replicated-events-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.replicated-events-out.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} + +# Configure the incoming channel (Kafka -> QueueManager) +mp.messaging.incoming.replicated-events-in.connector=smallrye-kafka +mp.messaging.incoming.replicated-events-in.topic=replicated-events +mp.messaging.incoming.replicated-events-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.replicated-events-in.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} +mp.messaging.incoming.replicated-events-in.group.id=app-2-consumer-group +mp.messaging.incoming.replicated-events-in.auto.offset.reset=earliest + +# Disable DevServices - Testcontainers will manage infrastructure +quarkus.devservices.enabled=false +quarkus.kafka.devservices.enabled=false + +# Reduce timeouts for faster tests +quarkus.messaging.kafka.health.timeout=5s + +# Enable debug logging +quarkus.log.category."io.a2a.server.events".level=DEBUG +quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +quarkus.log.category."io.a2a.client".level=DEBUG diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml new file mode 100644 index 000000000..d9f940e1a --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-tests-multi-instance-parent + 0.3.0.Beta3-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-tests-multi-instance-common + Java A2A Extras: Replicated Queue Manager Tests - Multi Instance Common + + + + + io.github.a2asdk + a2a-java-sdk-spec + + + + + io.github.a2asdk + a2a-java-sdk-server-common + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java new file mode 100644 index 000000000..bc1994cdb --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java @@ -0,0 +1,53 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance.common; + +import java.util.Collections; +import java.util.List; + +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.AgentSkill; +import io.a2a.spec.TransportProtocol; + +/** + * Shared AgentCard factory for multi-instance replication tests. + */ +public final class MultiInstanceReplicationAgentCards { + + private MultiInstanceReplicationAgentCards() { + } + + /** + * Creates an AgentCard for a test instance. + * + * @param instanceNumber the instance number (e.g., 1, 2) + * @param port the port number (e.g., 8081, 8082) + * @return the configured AgentCard + */ + public static AgentCard createAgentCard(int instanceNumber, int port) { + String url = "http://localhost:" + port; + + return new AgentCard.Builder() + .name("Multi-Instance Test Agent " + instanceNumber) + .description("Test agent for multi-instance replication testing - Instance " + instanceNumber) + .url(url) + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(false) + .stateTransitionHistory(false) + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList(new AgentSkill.Builder() + .id("replication_test") + .name("Replication Test") + .description("Fire-and-forget agent for testing replication") + .tags(Collections.singletonList("test")) + .build())) + .additionalInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), url))) + .protocolVersion("0.3.0") + .build(); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java new file mode 100644 index 000000000..a4b9ae4f2 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java @@ -0,0 +1,48 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance.common; + +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Task; +import io.a2a.spec.TextPart; + +/** + * Shared test agent executor for multi-instance replication tests. + * + * Behavior: + * 1. Creates task in SUBMITTED state on first message + * 2. Adds messages as artifacts on subsequent messages + * 3. Completes task when message contains "close" + */ +public class MultiInstanceReplicationAgentExecutor implements AgentExecutor { + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + Task task = context.getTask(); + TaskUpdater updater = new TaskUpdater(context, eventQueue); + + // Check if message contains "close" signal + boolean shouldClose = context.getMessage().getParts().stream() + .anyMatch(part -> part instanceof TextPart tp && + tp.getText() != null && + tp.getText().toLowerCase().contains("close")); + + if (shouldClose) { + // Close the task + updater.complete(); + } else if (task == null) { + // First message - create task in SUBMITTED state + updater.submit(); + } else { + // Subsequent messages - add as artifact + updater.addArtifact(context.getMessage().getParts()); + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater updater = new TaskUpdater(context, eventQueue); + updater.cancel(); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml new file mode 100644 index 000000000..16b696ca5 --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-queue-manager-replicated-tests-multi-instance-parent + 0.3.0.Beta3-SNAPSHOT + ../pom.xml + + + a2a-java-queue-manager-replicated-tests-multi-instance-tests + Java A2A Extras: Replicated Queue Manager Tests - Multi Instance Tests + + + + + io.github.a2asdk + a2a-java-sdk-client + test + + + io.github.a2asdk + a2a-java-sdk-transport-jsonrpc + test + + + + + io.quarkus + quarkus-junit5 + test + + + + + org.testcontainers + testcontainers + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + kafka + test + + + org.testcontainers + postgresql + test + + + + + org.awaitility + awaitility + test + + + + + org.slf4j + slf4j-api + test + + + ch.qos.logback + logback-classic + test + + + + + + + maven-surefire-plugin + + + + org.jboss.logmanager.LogManager + + + + + + diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java new file mode 100644 index 000000000..f7fd652bb --- /dev/null +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java @@ -0,0 +1,441 @@ +package io.a2a.extras.queuemanager.replicated.tests.multiinstance; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import io.a2a.A2A; +import io.a2a.client.Client; +import io.a2a.client.ClientEvent; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +/** + * Multi-instance replication test that validates event queue replication + * between two running Quarkus instances using Testcontainers. + * + * Test Architecture: + * - Shared KafkaContainer for event replication + * - Shared PostgreSQLContainer for task persistence + * - Two Quarkus application containers (app1 on 8081, app2 on 8082) + * - A2A Client instances to interact with both applications + */ +@Testcontainers +public class MultiInstanceReplicationTest { + + private static final String KAFKA_IMAGE = "confluentinc/cp-kafka:7.6.1"; + private static final String POSTGRES_IMAGE = "postgres:16-alpine"; + + private static Network network; + private static KafkaContainer kafka; + private static PostgreSQLContainer postgres; + private static GenericContainer app1; + private static GenericContainer app2; + + private static String app1Url; + private static String app2Url; + private static AgentCard app1Card; + private static AgentCard app2Card; + + private Client client1; + private Client client2; + + @BeforeAll + public static void setup() { + // Create a shared network for all containers + network = Network.newNetwork(); + + // Start Kafka container + kafka = new KafkaContainer(DockerImageName.parse(KAFKA_IMAGE)) + .withNetwork(network) + .withNetworkAliases("kafka") + .withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "true"); + kafka.start(); + + // Start PostgreSQL container + postgres = new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_IMAGE)) + .withNetwork(network) + .withNetworkAliases("postgres") + .withDatabaseName("a2adb") + .withUsername("a2auser") + .withPassword("a2apass"); + postgres.start(); + + // Get Kafka bootstrap servers for app configuration + // Kafka is accessible within the Docker network as "kafka:9092" + String kafkaBootstrapServers = "kafka:9092"; + + // PostgreSQL connection details for apps (using network alias) + String dbUrl = "jdbc:postgresql://postgres:5432/a2adb"; + String dbUser = "a2auser"; + String dbPassword = "a2apass"; + + // Build and start Quarkus app1 container + File app1Dir = new File("../quarkus-app-1").getAbsoluteFile(); + app1 = new GenericContainer<>( + new ImageFromDockerfile() + .withFileFromPath(".", app1Dir.toPath())) + .withNetwork(network) + .withNetworkAliases("app1") + .withEnv("KAFKA_BOOTSTRAP_SERVERS", kafkaBootstrapServers) + .withEnv("DATABASE_URL", dbUrl) + .withEnv("DATABASE_USER", dbUser) + .withEnv("DATABASE_PASSWORD", dbPassword) + .withExposedPorts(8081) + .waitingFor(Wait.forHttp("/q/health/ready") + .forPort(8081) + .withStartupTimeout(Duration.ofMinutes(2))); + app1.start(); + + // Build and start Quarkus app2 container + File app2Dir = new File("../quarkus-app-2").getAbsoluteFile(); + app2 = new GenericContainer<>( + new ImageFromDockerfile() + .withFileFromPath(".", app2Dir.toPath())) + .withNetwork(network) + .withNetworkAliases("app2") + .withEnv("KAFKA_BOOTSTRAP_SERVERS", kafkaBootstrapServers) + .withEnv("DATABASE_URL", dbUrl) + .withEnv("DATABASE_USER", dbUser) + .withEnv("DATABASE_PASSWORD", dbPassword) + .withExposedPorts(8082) + .waitingFor(Wait.forHttp("/q/health/ready") + .forPort(8082) + .withStartupTimeout(Duration.ofMinutes(2))); + app2.start(); + + // Store URLs for creating clients + app1Url = "http://localhost:" + app1.getMappedPort(8081); + app2Url = "http://localhost:" + app2.getMappedPort(8082); + + // Get AgentCards from both apps and patch URLs to use mapped ports + try { + AgentCard originalApp1Card = A2A.getAgentCard(app1Url); + AgentCard originalApp2Card = A2A.getAgentCard(app2Url); + + // Rebuild AgentCards with correct URLs (mapped ports, not container ports) + app1Card = new AgentCard.Builder(originalApp1Card) + .url(app1Url) + .build(); + + app2Card = new AgentCard.Builder(originalApp2Card) + .url(app2Url) + .build(); + } catch (Exception e) { + System.err.println("=== Failed to get AgentCards ==="); + System.err.println("App1 logs:"); + System.err.println(app1.getLogs()); + System.err.println("\nApp2 logs:"); + System.err.println(app2.getLogs()); + throw new RuntimeException("Failed to get AgentCards", e); + } + } + + @AfterAll + public static void teardown() { + if (app2 != null) { + app2.stop(); + } + if (app1 != null) { + app1.stop(); + } + if (postgres != null) { + postgres.stop(); + } + if (kafka != null) { + kafka.stop(); + } + if (network != null) { + network.close(); + } + } + + @AfterEach + public void cleanupClients() throws Exception { + if (client1 != null) { + client1.close(); + client1 = null; + } + if (client2 != null) { + client2.close(); + client2 = null; + } + } + + // Helper methods for creating A2A clients + + private Client createClient(AgentCard agentCard) throws A2AClientException { + ClientConfig config = ClientConfig.builder() + .setStreaming(true) + .build(); + + return Client.builder(agentCard) + .clientConfig(config) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .build(); + } + + private Client getClient1() throws A2AClientException { + if (client1 == null) { + client1 = createClient(app1Card); + } + return client1; + } + + private Client getClient2() throws A2AClientException { + if (client2 == null) { + client2 = createClient(app2Card); + } + return client2; + } + + @Test + public void testInfrastructureStartup() { + // Verify that all containers started successfully + assertTrue(kafka.isRunning(), "Kafka container should be running"); + assertTrue(postgres.isRunning(), "PostgreSQL container should be running"); + assertTrue(app1.isRunning(), "App1 container should be running"); + assertTrue(app2.isRunning(), "App2 container should be running"); + + assertNotNull(kafka.getBootstrapServers()); + assertNotNull(postgres.getJdbcUrl()); + assertNotNull(app1.getMappedPort(8081)); + assertNotNull(app2.getMappedPort(8082)); + } + + /** + * Main multi-instance replication test following architect's guidance: + * 1. Send initial message on app1 (creates task in non-final state) + * 2. Resubscribe to that task from both app1 and app2 + * 3. Send message on app1, verify both subscribers receive it + * 4. Send message on app2, verify both subscribers receive it + * 5. Send final message to transition task to COMPLETED + * 6. Verify final state reflected on both subscribers + */ + @Test + public void testMultiInstanceEventReplication() throws Exception { + final String taskId = "replication-test-task-" + System.currentTimeMillis(); + final String contextId = "replication-test-context"; + + // Step 1: Send initial message NON-streaming to create task + Message initialMessage = new Message.Builder(A2A.toUserMessage("Initial test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + // Use NON-streaming client to create the task + // Note: app1Card has the correct URL from Testcontainers setup + Client nonStreamingClient = Client.builder(app1Card) + .clientConfig(ClientConfig.builder().setStreaming(false).build()) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .build(); + + Task createdTask = null; + try { + nonStreamingClient.sendMessage(initialMessage, null); + + // Retrieve the task to verify it was created + createdTask = nonStreamingClient.getTask(new TaskQueryParams(taskId), null); + assertNotNull(createdTask, "Task should be created"); + + // Task should be in a non-final state (SUBMITTED or WORKING are both valid) + TaskState state = createdTask.getStatus().state(); + assertTrue(state == TaskState.SUBMITTED || state == TaskState.WORKING, + "Task should be in SUBMITTED or WORKING state, but was: " + state); + nonStreamingClient.close(); + } catch (Exception e) { + System.err.println("\n=== FAILED TO CREATE TASK ==="); + System.err.println("Error: " + e.getMessage()); + System.err.println("\n=== APP1 CONTAINER LOGS ==="); + System.err.println(app1.getLogs()); + + System.err.println("\n=== APP2 CONTAINER LOGS ==="); + System.err.println(app2.getLogs()); + + throw e; + } + + // Step 2: Subscribe from both app1 and app2 with proper latches + + // We need to wait for at least 3 new events after resubscription: + // 1. TaskArtifactUpdateEvent (message from app1) + // 2. TaskArtifactUpdateEvent (message from app2) + // 3. TaskStatusUpdateEvent(COMPLETED) (close message) + // Note: may also receive initial TaskEvent/TaskUpdateEvent when resubscribing + AtomicInteger app1EventCount = new AtomicInteger(0); + AtomicInteger app2EventCount = new AtomicInteger(0); + + // Track events received + CopyOnWriteArrayList app1Events = new CopyOnWriteArrayList<>(); + CopyOnWriteArrayList app2Events = new CopyOnWriteArrayList<>(); + + AtomicReference app1Error = new AtomicReference<>(); + AtomicReference app2Error = new AtomicReference<>(); + + // App1 subscriber + BiConsumer app1Subscriber = (event, card) -> { + app1Events.add(event); + app1EventCount.incrementAndGet(); + }; + + Consumer app1ErrorHandler = error -> { + if (!isStreamClosedError(error)) { + app1Error.set(error); + } + }; + + // App2 subscriber + BiConsumer app2Subscriber = (event, card) -> { + app2Events.add(event); + app2EventCount.incrementAndGet(); + }; + + Consumer app2ErrorHandler = error -> { + if (!isStreamClosedError(error)) { + app2Error.set(error); + } + }; + + // Start subscriptions (resubscribe returns void) + getClient1().resubscribe(new TaskIdParams(taskId), List.of(app1Subscriber), app1ErrorHandler); + getClient2().resubscribe(new TaskIdParams(taskId), List.of(app2Subscriber), app2ErrorHandler); + + // Wait for subscriptions to be established - at least one event should arrive on each + await() + .atMost(Duration.ofSeconds(10)) + .pollInterval(Duration.ofMillis(500)) + .until(() -> app1EventCount.get() >= 1 && app2EventCount.get() >= 1); + + // Step 3: Send message on app1 (should generate TaskArtifactUpdateEvent) + int app1BeforeMsg1 = app1EventCount.get(); + int app2BeforeMsg1 = app2EventCount.get(); + + Message messageFromApp1 = new Message.Builder(A2A.toUserMessage("Message from app1")) + .taskId(taskId) + .contextId(contextId) + .build(); + getClient1().sendMessage(messageFromApp1, List.of(), null); + + // Wait for both subscribers to receive the replicated event + await() + .atMost(Duration.ofSeconds(10)) + .pollInterval(Duration.ofMillis(500)) + .until(() -> app1EventCount.get() > app1BeforeMsg1 && + app2EventCount.get() > app2BeforeMsg1); + + // Step 4: Send message on app2 (should generate TaskArtifactUpdateEvent) + int app1BeforeMsg2 = app1EventCount.get(); + int app2BeforeMsg2 = app2EventCount.get(); + + Message messageFromApp2 = new Message.Builder(A2A.toUserMessage("Message from app2")) + .taskId(taskId) + .contextId(contextId) + .build(); + getClient2().sendMessage(messageFromApp2, List.of(), null); + + // Wait for both subscribers to receive the replicated event + await() + .atMost(Duration.ofSeconds(10)) + .pollInterval(Duration.ofMillis(500)) + .until(() -> app1EventCount.get() > app1BeforeMsg2 && + app2EventCount.get() > app2BeforeMsg2); + + // Step 5: Send close message (should generate TaskStatusUpdateEvent with COMPLETED) + int app1BeforeClose = app1EventCount.get(); + int app2BeforeClose = app2EventCount.get(); + + Message closeMessage = new Message.Builder(A2A.toUserMessage("close")) + .taskId(taskId) + .contextId(contextId) + .build(); + getClient1().sendMessage(closeMessage, List.of(), null); + + // Wait for both subscribers to receive the completion event + await() + .atMost(Duration.ofSeconds(10)) + .pollInterval(Duration.ofMillis(500)) + .until(() -> app1EventCount.get() > app1BeforeClose && + app2EventCount.get() > app2BeforeClose); + + + // Verify we got at least 3 new events after initial subscription (artifact1, artifact2, completed) + assertTrue(app1Events.size() >= 3, + "App1 should receive at least 3 events (got " + app1Events.size() + ")"); + assertTrue(app2Events.size() >= 3, + "App2 should receive at least 3 events (got " + app2Events.size() + ")"); + + // Verify no errors + if (app1Error.get() != null) { + throw new AssertionError("App1 subscriber error", app1Error.get()); + } + if (app2Error.get() != null) { + throw new AssertionError("App2 subscriber error", app2Error.get()); + } + + // Verify both received at least 3 events (could be more due to initial state events) + assertTrue(app1Events.size() >= 3, "App1 should receive at least 3 events, got: " + app1Events.size()); + assertTrue(app2Events.size() >= 3, "App2 should receive at least 3 events, got: " + app2Events.size()); + } + + /** + * Checks if the error is a normal stream closure error that should be ignored. + * HTTP/2 stream cancellation and closure are expected during cleanup. + */ + private boolean isStreamClosedError(Throwable error) { + if (error == null) { + return false; + } + + // Check for IOException which includes stream cancellation + if (error instanceof IOException) { + String message = error.getMessage(); + if (message != null) { + // Filter out normal stream closure/cancellation errors + if (message.contains("Stream closed") || + message.contains("Stream") && message.contains("cancelled") || + message.contains("EOF reached") || + message.contains("CANCEL")) { + return true; + } + } + } + + // Check cause recursively + Throwable cause = error.getCause(); + if (cause != null && cause != error) { + return isStreamClosedError(cause); + } + + return false; + } +} diff --git a/extras/queue-manager-replicated/tests/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml similarity index 93% rename from extras/queue-manager-replicated/tests/pom.xml rename to extras/queue-manager-replicated/tests-single-instance/pom.xml index 390d0bc3f..cfc449c75 100644 --- a/extras/queue-manager-replicated/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -10,8 +10,8 @@ ../pom.xml - a2a-java-queue-manager-replicated-tests - Java A2A Extras: Replicated Queue Manager Tests + a2a-java-queue-manager-replicated-tests-single-instance + Java A2A Extras: Replicated Queue Manager Tests - Single Instance @@ -68,11 +68,6 @@ quarkus-junit5 test - - io.rest-assured - rest-assured - test - io.quarkus quarkus-messaging-kafka diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java similarity index 100% rename from extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java rename to extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java similarity index 100% rename from extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java rename to extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java similarity index 100% rename from extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java rename to extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java similarity index 100% rename from extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java rename to extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java diff --git a/extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java similarity index 100% rename from extras/queue-manager-replicated/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java rename to extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java diff --git a/extras/queue-manager-replicated/tests/src/test/resources/application.properties b/extras/queue-manager-replicated/tests-single-instance/src/test/resources/application.properties similarity index 100% rename from extras/queue-manager-replicated/tests/src/test/resources/application.properties rename to extras/queue-manager-replicated/tests-single-instance/src/test/resources/application.properties diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index c0fcbba18..b59a9403b 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -62,6 +62,38 @@ public static class Builder { private String protocolVersion; private List signatures; + /** + * Creates a new Builder. + */ + public Builder() { + + } + + /** + * Creates a new Builder as a copy of an existing AgentCard. + * + * @param card the AgentCard to copy + */ + public Builder(AgentCard card) { + this.name = card.name; + this.description = card.description; + this.url = card.url; + this.provider = card.provider; + this.version = card.version; + this.documentationUrl = card.documentationUrl; + this.capabilities = card.capabilities; + this.defaultInputModes = card.defaultInputModes != null ? new ArrayList<>(card.defaultInputModes) : null; + this.defaultOutputModes = card.defaultOutputModes != null ? new ArrayList<>(card.defaultOutputModes) : null; + this.skills = card.skills != null ? new ArrayList<>(card.skills) : null; + this.supportsAuthenticatedExtendedCard = card.supportsAuthenticatedExtendedCard; + this.securitySchemes = card.securitySchemes != null ? Map.copyOf(card.securitySchemes) : null; + this.security = card.security != null ? new ArrayList<>(card.security) : null; + this.iconUrl = card.iconUrl; + this.additionalInterfaces = card.additionalInterfaces != null ? new ArrayList<>(card.additionalInterfaces) : null; + this.preferredTransport = card.preferredTransport; + this.protocolVersion = card.protocolVersion; + this.signatures = card.signatures != null ? new ArrayList<>(card.signatures) : null; + } public Builder name(String name) { this.name = name; From ba7f9fe71b41d04d0527bd08a210f10afc5c42ca Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 22 Oct 2025 18:36:03 +0200 Subject: [PATCH 171/493] feat: add JSON-RPC method to ServerCallContext.state (#353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added a `method` field to `ServerCallContext.state`, similar to `headers`. Issue: https://github.com/a2aproject/a2a-java/issues/352 Fixes #352 🦕 Signed-off-by: Emmanuel Hugonnet --- reference/jsonrpc/pom.xml | 10 + .../server/apps/quarkus/A2AServerRoutes.java | 40 +- .../apps/quarkus/A2AServerRoutesTest.java | 442 ++++++++++++++++++ reference/rest/pom.xml | 10 + .../server/rest/quarkus/A2AServerRoutes.java | 44 +- .../rest/quarkus/A2AServerRoutesTest.java | 307 ++++++++++++ .../jsonrpc/context/JSONRPCContextKeys.java | 24 + .../rest/context/RestContextKeys.java | 24 + 8 files changed, 864 insertions(+), 37 deletions(-) create mode 100644 reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java create mode 100644 reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java create mode 100644 transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java create mode 100644 transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 4059447a9..c263d7091 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -77,5 +77,15 @@ rest-assured test + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index ae9bb817b..df726ad88 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -1,7 +1,10 @@ package io.a2a.server.apps.quarkus; +import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.HEADERS_KEY; +import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.SERVER_SENT_EVENTS; import java.util.HashMap; import java.util.List; @@ -91,14 +94,20 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { JSONRPCResponse nonStreamingResponse = null; Multi> streamingResponse = null; JSONRPCErrorResponse error = null; - try { - if (isStreamingRequest(body)) { - streaming = true; - StreamingJSONRPCRequest request = Utils.OBJECT_MAPPER.readValue(body, StreamingJSONRPCRequest.class); + JsonNode node = Utils.OBJECT_MAPPER.readTree(body); + JsonNode method = node != null ? node.get("method") : null; + streaming = method != null && (SendStreamingMessageRequest.METHOD.equals(method.asText()) + || TaskResubscriptionRequest.METHOD.equals(method.asText())); + String methodName = (method != null && method.isTextual()) ? method.asText() : null; + if (methodName != null) { + context.getState().put(METHOD_NAME_KEY, methodName); + } + if (streaming) { + StreamingJSONRPCRequest request = Utils.OBJECT_MAPPER.treeToValue(node, StreamingJSONRPCRequest.class); streamingResponse = processStreamingRequest(request, context); } else { - NonStreamingJSONRPCRequest request = Utils.OBJECT_MAPPER.readValue(body, NonStreamingJSONRPCRequest.class); + NonStreamingJSONRPCRequest request = Utils.OBJECT_MAPPER.treeToValue(node, NonStreamingJSONRPCRequest.class); nonStreamingResponse = processNonStreamingRequest(request, context); } } catch (JsonProcessingException e) { @@ -201,17 +210,6 @@ private JSONRPCResponse generateErrorResponse(JSONRPCRequest request, JSON return new JSONRPCErrorResponse(request.getId(), error); } - private static boolean isStreamingRequest(String requestBody) { - try { - JsonNode node = Utils.OBJECT_MAPPER.readTree(requestBody); - JsonNode method = node != null ? node.get("method") : null; - return method != null && (SendStreamingMessageRequest.METHOD.equals(method.asText()) - || TaskResubscriptionRequest.METHOD.equals(method.asText())); - } catch (Exception e) { - return false; - } - } - static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { streamingMultiSseSupportSubscribedRunnable = runnable; } @@ -243,7 +241,7 @@ public String getUsername() { Map headers = new HashMap<>(); Set headerNames = rc.request().headers().names(); headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); - state.put("headers", headers); + state.put(HEADERS_KEY, headers); // Extract requested extensions from X-A2A-Extensions header List extensionHeaderValues = rc.request().headers().getAll(A2AHeaders.X_A2A_EXTENSIONS); @@ -266,8 +264,8 @@ private MultiSseSupport() { private static void initialize(HttpServerResponse response) { if (response.bytesWritten() == 0) { MultiMap headers = response.headers(); - if (headers.get("content-type") == null) { - headers.set("content-type", "text/event-stream"); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); } response.setChunked(true); } @@ -340,8 +338,8 @@ public Buffer apply(Object o) { private static void endOfStream(HttpServerResponse response) { if (response.bytesWritten() == 0) { // No item MultiMap headers = response.headers(); - if (headers.get("content-type") == null) { - headers.set("content-type", "text/event-stream"); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); } } response.end(); diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java new file mode 100644 index 000000000..fa7e77db3 --- /dev/null +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -0,0 +1,442 @@ +package io.a2a.server.apps.quarkus; + +import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; + +import jakarta.enterprise.inject.Instance; + +import io.a2a.server.ServerCallContext; +import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskPushNotificationConfigResponse; +import io.a2a.spec.GetTaskRequest; +import io.a2a.spec.GetTaskResponse; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendMessageResponse; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SendStreamingMessageResponse; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.SetTaskPushNotificationConfigResponse; +import io.a2a.spec.TaskResubscriptionRequest; +import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RequestBody; +import io.vertx.ext.web.RoutingContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +/** + * Unit test for JSON-RPC A2AServerRoutes that verifies the method names are properly set + * in the ServerCallContext for all request types. + */ +public class A2AServerRoutesTest { + + private A2AServerRoutes routes; + private JSONRPCHandler mockJsonRpcHandler; + private Executor mockExecutor; + private Instance mockCallContextFactory; + private RoutingContext mockRoutingContext; + private HttpServerRequest mockRequest; + private HttpServerResponse mockHttpResponse; + private MultiMap mockHeaders; + private RequestBody mockRequestBody; + + @BeforeEach + public void setUp() { + routes = new A2AServerRoutes(); + mockJsonRpcHandler = mock(JSONRPCHandler.class); + mockExecutor = mock(Executor.class); + mockCallContextFactory = mock(Instance.class); + mockRoutingContext = mock(RoutingContext.class); + mockRequest = mock(HttpServerRequest.class); + mockHttpResponse = mock(HttpServerResponse.class); + mockHeaders = MultiMap.caseInsensitiveMultiMap(); + mockRequestBody = mock(RequestBody.class); + + // Inject mocks via reflection since we can't use @InjectMocks + setField(routes, "jsonRpcHandler", mockJsonRpcHandler); + setField(routes, "executor", mockExecutor); + setField(routes, "callContextFactory", mockCallContextFactory); + + // Setup common mock behavior + when(mockCallContextFactory.isUnsatisfied()).thenReturn(true); + when(mockRoutingContext.request()).thenReturn(mockRequest); + when(mockRoutingContext.response()).thenReturn(mockHttpResponse); + when(mockRoutingContext.user()).thenReturn(null); + when(mockRequest.headers()).thenReturn(mockHeaders); + when(mockRoutingContext.body()).thenReturn(mockRequestBody); + + // Chain the response methods properly + when(mockHttpResponse.setStatusCode(any(Integer.class))).thenReturn(mockHttpResponse); + when(mockHttpResponse.putHeader(any(CharSequence.class), any(CharSequence.class))).thenReturn(mockHttpResponse); + when(mockHttpResponse.end(anyString())).thenReturn(null); + when(mockHttpResponse.setChunked(any(Boolean.class))).thenReturn(mockHttpResponse); + when(mockHttpResponse.headers()).thenReturn(mockHeaders); + } + + @Test + public void testSendMessage_MethodNameSetInContext() { + // Arrange - using valid JSON from JsonMessages test file + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + SendMessageResponse mockResponse = mock(SendMessageResponse.class); + when(mockJsonRpcHandler.onMessageSend(any(SendMessageRequest.class), any(ServerCallContext.class))) + .thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onMessageSend(any(SendMessageRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(SendMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testSendStreamingMessage_MethodNameSetInContext() { + // Arrange - using the same valid format as testSendMessage + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "message/stream", + "params": { + "message": { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "tell me a joke" + } + ], + "messageId": "message-1234", + "contextId": "context-1234", + "kind": "message" + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + } + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + @SuppressWarnings("unchecked") + Flow.Publisher mockPublisher = mock(Flow.Publisher.class); + when(mockJsonRpcHandler.onMessageSendStream(any(SendStreamingMessageRequest.class), + any(ServerCallContext.class))).thenReturn(mockPublisher); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onMessageSendStream(any(SendStreamingMessageRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(SendStreamingMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testGetTask_MethodNameSetInContext() { + // Arrange - based on GET_TASK_TEST_REQUEST from JsonMessages + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/get", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength": 10 + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + GetTaskResponse mockResponse = mock(GetTaskResponse.class); + when(mockJsonRpcHandler.onGetTask(any(GetTaskRequest.class), any(ServerCallContext.class))) + .thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onGetTask(any(GetTaskRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(GetTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testCancelTask_MethodNameSetInContext() { + // Arrange - based on CANCEL_TASK_TEST_REQUEST from JsonMessages + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/cancel", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "metadata": {} + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + CancelTaskResponse mockResponse = mock(CancelTaskResponse.class); + when(mockJsonRpcHandler.onCancelTask(any(CancelTaskRequest.class), any(ServerCallContext.class))) + .thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onCancelTask(any(CancelTaskRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(CancelTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testTaskResubscription_MethodNameSetInContext() { + // Arrange - minimal valid JSON for task resubscription + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/resubscribe", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64" + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + @SuppressWarnings("unchecked") + Flow.Publisher mockPublisher = mock(Flow.Publisher.class); + when(mockJsonRpcHandler.onResubscribeToTask(any(TaskResubscriptionRequest.class), + any(ServerCallContext.class))).thenReturn(mockPublisher); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onResubscribeToTask(any(TaskResubscriptionRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(TaskResubscriptionRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testSetTaskPushNotificationConfig_MethodNameSetInContext() { + // Arrange - based on SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST from JsonMessages + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/pushNotificationConfig/set", + "params": { + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + SetTaskPushNotificationConfigResponse mockResponse = mock(SetTaskPushNotificationConfigResponse.class); + when(mockJsonRpcHandler.setPushNotificationConfig(any(SetTaskPushNotificationConfigRequest.class), + any(ServerCallContext.class))).thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).setPushNotificationConfig(any(SetTaskPushNotificationConfigRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(SetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testGetTaskPushNotificationConfig_MethodNameSetInContext() { + // Arrange - based on GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST from JsonMessages + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/pushNotificationConfig/get", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "metadata": {} + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + GetTaskPushNotificationConfigResponse mockResponse = mock(GetTaskPushNotificationConfigResponse.class); + when(mockJsonRpcHandler.getPushNotificationConfig(any(GetTaskPushNotificationConfigRequest.class), + any(ServerCallContext.class))).thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).getPushNotificationConfig(any(GetTaskPushNotificationConfigRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(GetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testListTaskPushNotificationConfig_MethodNameSetInContext() { + // Arrange - minimal valid JSON for list task push notification config + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/pushNotificationConfig/list", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64" + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + ListTaskPushNotificationConfigResponse mockResponse = mock(ListTaskPushNotificationConfigResponse.class); + when(mockJsonRpcHandler.listPushNotificationConfig(any(ListTaskPushNotificationConfigRequest.class), + any(ServerCallContext.class))).thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).listPushNotificationConfig(any(ListTaskPushNotificationConfigRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(ListTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testDeleteTaskPushNotificationConfig_MethodNameSetInContext() { + // Arrange - minimal valid JSON for delete task push notification config + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "method": "tasks/pushNotificationConfig/delete", + "params": { + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "pushNotificationConfigId": "config-456" + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + DeleteTaskPushNotificationConfigResponse mockResponse = mock(DeleteTaskPushNotificationConfigResponse.class); + when(mockJsonRpcHandler.deletePushNotificationConfig(any(DeleteTaskPushNotificationConfigRequest.class), + any(ServerCallContext.class))).thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).deletePushNotificationConfig(any(DeleteTaskPushNotificationConfigRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(DeleteTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testGetAuthenticatedExtendedCard_MethodNameSetInContext() { + // Arrange + String jsonRpcRequest = "{\"jsonrpc\":\"2.0\",\"method\":\"" + GetAuthenticatedExtendedCardRequest.METHOD + + "\",\"id\":1}"; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + GetAuthenticatedExtendedCardResponse mockResponse = mock(GetAuthenticatedExtendedCardResponse.class); + when(mockJsonRpcHandler.onGetAuthenticatedExtendedCardRequest( + any(GetAuthenticatedExtendedCardRequest.class), any(ServerCallContext.class))) + .thenReturn(mockResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onGetAuthenticatedExtendedCardRequest( + any(GetAuthenticatedExtendedCardRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(GetAuthenticatedExtendedCardRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + /** + * Helper method to set a field via reflection for testing purposes. + */ + private void setField(Object target, String fieldName, Object value) { + try { + var field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + } catch (Exception e) { + throw new RuntimeException("Failed to set field: " + fieldName, e); + } + } +} diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 3a7964560..5b0fecc72 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -87,6 +87,16 @@ rest-assured test + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index a2f3a9348..b923dde5c 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -1,7 +1,10 @@ package io.a2a.server.rest.quarkus; +import static io.a2a.transport.rest.context.RestContextKeys.HEADERS_KEY; +import static io.a2a.transport.rest.context.RestContextKeys.METHOD_NAME_KEY; import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.SERVER_SENT_EVENTS; import java.util.concurrent.Executor; import java.util.concurrent.Flow; @@ -42,6 +45,15 @@ import java.util.Set; import io.a2a.server.extensions.A2AExtensions; +import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskRequest; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.TaskResubscriptionRequest; import org.jspecify.annotations.Nullable; @Singleton @@ -65,7 +77,7 @@ public class A2AServerRoutes { @Route(regex = "^/v1/message:send$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void sendMessage(@Body String body, RoutingContext rc) { - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, SendMessageRequest.METHOD); HTTPRestResponse response = null; try { response = jsonRestHandler.sendMessage(body, context); @@ -78,7 +90,7 @@ public void sendMessage(@Body String body, RoutingContext rc) { @Route(regex = "^/v1/message:stream$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void sendMessageStreaming(@Body String body, RoutingContext rc) { - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, SendStreamingMessageRequest.METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { @@ -104,7 +116,7 @@ public void sendMessageStreaming(@Body String body, RoutingContext rc) { @Route(path = "/v1/tasks/:id", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void getTask(RoutingContext rc) { String taskId = rc.pathParam("id"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, GetTaskRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -128,7 +140,7 @@ public void getTask(RoutingContext rc) { @Route(regex = "^/v1/tasks/([^/]+):cancel$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void cancelTask(RoutingContext rc) { String taskId = rc.pathParam("param0"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, CancelTaskRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -161,7 +173,7 @@ private void sendResponse(RoutingContext rc, @Nullable HTTPRestResponse response @Route(regex = "^/v1/tasks/([^/]+):subscribe$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void resubscribeTask(RoutingContext rc) { String taskId = rc.pathParam("param0"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, TaskResubscriptionRequest.METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { @@ -191,7 +203,7 @@ public void resubscribeTask(RoutingContext rc) { @Route(path = "/v1/tasks/:id/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void setTaskPushNotificationConfiguration(@Body String body, RoutingContext rc) { String taskId = rc.pathParam("id"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, SetTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -210,7 +222,7 @@ public void setTaskPushNotificationConfiguration(@Body String body, RoutingConte public void getTaskPushNotificationConfiguration(RoutingContext rc) { String taskId = rc.pathParam("id"); String configId = rc.pathParam("configId"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, GetTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -228,7 +240,7 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) { @Route(path = "/v1/tasks/:id/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void listTaskPushNotificationConfigurations(RoutingContext rc) { String taskId = rc.pathParam("id"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, ListTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -247,7 +259,7 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { String taskId = rc.pathParam("id"); String configId = rc.pathParam("configId"); - ServerCallContext context = createCallContext(rc); + ServerCallContext context = createCallContext(rc, DeleteTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -294,8 +306,7 @@ static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { streamingMultiSseSupportSubscribedRunnable = runnable; } - private ServerCallContext createCallContext(RoutingContext rc) { - + private ServerCallContext createCallContext(RoutingContext rc, String jsonRpcMethodName) { if (callContextFactory.isUnsatisfied()) { User user; if (rc.user() == null) { @@ -328,7 +339,8 @@ String getUsername() { Map headers = new HashMap<>(); Set headerNames = rc.request().headers().names(); headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); - state.put("headers", headers); + state.put(HEADERS_KEY, headers); + state.put(METHOD_NAME_KEY, jsonRpcMethodName); // Extract requested extensions from X-A2A-Extensions header List extensionHeaderValues = rc.request().headers().getAll(A2AHeaders.X_A2A_EXTENSIONS); @@ -351,8 +363,8 @@ private MultiSseSupport() { private static void initialize(HttpServerResponse response) { if (response.bytesWritten() == 0) { MultiMap headers = response.headers(); - if (headers.get("content-type") == null) { - headers.set("content-type", "text/event-stream"); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); } response.setChunked(true); } @@ -426,8 +438,8 @@ public Buffer apply(Object o) { private static void endOfStream(HttpServerResponse response) { if (response.bytesWritten() == 0) { // No item MultiMap headers = response.headers(); - if (headers.get("content-type") == null) { - headers.set("content-type", "text/event-stream"); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); } } response.end(); diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java new file mode 100644 index 000000000..83d54a40c --- /dev/null +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -0,0 +1,307 @@ +package io.a2a.server.rest.quarkus; + +import static io.a2a.transport.rest.context.RestContextKeys.METHOD_NAME_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.Executor; + +import jakarta.enterprise.inject.Instance; + +import io.a2a.server.ServerCallContext; +import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskRequest; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.TaskResubscriptionRequest; +import io.a2a.transport.rest.handler.RestHandler; +import io.a2a.transport.rest.handler.RestHandler.HTTPRestResponse; +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RequestBody; +import io.vertx.ext.web.RoutingContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +/** + * Unit test for A2AServerRoutes that verifies the method names are properly set + * in the ServerCallContext for all route handlers. + */ +public class A2AServerRoutesTest { + + private A2AServerRoutes routes; + private RestHandler mockRestHandler; + private Executor mockExecutor; + private Instance mockCallContextFactory; + private RoutingContext mockRoutingContext; + private HttpServerRequest mockRequest; + private HttpServerResponse mockResponse; + private MultiMap mockHeaders; + private MultiMap mockParams; + private RequestBody mockRequestBody; + + @BeforeEach + public void setUp() { + routes = new A2AServerRoutes(); + mockRestHandler = mock(RestHandler.class); + mockExecutor = mock(Executor.class); + mockCallContextFactory = mock(Instance.class); + mockRoutingContext = mock(RoutingContext.class); + mockRequest = mock(HttpServerRequest.class); + mockResponse = mock(HttpServerResponse.class); + mockHeaders = MultiMap.caseInsensitiveMultiMap(); + mockParams = MultiMap.caseInsensitiveMultiMap(); + mockRequestBody = mock(RequestBody.class); + + // Inject mocks via reflection since we can't use @InjectMocks + setField(routes, "jsonRestHandler", mockRestHandler); + setField(routes, "executor", mockExecutor); + setField(routes, "callContextFactory", mockCallContextFactory); + + // Setup common mock behavior + when(mockCallContextFactory.isUnsatisfied()).thenReturn(true); + when(mockRoutingContext.request()).thenReturn(mockRequest); + when(mockRoutingContext.response()).thenReturn(mockResponse); + when(mockRoutingContext.user()).thenReturn(null); + when(mockRequest.headers()).thenReturn(mockHeaders); + when(mockRequest.params()).thenReturn(mockParams); + when(mockRoutingContext.body()).thenReturn(mockRequestBody); + when(mockRequestBody.asString()).thenReturn("{}"); + when(mockResponse.setStatusCode(any(Integer.class))).thenReturn(mockResponse); + when(mockResponse.putHeader(any(CharSequence.class), any(CharSequence.class))).thenReturn(mockResponse); + when(mockResponse.end()).thenReturn(Future.succeededFuture()); + when(mockResponse.end(anyString())).thenReturn(Future.succeededFuture()); + } + + @Test + public void testSendMessage_MethodNameSetInContext() { + // Arrange + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.sendMessage(anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.sendMessage("{}", mockRoutingContext); + + // Assert + verify(mockRestHandler).sendMessage(eq("{}"), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(SendMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testSendMessageStreaming_MethodNameSetInContext() { + // Arrange + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.sendStreamingMessage(anyString(), any(ServerCallContext.class))) + .thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.sendMessageStreaming("{}", mockRoutingContext); + + // Assert + verify(mockRestHandler).sendStreamingMessage(eq("{}"), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(SendStreamingMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testGetTask_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{test:value}"); + when(mockRestHandler.getTask(anyString(), any(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.getTask(mockRoutingContext); + + // Assert + verify(mockRestHandler).getTask(eq("task123"), eq(null), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(GetTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testCancelTask_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("param0")).thenReturn("task123"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.cancelTask(anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.cancelTask(mockRoutingContext); + + // Assert + verify(mockRestHandler).cancelTask(eq("task123"), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(CancelTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testResubscribeTask_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("param0")).thenReturn("task123"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.resubscribeTask(anyString(), any(ServerCallContext.class))) + .thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.resubscribeTask(mockRoutingContext); + + // Assert + verify(mockRestHandler).resubscribeTask(eq("task123"), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(TaskResubscriptionRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testSetTaskPushNotificationConfiguration_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.setTaskPushNotificationConfiguration(anyString(), anyString(), + any(ServerCallContext.class))).thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.setTaskPushNotificationConfiguration("{}", mockRoutingContext); + + // Assert + verify(mockRestHandler).setTaskPushNotificationConfiguration(eq("task123"), eq("{}"), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(SetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("configId")).thenReturn("config456"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.getTaskPushNotificationConfiguration(anyString(), anyString(), + any(ServerCallContext.class))).thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.getTaskPushNotificationConfiguration(mockRoutingContext); + + // Assert + verify(mockRestHandler).getTaskPushNotificationConfiguration(eq("task123"), eq("config456"), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(GetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.listTaskPushNotificationConfigurations(anyString(), any(ServerCallContext.class))) + .thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.listTaskPushNotificationConfigurations(mockRoutingContext); + + // Assert + verify(mockRestHandler).listTaskPushNotificationConfigurations(eq("task123"), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(ListTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + @Test + public void testDeleteTaskPushNotificationConfiguration_MethodNameSetInContext() { + // Arrange + when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("configId")).thenReturn("config456"); + HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); + when(mockHttpResponse.getStatusCode()).thenReturn(200); + when(mockHttpResponse.getContentType()).thenReturn("application/json"); + when(mockHttpResponse.getBody()).thenReturn("{}"); + when(mockRestHandler.deleteTaskPushNotificationConfiguration(anyString(), anyString(), + any(ServerCallContext.class))).thenReturn(mockHttpResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.deleteTaskPushNotificationConfiguration(mockRoutingContext); + + // Assert + verify(mockRestHandler).deleteTaskPushNotificationConfiguration(eq("task123"), eq("config456"), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals(DeleteTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + } + + /** + * Helper method to set a field via reflection for testing purposes. + */ + private void setField(Object target, String fieldName, Object value) { + try { + var field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + } catch (Exception e) { + throw new RuntimeException("Failed to set field: " + fieldName, e); + } + } +} diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java new file mode 100644 index 000000000..015e3860a --- /dev/null +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java @@ -0,0 +1,24 @@ +package io.a2a.transport.jsonrpc.context; + +/** + * Shared JSON-RPC context keys for A2A protocol data. + * + * These keys provide access to JSON-RPC context information, + * enabling rich context access in service method implementations. + */ +public final class JSONRPCContextKeys { + + /** + * Context key for storing the headers. + */ + public static final String HEADERS_KEY = "headers"; + + /** + * Context key for storing the method name being called. + */ + public static final String METHOD_NAME_KEY = "method"; + + private JSONRPCContextKeys() { + // Utility class + } +} diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java b/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java new file mode 100644 index 000000000..de35ca0cf --- /dev/null +++ b/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java @@ -0,0 +1,24 @@ +package io.a2a.transport.rest.context; + +/** + * Shared REST context keys for A2A protocol data. + * + * These keys provide access to REST context information, + * enabling rich context access in service method implementations. + */ +public final class RestContextKeys { + + /** + * Context key for storing the headers. + */ + public static final String HEADERS_KEY = "headers"; + + /** + * Context key for storing the method name being called. + */ + public static final String METHOD_NAME_KEY = "method"; + + private RestContextKeys() { + // Utility class + } +} From 800366e6cc73e9afa117f356a94909729022a423 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 24 Oct 2025 11:22:27 +0200 Subject: [PATCH 172/493] chore: fixing issue #390 by adding copy builders to TaskArtifactUpdateEvent and TaskStatusUpdateEvent. (#391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #390 🦕 Signed-off-by: Emmanuel Hugonnet --- .../java/io/a2a/spec/TaskArtifactUpdateEvent.java | 12 ++++++++++++ .../main/java/io/a2a/spec/TaskStatusUpdateEvent.java | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index fec66caa8..1e1cad947 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -94,6 +94,18 @@ public static class Builder { private Boolean lastChunk; private Map metadata; + public Builder() { + } + + public Builder(TaskArtifactUpdateEvent existingTaskArtifactUpdateEvent) { + this.taskId = existingTaskArtifactUpdateEvent.taskId; + this.artifact = existingTaskArtifactUpdateEvent.artifact; + this.contextId = existingTaskArtifactUpdateEvent.contextId; + this.append = existingTaskArtifactUpdateEvent.append; + this.lastChunk = existingTaskArtifactUpdateEvent.lastChunk; + this.metadata = existingTaskArtifactUpdateEvent.metadata; + } + public Builder taskId(String taskId) { this.taskId = taskId; return this; diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 478ec1946..25e2cd170 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -86,6 +86,16 @@ public static class Builder { private boolean isFinal; private Map metadata; + public Builder() { + } + + public Builder(TaskStatusUpdateEvent existingTaskStatusUpdateEvent) { + this.taskId = existingTaskStatusUpdateEvent.taskId; + this.status = existingTaskStatusUpdateEvent.status; + this.contextId = existingTaskStatusUpdateEvent.contextId; + this.isFinal = existingTaskStatusUpdateEvent.isFinal; + this.metadata = existingTaskStatusUpdateEvent.metadata; + } public Builder taskId(String id) { this.taskId = id; return this; From 6f1b5b256a62e06f3f9b13cdd646a2595120ee5e Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 27 Oct 2025 14:54:25 +0000 Subject: [PATCH 173/493] feat: Add cloud deployment example with event replication (#389) Add Kubernetes deployment example demonstrating multi-instance A2A agents with database persistence and Kafka-based event replication. Fixes https://github.com/a2aproject/a2a-java/issues/281 and https://github.com/a2aproject/a2a-java/issues/373 --------- Co-authored-by: Farah Juma --- .../workflows/cloud-deployment-example.yml | 101 +++ examples/cloud-deployment/README.md | 579 ++++++++++++++++++ .../cloud-deployment/k8s/00-namespace.yaml | 4 + .../cloud-deployment/k8s/01-postgres.yaml | 74 +++ examples/cloud-deployment/k8s/02-kafka.yaml | 65 ++ .../cloud-deployment/k8s/03-kafka-topic.yaml | 16 + .../k8s/04-agent-configmap.yaml | 11 + .../k8s/05-agent-deployment.yaml | 96 +++ examples/cloud-deployment/kind-config.yaml | 24 + examples/cloud-deployment/scripts/cleanup.sh | 82 +++ examples/cloud-deployment/scripts/deploy.sh | 288 +++++++++ examples/cloud-deployment/scripts/verify.sh | 86 +++ examples/cloud-deployment/server/Dockerfile | 22 + examples/cloud-deployment/server/kill | 1 + examples/cloud-deployment/server/pom.xml | 140 +++++ .../cloud/CloudAgentCardProducer.java | 57 ++ .../cloud/CloudAgentExecutorProducer.java | 132 ++++ .../src/main/resources/META-INF/beans.xml | 5 + .../src/main/resources/application.properties | 50 ++ .../examples/cloud/A2ACloudExampleClient.java | 421 +++++++++++++ examples/helloworld/pom.xml | 2 +- pom.xml | 1 + 22 files changed, 2256 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/cloud-deployment-example.yml create mode 100644 examples/cloud-deployment/README.md create mode 100644 examples/cloud-deployment/k8s/00-namespace.yaml create mode 100644 examples/cloud-deployment/k8s/01-postgres.yaml create mode 100644 examples/cloud-deployment/k8s/02-kafka.yaml create mode 100644 examples/cloud-deployment/k8s/03-kafka-topic.yaml create mode 100644 examples/cloud-deployment/k8s/04-agent-configmap.yaml create mode 100644 examples/cloud-deployment/k8s/05-agent-deployment.yaml create mode 100644 examples/cloud-deployment/kind-config.yaml create mode 100755 examples/cloud-deployment/scripts/cleanup.sh create mode 100755 examples/cloud-deployment/scripts/deploy.sh create mode 100755 examples/cloud-deployment/scripts/verify.sh create mode 100644 examples/cloud-deployment/server/Dockerfile create mode 100644 examples/cloud-deployment/server/kill create mode 100644 examples/cloud-deployment/server/pom.xml create mode 100644 examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java create mode 100644 examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java create mode 100644 examples/cloud-deployment/server/src/main/resources/META-INF/beans.xml create mode 100644 examples/cloud-deployment/server/src/main/resources/application.properties create mode 100644 examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java diff --git a/.github/workflows/cloud-deployment-example.yml b/.github/workflows/cloud-deployment-example.yml new file mode 100644 index 000000000..f52ea5111 --- /dev/null +++ b/.github/workflows/cloud-deployment-example.yml @@ -0,0 +1,101 @@ +name: Cloud Deployment Example Test + +on: + push: + pull_request: + workflow_dispatch: + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + test-cloud-deployment: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Install Kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + kind version + + - name: Build project + run: mvn -B clean install -DskipTests -f pom.xml + + - name: Deploy with Kind + working-directory: examples/cloud-deployment/scripts + run: | + chmod +x deploy.sh + ./deploy.sh --container-tool docker + + - name: Verify deployment + working-directory: examples/cloud-deployment/scripts + run: | + chmod +x verify.sh + ./verify.sh + + - name: Verify agent card is accessible + run: | + echo "Testing agent card endpoint at http://localhost:8080/.well-known/agent-card.json" + curl -f http://localhost:8080/.well-known/agent-card.json || (echo "Agent card not accessible" && exit 1) + + - name: Run test client + working-directory: examples/cloud-deployment/server + run: | + mvn test-compile exec:java \ + -Dexec.mainClass="io.a2a.examples.cloud.A2ACloudExampleClient" \ + -Dexec.classpathScope=test \ + -Dagent.url=http://localhost:8080 + + - name: Show diagnostics on failure + if: failure() + run: | + echo "=== Agent Pod Status ===" + kubectl get pods -n a2a-demo -l app=a2a-agent -o wide + + echo "" + echo "=== Agent Pod Descriptions ===" + for pod in $(kubectl get pods -n a2a-demo -l app=a2a-agent -o jsonpath='{.items[*].metadata.name}'); do + echo "--- Pod: $pod ---" + kubectl describe pod $pod -n a2a-demo | tail -30 + done + + echo "" + echo "=== Recent Events ===" + kubectl get events -n a2a-demo --sort-by='.lastTimestamp' | tail -20 + + echo "" + echo "=== Agent Pod Logs ===" + for pod in $(kubectl get pods -n a2a-demo -l app=a2a-agent -o jsonpath='{.items[*].metadata.name}'); do + echo "--- Logs for $pod ---" + kubectl logs -n a2a-demo $pod --tail=100 || true + done + + echo "" + echo "=== PostgreSQL Logs ===" + kubectl logs -n a2a-demo postgres-0 --tail=50 || true + + echo "" + echo "=== Kafka Logs ===" + kubectl logs -n kafka -l strimzi.io/cluster=a2a-kafka --tail=50 || true + + - name: Cleanup + if: always() + run: | + cd examples/cloud-deployment/scripts + echo "y" | ./cleanup.sh --container-tool docker || true + + diff --git a/examples/cloud-deployment/README.md b/examples/cloud-deployment/README.md new file mode 100644 index 000000000..989ba173b --- /dev/null +++ b/examples/cloud-deployment/README.md @@ -0,0 +1,579 @@ +# A2A Cloud Deployment Example + +This example demonstrates deploying an A2A agent to Kubernetes with: +- **Multiple pods** (2 replicas) for load balancing +- **PostgreSQL database** for persistent task storage +- **Kafka event replication** for cross-pod event streaming +- **JSON-RPC transport** for client-server communication + +Note that the aim of this example is just to demonstrate how to set up a2a-java in a cloud environment. Hence, it doesn't do anything with an LLM, but shows that it can be configured to work in a cloud, or other distributed, environment. + +## Architecture + +``` + localhost:8080 + (extraPortMappings) + ▲ + │ +┌───────────────────────────────────┼───────────────────────┐ +│ Kubernetes Cluster (Kind) │ │ +│ │ │ +│ ┌───────────▼──────────┐ │ +│ │ Service (NodePort) │ │ +│ │ Round-Robin │ │ +│ └───────────┬──────────┘ │ +│ │ │ +│ ┌───────────┴──────────┐ │ +│ ▼ ▼ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ A2A Agent │ │ A2A Agent │ │ +│ │ Pod 1 │ │ Pod 2 │ │ +│ └────┬────────┬───┘ └───┬────────┬────┘ │ +│ │ │ │ │ │ +│ │ └───────────┘ │ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌────────────────┐ ┌─────────────────┐ │ +│ │ PostgreSQL DB │ │ Kafka │ │ +│ │ (Task Store) │ │ (Queue Manager)│ │ +│ └────────────────┘ └─────────────────┘ │ +│ ▲ ▲ │ +│ │ │ │ +│ Task Persistence Event Replication │ +└───────────────────────────────────────────────────────────┘ + ▲ + │ + ┌──────┴──────┐ + │ Client │ + │ (External) │ + └─────────────┘ +``` + +## What This Example Demonstrates + +1. **Load Balancing**: Messages sent to the service are distributed across pods via round-robin +2. **Event Replication**: Events from one pod are replicated to other pods via Kafka +3. **Task Persistence**: Task state is stored in PostgreSQL and shared across all pods +4. **Streaming Subscriptions**: Clients can subscribe to task updates and receive events from any pod +5. **Fire-and-Forget Pattern**: Tasks remain in WORKING state until explicitly completed +6. **Command-Based Protocol**: Simple message protocol ("start", "process", "complete") + +## Prerequisites + +- **Kind** (Kubernetes IN Docker) v0.20+ +- **kubectl** (v1.27+) +- **Maven** (3.8+) +- **Java** 17+ +- **Container runtime**: Docker or Podman + +## Quick Start + +### 1. Install Prerequisites + +**Install Kind:** +See https://kind.sigs.k8s.io/docs/user/quick-start/ for installation instructions. + +**Install kubectl:** +See https://kubernetes.io/docs/tasks/tools/ for installation instructions. + +### 2. Deploy the Stack + +The deployment script will automatically create the Kind cluster and deploy all components: + +```bash +cd scripts +./deploy.sh +``` + +**If using Podman instead of Docker:** +```bash +./deploy.sh --container-tool podman +``` + +Note that using Kind with Podman on Linux may have some occasional issues due to Kind's experimental support for Podman. In our testing, a reboot normally solves this. + +The script will: +- Create Kind cluster with local registry support (if not already exists) +- Set up local container registry (localhost:5001) +- Install Strimzi Kafka operator +- Deploy PostgreSQL +- Deploy Kafka cluster (using KRaft mode) +- Build and deploy the A2A agent (2 pods) + +**Note:** You don't need to manually create the Kind cluster - the script handles everything. + +### 3. Verify Deployment + +```bash +./verify.sh +``` + +Expected output: +``` +✓ Namespace 'a2a-demo' exists +✓ PostgreSQL is running +✓ Kafka is ready +✓ Agent pods are running (2/2 ready) +✓ Agent service exists +``` + +### 4. Test Multi-Pod Behavior + +#### Understanding the NodePort Setup + +The agent service uses **NodePort** with Kind **extraPortMappings** to expose the service: + +- Kind maps **host port 8080** → **node port 30080** (configured in `kind-config.yaml`) +- Kubernetes Service maps **NodePort 30080** → **pod port 8080** (configured in `k8s/05-agent-deployment.yaml`) +- Result: Access the agent at **http://localhost:8080** from your host machine + +This approach provides the same round-robin load balancing as a real LoadBalancer but works consistently across all platforms (macOS, Linux, Windows, and CI environments like GitHub Actions). + +#### Run the Test Client + +```bash +cd ../server +mvn test-compile exec:java \ + -Dexec.mainClass="io.a2a.examples.cloud.A2ACloudExampleClient" \ + -Dexec.classpathScope=test \ + -Dagent.url="http://localhost:8080" +``` + +Expected output: +``` +============================================= +A2A Cloud Deployment Example Client +============================================= + +Agent URL: http://localhost:8080 +Process messages: 8 +Message interval: 1500ms + +Fetching agent card... +✓ Agent: Cloud Deployment Demo Agent +✓ Description: Demonstrates A2A multi-pod deployment with Kafka event replication, PostgreSQL persistence, and round-robin load balancing across Kubernetes pods + +Client task ID: cloud-test-1234567890 + +Step 1: Sending 'start' to create task... +✓ Task created: + State: WORKING + +Step 2: Subscribing to task for streaming updates... +✓ Subscribed to task updates + Artifact #1: Started by a2a-agent-7b8f9c-abc12 + → Pod: a2a-agent-7b8f9c-abc12 (Total unique pods: 1) + +Step 3: Sending 8 'process' messages (interval: 1500ms)... +-------------------------------------------- +✓ Process message 1 sent + Artifact #2: Processed by a2a-agent-7b8f9c-xyz34 + → Pod: a2a-agent-7b8f9c-xyz34 (Total unique pods: 2) +✓ Process message 2 sent + Artifact #3: Processed by a2a-agent-7b8f9c-abc12 + → Pod: a2a-agent-7b8f9c-abc12 (Total unique pods: 2) +✓ Process message 3 sent + Artifact #4: Processed by a2a-agent-7b8f9c-xyz34 + → Pod: a2a-agent-7b8f9c-xyz34 (Total unique pods: 2) +... + +Waiting for process artifacts to arrive... + +Step 4: Sending 'complete' to finalize task... +✓ Complete message sent, task state: COMPLETED + Artifact #10: Completed by a2a-agent-7b8f9c-abc12 + +Waiting for task to complete... + Task reached final state: COMPLETED + +============================================= +Test Results +============================================= +Total artifacts received: 10 +Unique pods observed: 2 +Pod names: [a2a-agent-7b8f9c-abc12, a2a-agent-7b8f9c-xyz34] + +✓ TEST PASSED - Successfully demonstrated multi-pod processing! + Messages were handled by 2 different pods. + This proves that: + - Load balancing is working (round-robin across pods) + - Event replication is working (subscriber sees events from all pods) + - Database persistence is working (task state shared across pods) +``` + +## How It Works + +### Agent Implementation + +The agent (`CloudAgentExecutorProducer`) implements a command-based protocol: + +```java +@Override +public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater updater = new TaskUpdater(context, eventQueue); + String messageText = extractTextFromMessage(context.getMessage()).trim().toLowerCase(); + + // Get pod name from Kubernetes downward API + String podName = System.getenv("POD_NAME"); + + if ("complete".equals(messageText)) { + // Completion trigger - add final artifact and complete + String artifactText = "Completed by " + podName; + List> parts = List.of(new TextPart(artifactText, null)); + updater.addArtifact(parts); + updater.complete(); // Transition to COMPLETED state + } else if (context.getTask() == null) { + // Initial "start" message - create task in SUBMITTED → WORKING state + updater.submit(); + updater.startWork(); + String artifactText = "Started by " + podName; + List> parts = List.of(new TextPart(artifactText, null)); + updater.addArtifact(parts); + } else { + // Subsequent "process" messages - add artifacts (fire-and-forget, stays WORKING) + String artifactText = "Processed by " + podName; + List> parts = List.of(new TextPart(artifactText, null)); + updater.addArtifact(parts); + } +} +``` + +**Message Protocol**: +- `"start"`: Initialize task (SUBMITTED → WORKING), adds "Started by {pod-name}" +- `"process"`: Add artifact "Processed by {pod-name}" (fire-and-forget, stays WORKING) +- `"complete"`: Add artifact "Completed by {pod-name}" and transition to COMPLETED + +### Cloud-Native Components + +1. **Database Persistence** (`JpaDatabaseTaskStore`): + - Tasks are stored in PostgreSQL + - All pods read/write to the same database + - Ensures task state is consistent across pods. + + More information about `JpaDatabaseTaskStore` can be found [here](../../extras/task-store-database-jpa/README.md) + + +2. **Event Replication** (`ReplicatedQueueManager` + `ReactiveMessagingReplicationStrategy`): + - Events are published to Kafka topic `a2a-replicated-events`. + - All pods subscribe to the same topic + - Events from pod A are replicated to pod B's queue + + More information about `ReplicatedQueueManager` and `ReactiveMessagingReplicationStrategy` can be found [here](../../extras/queue-manager-replicated/README.md) + + +3. **Configuration** (`application.properties`): + - Database URL, credentials + - Kafka bootstrap servers + - Reactive Messaging channel configuration + +### Load Balancing Flow + +``` +Client sends "start"/"process"/"complete" message + ↓ +Kubernetes Service (round-robin) + ↓ +Pod A or Pod B (alternates) + ↓ +AgentExecutor processes command + ↓ +Enqueues artifact with pod name ("Started by"/"Processed by"/"Completed by") + ↓ +Event published to Kafka topic (a2a-replicated-events) + ↓ +All pods receive replicated event + ↓ +Streaming subscriber receives artifact (regardless of which pod sent it) +``` + +## Configuration + +### Environment Variables + +The following environment variables are configured via ConfigMap (`k8s/04-agent-configmap.yaml`): + +| Variable | Description | Example | +|----------|-------------|---------| +| `POD_NAME` | Pod name (from downward API) | `a2a-agent-7b8f9c-abc12` | +| `DATABASE_URL` | PostgreSQL JDBC URL | `jdbc:postgresql://postgres.a2a-demo.svc.cluster.local:5432/a2a` | +| `DATABASE_USER` | Database username | `a2a` | +| `DATABASE_PASSWORD` | Database password | `a2a` | +| `KAFKA_BOOTSTRAP_SERVERS` | Kafka brokers | `a2a-kafka-kafka-bootstrap.kafka.svc.cluster.local:9092` | +| `AGENT_URL` | Public agent URL | `http://localhost:8080` | + +### Scaling + +To change the number of agent pods, edit `k8s/05-agent-deployment.yaml`: + +```yaml +spec: + replicas: 2 # Change to desired number +``` + +Then apply: +```bash +kubectl apply -f k8s/05-agent-deployment.yaml +``` + +## Troubleshooting + +### Pods Not Starting + +**Check pod status:** +```bash +kubectl get pods -n a2a-demo +kubectl describe pod -n a2a-demo +kubectl logs -n a2a-demo +``` + +**Common issues:** +- **ImagePullBackOff**: Image not pushed to local registry + - Solution: Ensure registry is running and push completed successfully + - Check: `curl http://localhost:5001/v2/_catalog` should list the image +- **CrashLoopBackOff**: Application startup failure + - Check logs: `kubectl logs -n a2a-demo` + - Common causes: Database not ready, Kafka not ready + +### Registry Issues + +**Registry not accessible:** + +```bash +# Verify registry is running +docker ps | grep kind-registry +# or for Podman: +podman ps | grep kind-registry + +# Verify registry is accessible +curl http://localhost:5001/v2/ +# Should return: {} + +# List images in registry +curl http://localhost:5001/v2/_catalog +``` + +**Image push failures:** +- Check registry container is running +- For Podman: Ensure you use `--tls-verify=false` flag when pushing +- Restart registry if needed: `docker/podman stop kind-registry && docker/podman start kind-registry` + +### Database Connection Failures + +**Check PostgreSQL status:** +```bash +kubectl get pods -n a2a-demo -l app=postgres +kubectl logs -n a2a-demo +``` + +**Test connection from agent pod:** +```bash +kubectl exec -it -n a2a-demo -- bash +# Inside pod: +curl telnet://postgres.a2a-demo.svc.cluster.local:5432 +``` + +**Common issues:** +- PostgreSQL pod not ready: Wait for it to become Ready +- Wrong credentials: Check ConfigMap values match PostgreSQL config + +### Kafka Connection Failures + +**Check Kafka status:** +```bash +kubectl get kafka -n a2a-demo +kubectl get pods -n a2a-demo -l strimzi.io/cluster=a2a-kafka +``` + +**Common issues:** +- Kafka not ready: Kafka takes 2-5 minutes to start fully + - Wait for `kubectl wait --for=condition=Ready kafka/a2a-kafka -n a2a-demo` +- Topic not created: Kafka auto-creates topics on first publish + +### Test Client Failures + +**Connection refused errors:** +```bash +# Verify agent card is accessible via NodePort +curl http://localhost:8080/.well-known/agent-card.json + +# If this fails, check: +# 1. Agent pods are ready +kubectl get pods -n a2a-demo -l app=a2a-agent + +# 2. Service exists and has correct NodePort +kubectl get svc a2a-agent-service -n a2a-demo + +# Expected output: +# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +# a2a-agent-service NodePort 10.96.123.45 8080:30080/TCP 5m + +# 3. Verify Kind extraPortMappings are configured +docker ps | grep kind-control-plane +# or for Podman: +podman ps | grep kind-control-plane +``` + +**Only seeing 1 pod:** +- Check both pods are Running: `kubectl get pods -n a2a-demo -l app=a2a-agent` +- The test client creates fresh HTTP connections for each message to force load balancing +- If still seeing 1 pod, check service sessionAffinity is set to `None` (see `k8s/05-agent-deployment.yaml`) +- Try increasing PROCESS_MESSAGE_COUNT in test client for more samples + +### Strimzi Installation Issues + +**Strimzi operator not ready:** +```bash +kubectl get pods -n kafka +kubectl logs -n kafka +``` + +**CRD not found:** +```bash +# Check if Kafka CRD is installed +kubectl get crd kafkas.kafka.strimzi.io + +# If missing, reinstall Strimzi +kubectl create -f 'https://strimzi.io/install/latest?namespace=kafka' -n kafka +``` + +### Kind Resource Issues + +**Insufficient resources:** +Kind uses your Docker/Podman resources. Increase Docker Desktop memory/CPU limits if needed. + +**Disk space:** +```bash +# Check disk usage inside Kind node +docker exec -it kind-control-plane df -h +# or for Podman: +podman exec -it kind-control-plane df -h + +# Clean up old images +docker system prune -a # or: podman system prune -a +``` + +## Cleanup + +To remove all deployed resources: + +```bash +cd scripts +./cleanup.sh +``` + +This will delete: +- A2A agent deployment and service +- Kafka cluster +- PostgreSQL +- Namespace `a2a-demo` + +To also remove Strimzi operator: +```bash +kubectl delete namespace kafka +``` + +To delete the Kind cluster: +```bash +kind delete cluster +``` + +### Complete Clean Slate + +For a completely fresh start (useful for testing from scratch): + +```bash +# Delete Kind cluster +kind delete cluster + +# Remove local registry container +docker stop kind-registry && docker rm kind-registry +# or for Podman: +podman stop kind-registry && podman rm kind-registry + +# Optional: Clean up container images +docker system prune -a # or: podman system prune -a +``` + +Then re-run `./deploy.sh` to start fresh. + +## Project Structure + +``` +cloud-deployment/ +├── server/ +│ ├── src/main/java/io/a2a/examples/cloud/ +│ │ ├── CloudAgentCardProducer.java # Agent card configuration +│ │ └── CloudAgentExecutorProducer.java # Agent business logic +│ ├── src/main/resources/ +│ │ └── application.properties # Application configuration +│ ├── src/test/java/io/a2a/examples/cloud/ +│ │ └── A2ACloudExampleClient.java # Test client +│ ├── pom.xml # Maven dependencies +│ └── Dockerfile # Container image +├── k8s/ +│ ├── 00-namespace.yaml # Kubernetes namespace +│ ├── 01-postgres.yaml # PostgreSQL deployment +│ ├── 02-kafka.yaml # Strimzi Kafka cluster +│ ├── 03-kafka-topic.yaml # Kafka topic +│ ├── 04-agent-configmap.yaml # Configuration +│ └── 05-agent-deployment.yaml # Agent deployment + service +├── scripts/ +│ ├── deploy.sh # Automated deployment +│ ├── verify.sh # Health checks +│ └── cleanup.sh # Resource cleanup +└── README.md # This file +``` + +## Key Dependencies + +From `pom.xml`: + +```xml + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + + + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + + + + + io.quarkus + quarkus-messaging-kafka + +``` + +## Next Steps + +- **Production deployment**: Use a real Kubernetes cluster (e.g. OpenShift) with proper LoadBalancer or Ingress +- **Secrets management**: Use Kubernetes Secrets for credentials +- **Monitoring**: Add Prometheus metrics and Grafana dashboards +- **Autoscaling**: Configure Horizontal Pod Autoscaler based on CPU/memory +- **Persistent storage**: Use PersistentVolumes for PostgreSQL in production +- **TLS**: Enable TLS for Kafka and PostgreSQL connections +- **Resource limits**: Fine-tune CPU/memory requests and limits + +## References + +- [A2A Protocol Specification](https://github.com/a2aproject/a2a) +- [Kind Quick Start](https://kind.sigs.k8s.io/docs/user/quick-start/) +- [Kind Local Registry](https://kind.sigs.k8s.io/docs/user/local-registry/) +- [Kind extraPortMappings](https://kind.sigs.k8s.io/docs/user/configuration/#extra-port-mappings) +- [Strimzi Kafka Operator](https://strimzi.io/) +- [Quarkus Reactive Messaging](https://quarkus.io/guides/kafka) +- [Kubernetes Downward API](https://kubernetes.io/docs/concepts/workloads/pods/downward-api/) diff --git a/examples/cloud-deployment/k8s/00-namespace.yaml b/examples/cloud-deployment/k8s/00-namespace.yaml new file mode 100644 index 000000000..f232b6816 --- /dev/null +++ b/examples/cloud-deployment/k8s/00-namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: a2a-demo diff --git a/examples/cloud-deployment/k8s/01-postgres.yaml b/examples/cloud-deployment/k8s/01-postgres.yaml new file mode 100644 index 000000000..b81e94d54 --- /dev/null +++ b/examples/cloud-deployment/k8s/01-postgres.yaml @@ -0,0 +1,74 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres-config + namespace: a2a-demo +data: + POSTGRES_DB: a2a + POSTGRES_USER: a2a + POSTGRES_PASSWORD: a2a +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: a2a-demo +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 + clusterIP: None +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres + namespace: a2a-demo +spec: + serviceName: postgres + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: mirror.gcr.io/postgres:16 + ports: + - containerPort: 5432 + name: postgres + envFrom: + - configMapRef: + name: postgres-config + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + readinessProbe: + exec: + command: + - pg_isready + - -U + - a2a + initialDelaySeconds: 5 + periodSeconds: 5 + volumeClaimTemplates: + - metadata: + name: postgres-storage + spec: + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: 1Gi diff --git a/examples/cloud-deployment/k8s/02-kafka.yaml b/examples/cloud-deployment/k8s/02-kafka.yaml new file mode 100644 index 000000000..044aeb1ac --- /dev/null +++ b/examples/cloud-deployment/k8s/02-kafka.yaml @@ -0,0 +1,65 @@ +--- +# KafkaNodePool for KRaft mode +apiVersion: kafka.strimzi.io/v1beta2 +kind: KafkaNodePool +metadata: + name: broker + namespace: kafka + labels: + strimzi.io/cluster: a2a-kafka +spec: + replicas: 1 + roles: + - broker + - controller + storage: + type: ephemeral + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" +--- +# Kafka cluster +apiVersion: kafka.strimzi.io/v1beta2 +kind: Kafka +metadata: + name: a2a-kafka + namespace: kafka + annotations: + strimzi.io/node-pools: enabled + strimzi.io/kraft: enabled +spec: + kafka: + version: 4.0.0 + metadataVersion: 4.0-IV0 + listeners: + - name: plain + port: 9092 + type: internal + tls: false + config: + offsets.topic.replication.factor: 1 + transaction.state.log.replication.factor: 1 + transaction.state.log.min.isr: 1 + default.replication.factor: 1 + min.insync.replicas: 1 + entityOperator: + topicOperator: + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "200m" + userOperator: + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "200m" diff --git a/examples/cloud-deployment/k8s/03-kafka-topic.yaml b/examples/cloud-deployment/k8s/03-kafka-topic.yaml new file mode 100644 index 000000000..2fa7fca9c --- /dev/null +++ b/examples/cloud-deployment/k8s/03-kafka-topic.yaml @@ -0,0 +1,16 @@ +--- +# Kafka topic for A2A event replication +apiVersion: kafka.strimzi.io/v1beta2 +kind: KafkaTopic +metadata: + name: a2a-replicated-events + namespace: kafka + labels: + strimzi.io/cluster: a2a-kafka +spec: + partitions: 3 + replicas: 1 + config: + retention.ms: 3600000 # 1 hour retention + segment.bytes: 1073741824 + cleanup.policy: delete diff --git a/examples/cloud-deployment/k8s/04-agent-configmap.yaml b/examples/cloud-deployment/k8s/04-agent-configmap.yaml new file mode 100644 index 000000000..4c7195d82 --- /dev/null +++ b/examples/cloud-deployment/k8s/04-agent-configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: agent-config + namespace: a2a-demo +data: + DATABASE_URL: "jdbc:postgresql://postgres.a2a-demo.svc.cluster.local:5432/a2a" + DATABASE_USER: "a2a" + DATABASE_PASSWORD: "a2a" + KAFKA_BOOTSTRAP_SERVERS: "a2a-kafka-kafka-bootstrap.kafka.svc.cluster.local:9092" + AGENT_URL: "http://localhost:8080" diff --git a/examples/cloud-deployment/k8s/05-agent-deployment.yaml b/examples/cloud-deployment/k8s/05-agent-deployment.yaml new file mode 100644 index 000000000..27d141ad2 --- /dev/null +++ b/examples/cloud-deployment/k8s/05-agent-deployment.yaml @@ -0,0 +1,96 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: a2a-agent + namespace: a2a-demo + labels: + app: a2a-agent +spec: + replicas: 2 + selector: + matchLabels: + app: a2a-agent + template: + metadata: + labels: + app: a2a-agent + spec: + containers: + - name: a2a-agent + image: localhost:5001/a2a-cloud-deployment:latest + # Always pull to ensure latest image is used when rebuilding with :latest tag + # For production, use digest-based image references (e.g., @sha256:...) + imagePullPolicy: Always + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: DATABASE_URL + valueFrom: + configMapKeyRef: + name: agent-config + key: DATABASE_URL + - name: DATABASE_USER + valueFrom: + configMapKeyRef: + name: agent-config + key: DATABASE_USER + - name: DATABASE_PASSWORD + valueFrom: + configMapKeyRef: + name: agent-config + key: DATABASE_PASSWORD + - name: KAFKA_BOOTSTRAP_SERVERS + valueFrom: + configMapKeyRef: + name: agent-config + key: KAFKA_BOOTSTRAP_SERVERS + - name: AGENT_URL + valueFrom: + configMapKeyRef: + name: agent-config + key: AGENT_URL + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health/live + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 +--- +apiVersion: v1 +kind: Service +metadata: + name: a2a-agent-service + namespace: a2a-demo +spec: + selector: + app: a2a-agent + ports: + - protocol: TCP + port: 8080 # Service port inside cluster + targetPort: 8080 # Pod port + nodePort: 30080 # Fixed NodePort (mapped to host port 8080 via Kind config) + type: NodePort + sessionAffinity: None # Ensures round-robin load balancing diff --git a/examples/cloud-deployment/kind-config.yaml b/examples/cloud-deployment/kind-config.yaml new file mode 100644 index 000000000..821027be6 --- /dev/null +++ b/examples/cloud-deployment/kind-config.yaml @@ -0,0 +1,24 @@ +# Kind cluster configuration for A2A Cloud Deployment Example +# This config enables: +# - Local registry integration +# - NodePort service access via extraPortMappings +# - Sufficient resources for PostgreSQL, Kafka, and 2 agent pods + +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 + +# Enable local registry support +containerdConfigPatches: +- |- + [plugins."io.containerd.grpc.v1.cri".registry] + config_path = "/etc/containerd/certs.d" + +nodes: +# Single control-plane node is sufficient for this demo +- role: control-plane + # Map host port 8080 to NodePort 30080 for agent service access + extraPortMappings: + - containerPort: 30080 # NodePort on Kubernetes node + hostPort: 8080 # Accessible on localhost:8080 + listenAddress: "0.0.0.0" + protocol: TCP diff --git a/examples/cloud-deployment/scripts/cleanup.sh b/examples/cloud-deployment/scripts/cleanup.sh new file mode 100755 index 000000000..7d97840c6 --- /dev/null +++ b/examples/cloud-deployment/scripts/cleanup.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +echo "============================================" +echo "A2A Cloud Deployment - Cleanup Script" +echo "============================================" +echo "" + +# Color codes +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Parse command line arguments +CONTAINER_TOOL="docker" +while [[ $# -gt 0 ]]; do + case $1 in + --container-tool) + CONTAINER_TOOL="$2" + shift 2 + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + echo "Usage: $0 [--container-tool docker|podman]" + exit 1 + ;; + esac +done + +# Configure Kind to use podman if specified +if [ "$CONTAINER_TOOL" = "podman" ]; then + export KIND_EXPERIMENTAL_PROVIDER=podman +fi + +echo -e "${YELLOW}This will delete all resources in the a2a-demo namespace and the Kind cluster${NC}" +read -p "Are you sure you want to continue? (y/N) " -n 1 -r +echo "" + +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Cleanup cancelled" + exit 0 +fi + +echo "" +echo "Deleting A2A Agent..." +kubectl delete -f ../k8s/05-agent-deployment.yaml --ignore-not-found=true + +echo "" +echo "Deleting ConfigMap..." +kubectl delete -f ../k8s/04-agent-configmap.yaml --ignore-not-found=true + +echo "" +echo "Deleting Kafka topic..." +kubectl delete -f ../k8s/03-kafka-topic.yaml --ignore-not-found=true + +echo "" +echo "Deleting Kafka..." +kubectl delete -f ../k8s/02-kafka.yaml --ignore-not-found=true + +echo "" +echo "Deleting PostgreSQL..." +kubectl delete -f ../k8s/01-postgres.yaml --ignore-not-found=true + +echo "" +echo "Deleting namespace..." +kubectl delete -f ../k8s/00-namespace.yaml --ignore-not-found=true + +echo "" +echo "Deleting Kind cluster..." +kind delete cluster + +echo "" +echo "Stopping and removing registry container..." +$CONTAINER_TOOL stop kind-registry > /dev/null 2>&1 || true +$CONTAINER_TOOL rm kind-registry > /dev/null 2>&1 || true + +echo "" +echo -e "${GREEN}Cleanup completed${NC}" +echo "" +echo -e "${YELLOW}Note: Strimzi operator was not removed${NC}" +echo "To remove Strimzi operator, run:" +echo " kubectl delete namespace kafka" diff --git a/examples/cloud-deployment/scripts/deploy.sh b/examples/cloud-deployment/scripts/deploy.sh new file mode 100755 index 000000000..4c7afeb4a --- /dev/null +++ b/examples/cloud-deployment/scripts/deploy.sh @@ -0,0 +1,288 @@ +#!/bin/bash +set -e + +echo "=========================================" +echo "A2A Cloud Deployment - Deployment Script" +echo "=========================================" +echo "" + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Parse command line arguments +CONTAINER_TOOL="docker" +while [[ $# -gt 0 ]]; do + case $1 in + --container-tool) + CONTAINER_TOOL="$2" + shift 2 + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + echo "Usage: $0 [--container-tool docker|podman]" + exit 1 + ;; + esac +done + +echo "Container tool: $CONTAINER_TOOL" +echo "" + +# Configure Kind to use podman if specified +if [ "$CONTAINER_TOOL" = "podman" ]; then + export KIND_EXPERIMENTAL_PROVIDER=podman + echo "Configured Kind to use podman provider" + echo "" +fi + +# Check if Kind is installed +if ! command -v kind &> /dev/null; then + echo -e "${RED}Error: Kind is not installed${NC}" + echo "Please install Kind first: https://kind.sigs.k8s.io/docs/user/quick-start/#installation" + exit 1 +fi + +# Check if kubectl is installed +if ! command -v kubectl &> /dev/null; then + echo -e "${RED}Error: kubectl is not installed${NC}" + echo "Please install kubectl first: https://kubernetes.io/docs/tasks/tools/" + exit 1 +fi + +# Setup local registry +echo "Setting up local registry..." +REG_NAME='kind-registry' +REG_PORT='5001' + +# Create registry container if it doesn't exist +if [ "$($CONTAINER_TOOL inspect -f '{{.State.Running}}' "${REG_NAME}" 2>/dev/null || true)" != 'true' ]; then + echo "Creating registry container..." + $CONTAINER_TOOL run \ + -d --restart=always -p "127.0.0.1:${REG_PORT}:5000" --network bridge --name "${REG_NAME}" \ + mirror.gcr.io/library/registry:2 + echo -e "${GREEN}✓ Registry container created${NC}" +else + echo -e "${GREEN}✓ Registry container already running${NC}" +fi + +# Create Kind cluster if it doesn't exist +echo "" +if ! kind get clusters 2>/dev/null | grep -q '^kind$'; then + echo "Creating Kind cluster..." + kind create cluster --config=../kind-config.yaml + echo -e "${GREEN}✓ Kind cluster created${NC}" +else + # Check if cluster is healthy by trying to get nodes + if ! kubectl get nodes &>/dev/null; then + echo -e "${RED}Error: Existing Kind cluster is not healthy${NC}" + echo "" + echo "The cluster exists but is not responding. This usually means:" + echo " - The cluster containers are stopped" + echo " - The cluster is in a corrupted state" + echo "" + echo "To fix this, delete the cluster and re-run this script:" + echo " kind delete cluster" + echo " ./deploy.sh" + echo "" + exit 1 + else + echo -e "${GREEN}✓ Kind cluster already exists and is healthy${NC}" + fi +fi + +# Configure registry on cluster nodes +echo "" +echo "Configuring registry on cluster nodes..." +REGISTRY_DIR="/etc/containerd/certs.d/localhost:${REG_PORT}" +for node in $(kind get nodes); do + $CONTAINER_TOOL exec "${node}" mkdir -p "${REGISTRY_DIR}" + cat < /dev/null 2>&1; then + echo -e "${GREEN}✓ Registry accessible at localhost:${REG_PORT}${NC}" +else + echo -e "${RED}ERROR: Registry not accessible${NC}" + exit 1 +fi + +# Build the project +echo "" +echo "Building the project..." +cd ../server +mvn clean package -DskipTests +echo -e "${GREEN}✓ Project built successfully${NC}" + +# Build and push container image to local registry +REGISTRY="localhost:${REG_PORT}" +echo "" +echo "Building container image..." +$CONTAINER_TOOL build -t ${REGISTRY}/a2a-cloud-deployment:latest . +echo -e "${GREEN}✓ Container image built${NC}" + +echo "Pushing image to local registry..." +if [ "$CONTAINER_TOOL" = "podman" ]; then + $CONTAINER_TOOL push --tls-verify=false ${REGISTRY}/a2a-cloud-deployment:latest +else + $CONTAINER_TOOL push ${REGISTRY}/a2a-cloud-deployment:latest +fi +echo -e "${GREEN}✓ Image pushed to registry${NC}" + +# Go back to scripts directory +cd ../scripts + +# Install Strimzi operator if not already installed +echo "" +echo "Checking for Strimzi operator..." + +# Ensure kafka namespace exists +if ! kubectl get namespace kafka > /dev/null 2>&1; then + echo "Creating kafka namespace..." + kubectl create namespace kafka +fi + +if ! kubectl get crd kafkas.kafka.strimzi.io > /dev/null 2>&1; then + echo "Installing Strimzi operator..." + kubectl create -f 'https://strimzi.io/install/latest?namespace=kafka' -n kafka + + echo "Waiting for Strimzi operator deployment to be created..." + for i in {1..30}; do + if kubectl get deployment strimzi-cluster-operator -n kafka > /dev/null 2>&1; then + echo "Deployment found" + break + fi + if [ $i -eq 30 ]; then + echo -e "${RED}ERROR: Deployment not found after 30 seconds${NC}" + exit 1 + fi + sleep 1 + done + + echo "Waiting for Strimzi operator to be ready..." + kubectl wait --for=condition=Available deployment/strimzi-cluster-operator -n kafka --timeout=300s + kubectl wait --for=condition=Ready pod -l name=strimzi-cluster-operator -n kafka --timeout=300s + echo -e "${GREEN}✓ Strimzi operator installed${NC}" +else + echo -e "${GREEN}✓ Strimzi operator already installed${NC}" +fi + +# Create namespace +echo "" +echo "Creating namespace..." +kubectl apply -f ../k8s/00-namespace.yaml +echo -e "${GREEN}✓ Namespace created${NC}" + +# Deploy PostgreSQL +echo "" +echo "Deploying PostgreSQL..." +kubectl apply -f ../k8s/01-postgres.yaml +echo "Waiting for PostgreSQL to be ready..." +kubectl wait --for=condition=Ready pod -l app=postgres -n a2a-demo --timeout=120s +echo -e "${GREEN}✓ PostgreSQL deployed${NC}" + +# Deploy Kafka +echo "" +echo "Deploying Kafka..." +kubectl apply -f ../k8s/02-kafka.yaml +echo "Waiting for Kafka to be ready (using KRaft mode, typically 2-3 minutes)..." + +# Monitor progress while waiting +for i in {1..60}; do + echo "Checking Kafka status (attempt $i/60)..." + kubectl get kafka -n kafka -o wide 2>/dev/null || true + kubectl get pods -n kafka -l strimzi.io/cluster=a2a-kafka 2>/dev/null || true + + if kubectl wait --for=condition=Ready kafka/a2a-kafka -n kafka --timeout=10s 2>/dev/null; then + echo -e "${GREEN}✓ Kafka deployed${NC}" + break + fi + + if [ $i -eq 60 ]; then + echo -e "${RED}ERROR: Timeout waiting for Kafka${NC}" + kubectl describe kafka/a2a-kafka -n kafka + kubectl get events -n kafka --sort-by='.lastTimestamp' + exit 1 + fi +done + +# Create Kafka Topic for event replication +echo "" +echo "Creating Kafka topic for event replication..." +kubectl apply -f ../k8s/03-kafka-topic.yaml +echo "Waiting for Kafka topic to be ready..." +kubectl wait --for=condition=Ready kafkatopic/a2a-replicated-events -n kafka --timeout=60s +echo -e "${GREEN}✓ Kafka topic created${NC}" + +# Deploy Agent ConfigMap +echo "" +echo "Deploying Agent ConfigMap..." +kubectl apply -f ../k8s/04-agent-configmap.yaml +echo -e "${GREEN}✓ ConfigMap deployed${NC}" + +# Deploy Agent +if [ "${SKIP_AGENT_DEPLOY}" != "true" ]; then + echo "" + echo "Deploying A2A Agent..." + kubectl apply -f ../k8s/05-agent-deployment.yaml + + echo "Waiting for Agent pods to be ready..." + kubectl wait --for=condition=Ready pod -l app=a2a-agent -n a2a-demo --timeout=120s + echo -e "${GREEN}✓ Agent deployed${NC}" +else + echo "" + echo -e "${YELLOW}⚠ Skipping agent deployment (SKIP_AGENT_DEPLOY=true)${NC}" + echo " ConfigMap has been deployed, you can manually deploy the agent with:" + echo " kubectl apply -f ../k8s/05-agent-deployment.yaml" +fi + +echo "" +echo -e "${GREEN}=========================================${NC}" +echo -e "${GREEN}Deployment completed successfully!${NC}" +echo -e "${GREEN}=========================================${NC}" +echo "" +echo "To verify the deployment, run:" +echo " ./verify.sh" +echo "" +echo "To access the agent (via NodePort):" +echo " curl http://localhost:8080/.well-known/agent-card.json" +echo "" +echo "To run the test client (demonstrating load balancing):" +echo " cd ../server" +echo " mvn test-compile exec:java -Dexec.classpathScope=test \\" +echo " -Dexec.mainClass=\"io.a2a.examples.cloud.A2ACloudExampleClient\" \\" +echo " -Dagent.url=\"http://localhost:8080\"" diff --git a/examples/cloud-deployment/scripts/verify.sh b/examples/cloud-deployment/scripts/verify.sh new file mode 100755 index 000000000..b9905b0d7 --- /dev/null +++ b/examples/cloud-deployment/scripts/verify.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +echo "============================================" +echo "A2A Cloud Deployment - Verification Script" +echo "============================================" +echo "" + +# Color codes +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Check namespace exists +echo "Checking namespace..." +if kubectl get namespace a2a-demo > /dev/null 2>&1; then + echo -e "${GREEN}✓ Namespace 'a2a-demo' exists${NC}" +else + echo -e "${RED}✗ Namespace 'a2a-demo' not found${NC}" + exit 1 +fi + +# Check PostgreSQL +echo "" +echo "Checking PostgreSQL..." +POSTGRES_READY=$(kubectl get pods -n a2a-demo -l app=postgres -o jsonpath='{.items[0].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null) +if [ "$POSTGRES_READY" = "True" ]; then + echo -e "${GREEN}✓ PostgreSQL is ready${NC}" + kubectl get pods -n a2a-demo -l app=postgres +else + echo -e "${RED}✗ PostgreSQL is not ready (Ready: $POSTGRES_READY)${NC}" + kubectl get pods -n a2a-demo -l app=postgres +fi + +# Check Kafka +echo "" +echo "Checking Kafka..." +KAFKA_READY=$(kubectl get kafka a2a-kafka -n kafka -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null) +if [ "$KAFKA_READY" = "True" ]; then + echo -e "${GREEN}✓ Kafka is ready${NC}" + kubectl get kafka -n kafka +else + echo -e "${YELLOW}⚠ Kafka may not be fully ready (Status: $KAFKA_READY)${NC}" + kubectl get kafka -n kafka +fi + +# Check Agent pods +echo "" +echo "Checking A2A Agent pods..." +AGENT_PODS=$(kubectl get pods -n a2a-demo -l app=a2a-agent -o jsonpath='{.items | length}' 2>/dev/null) +AGENT_READY=$(kubectl get pods -n a2a-demo -l app=a2a-agent -o jsonpath='{range .items[*]}{.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}' 2>/dev/null | grep -c "True") + +echo "Total pods: $AGENT_PODS" +echo "Ready pods: $AGENT_READY" + +if [ "$AGENT_READY" -ge 2 ]; then + echo -e "${GREEN}✓ Agent pods are running${NC}" + kubectl get pods -n a2a-demo -l app=a2a-agent -o wide +else + echo -e "${YELLOW}⚠ Not all agent pods are ready${NC}" + kubectl get pods -n a2a-demo -l app=a2a-agent -o wide +fi + +# Check Agent service +echo "" +echo "Checking A2A Agent service..." +if kubectl get svc a2a-agent-service -n a2a-demo > /dev/null 2>&1; then + echo -e "${GREEN}✓ Agent service exists${NC}" + kubectl get svc a2a-agent-service -n a2a-demo +else + echo -e "${RED}✗ Agent service not found${NC}" +fi + +# Test agent endpoint +echo "" +echo "==========================================" +echo "To test the agent (via NodePort):" +echo "" +echo " curl http://localhost:8080/.well-known/agent-card.json" +echo "" +echo "To run the test client (demonstrating load balancing):" +echo " cd ../server" +echo " mvn test-compile exec:java -Dexec.classpathScope=test \\" +echo " -Dexec.mainClass=\"io.a2a.examples.cloud.A2ACloudExampleClient\" \\" +echo " -Dagent.url=\"http://localhost:8080\"" +echo "==========================================" diff --git a/examples/cloud-deployment/server/Dockerfile b/examples/cloud-deployment/server/Dockerfile new file mode 100644 index 000000000..25e1f593c --- /dev/null +++ b/examples/cloud-deployment/server/Dockerfile @@ -0,0 +1,22 @@ +FROM registry.access.redhat.com/ubi9/openjdk-17:1.20 + +ENV LANGUAGE='en_US:en' + +# Labels for documentation and metadata +LABEL description="A2A Cloud Deployment Demo - Multi-pod agent with Kafka event replication" +LABEL version="1.0.0" + +# Copy the Quarkus application (fast-jar layout) +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 + +# Java options for Quarkus +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/examples/cloud-deployment/server/kill b/examples/cloud-deployment/server/kill new file mode 100644 index 000000000..543679eab --- /dev/null +++ b/examples/cloud-deployment/server/kill @@ -0,0 +1 @@ +(eval):1: command not found: PF_PID= diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml new file mode 100644 index 000000000..f3589f68c --- /dev/null +++ b/examples/cloud-deployment/server/pom.xml @@ -0,0 +1,140 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.3.0.Beta3-SNAPSHOT + ../../../pom.xml + + + a2a-java-sdk-examples-cloud-deployment-server + + A2A Java SDK - Cloud Deployment Example Server + Example demonstrating A2A agent deployment in Kubernetes with database persistence and event replication + + + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + ${project.version} + + + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + ${project.version} + + + + + io.github.a2asdk + a2a-java-extras-push-notification-config-store-database-jpa + ${project.version} + + + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + ${project.version} + + + + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + ${project.version} + + + + + io.quarkus + quarkus-messaging-kafka + + + + + io.quarkus + quarkus-jdbc-postgresql + + + + + io.quarkus + quarkus-hibernate-orm + + + + + io.quarkus + quarkus-resteasy-jackson + + + + + io.quarkus + quarkus-smallrye-health + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + + + org.slf4j + slf4j-api + + + + + io.github.a2asdk + a2a-java-sdk-client + test + + + + + + + io.quarkus + quarkus-maven-plugin + true + + + + build + generate-code + generate-code-tests + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + + + + + + + + native + + native + + + + diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java new file mode 100644 index 000000000..3a35a6e8b --- /dev/null +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -0,0 +1,57 @@ +package io.a2a.examples.cloud; + +import io.a2a.server.PublicAgentCard; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentSkill; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.util.Collections; +import java.util.List; + +/** + * Producer for the cloud deployment example agent card. + */ +@ApplicationScoped +public class CloudAgentCardProducer { + + @ConfigProperty(name = "agent.url", defaultValue = "http://localhost:8080") + String agentUrl; + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + return new AgentCard.Builder() + .name("Cloud Deployment Demo Agent") + .description("Demonstrates A2A multi-pod deployment with Kafka event replication, " + + "PostgreSQL persistence, and round-robin load balancing across Kubernetes pods") + .url(agentUrl) + .version("1.0.0") + .capabilities(new AgentCapabilities.Builder() + .streaming(true) + .pushNotifications(false) + .stateTransitionHistory(false) + .build()) + .defaultInputModes(Collections.singletonList("text")) + .defaultOutputModes(Collections.singletonList("text")) + .skills(Collections.singletonList( + new AgentSkill.Builder() + .id("multi_pod_demo") + .name("Multi-Pod Replication Demo") + .description("Demonstrates cross-pod event replication. " + + "Send 'start' to initialize, 'process' to add artifacts, " + + "'complete' to finalize. Each artifact shows which pod processed it.") + .tags(List.of("demo", "cloud", "kubernetes", "replication")) + .examples(List.of( + "start", + "process", + "complete" + )) + .build() + )) + .protocolVersion("0.3.0") + .build(); + } +} diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java new file mode 100644 index 000000000..17750d4a7 --- /dev/null +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java @@ -0,0 +1,132 @@ +package io.a2a.examples.cloud; + +import io.a2a.server.agentexecution.AgentExecutor; +import io.a2a.server.agentexecution.RequestContext; +import io.a2a.server.events.EventQueue; +import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.InternalError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Producer for the cloud deployment example agent executor. + */ +@ApplicationScoped +public class CloudAgentExecutorProducer { + + private static final Logger LOGGER = LoggerFactory.getLogger(CloudAgentExecutorProducer.class); + + @Produces + public AgentExecutor agentExecutor() { + return new CloudAgentExecutor(); + } + + /** + * Modernized agent executor demonstrating multi-pod deployment with event replication. + * + * Message Protocol: + * - "start": Initialize task (SUBMITTED → WORKING), adds "Started by {pod-name}" + * - "process": Add artifact "Processed by {pod-name}" (fire-and-forget, stays WORKING) + * - "complete": Add artifact "Completed by {pod-name}" and transition to COMPLETED + * + * This demonstrates: + * - Cross-pod event replication via Kafka + * - Fire-and-forget pattern with controlled completion + * - Round-robin load balancing across pods + */ + private static class CloudAgentExecutor implements AgentExecutor { + + @Override + public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + TaskUpdater updater = new TaskUpdater(context, eventQueue); + + try { + // Extract user message and normalize + String messageText = extractTextFromMessage(context.getMessage()).trim().toLowerCase(); + LOGGER.info("Received message: '{}'", messageText); + + // Get pod name from environment (set by Kubernetes Downward API) + String podName = System.getenv("POD_NAME"); + if (podName == null || podName.isEmpty()) { + podName = "unknown-pod"; + } + LOGGER.info("Processing on pod: {}", podName); + + // Simulate some processing time to make cross-pod behavior more visible + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new InternalError("Processing interrupted"); + } + + // Handle message based on command + if ("complete".equals(messageText)) { + // Completion trigger - add final artifact and complete + LOGGER.info("Completion requested on pod: {}", podName); + String artifactText = "Completed by " + podName; + List> parts = List.of(new TextPart(artifactText, null)); + updater.addArtifact(parts); + updater.complete(); + LOGGER.info("Task completed on pod: {}", podName); + + } else if (context.getTask() == null) { + // Initial message - create task in SUBMITTED → WORKING state + LOGGER.info("Creating new task on pod: {}", podName); + updater.submit(); + updater.startWork(); + String artifactText = "Started by " + podName; + List> parts = List.of(new TextPart(artifactText, null)); + updater.addArtifact(parts); + LOGGER.info("Task created and started on pod: {}", podName); + + } else { + // Subsequent messages - add artifacts (fire-and-forget, stays in WORKING) + LOGGER.info("Adding artifact on pod: {}", podName); + String artifactText = "Processed by " + podName; + List> parts = List.of(new TextPart(artifactText, null)); + updater.addArtifact(parts); + // No state change - task remains in WORKING + LOGGER.info("Artifact added on pod: {}", podName); + } + + } catch (JSONRPCError e) { + LOGGER.error("JSONRPC error processing task", e); + throw e; + } catch (Exception e) { + LOGGER.error("Error processing task", e); + throw new InternalError("Processing failed: " + e.getMessage()); + } + } + + @Override + public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + LOGGER.info("Task cancellation requested"); + TaskUpdater updater = new TaskUpdater(context, eventQueue); + updater.cancel(); + } + + /** + * Extracts text content from a message. + */ + private String extractTextFromMessage(Message message) { + StringBuilder textBuilder = new StringBuilder(); + if (message.getParts() != null) { + for (Part part : message.getParts()) { + if (part instanceof TextPart textPart) { + textBuilder.append(textPart.getText()); + } + } + } + return textBuilder.toString(); + } + } +} diff --git a/examples/cloud-deployment/server/src/main/resources/META-INF/beans.xml b/examples/cloud-deployment/server/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..f8e1779ce --- /dev/null +++ b/examples/cloud-deployment/server/src/main/resources/META-INF/beans.xml @@ -0,0 +1,5 @@ + + + diff --git a/examples/cloud-deployment/server/src/main/resources/application.properties b/examples/cloud-deployment/server/src/main/resources/application.properties new file mode 100644 index 000000000..653c36398 --- /dev/null +++ b/examples/cloud-deployment/server/src/main/resources/application.properties @@ -0,0 +1,50 @@ +# Agent Configuration +agent.url=${AGENT_URL:http://localhost:8080} + +# HTTP Server +quarkus.http.port=8080 +quarkus.http.host=0.0.0.0 + +# Database Configuration (PostgreSQL) +quarkus.datasource.db-kind=postgresql +quarkus.datasource.jdbc.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/a2a} +quarkus.datasource.username=${DATABASE_USER:a2a} +quarkus.datasource.password=${DATABASE_PASSWORD:a2a} +quarkus.datasource.jdbc.max-size=16 + +# Hibernate ORM - Configure persistence unit "a2a-java" +quarkus.hibernate-orm."a2a-java".datasource= +quarkus.hibernate-orm."a2a-java".database.generation=update +quarkus.hibernate-orm."a2a-java".log.sql=false +quarkus.hibernate-orm."a2a-java".packages=io.a2a.extras.taskstore.database.jpa,io.a2a.extras.pushnotificationconfigstore.database.jpa + +# Kafka Configuration for Event Replication +kafka.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} + +# MicroProfile Reactive Messaging - Outgoing (Publish to Kafka) +mp.messaging.outgoing.replicated-events-out.connector=smallrye-kafka +mp.messaging.outgoing.replicated-events-out.topic=a2a-replicated-events +mp.messaging.outgoing.replicated-events-out.value.serializer=org.apache.kafka.common.serialization.StringSerializer + +# MicroProfile Reactive Messaging - Incoming (Subscribe from Kafka) +mp.messaging.incoming.replicated-events-in.connector=smallrye-kafka +mp.messaging.incoming.replicated-events-in.topic=a2a-replicated-events +# Each pod needs a unique consumer group to receive ALL events (broadcast behavior) +# Using POD_NAME from Kubernetes Downward API ensures each instance gets its own group +mp.messaging.incoming.replicated-events-in.group.id=a2a-cloud-${POD_NAME:local} +mp.messaging.incoming.replicated-events-in.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.replicated-events-in.auto.offset.reset=earliest + +# Logging +# Default to INFO level for production-like behavior +# To enable DEBUG logging for troubleshooting, set these to DEBUG: +# quarkus.log.category."io.a2a.examples.cloud".level=DEBUG +# quarkus.log.category."io.a2a.server.events".level=DEBUG +# quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +# quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.level=INFO +quarkus.log.category."io.a2a".level=INFO +quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n + +# Health checks +quarkus.smallrye-health.root-path=/health diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java new file mode 100644 index 000000000..d894d9f10 --- /dev/null +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -0,0 +1,421 @@ +package io.a2a.examples.cloud; + +import io.a2a.A2A; +import io.a2a.client.Client; +import io.a2a.client.ClientEvent; +import io.a2a.client.MessageEvent; +import io.a2a.client.TaskEvent; +import io.a2a.client.TaskUpdateEvent; +import io.a2a.client.config.ClientConfig; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.a2a.spec.A2AClientError; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TextPart; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Test client demonstrating multi-pod A2A agent deployment with modernized message protocol. + *

+ * This client: + * 1. Sends "start" to create task in SUBMITTED → WORKING state + * 2. Creates subscription to receive streaming updates + * 3. Sends multiple "process" messages that add artifacts showing which pod processed each + * 4. Verifies that at least 2 different pods handled requests (proving load balancing) + * 5. Sends "complete" to finalize task and close stream + *

+ * Message Protocol: + * - "start": Initialize task (SUBMITTED → WORKING), adds "Started by {pod-name}" + * - "process": Add artifact "Processed by {pod-name}" (fire-and-forget, stays WORKING) + * - "complete": Add artifact "Completed by {pod-name}" and transition to COMPLETED + *

+ * Usage: Run after deploying the agent to Kubernetes and setting up port-forward: + * kubectl port-forward -n a2a-demo svc/a2a-agent-service 8080:8080 + */ +public class A2ACloudExampleClient { + + private static final String AGENT_URL = System.getProperty("agent.url", "http://localhost:8080"); + private static final int PROCESS_MESSAGE_COUNT = 8; // Number of "process" messages to send + private static final int MESSAGE_INTERVAL_MS = 1500; + + // Test state + private final Map observedPods = Collections.synchronizedMap(new HashMap<>()); + private final AtomicInteger artifactCount = new AtomicInteger(0); + private final AtomicBoolean testFailed = new AtomicBoolean(false); + private final CountDownLatch taskCreationLatch = new CountDownLatch(1); + private final CountDownLatch completionLatch = new CountDownLatch(1); + private String serverTaskId; + + // Clients + private Client streamingClient; + private Client nonStreamingClient; + private ClientConfig nonStreamingConfig; + + public static void main(String[] args) throws Exception { + new A2ACloudExampleClient().run(); + } + + private void run() throws Exception { + printHeader(); + + AgentCard agentCard = fetchAndConfigureAgentCard(); + String clientTaskId = generateClientTaskId(); + + createClients(agentCard); + + sendStartMessage(clientTaskId); + subscribeToTaskUpdates(); + sendProcessMessages(); + sendCompleteMessage(); + + waitForCompletion(); + printResults(); + } + + private void printHeader() { + System.out.println("============================================="); + System.out.println("A2A Cloud Deployment Example Client"); + System.out.println("============================================="); + System.out.println(); + System.out.println("Agent URL: " + AGENT_URL); + System.out.println("Process messages: " + PROCESS_MESSAGE_COUNT); + System.out.println("Message interval: " + MESSAGE_INTERVAL_MS + "ms"); + System.out.println(); + } + + private AgentCard fetchAndConfigureAgentCard() { + System.out.println("Fetching agent card..."); + AgentCard fetchedCard = A2A.getAgentCard(AGENT_URL); + System.out.println("✓ Agent: " + fetchedCard.name()); + System.out.println("✓ Description: " + fetchedCard.description()); + + // Override agent card URL to use the port-forwarded URL instead of internal K8s service URL + AgentCard agentCard = new AgentCard.Builder(fetchedCard) + .url(AGENT_URL) // Use localhost URL for port-forwarded connection + .build(); + System.out.println(); + return agentCard; + } + + private String generateClientTaskId() { + String clientTaskId = "cloud-test-" + System.currentTimeMillis(); + System.out.println("Client task ID: " + clientTaskId); + System.out.println(); + return clientTaskId; + } + + private void createClients(AgentCard agentCard) { + System.out.println("Creating streaming client for subscription..."); + ClientConfig streamingConfig = new ClientConfig.Builder() + .setStreaming(true) + .build(); + + streamingClient = Client.builder(agentCard) + .clientConfig(streamingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + System.out.println("Creating non-streaming client for sending messages..."); + nonStreamingConfig = new ClientConfig.Builder() + .setStreaming(false) + .build(); + + nonStreamingClient = Client.builder(agentCard) + .clientConfig(nonStreamingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + System.out.println("✓ Clients created"); + System.out.println(); + } + + private void sendStartMessage(String clientTaskId) { + System.out.println("Step 1: Sending 'start' to create task..."); + Message startMessage = A2A.toUserMessage("start", clientTaskId); + + try { + nonStreamingClient.sendMessage(startMessage, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof TaskEvent te) { + serverTaskId = te.getTask().getId(); + System.out.println("✓ Task created: " + serverTaskId); + System.out.println(" State: " + te.getTask().getStatus().state()); + taskCreationLatch.countDown(); + } + }), error -> { + System.err.println("✗ Failed to create task: " + error.getMessage()); + testFailed.set(true); + taskCreationLatch.countDown(); + }); + + // Wait for task creation to complete (max 5 seconds) + if (!taskCreationLatch.await(5, TimeUnit.SECONDS)) { + System.err.println("✗ Timeout waiting for task creation"); + System.exit(1); + } + + if (serverTaskId == null) { + System.err.println("✗ Failed to get server task ID"); + System.exit(1); + } + } catch (Exception e) { + System.err.println("✗ Failed to create task: " + e.getMessage()); + System.exit(1); + } + } + + private void subscribeToTaskUpdates() { + System.out.println(); + System.out.println("Step 2: Subscribing to task for streaming updates..."); + + AtomicBoolean subscribed = new AtomicBoolean(false); + int maxRetries = 3; + + for (int attempt = 1; attempt <= maxRetries; attempt++) { + try { + if (attempt > 1) { + System.out.println("Retry attempt " + attempt + "/" + maxRetries + "..."); + Thread.sleep(1000); // Wait for Kafka events to propagate + } + + streamingClient.resubscribe( + new TaskIdParams(serverTaskId), + List.of(this::handleSubscriptionEvent), + this::handleSubscriptionError + ); + + System.out.println("✓ Subscribed to task updates"); + subscribed.set(true); + break; + } catch (Exception e) { + if (attempt < maxRetries) { + System.out.println("⚠ Failed to subscribe (attempt " + attempt + "/" + maxRetries + "): " + e.getMessage()); + } else { + System.err.println("✗ Failed to subscribe after " + maxRetries + " attempts: " + e.getMessage()); + System.exit(1); + } + } + } + + if (!subscribed.get()) { + System.err.println("✗ Failed to establish subscription"); + System.exit(1); + } + } + + private void handleSubscriptionEvent(ClientEvent event, AgentCard card) { + if (event instanceof TaskUpdateEvent tue) { + if (tue.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifactEvent) { + int count = artifactCount.incrementAndGet(); + String artifactText = extractTextFromArtifact(artifactEvent); + System.out.println(" Artifact #" + count + ": " + artifactText); + + // Extract pod name from artifact text + String podName = extractPodName(artifactText); + if (podName != null && !podName.equals("unknown-pod")) { + int invokeCount = observedPods.getOrDefault(podName, 0); + observedPods.put(podName, ++invokeCount); + System.out.println(" → Pod: " + podName + " (Total unique pods: " + observedPods.size() + ")"); + } + } + } else if (event instanceof TaskEvent te) { + // Check for task completion + if (te.getTask().getStatus().state().isFinal()) { + System.out.println(" Task reached final state: " + te.getTask().getStatus().state()); + completionLatch.countDown(); + } + } + } + + private void handleSubscriptionError(Throwable error) { + // Filter out normal stream closure errors (expected when task completes) + if (!isStreamClosedError(error)) { + System.err.println("✗ Subscription error: " + error.getMessage()); + testFailed.set(true); + } else { + System.out.println("ℹ Subscription stream closed (expected after task completion)"); + } + } + + private void sendProcessMessages() throws InterruptedException { + System.out.println(); + System.out.println("Step 3: Sending " + PROCESS_MESSAGE_COUNT + " 'process' messages (interval: " + MESSAGE_INTERVAL_MS + "ms)..."); + System.out.println("--------------------------------------------"); + + for (int i = 1; i <= PROCESS_MESSAGE_COUNT; i++) { + final int messageNum = i; + + // Create a new client for each request to force new HTTP connection + Client freshClient = Client.builder(streamingClient.getAgentCard()) + .clientConfig(nonStreamingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(new TextPart("process")) + .taskId(serverTaskId) + .build(); + + try { + freshClient.sendMessage(message, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof MessageEvent || event instanceof TaskEvent) { + System.out.println("✓ Process message " + messageNum + " sent"); + } + }), error -> { + System.err.println("✗ Process message " + messageNum + " failed: " + error.getMessage()); + testFailed.set(true); + }); + + Thread.sleep(MESSAGE_INTERVAL_MS); + } catch (Exception e) { + System.err.println("✗ Failed to send process message " + i + ": " + e.getMessage()); + testFailed.set(true); + } + } + + // Wait for process artifacts to arrive via subscription + System.out.println(); + System.out.println("Waiting for process artifacts to arrive..."); + Thread.sleep(2000); + } + + private void sendCompleteMessage() { + System.out.println(); + System.out.println("Step 4: Sending 'complete' to finalize task..."); + + Client completeClient = Client.builder(streamingClient.getAgentCard()) + .clientConfig(nonStreamingConfig) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) + .build(); + + Message completeMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(new TextPart("complete")) + .taskId(serverTaskId) + .build(); + + try { + completeClient.sendMessage(completeMessage, List.of((ClientEvent event, AgentCard card) -> { + if (event instanceof TaskEvent te) { + System.out.println("✓ Complete message sent, task state: " + te.getTask().getStatus().state()); + } + }), error -> { + System.err.println("✗ Failed to send complete message: " + error.getMessage()); + testFailed.set(true); + }); + } catch (Exception e) { + System.err.println("✗ Failed to send complete message: " + e.getMessage()); + testFailed.set(true); + } + } + + private void waitForCompletion() throws InterruptedException { + System.out.println(); + System.out.println("Waiting for task to complete..."); + if (!completionLatch.await(10, TimeUnit.SECONDS)) { + System.err.println("⚠ Timeout waiting for task completion"); + } + } + + private void printResults() { + System.out.println(); + System.out.println("============================================="); + System.out.println("Test Results"); + System.out.println("============================================="); + System.out.println("Total artifacts received: " + artifactCount.get()); + System.out.println("Unique pods observed: " + observedPods.size()); + System.out.println("Pod names and counts: " + observedPods); + System.out.println(); + + if (testFailed.get()) { + System.out.println("✗ TEST FAILED - Errors occurred during execution"); + System.exit(1); + } else if (observedPods.size() < 2) { + System.out.println("✗ TEST FAILED - Expected at least 2 different pods, but only saw: " + observedPods.size()); + System.out.println(" This suggests load balancing is not working correctly."); + System.exit(1); + } else { + System.out.println("✓ TEST PASSED - Successfully demonstrated multi-pod processing!"); + System.out.println(" Messages were handled by " + observedPods.size() + " different pods."); + System.out.println(" This proves that:"); + System.out.println(" - Load balancing is working (round-robin across pods)"); + System.out.println(" - Event replication is working (subscriber sees events from all pods)"); + System.out.println(" - Database persistence is working (task state shared across pods)"); + System.exit(0); + } + } + + private static String extractTextFromArtifact(TaskArtifactUpdateEvent event) { + StringBuilder text = new StringBuilder(); + if (event.getArtifact() != null) { + for (Part part : event.getArtifact().parts()) { + if (part instanceof TextPart textPart) { + text.append(textPart.getText()); + } + } + } + return text.toString(); + } + + private static String extractPodName(String artifactText) { + // Artifact text format: "Started by " or "Processed by " or "Completed by " + if (artifactText != null) { + if (artifactText.startsWith("Started by ")) { + return artifactText.substring("Started by ".length()).trim(); + } else if (artifactText.startsWith("Processed by ")) { + return artifactText.substring("Processed by ".length()).trim(); + } else if (artifactText.startsWith("Completed by ")) { + return artifactText.substring("Completed by ".length()).trim(); + } + } + return null; + } + + /** + * Checks if the error is a normal stream closure error that should be ignored. + * HTTP/2 stream cancellation and closure are expected when task completes and queue closes. + * Based on MultiInstanceReplicationTest.isStreamClosedError(). + */ + private static boolean isStreamClosedError(Throwable error) { + if (error == null) { + return false; + } + + // Check for IOException which includes stream cancellation + if (error instanceof IOException) { + String message = error.getMessage(); + if (message != null) { + // Filter out normal stream closure/cancellation errors + if (message.contains("Stream closed") || + message.contains("Stream") && message.contains("cancelled") || + message.contains("EOF reached") || + message.contains("CANCEL")) { + return true; + } + } + } + + // Check cause recursively + Throwable cause = error.getCause(); + if (cause != null && cause != error) { + return isStreamClosedError(cause); + } + + return false; + } +} diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 97505f7ea..bef0b94bf 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -14,7 +14,7 @@ a2a-java-sdk-examples-parent pom - Java SDK A2A Examples + Java SDK A2A Examples: Hello World Examples for the Java SDK for the Agent2Agent Protocol (A2A) diff --git a/pom.xml b/pom.xml index 05301c45b..736ae5440 100644 --- a/pom.xml +++ b/pom.xml @@ -439,6 +439,7 @@ client/transport/spi common examples/helloworld + examples/cloud-deployment/server extras/common extras/task-store-database-jpa extras/push-notification-config-store-database-jpa From 15fa4567ff5a4a2b1f5e6cb8c2d247e05bc6bef6 Mon Sep 17 00:00:00 2001 From: leehuiseok <162463130+leehuiseok@users.noreply.github.com> Date: Tue, 28 Oct 2025 05:59:20 +0900 Subject: [PATCH 174/493] feat: add ArtifactUtils utility class and corresponding tests (#303) (#350) ## feat: add `ArtifactUtils` utility class and corresponding unit tests ### Description This PR introduces the `ArtifactUtils` utility class, which provides a set of static helper methods for creating `Artifact` instances in a standardized and concise way. It simplifies the creation of artifacts with different content types (`TextPart`, `DataPart`, etc.) while ensuring consistent ID generation and description handling. ### Details - Added `ArtifactUtils` as a `final` utility class with a private constructor to prevent instantiation. - Implemented multiple overloaded factory methods for flexible artifact creation: - `newArtifact(List> parts, String name, String description)` - `newArtifact(List> parts, String name)` - `newTextArtifact(String name, String text, String description)` - `newTextArtifact(String name, String text)` - `newDataArtifact(String name, Map data, String description)` - `newDataArtifact(String name, Map data)` - Each artifact automatically receives a unique `artifactId` via `UUID.randomUUID()`. - Uses `List.of()` for concise creation of single-part artifacts (`TextPart`, `DataPart`). ### Tests Added comprehensive unit tests in `ArtifactUtilsTest` to verify: - UUID generation through mocking - Proper assignment of `name`, `description`, and `parts` - Correct creation of both text-based and data-based artifacts - Non-null and non-empty `artifactId` values ### Example Usage ```java Artifact textArtifact = ArtifactUtils.newTextArtifact("User Profile", "Basic user info"); Artifact dataArtifact = ArtifactUtils.newDataArtifact("Config", Map.of("version", "1.0")); ``` Fixes:#303 --- .../io/a2a/server/util/ArtifactUtils.java | 103 ++++++++++++ .../io/a2a/server/util/ArtifactUtilsTest.java | 154 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java create mode 100644 server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java diff --git a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java new file mode 100644 index 000000000..7a8637680 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java @@ -0,0 +1,103 @@ +package io.a2a.server.util; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import io.a2a.spec.Artifact; +import io.a2a.spec.DataPart; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; + +/** + * Utility functions for creating A2A Artifact objects. + */ +public final class ArtifactUtils { + + private ArtifactUtils() { + // Utility class - prevent instantiation + } + + /** + * Creates a new Artifact object. + * + * @param name The human-readable name of the artifact. + * @param parts The list of {@code Part} objects forming the artifact's content. + * @param description An optional description of the artifact. + * @return A new {@code Artifact} object with a generated artifact_id. + */ + public static Artifact newArtifact(String name, List> parts, String description) { + return new Artifact( + UUID.randomUUID().toString(), + name, + description, + parts, + null + ); + } + + /** + * Creates a new Artifact object with empty description. + * + * @param name The human-readable name of the artifact. + * @param parts The list of {@code Part} objects forming the artifact's content. + * @return A new {@code Artifact} object with a generated artifact_id. + */ + public static Artifact newArtifact(String name, List> parts) { + return newArtifact(name, parts, null); + } + + /** + * Creates a new Artifact object containing only a single TextPart. + * + * @param name The human-readable name of the artifact. + * @param text The text content of the artifact. + * @param description An optional description of the artifact. + * @return A new {@code Artifact} object with a generated artifact_id. + */ + public static Artifact newTextArtifact(String name, String text, String description) { + return newArtifact( + name, + List.of(new TextPart(text)), + description + ); + } + + /** + * Creates a new Artifact object containing only a single TextPart with empty description. + * + * @param name The human-readable name of the artifact. + * @param text The text content of the artifact. + * @return A new {@code Artifact} object with a generated artifact_id. + */ + public static Artifact newTextArtifact(String name, String text) { + return newTextArtifact(name, text, null); + } + + /** + * Creates a new Artifact object containing only a single DataPart. + * + * @param name The human-readable name of the artifact. + * @param data The structured data content of the artifact. + * @param description An optional description of the artifact. + * @return A new {@code Artifact} object with a generated artifact_id. + */ + public static Artifact newDataArtifact(String name, Map data, String description) { + return newArtifact( + name, + List.of(new DataPart(data)), + description + ); + } + + /** + * Creates a new Artifact object containing only a single DataPart with empty description. + * + * @param name The human-readable name of the artifact. + * @param data The structured data content of the artifact. + * @return A new {@code Artifact} object with a generated artifact_id. + */ + public static Artifact newDataArtifact(String name, Map data) { + return newDataArtifact(name, data, null); + } +} diff --git a/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java b/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java new file mode 100644 index 000000000..b41197bfc --- /dev/null +++ b/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java @@ -0,0 +1,154 @@ +package io.a2a.server.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +import io.a2a.spec.Artifact; +import io.a2a.spec.DataPart; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +class ArtifactUtilsTest { + + @Test + void testNewArtifactAssignsPartsNameDescription() { + // Given + List> parts = List.of(new TextPart("Sample text")); + String name = "My Artifact"; + String description = "This is a test artifact."; + + // When + Artifact artifact = ArtifactUtils.newArtifact(name, parts, description); + + // Then + assertEquals(parts, artifact.parts()); + assertEquals(name, artifact.name()); + assertEquals(description, artifact.description()); + + // Then + assertNotNull(artifact.artifactId()); + assertFalse(artifact.artifactId().isBlank()); + assertDoesNotThrow(() -> UUID.fromString(artifact.artifactId())); + } + + @Test + void testNewArtifactEmptyDescriptionIfNotProvided() { + // Given + List> parts = List.of(new TextPart("Another sample")); + String name = "Artifact_No_Desc"; + + // When + Artifact artifact = ArtifactUtils.newArtifact(name, parts); + + // Then + assertEquals(null, artifact.description()); } + + @Test + void testNewTextArtifactCreatesSingleTextPart() { + // Given + String text = "This is a text artifact."; + String name = "Text_Artifact"; + + // When + Artifact artifact = ArtifactUtils.newTextArtifact(name, text); + + // Then + assertEquals(1, artifact.parts().size()); + assertInstanceOf(TextPart.class, artifact.parts().get(0)); + } + + @Test + void testNewTextArtifactPartContainsProvidedText() { + // Given + String text = "Hello, world!"; + String name = "Greeting_Artifact"; + + // When + Artifact artifact = ArtifactUtils.newTextArtifact(name, text); + + // Then + TextPart textPart = (TextPart) artifact.parts().get(0); + assertEquals(text, textPart.getText()); + } + + @Test + void testNewTextArtifactAssignsNameDescription() { + // Given + String text = "Some content."; + String name = "Named_Text_Artifact"; + String description = "Description for text artifact."; + + // When + Artifact artifact = ArtifactUtils.newTextArtifact(name, text, description); + + // Then + assertEquals(name, artifact.name()); + assertEquals(description, artifact.description()); + } + + @Test + void testNewDataArtifactCreatesSingleDataPart() { + // Given + Map sampleData = Map.of("key", "value", "number", 123); + String name = "Data_Artifact"; + + // When + Artifact artifact = ArtifactUtils.newDataArtifact(name, sampleData); + + // Then + assertEquals(1, artifact.parts().size()); + assertInstanceOf(DataPart.class, artifact.parts().get(0)); + } + + @Test + void testNewDataArtifactPartContainsProvidedData() { + // Given + Map sampleData = Map.of("content", "test_data", "is_valid", true); + String name = "Structured_Data_Artifact"; + + // When + Artifact artifact = ArtifactUtils.newDataArtifact(name, sampleData); + + // Then + DataPart dataPart = (DataPart) artifact.parts().get(0); + assertEquals(sampleData, dataPart.getData()); + } + + @Test + void testNewDataArtifactAssignsNameDescription() { + // Given + Map sampleData = Map.of("info", "some details"); + String name = "Named_Data_Artifact"; + String description = "Description for data artifact."; + + // When + Artifact artifact = ArtifactUtils.newDataArtifact(name, sampleData, description); + + // Then + assertEquals(name, artifact.name()); + assertEquals(description, artifact.description()); + } + + @Test + void testArtifactIdIsNotNull() { + // Given + List> parts = List.of(new TextPart("Test")); + String name = "Test_Artifact"; + + // When + Artifact artifact = ArtifactUtils.newArtifact(name, parts); + + // Then + assertNotNull(artifact.artifactId()); + assertTrue(artifact.artifactId().length() > 0); + } +} From 85dd7475d69871ec96f96b280e267e302992b7b2 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 28 Oct 2025 13:44:30 +0000 Subject: [PATCH 175/493] feat: Add optional entity operator wait skip for Kind (#397) Add SKIP_ENTITY_OPERATOR_WAIT environment variable to work around entity operator timeout issues in some environments (Linux + Podman). When set to 'true': - Checks only Kafka broker pod readiness (not full Kafka resource) - Polls Kafka broker directly to verify topic creation This is an opt-in workaround that doesn't affect standard deployments. Also improves user messaging: - Added timeout information (10 minutes) - Added proactive hints about workaround if timeout occurs - Set expectations for topic creation timing --- examples/cloud-deployment/README.md | 17 +++- examples/cloud-deployment/scripts/deploy.sh | 94 ++++++++++++++++----- 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/examples/cloud-deployment/README.md b/examples/cloud-deployment/README.md index 989ba173b..bf1e4cd60 100644 --- a/examples/cloud-deployment/README.md +++ b/examples/cloud-deployment/README.md @@ -90,7 +90,22 @@ cd scripts ./deploy.sh --container-tool podman ``` -Note that using Kind with Podman on Linux may have some occasional issues due to Kind's experimental support for Podman. In our testing, a reboot normally solves this. +Note that using Kind with Podman on Linux may have some occasional issues due to Kind's experimental support for Podman. In our testing, a reboot normally solves this. + +**Troubleshooting entity operator timeout:** + +In some environments (particularly Linux with Podman), the Kafka entity operator may not start properly, causing deployment to timeout while waiting for Kafka to be ready. If you encounter this issue, you can skip the entity operator wait: + +```bash +export SKIP_ENTITY_OPERATOR_WAIT=true +./deploy.sh --container-tool podman +``` + +This tells the script to: +- Check only the Kafka broker pod (not the full Kafka resource with entity operator) +- Poll the Kafka broker directly to verify topic creation (instead of waiting for the topic operator) + +The entity operator manages topic and user resources, but the broker handles the actual message streaming. Skipping the entity operator wait does not affect the demo's core functionality. The script will: - Create Kind cluster with local registry support (if not already exists) diff --git a/examples/cloud-deployment/scripts/deploy.sh b/examples/cloud-deployment/scripts/deploy.sh index 4c7afeb4a..e267f3302 100755 --- a/examples/cloud-deployment/scripts/deploy.sh +++ b/examples/cloud-deployment/scripts/deploy.sh @@ -219,34 +219,88 @@ echo -e "${GREEN}✓ PostgreSQL deployed${NC}" echo "" echo "Deploying Kafka..." kubectl apply -f ../k8s/02-kafka.yaml -echo "Waiting for Kafka to be ready (using KRaft mode, typically 2-3 minutes)..." +echo "Waiting for Kafka to be ready (using KRaft mode, typically 2-3 minutes. Timeout is 10 minutes)..." -# Monitor progress while waiting -for i in {1..60}; do - echo "Checking Kafka status (attempt $i/60)..." - kubectl get kafka -n kafka -o wide 2>/dev/null || true - kubectl get pods -n kafka -l strimzi.io/cluster=a2a-kafka 2>/dev/null || true +# Check if we should skip entity operator wait (workaround for some environments) +if [ "${SKIP_ENTITY_OPERATOR_WAIT}" = "true" ]; then + echo -e "${YELLOW}⚠ SKIP_ENTITY_OPERATOR_WAIT is set - checking broker pod only${NC}" - if kubectl wait --for=condition=Ready kafka/a2a-kafka -n kafka --timeout=10s 2>/dev/null; then - echo -e "${GREEN}✓ Kafka deployed${NC}" - break - fi + # Wait for broker pod to be ready (skip entity operator check) + for i in {1..60}; do + echo "Checking Kafka broker status (attempt $i/60)..." + kubectl get pods -n kafka -l strimzi.io/cluster=a2a-kafka 2>/dev/null || true - if [ $i -eq 60 ]; then - echo -e "${RED}ERROR: Timeout waiting for Kafka${NC}" - kubectl describe kafka/a2a-kafka -n kafka - kubectl get events -n kafka --sort-by='.lastTimestamp' - exit 1 - fi -done + if kubectl wait --for=condition=Ready pod/a2a-kafka-broker-0 -n kafka --timeout=5s 2>/dev/null; then + echo -e "${GREEN}✓ Kafka broker pod is ready${NC}" + echo -e "${YELLOW}⚠ Entity operator may not be ready, but this does not affect functionality${NC}" + break + fi + + if [ $i -eq 60 ]; then + echo -e "${RED}ERROR: Timeout waiting for Kafka broker${NC}" + kubectl get pods -n kafka -l strimzi.io/cluster=a2a-kafka + kubectl describe pod a2a-kafka-broker-0 -n kafka 2>/dev/null || true + exit 1 + fi + + sleep 5 + done +else + echo -e "${YELLOW} If waiting for Kafka times out, run ./cleanup.sh, and retry having set 'SKIP_ENTITY_OPERATOR_WAIT=true'${NC}" + # Standard wait for full Kafka resource (includes entity operator) + for i in {1..60}; do + echo "Checking Kafka status (attempt $i/60)..." + kubectl get kafka -n kafka -o wide 2>/dev/null || true + kubectl get pods -n kafka -l strimzi.io/cluster=a2a-kafka 2>/dev/null || true + + if kubectl wait --for=condition=Ready kafka/a2a-kafka -n kafka --timeout=10s 2>/dev/null; then + echo -e "${GREEN}✓ Kafka deployed${NC}" + break + fi + + if [ $i -eq 60 ]; then + echo -e "${RED}ERROR: Timeout waiting for Kafka${NC}" + kubectl describe kafka/a2a-kafka -n kafka + kubectl get events -n kafka --sort-by='.lastTimestamp' + exit 1 + fi + done +fi # Create Kafka Topic for event replication echo "" echo "Creating Kafka topic for event replication..." kubectl apply -f ../k8s/03-kafka-topic.yaml -echo "Waiting for Kafka topic to be ready..." -kubectl wait --for=condition=Ready kafkatopic/a2a-replicated-events -n kafka --timeout=60s -echo -e "${GREEN}✓ Kafka topic created${NC}" + +if [ "${SKIP_ENTITY_OPERATOR_WAIT}" = "true" ]; then + echo -e "${YELLOW}⚠ SKIP_ENTITY_OPERATOR_WAIT is set - polling Kafka broker for topic${NC}" + echo " Topic operator may not be ready, waiting for broker to create topic. This check can take several minutes..." + + # Wait for topic to actually exist in Kafka broker (not just CRD) + for i in {1..30}; do + if kubectl exec a2a-kafka-broker-0 -n kafka -- \ + /opt/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092 2>/dev/null | \ + grep -q "a2a-replicated-events"; then + echo -e "${GREEN}✓ Topic exists in Kafka broker${NC}" + break + fi + if [ $i -eq 30 ]; then + echo -e "${RED}ERROR: Topic not found in broker after 30 attempts${NC}" + exit 1 + fi + sleep 2 + done +else + echo "Waiting for Kafka topic to be ready..." + if kubectl wait --for=condition=Ready kafkatopic/a2a-replicated-events -n kafka --timeout=60s; then + echo -e "${GREEN}✓ Kafka topic created${NC}" + else + echo -e "${RED}ERROR: Timeout waiting for Kafka topic${NC}" + echo -e "${YELLOW}The topic operator may not be ready in this environment.${NC}" + echo -e "${YELLOW}Run ./cleanup.sh, then retry with: export SKIP_ENTITY_OPERATOR_WAIT=true${NC}" + exit 1 + fi +fi # Deploy Agent ConfigMap echo "" From 713120fe2cf3958b842502b9c0bd5e536d438e54 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 28 Oct 2025 13:59:33 +0000 Subject: [PATCH 176/493] =?UTF-8?q?fix:=20Filter=20benign=20stream=20error?= =?UTF-8?q?s=20in=20KafkaReplicationIntegrationTest=20a=E2=80=A6=20(#398)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …nd improve CI diagnostics - Filter HTTP/2 stream cancellation errors in KafkaReplicationIntegrationTest (same fix as PR #380) - Add surefire reports and build logs upload to build-and-test workflow - Enhance TCK workflow to capture test output, server logs, and compliance reports --- .github/workflows/build-and-test.yml | 20 ++++ .github/workflows/run-tck.yml | 98 ++++++++----------- .../KafkaReplicationIntegrationTest.java | 58 ++++++++++- 3 files changed, 118 insertions(+), 58 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 6d565ec44..e2fb7253d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -27,3 +27,23 @@ jobs: cache: maven - name: Build with Maven and run tests run: mvn -B package --file pom.xml -fae + - name: Upload Test Reports + if: failure() + uses: actions/upload-artifact@v4 + with: + name: surefire-reports-java-${{ matrix.java-version }} + path: | + **/target/surefire-reports/ + **/target/failsafe-reports/ + retention-days: 7 + if-no-files-found: warn + - name: Upload Build Logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: build-logs-java-${{ matrix.java-version }} + path: | + **/target/*.log + **/target/quarkus.log + retention-days: 3 + if-no-files-found: ignore diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index d88652c91..8dddf8124 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -100,85 +100,71 @@ jobs: id: run-tck timeout-minutes: 5 run: | - ./run_tck.py --sut-url ${{ env.SUT_JSONRPC_URL }} --category all --transports jsonrpc,grpc,rest --compliance-report report.json + ./run_tck.py --sut-url ${{ env.SUT_JSONRPC_URL }} --category all --transports jsonrpc,grpc,rest --compliance-report report.json 2>&1 | tee tck-output.log working-directory: tck/a2a-tck - - name: Capture Thread Dump + - name: Capture Diagnostics on Failure if: failure() run: | + echo "=== Capturing diagnostic information ===" + + # Create diagnostics directory + mkdir -p tck/target/diagnostics + + # Capture process list + echo "📋 Capturing process list..." + ps auxww > tck/target/diagnostics/processes.txt + # Find the actual Quarkus JVM (child of Maven process), not the Maven parent # Look for the dev.jar process which is the actual application QUARKUS_PID=$(pgrep -f "a2a-tck-server-dev.jar" || echo "") if [ -n "$QUARKUS_PID" ]; then echo "📊 Capturing thread dump for Quarkus JVM PID $QUARKUS_PID" - jstack $QUARKUS_PID > tck/target/thread-dump.txt || echo "Failed to capture thread dump" - if [ -f tck/target/thread-dump.txt ]; then - echo "✅ Thread dump captured ($(wc -l < tck/target/thread-dump.txt) lines)" + jstack $QUARKUS_PID > tck/target/diagnostics/thread-dump.txt || echo "Failed to capture thread dump" + if [ -f tck/target/diagnostics/thread-dump.txt ]; then + echo "✅ Thread dump captured ($(wc -l < tck/target/diagnostics/thread-dump.txt) lines)" fi else echo "⚠️ No Quarkus JVM process found for thread dump" echo "Available Java processes:" - ps aux | grep java || true + ps aux | grep java | tee -a tck/target/diagnostics/processes.txt || true fi - - name: Capture Heap Dump - if: failure() - run: | - # Find the actual Quarkus JVM (child of Maven process), not the Maven parent - QUARKUS_PID=$(pgrep -f "a2a-tck-server-dev.jar" || echo "") - if [ -n "$QUARKUS_PID" ]; then - echo "📊 Capturing heap dump for Quarkus JVM PID $QUARKUS_PID" - jmap -dump:live,format=b,file=tck/target/heap-dump.hprof $QUARKUS_PID || echo "Failed to capture heap dump" - if [ -f tck/target/heap-dump.hprof ]; then - SIZE=$(du -h tck/target/heap-dump.hprof | cut -f1) - echo "✅ Heap dump captured ($SIZE)" - # Compress to reduce artifact size - gzip tck/target/heap-dump.hprof - COMPRESSED_SIZE=$(du -h tck/target/heap-dump.hprof.gz | cut -f1) - echo "✅ Compressed heap dump ($COMPRESSED_SIZE)" - fi - else - echo "⚠️ No Quarkus JVM process found for heap dump" - echo "Available Java processes:" - ps aux | grep java || true + + # Capture Quarkus application logs (if available) + echo "📝 Checking for Quarkus logs..." + if [ -f tck/target/quarkus.log ]; then + cp tck/target/quarkus.log tck/target/diagnostics/ + echo "✅ Copied quarkus.log ($(wc -l < tck/target/quarkus.log) lines)" + fi + + # Copy TCK server logs + if [ -f tck/target/tck-test.log ]; then + cp tck/target/tck-test.log tck/target/diagnostics/ + echo "✅ Copied tck-test.log ($(wc -l < tck/target/tck-test.log) lines)" fi + + echo "" + echo "=== Diagnostic capture complete ===" - name: Stop Quarkus Server if: always() run: | # Find and kill the Quarkus process to ensure logs are flushed pkill -f "quarkus:dev" || true sleep 2 - - name: Verify TCK Log - if: failure() - run: | - echo "Checking for log file..." - if [ -f tck/target/tck-test.log ]; then - echo "✅ Log file exists ($(wc -l < tck/target/tck-test.log) lines)" - ls -lh tck/target/tck-test.log - else - echo "❌ Log file not found at tck/target/tck-test.log" - echo "Contents of tck/target/:" - ls -la tck/target/ || echo "tck/target/ does not exist" - fi - - name: Upload TCK Log + - name: Upload TCK Diagnostics if: failure() uses: actions/upload-artifact@v4 with: - name: tck-test-log-java-${{ matrix.java-version }} - path: tck/target/tck-test.log - retention-days: 2 + name: tck-diagnostics-java-${{ matrix.java-version }} + path: | + tck/target/diagnostics/ + tck/a2a-tck/tck-output.log + retention-days: 7 if-no-files-found: warn - - name: Upload Thread Dump - if: failure() - uses: actions/upload-artifact@v4 - with: - name: thread-dump-java-${{ matrix.java-version }} - path: tck/target/thread-dump.txt - retention-days: 2 - if-no-files-found: warn - - name: Upload Heap Dump - if: failure() + - name: Upload TCK Compliance Report + if: always() uses: actions/upload-artifact@v4 with: - name: heap-dump-java-${{ matrix.java-version }} - path: tck/target/heap-dump.hprof.gz - retention-days: 2 - if-no-files-found: warn + name: tck-compliance-report-java-${{ matrix.java-version }} + path: tck/a2a-tck/report.json + retention-days: 14 + if-no-files-found: ignore diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index fee44ca93..52f5676d3 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -7,7 +7,10 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -236,9 +239,13 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { } }; - // Create error handler + // Create error handler - filter out benign stream closed errors. + // HTTP/2 streams are cancelled during normal cleanup when subscriptions end, + // which is expected behavior and not an actual error condition. Consumer errorHandler = error -> { - errorRef.set(error); + if (!isStreamClosedError(error)) { + errorRef.set(error); + } resubscribeLatch.countDown(); }; @@ -423,4 +430,51 @@ public void testPoisonPillGenerationOnTaskFinalization() throws Exception { assertEquals(taskId, closedEvent.getTaskId(), "QueueClosedEvent task ID should match"); } + /** + * Checks if an error is a benign stream closed/cancelled error that should be ignored. + * HTTP/2 streams can be cancelled during normal cleanup, which is not an actual error. + * + * @param error the throwable to check (may be null) + * @return true if this is a benign stream closure error that should be ignored + */ + private boolean isStreamClosedError(Throwable error) { + return isStreamClosedError(error, new HashSet<>()); + } + + /** + * Internal recursive implementation with cycle detection to prevent infinite recursion. + * + * @param error the throwable to check + * @param visited set of already-visited throwables to detect cycles + * @return true if this is a benign stream closure error + */ + private boolean isStreamClosedError(Throwable error, Set visited) { + if (error == null || !visited.add(error)) { + // Null or already visited (cycle detected) + return false; + } + + // Check for IOException which includes stream cancellation + if (error instanceof IOException) { + String message = error.getMessage(); + if (message != null) { + // Filter out normal stream closure/cancellation errors + if (message.contains("Stream closed") || + message.contains("Stream") && message.contains("cancelled") || + message.contains("EOF reached") || + message.contains("CANCEL")) { + return true; + } + } + } + + // Check cause recursively with cycle detection + Throwable cause = error.getCause(); + if (cause != null) { + return isStreamClosedError(cause, visited); + } + + return false; + } + } \ No newline at end of file From 00f8b8790fb382ca3163962e7e72e1dbc0a60b42 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 28 Oct 2025 10:01:50 -0400 Subject: [PATCH 177/493] chore: Release 0.3.0.Final --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/cloud-deployment/server/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/common/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests-multi-instance/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-1/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-2/pom.xml | 2 +- .../tests-multi-instance/quarkus-common/pom.xml | 2 +- .../tests-multi-instance/tests/pom.xml | 2 +- extras/queue-manager-replicated/tests-single-instance/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 37 files changed, 38 insertions(+), 38 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 7dcf9eca6..332ddd995 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 763740c62..ae949cc12 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 6196dc520..0b8e5c819 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 9daf6bcd1..3658a0b45 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index 453a66bca..b07d672d4 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 2d8744088..4a762745c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-common diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index f3589f68c..00572ed8d 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../../pom.xml diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 42c50d78d..90da6e004 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 4a8ecd6dc..24b0d6159 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Beta3-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Beta3-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Final +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Final //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index bef0b94bf..66940a387 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 0929a907e..96001f9f4 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-examples-server diff --git a/extras/common/pom.xml b/extras/common/pom.xml index 632b33e03..f69af2bbf 100644 --- a/extras/common/pom.xml +++ b/extras/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 0cd78769e..c8fefc5e4 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 48717409b..4fc4550b7 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index a27e91bf8..b8c3fef98 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index c6219856c..917ea52f8 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml index bd442cf96..9993f83a9 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml index 08afad676..ad1935815 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml index 9a7c9fd42..a8d6e34e4 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml index d9f940e1a..35ddc799d 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml index 16b696ca5..94d1438bf 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index cfc449c75..dad8ef5e0 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index ad7ca99ad..f7b8816a6 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index 4e138b09b..56bc6db24 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index 736ae5440..46d29db66 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 4ccccdc4d..9c16f1158 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 4e9a6f335..0e84ea936 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index c263d7091..9ceb7800f 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 5b0fecc72..3a8416335 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index ca78345e4..d48f69036 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 64049c46f..e7805eb4f 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index f699612a2..afe9f76bb 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index c33cf6e8b..de276a2bc 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index bdd5c1700..ac01d5aff 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index bfa98f3d7..4f428d1ff 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 638eaebca..0b4bd027b 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index ae86cbac5..7dc589343 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Beta3-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-transport-rest From 301a0836d287b1ef5a380ed4640a9d3b533d05bd Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 28 Oct 2025 10:04:31 -0400 Subject: [PATCH 178/493] chore: Next is 0.4.0.Alpha1 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/cloud-deployment/server/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/common/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests-multi-instance/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-1/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-2/pom.xml | 2 +- .../tests-multi-instance/quarkus-common/pom.xml | 2 +- .../tests-multi-instance/tests/pom.xml | 2 +- extras/queue-manager-replicated/tests-single-instance/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 37 files changed, 38 insertions(+), 38 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 332ddd995..325175a12 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index ae949cc12..7965dcaa2 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 0b8e5c819..278e4fb96 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 3658a0b45..4353383c0 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index b07d672d4..bb806abb8 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 4a762745c..821ae168c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index 00572ed8d..f6de4ed4c 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 90da6e004..8f5b63406 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 24b0d6159..5a3e46a4f 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Final -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Final +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.4.0.Alpha1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.4.0.Alpha1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 66940a387..d5ebb596d 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 96001f9f4..5c660336f 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/extras/common/pom.xml b/extras/common/pom.xml index f69af2bbf..416a39df6 100644 --- a/extras/common/pom.xml +++ b/extras/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index c8fefc5e4..821eaa5d9 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 4fc4550b7..c5d1ee8bf 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index b8c3fef98..f4da4d1e4 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 917ea52f8..77f51477d 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml index 9993f83a9..67c1bb384 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml index ad1935815..04d1389e5 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml index a8d6e34e4..66cdb7822 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml index 35ddc799d..e54b8f786 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml index 94d1438bf..fe378dd29 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index dad8ef5e0..b92043be6 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index f7b8816a6..3f5aee4f1 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index 56bc6db24..98640e8f2 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index 46d29db66..02129d631 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 9c16f1158..f93374a16 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 0e84ea936..0db7b0ca7 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 9ceb7800f..11fa35377 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 3a8416335..b1ef2b03c 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index d48f69036..2165790ed 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index e7805eb4f..4610f3142 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index afe9f76bb..af6a5a023 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index de276a2bc..1a9a984c9 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index ac01d5aff..da377b10a 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 4f428d1ff..6fdf4fc97 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 0b4bd027b..2b302ec53 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 7dc589343..749f97771 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-rest From 350501ffea1c98ecc36917756c5f2eff958596f9 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 28 Oct 2025 14:36:35 +0000 Subject: [PATCH 179/493] fix: javadoc issues uncovered when using -Prelease (#404) --- .../extras/taskstore/database/jpa/JpaDatabaseTaskStore.java | 4 ++-- .../src/main/java/io/a2a/server/tasks/TaskStateProvider.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index c4d814b78..a90cf6294 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -94,12 +94,12 @@ public void delete(String taskId) { /** * Determines if a task is considered active for queue management purposes. - *

- * A task is active if: + *

A task is active if:

*
    *
  • Its state is not final, OR
  • *
  • Its state is final but it was finalized within the grace period
  • *
+ *

* The grace period handles the race condition where events are published to Kafka * while a task is active, but consumed on a replica node after the task is finalized. *

diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java index f467ee516..837befa48 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStateProvider.java @@ -28,7 +28,7 @@ public interface TaskStateProvider { *

*
    *
  • Its state is not final, OR
  • - *
  • Its state is final but finalized within the grace period (now < finalizedAt + gracePeriod)
  • + *
  • Its state is final but finalized within the grace period (now < finalizedAt + gracePeriod)
  • *
*

* This method is used to decide whether to process late-arriving events. From a3fb5f04f392405865e13a7b0290797b43010b12 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 28 Oct 2025 14:45:58 +0000 Subject: [PATCH 180/493] chore: Add workflow to build with -Prelease (#405) --- .../workflows/build-with-release-profile.yml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/build-with-release-profile.yml diff --git a/.github/workflows/build-with-release-profile.yml b/.github/workflows/build-with-release-profile.yml new file mode 100644 index 000000000..2eafdcafc --- /dev/null +++ b/.github/workflows/build-with-release-profile.yml @@ -0,0 +1,61 @@ +name: Build with '-Prelease' + +# Simply runs the build with -Prelease to avoid nasty surprises when running the release-to-maven-central workflow. + + +on: + # Handle all branches for now + push: + pull_request: + workflow_dispatch: + +# Only run the latest job +concurrency: + group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + build: + # Only run this job for the main repository, not for forks + if: github.repository == 'a2aproject/a2a-java' + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Use secrets to import GPG key + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} + passphrase: ${{ secrets.GPG_SIGNING_PASSPHRASE }} + + # Create settings.xml for Maven since it needs the 'central-a2asdk-temp' server. + # Populate wqith username and password from secrets + - name: Create settings.xml + run: | + mkdir -p ~/.m2 + echo "central-a2asdk-temp${{ secrets.CENTRAL_TOKEN_USERNAME }}${{ secrets.CENTRAL_TOKEN_PASSWORD }}" > ~/.m2/settings.xml + + # Deploy to Maven Central + # -s uses the settings file we created. + - name: Build with same arguments as deploy job + run: > + mvn -B install + -s ~/.m2/settings.xml + -P release + -DskipTests + -Drelease.auto.publish=true + env: + # GPG passphrase is set as an environment variable for the gpg plugin to use + GPG_PASSPHRASE: ${{ secrets.GPG_SIGNING_PASSPHRASE }} \ No newline at end of file From 0719afa13c684a3f07827ccba69572f924f9f9d0 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 28 Oct 2025 10:47:38 -0400 Subject: [PATCH 181/493] chore: Release 0.3.0.Final --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/cloud-deployment/server/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/common/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests-multi-instance/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-1/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-2/pom.xml | 2 +- .../tests-multi-instance/quarkus-common/pom.xml | 2 +- .../tests-multi-instance/tests/pom.xml | 2 +- extras/queue-manager-replicated/tests-single-instance/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 37 files changed, 38 insertions(+), 38 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 325175a12..332ddd995 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 7965dcaa2..ae949cc12 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 278e4fb96..0b8e5c819 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 4353383c0..3658a0b45 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index bb806abb8..b07d672d4 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 821ae168c..4a762745c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-common diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index f6de4ed4c..00572ed8d 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../../pom.xml diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 8f5b63406..90da6e004 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 5a3e46a4f..24b0d6159 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.4.0.Alpha1-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.4.0.Alpha1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Final +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Final //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index d5ebb596d..66940a387 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 5c660336f..96001f9f4 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-examples-server diff --git a/extras/common/pom.xml b/extras/common/pom.xml index 416a39df6..f69af2bbf 100644 --- a/extras/common/pom.xml +++ b/extras/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 821eaa5d9..c8fefc5e4 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index c5d1ee8bf..4fc4550b7 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index f4da4d1e4..b8c3fef98 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 77f51477d..917ea52f8 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml index 67c1bb384..9993f83a9 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml index 04d1389e5..ad1935815 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml index 66cdb7822..a8d6e34e4 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml index e54b8f786..35ddc799d 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml index fe378dd29..94d1438bf 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index b92043be6..dad8ef5e0 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index 3f5aee4f1..f7b8816a6 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index 98640e8f2..56bc6db24 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index 02129d631..46d29db66 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index f93374a16..9c16f1158 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 0db7b0ca7..0e84ea936 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 11fa35377..9ceb7800f 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index b1ef2b03c..3a8416335 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index 2165790ed..d48f69036 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 4610f3142..e7805eb4f 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index af6a5a023..afe9f76bb 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 1a9a984c9..de276a2bc 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index da377b10a..ac01d5aff 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 6fdf4fc97..4f428d1ff 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 2b302ec53..0b4bd027b 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 749f97771..7dc589343 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.4.0.Alpha1-SNAPSHOT + 0.3.0.Final ../../pom.xml a2a-java-sdk-transport-rest From 5dcef7e5d8bc313069f95b79d23d576e086f56fe Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Tue, 28 Oct 2025 10:55:04 -0400 Subject: [PATCH 182/493] chore: Next is 0.4.0.Alpha1 --- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/cloud-deployment/server/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/common/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests-multi-instance/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-1/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-2/pom.xml | 2 +- .../tests-multi-instance/quarkus-common/pom.xml | 2 +- .../tests-multi-instance/tests/pom.xml | 2 +- extras/queue-manager-replicated/tests-single-instance/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 37 files changed, 38 insertions(+), 38 deletions(-) diff --git a/client/base/pom.xml b/client/base/pom.xml index 332ddd995..325175a12 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index ae949cc12..7965dcaa2 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 0b8e5c819..278e4fb96 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 3658a0b45..4353383c0 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index b07d672d4..bb806abb8 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 4a762745c..821ae168c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-common diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index 00572ed8d..f6de4ed4c 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../../pom.xml diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 90da6e004..8f5b63406 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 24b0d6159..5a3e46a4f 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:0.3.0.Final -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.3.0.Final +//DEPS io.github.a2asdk:a2a-java-sdk-client:0.4.0.Alpha1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:0.4.0.Alpha1-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index 66940a387..d5ebb596d 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 96001f9f4..5c660336f 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-examples-server diff --git a/extras/common/pom.xml b/extras/common/pom.xml index f69af2bbf..416a39df6 100644 --- a/extras/common/pom.xml +++ b/extras/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index c8fefc5e4..821eaa5d9 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 4fc4550b7..c5d1ee8bf 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index b8c3fef98..f4da4d1e4 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 917ea52f8..77f51477d 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml index 9993f83a9..67c1bb384 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml index ad1935815..04d1389e5 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml index a8d6e34e4..66cdb7822 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml index 35ddc799d..e54b8f786 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml index 94d1438bf..fe378dd29 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index dad8ef5e0..b92043be6 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index f7b8816a6..3f5aee4f1 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index 56bc6db24..98640e8f2 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-http-client diff --git a/pom.xml b/pom.xml index 46d29db66..02129d631 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 9c16f1158..f93374a16 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 0e84ea936..0db7b0ca7 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 9ceb7800f..11fa35377 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 3a8416335..b1ef2b03c 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index d48f69036..2165790ed 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index e7805eb4f..4610f3142 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index afe9f76bb..af6a5a023 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index de276a2bc..1a9a984c9 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index ac01d5aff..da377b10a 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 4f428d1ff..6fdf4fc97 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 0b4bd027b..2b302ec53 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 7dc589343..749f97771 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 0.3.0.Final + 0.4.0.Alpha1-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-rest From 758056dab03e0c05f83b591b690e7f65532e95d1 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 28 Oct 2025 15:20:06 +0000 Subject: [PATCH 183/493] fix: use pull_request_trigger to get access to secrets (#406) --- .github/workflows/build-with-release-profile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-with-release-profile.yml b/.github/workflows/build-with-release-profile.yml index 2eafdcafc..129833307 100644 --- a/.github/workflows/build-with-release-profile.yml +++ b/.github/workflows/build-with-release-profile.yml @@ -6,7 +6,7 @@ name: Build with '-Prelease' on: # Handle all branches for now push: - pull_request: + pull_request_target: workflow_dispatch: # Only run the latest job @@ -47,7 +47,7 @@ jobs: mkdir -p ~/.m2 echo "central-a2asdk-temp${{ secrets.CENTRAL_TOKEN_USERNAME }}${{ secrets.CENTRAL_TOKEN_PASSWORD }}" > ~/.m2/settings.xml - # Deploy to Maven Central + # Build with the same settings as the deploy job # -s uses the settings file we created. - name: Build with same arguments as deploy job run: > From f9cfbdd797541fa749c7c3f6f6e4217503e71d06 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 29 Oct 2025 12:14:05 -0400 Subject: [PATCH 184/493] fix: Add missing extensions to Artifact and Message (#409) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #408 🦕 --- .../core/EventSerializationTest.java | 2 +- .../io/a2a/server/util/ArtifactUtils.java | 1 + .../java/io/a2a/grpc/utils/ProtoUtils.java | 6 ++++-- spec/src/main/java/io/a2a/spec/Artifact.java | 12 +++++++++-- spec/src/main/java/io/a2a/spec/Message.java | 21 +++++++++++++++---- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index f05201fb7..7e18ca4a8 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -139,7 +139,7 @@ public void testTaskStatusUpdateEventSerialization() throws JsonProcessingExcept public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingException { // Create a TaskArtifactUpdateEvent List> parts = List.of(new TextPart("Test artifact content")); - Artifact artifact = new Artifact("test-artifact-123", "Test Artifact", "Test description", parts, null); + Artifact artifact = new Artifact("test-artifact-123", "Test Artifact", "Test description", parts, null, null); TaskArtifactUpdateEvent originalEvent = new TaskArtifactUpdateEvent.Builder() .taskId("test-task-xyz") .contextId("test-context-uvw") diff --git a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java index 7a8637680..406113fd4 100644 --- a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java +++ b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java @@ -32,6 +32,7 @@ public static Artifact newArtifact(String name, List> parts, String desc name, description, parts, + null, null ); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 701947b42..357016054 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -875,7 +875,8 @@ public static Message message(io.a2a.grpc.MessageOrBuilder message) { message.getContextId().isEmpty() ? null : message.getContextId(), message.getTaskId().isEmpty() ? null : message.getTaskId(), null, // referenceTaskIds is not in grpc message - struct(message.getMetadata()) + struct(message.getMetadata()), + message.getExtensionsList().isEmpty() ? null : message.getExtensionsList() ); } @@ -906,7 +907,8 @@ private static Artifact artifact(io.a2a.grpc.ArtifactOrBuilder artifact) { artifact.getName(), artifact.getDescription(), artifact.getPartsList().stream().map(item -> part(item)).collect(Collectors.toList()), - struct(artifact.getMetadata()) + struct(artifact.getMetadata()), + artifact.getExtensionsList().isEmpty() ? null : artifact.getExtensionsList() ); } diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java index cb4d1504b..87a48cee4 100644 --- a/spec/src/main/java/io/a2a/spec/Artifact.java +++ b/spec/src/main/java/io/a2a/spec/Artifact.java @@ -12,7 +12,8 @@ */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record Artifact(String artifactId, String name, String description, List> parts, Map metadata) { +public record Artifact(String artifactId, String name, String description, List> parts, Map metadata, + List extensions) { public Artifact { Assert.checkNotNullParam("artifactId", artifactId); @@ -28,6 +29,7 @@ public static class Builder { private String description; private List> parts; private Map metadata; + private List extensions; public Builder(){ } @@ -38,6 +40,7 @@ public Builder(Artifact existingArtifact) { description = existingArtifact.description; parts = existingArtifact.parts; metadata = existingArtifact.metadata; + extensions = existingArtifact.extensions; } public Builder artifactId(String artifactId) { @@ -71,8 +74,13 @@ public Builder metadata(Map metadata) { return this; } + public Builder extensions(List extensions) { + this.extensions = this.extensions = (extensions == null) ? null : List.copyOf(extensions); + return this; + } + public Artifact build() { - return new Artifact(artifactId, name, description, parts, metadata); + return new Artifact(artifactId, name, description, parts, metadata, extensions); } } } diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index 8278bec7e..dd7e860a5 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -34,17 +34,18 @@ public final class Message implements EventKind, StreamingEventKind { private final Map metadata; private final String kind; private final List referenceTaskIds; + private final List extensions; public Message(Role role, List> parts, String messageId, String contextId, String taskId, - List referenceTaskIds, Map metadata) { - this(role, parts, messageId, contextId, taskId, referenceTaskIds, metadata, MESSAGE); + List referenceTaskIds, Map metadata, List extensions) { + this(role, parts, messageId, contextId, taskId, referenceTaskIds, metadata, extensions, MESSAGE); } @JsonCreator public Message(@JsonProperty("role") Role role, @JsonProperty("parts") List> parts, @JsonProperty("messageId") String messageId, @JsonProperty("contextId") String contextId, @JsonProperty("taskId") String taskId, @JsonProperty("referenceTaskIds") List referenceTaskIds, - @JsonProperty("metadata") Map metadata, + @JsonProperty("metadata") Map metadata, @JsonProperty("extensions") List extensions, @JsonProperty("kind") String kind) { Assert.checkNotNullParam("kind", kind); Assert.checkNotNullParam("parts", parts); @@ -63,6 +64,7 @@ public Message(@JsonProperty("role") Role role, @JsonProperty("parts") List getReferenceTaskIds() { return referenceTaskIds; } + public List getExtensions() { + return extensions; + } + @Override public String getKind() { return kind; @@ -132,6 +138,7 @@ public static class Builder { private String taskId; private List referenceTaskIds; private Map metadata; + private List extensions; public Builder() { } @@ -144,6 +151,7 @@ public Builder(Message message) { taskId = message.taskId; referenceTaskIds = message.referenceTaskIds; metadata = message.metadata; + extensions = message.extensions; } public Builder role(Role role) { @@ -186,9 +194,14 @@ public Builder metadata(Map metadata) { return this; } + public Builder extensions(List extensions) { + this.extensions = (extensions == null) ? null : List.copyOf(extensions); + return this; + } + public Message build() { return new Message(role, parts, messageId == null ? UUID.randomUUID().toString() : messageId, - contextId, taskId, referenceTaskIds, metadata); + contextId, taskId, referenceTaskIds, metadata, extensions); } } } From 3db87786d12f36f81b9da68f929bc965a598e302 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 29 Oct 2025 12:15:43 -0400 Subject: [PATCH 185/493] chore: Update COMMUNITY_ARTICLES.md (#407) --- COMMUNITY_ARTICLES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/COMMUNITY_ARTICLES.md b/COMMUNITY_ARTICLES.md index f28ef059a..65f3b70a4 100644 --- a/COMMUNITY_ARTICLES.md +++ b/COMMUNITY_ARTICLES.md @@ -17,10 +17,14 @@ Here are some articles and videos from the community that you may find helpful. * [Getting Started with Quarkus and A2A Java SDK 0.3.0](https://quarkus.io/blog/quarkus-a2a-java-0-3-0-alpha-release/): Learn about what's new in the 0.3.0 version of the A2A Java SDK. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/getting-started-with-quarkus-and-a2a-java-sdk-0-3-0-c4e70620a805). -* [A2A Java SDK: Support for the REST Transport is Now Here](https://medium.com/google-cloud/a2a-java-sdk-support-for-the-rest-transport-is-now-here-56c666060b32): Learn about the new support for the REST transport introduced in A2A Java SDK 0.3.0.Beta1. +* [A2A Java SDK: Support for the REST Transport is Now Here](https://quarkus.io/blog/quarkus-a2a-java-0-3-0-beta-release/): Learn about the new support for the REST transport introduced in A2A Java SDK 0.3.0.Beta1. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/a2a-java-sdk-support-for-the-rest-transport-is-now-here-56c666060b32). * [Multi-Language Agent Collaboration and Interoperability with A2A](https://medium.com/google-cloud/multi-language-agent-collaboration-and-interoperability-with-a2a-e5fe80b4962d): A deep dive into a multi-language, multi-agent sample where agents written in Java, Python, and TypeScript work together to achieve a common goal: content creation. +* [Getting Started with A2A Java SDK and gRPC](https://quarkus.io/blog/quarkus-a2a-java-grpc/): Let's learn how to create an A2A server agent and an A2A client that can communicate using the gRPC transport. This article also appeared in the [Medium Google Cloud publication](https://medium.com/google-cloud/getting-started-with-the-a2a-java-sdk-and-grpc-11c987df73c1). + +* [How to Secure Your A2A Server Agent with Keycloak OAuth2](https://quarkus.io/blog/quarkus-a2a-java-security/): This blog post shows how to secure an A2A server agent using OAuth2 bearer tokens via Keycloak and shows how to enable an A2A client to automatically obtain and pass the required token in each request. + ## A2A Java SDK for Jakarta Servers * [Building your First A2A Agent](https://www.wildfly.org/news/2025/08/07/Building-your-First-A2A-Agent/): An introduction on how to use the A2A Java SDK for Jakarta Servers with WildFly. From dc604d483b3eda31acc7aaa8be1f4eb10c5041d2 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 29 Oct 2025 19:02:27 -0400 Subject: [PATCH 186/493] fix: Fix typo in Artifact.Builder#extensions (#413) --- spec/src/main/java/io/a2a/spec/Artifact.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java index 87a48cee4..798ac5823 100644 --- a/spec/src/main/java/io/a2a/spec/Artifact.java +++ b/spec/src/main/java/io/a2a/spec/Artifact.java @@ -75,7 +75,7 @@ public Builder metadata(Map metadata) { } public Builder extensions(List extensions) { - this.extensions = this.extensions = (extensions == null) ? null : List.copyOf(extensions); + this.extensions = (extensions == null) ? null : List.copyOf(extensions); return this; } From 21bc0bd3a3719b88b5758c67df4eb1944718a2f3 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 30 Oct 2025 16:01:26 +0000 Subject: [PATCH 187/493] fix: Clean up the k8s example pom, and the extras application properties (#415) Fixes #414 --- examples/cloud-deployment/server/pom.xml | 29 ++++--------------- .../src/main/resources/application.properties | 3 -- .../src/main/resources/application.properties | 3 -- .../src/test/resources/application.properties | 3 -- 4 files changed, 6 insertions(+), 32 deletions(-) diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index f6de4ed4c..a3c0792b5 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -17,7 +17,7 @@ Example demonstrating A2A agent deployment in Kubernetes with database persistence and event replication - + io.github.a2asdk a2a-java-sdk-reference-jsonrpc @@ -45,14 +45,17 @@ ${project.version} - + io.github.a2asdk a2a-java-queue-manager-replication-mp-reactive ${project.version} - + io.quarkus quarkus-messaging-kafka @@ -70,32 +73,12 @@ quarkus-hibernate-orm - - - io.quarkus - quarkus-resteasy-jackson - - io.quarkus quarkus-smallrye-health - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - - - - - org.slf4j - slf4j-api - - - io.github.a2asdk a2a-java-sdk-client diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties index f2d484ef3..d0692ca53 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties @@ -1,9 +1,6 @@ # Application HTTP Port quarkus.http.port=8081 -# Select our ReplicatedQueueManager and JpaDatabaseTaskStore as the active implementations -quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager,io.a2a.extras.taskstore.database.jpa.JpaDatabaseTaskStore - # Configure PostgreSQL database (connection details will be provided by Testcontainers) quarkus.datasource."a2a-java".db-kind=postgresql quarkus.datasource."a2a-java".jdbc.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/a2adb} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties index ca4698aa2..0b647f3a5 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties @@ -1,9 +1,6 @@ # Application HTTP Port quarkus.http.port=8082 -# Select our ReplicatedQueueManager and JpaDatabaseTaskStore as the active implementations -quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager,io.a2a.extras.taskstore.database.jpa.JpaDatabaseTaskStore - # Configure PostgreSQL database (connection details will be provided by Testcontainers) quarkus.datasource."a2a-java".db-kind=postgresql quarkus.datasource."a2a-java".jdbc.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/a2adb} diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/resources/application.properties b/extras/queue-manager-replicated/tests-single-instance/src/test/resources/application.properties index 1e02c4559..15aed5bc3 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/resources/application.properties +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/resources/application.properties @@ -1,6 +1,3 @@ -# Select our ReplicatedQueueManager as the active implementation -quarkus.arc.selected-alternatives=io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager,io.a2a.extras.taskstore.database.jpa.JpaDatabaseTaskStore - # Configure in-memory H2 database for testing quarkus.datasource."a2a-java".db-kind=h2 quarkus.datasource."a2a-java".jdbc.url=jdbc:h2:mem:test From 19503638416e840b3bc5fa2e99912aee2e1c1d22 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 30 Oct 2025 17:53:20 +0000 Subject: [PATCH 188/493] fix: Changes needed to have ConfigProperty injection working in Jakarta (#417) --- .../java/io/a2a/server/util/async/AsyncExecutorProducer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index d6f0e996e..49e69f99e 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -12,6 +12,8 @@ import jakarta.annotation.PreDestroy; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,12 +23,15 @@ public class AsyncExecutorProducer { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncExecutorProducer.class); + @Inject // Needed to work in standard Jakarta runtimes (Quarkus skips this) @ConfigProperty(name = "a2a.executor.core-pool-size", defaultValue = "5") int corePoolSize; + @Inject // Needed to work in standard Jakarta runtimes (Quarkus skips this) @ConfigProperty(name = "a2a.executor.max-pool-size", defaultValue = "50") int maxPoolSize; + @Inject // Needed to work in standard Jakarta runtimes (Quarkus skips this) @ConfigProperty(name = "a2a.executor.keep-alive-seconds", defaultValue = "60") long keepAliveSeconds; From 5031b8faba7b78bbd508a3d21a6b1940ecf19013 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 30 Oct 2025 20:50:16 +0000 Subject: [PATCH 189/493] chore: Run TCK on 0.3.x branch too, and use latest TCK (#421) --- .github/workflows/run-tck.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 8dddf8124..375d7f6d3 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -5,14 +5,17 @@ on: push: branches: - main + - 0.3.x pull_request: branches: - main + - 0.3.x workflow_dispatch: env: + # TODO once we have the TCK for 0.4.0 we will need to look at the branch to decide which TCK version to run. # Tag of the TCK - TCK_VERSION: 0.3.0.beta2 + TCK_VERSION: 0.3.0.beta3 # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 # SUT_JSONRPC_URL to use for the TCK and the server agent From c0f34ee3bb7be10a8d0fcfd900c7ee47e930b38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=AA=85=ED=98=84?= <117622409+flex-myeonghyeon@users.noreply.github.com> Date: Tue, 4 Nov 2025 01:54:43 +0900 Subject: [PATCH 190/493] fix: clear status.message after adding to history in TaskManager.updateWithMessage #424 (#425) --- .../java/io/a2a/server/tasks/TaskManager.java | 10 +++-- .../io/a2a/server/tasks/TaskManagerTest.java | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 523b9346f..dff182af4 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -104,12 +104,16 @@ public Event process(Event event) throws A2AServerException { } public Task updateWithMessage(Message message, Task task) { - List history = task.getHistory() == null ? new ArrayList<>() : new ArrayList<>(task.getHistory()); - if (task.getStatus().message() != null) { - history.add(task.getStatus().message()); + List history = new ArrayList<>(task.getHistory()); + + TaskStatus status = task.getStatus(); + if (status.message() != null) { + history.add(status.message()); + status = new TaskStatus(status.state(), null, status.timestamp()); } history.add(message); task = new Task.Builder(task) + .status(status) .history(history) .build(); saveTask(task); diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 25b5ead11..637509ccf 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -692,4 +692,48 @@ public void testSaveTaskInternal() throws A2AServerException { assertEquals("test-context", taskManagerWithoutId.getContextId()); assertSame(savedTask, taskManagerWithoutId.getTask()); } + + @Test + public void testUpdateWithMessage() throws A2AServerException { + Message initialMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("initial message"))) + .messageId("initial-msg-id") + .build(); + + TaskManager taskManagerWithInitialMessage = new TaskManager(null, null, taskStore, initialMessage); + + Message taskMessage = new Message.Builder() + .role(Message.Role.AGENT) + .parts(Collections.singletonList(new TextPart("task message"))) + .messageId("task-msg-id") + .build(); + + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("new-task-id") + .contextId("some-context") + .status(new TaskStatus(TaskState.SUBMITTED, taskMessage, null)) + .isFinal(false) + .build(); + + Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); + + Message updateMessage = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("update message"))) + .messageId("update-msg-id") + .build(); + + Task updated = taskManagerWithInitialMessage.updateWithMessage(updateMessage, saved); + + // There should now be a history containing the initialMessage, task message and update message + assertNotNull(updated.getHistory()); + assertEquals(3, updated.getHistory().size()); + assertEquals("initial message", ((TextPart) updated.getHistory().get(0).getParts().get(0)).getText()); + + // The message in the current state should be null + assertNull(updated.getStatus().message()); + assertEquals("task message", ((TextPart) updated.getHistory().get(1).getParts().get(0)).getText()); + assertEquals("update message", ((TextPart) updated.getHistory().get(2).getParts().get(0)).getText()); + } } From 425508de32b3736c4d70ce1066276cf754cd8682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=AA=85=ED=98=84?= <117622409+flex-myeonghyeon@users.noreply.github.com> Date: Tue, 4 Nov 2025 02:01:05 +0900 Subject: [PATCH 191/493] fix: merge metadata instead of replacing in TaskManager.TaskStatusUpdateEvent #426 (#427) --- .../src/main/java/io/a2a/server/tasks/TaskManager.java | 6 +++++- .../test/java/io/a2a/server/tasks/TaskManagerTest.java | 10 ++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index dff182af4..4b1112e22 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -5,7 +5,9 @@ import static io.a2a.util.Utils.appendArtifactToTask; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; @@ -78,7 +80,9 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { // Handle metadata from the event if (event.getMetadata() != null) { - builder.metadata(event.getMetadata()); + Map metadata = task.getMetadata() == null ? new HashMap<>() : new HashMap<>(task.getMetadata()); + metadata.putAll(event.getMetadata()); + builder.metadata(metadata); } task = builder.build(); diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 637509ccf..1a4850bda 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -588,8 +588,8 @@ public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { } @Test - public void testSaveTaskEventMetadataUpdateOverwritesExisting() throws A2AServerException { - // Test that metadata update overwrites existing metadata + public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { + // Test that metadata update merges with existing metadata Map originalMetadata = new HashMap<>(); originalMetadata.put("original_key", "original_value"); @@ -612,8 +612,10 @@ public void testSaveTaskEventMetadataUpdateOverwritesExisting() throws A2AServer taskManager.saveTaskEvent(event); Task updatedTask = taskManager.getTask(); - assertEquals(newMetadata, updatedTask.getMetadata()); - assertNotEquals(originalMetadata, updatedTask.getMetadata()); + + Map mergedMetadata = new HashMap<>(originalMetadata); + mergedMetadata.putAll(newMetadata); + assertEquals(mergedMetadata, updatedTask.getMetadata()); } @Test From f36db5ee5c250421e99716fc1062e17258eaddb8 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Mon, 3 Nov 2025 19:27:38 +0100 Subject: [PATCH 192/493] fix: fixing the handling of historyLength being set to 0 by default in gRPC (#429) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Using int instead of Integer and setting the default value to 0. * If historyLength == 0 then we should return the full history * Removing the handling of null Issue: https://github.com/a2aproject/a2a-java/issues/423 Fixes #423 🦕 Signed-off-by: Emmanuel Hugonnet --- .../java/io/a2a/client/transport/grpc/GrpcTransport.java | 8 +------- .../java/io/a2a/client/transport/rest/RestTransport.java | 2 +- .../java/io/a2a/server/rest/quarkus/A2AServerRoutes.java | 4 ++-- .../io/a2a/server/rest/quarkus/A2AServerRoutesTest.java | 5 +++-- .../a2a/server/requesthandlers/DefaultRequestHandler.java | 6 ++---- spec/src/main/java/io/a2a/spec/TaskQueryParams.java | 8 ++++---- .../java/io/a2a/transport/rest/handler/RestHandler.java | 2 +- .../io/a2a/transport/rest/handler/RestHandlerTest.java | 6 +++--- 8 files changed, 17 insertions(+), 24 deletions(-) diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 5a8437679..224433393 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -1,9 +1,5 @@ package io.a2a.client.transport.grpc; -import static io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub; -import static io.a2a.grpc.A2AServiceGrpc.A2AServiceStub; -import static io.a2a.grpc.utils.ProtoUtils.FromProto; -import static io.a2a.grpc.utils.ProtoUtils.ToProto; import static io.a2a.util.Assert.checkNotNullParam; import java.util.List; @@ -127,9 +123,7 @@ public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context GetTaskRequest.Builder requestBuilder = GetTaskRequest.newBuilder(); requestBuilder.setName("tasks/" + request.id()); - if (request.historyLength() != null) { - requestBuilder.setHistoryLength(request.historyLength()); - } + requestBuilder.setHistoryLength(request.historyLength()); GetTaskRequest getTaskRequest = requestBuilder.build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskRequest.METHOD, getTaskRequest, agentCard, context); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index f659589b7..5b67d97e0 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -125,7 +125,7 @@ public Task getTask(TaskQueryParams taskQueryParams, @Nullable ClientCallContext agentCard, context); try { String url; - if (taskQueryParams.historyLength() != null) { + if (taskQueryParams.historyLength() > 0) { url = agentUrl + String.format("/v1/tasks/%1s?historyLength=%2d", taskQueryParams.id(), taskQueryParams.historyLength()); } else { url = agentUrl + String.format("/v1/tasks/%1s", taskQueryParams.id()); diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index b923dde5c..4f0173070 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -122,9 +122,9 @@ public void getTask(RoutingContext rc) { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - Integer historyLength = null; + int historyLength = 0; if (rc.request().params().contains("history_length")) { - historyLength = Integer.valueOf(rc.request().params().get("history_length")); + historyLength = Integer.parseInt(rc.request().params().get("history_length")); } response = jsonRestHandler.getTask(taskId, historyLength, context); } diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java index 83d54a40c..09cbc9926 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -137,7 +138,7 @@ public void testGetTask_MethodNameSetInContext() { when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{test:value}"); - when(mockRestHandler.getTask(anyString(), any(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + when(mockRestHandler.getTask(anyString(), anyInt(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -145,7 +146,7 @@ public void testGetTask_MethodNameSetInContext() { routes.getTask(mockRoutingContext); // Assert - verify(mockRestHandler).getTask(eq("task123"), eq(null), contextCaptor.capture()); + verify(mockRestHandler).getTask(eq("task123"), anyInt(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(GetTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index c05dcfd24..bb85eea6e 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -19,7 +19,6 @@ import java.util.function.Supplier; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import io.a2a.server.ServerCallContext; @@ -34,7 +33,6 @@ import io.a2a.server.events.TaskQueueExistsException; import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.server.tasks.PushNotificationSender; -import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; @@ -103,10 +101,10 @@ public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws LOGGER.debug("No task found for {}. Throwing TaskNotFoundError", params.id()); throw new TaskNotFoundError(); } - if (params.historyLength() != null && task.getHistory() != null && params.historyLength() < task.getHistory().size()) { + if (task.getHistory() != null && params.historyLength() < task.getHistory().size()) { List history; if (params.historyLength() <= 0) { - history = new ArrayList<>(); + history = task.getHistory(); } else { history = task.getHistory().subList( task.getHistory().size() - params.historyLength(), diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index a6154d67d..92c2453c5 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -17,20 +17,20 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record TaskQueryParams(String id, @Nullable Integer historyLength, @Nullable Map metadata) { +public record TaskQueryParams(String id, int historyLength, @Nullable Map metadata) { public TaskQueryParams { Assert.checkNotNullParam("id", id); - if (historyLength != null && historyLength < 0) { + if (historyLength < 0) { throw new IllegalArgumentException("Invalid history length"); } } public TaskQueryParams(String id) { - this(id, null, null); + this(id, 0, null); } - public TaskQueryParams(String id, @Nullable Integer historyLength) { + public TaskQueryParams(String id, int historyLength) { this(id, historyLength, null); } } diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 586149948..85d307f53 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -162,7 +162,7 @@ public HTTPRestResponse resubscribeTask(String taskId, ServerCallContext context } } - public HTTPRestResponse getTask(String taskId, @Nullable Integer historyLength, ServerCallContext context) { + public HTTPRestResponse getTask(String taskId, int historyLength, ServerCallContext context) { try { TaskQueryParams params = new TaskQueryParams(taskId, historyLength); Task task = requestHandler.onGetTask(params, context); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 58e74f85e..74a505236 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -26,7 +26,7 @@ public void testGetTaskSuccess() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(),null, callContext); + RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(), 0, callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -43,7 +43,7 @@ public void testGetTaskSuccess() { public void testGetTaskNotFound() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - RestHandler.HTTPRestResponse response = handler.getTask("nonexistent", null, callContext); + RestHandler.HTTPRestResponse response = handler.getTask("nonexistent", 0, callContext); Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -315,7 +315,7 @@ public void testHttpStatusCodeMapping() { Assertions.assertEquals(400, response.getStatusCode()); // Test 404 for not found - response = handler.getTask("nonexistent", null, callContext); + response = handler.getTask("nonexistent", 0, callContext); Assertions.assertEquals(404, response.getStatusCode()); } From e2eba4e6fca81bad0500951bfa1535f81cf7b668 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 4 Nov 2025 11:54:33 +0000 Subject: [PATCH 193/493] fix: Change image for the multi instance replicated queuemanager test (#439) openjdk:17-slim has disappeared --- .../tests-multi-instance/quarkus-app-1/Dockerfile | 2 +- .../tests-multi-instance/quarkus-app-2/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile index fb8d091ba..218da8c35 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM mirror.gcr.io/eclipse-temurin:17-jre-jammy WORKDIR /app diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile index 920d34747..bc0b294be 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM mirror.gcr.io/eclipse-temurin:17-jre-jammy WORKDIR /app From 5c5970df5dcd85dd38892f9651c047a4a2ab3126 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 4 Nov 2025 13:54:33 +0000 Subject: [PATCH 194/493] fix: Make sure we use @Inject on @ConfigProperty (#437) Jakarta needs this or it ignores the config --- .../java/io/a2a/examples/cloud/CloudAgentCardProducer.java | 3 +++ .../extras/taskstore/database/jpa/JpaDatabaseTaskStore.java | 1 + 2 files changed, 4 insertions(+) diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java index 3a35a6e8b..2894c84a9 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -6,6 +6,8 @@ import io.a2a.spec.AgentSkill; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + import org.eclipse.microprofile.config.inject.ConfigProperty; import java.util.Collections; @@ -17,6 +19,7 @@ @ApplicationScoped public class CloudAgentCardProducer { + @Inject @ConfigProperty(name = "agent.url", defaultValue = "http://localhost:8080") String agentUrl; diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index a90cf6294..44837ae85 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -34,6 +34,7 @@ public class JpaDatabaseTaskStore implements TaskStore, TaskStateProvider { @Inject Event taskFinalizedEvent; + @Inject @ConfigProperty(name = "a2a.replication.grace-period-seconds", defaultValue = "15") long gracePeriodSeconds; From 465a5e420d8be4ac7c547d9e1fd256f3c6da952f Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 4 Nov 2025 15:09:16 +0000 Subject: [PATCH 195/493] =?UTF-8?q?fix:=20Wait=20for=20agent=20completion?= =?UTF-8?q?=20and=20ensure=20all=20events=20processed=20in=20blo=E2=80=A6?= =?UTF-8?q?=20(#431)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …cking calls Fixes race condition where DefaultRequestHandler.onMessageSend() returns before all task events are fully processed and persisted to TaskStore, resulting in incomplete Task objects being returned to clients (missing artifacts, incorrect state). Root Cause: - Blocking calls interrupted immediately after first event and returned to client before background event consumption completed - Agent execution and event processing happened asynchronously in background - No synchronization to ensure all events were consumed and persisted before returning Task to client Solution (4-step process): 1. Wait for agent to finish enqueueing events (configurable timeout) 2. Close the queue to signal consumption can complete (breaks dependency) 3. Wait for consumption to finish processing events (configurable timeout) 4. Fetch final task state from TaskStore (has all artifacts and correct state) This ensures blocking calls return complete Task objects with all artifacts and correct state, including support for fire-and-forget tasks that never emit final state events. Added unit tests: - testBlockingFireAndForgetReturnsNonFinalTask: Validates fire-and-forget pattern - testBlockingCallReturnsCompleteTaskWithArtifacts: Ensures all artifacts included Fixes #428 🦕 --- .../DefaultRequestHandler.java | 111 ++++++++++- .../io/a2a/server/tasks/ResultAggregator.java | 39 ++-- .../AbstractA2ARequestHandlerTest.java | 3 +- .../DefaultRequestHandlerTest.java | 184 +++++++++++++++--- .../grpc/handler/GrpcHandlerTest.java | 12 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 23 ++- 6 files changed, 308 insertions(+), 64 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index bb85eea6e..de62f1a0f 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -3,6 +3,7 @@ import static io.a2a.server.util.async.AsyncUtils.convertingProcessor; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import static io.a2a.server.util.async.AsyncUtils.processor; +import static java.util.concurrent.TimeUnit.*; import java.util.ArrayList; import java.util.List; @@ -51,11 +52,12 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskNotCancelableError; -import io.a2a.spec.TaskState; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; import io.a2a.spec.UnsupportedOperationError; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +66,26 @@ public class DefaultRequestHandler implements RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRequestHandler.class); + /** + * Timeout in seconds to wait for agent execution to complete in blocking calls. + * This allows slow agents (LLM-based, data processing, external APIs) sufficient time. + * Configurable via: a2a.blocking.agent.timeout.seconds + * Default: 30 seconds + */ + @Inject + @ConfigProperty(name = "a2a.blocking.agent.timeout.seconds", defaultValue = "30") + int agentCompletionTimeoutSeconds; + + /** + * Timeout in seconds to wait for event consumption to complete in blocking calls. + * This ensures all events are processed and persisted before returning to client. + * Configurable via: a2a.blocking.consumption.timeout.seconds + * Default: 5 seconds + */ + @Inject + @ConfigProperty(name = "a2a.blocking.consumption.timeout.seconds", defaultValue = "5") + int consumptionCompletionTimeoutSeconds; + private final AgentExecutor agentExecutor; private final TaskStore taskStore; private final QueueManager queueManager; @@ -93,6 +115,19 @@ public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, this.requestContextBuilder = () -> new SimpleRequestContextBuilder(taskStore, false); } + /** + * For testing + */ + public static DefaultRequestHandler create(AgentExecutor agentExecutor, TaskStore taskStore, + QueueManager queueManager, PushNotificationConfigStore pushConfigStore, + PushNotificationSender pushSender, Executor executor) { + DefaultRequestHandler handler = + new DefaultRequestHandler(agentExecutor, taskStore, queueManager, pushConfigStore, pushSender, executor); + handler.agentCompletionTimeoutSeconds = 5; + handler.consumptionCompletionTimeoutSeconds = 2; + return handler; + } + @Override public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws JSONRPCError { LOGGER.debug("onGetTask {}", params.id()); @@ -192,6 +227,7 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId, mss.requestContext, queue); ResultAggregator.EventTypeAndInterrupt etai = null; + EventKind kind = null; // Declare outside try block so it's in scope for return try { // Create callback for push notifications during background event processing Runnable pushNotificationCallback = () -> sendPushNotification(taskId, resultAggregator); @@ -201,7 +237,10 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte // This callback must be added before we start consuming. Otherwise, // any errors thrown by the producerRunnable are not picked up by the consumer producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); - etai = resultAggregator.consumeAndBreakOnInterrupt(consumer, blocking, pushNotificationCallback); + + // Get agent future before consuming (for blocking calls to wait for agent completion) + CompletableFuture agentFuture = runningAgents.get(taskId); + etai = resultAggregator.consumeAndBreakOnInterrupt(consumer, blocking); if (etai == null) { LOGGER.debug("No result, throwing InternalError"); @@ -210,7 +249,69 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte interruptedOrNonBlocking = etai.interrupted(); LOGGER.debug("Was interrupted or non-blocking: {}", interruptedOrNonBlocking); - EventKind kind = etai.eventType(); + // For blocking calls that were interrupted (returned on first event), + // wait for agent execution and event processing BEFORE returning to client. + // This ensures the returned Task has all artifacts and current state. + // We do this HERE (not in ResultAggregator) to avoid blocking Vert.x worker threads + // during the consumption loop itself. + kind = etai.eventType(); + if (blocking && interruptedOrNonBlocking) { + // For blocking calls: ensure all events are processed before returning + // Order of operations is critical to avoid circular dependency: + // 1. Wait for agent to finish enqueueing events + // 2. Close the queue to signal consumption can complete + // 3. Wait for consumption to finish processing events + // 4. Fetch final task state from TaskStore + + try { + // Step 1: Wait for agent to finish (with configurable timeout) + if (agentFuture != null) { + try { + agentFuture.get(agentCompletionTimeoutSeconds, SECONDS); + LOGGER.debug("Agent completed for task {}", taskId); + } catch (java.util.concurrent.TimeoutException e) { + // Agent still running after timeout - that's fine, events already being processed + LOGGER.debug("Agent still running for task {} after {}s", taskId, agentCompletionTimeoutSeconds); + } + } + + // Step 2: Close the queue to signal consumption can complete + // For fire-and-forget tasks, there's no final event, so we need to close the queue + // This allows EventConsumer.consumeAll() to exit + queue.close(false, false); // graceful close, don't notify parent yet + LOGGER.debug("Closed queue for task {} to allow consumption completion", taskId); + + // Step 3: Wait for consumption to complete (now that queue is closed) + if (etai.consumptionFuture() != null) { + etai.consumptionFuture().get(consumptionCompletionTimeoutSeconds, SECONDS); + LOGGER.debug("Consumption completed for task {}", taskId); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + String msg = String.format("Error waiting for task %s completion", taskId); + LOGGER.warn(msg, e); + throw new InternalError(msg); + } catch (java.util.concurrent.ExecutionException e) { + String msg = String.format("Error during task %s execution", taskId); + LOGGER.warn(msg, e.getCause()); + throw new InternalError(msg); + } catch (java.util.concurrent.TimeoutException e) { + String msg = String.format("Timeout waiting for consumption to complete for task %s", taskId); + LOGGER.warn(msg, taskId); + throw new InternalError(msg); + } + + // Step 4: Fetch the final task state from TaskStore (all events have been processed) + Task updatedTask = taskStore.get(taskId); + if (updatedTask != null) { + kind = updatedTask; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Fetched final task for {} with state {} and {} artifacts", + taskId, updatedTask.getStatus().state(), + updatedTask.getArtifacts().size()); + } + } + } if (kind instanceof Task taskResult && !taskId.equals(taskResult.getId())) { throw new InternalError("Task ID mismatch in agent response"); } @@ -227,8 +328,8 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte trackBackgroundTask(cleanupProducer(agentFuture, etai != null ? etai.consumptionFuture() : null, taskId, queue, false)); } - LOGGER.debug("Returning: {}", etai.eventType()); - return etai.eventType(); + LOGGER.debug("Returning: {}", kind); + return kind; } @Override diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index f73242491..26767a90b 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -11,20 +11,20 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueueItem; import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.EventKind; +import io.a2a.spec.InternalError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.util.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ResultAggregator { private static final Logger LOGGER = LoggerFactory.getLogger(ResultAggregator.class); @@ -106,10 +106,6 @@ public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { } public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking) throws JSONRPCError { - return consumeAndBreakOnInterrupt(consumer, blocking, null); - } - - public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking, Runnable eventCallback) throws JSONRPCError { Flow.Publisher allItems = consumer.consumeAll(); AtomicReference message = new AtomicReference<>(); AtomicBoolean interrupted = new AtomicBoolean(false); @@ -180,11 +176,11 @@ else if (!blocking) { shouldInterrupt = true; continueInBackground = true; } - else { - // For ALL blocking calls (both final and non-final events), use background consumption - // This ensures all events are processed and persisted to TaskStore in background - // Queue lifecycle is now managed by DefaultRequestHandler.cleanupProducer() - // which waits for BOTH agent and consumption futures before closing queues + else if (blocking) { + // For blocking calls: Interrupt to free Vert.x thread, but continue in background + // Python's async consumption doesn't block threads, but Java's does + // So we interrupt to return quickly, then rely on background consumption + // DefaultRequestHandler will fetch the final state from TaskStore shouldInterrupt = true; continueInBackground = true; if (LOGGER.isDebugEnabled()) { @@ -198,10 +194,17 @@ else if (!blocking) { interrupted.set(true); completionFuture.complete(null); - // Signal that cleanup can proceed while consumption continues in background. - // This prevents infinite hangs for fire-and-forget agents that never emit final events. - // Processing continues (return true below) and all events are still persisted to TaskStore. - consumptionCompletionFuture.complete(null); + // For blocking calls, DON'T complete consumptionCompletionFuture here. + // Let it complete naturally when subscription finishes (onComplete callback below). + // This ensures all events are processed and persisted to TaskStore before + // DefaultRequestHandler.cleanupProducer() proceeds with cleanup. + // + // For non-blocking and auth-required calls, complete immediately to allow + // cleanup to proceed while consumption continues in background. + if (!blocking) { + consumptionCompletionFuture.complete(null); + } + // else: blocking calls wait for actual consumption completion in onComplete // Continue consuming in background - keep requesting events // Note: continueInBackground is always true when shouldInterrupt is true @@ -244,8 +247,8 @@ else if (!blocking) { } } - // Background consumption continues automatically via the subscription - // returning true in the consumer function keeps the subscription alive + // Note: For blocking calls that were interrupted, the wait logic has been moved + // to DefaultRequestHandler.onMessageSend() to avoid blocking Vert.x worker threads. // Queue lifecycle is managed by DefaultRequestHandler.cleanupProducer() Throwable error = errorRef.get(); diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 9447203e1..d654a83a6 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -98,7 +98,8 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC PushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); PushNotificationSender pushSender = new BasePushNotificationSender(pushConfigStore, httpClient); - requestHandler = new DefaultRequestHandler(executor, taskStore, queueManager, pushConfigStore, pushSender, internalExecutor); + requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, pushConfigStore, pushSender, internalExecutor); } @AfterEach diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 968c7812d..4fa3a2a15 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -1,7 +1,10 @@ package io.a2a.server.requesthandlers; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -16,6 +19,7 @@ import io.a2a.server.events.EventQueue; import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.tasks.InMemoryTaskStore; +import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; @@ -50,7 +54,7 @@ void setUp() { queueManager = new InMemoryQueueManager(taskStore); agentExecutor = new TestAgentExecutor(); - requestHandler = new DefaultRequestHandler( + requestHandler = DefaultRequestHandler.create( agentExecutor, taskStore, queueManager, @@ -418,35 +422,99 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception { } /** - * Test that blocking message calls persist all events in background. - * This test proves the bug where blocking calls stop consuming events after - * the first event is returned, causing subsequent events to be lost. + * Test that blocking message call waits for agent to finish and returns complete Task + * even when agent does fire-and-forget (emits non-final state and returns). * * Expected behavior: - * 1. Blocking call returns immediately with first event (WORKING state) + * 1. Agent emits WORKING state with artifacts + * 2. Agent's execute() method returns WITHOUT emitting final state + * 3. Blocking onMessageSend() should wait for agent execution to complete + * 4. Blocking onMessageSend() should wait for all queued events to be processed + * 5. Returned Task should have WORKING state with all artifacts included + * + * This tests fire-and-forget pattern with blocking calls. + */ + @Test + @Timeout(15) + void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception { + String taskId = "blocking-fire-forget-task"; + String contextId = "blocking-fire-forget-ctx"; + + Message message = new Message.Builder() + .messageId("msg-blocking-fire-forget") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendConfiguration config = new MessageSendConfiguration.Builder() + .blocking(true) + .build(); + + MessageSendParams params = new MessageSendParams(message, config, null); + + // Agent that does fire-and-forget: emits WORKING with artifact but never completes + agentExecutor.setExecuteCallback((context, queue) -> { + TaskUpdater updater = new TaskUpdater(context, queue); + + // Start work (WORKING state) + updater.startWork(); + + // Add artifact + updater.addArtifact( + List.of(new TextPart("Fire and forget artifact", null)), + "artifact-1", "FireForget", null); + + // Agent returns WITHOUT calling updater.complete() + // Task stays in WORKING state (non-final) + }); + + // Call blocking onMessageSend - should wait for agent to finish + Object result = requestHandler.onMessageSend(params, serverCallContext); + + // The returned result should be a Task in WORKING state with artifact + assertTrue(result instanceof Task, "Result should be a Task"); + Task returnedTask = (Task) result; + + // Verify task is in WORKING state (non-final, fire-and-forget) + assertEquals(TaskState.WORKING, returnedTask.getStatus().state(), + "Returned task should be WORKING (fire-and-forget), got: " + returnedTask.getStatus().state()); + + // Verify artifacts are included in the returned task + assertNotNull(returnedTask.getArtifacts(), + "Returned task should have artifacts"); + assertTrue(returnedTask.getArtifacts().size() >= 1, + "Returned task should have at least 1 artifact, got: " + + returnedTask.getArtifacts().size()); + } + + /** + * Test that non-blocking message call returns immediately and persists all events in background. + * + * Expected behavior: + * 1. Non-blocking call returns immediately with first event (WORKING state) * 2. Agent continues running in background and produces more events * 3. Background consumption continues and persists all events to TaskStore - * 4. Final task state (COMPLETED) is persisted despite blocking call having returned - * - * This test will FAIL before the fix is applied, demonstrating the bug. + * 4. Final task state (COMPLETED) is persisted in background */ @Test @Timeout(15) - void testBlockingMessagePersistsAllEventsInBackground() throws Exception { + void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { String taskId = "blocking-persist-task"; String contextId = "blocking-persist-ctx"; Message message = new Message.Builder() - .messageId("msg-blocking-persist") + .messageId("msg-nonblocking-persist") .role(Message.Role.USER) .parts(new TextPart("test message")) .taskId(taskId) .contextId(contextId) .build(); - // Explicitly set blocking=true (though it's the default) + // Set blocking=false for non-blocking behavior MessageSendConfiguration config = new MessageSendConfiguration.Builder() - .blocking(true) + .blocking(false) .build(); MessageSendParams params = new MessageSendParams(message, config, null); @@ -468,7 +536,7 @@ void testBlockingMessagePersistsAllEventsInBackground() throws Exception { queue.enqueueEvent(workingTask); firstEventEmitted.countDown(); - // Sleep to ensure the blocking call has returned before we emit more events + // Sleep to ensure the non-blocking call has returned before we emit more events try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -485,8 +553,7 @@ void testBlockingMessagePersistsAllEventsInBackground() throws Exception { } // Emit final event (COMPLETED state) - // This event will be LOST with the current bug, because the consumer - // has already stopped after returning the first event + // This event should be persisted to TaskStore in background Task completedTask = new Task.Builder() .id(taskId) .contextId(contextId) @@ -495,21 +562,21 @@ void testBlockingMessagePersistsAllEventsInBackground() throws Exception { queue.enqueueEvent(completedTask); }); - // Call blocking onMessageSend + // Call non-blocking onMessageSend Object result = requestHandler.onMessageSend(params, serverCallContext); // Assertion 1: The immediate result should be the first event (WORKING) assertTrue(result instanceof Task, "Result should be a Task"); Task immediateTask = (Task) result; - assertTrue(immediateTask.getStatus().state() == TaskState.WORKING, - "Immediate return should show WORKING state, got: " + immediateTask.getStatus().state()); + assertEquals(TaskState.WORKING, immediateTask.getStatus().state(), + "Non-blocking should return immediately with WORKING state, got: " + immediateTask.getStatus().state()); - // At this point, the blocking call has returned, but the agent is still running + // At this point, the non-blocking call has returned, but the agent is still running // Allow the agent to emit the final COMPLETED event allowCompletion.countDown(); - // Assertion 2: Poll for the final task state to be persisted + // Assertion 2: Poll for the final task state to be persisted in background // Use polling loop instead of fixed sleep for faster and more reliable test long timeoutMs = 5000; long startTime = System.currentTimeMillis(); @@ -530,8 +597,7 @@ void testBlockingMessagePersistsAllEventsInBackground() throws Exception { completedStateFound, "Final task state should be COMPLETED (background consumption should have processed it), got: " + (persistedTask != null ? persistedTask.getStatus().state() : "null") + - " after " + (System.currentTimeMillis() - startTime) + "ms. " + - "This failure proves the bug - events after the first are lost." + " after " + (System.currentTimeMillis() - startTime) + "ms" ); } @@ -654,6 +720,80 @@ void testMainQueueClosesForFinalizedTasks() throws Exception { "Queue for finalized task should be null or closed"); } + /** + * Test that blocking message call returns a Task with ALL artifacts included. + * This reproduces the reported bug: blocking call returns before artifacts are processed. + * + * Expected behavior: + * 1. Agent emits multiple artifacts via TaskUpdater + * 2. Blocking onMessageSend() should wait for ALL events to be processed + * 3. Returned Task should have all artifacts included in COMPLETED state + * + * Bug manifestation: + * - onMessageSend() returns after first event + * - Artifacts are still being processed in background + * - Returned Task is incomplete + */ + @Test + @Timeout(15) + void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception { + String taskId = "blocking-artifacts-task"; + String contextId = "blocking-artifacts-ctx"; + + Message message = new Message.Builder() + .messageId("msg-blocking-artifacts") + .role(Message.Role.USER) + .parts(new TextPart("test message")) + .taskId(taskId) + .contextId(contextId) + .build(); + + MessageSendConfiguration config = new MessageSendConfiguration.Builder() + .blocking(true) + .build(); + + MessageSendParams params = new MessageSendParams(message, config, null); + + // Agent that uses TaskUpdater to emit multiple artifacts (like real agents do) + agentExecutor.setExecuteCallback((context, queue) -> { + TaskUpdater updater = new TaskUpdater(context, queue); + + // Start work (WORKING state) + updater.startWork(); + + // Add first artifact + updater.addArtifact( + List.of(new TextPart("First artifact", null)), + "artifact-1", "First", null); + + // Add second artifact + updater.addArtifact( + List.of(new TextPart("Second artifact", null)), + "artifact-2", "Second", null); + + // Complete the task + updater.complete(); + }); + + // Call blocking onMessageSend - should wait for ALL events + Object result = requestHandler.onMessageSend(params, serverCallContext); + + // The returned result should be a Task with ALL artifacts + assertTrue(result instanceof Task, "Result should be a Task"); + Task returnedTask = (Task) result; + + // Verify task is completed + assertEquals(TaskState.COMPLETED, returnedTask.getStatus().state(), + "Returned task should be COMPLETED"); + + // Verify artifacts are included in the returned task + assertNotNull(returnedTask.getArtifacts(), + "Returned task should have artifacts"); + assertTrue(returnedTask.getArtifacts().size() >= 2, + "Returned task should have at least 2 artifacts, got: " + + returnedTask.getArtifacts().size()); + } + /** * Simple test agent executor that allows controlling execution timing */ diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 50fa8979c..d7a2193bd 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -280,8 +280,8 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { @Test public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); @@ -655,8 +655,8 @@ public void testListPushNotificationConfigNotSupported() throws Exception { @Test public void testListPushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { @@ -728,8 +728,8 @@ public void testDeletePushNotificationConfigNotSupported() throws Exception { @Test public void testDeletePushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 45109cced..c2cf1f751 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1112,8 +1112,8 @@ public void testPushNotificationsNotSupportedError() { @Test public void testOnGetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); @@ -1131,8 +1131,8 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { @Test public void testOnSetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); @@ -1222,8 +1222,8 @@ public void testDefaultRequestHandlerWithCustomComponents() { @Test public void testOnMessageSendErrorHandling() { - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); @@ -1244,8 +1244,7 @@ public void testOnMessageSendErrorHandling() { new UnsupportedOperationError()) .when(mock).consumeAndBreakOnInterrupt( Mockito.any(EventConsumer.class), - Mockito.anyBoolean(), - Mockito.any()); + Mockito.anyBoolean()); })){ response = handler.onMessageSend(request, callContext); } @@ -1376,8 +1375,8 @@ public void testListPushNotificationConfigNotSupported() { @Test public void testListPushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { @@ -1468,8 +1467,8 @@ public void testDeletePushNotificationConfigNotSupported() { @Test public void testDeletePushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create( + executor, taskStore, queueManager, null, null, internalExecutor); JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { From 8197ec88cc90cd69aa31d451d26493b28f23d6c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 5 Nov 2025 11:21:22 +0100 Subject: [PATCH 196/493] fix: NPE with test JSONRPCHandlerTest.testOnMessageNewMessageSuccessMocks (#444) Simple fix for JSONRPCHandlerTest.testOnMessageNewMessageSuccessMocks ensuring that the DoneCallback is not null when mocking. Signed-off-by: Emmanuel Hugonnet --- .../src/main/java/io/a2a/server/events/EnhancedRunnable.java | 1 - .../io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java b/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java index 17d5b3e9a..380cb04f4 100644 --- a/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java +++ b/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java @@ -1,6 +1,5 @@ package io.a2a.server.events; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index c2cf1f751..450c0e76d 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -186,7 +186,8 @@ public void testOnMessageNewMessageSuccessMocks() { SendMessageResponse response; try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, - (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll();})){ + (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll(); + Mockito.doCallRealMethod().when(mock).createAgentRunnableDoneCallback();})){ response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); From da7ee20d309c14ac1ea837c789956bc47a6ac0b2 Mon Sep 17 00:00:00 2001 From: Farah Juma Date: Wed, 5 Nov 2025 09:52:57 -0500 Subject: [PATCH 197/493] chore: Update to the latest a2a.proto file and some corresponding updates for 0.4.0 (#392) This PR updates a2a.proto to the latest version from the current a2a main branch (e7cf203ecdd7003c0f1740bb712248d2b5252bf1). It also updates the tests to use parts instead of content for gRPC messages. Fixes: #355 #357 #356 https://github.com/a2aproject/a2a-java/issues/362 https://github.com/a2aproject/a2a-java/issues/364 --------- Co-authored-by: Emmanuel Hugonnet --- .../transport/rest/JsonRestMessages.java | 8 +- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 430 +++--- .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 131 +- .../io/a2a/grpc/APIKeySecurityScheme.java | 8 + .../java/io/a2a/grpc/AgentCapabilities.java | 85 ++ .../a2a/grpc/AgentCapabilitiesOrBuilder.java | 10 + .../src/main/java/io/a2a/grpc/AgentCard.java | 170 ++- .../java/io/a2a/grpc/AgentCardOrBuilder.java | 20 + .../java/io/a2a/grpc/AgentCardSignature.java | 2 + .../main/java/io/a2a/grpc/AgentExtension.java | 2 + .../main/java/io/a2a/grpc/AgentInterface.java | 2 + .../main/java/io/a2a/grpc/AgentProvider.java | 2 + .../src/main/java/io/a2a/grpc/AgentSkill.java | 16 +- .../java/io/a2a/grpc/AgentSkillOrBuilder.java | 4 +- .../src/main/java/io/a2a/grpc/Artifact.java | 23 +- .../java/io/a2a/grpc/ArtifactOrBuilder.java | 6 +- .../java/io/a2a/grpc/AuthenticationInfo.java | 2 + .../a2a/grpc/AuthorizationCodeOAuthFlow.java | 8 + .../java/io/a2a/grpc/CancelTaskRequest.java | 29 +- .../a2a/grpc/CancelTaskRequestOrBuilder.java | 6 +- .../a2a/grpc/ClientCredentialsOAuthFlow.java | 8 + ...eateTaskPushNotificationConfigRequest.java | 112 +- ...ushNotificationConfigRequestOrBuilder.java | 28 +- .../src/main/java/io/a2a/grpc/DataPart.java | 2 + ...leteTaskPushNotificationConfigRequest.java | 29 +- ...ushNotificationConfigRequestOrBuilder.java | 6 +- .../src/main/java/io/a2a/grpc/FilePart.java | 138 ++ .../java/io/a2a/grpc/FilePartOrBuilder.java | 12 + .../java/io/a2a/grpc/GetAgentCardRequest.java | 4 +- .../GetTaskPushNotificationConfigRequest.java | 29 +- ...ushNotificationConfigRequestOrBuilder.java | 6 +- .../main/java/io/a2a/grpc/GetTaskRequest.java | 45 +- .../io/a2a/grpc/GetTaskRequestOrBuilder.java | 10 +- .../io/a2a/grpc/HTTPAuthSecurityScheme.java | 8 + .../java/io/a2a/grpc/ImplicitOAuthFlow.java | 8 + ...ListTaskPushNotificationConfigRequest.java | 29 +- ...ushNotificationConfigRequestOrBuilder.java | 6 +- ...istTaskPushNotificationConfigResponse.java | 100 ++ ...shNotificationConfigResponseOrBuilder.java | 20 + .../java/io/a2a/grpc/ListTasksRequest.java | 1348 +++++++++++++++++ .../a2a/grpc/ListTasksRequestOrBuilder.java | 138 ++ .../java/io/a2a/grpc/ListTasksResponse.java | 1073 +++++++++++++ .../a2a/grpc/ListTasksResponseOrBuilder.java | 88 ++ .../src/main/java/io/a2a/grpc/Message.java | 625 +++++--- .../java/io/a2a/grpc/MessageOrBuilder.java | 79 +- .../io/a2a/grpc/MutualTlsSecurityScheme.java | 8 + .../io/a2a/grpc/OAuth2SecurityScheme.java | 8 + .../src/main/java/io/a2a/grpc/OAuthFlows.java | 8 + .../a2a/grpc/OpenIdConnectSecurityScheme.java | 8 + spec-grpc/src/main/java/io/a2a/grpc/Part.java | 247 ++- .../main/java/io/a2a/grpc/PartOrBuilder.java | 27 + .../java/io/a2a/grpc/PasswordOAuthFlow.java | 8 + .../io/a2a/grpc/PushNotificationConfig.java | 16 +- .../grpc/PushNotificationConfigOrBuilder.java | 4 +- .../main/java/io/a2a/grpc/SecurityScheme.java | 8 + .../io/a2a/grpc/SendMessageConfiguration.java | 2 + .../java/io/a2a/grpc/SendMessageRequest.java | 146 ++ .../a2a/grpc/SendMessageRequestOrBuilder.java | 36 + .../java/io/a2a/grpc/SendMessageResponse.java | 2 + .../main/java/io/a2a/grpc/StreamResponse.java | 2 + spec-grpc/src/main/java/io/a2a/grpc/Task.java | 51 +- .../io/a2a/grpc/TaskArtifactUpdateEvent.java | 2 + .../main/java/io/a2a/grpc/TaskOrBuilder.java | 14 +- .../a2a/grpc/TaskPushNotificationConfig.java | 77 +- .../TaskPushNotificationConfigOrBuilder.java | 18 +- .../src/main/java/io/a2a/grpc/TaskState.java | 1 + .../src/main/java/io/a2a/grpc/TaskStatus.java | 2 + .../io/a2a/grpc/TaskStatusUpdateEvent.java | 2 + .../io/a2a/grpc/TaskSubscriptionRequest.java | 29 +- .../TaskSubscriptionRequestOrBuilder.java | 6 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 24 +- spec-grpc/src/main/proto/a2a.proto | 207 ++- .../java/io/a2a/grpc/utils/ToProtoTest.java | 24 +- .../grpc/handler/GrpcHandlerTest.java | 2 +- .../rest/handler/RestHandlerTest.java | 10 +- 75 files changed, 5283 insertions(+), 631 deletions(-) create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java index e94fcca1f..8842fe2b8 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java @@ -13,7 +13,7 @@ public class JsonRestMessages { "messageId": "message-1234", "contextId": "context-1234", "role": "ROLE_USER", - "content": [{ + "parts": [{ "text": "tell me a joke" }], "metadata": { @@ -35,7 +35,7 @@ public class JsonRestMessages { "contextId": "context-1234", "taskId": "9b511af4-b27c-47fa-aecf-2a93c08a44f8", "role": "ROLE_USER", - "content": [ + "parts": [ { "text": "tell me a joke" } @@ -81,7 +81,7 @@ public class JsonRestMessages { "history": [ { "role": "ROLE_USER", - "content": [ + "parts": [ { "text": "tell me a joke" }, @@ -624,7 +624,7 @@ public class JsonRestMessages { { "message": { "role": "ROLE_USER", - "content": [ + "parts": [ { "text": "tell me some jokes" } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index 69060d5aa..f6ec5eb57 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -231,6 +231,16 @@ public static void registerAllExtensions( static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ListTasksRequest_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ListTasksResponse_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_CancelTaskRequest_descriptor; static final @@ -307,188 +317,202 @@ public static void registerAllExtensions( "Status\022 \n\005state\030\001 \001(\0162\021.a2a.v1.TaskState" + "\022(\n\006update\030\002 \001(\0132\017.a2a.v1.MessageR\007messa" + "ge\022-\n\ttimestamp\030\003 \001(\0132\032.google.protobuf." + - "Timestamp\"b\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022 \n\004fil" + - "e\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004data\030\003 \001(" + - "\0132\020.a2a.v1.DataPartH\000B\006\n\004part\"Y\n\010FilePar" + - "t\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n\017file_with_" + - "bytes\030\002 \001(\014H\000\022\021\n\tmime_type\030\003 \001(\tB\006\n\004file" + - "\"1\n\010DataPart\022%\n\004data\030\001 \001(\0132\027.google.prot" + - "obuf.Struct\"\274\001\n\007Message\022\022\n\nmessage_id\030\001 " + - "\001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t" + - "\022\032\n\004role\030\004 \001(\0162\014.a2a.v1.Role\022\035\n\007content\030" + - "\005 \003(\0132\014.a2a.v1.Part\022)\n\010metadata\030\006 \001(\0132\027." + - "google.protobuf.Struct\022\022\n\nextensions\030\007 \003" + - "(\t\"\236\001\n\010Artifact\022\023\n\013artifact_id\030\001 \001(\t\022\014\n\004" + - "name\030\003 \001(\t\022\023\n\013description\030\004 \001(\t\022\033\n\005parts" + - "\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010metadata\030\006 \001(\0132\027" + - ".google.protobuf.Struct\022\022\n\nextensions\030\007 " + - "\003(\t\"\232\001\n\025TaskStatusUpdateEvent\022\017\n\007task_id" + - "\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\006status\030\003 \001" + - "(\0132\022.a2a.v1.TaskStatus\022\r\n\005final\030\004 \001(\010\022)\n" + - "\010metadata\030\005 \001(\0132\027.google.protobuf.Struct" + - "\"\261\001\n\027TaskArtifactUpdateEvent\022\017\n\007task_id\030" + - "\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\010artifact\030\003 " + - "\001(\0132\020.a2a.v1.Artifact\022\016\n\006append\030\004 \001(\010\022\022\n" + - "\nlast_chunk\030\005 \001(\010\022)\n\010metadata\030\006 \001(\0132\027.go" + - "ogle.protobuf.Struct\"t\n\026PushNotification" + - "Config\022\n\n\002id\030\001 \001(\t\022\013\n\003url\030\002 \001(\t\022\r\n\005token" + - "\030\003 \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a.v1." + - "AuthenticationInfo\":\n\022AuthenticationInfo" + - "\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credentials\030\002 \001(\t\"0" + - "\n\016AgentInterface\022\013\n\003url\030\001 \001(\t\022\021\n\ttranspo" + - "rt\030\002 \001(\t\"\242\005\n\tAgentCard\022\030\n\020protocol_versi" + - "on\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013description\030\002 " + - "\001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred_transport\030" + - "\016 \001(\t\0225\n\025additional_interfaces\030\017 \003(\0132\026.a" + - "2a.v1.AgentInterface\022\'\n\010provider\030\004 \001(\0132\025" + - ".a2a.v1.AgentProvider\022\017\n\007version\030\005 \001(\t\022\031" + - "\n\021documentation_url\030\006 \001(\t\022/\n\014capabilitie" + - "s\030\007 \001(\0132\031.a2a.v1.AgentCapabilities\022@\n\020se" + - "curity_schemes\030\010 \003(\0132&.a2a.v1.AgentCard." + - "SecuritySchemesEntry\022\"\n\010security\030\t \003(\0132\020" + - ".a2a.v1.Security\022\033\n\023default_input_modes\030" + - "\n \003(\t\022\034\n\024default_output_modes\030\013 \003(\t\022\"\n\006s" + - "kills\030\014 \003(\0132\022.a2a.v1.AgentSkill\022,\n$suppo" + - "rts_authenticated_extended_card\030\r \001(\010\022.\n" + - "\nsignatures\030\021 \003(\0132\032.a2a.v1.AgentCardSign" + - "ature\032N\n\024SecuritySchemesEntry\022\013\n\003key\030\001 \001" + - "(\t\022%\n\005value\030\002 \001(\0132\026.a2a.v1.SecuritySchem" + - "e:\0028\001\"2\n\rAgentProvider\022\013\n\003url\030\001 \001(\t\022\024\n\014o" + - "rganization\030\002 \001(\t\"n\n\021AgentCapabilities\022\021" + - "\n\tstreaming\030\001 \001(\010\022\032\n\022push_notifications\030" + - "\002 \001(\010\022*\n\nextensions\030\003 \003(\0132\026.a2a.v1.Agent" + - "Extension\"m\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t" + - "\022\023\n\013description\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022" + - "\'\n\006params\030\004 \001(\0132\027.google.protobuf.Struct" + - "\"\252\001\n\nAgentSkill\022\n\n\002id\030\001 \001(\t\022\014\n\004name\030\002 \001(" + - "\t\022\023\n\013description\030\003 \001(\t\022\014\n\004tags\030\004 \003(\t\022\020\n\010" + - "examples\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014o" + - "utput_modes\030\007 \003(\t\022\"\n\010security\030\010 \003(\0132\020.a2" + - "a.v1.Security\"m\n\022AgentCardSignature\022\026\n\tp" + - "rotected\030\001 \001(\tB\003\340A\002\022\026\n\tsignature\030\002 \001(\tB\003" + - "\340A\002\022\'\n\006header\030\003 \001(\0132\027.google.protobuf.St" + - "ruct\"l\n\032TaskPushNotificationConfig\022\014\n\004na" + - "me\030\001 \001(\t\022@\n\030push_notification_config\030\002 \001" + - "(\0132\036.a2a.v1.PushNotificationConfig\"\032\n\nSt" + - "ringList\022\014\n\004list\030\001 \003(\t\"~\n\010Security\022.\n\007sc" + - "hemes\030\001 \003(\0132\035.a2a.v1.Security.SchemesEnt" + - "ry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001(\t\022!\n\005value" + - "\030\002 \001(\0132\022.a2a.v1.StringList:\0028\001\"\361\002\n\016Secur" + - "ityScheme\022?\n\027api_key_security_scheme\030\001 \001" + - "(\0132\034.a2a.v1.APIKeySecuritySchemeH\000\022C\n\031ht" + - "tp_auth_security_scheme\030\002 \001(\0132\036.a2a.v1.H" + - "TTPAuthSecuritySchemeH\000\022>\n\026oauth2_securi" + - "ty_scheme\030\003 \001(\0132\034.a2a.v1.OAuth2SecurityS" + - "chemeH\000\022N\n\037open_id_connect_security_sche" + - "me\030\004 \001(\0132#.a2a.v1.OpenIdConnectSecurityS" + - "chemeH\000\022?\n\024mtls_security_scheme\030\005 \001(\0132\037." + - "a2a.v1.MutualTlsSecuritySchemeH\000B\010\n\006sche" + - "me\"K\n\024APIKeySecurityScheme\022\023\n\013descriptio" + - "n\030\001 \001(\t\022\020\n\010location\030\002 \001(\t\022\014\n\004name\030\003 \001(\t\"" + - "T\n\026HTTPAuthSecurityScheme\022\023\n\013description" + - "\030\001 \001(\t\022\016\n\006scheme\030\002 \001(\t\022\025\n\rbearer_format\030" + - "\003 \001(\t\"k\n\024OAuth2SecurityScheme\022\023\n\013descrip" + - "tion\030\001 \001(\t\022!\n\005flows\030\002 \001(\0132\022.a2a.v1.OAuth" + - "Flows\022\033\n\023oauth2_metadata_url\030\003 \001(\t\"O\n\033Op" + - "enIdConnectSecurityScheme\022\023\n\013description" + - "\030\001 \001(\t\022\033\n\023open_id_connect_url\030\002 \001(\t\".\n\027M" + - "utualTlsSecurityScheme\022\023\n\013description\030\001 " + - "\001(\t\"\366\001\n\nOAuthFlows\022@\n\022authorization_code" + - "\030\001 \001(\0132\".a2a.v1.AuthorizationCodeOAuthFl" + - "owH\000\022@\n\022client_credentials\030\002 \001(\0132\".a2a.v" + - "1.ClientCredentialsOAuthFlowH\000\022-\n\010implic" + - "it\030\003 \001(\0132\031.a2a.v1.ImplicitOAuthFlowH\000\022-\n" + - "\010password\030\004 \001(\0132\031.a2a.v1.PasswordOAuthFl" + - "owH\000B\006\n\004flow\"\316\001\n\032AuthorizationCodeOAuthF" + - "low\022\031\n\021authorization_url\030\001 \001(\t\022\021\n\ttoken_" + - "url\030\002 \001(\t\022\023\n\013refresh_url\030\003 \001(\t\022>\n\006scopes" + - "\030\004 \003(\0132..a2a.v1.AuthorizationCodeOAuthFl" + - "ow.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001" + - "(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\263\001\n\032ClientCredenti" + - "alsOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t\022\023\n\013refre" + - "sh_url\030\002 \001(\t\022>\n\006scopes\030\003 \003(\0132..a2a.v1.Cl" + - "ientCredentialsOAuthFlow.ScopesEntry\032-\n\013" + - "ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:" + - "\0028\001\"\251\001\n\021ImplicitOAuthFlow\022\031\n\021authorizati" + - "on_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\0225\n\006sco" + - "pes\030\003 \003(\0132%.a2a.v1.ImplicitOAuthFlow.Sco" + - "pesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005" + - "value\030\002 \001(\t:\0028\001\"\241\001\n\021PasswordOAuthFlow\022\021\n" + - "\ttoken_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\0225\n" + - "\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuthFlow" + - ".ScopesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t" + - "\022\r\n\005value\030\002 \001(\t:\0028\001\"\250\001\n\022SendMessageReque" + - "st\022.\n\007request\030\001 \001(\0132\017.a2a.v1.MessageB\003\340A" + - "\002R\007message\0227\n\rconfiguration\030\002 \001(\0132 .a2a." + - "v1.SendMessageConfiguration\022)\n\010metadata\030" + - "\003 \001(\0132\027.google.protobuf.Struct\";\n\016GetTas" + - "kRequest\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\026\n\016history_l" + - "ength\030\002 \001(\005\"!\n\021CancelTaskRequest\022\014\n\004name" + - "\030\001 \001(\t\"4\n$GetTaskPushNotificationConfigR" + - "equest\022\014\n\004name\030\001 \001(\t\"7\n\'DeleteTaskPushNo" + - "tificationConfigRequest\022\014\n\004name\030\001 \001(\t\"\217\001" + - "\n\'CreateTaskPushNotificationConfigReques" + - "t\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\tconfig_id\030\002 \001(" + - "\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a2a.v1.TaskPush" + - "NotificationConfigB\003\340A\002\"\'\n\027TaskSubscript" + - "ionRequest\022\014\n\004name\030\001 \001(\t\"^\n%ListTaskPush" + - "NotificationConfigRequest\022\016\n\006parent\030\001 \001(" + - "\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t" + - "\"\025\n\023GetAgentCardRequest\"g\n\023SendMessageRe" + - "sponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003" + - "msg\030\002 \001(\0132\017.a2a.v1.MessageH\000R\007messageB\t\n" + - "\007payload\"\326\001\n\016StreamResponse\022\034\n\004task\030\001 \001(" + - "\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1." + - "MessageH\000R\007message\0226\n\rstatus_update\030\003 \001(" + - "\0132\035.a2a.v1.TaskStatusUpdateEventH\000\022:\n\017ar" + - "tifact_update\030\004 \001(\0132\037.a2a.v1.TaskArtifac" + - "tUpdateEventH\000B\t\n\007payload\"v\n&ListTaskPus" + - "hNotificationConfigResponse\0223\n\007configs\030\001" + - " \003(\0132\".a2a.v1.TaskPushNotificationConfig" + - "\022\027\n\017next_page_token\030\002 \001(\t*\372\001\n\tTaskState\022" + - "\032\n\026TASK_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STAT" + - "E_SUBMITTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n" + - "\024TASK_STATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FA" + - "ILED\020\004\022\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK" + - "_STATE_INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_RE" + - "JECTED\020\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;" + - "\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USE" + - "R\020\001\022\016\n\nROLE_AGENT\020\0022\272\n\n\nA2AService\022c\n\013Se" + - "ndMessage\022\032.a2a.v1.SendMessageRequest\032\033." + - "a2a.v1.SendMessageResponse\"\033\202\323\344\223\002\025\"\020/v1/" + - "message:send:\001*\022k\n\024SendStreamingMessage\022" + - "\032.a2a.v1.SendMessageRequest\032\026.a2a.v1.Str" + - "eamResponse\"\035\202\323\344\223\002\027\"\022/v1/message:stream:" + - "\001*0\001\022R\n\007GetTask\022\026.a2a.v1.GetTaskRequest\032" + - "\014.a2a.v1.Task\"!\332A\004name\202\323\344\223\002\024\022\022/v1/{name=" + - "tasks/*}\022[\n\nCancelTask\022\031.a2a.v1.CancelTa" + - "skRequest\032\014.a2a.v1.Task\"$\202\323\344\223\002\036\"\031/v1/{na" + - "me=tasks/*}:cancel:\001*\022s\n\020TaskSubscriptio" + - "n\022\037.a2a.v1.TaskSubscriptionRequest\032\026.a2a" + - ".v1.StreamResponse\"$\202\323\344\223\002\036\022\034/v1/{name=ta" + - "sks/*}:subscribe0\001\022\304\001\n CreateTaskPushNot" + - "ificationConfig\022/.a2a.v1.CreateTaskPushN" + - "otificationConfigRequest\032\".a2a.v1.TaskPu" + - "shNotificationConfig\"K\332A\rparent,config\202\323" + - "\344\223\0025\"+/v1/{parent=task/*/pushNotificatio" + - "nConfigs}:\006config\022\256\001\n\035GetTaskPushNotific" + - "ationConfig\022,.a2a.v1.GetTaskPushNotifica" + - "tionConfigRequest\032\".a2a.v1.TaskPushNotif" + - "icationConfig\";\332A\004name\202\323\344\223\002.\022,/v1/{name=" + - "tasks/*/pushNotificationConfigs/*}\022\276\001\n\036L" + - "istTaskPushNotificationConfig\022-.a2a.v1.L" + - "istTaskPushNotificationConfigRequest\032..a" + - "2a.v1.ListTaskPushNotificationConfigResp" + - "onse\"=\332A\006parent\202\323\344\223\002.\022,/v1/{parent=tasks" + - "/*}/pushNotificationConfigs\022P\n\014GetAgentC" + - "ard\022\033.a2a.v1.GetAgentCardRequest\032\021.a2a.v" + - "1.AgentCard\"\020\202\323\344\223\002\n\022\010/v1/card\022\250\001\n Delete" + - "TaskPushNotificationConfig\022/.a2a.v1.Dele" + - "teTaskPushNotificationConfigRequest\032\026.go" + - "ogle.protobuf.Empty\";\332A\004name\202\323\344\223\002.*,/v1/" + - "{name=tasks/*/pushNotificationConfigs/*}" + - "B7\n\013io.a2a.grpcB\003A2AP\001Z\030google.golang.or" + - "g/a2a/v1\252\002\006A2a.V1b\006proto3" + "Timestamp\"\215\001\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022 \n\004fi" + + "le\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004data\030\003 \001" + + "(\0132\020.a2a.v1.DataPartH\000\022)\n\010metadata\030\004 \001(\013" + + "2\027.google.protobuf.StructB\006\n\004part\"g\n\010Fil" + + "ePart\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n\017file_w" + + "ith_bytes\030\002 \001(\014H\000\022\021\n\tmime_type\030\003 \001(\t\022\014\n\004" + + "name\030\004 \001(\tB\006\n\004file\"1\n\010DataPart\022%\n\004data\030\001" + + " \001(\0132\027.google.protobuf.Struct\"\326\001\n\007Messag" + + "e\022\022\n\nmessage_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(" + + "\t\022\017\n\007task_id\030\003 \001(\t\022\032\n\004role\030\004 \001(\0162\014.a2a.v" + + "1.Role\022\033\n\005parts\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010m" + + "etadata\030\006 \001(\0132\027.google.protobuf.Struct\022\022" + + "\n\nextensions\030\007 \003(\t\022\032\n\022reference_task_ids" + + "\030\010 \003(\t\"\236\001\n\010Artifact\022\023\n\013artifact_id\030\001 \001(\t" + + "\022\014\n\004name\030\003 \001(\t\022\023\n\013description\030\004 \001(\t\022\033\n\005p" + + "arts\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010metadata\030\006 \001" + + "(\0132\027.google.protobuf.Struct\022\022\n\nextension" + + "s\030\007 \003(\t\"\232\001\n\025TaskStatusUpdateEvent\022\017\n\007tas" + + "k_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\006status" + + "\030\003 \001(\0132\022.a2a.v1.TaskStatus\022\r\n\005final\030\004 \001(" + + "\010\022)\n\010metadata\030\005 \001(\0132\027.google.protobuf.St" + + "ruct\"\261\001\n\027TaskArtifactUpdateEvent\022\017\n\007task" + + "_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\010artifac" + + "t\030\003 \001(\0132\020.a2a.v1.Artifact\022\016\n\006append\030\004 \001(" + + "\010\022\022\n\nlast_chunk\030\005 \001(\010\022)\n\010metadata\030\006 \001(\0132" + + "\027.google.protobuf.Struct\"t\n\026PushNotifica" + + "tionConfig\022\n\n\002id\030\001 \001(\t\022\013\n\003url\030\002 \001(\t\022\r\n\005t" + + "oken\030\003 \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a" + + ".v1.AuthenticationInfo\":\n\022Authentication" + + "Info\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credentials\030\002 \001" + + "(\t\"0\n\016AgentInterface\022\013\n\003url\030\001 \001(\t\022\021\n\ttra" + + "nsport\030\002 \001(\t\"\264\005\n\tAgentCard\022\030\n\020protocol_v" + + "ersion\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013descriptio" + + "n\030\002 \001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred_transp" + + "ort\030\016 \001(\t\0225\n\025additional_interfaces\030\017 \003(\013" + + "2\026.a2a.v1.AgentInterface\022\'\n\010provider\030\004 \001" + + "(\0132\025.a2a.v1.AgentProvider\022\017\n\007version\030\005 \001" + + "(\t\022\031\n\021documentation_url\030\006 \001(\t\022/\n\014capabil" + + "ities\030\007 \001(\0132\031.a2a.v1.AgentCapabilities\022@" + + "\n\020security_schemes\030\010 \003(\0132&.a2a.v1.AgentC" + + "ard.SecuritySchemesEntry\022\"\n\010security\030\t \003" + + "(\0132\020.a2a.v1.Security\022\033\n\023default_input_mo" + + "des\030\n \003(\t\022\034\n\024default_output_modes\030\013 \003(\t\022" + + "\"\n\006skills\030\014 \003(\0132\022.a2a.v1.AgentSkill\022,\n$s" + + "upports_authenticated_extended_card\030\r \001(" + + "\010\022.\n\nsignatures\030\021 \003(\0132\032.a2a.v1.AgentCard" + + "Signature\022\020\n\010icon_url\030\022 \001(\t\032N\n\024SecurityS" + + "chemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132" + + "\026.a2a.v1.SecurityScheme:\0028\001\"2\n\rAgentProv" + + "ider\022\013\n\003url\030\001 \001(\t\022\024\n\014organization\030\002 \001(\t\"" + + "\220\001\n\021AgentCapabilities\022\021\n\tstreaming\030\001 \001(\010" + + "\022\032\n\022push_notifications\030\002 \001(\010\022*\n\nextensio" + + "ns\030\003 \003(\0132\026.a2a.v1.AgentExtension\022 \n\030stat" + + "e_transition_history\030\004 \001(\010\"m\n\016AgentExten" + + "sion\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030\002 \001(\t\022\020" + + "\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132\027.googl" + + "e.protobuf.Struct\"\252\001\n\nAgentSkill\022\n\n\002id\030\001" + + " \001(\t\022\014\n\004name\030\002 \001(\t\022\023\n\013description\030\003 \001(\t\022" + + "\014\n\004tags\030\004 \003(\t\022\020\n\010examples\030\005 \003(\t\022\023\n\013input" + + "_modes\030\006 \003(\t\022\024\n\014output_modes\030\007 \003(\t\022\"\n\010se" + + "curity\030\010 \003(\0132\020.a2a.v1.Security\"m\n\022AgentC" + + "ardSignature\022\026\n\tprotected\030\001 \001(\tB\003\340A\002\022\026\n\t" + + "signature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\0132\027.g" + + "oogle.protobuf.Struct\"l\n\032TaskPushNotific" + + "ationConfig\022\014\n\004name\030\001 \001(\t\022@\n\030push_notifi" + + "cation_config\030\002 \001(\0132\036.a2a.v1.PushNotific" + + "ationConfig\"\032\n\nStringList\022\014\n\004list\030\001 \003(\t\"" + + "~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035.a2a.v1.Se" + + "curity.SchemesEntry\032B\n\014SchemesEntry\022\013\n\003k" + + "ey\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1.StringL" + + "ist:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027api_key_se" + + "curity_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySecur" + + "itySchemeH\000\022C\n\031http_auth_security_scheme" + + "\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecuritySchemeH\000" + + "\022>\n\026oauth2_security_scheme\030\003 \001(\0132\034.a2a.v" + + "1.OAuth2SecuritySchemeH\000\022N\n\037open_id_conn" + + "ect_security_scheme\030\004 \001(\0132#.a2a.v1.OpenI" + + "dConnectSecuritySchemeH\000\022?\n\024mtls_securit" + + "y_scheme\030\005 \001(\0132\037.a2a.v1.MutualTlsSecurit" + + "ySchemeH\000B\010\n\006scheme\"K\n\024APIKeySecuritySch" + + "eme\022\023\n\013description\030\001 \001(\t\022\020\n\010location\030\002 \001" + + "(\t\022\014\n\004name\030\003 \001(\t\"T\n\026HTTPAuthSecuritySche" + + "me\022\023\n\013description\030\001 \001(\t\022\016\n\006scheme\030\002 \001(\t\022" + + "\025\n\rbearer_format\030\003 \001(\t\"k\n\024OAuth2Security" + + "Scheme\022\023\n\013description\030\001 \001(\t\022!\n\005flows\030\002 \001" + + "(\0132\022.a2a.v1.OAuthFlows\022\033\n\023oauth2_metadat" + + "a_url\030\003 \001(\t\"O\n\033OpenIdConnectSecuritySche" + + "me\022\023\n\013description\030\001 \001(\t\022\033\n\023open_id_conne" + + "ct_url\030\002 \001(\t\".\n\027MutualTlsSecurityScheme\022" + + "\023\n\013description\030\001 \001(\t\"\366\001\n\nOAuthFlows\022@\n\022a" + + "uthorization_code\030\001 \001(\0132\".a2a.v1.Authori" + + "zationCodeOAuthFlowH\000\022@\n\022client_credenti" + + "als\030\002 \001(\0132\".a2a.v1.ClientCredentialsOAut" + + "hFlowH\000\022-\n\010implicit\030\003 \001(\0132\031.a2a.v1.Impli" + + "citOAuthFlowH\000\022-\n\010password\030\004 \001(\0132\031.a2a.v" + + "1.PasswordOAuthFlowH\000B\006\n\004flow\"\316\001\n\032Author" + + "izationCodeOAuthFlow\022\031\n\021authorization_ur" + + "l\030\001 \001(\t\022\021\n\ttoken_url\030\002 \001(\t\022\023\n\013refresh_ur" + + "l\030\003 \001(\t\022>\n\006scopes\030\004 \003(\0132..a2a.v1.Authori" + + "zationCodeOAuthFlow.ScopesEntry\032-\n\013Scope" + + "sEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\263" + + "\001\n\032ClientCredentialsOAuthFlow\022\021\n\ttoken_u" + + "rl\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\022>\n\006scopes\030" + + "\003 \003(\0132..a2a.v1.ClientCredentialsOAuthFlo" + + "w.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(" + + "\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021ImplicitOAuthFl" + + "ow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013refresh" + + "_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a.v1.Impl" + + "icitOAuthFlow.ScopesEntry\032-\n\013ScopesEntry" + + "\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\241\001\n\021Pas" + + "swordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t\022\023\n\013ref" + + "resh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a.v1." + + "PasswordOAuthFlow.ScopesEntry\032-\n\013ScopesE" + + "ntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\250\001\n" + + "\022SendMessageRequest\022.\n\007request\030\001 \001(\0132\017.a" + + "2a.v1.MessageB\003\340A\002R\007message\0227\n\rconfigura" + + "tion\030\002 \001(\0132 .a2a.v1.SendMessageConfigura" + + "tion\022)\n\010metadata\030\003 \001(\0132\027.google.protobuf" + + ".Struct\";\n\016GetTaskRequest\022\021\n\004name\030\001 \001(\tB" + + "\003\340A\002\022\026\n\016history_length\030\002 \001(\005\"\332\001\n\020ListTas" + + "ksRequest\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006status\030" + + "\002 \001(\0162\021.a2a.v1.TaskState\022\021\n\tpage_size\030\003 " + + "\001(\005\022\022\n\npage_token\030\004 \001(\t\022\026\n\016history_lengt" + + "h\030\005 \001(\005\0225\n\021last_updated_time\030\006 \001(\0132\032.goo" + + "gle.protobuf.Timestamp\022\031\n\021include_artifa" + + "cts\030\007 \001(\010\"]\n\021ListTasksResponse\022\033\n\005tasks\030" + + "\001 \003(\0132\014.a2a.v1.Task\022\027\n\017next_page_token\030\002" + + " \001(\t\022\022\n\ntotal_size\030\003 \001(\005\"!\n\021CancelTaskRe" + + "quest\022\014\n\004name\030\001 \001(\t\"4\n$GetTaskPushNotifi" + + "cationConfigRequest\022\014\n\004name\030\001 \001(\t\"7\n\'Del" + + "eteTaskPushNotificationConfigRequest\022\014\n\004" + + "name\030\001 \001(\t\"\217\001\n\'CreateTaskPushNotificatio" + + "nConfigRequest\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\tc" + + "onfig_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a2" + + "a.v1.TaskPushNotificationConfigB\003\340A\002\"\'\n\027" + + "TaskSubscriptionRequest\022\014\n\004name\030\001 \001(\t\"^\n" + + "%ListTaskPushNotificationConfigRequest\022\016" + + "\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npag" + + "e_token\030\003 \001(\t\"\025\n\023GetAgentCardRequest\"g\n\023" + + "SendMessageResponse\022\034\n\004task\030\001 \001(\0132\014.a2a." + + "v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.MessageH" + + "\000R\007messageB\t\n\007payload\"\326\001\n\016StreamResponse" + + "\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 " + + "\001(\0132\017.a2a.v1.MessageH\000R\007message\0226\n\rstatu" + + "s_update\030\003 \001(\0132\035.a2a.v1.TaskStatusUpdate" + + "EventH\000\022:\n\017artifact_update\030\004 \001(\0132\037.a2a.v" + + "1.TaskArtifactUpdateEventH\000B\t\n\007payload\"v" + + "\n&ListTaskPushNotificationConfigResponse" + + "\0223\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPushNotif" + + "icationConfig\022\027\n\017next_page_token\030\002 \001(\t*\372" + + "\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020\000" + + "\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STATE" + + "_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n\021" + + "TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCEL" + + "LED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n\023" + + "TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUTH" + + "_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020" + + "\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\220\013\n\nA2A" + + "Service\022c\n\013SendMessage\022\032.a2a.v1.SendMess" + + "ageRequest\032\033.a2a.v1.SendMessageResponse\"" + + "\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024SendStre" + + "amingMessage\022\032.a2a.v1.SendMessageRequest" + + "\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"\022/v1/me" + + "ssage:stream:\001*0\001\022R\n\007GetTask\022\026.a2a.v1.Ge" + + "tTaskRequest\032\014.a2a.v1.Task\"!\332A\004name\202\323\344\223\002" + + "\024\022\022/v1/{name=tasks/*}\022S\n\tListTasks\022\030.a2a" + + ".v1.ListTasksRequest\032\031.a2a.v1.ListTasksR" + + "esponse\"\021\202\323\344\223\002\013\022\t/v1/tasks\022[\n\nCancelTask" + + "\022\031.a2a.v1.CancelTaskRequest\032\014.a2a.v1.Tas" + + "k\"$\202\323\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*\022" + + "s\n\020TaskSubscription\022\037.a2a.v1.TaskSubscri" + + "ptionRequest\032\026.a2a.v1.StreamResponse\"$\202\323" + + "\344\223\002\036\022\034/v1/{name=tasks/*}:subscribe0\001\022\305\001\n" + + " CreateTaskPushNotificationConfig\022/.a2a." + + "v1.CreateTaskPushNotificationConfigReque" + + "st\032\".a2a.v1.TaskPushNotificationConfig\"L" + + "\332A\rparent,config\202\323\344\223\0026\",/v1/{parent=task" + + "s/*/pushNotificationConfigs}:\006config\022\256\001\n" + + "\035GetTaskPushNotificationConfig\022,.a2a.v1." + + "GetTaskPushNotificationConfigRequest\032\".a" + + "2a.v1.TaskPushNotificationConfig\";\332A\004nam" + + "e\202\323\344\223\002.\022,/v1/{name=tasks/*/pushNotificat" + + "ionConfigs/*}\022\276\001\n\036ListTaskPushNotificati" + + "onConfig\022-.a2a.v1.ListTaskPushNotificati" + + "onConfigRequest\032..a2a.v1.ListTaskPushNot" + + "ificationConfigResponse\"=\332A\006parent\202\323\344\223\002." + + "\022,/v1/{parent=tasks/*}/pushNotificationC" + + "onfigs\022P\n\014GetAgentCard\022\033.a2a.v1.GetAgent" + + "CardRequest\032\021.a2a.v1.AgentCard\"\020\202\323\344\223\002\n\022\010" + + "/v1/card\022\250\001\n DeleteTaskPushNotificationC" + + "onfig\022/.a2a.v1.DeleteTaskPushNotificatio" + + "nConfigRequest\032\026.google.protobuf.Empty\";" + + "\332A\004name\202\323\344\223\002.*,/v1/{name=tasks/*/pushNot" + + "ificationConfigs/*}B7\n\013io.a2a.grpcB\003A2AP" + + "\001Z\030google.golang.org/a2a/v1\252\002\006A2a.V1b\006pr" + + "oto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -523,13 +547,13 @@ public static void registerAllExtensions( internal_static_a2a_v1_Part_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_Part_descriptor, - new java.lang.String[] { "Text", "File", "Data", "Part", }); + new java.lang.String[] { "Text", "File", "Data", "Metadata", "Part", }); internal_static_a2a_v1_FilePart_descriptor = getDescriptor().getMessageTypes().get(4); internal_static_a2a_v1_FilePart_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_FilePart_descriptor, - new java.lang.String[] { "FileWithUri", "FileWithBytes", "MimeType", "File", }); + new java.lang.String[] { "FileWithUri", "FileWithBytes", "MimeType", "Name", "File", }); internal_static_a2a_v1_DataPart_descriptor = getDescriptor().getMessageTypes().get(5); internal_static_a2a_v1_DataPart_fieldAccessorTable = new @@ -541,7 +565,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_Message_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_Message_descriptor, - new java.lang.String[] { "MessageId", "ContextId", "TaskId", "Role", "Content", "Metadata", "Extensions", }); + new java.lang.String[] { "MessageId", "ContextId", "TaskId", "Role", "Parts", "Metadata", "Extensions", "ReferenceTaskIds", }); internal_static_a2a_v1_Artifact_descriptor = getDescriptor().getMessageTypes().get(7); internal_static_a2a_v1_Artifact_fieldAccessorTable = new @@ -583,7 +607,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCard_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCard_descriptor, - new java.lang.String[] { "ProtocolVersion", "Name", "Description", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", "Signatures", }); + new java.lang.String[] { "ProtocolVersion", "Name", "Description", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", "Signatures", "IconUrl", }); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor = internal_static_a2a_v1_AgentCard_descriptor.getNestedTypes().get(0); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new @@ -601,7 +625,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCapabilities_descriptor, - new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", }); + new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "StateTransitionHistory", }); internal_static_a2a_v1_AgentExtension_descriptor = getDescriptor().getMessageTypes().get(16); internal_static_a2a_v1_AgentExtension_fieldAccessorTable = new @@ -746,62 +770,74 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskRequest_descriptor, new java.lang.String[] { "Name", "HistoryLength", }); - internal_static_a2a_v1_CancelTaskRequest_descriptor = + internal_static_a2a_v1_ListTasksRequest_descriptor = getDescriptor().getMessageTypes().get(35); + internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ListTasksRequest_descriptor, + new java.lang.String[] { "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "LastUpdatedTime", "IncludeArtifacts", }); + internal_static_a2a_v1_ListTasksResponse_descriptor = + getDescriptor().getMessageTypes().get(36); + internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ListTasksResponse_descriptor, + new java.lang.String[] { "Tasks", "NextPageToken", "TotalSize", }); + internal_static_a2a_v1_CancelTaskRequest_descriptor = + getDescriptor().getMessageTypes().get(37); internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CancelTaskRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(36); + getDescriptor().getMessageTypes().get(38); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(37); + getDescriptor().getMessageTypes().get(39); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(38); + getDescriptor().getMessageTypes().get(40); internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Parent", "ConfigId", "Config", }); internal_static_a2a_v1_TaskSubscriptionRequest_descriptor = - getDescriptor().getMessageTypes().get(39); + getDescriptor().getMessageTypes().get(41); internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskSubscriptionRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageTypes().get(40); + getDescriptor().getMessageTypes().get(42); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Parent", "PageSize", "PageToken", }); internal_static_a2a_v1_GetAgentCardRequest_descriptor = - getDescriptor().getMessageTypes().get(41); + getDescriptor().getMessageTypes().get(43); internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetAgentCardRequest_descriptor, new java.lang.String[] { }); internal_static_a2a_v1_SendMessageResponse_descriptor = - getDescriptor().getMessageTypes().get(42); + getDescriptor().getMessageTypes().get(44); internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SendMessageResponse_descriptor, new java.lang.String[] { "Task", "Msg", "Payload", }); internal_static_a2a_v1_StreamResponse_descriptor = - getDescriptor().getMessageTypes().get(43); + getDescriptor().getMessageTypes().get(45); internal_static_a2a_v1_StreamResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_StreamResponse_descriptor, new java.lang.String[] { "Task", "Msg", "StatusUpdate", "ArtifactUpdate", "Payload", }); internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor = - getDescriptor().getMessageTypes().get(44); + getDescriptor().getMessageTypes().get(46); internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor, diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java index 6f83ead1d..3b1dbefcd 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -14,9 +14,6 @@ * - TaskPushNotificationConfig are a resource whose parent is a task. * They have get, list and create methods. * - AgentCard is a static resource with only a get method. - * fields are not present as they don't comply with AIP rules, and the - * optional history_length on the get task method is not present as it also - * violates AIP-127 and AIP-131. * */ @javax.annotation.Generated( @@ -123,6 +120,37 @@ io.a2a.grpc.Task> getGetTaskMethod() { return getGetTaskMethod; } + private static volatile io.grpc.MethodDescriptor getListTasksMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "ListTasks", + requestType = io.a2a.grpc.ListTasksRequest.class, + responseType = io.a2a.grpc.ListTasksResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getListTasksMethod() { + io.grpc.MethodDescriptor getListTasksMethod; + if ((getListTasksMethod = A2AServiceGrpc.getListTasksMethod) == null) { + synchronized (A2AServiceGrpc.class) { + if ((getListTasksMethod = A2AServiceGrpc.getListTasksMethod) == null) { + A2AServiceGrpc.getListTasksMethod = getListTasksMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "ListTasks")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.ListTasksRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + io.a2a.grpc.ListTasksResponse.getDefaultInstance())) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("ListTasks")) + .build(); + } + } + } + return getListTasksMethod; + } + private static volatile io.grpc.MethodDescriptor getCancelTaskMethod; @@ -411,9 +439,6 @@ public A2AServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions * - TaskPushNotificationConfig are a resource whose parent is a task. * They have get, list and create methods. * - AgentCard is a static resource with only a get method. - * fields are not present as they don't comply with AIP rules, and the - * optional history_length on the get task method is not present as it also - * violates AIP-127 and AIP-131. * */ public interface AsyncService { @@ -450,6 +475,16 @@ default void getTask(io.a2a.grpc.GetTaskRequest request, io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetTaskMethod(), responseObserver); } + /** + *

+     * List tasks with optional filtering and pagination.
+     * 
+ */ + default void listTasks(io.a2a.grpc.ListTasksRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getListTasksMethod(), responseObserver); + } + /** *
      * Cancel a task from the agent. If supported one should expect no
@@ -538,9 +573,6 @@ default void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotifica
    * - TaskPushNotificationConfig are a resource whose parent is a task.
    *   They have get, list and create methods.
    * - AgentCard is a static resource with only a get method.
-   * fields are not present as they don't comply with AIP rules, and the
-   * optional history_length on the get task method is not present as it also
-   * violates AIP-127 and AIP-131.
    * 
*/ public static abstract class A2AServiceImplBase @@ -564,9 +596,6 @@ public static abstract class A2AServiceImplBase * - TaskPushNotificationConfig are a resource whose parent is a task. * They have get, list and create methods. * - AgentCard is a static resource with only a get method. - * fields are not present as they don't comply with AIP rules, and the - * optional history_length on the get task method is not present as it also - * violates AIP-127 and AIP-131. * */ public static final class A2AServiceStub @@ -617,6 +646,17 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, getChannel().newCall(getGetTaskMethod(), getCallOptions()), request, responseObserver); } + /** + *
+     * List tasks with optional filtering and pagination.
+     * 
+ */ + public void listTasks(io.a2a.grpc.ListTasksRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getListTasksMethod(), getCallOptions()), request, responseObserver); + } + /** *
      * Cancel a task from the agent. If supported one should expect no
@@ -712,9 +752,6 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat
    * - TaskPushNotificationConfig are a resource whose parent is a task.
    *   They have get, list and create methods.
    * - AgentCard is a static resource with only a get method.
-   * fields are not present as they don't comply with AIP rules, and the
-   * optional history_length on the get task method is not present as it also
-   * violates AIP-127 and AIP-131.
    * 
*/ public static final class A2AServiceBlockingV2Stub @@ -764,6 +801,16 @@ public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) { getChannel(), getGetTaskMethod(), getCallOptions(), request); } + /** + *
+     * List tasks with optional filtering and pagination.
+     * 
+ */ + public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getListTasksMethod(), getCallOptions(), request); + } + /** *
      * Cancel a task from the agent. If supported one should expect no
@@ -854,9 +901,6 @@ public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.De
    * - TaskPushNotificationConfig are a resource whose parent is a task.
    *   They have get, list and create methods.
    * - AgentCard is a static resource with only a get method.
-   * fields are not present as they don't comply with AIP rules, and the
-   * optional history_length on the get task method is not present as it also
-   * violates AIP-127 and AIP-131.
    * 
*/ public static final class A2AServiceBlockingStub @@ -905,6 +949,16 @@ public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) { getChannel(), getGetTaskMethod(), getCallOptions(), request); } + /** + *
+     * List tasks with optional filtering and pagination.
+     * 
+ */ + public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getListTasksMethod(), getCallOptions(), request); + } + /** *
      * Cancel a task from the agent. If supported one should expect no
@@ -994,9 +1048,6 @@ public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.De
    * - TaskPushNotificationConfig are a resource whose parent is a task.
    *   They have get, list and create methods.
    * - AgentCard is a static resource with only a get method.
-   * fields are not present as they don't comply with AIP rules, and the
-   * optional history_length on the get task method is not present as it also
-   * violates AIP-127 and AIP-131.
    * 
*/ public static final class A2AServiceFutureStub @@ -1035,6 +1086,17 @@ public com.google.common.util.concurrent.ListenableFuture getT getChannel().newCall(getGetTaskMethod(), getCallOptions()), request); } + /** + *
+     * List tasks with optional filtering and pagination.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture listTasks( + io.a2a.grpc.ListTasksRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getListTasksMethod(), getCallOptions()), request); + } + /** *
      * Cancel a task from the agent. If supported one should expect no
@@ -1106,13 +1168,14 @@ public com.google.common.util.concurrent.ListenableFuture implements
       io.grpc.stub.ServerCalls.UnaryMethod,
@@ -1143,6 +1206,10 @@ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserv
           serviceImpl.getTask((io.a2a.grpc.GetTaskRequest) request,
               (io.grpc.stub.StreamObserver) responseObserver);
           break;
+        case METHODID_LIST_TASKS:
+          serviceImpl.listTasks((io.a2a.grpc.ListTasksRequest) request,
+              (io.grpc.stub.StreamObserver) responseObserver);
+          break;
         case METHODID_CANCEL_TASK:
           serviceImpl.cancelTask((io.a2a.grpc.CancelTaskRequest) request,
               (io.grpc.stub.StreamObserver) responseObserver);
@@ -1210,6 +1277,13 @@ public static final io.grpc.ServerServiceDefinition bindService(AsyncService ser
               io.a2a.grpc.GetTaskRequest,
               io.a2a.grpc.Task>(
                 service, METHODID_GET_TASK)))
+        .addMethod(
+          getListTasksMethod(),
+          io.grpc.stub.ServerCalls.asyncUnaryCall(
+            new MethodHandlers<
+              io.a2a.grpc.ListTasksRequest,
+              io.a2a.grpc.ListTasksResponse>(
+                service, METHODID_LIST_TASKS)))
         .addMethod(
           getCancelTaskMethod(),
           io.grpc.stub.ServerCalls.asyncUnaryCall(
@@ -1310,6 +1384,7 @@ public static io.grpc.ServiceDescriptor getServiceDescriptor() {
               .addMethod(getSendMessageMethod())
               .addMethod(getSendStreamingMessageMethod())
               .addMethod(getGetTaskMethod())
+              .addMethod(getListTasksMethod())
               .addMethod(getCancelTaskMethod())
               .addMethod(getTaskSubscriptionMethod())
               .addMethod(getCreateTaskPushNotificationConfigMethod())
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java
index 2f749cdb9..118d0947e 100644
--- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java
+++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java
@@ -6,6 +6,10 @@
 package io.a2a.grpc;
 
 /**
+ * 
+ * --8<-- [start:APIKeySecurityScheme]
+ * 
+ * * Protobuf type {@code a2a.v1.APIKeySecurityScheme} */ @com.google.protobuf.Generated @@ -364,6 +368,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:APIKeySecurityScheme]
+   * 
+ * * Protobuf type {@code a2a.v1.APIKeySecurityScheme} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java index bc9202234..a01a1734b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:AgentCapabilities]
  * Defines the A2A feature set supported by the agent
  * 
* @@ -139,6 +140,21 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( return extensions_.get(index); } + public static final int STATE_TRANSITION_HISTORY_FIELD_NUMBER = 4; + private boolean stateTransitionHistory_ = false; + /** + *
+   * If the agent provides a history of state transitions for a task.
+   * 
+ * + * bool state_transition_history = 4; + * @return The stateTransitionHistory. + */ + @java.lang.Override + public boolean getStateTransitionHistory() { + return stateTransitionHistory_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -162,6 +178,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < extensions_.size(); i++) { output.writeMessage(3, extensions_.get(i)); } + if (stateTransitionHistory_ != false) { + output.writeBool(4, stateTransitionHistory_); + } getUnknownFields().writeTo(output); } @@ -183,6 +202,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, extensions_.get(i)); } + if (stateTransitionHistory_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(4, stateTransitionHistory_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -204,6 +227,8 @@ public boolean equals(final java.lang.Object obj) { != other.getPushNotifications()) return false; if (!getExtensionsList() .equals(other.getExtensionsList())) return false; + if (getStateTransitionHistory() + != other.getStateTransitionHistory()) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -225,6 +250,9 @@ public int hashCode() { hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER; hash = (53 * hash) + getExtensionsList().hashCode(); } + hash = (37 * hash) + STATE_TRANSITION_HISTORY_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getStateTransitionHistory()); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -324,6 +352,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentCapabilities]
    * Defines the A2A feature set supported by the agent
    * 
* @@ -369,6 +398,7 @@ public Builder clear() { extensionsBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00000004); + stateTransitionHistory_ = false; return this; } @@ -421,6 +451,9 @@ private void buildPartial0(io.a2a.grpc.AgentCapabilities result) { if (((from_bitField0_ & 0x00000002) != 0)) { result.pushNotifications_ = pushNotifications_; } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.stateTransitionHistory_ = stateTransitionHistory_; + } } @java.lang.Override @@ -467,6 +500,9 @@ public Builder mergeFrom(io.a2a.grpc.AgentCapabilities other) { } } } + if (other.getStateTransitionHistory() != false) { + setStateTransitionHistory(other.getStateTransitionHistory()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -516,6 +552,11 @@ public Builder mergeFrom( } break; } // case 26 + case 32: { + stateTransitionHistory_ = input.readBool(); + bitField0_ |= 0x00000008; + break; + } // case 32 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -933,6 +974,50 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder( return extensionsBuilder_; } + private boolean stateTransitionHistory_ ; + /** + *
+     * If the agent provides a history of state transitions for a task.
+     * 
+ * + * bool state_transition_history = 4; + * @return The stateTransitionHistory. + */ + @java.lang.Override + public boolean getStateTransitionHistory() { + return stateTransitionHistory_; + } + /** + *
+     * If the agent provides a history of state transitions for a task.
+     * 
+ * + * bool state_transition_history = 4; + * @param value The stateTransitionHistory to set. + * @return This builder for chaining. + */ + public Builder setStateTransitionHistory(boolean value) { + + stateTransitionHistory_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * If the agent provides a history of state transitions for a task.
+     * 
+ * + * bool state_transition_history = 4; + * @return This builder for chaining. + */ + public Builder clearStateTransitionHistory() { + bitField0_ = (bitField0_ & ~0x00000008); + stateTransitionHistory_ = false; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCapabilities) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java index d009adff5..df1a236ac 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java @@ -73,4 +73,14 @@ public interface AgentCapabilitiesOrBuilder extends */ io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( int index); + + /** + *
+   * If the agent provides a history of state transitions for a task.
+   * 
+ * + * bool state_transition_history = 4; + * @return The stateTransitionHistory. + */ + boolean getStateTransitionHistory(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index 60d2cfb51..6a001ac2c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -7,12 +7,13 @@ /** *
+ * --8<-- [start:AgentCard]
  * AgentCard conveys key information:
  * - Overall details (version, name, description, uses)
  * - Skills; a set of actions/solutions the agent can perform
  * - Default modalities/content types supported by the agent.
  * - Authentication requirements
- * Next ID: 18
+ * Next ID: 19
  * 
* * Protobuf type {@code a2a.v1.AgentCard} @@ -52,6 +53,7 @@ private AgentCard() { com.google.protobuf.LazyStringArrayList.emptyList(); skills_ = java.util.Collections.emptyList(); signatures_ = java.util.Collections.emptyList(); + iconUrl_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -1043,6 +1045,53 @@ public io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( return signatures_.get(index); } + public static final int ICON_URL_FIELD_NUMBER = 18; + @SuppressWarnings("serial") + private volatile java.lang.Object iconUrl_ = ""; + /** + *
+   * An optional URL to an icon for the agent.
+   * 
+ * + * string icon_url = 18; + * @return The iconUrl. + */ + @java.lang.Override + public java.lang.String getIconUrl() { + java.lang.Object ref = iconUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + iconUrl_ = s; + return s; + } + } + /** + *
+   * An optional URL to an icon for the agent.
+   * 
+ * + * string icon_url = 18; + * @return The bytes for iconUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIconUrlBytes() { + java.lang.Object ref = iconUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + iconUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -1111,6 +1160,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < signatures_.size(); i++) { output.writeMessage(17, signatures_.get(i)); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(iconUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 18, iconUrl_); + } getUnknownFields().writeTo(output); } @@ -1195,6 +1247,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(17, signatures_.get(i)); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(iconUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(18, iconUrl_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -1250,6 +1305,8 @@ public boolean equals(final java.lang.Object obj) { != other.getSupportsAuthenticatedExtendedCard()) return false; if (!getSignaturesList() .equals(other.getSignaturesList())) return false; + if (!getIconUrl() + .equals(other.getIconUrl())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -1314,6 +1371,8 @@ public int hashCode() { hash = (37 * hash) + SIGNATURES_FIELD_NUMBER; hash = (53 * hash) + getSignaturesList().hashCode(); } + hash = (37 * hash) + ICON_URL_FIELD_NUMBER; + hash = (53 * hash) + getIconUrl().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -1413,12 +1472,13 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentCard]
    * AgentCard conveys key information:
    * - Overall details (version, name, description, uses)
    * - Skills; a set of actions/solutions the agent can perform
    * - Default modalities/content types supported by the agent.
    * - Authentication requirements
-   * Next ID: 18
+   * Next ID: 19
    * 
* * Protobuf type {@code a2a.v1.AgentCard} @@ -1538,6 +1598,7 @@ public Builder clear() { signaturesBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00010000); + iconUrl_ = ""; return this; } @@ -1659,6 +1720,9 @@ private void buildPartial0(io.a2a.grpc.AgentCard result) { if (((from_bitField0_ & 0x00008000) != 0)) { result.supportsAuthenticatedExtendedCard_ = supportsAuthenticatedExtendedCard_; } + if (((from_bitField0_ & 0x00020000) != 0)) { + result.iconUrl_ = iconUrl_; + } result.bitField0_ |= to_bitField0_; } @@ -1845,6 +1909,11 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } } } + if (!other.getIconUrl().isEmpty()) { + iconUrl_ = other.iconUrl_; + bitField0_ |= 0x00020000; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -1998,6 +2067,11 @@ public Builder mergeFrom( } break; } // case 138 + case 146: { + iconUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00020000; + break; + } // case 146 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -5078,6 +5152,98 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder( return signaturesBuilder_; } + private java.lang.Object iconUrl_ = ""; + /** + *
+     * An optional URL to an icon for the agent.
+     * 
+ * + * string icon_url = 18; + * @return The iconUrl. + */ + public java.lang.String getIconUrl() { + java.lang.Object ref = iconUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + iconUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * An optional URL to an icon for the agent.
+     * 
+ * + * string icon_url = 18; + * @return The bytes for iconUrl. + */ + public com.google.protobuf.ByteString + getIconUrlBytes() { + java.lang.Object ref = iconUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + iconUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * An optional URL to an icon for the agent.
+     * 
+ * + * string icon_url = 18; + * @param value The iconUrl to set. + * @return This builder for chaining. + */ + public Builder setIconUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + iconUrl_ = value; + bitField0_ |= 0x00020000; + onChanged(); + return this; + } + /** + *
+     * An optional URL to an icon for the agent.
+     * 
+ * + * string icon_url = 18; + * @return This builder for chaining. + */ + public Builder clearIconUrl() { + iconUrl_ = getDefaultInstance().getIconUrl(); + bitField0_ = (bitField0_ & ~0x00020000); + onChanged(); + return this; + } + /** + *
+     * An optional URL to an icon for the agent.
+     * 
+ * + * string icon_url = 18; + * @param value The bytes for iconUrl to set. + * @return This builder for chaining. + */ + public Builder setIconUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + iconUrl_ = value; + bitField0_ |= 0x00020000; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCard) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java index 3ad50a5fd..da51cf2d8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java @@ -623,4 +623,24 @@ io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( */ io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( int index); + + /** + *
+   * An optional URL to an icon for the agent.
+   * 
+ * + * string icon_url = 18; + * @return The iconUrl. + */ + java.lang.String getIconUrl(); + /** + *
+   * An optional URL to an icon for the agent.
+   * 
+ * + * string icon_url = 18; + * @return The bytes for iconUrl. + */ + com.google.protobuf.ByteString + getIconUrlBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java index 790bcd941..132005624 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:AgentCardSignature]
  * AgentCardSignature represents a JWS signature of an AgentCard.
  * This follows the JSON format of an RFC 7515 JSON Web Signature (JWS).
  * 
@@ -369,6 +370,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentCardSignature]
    * AgentCardSignature represents a JWS signature of an AgentCard.
    * This follows the JSON format of an RFC 7515 JSON Web Signature (JWS).
    * 
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java index dd3906cb5..99a651f90 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:AgentExtension]
  * A declaration of an extension supported by an Agent.
  * 
* @@ -398,6 +399,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentExtension]
    * A declaration of an extension supported by an Agent.
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java index 120acd347..8e74499bf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:AgentInterface]
  * Defines additional transport information for the agent.
  * 
* @@ -315,6 +316,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentInterface]
    * Defines additional transport information for the agent.
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java index 57226e43c..32ed37765 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:AgentProvider]
  * Represents information about the service provider of an agent.
  * 
* @@ -315,6 +316,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentProvider]
    * Represents information about the service provider of an agent.
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java index 794c3db57..0895eaff1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:AgentSkill]
  * AgentSkill represents a unit of action/solution that the agent can perform.
  * One can think of this as a type of highly reliable solution that an agent
  * can be tasked to provide. Agents have the autonomy to choose how and when
@@ -68,7 +69,7 @@ private AgentSkill() {
   private volatile java.lang.Object id_ = "";
   /**
    * 
-   * Unique id of the skill within this agent.
+   * Unique identifier of the skill within this agent.
    * 
* * string id = 1; @@ -89,7 +90,7 @@ public java.lang.String getId() { } /** *
-   * Unique id of the skill within this agent.
+   * Unique identifier of the skill within this agent.
    * 
* * string id = 1; @@ -774,6 +775,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:AgentSkill]
    * AgentSkill represents a unit of action/solution that the agent can perform.
    * One can think of this as a type of highly reliable solution that an agent
    * can be tasked to provide. Agents have the autonomy to choose how and when
@@ -1096,7 +1098,7 @@ public Builder mergeFrom(
     private java.lang.Object id_ = "";
     /**
      * 
-     * Unique id of the skill within this agent.
+     * Unique identifier of the skill within this agent.
      * 
* * string id = 1; @@ -1116,7 +1118,7 @@ public java.lang.String getId() { } /** *
-     * Unique id of the skill within this agent.
+     * Unique identifier of the skill within this agent.
      * 
* * string id = 1; @@ -1137,7 +1139,7 @@ public java.lang.String getId() { } /** *
-     * Unique id of the skill within this agent.
+     * Unique identifier of the skill within this agent.
      * 
* * string id = 1; @@ -1154,7 +1156,7 @@ public Builder setId( } /** *
-     * Unique id of the skill within this agent.
+     * Unique identifier of the skill within this agent.
      * 
* * string id = 1; @@ -1168,7 +1170,7 @@ public Builder clearId() { } /** *
-     * Unique id of the skill within this agent.
+     * Unique identifier of the skill within this agent.
      * 
* * string id = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java index 88ebf7448..225316b1a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -12,7 +12,7 @@ public interface AgentSkillOrBuilder extends /** *
-   * Unique id of the skill within this agent.
+   * Unique identifier of the skill within this agent.
    * 
* * string id = 1; @@ -21,7 +21,7 @@ public interface AgentSkillOrBuilder extends java.lang.String getId(); /** *
-   * Unique id of the skill within this agent.
+   * Unique identifier of the skill within this agent.
    * 
* * string id = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java index e9be6369a..357d0c3a2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:Artifact]
  * Artifacts are the container for task completed results. These are similar
  * to Messages but are intended to be the product of a task, as opposed to
  * point-to-point communication.
@@ -61,7 +62,8 @@ private Artifact() {
   private volatile java.lang.Object artifactId_ = "";
   /**
    * 
-   * Unique id for the artifact. It must be at least unique within a task.
+   * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+   * within a task.
    * 
* * string artifact_id = 1; @@ -82,7 +84,8 @@ public java.lang.String getArtifactId() { } /** *
-   * Unique id for the artifact. It must be at least unique within a task.
+   * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+   * within a task.
    * 
* * string artifact_id = 1; @@ -573,6 +576,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:Artifact]
    * Artifacts are the container for task completed results. These are similar
    * to Messages but are intended to be the product of a task, as opposed to
    * point-to-point communication.
@@ -857,7 +861,8 @@ public Builder mergeFrom(
     private java.lang.Object artifactId_ = "";
     /**
      * 
-     * Unique id for the artifact. It must be at least unique within a task.
+     * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+     * within a task.
      * 
* * string artifact_id = 1; @@ -877,7 +882,8 @@ public java.lang.String getArtifactId() { } /** *
-     * Unique id for the artifact. It must be at least unique within a task.
+     * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+     * within a task.
      * 
* * string artifact_id = 1; @@ -898,7 +904,8 @@ public java.lang.String getArtifactId() { } /** *
-     * Unique id for the artifact. It must be at least unique within a task.
+     * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+     * within a task.
      * 
* * string artifact_id = 1; @@ -915,7 +922,8 @@ public Builder setArtifactId( } /** *
-     * Unique id for the artifact. It must be at least unique within a task.
+     * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+     * within a task.
      * 
* * string artifact_id = 1; @@ -929,7 +937,8 @@ public Builder clearArtifactId() { } /** *
-     * Unique id for the artifact. It must be at least unique within a task.
+     * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+     * within a task.
      * 
* * string artifact_id = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java index 05c14a8eb..341c9e1a1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java @@ -12,7 +12,8 @@ public interface ArtifactOrBuilder extends /** *
-   * Unique id for the artifact. It must be at least unique within a task.
+   * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+   * within a task.
    * 
* * string artifact_id = 1; @@ -21,7 +22,8 @@ public interface ArtifactOrBuilder extends java.lang.String getArtifactId(); /** *
-   * Unique id for the artifact. It must be at least unique within a task.
+   * Unique identifier (e.g. UUID) for the artifact. It must be at least unique
+   * within a task.
    * 
* * string artifact_id = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java index c7a21b14d..9456ffda4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:PushNotificationAuthenticationInfo]
  * Defines authentication details, used for push notifications.
  * 
* @@ -325,6 +326,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:PushNotificationAuthenticationInfo]
    * Defines authentication details, used for push notifications.
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java index 25d4dfd17..a4faf832e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:AuthorizationCodeOAuthFlow]
+ * 
+ * * Protobuf type {@code a2a.v1.AuthorizationCodeOAuthFlow} */ @com.google.protobuf.Generated @@ -503,6 +507,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:AuthorizationCodeOAuthFlow]
+   * 
+ * * Protobuf type {@code a2a.v1.AuthorizationCodeOAuthFlow} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java index 8674c35c3..8e4c40cb7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:CancelTaskRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.CancelTaskRequest} */ @com.google.protobuf.Generated @@ -49,7 +53,8 @@ private CancelTaskRequest() { private volatile java.lang.Object name_ = ""; /** *
-   * name=tasks/{id}
+   * The resource name of the task to cancel.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; @@ -70,7 +75,8 @@ public java.lang.String getName() { } /** *
-   * name=tasks/{id}
+   * The resource name of the task to cancel.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; @@ -248,6 +254,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:CancelTaskRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.CancelTaskRequest} */ public static final class Builder extends @@ -388,7 +398,8 @@ public Builder mergeFrom( private java.lang.Object name_ = ""; /** *
-     * name=tasks/{id}
+     * The resource name of the task to cancel.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -408,7 +419,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task to cancel.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -429,7 +441,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task to cancel.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -446,7 +459,8 @@ public Builder setName( } /** *
-     * name=tasks/{id}
+     * The resource name of the task to cancel.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -460,7 +474,8 @@ public Builder clearName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task to cancel.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java index 95431bfc4..5c571df44 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java @@ -12,7 +12,8 @@ public interface CancelTaskRequestOrBuilder extends /** *
-   * name=tasks/{id}
+   * The resource name of the task to cancel.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; @@ -21,7 +22,8 @@ public interface CancelTaskRequestOrBuilder extends java.lang.String getName(); /** *
-   * name=tasks/{id}
+   * The resource name of the task to cancel.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java index 184e94596..7e29d33d4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:ClientCredentialsOAuthFlow]
+ * 
+ * * Protobuf type {@code a2a.v1.ClientCredentialsOAuthFlow} */ @com.google.protobuf.Generated @@ -443,6 +447,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:ClientCredentialsOAuthFlow]
+   * 
+ * * Protobuf type {@code a2a.v1.ClientCredentialsOAuthFlow} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java index 1bd069a94..8afc6ffc8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:SetTaskPushNotificationConfigRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} */ @com.google.protobuf.Generated @@ -51,8 +55,8 @@ private CreateTaskPushNotificationConfigRequest() { private volatile java.lang.Object parent_ = ""; /** *
-   * The task resource for this config.
-   * Format: tasks/{id}
+   * The parent task resource for this config.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -73,8 +77,8 @@ public java.lang.String getParent() { } /** *
-   * The task resource for this config.
-   * Format: tasks/{id}
+   * The parent task resource for this config.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -99,6 +103,10 @@ public java.lang.String getParent() { @SuppressWarnings("serial") private volatile java.lang.Object configId_ = ""; /** + *
+   * The ID for the new config.
+   * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The configId. */ @@ -116,6 +124,10 @@ public java.lang.String getConfigId() { } } /** + *
+   * The ID for the new config.
+   * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for configId. */ @@ -137,6 +149,10 @@ public java.lang.String getConfigId() { public static final int CONFIG_FIELD_NUMBER = 3; private io.a2a.grpc.TaskPushNotificationConfig config_; /** + *
+   * The configuration to create.
+   * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ @@ -145,6 +161,10 @@ public boolean hasConfig() { return ((bitField0_ & 0x00000001) != 0); } /** + *
+   * The configuration to create.
+   * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ @@ -153,6 +173,10 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfig() { return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; } /** + *
+   * The configuration to create.
+   * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override @@ -343,6 +367,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:SetTaskPushNotificationConfigRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} */ public static final class Builder extends @@ -526,8 +554,8 @@ public Builder mergeFrom( private java.lang.Object parent_ = ""; /** *
-     * The task resource for this config.
-     * Format: tasks/{id}
+     * The parent task resource for this config.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -547,8 +575,8 @@ public java.lang.String getParent() { } /** *
-     * The task resource for this config.
-     * Format: tasks/{id}
+     * The parent task resource for this config.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -569,8 +597,8 @@ public java.lang.String getParent() { } /** *
-     * The task resource for this config.
-     * Format: tasks/{id}
+     * The parent task resource for this config.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -587,8 +615,8 @@ public Builder setParent( } /** *
-     * The task resource for this config.
-     * Format: tasks/{id}
+     * The parent task resource for this config.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -602,8 +630,8 @@ public Builder clearParent() { } /** *
-     * The task resource for this config.
-     * Format: tasks/{id}
+     * The parent task resource for this config.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -622,6 +650,10 @@ public Builder setParentBytes( private java.lang.Object configId_ = ""; /** + *
+     * The ID for the new config.
+     * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The configId. */ @@ -638,6 +670,10 @@ public java.lang.String getConfigId() { } } /** + *
+     * The ID for the new config.
+     * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for configId. */ @@ -655,6 +691,10 @@ public java.lang.String getConfigId() { } } /** + *
+     * The ID for the new config.
+     * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The configId to set. * @return This builder for chaining. @@ -668,6 +708,10 @@ public Builder setConfigId( return this; } /** + *
+     * The ID for the new config.
+     * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ @@ -678,6 +722,10 @@ public Builder clearConfigId() { return this; } /** + *
+     * The ID for the new config.
+     * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for configId to set. * @return This builder for chaining. @@ -696,6 +744,10 @@ public Builder setConfigIdBytes( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configBuilder_; /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ @@ -703,6 +755,10 @@ public boolean hasConfig() { return ((bitField0_ & 0x00000004) != 0); } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ @@ -714,6 +770,10 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfig() { } } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { @@ -730,6 +790,10 @@ public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { return this; } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setConfig( @@ -744,6 +808,10 @@ public Builder setConfig( return this; } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { @@ -765,6 +833,10 @@ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { return this; } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearConfig() { @@ -778,6 +850,10 @@ public Builder clearConfig() { return this; } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { @@ -786,6 +862,10 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { return internalGetConfigFieldBuilder().getBuilder(); } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { @@ -797,6 +877,10 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { } } /** + *
+     * The configuration to create.
+     * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java index 003750d66..3bba0f0ba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java @@ -12,8 +12,8 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends /** *
-   * The task resource for this config.
-   * Format: tasks/{id}
+   * The parent task resource for this config.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -22,8 +22,8 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends java.lang.String getParent(); /** *
-   * The task resource for this config.
-   * Format: tasks/{id}
+   * The parent task resource for this config.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -33,11 +33,19 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends getParentBytes(); /** + *
+   * The ID for the new config.
+   * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The configId. */ java.lang.String getConfigId(); /** + *
+   * The ID for the new config.
+   * 
+ * * string config_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for configId. */ @@ -45,16 +53,28 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends getConfigIdBytes(); /** + *
+   * The configuration to create.
+   * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ boolean hasConfig(); /** + *
+   * The configuration to create.
+   * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ io.a2a.grpc.TaskPushNotificationConfig getConfig(); /** + *
+   * The configuration to create.
+   * 
+ * * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java index de2f2edae..a74c5f1f7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:DataPart]
  * DataPart represents a structured blob. This is most commonly a JSON payload.
  * 
* @@ -238,6 +239,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:DataPart]
    * DataPart represents a structured blob. This is most commonly a JSON payload.
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java index 3f3fcc441..bf667f37b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:DeleteTaskPushNotificationConfigRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} */ @com.google.protobuf.Generated @@ -49,7 +53,8 @@ private DeleteTaskPushNotificationConfigRequest() { private volatile java.lang.Object name_ = ""; /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to delete.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -70,7 +75,8 @@ public java.lang.String getName() { } /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to delete.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -248,6 +254,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:DeleteTaskPushNotificationConfigRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} */ public static final class Builder extends @@ -388,7 +398,8 @@ public Builder mergeFrom( private java.lang.Object name_ = ""; /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to delete.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -408,7 +419,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to delete.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -429,7 +441,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to delete.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -446,7 +459,8 @@ public Builder setName( } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to delete.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -460,7 +474,8 @@ public Builder clearName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to delete.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java index a1ad16ac0..33cd1d13d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java @@ -12,7 +12,8 @@ public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to delete.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -21,7 +22,8 @@ public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends java.lang.String getName(); /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to delete.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java index ab117dd38..895266604 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:FilePart]
  * FilePart represents the different ways files can be provided. If files are
  * small, directly feeding the bytes is supported via file_with_bytes. If the
  * file is large, the agent should read the content as appropriate directly
@@ -36,6 +37,7 @@ private FilePart(com.google.protobuf.GeneratedMessage.Builder builder) {
   }
   private FilePart() {
     mimeType_ = "";
+    name_ = "";
   }
 
   public static final com.google.protobuf.Descriptors.Descriptor
@@ -205,6 +207,45 @@ public java.lang.String getMimeType() {
     }
   }
 
+  public static final int NAME_FIELD_NUMBER = 4;
+  @SuppressWarnings("serial")
+  private volatile java.lang.Object name_ = "";
+  /**
+   * string name = 4;
+   * @return The name.
+   */
+  @java.lang.Override
+  public java.lang.String getName() {
+    java.lang.Object ref = name_;
+    if (ref instanceof java.lang.String) {
+      return (java.lang.String) ref;
+    } else {
+      com.google.protobuf.ByteString bs = 
+          (com.google.protobuf.ByteString) ref;
+      java.lang.String s = bs.toStringUtf8();
+      name_ = s;
+      return s;
+    }
+  }
+  /**
+   * string name = 4;
+   * @return The bytes for name.
+   */
+  @java.lang.Override
+  public com.google.protobuf.ByteString
+      getNameBytes() {
+    java.lang.Object ref = name_;
+    if (ref instanceof java.lang.String) {
+      com.google.protobuf.ByteString b = 
+          com.google.protobuf.ByteString.copyFromUtf8(
+              (java.lang.String) ref);
+      name_ = b;
+      return b;
+    } else {
+      return (com.google.protobuf.ByteString) ref;
+    }
+  }
+
   private byte memoizedIsInitialized = -1;
   @java.lang.Override
   public final boolean isInitialized() {
@@ -229,6 +270,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output)
     if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mimeType_)) {
       com.google.protobuf.GeneratedMessage.writeString(output, 3, mimeType_);
     }
+    if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) {
+      com.google.protobuf.GeneratedMessage.writeString(output, 4, name_);
+    }
     getUnknownFields().writeTo(output);
   }
 
@@ -249,6 +293,9 @@ public int getSerializedSize() {
     if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mimeType_)) {
       size += com.google.protobuf.GeneratedMessage.computeStringSize(3, mimeType_);
     }
+    if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) {
+      size += com.google.protobuf.GeneratedMessage.computeStringSize(4, name_);
+    }
     size += getUnknownFields().getSerializedSize();
     memoizedSize = size;
     return size;
@@ -266,6 +313,8 @@ public boolean equals(final java.lang.Object obj) {
 
     if (!getMimeType()
         .equals(other.getMimeType())) return false;
+    if (!getName()
+        .equals(other.getName())) return false;
     if (!getFileCase().equals(other.getFileCase())) return false;
     switch (fileCase_) {
       case 1:
@@ -292,6 +341,8 @@ public int hashCode() {
     hash = (19 * hash) + getDescriptor().hashCode();
     hash = (37 * hash) + MIME_TYPE_FIELD_NUMBER;
     hash = (53 * hash) + getMimeType().hashCode();
+    hash = (37 * hash) + NAME_FIELD_NUMBER;
+    hash = (53 * hash) + getName().hashCode();
     switch (fileCase_) {
       case 1:
         hash = (37 * hash) + FILE_WITH_URI_FIELD_NUMBER;
@@ -403,6 +454,7 @@ protected Builder newBuilderForType(
   }
   /**
    * 
+   * --8<-- [start:FilePart]
    * FilePart represents the different ways files can be provided. If files are
    * small, directly feeding the bytes is supported via file_with_bytes. If the
    * file is large, the agent should read the content as appropriate directly
@@ -443,6 +495,7 @@ public Builder clear() {
       super.clear();
       bitField0_ = 0;
       mimeType_ = "";
+      name_ = "";
       fileCase_ = 0;
       file_ = null;
       return this;
@@ -482,6 +535,9 @@ private void buildPartial0(io.a2a.grpc.FilePart result) {
       if (((from_bitField0_ & 0x00000004) != 0)) {
         result.mimeType_ = mimeType_;
       }
+      if (((from_bitField0_ & 0x00000008) != 0)) {
+        result.name_ = name_;
+      }
     }
 
     private void buildPartialOneofs(io.a2a.grpc.FilePart result) {
@@ -506,6 +562,11 @@ public Builder mergeFrom(io.a2a.grpc.FilePart other) {
         bitField0_ |= 0x00000004;
         onChanged();
       }
+      if (!other.getName().isEmpty()) {
+        name_ = other.name_;
+        bitField0_ |= 0x00000008;
+        onChanged();
+      }
       switch (other.getFileCase()) {
         case FILE_WITH_URI: {
           fileCase_ = 1;
@@ -563,6 +624,11 @@ public Builder mergeFrom(
               bitField0_ |= 0x00000004;
               break;
             } // case 26
+            case 34: {
+              name_ = input.readStringRequireUtf8();
+              bitField0_ |= 0x00000008;
+              break;
+            } // case 34
             default: {
               if (!super.parseUnknownField(input, extensionRegistry, tag)) {
                 done = true; // was an endgroup tag
@@ -802,6 +868,78 @@ public Builder setMimeTypeBytes(
       return this;
     }
 
+    private java.lang.Object name_ = "";
+    /**
+     * string name = 4;
+     * @return The name.
+     */
+    public java.lang.String getName() {
+      java.lang.Object ref = name_;
+      if (!(ref instanceof java.lang.String)) {
+        com.google.protobuf.ByteString bs =
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        name_ = s;
+        return s;
+      } else {
+        return (java.lang.String) ref;
+      }
+    }
+    /**
+     * string name = 4;
+     * @return The bytes for name.
+     */
+    public com.google.protobuf.ByteString
+        getNameBytes() {
+      java.lang.Object ref = name_;
+      if (ref instanceof String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        name_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+    /**
+     * string name = 4;
+     * @param value The name to set.
+     * @return This builder for chaining.
+     */
+    public Builder setName(
+        java.lang.String value) {
+      if (value == null) { throw new NullPointerException(); }
+      name_ = value;
+      bitField0_ |= 0x00000008;
+      onChanged();
+      return this;
+    }
+    /**
+     * string name = 4;
+     * @return This builder for chaining.
+     */
+    public Builder clearName() {
+      name_ = getDefaultInstance().getName();
+      bitField0_ = (bitField0_ & ~0x00000008);
+      onChanged();
+      return this;
+    }
+    /**
+     * string name = 4;
+     * @param value The bytes for name to set.
+     * @return This builder for chaining.
+     */
+    public Builder setNameBytes(
+        com.google.protobuf.ByteString value) {
+      if (value == null) { throw new NullPointerException(); }
+      checkByteStringIsUtf8(value);
+      name_ = value;
+      bitField0_ |= 0x00000008;
+      onChanged();
+      return this;
+    }
+
     // @@protoc_insertion_point(builder_scope:a2a.v1.FilePart)
   }
 
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java
index 3f53232b3..2fc5d42e9 100644
--- a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java
+++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java
@@ -50,5 +50,17 @@ public interface FilePartOrBuilder extends
   com.google.protobuf.ByteString
       getMimeTypeBytes();
 
+  /**
+   * string name = 4;
+   * @return The name.
+   */
+  java.lang.String getName();
+  /**
+   * string name = 4;
+   * @return The bytes for name.
+   */
+  com.google.protobuf.ByteString
+      getNameBytes();
+
   io.a2a.grpc.FilePart.FileCase getFileCase();
 }
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java
index 22f7e9499..6c85ec5ec 100644
--- a/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java
+++ b/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java
@@ -7,7 +7,7 @@
 
 /**
  * 
- * Empty. Added to fix linter violation.
+ * --8<-- [start:GetAuthenticatedExtendedCardRequest]
  * 
* * Protobuf type {@code a2a.v1.GetAgentCardRequest} @@ -195,7 +195,7 @@ protected Builder newBuilderForType( } /** *
-   * Empty. Added to fix linter violation.
+   * --8<-- [start:GetAuthenticatedExtendedCardRequest]
    * 
* * Protobuf type {@code a2a.v1.GetAgentCardRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java index f828d56cd..342eaade1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:GetTaskPushNotificationConfigRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.GetTaskPushNotificationConfigRequest} */ @com.google.protobuf.Generated @@ -49,7 +53,8 @@ private GetTaskPushNotificationConfigRequest() { private volatile java.lang.Object name_ = ""; /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to retrieve.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -70,7 +75,8 @@ public java.lang.String getName() { } /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to retrieve.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -248,6 +254,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:GetTaskPushNotificationConfigRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.GetTaskPushNotificationConfigRequest} */ public static final class Builder extends @@ -388,7 +398,8 @@ public Builder mergeFrom( private java.lang.Object name_ = ""; /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to retrieve.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -408,7 +419,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to retrieve.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -429,7 +441,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to retrieve.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -446,7 +459,8 @@ public Builder setName( } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to retrieve.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -460,7 +474,8 @@ public Builder clearName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{push_id}
+     * The resource name of the config to retrieve.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java index 420bdfdb9..b11dbc7d6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java @@ -12,7 +12,8 @@ public interface GetTaskPushNotificationConfigRequestOrBuilder extends /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to retrieve.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -21,7 +22,8 @@ public interface GetTaskPushNotificationConfigRequestOrBuilder extends java.lang.String getName(); /** *
-   * name=tasks/{id}/pushNotificationConfigs/{push_id}
+   * The resource name of the config to retrieve.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java index 717b8afc6..35e5bdcc7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:GetTaskRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.GetTaskRequest} */ @com.google.protobuf.Generated @@ -49,7 +53,8 @@ private GetTaskRequest() { private volatile java.lang.Object name_ = ""; /** *
-   * name=tasks/{id}
+   * The resource name of the task.
+   * Format: tasks/{task_id}
    * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -70,7 +75,8 @@ public java.lang.String getName() { } /** *
-   * name=tasks/{id}
+   * The resource name of the task.
+   * Format: tasks/{task_id}
    * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -94,6 +100,10 @@ public java.lang.String getName() { public static final int HISTORY_LENGTH_FIELD_NUMBER = 2; private int historyLength_ = 0; /** + *
+   * The number of most recent messages from the task's history to retrieve.
+   * 
+ * * int32 history_length = 2; * @return The historyLength. */ @@ -270,6 +280,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:GetTaskRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.GetTaskRequest} */ public static final class Builder extends @@ -422,7 +436,8 @@ public Builder mergeFrom( private java.lang.Object name_ = ""; /** *
-     * name=tasks/{id}
+     * The resource name of the task.
+     * Format: tasks/{task_id}
      * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -442,7 +457,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task.
+     * Format: tasks/{task_id}
      * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -463,7 +479,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task.
+     * Format: tasks/{task_id}
      * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -480,7 +497,8 @@ public Builder setName( } /** *
-     * name=tasks/{id}
+     * The resource name of the task.
+     * Format: tasks/{task_id}
      * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -494,7 +512,8 @@ public Builder clearName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task.
+     * Format: tasks/{task_id}
      * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -513,6 +532,10 @@ public Builder setNameBytes( private int historyLength_ ; /** + *
+     * The number of most recent messages from the task's history to retrieve.
+     * 
+ * * int32 history_length = 2; * @return The historyLength. */ @@ -521,6 +544,10 @@ public int getHistoryLength() { return historyLength_; } /** + *
+     * The number of most recent messages from the task's history to retrieve.
+     * 
+ * * int32 history_length = 2; * @param value The historyLength to set. * @return This builder for chaining. @@ -533,6 +560,10 @@ public Builder setHistoryLength(int value) { return this; } /** + *
+     * The number of most recent messages from the task's history to retrieve.
+     * 
+ * * int32 history_length = 2; * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java index 4e686dc47..9de92dc09 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java @@ -12,7 +12,8 @@ public interface GetTaskRequestOrBuilder extends /** *
-   * name=tasks/{id}
+   * The resource name of the task.
+   * Format: tasks/{task_id}
    * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -21,7 +22,8 @@ public interface GetTaskRequestOrBuilder extends java.lang.String getName(); /** *
-   * name=tasks/{id}
+   * The resource name of the task.
+   * Format: tasks/{task_id}
    * 
* * string name = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -31,6 +33,10 @@ public interface GetTaskRequestOrBuilder extends getNameBytes(); /** + *
+   * The number of most recent messages from the task's history to retrieve.
+   * 
+ * * int32 history_length = 2; * @return The historyLength. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java index 2c612d7f9..9a99d69bd 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:HTTPAuthSecurityScheme]
+ * 
+ * * Protobuf type {@code a2a.v1.HTTPAuthSecurityScheme} */ @com.google.protobuf.Generated @@ -374,6 +378,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:HTTPAuthSecurityScheme]
+   * 
+ * * Protobuf type {@code a2a.v1.HTTPAuthSecurityScheme} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java index 7593dde41..7466cee14 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:ImplicitOAuthFlow]
+ * 
+ * * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} */ @com.google.protobuf.Generated @@ -443,6 +447,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:ImplicitOAuthFlow]
+   * 
+ * * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java index 6563baeb8..a65bfb060 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:ListTaskPushNotificationConfigRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigRequest} */ @com.google.protobuf.Generated @@ -50,7 +54,8 @@ private ListTaskPushNotificationConfigRequest() { private volatile java.lang.Object parent_ = ""; /** *
-   * parent=tasks/{id}
+   * The parent task resource.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1; @@ -71,7 +76,8 @@ public java.lang.String getParent() { } /** *
-   * parent=tasks/{id}
+   * The parent task resource.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1; @@ -344,6 +350,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:ListTaskPushNotificationConfigRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigRequest} */ public static final class Builder extends @@ -510,7 +520,8 @@ public Builder mergeFrom( private java.lang.Object parent_ = ""; /** *
-     * parent=tasks/{id}
+     * The parent task resource.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1; @@ -530,7 +541,8 @@ public java.lang.String getParent() { } /** *
-     * parent=tasks/{id}
+     * The parent task resource.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1; @@ -551,7 +563,8 @@ public java.lang.String getParent() { } /** *
-     * parent=tasks/{id}
+     * The parent task resource.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1; @@ -568,7 +581,8 @@ public Builder setParent( } /** *
-     * parent=tasks/{id}
+     * The parent task resource.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1; @@ -582,7 +596,8 @@ public Builder clearParent() { } /** *
-     * parent=tasks/{id}
+     * The parent task resource.
+     * Format: tasks/{task_id}
      * 
* * string parent = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java index 77f60b630..a52c00912 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java @@ -12,7 +12,8 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends /** *
-   * parent=tasks/{id}
+   * The parent task resource.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1; @@ -21,7 +22,8 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends java.lang.String getParent(); /** *
-   * parent=tasks/{id}
+   * The parent task resource.
+   * Format: tasks/{task_id}
    * 
* * string parent = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java index 8c22e9f5e..86dcfed1a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:ListTaskPushNotificationConfigSuccessResponse]
+ * 
+ * * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigResponse} */ @com.google.protobuf.Generated @@ -49,6 +53,10 @@ private ListTaskPushNotificationConfigResponse() { @SuppressWarnings("serial") private java.util.List configs_; /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override @@ -56,6 +64,10 @@ public java.util.List getConfigsList() { return configs_; } /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override @@ -64,6 +76,10 @@ public java.util.List getConfigsList() { return configs_; } /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override @@ -71,6 +87,10 @@ public int getConfigsCount() { return configs_.size(); } /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override @@ -78,6 +98,10 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { return configs_.get(index); } /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ @java.lang.Override @@ -305,6 +329,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:ListTaskPushNotificationConfigSuccessResponse]
+   * 
+ * * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigResponse} */ public static final class Builder extends @@ -514,6 +542,10 @@ private void ensureConfigsIsMutable() { io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configsBuilder_; /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public java.util.List getConfigsList() { @@ -524,6 +556,10 @@ public java.util.List getConfigsList() { } } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public int getConfigsCount() { @@ -534,6 +570,10 @@ public int getConfigsCount() { } } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { @@ -544,6 +584,10 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index) { } } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder setConfigs( @@ -561,6 +605,10 @@ public Builder setConfigs( return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder setConfigs( @@ -575,6 +623,10 @@ public Builder setConfigs( return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs(io.a2a.grpc.TaskPushNotificationConfig value) { @@ -591,6 +643,10 @@ public Builder addConfigs(io.a2a.grpc.TaskPushNotificationConfig value) { return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs( @@ -608,6 +664,10 @@ public Builder addConfigs( return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs( @@ -622,6 +682,10 @@ public Builder addConfigs( return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addConfigs( @@ -636,6 +700,10 @@ public Builder addConfigs( return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder addAllConfigs( @@ -651,6 +719,10 @@ public Builder addAllConfigs( return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder clearConfigs() { @@ -664,6 +736,10 @@ public Builder clearConfigs() { return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public Builder removeConfigs(int index) { @@ -677,6 +753,10 @@ public Builder removeConfigs(int index) { return this; } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigsBuilder( @@ -684,6 +764,10 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigsBuilder( return internalGetConfigsFieldBuilder().getBuilder(index); } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( @@ -694,6 +778,10 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( } } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public java.util.List @@ -705,6 +793,10 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( } } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder() { @@ -712,6 +804,10 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder() { io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()); } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder( @@ -720,6 +816,10 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder addConfigsBuilder( index, io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()); } /** + *
+     * The list of push notification configurations.
+     * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ public java.util.List diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java index e23fa8182..2e03d4a6a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java @@ -11,24 +11,44 @@ public interface ListTaskPushNotificationConfigResponseOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ java.util.List getConfigsList(); /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ io.a2a.grpc.TaskPushNotificationConfig getConfigs(int index); /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ int getConfigsCount(); /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ java.util.List getConfigsOrBuilderList(); /** + *
+   * The list of push notification configurations.
+   * 
+ * * repeated .a2a.v1.TaskPushNotificationConfig configs = 1; */ io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigsOrBuilder( diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java new file mode 100644 index 000000000..12e05bb63 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java @@ -0,0 +1,1348 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * --8<-- [start:ListTasksRequest]
+ * 
+ * + * Protobuf type {@code a2a.v1.ListTasksRequest} + */ +@com.google.protobuf.Generated +public final class ListTasksRequest extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ListTasksRequest) + ListTasksRequestOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + ListTasksRequest.class.getName()); + } + // Use ListTasksRequest.newBuilder() to construct. + private ListTasksRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ListTasksRequest() { + contextId_ = ""; + status_ = 0; + pageToken_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTasksRequest.class, io.a2a.grpc.ListTasksRequest.Builder.class); + } + + private int bitField0_; + public static final int CONTEXT_ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object contextId_ = ""; + /** + *
+   * Filter tasks by context ID to get tasks from a specific conversation
+   * or session.
+   * 
+ * + * string context_id = 1; + * @return The contextId. + */ + @java.lang.Override + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } + } + /** + *
+   * Filter tasks by context ID to get tasks from a specific conversation
+   * or session.
+   * 
+ * + * string context_id = 1; + * @return The bytes for contextId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int STATUS_FIELD_NUMBER = 2; + private int status_ = 0; + /** + *
+   * Filter tasks by their current status state.
+   * 
+ * + * .a2a.v1.TaskState status = 2; + * @return The enum numeric value on the wire for status. + */ + @java.lang.Override public int getStatusValue() { + return status_; + } + /** + *
+   * Filter tasks by their current status state.
+   * 
+ * + * .a2a.v1.TaskState status = 2; + * @return The status. + */ + @java.lang.Override public io.a2a.grpc.TaskState getStatus() { + io.a2a.grpc.TaskState result = io.a2a.grpc.TaskState.forNumber(status_); + return result == null ? io.a2a.grpc.TaskState.UNRECOGNIZED : result; + } + + public static final int PAGE_SIZE_FIELD_NUMBER = 3; + private int pageSize_ = 0; + /** + *
+   * Maximum number of tasks to return. Must be between 1 and 100.
+   * Defaults to 50 if not specified.
+   * 
+ * + * int32 page_size = 3; + * @return The pageSize. + */ + @java.lang.Override + public int getPageSize() { + return pageSize_; + } + + public static final int PAGE_TOKEN_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private volatile java.lang.Object pageToken_ = ""; + /** + *
+   * Token for pagination. Use the next_page_token from a previous
+   * ListTasksResponse.
+   * 
+ * + * string page_token = 4; + * @return The pageToken. + */ + @java.lang.Override + public java.lang.String getPageToken() { + java.lang.Object ref = pageToken_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + pageToken_ = s; + return s; + } + } + /** + *
+   * Token for pagination. Use the next_page_token from a previous
+   * ListTasksResponse.
+   * 
+ * + * string page_token = 4; + * @return The bytes for pageToken. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getPageTokenBytes() { + java.lang.Object ref = pageToken_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + pageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int HISTORY_LENGTH_FIELD_NUMBER = 5; + private int historyLength_ = 0; + /** + *
+   * Number of recent messages to include in each task's history.
+   * Must be non-negative. Defaults to 0 if not specified.
+   * 
+ * + * int32 history_length = 5; + * @return The historyLength. + */ + @java.lang.Override + public int getHistoryLength() { + return historyLength_; + } + + public static final int LAST_UPDATED_TIME_FIELD_NUMBER = 6; + private com.google.protobuf.Timestamp lastUpdatedTime_; + /** + *
+   * Filter tasks updated after this timestamp. Only tasks with a last
+   * updated time greater than or equal to this value will be returned.
+   * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + * @return Whether the lastUpdatedTime field is set. + */ + @java.lang.Override + public boolean hasLastUpdatedTime() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Filter tasks updated after this timestamp. Only tasks with a last
+   * updated time greater than or equal to this value will be returned.
+   * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + * @return The lastUpdatedTime. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getLastUpdatedTime() { + return lastUpdatedTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + } + /** + *
+   * Filter tasks updated after this timestamp. Only tasks with a last
+   * updated time greater than or equal to this value will be returned.
+   * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getLastUpdatedTimeOrBuilder() { + return lastUpdatedTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + } + + public static final int INCLUDE_ARTIFACTS_FIELD_NUMBER = 7; + private boolean includeArtifacts_ = false; + /** + *
+   * Whether to include artifacts in the returned tasks.
+   * Defaults to false to reduce payload size.
+   * 
+ * + * bool include_artifacts = 7; + * @return The includeArtifacts. + */ + @java.lang.Override + public boolean getIncludeArtifacts() { + return includeArtifacts_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, contextId_); + } + if (status_ != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED.getNumber()) { + output.writeEnum(2, status_); + } + if (pageSize_ != 0) { + output.writeInt32(3, pageSize_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, pageToken_); + } + if (historyLength_ != 0) { + output.writeInt32(5, historyLength_); + } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(6, getLastUpdatedTime()); + } + if (includeArtifacts_ != false) { + output.writeBool(7, includeArtifacts_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(contextId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, contextId_); + } + if (status_ != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED.getNumber()) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(2, status_); + } + if (pageSize_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, pageSize_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, pageToken_); + } + if (historyLength_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(5, historyLength_); + } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(6, getLastUpdatedTime()); + } + if (includeArtifacts_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(7, includeArtifacts_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ListTasksRequest)) { + return super.equals(obj); + } + io.a2a.grpc.ListTasksRequest other = (io.a2a.grpc.ListTasksRequest) obj; + + if (!getContextId() + .equals(other.getContextId())) return false; + if (status_ != other.status_) return false; + if (getPageSize() + != other.getPageSize()) return false; + if (!getPageToken() + .equals(other.getPageToken())) return false; + if (getHistoryLength() + != other.getHistoryLength()) return false; + if (hasLastUpdatedTime() != other.hasLastUpdatedTime()) return false; + if (hasLastUpdatedTime()) { + if (!getLastUpdatedTime() + .equals(other.getLastUpdatedTime())) return false; + } + if (getIncludeArtifacts() + != other.getIncludeArtifacts()) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + CONTEXT_ID_FIELD_NUMBER; + hash = (53 * hash) + getContextId().hashCode(); + hash = (37 * hash) + STATUS_FIELD_NUMBER; + hash = (53 * hash) + status_; + hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; + hash = (53 * hash) + getPageSize(); + hash = (37 * hash) + PAGE_TOKEN_FIELD_NUMBER; + hash = (53 * hash) + getPageToken().hashCode(); + hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getHistoryLength(); + if (hasLastUpdatedTime()) { + hash = (37 * hash) + LAST_UPDATED_TIME_FIELD_NUMBER; + hash = (53 * hash) + getLastUpdatedTime().hashCode(); + } + hash = (37 * hash) + INCLUDE_ARTIFACTS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getIncludeArtifacts()); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ListTasksRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTasksRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTasksRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ListTasksRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ListTasksRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTasksRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ListTasksRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * --8<-- [start:ListTasksRequest]
+   * 
+ * + * Protobuf type {@code a2a.v1.ListTasksRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ListTasksRequest) + io.a2a.grpc.ListTasksRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTasksRequest.class, io.a2a.grpc.ListTasksRequest.Builder.class); + } + + // Construct using io.a2a.grpc.ListTasksRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage + .alwaysUseFieldBuilders) { + internalGetLastUpdatedTimeFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + contextId_ = ""; + status_ = 0; + pageSize_ = 0; + pageToken_ = ""; + historyLength_ = 0; + lastUpdatedTime_ = null; + if (lastUpdatedTimeBuilder_ != null) { + lastUpdatedTimeBuilder_.dispose(); + lastUpdatedTimeBuilder_ = null; + } + includeArtifacts_ = false; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksRequest_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ListTasksRequest getDefaultInstanceForType() { + return io.a2a.grpc.ListTasksRequest.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ListTasksRequest build() { + io.a2a.grpc.ListTasksRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ListTasksRequest buildPartial() { + io.a2a.grpc.ListTasksRequest result = new io.a2a.grpc.ListTasksRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.ListTasksRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.contextId_ = contextId_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.status_ = status_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.pageSize_ = pageSize_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.pageToken_ = pageToken_; + } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.historyLength_ = historyLength_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000020) != 0)) { + result.lastUpdatedTime_ = lastUpdatedTimeBuilder_ == null + ? lastUpdatedTime_ + : lastUpdatedTimeBuilder_.build(); + to_bitField0_ |= 0x00000001; + } + if (((from_bitField0_ & 0x00000040) != 0)) { + result.includeArtifacts_ = includeArtifacts_; + } + result.bitField0_ |= to_bitField0_; + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ListTasksRequest) { + return mergeFrom((io.a2a.grpc.ListTasksRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) { + if (other == io.a2a.grpc.ListTasksRequest.getDefaultInstance()) return this; + if (!other.getContextId().isEmpty()) { + contextId_ = other.contextId_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.status_ != 0) { + setStatusValue(other.getStatusValue()); + } + if (other.getPageSize() != 0) { + setPageSize(other.getPageSize()); + } + if (!other.getPageToken().isEmpty()) { + pageToken_ = other.pageToken_; + bitField0_ |= 0x00000008; + onChanged(); + } + if (other.getHistoryLength() != 0) { + setHistoryLength(other.getHistoryLength()); + } + if (other.hasLastUpdatedTime()) { + mergeLastUpdatedTime(other.getLastUpdatedTime()); + } + if (other.getIncludeArtifacts() != false) { + setIncludeArtifacts(other.getIncludeArtifacts()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + contextId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 16: { + status_ = input.readEnum(); + bitField0_ |= 0x00000002; + break; + } // case 16 + case 24: { + pageSize_ = input.readInt32(); + bitField0_ |= 0x00000004; + break; + } // case 24 + case 34: { + pageToken_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000008; + break; + } // case 34 + case 40: { + historyLength_ = input.readInt32(); + bitField0_ |= 0x00000010; + break; + } // case 40 + case 50: { + input.readMessage( + internalGetLastUpdatedTimeFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000020; + break; + } // case 50 + case 56: { + includeArtifacts_ = input.readBool(); + bitField0_ |= 0x00000040; + break; + } // case 56 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object contextId_ = ""; + /** + *
+     * Filter tasks by context ID to get tasks from a specific conversation
+     * or session.
+     * 
+ * + * string context_id = 1; + * @return The contextId. + */ + public java.lang.String getContextId() { + java.lang.Object ref = contextId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + contextId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Filter tasks by context ID to get tasks from a specific conversation
+     * or session.
+     * 
+ * + * string context_id = 1; + * @return The bytes for contextId. + */ + public com.google.protobuf.ByteString + getContextIdBytes() { + java.lang.Object ref = contextId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + contextId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Filter tasks by context ID to get tasks from a specific conversation
+     * or session.
+     * 
+ * + * string context_id = 1; + * @param value The contextId to set. + * @return This builder for chaining. + */ + public Builder setContextId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + contextId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
+     * Filter tasks by context ID to get tasks from a specific conversation
+     * or session.
+     * 
+ * + * string context_id = 1; + * @return This builder for chaining. + */ + public Builder clearContextId() { + contextId_ = getDefaultInstance().getContextId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
+     * Filter tasks by context ID to get tasks from a specific conversation
+     * or session.
+     * 
+ * + * string context_id = 1; + * @param value The bytes for contextId to set. + * @return This builder for chaining. + */ + public Builder setContextIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + contextId_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private int status_ = 0; + /** + *
+     * Filter tasks by their current status state.
+     * 
+ * + * .a2a.v1.TaskState status = 2; + * @return The enum numeric value on the wire for status. + */ + @java.lang.Override public int getStatusValue() { + return status_; + } + /** + *
+     * Filter tasks by their current status state.
+     * 
+ * + * .a2a.v1.TaskState status = 2; + * @param value The enum numeric value on the wire for status to set. + * @return This builder for chaining. + */ + public Builder setStatusValue(int value) { + status_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Filter tasks by their current status state.
+     * 
+ * + * .a2a.v1.TaskState status = 2; + * @return The status. + */ + @java.lang.Override + public io.a2a.grpc.TaskState getStatus() { + io.a2a.grpc.TaskState result = io.a2a.grpc.TaskState.forNumber(status_); + return result == null ? io.a2a.grpc.TaskState.UNRECOGNIZED : result; + } + /** + *
+     * Filter tasks by their current status state.
+     * 
+ * + * .a2a.v1.TaskState status = 2; + * @param value The status to set. + * @return This builder for chaining. + */ + public Builder setStatus(io.a2a.grpc.TaskState value) { + if (value == null) { throw new NullPointerException(); } + bitField0_ |= 0x00000002; + status_ = value.getNumber(); + onChanged(); + return this; + } + /** + *
+     * Filter tasks by their current status state.
+     * 
+ * + * .a2a.v1.TaskState status = 2; + * @return This builder for chaining. + */ + public Builder clearStatus() { + bitField0_ = (bitField0_ & ~0x00000002); + status_ = 0; + onChanged(); + return this; + } + + private int pageSize_ ; + /** + *
+     * Maximum number of tasks to return. Must be between 1 and 100.
+     * Defaults to 50 if not specified.
+     * 
+ * + * int32 page_size = 3; + * @return The pageSize. + */ + @java.lang.Override + public int getPageSize() { + return pageSize_; + } + /** + *
+     * Maximum number of tasks to return. Must be between 1 and 100.
+     * Defaults to 50 if not specified.
+     * 
+ * + * int32 page_size = 3; + * @param value The pageSize to set. + * @return This builder for chaining. + */ + public Builder setPageSize(int value) { + + pageSize_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Maximum number of tasks to return. Must be between 1 and 100.
+     * Defaults to 50 if not specified.
+     * 
+ * + * int32 page_size = 3; + * @return This builder for chaining. + */ + public Builder clearPageSize() { + bitField0_ = (bitField0_ & ~0x00000004); + pageSize_ = 0; + onChanged(); + return this; + } + + private java.lang.Object pageToken_ = ""; + /** + *
+     * Token for pagination. Use the next_page_token from a previous
+     * ListTasksResponse.
+     * 
+ * + * string page_token = 4; + * @return The pageToken. + */ + public java.lang.String getPageToken() { + java.lang.Object ref = pageToken_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + pageToken_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Token for pagination. Use the next_page_token from a previous
+     * ListTasksResponse.
+     * 
+ * + * string page_token = 4; + * @return The bytes for pageToken. + */ + public com.google.protobuf.ByteString + getPageTokenBytes() { + java.lang.Object ref = pageToken_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + pageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Token for pagination. Use the next_page_token from a previous
+     * ListTasksResponse.
+     * 
+ * + * string page_token = 4; + * @param value The pageToken to set. + * @return This builder for chaining. + */ + public Builder setPageToken( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + pageToken_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Token for pagination. Use the next_page_token from a previous
+     * ListTasksResponse.
+     * 
+ * + * string page_token = 4; + * @return This builder for chaining. + */ + public Builder clearPageToken() { + pageToken_ = getDefaultInstance().getPageToken(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + *
+     * Token for pagination. Use the next_page_token from a previous
+     * ListTasksResponse.
+     * 
+ * + * string page_token = 4; + * @param value The bytes for pageToken to set. + * @return This builder for chaining. + */ + public Builder setPageTokenBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + pageToken_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + + private int historyLength_ ; + /** + *
+     * Number of recent messages to include in each task's history.
+     * Must be non-negative. Defaults to 0 if not specified.
+     * 
+ * + * int32 history_length = 5; + * @return The historyLength. + */ + @java.lang.Override + public int getHistoryLength() { + return historyLength_; + } + /** + *
+     * Number of recent messages to include in each task's history.
+     * Must be non-negative. Defaults to 0 if not specified.
+     * 
+ * + * int32 history_length = 5; + * @param value The historyLength to set. + * @return This builder for chaining. + */ + public Builder setHistoryLength(int value) { + + historyLength_ = value; + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
+     * Number of recent messages to include in each task's history.
+     * Must be non-negative. Defaults to 0 if not specified.
+     * 
+ * + * int32 history_length = 5; + * @return This builder for chaining. + */ + public Builder clearHistoryLength() { + bitField0_ = (bitField0_ & ~0x00000010); + historyLength_ = 0; + onChanged(); + return this; + } + + private com.google.protobuf.Timestamp lastUpdatedTime_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> lastUpdatedTimeBuilder_; + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + * @return Whether the lastUpdatedTime field is set. + */ + public boolean hasLastUpdatedTime() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + * @return The lastUpdatedTime. + */ + public com.google.protobuf.Timestamp getLastUpdatedTime() { + if (lastUpdatedTimeBuilder_ == null) { + return lastUpdatedTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + } else { + return lastUpdatedTimeBuilder_.getMessage(); + } + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + public Builder setLastUpdatedTime(com.google.protobuf.Timestamp value) { + if (lastUpdatedTimeBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + lastUpdatedTime_ = value; + } else { + lastUpdatedTimeBuilder_.setMessage(value); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + public Builder setLastUpdatedTime( + com.google.protobuf.Timestamp.Builder builderForValue) { + if (lastUpdatedTimeBuilder_ == null) { + lastUpdatedTime_ = builderForValue.build(); + } else { + lastUpdatedTimeBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + public Builder mergeLastUpdatedTime(com.google.protobuf.Timestamp value) { + if (lastUpdatedTimeBuilder_ == null) { + if (((bitField0_ & 0x00000020) != 0) && + lastUpdatedTime_ != null && + lastUpdatedTime_ != com.google.protobuf.Timestamp.getDefaultInstance()) { + getLastUpdatedTimeBuilder().mergeFrom(value); + } else { + lastUpdatedTime_ = value; + } + } else { + lastUpdatedTimeBuilder_.mergeFrom(value); + } + if (lastUpdatedTime_ != null) { + bitField0_ |= 0x00000020; + onChanged(); + } + return this; + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + public Builder clearLastUpdatedTime() { + bitField0_ = (bitField0_ & ~0x00000020); + lastUpdatedTime_ = null; + if (lastUpdatedTimeBuilder_ != null) { + lastUpdatedTimeBuilder_.dispose(); + lastUpdatedTimeBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + public com.google.protobuf.Timestamp.Builder getLastUpdatedTimeBuilder() { + bitField0_ |= 0x00000020; + onChanged(); + return internalGetLastUpdatedTimeFieldBuilder().getBuilder(); + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + public com.google.protobuf.TimestampOrBuilder getLastUpdatedTimeOrBuilder() { + if (lastUpdatedTimeBuilder_ != null) { + return lastUpdatedTimeBuilder_.getMessageOrBuilder(); + } else { + return lastUpdatedTime_ == null ? + com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + } + } + /** + *
+     * Filter tasks updated after this timestamp. Only tasks with a last
+     * updated time greater than or equal to this value will be returned.
+     * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> + internalGetLastUpdatedTimeFieldBuilder() { + if (lastUpdatedTimeBuilder_ == null) { + lastUpdatedTimeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>( + getLastUpdatedTime(), + getParentForChildren(), + isClean()); + lastUpdatedTime_ = null; + } + return lastUpdatedTimeBuilder_; + } + + private boolean includeArtifacts_ ; + /** + *
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
+     * 
+ * + * bool include_artifacts = 7; + * @return The includeArtifacts. + */ + @java.lang.Override + public boolean getIncludeArtifacts() { + return includeArtifacts_; + } + /** + *
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
+     * 
+ * + * bool include_artifacts = 7; + * @param value The includeArtifacts to set. + * @return This builder for chaining. + */ + public Builder setIncludeArtifacts(boolean value) { + + includeArtifacts_ = value; + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
+     * 
+ * + * bool include_artifacts = 7; + * @return This builder for chaining. + */ + public Builder clearIncludeArtifacts() { + bitField0_ = (bitField0_ & ~0x00000040); + includeArtifacts_ = false; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ListTasksRequest) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ListTasksRequest) + private static final io.a2a.grpc.ListTasksRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ListTasksRequest(); + } + + public static io.a2a.grpc.ListTasksRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ListTasksRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ListTasksRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java new file mode 100644 index 000000000..70a7061ff --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java @@ -0,0 +1,138 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ListTasksRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ListTasksRequest) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Filter tasks by context ID to get tasks from a specific conversation
+   * or session.
+   * 
+ * + * string context_id = 1; + * @return The contextId. + */ + java.lang.String getContextId(); + /** + *
+   * Filter tasks by context ID to get tasks from a specific conversation
+   * or session.
+   * 
+ * + * string context_id = 1; + * @return The bytes for contextId. + */ + com.google.protobuf.ByteString + getContextIdBytes(); + + /** + *
+   * Filter tasks by their current status state.
+   * 
+ * + * .a2a.v1.TaskState status = 2; + * @return The enum numeric value on the wire for status. + */ + int getStatusValue(); + /** + *
+   * Filter tasks by their current status state.
+   * 
+ * + * .a2a.v1.TaskState status = 2; + * @return The status. + */ + io.a2a.grpc.TaskState getStatus(); + + /** + *
+   * Maximum number of tasks to return. Must be between 1 and 100.
+   * Defaults to 50 if not specified.
+   * 
+ * + * int32 page_size = 3; + * @return The pageSize. + */ + int getPageSize(); + + /** + *
+   * Token for pagination. Use the next_page_token from a previous
+   * ListTasksResponse.
+   * 
+ * + * string page_token = 4; + * @return The pageToken. + */ + java.lang.String getPageToken(); + /** + *
+   * Token for pagination. Use the next_page_token from a previous
+   * ListTasksResponse.
+   * 
+ * + * string page_token = 4; + * @return The bytes for pageToken. + */ + com.google.protobuf.ByteString + getPageTokenBytes(); + + /** + *
+   * Number of recent messages to include in each task's history.
+   * Must be non-negative. Defaults to 0 if not specified.
+   * 
+ * + * int32 history_length = 5; + * @return The historyLength. + */ + int getHistoryLength(); + + /** + *
+   * Filter tasks updated after this timestamp. Only tasks with a last
+   * updated time greater than or equal to this value will be returned.
+   * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + * @return Whether the lastUpdatedTime field is set. + */ + boolean hasLastUpdatedTime(); + /** + *
+   * Filter tasks updated after this timestamp. Only tasks with a last
+   * updated time greater than or equal to this value will be returned.
+   * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + * @return The lastUpdatedTime. + */ + com.google.protobuf.Timestamp getLastUpdatedTime(); + /** + *
+   * Filter tasks updated after this timestamp. Only tasks with a last
+   * updated time greater than or equal to this value will be returned.
+   * 
+ * + * .google.protobuf.Timestamp last_updated_time = 6; + */ + com.google.protobuf.TimestampOrBuilder getLastUpdatedTimeOrBuilder(); + + /** + *
+   * Whether to include artifacts in the returned tasks.
+   * Defaults to false to reduce payload size.
+   * 
+ * + * bool include_artifacts = 7; + * @return The includeArtifacts. + */ + boolean getIncludeArtifacts(); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java new file mode 100644 index 000000000..20ed57e27 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java @@ -0,0 +1,1073 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +/** + *
+ * --8<-- [start:ListTasksResponse]
+ * 
+ * + * Protobuf type {@code a2a.v1.ListTasksResponse} + */ +@com.google.protobuf.Generated +public final class ListTasksResponse extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ListTasksResponse) + ListTasksResponseOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 31, + /* patch= */ 1, + /* suffix= */ "", + ListTasksResponse.class.getName()); + } + // Use ListTasksResponse.newBuilder() to construct. + private ListTasksResponse(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ListTasksResponse() { + tasks_ = java.util.Collections.emptyList(); + nextPageToken_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTasksResponse.class, io.a2a.grpc.ListTasksResponse.Builder.class); + } + + public static final int TASKS_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private java.util.List tasks_; + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + @java.lang.Override + public java.util.List getTasksList() { + return tasks_; + } + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + @java.lang.Override + public java.util.List + getTasksOrBuilderList() { + return tasks_; + } + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + @java.lang.Override + public int getTasksCount() { + return tasks_.size(); + } + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + @java.lang.Override + public io.a2a.grpc.Task getTasks(int index) { + return tasks_.get(index); + } + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + @java.lang.Override + public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( + int index) { + return tasks_.get(index); + } + + public static final int NEXT_PAGE_TOKEN_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object nextPageToken_ = ""; + /** + *
+   * Token for retrieving the next page of results.
+   * Empty string if no more results.
+   * 
+ * + * string next_page_token = 2; + * @return The nextPageToken. + */ + @java.lang.Override + public java.lang.String getNextPageToken() { + java.lang.Object ref = nextPageToken_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nextPageToken_ = s; + return s; + } + } + /** + *
+   * Token for retrieving the next page of results.
+   * Empty string if no more results.
+   * 
+ * + * string next_page_token = 2; + * @return The bytes for nextPageToken. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getNextPageTokenBytes() { + java.lang.Object ref = nextPageToken_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nextPageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TOTAL_SIZE_FIELD_NUMBER = 3; + private int totalSize_ = 0; + /** + *
+   * Total number of tasks available (before pagination).
+   * 
+ * + * int32 total_size = 3; + * @return The totalSize. + */ + @java.lang.Override + public int getTotalSize() { + return totalSize_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + for (int i = 0; i < tasks_.size(); i++) { + output.writeMessage(1, tasks_.get(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(nextPageToken_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, nextPageToken_); + } + if (totalSize_ != 0) { + output.writeInt32(3, totalSize_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < tasks_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, tasks_.get(i)); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(nextPageToken_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, nextPageToken_); + } + if (totalSize_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, totalSize_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ListTasksResponse)) { + return super.equals(obj); + } + io.a2a.grpc.ListTasksResponse other = (io.a2a.grpc.ListTasksResponse) obj; + + if (!getTasksList() + .equals(other.getTasksList())) return false; + if (!getNextPageToken() + .equals(other.getNextPageToken())) return false; + if (getTotalSize() + != other.getTotalSize()) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + if (getTasksCount() > 0) { + hash = (37 * hash) + TASKS_FIELD_NUMBER; + hash = (53 * hash) + getTasksList().hashCode(); + } + hash = (37 * hash) + NEXT_PAGE_TOKEN_FIELD_NUMBER; + hash = (53 * hash) + getNextPageToken().hashCode(); + hash = (37 * hash) + TOTAL_SIZE_FIELD_NUMBER; + hash = (53 * hash) + getTotalSize(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ListTasksResponse parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTasksResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ListTasksResponse parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ListTasksResponse parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ListTasksResponse parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ListTasksResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ListTasksResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * --8<-- [start:ListTasksResponse]
+   * 
+ * + * Protobuf type {@code a2a.v1.ListTasksResponse} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ListTasksResponse) + io.a2a.grpc.ListTasksResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ListTasksResponse.class, io.a2a.grpc.ListTasksResponse.Builder.class); + } + + // Construct using io.a2a.grpc.ListTasksResponse.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + if (tasksBuilder_ == null) { + tasks_ = java.util.Collections.emptyList(); + } else { + tasks_ = null; + tasksBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000001); + nextPageToken_ = ""; + totalSize_ = 0; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ListTasksResponse_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ListTasksResponse getDefaultInstanceForType() { + return io.a2a.grpc.ListTasksResponse.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ListTasksResponse build() { + io.a2a.grpc.ListTasksResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ListTasksResponse buildPartial() { + io.a2a.grpc.ListTasksResponse result = new io.a2a.grpc.ListTasksResponse(this); + buildPartialRepeatedFields(result); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartialRepeatedFields(io.a2a.grpc.ListTasksResponse result) { + if (tasksBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + tasks_ = java.util.Collections.unmodifiableList(tasks_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.tasks_ = tasks_; + } else { + result.tasks_ = tasksBuilder_.build(); + } + } + + private void buildPartial0(io.a2a.grpc.ListTasksResponse result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000002) != 0)) { + result.nextPageToken_ = nextPageToken_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.totalSize_ = totalSize_; + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ListTasksResponse) { + return mergeFrom((io.a2a.grpc.ListTasksResponse)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ListTasksResponse other) { + if (other == io.a2a.grpc.ListTasksResponse.getDefaultInstance()) return this; + if (tasksBuilder_ == null) { + if (!other.tasks_.isEmpty()) { + if (tasks_.isEmpty()) { + tasks_ = other.tasks_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureTasksIsMutable(); + tasks_.addAll(other.tasks_); + } + onChanged(); + } + } else { + if (!other.tasks_.isEmpty()) { + if (tasksBuilder_.isEmpty()) { + tasksBuilder_.dispose(); + tasksBuilder_ = null; + tasks_ = other.tasks_; + bitField0_ = (bitField0_ & ~0x00000001); + tasksBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetTasksFieldBuilder() : null; + } else { + tasksBuilder_.addAllMessages(other.tasks_); + } + } + } + if (!other.getNextPageToken().isEmpty()) { + nextPageToken_ = other.nextPageToken_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (other.getTotalSize() != 0) { + setTotalSize(other.getTotalSize()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + io.a2a.grpc.Task m = + input.readMessage( + io.a2a.grpc.Task.parser(), + extensionRegistry); + if (tasksBuilder_ == null) { + ensureTasksIsMutable(); + tasks_.add(m); + } else { + tasksBuilder_.addMessage(m); + } + break; + } // case 10 + case 18: { + nextPageToken_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 24: { + totalSize_ = input.readInt32(); + bitField0_ |= 0x00000004; + break; + } // case 24 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.util.List tasks_ = + java.util.Collections.emptyList(); + private void ensureTasksIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + tasks_ = new java.util.ArrayList(tasks_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> tasksBuilder_; + + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public java.util.List getTasksList() { + if (tasksBuilder_ == null) { + return java.util.Collections.unmodifiableList(tasks_); + } else { + return tasksBuilder_.getMessageList(); + } + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public int getTasksCount() { + if (tasksBuilder_ == null) { + return tasks_.size(); + } else { + return tasksBuilder_.getCount(); + } + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public io.a2a.grpc.Task getTasks(int index) { + if (tasksBuilder_ == null) { + return tasks_.get(index); + } else { + return tasksBuilder_.getMessage(index); + } + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder setTasks( + int index, io.a2a.grpc.Task value) { + if (tasksBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureTasksIsMutable(); + tasks_.set(index, value); + onChanged(); + } else { + tasksBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder setTasks( + int index, io.a2a.grpc.Task.Builder builderForValue) { + if (tasksBuilder_ == null) { + ensureTasksIsMutable(); + tasks_.set(index, builderForValue.build()); + onChanged(); + } else { + tasksBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder addTasks(io.a2a.grpc.Task value) { + if (tasksBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureTasksIsMutable(); + tasks_.add(value); + onChanged(); + } else { + tasksBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder addTasks( + int index, io.a2a.grpc.Task value) { + if (tasksBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureTasksIsMutable(); + tasks_.add(index, value); + onChanged(); + } else { + tasksBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder addTasks( + io.a2a.grpc.Task.Builder builderForValue) { + if (tasksBuilder_ == null) { + ensureTasksIsMutable(); + tasks_.add(builderForValue.build()); + onChanged(); + } else { + tasksBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder addTasks( + int index, io.a2a.grpc.Task.Builder builderForValue) { + if (tasksBuilder_ == null) { + ensureTasksIsMutable(); + tasks_.add(index, builderForValue.build()); + onChanged(); + } else { + tasksBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder addAllTasks( + java.lang.Iterable values) { + if (tasksBuilder_ == null) { + ensureTasksIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, tasks_); + onChanged(); + } else { + tasksBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder clearTasks() { + if (tasksBuilder_ == null) { + tasks_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + tasksBuilder_.clear(); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public Builder removeTasks(int index) { + if (tasksBuilder_ == null) { + ensureTasksIsMutable(); + tasks_.remove(index); + onChanged(); + } else { + tasksBuilder_.remove(index); + } + return this; + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public io.a2a.grpc.Task.Builder getTasksBuilder( + int index) { + return internalGetTasksFieldBuilder().getBuilder(index); + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( + int index) { + if (tasksBuilder_ == null) { + return tasks_.get(index); } else { + return tasksBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public java.util.List + getTasksOrBuilderList() { + if (tasksBuilder_ != null) { + return tasksBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(tasks_); + } + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public io.a2a.grpc.Task.Builder addTasksBuilder() { + return internalGetTasksFieldBuilder().addBuilder( + io.a2a.grpc.Task.getDefaultInstance()); + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public io.a2a.grpc.Task.Builder addTasksBuilder( + int index) { + return internalGetTasksFieldBuilder().addBuilder( + index, io.a2a.grpc.Task.getDefaultInstance()); + } + /** + *
+     * Array of tasks matching the specified criteria.
+     * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + public java.util.List + getTasksBuilderList() { + return internalGetTasksFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> + internalGetTasksFieldBuilder() { + if (tasksBuilder_ == null) { + tasksBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder>( + tasks_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + tasks_ = null; + } + return tasksBuilder_; + } + + private java.lang.Object nextPageToken_ = ""; + /** + *
+     * Token for retrieving the next page of results.
+     * Empty string if no more results.
+     * 
+ * + * string next_page_token = 2; + * @return The nextPageToken. + */ + public java.lang.String getNextPageToken() { + java.lang.Object ref = nextPageToken_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + nextPageToken_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+     * Token for retrieving the next page of results.
+     * Empty string if no more results.
+     * 
+ * + * string next_page_token = 2; + * @return The bytes for nextPageToken. + */ + public com.google.protobuf.ByteString + getNextPageTokenBytes() { + java.lang.Object ref = nextPageToken_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + nextPageToken_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+     * Token for retrieving the next page of results.
+     * Empty string if no more results.
+     * 
+ * + * string next_page_token = 2; + * @param value The nextPageToken to set. + * @return This builder for chaining. + */ + public Builder setNextPageToken( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + nextPageToken_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
+     * Token for retrieving the next page of results.
+     * Empty string if no more results.
+     * 
+ * + * string next_page_token = 2; + * @return This builder for chaining. + */ + public Builder clearNextPageToken() { + nextPageToken_ = getDefaultInstance().getNextPageToken(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
+     * Token for retrieving the next page of results.
+     * Empty string if no more results.
+     * 
+ * + * string next_page_token = 2; + * @param value The bytes for nextPageToken to set. + * @return This builder for chaining. + */ + public Builder setNextPageTokenBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + nextPageToken_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private int totalSize_ ; + /** + *
+     * Total number of tasks available (before pagination).
+     * 
+ * + * int32 total_size = 3; + * @return The totalSize. + */ + @java.lang.Override + public int getTotalSize() { + return totalSize_; + } + /** + *
+     * Total number of tasks available (before pagination).
+     * 
+ * + * int32 total_size = 3; + * @param value The totalSize to set. + * @return This builder for chaining. + */ + public Builder setTotalSize(int value) { + + totalSize_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * Total number of tasks available (before pagination).
+     * 
+ * + * int32 total_size = 3; + * @return This builder for chaining. + */ + public Builder clearTotalSize() { + bitField0_ = (bitField0_ & ~0x00000004); + totalSize_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ListTasksResponse) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ListTasksResponse) + private static final io.a2a.grpc.ListTasksResponse DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ListTasksResponse(); + } + + public static io.a2a.grpc.ListTasksResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ListTasksResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ListTasksResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java new file mode 100644 index 000000000..08b8eb22a --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java @@ -0,0 +1,88 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.31.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ListTasksResponseOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ListTasksResponse) + com.google.protobuf.MessageOrBuilder { + + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + java.util.List + getTasksList(); + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + io.a2a.grpc.Task getTasks(int index); + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + int getTasksCount(); + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + java.util.List + getTasksOrBuilderList(); + /** + *
+   * Array of tasks matching the specified criteria.
+   * 
+ * + * repeated .a2a.v1.Task tasks = 1; + */ + io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( + int index); + + /** + *
+   * Token for retrieving the next page of results.
+   * Empty string if no more results.
+   * 
+ * + * string next_page_token = 2; + * @return The nextPageToken. + */ + java.lang.String getNextPageToken(); + /** + *
+   * Token for retrieving the next page of results.
+   * Empty string if no more results.
+   * 
+ * + * string next_page_token = 2; + * @return The bytes for nextPageToken. + */ + com.google.protobuf.ByteString + getNextPageTokenBytes(); + + /** + *
+   * Total number of tasks available (before pagination).
+   * 
+ * + * int32 total_size = 3; + * @return The totalSize. + */ + int getTotalSize(); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Message.java b/spec-grpc/src/main/java/io/a2a/grpc/Message.java index b4b63ecb1..0e32af8e6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Message.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Message.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:Message]
  * Message is one unit of communication between client and server. It is
  * associated with a context and optionally a task. Since the server is
  * responsible for the context definition, it must always provide a context_id
@@ -42,9 +43,11 @@ private Message() {
     contextId_ = "";
     taskId_ = "";
     role_ = 0;
-    content_ = java.util.Collections.emptyList();
+    parts_ = java.util.Collections.emptyList();
     extensions_ =
         com.google.protobuf.LazyStringArrayList.emptyList();
+    referenceTaskIds_ =
+        com.google.protobuf.LazyStringArrayList.emptyList();
   }
 
   public static final com.google.protobuf.Descriptors.Descriptor
@@ -66,8 +69,8 @@ private Message() {
   private volatile java.lang.Object messageId_ = "";
   /**
    * 
-   * The message id of the message. This is required and created by the
-   * message creator.
+   * The unique identifier (e.g. UUID)of the message. This is required and
+   * created by the message creator.
    * 
* * string message_id = 1; @@ -88,8 +91,8 @@ public java.lang.String getMessageId() { } /** *
-   * The message id of the message. This is required and created by the
-   * message creator.
+   * The unique identifier (e.g. UUID)of the message. This is required and
+   * created by the message creator.
    * 
* * string message_id = 1; @@ -234,70 +237,70 @@ public java.lang.String getTaskId() { return result == null ? io.a2a.grpc.Role.UNRECOGNIZED : result; } - public static final int CONTENT_FIELD_NUMBER = 5; + public static final int PARTS_FIELD_NUMBER = 5; @SuppressWarnings("serial") - private java.util.List content_; + private java.util.List parts_; /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override - public java.util.List getContentList() { - return content_; + public java.util.List getPartsList() { + return parts_; } /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override public java.util.List - getContentOrBuilderList() { - return content_; + getPartsOrBuilderList() { + return parts_; } /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override - public int getContentCount() { - return content_.size(); + public int getPartsCount() { + return parts_.size(); } /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override - public io.a2a.grpc.Part getContent(int index) { - return content_.get(index); + public io.a2a.grpc.Part getParts(int index) { + return parts_.get(index); } /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ @java.lang.Override - public io.a2a.grpc.PartOrBuilder getContentOrBuilder( + public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index) { - return content_.get(index); + return parts_.get(index); } public static final int METADATA_FIELD_NUMBER = 6; @@ -394,6 +397,59 @@ public java.lang.String getExtensions(int index) { return extensions_.getByteString(index); } + public static final int REFERENCE_TASK_IDS_FIELD_NUMBER = 8; + @SuppressWarnings("serial") + private com.google.protobuf.LazyStringArrayList referenceTaskIds_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @return A list containing the referenceTaskIds. + */ + public com.google.protobuf.ProtocolStringList + getReferenceTaskIdsList() { + return referenceTaskIds_; + } + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @return The count of referenceTaskIds. + */ + public int getReferenceTaskIdsCount() { + return referenceTaskIds_.size(); + } + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index of the element to return. + * @return The referenceTaskIds at the given index. + */ + public java.lang.String getReferenceTaskIds(int index) { + return referenceTaskIds_.get(index); + } + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index of the value to return. + * @return The bytes of the referenceTaskIds at the given index. + */ + public com.google.protobuf.ByteString + getReferenceTaskIdsBytes(int index) { + return referenceTaskIds_.getByteString(index); + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -420,8 +476,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (role_ != io.a2a.grpc.Role.ROLE_UNSPECIFIED.getNumber()) { output.writeEnum(4, role_); } - for (int i = 0; i < content_.size(); i++) { - output.writeMessage(5, content_.get(i)); + for (int i = 0; i < parts_.size(); i++) { + output.writeMessage(5, parts_.get(i)); } if (((bitField0_ & 0x00000001) != 0)) { output.writeMessage(6, getMetadata()); @@ -429,6 +485,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < extensions_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 7, extensions_.getRaw(i)); } + for (int i = 0; i < referenceTaskIds_.size(); i++) { + com.google.protobuf.GeneratedMessage.writeString(output, 8, referenceTaskIds_.getRaw(i)); + } getUnknownFields().writeTo(output); } @@ -451,9 +510,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeEnumSize(4, role_); } - for (int i = 0; i < content_.size(); i++) { + for (int i = 0; i < parts_.size(); i++) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, content_.get(i)); + .computeMessageSize(5, parts_.get(i)); } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream @@ -467,6 +526,14 @@ public int getSerializedSize() { size += dataSize; size += 1 * getExtensionsList().size(); } + { + int dataSize = 0; + for (int i = 0; i < referenceTaskIds_.size(); i++) { + dataSize += computeStringSizeNoTag(referenceTaskIds_.getRaw(i)); + } + size += dataSize; + size += 1 * getReferenceTaskIdsList().size(); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -489,8 +556,8 @@ public boolean equals(final java.lang.Object obj) { if (!getTaskId() .equals(other.getTaskId())) return false; if (role_ != other.role_) return false; - if (!getContentList() - .equals(other.getContentList())) return false; + if (!getPartsList() + .equals(other.getPartsList())) return false; if (hasMetadata() != other.hasMetadata()) return false; if (hasMetadata()) { if (!getMetadata() @@ -498,6 +565,8 @@ public boolean equals(final java.lang.Object obj) { } if (!getExtensionsList() .equals(other.getExtensionsList())) return false; + if (!getReferenceTaskIdsList() + .equals(other.getReferenceTaskIdsList())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -517,9 +586,9 @@ public int hashCode() { hash = (53 * hash) + getTaskId().hashCode(); hash = (37 * hash) + ROLE_FIELD_NUMBER; hash = (53 * hash) + role_; - if (getContentCount() > 0) { - hash = (37 * hash) + CONTENT_FIELD_NUMBER; - hash = (53 * hash) + getContentList().hashCode(); + if (getPartsCount() > 0) { + hash = (37 * hash) + PARTS_FIELD_NUMBER; + hash = (53 * hash) + getPartsList().hashCode(); } if (hasMetadata()) { hash = (37 * hash) + METADATA_FIELD_NUMBER; @@ -529,6 +598,10 @@ public int hashCode() { hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER; hash = (53 * hash) + getExtensionsList().hashCode(); } + if (getReferenceTaskIdsCount() > 0) { + hash = (37 * hash) + REFERENCE_TASK_IDS_FIELD_NUMBER; + hash = (53 * hash) + getReferenceTaskIdsList().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -628,6 +701,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:Message]
    * Message is one unit of communication between client and server. It is
    * associated with a context and optionally a task. Since the server is
    * responsible for the context definition, it must always provide a context_id
@@ -669,7 +743,7 @@ private Builder(
     private void maybeForceBuilderInitialization() {
       if (com.google.protobuf.GeneratedMessage
               .alwaysUseFieldBuilders) {
-        internalGetContentFieldBuilder();
+        internalGetPartsFieldBuilder();
         internalGetMetadataFieldBuilder();
       }
     }
@@ -681,11 +755,11 @@ public Builder clear() {
       contextId_ = "";
       taskId_ = "";
       role_ = 0;
-      if (contentBuilder_ == null) {
-        content_ = java.util.Collections.emptyList();
+      if (partsBuilder_ == null) {
+        parts_ = java.util.Collections.emptyList();
       } else {
-        content_ = null;
-        contentBuilder_.clear();
+        parts_ = null;
+        partsBuilder_.clear();
       }
       bitField0_ = (bitField0_ & ~0x00000010);
       metadata_ = null;
@@ -695,6 +769,8 @@ public Builder clear() {
       }
       extensions_ =
           com.google.protobuf.LazyStringArrayList.emptyList();
+      referenceTaskIds_ =
+          com.google.protobuf.LazyStringArrayList.emptyList();
       return this;
     }
 
@@ -728,14 +804,14 @@ public io.a2a.grpc.Message buildPartial() {
     }
 
     private void buildPartialRepeatedFields(io.a2a.grpc.Message result) {
-      if (contentBuilder_ == null) {
+      if (partsBuilder_ == null) {
         if (((bitField0_ & 0x00000010) != 0)) {
-          content_ = java.util.Collections.unmodifiableList(content_);
+          parts_ = java.util.Collections.unmodifiableList(parts_);
           bitField0_ = (bitField0_ & ~0x00000010);
         }
-        result.content_ = content_;
+        result.parts_ = parts_;
       } else {
-        result.content_ = contentBuilder_.build();
+        result.parts_ = partsBuilder_.build();
       }
     }
 
@@ -764,6 +840,10 @@ private void buildPartial0(io.a2a.grpc.Message result) {
         extensions_.makeImmutable();
         result.extensions_ = extensions_;
       }
+      if (((from_bitField0_ & 0x00000080) != 0)) {
+        referenceTaskIds_.makeImmutable();
+        result.referenceTaskIds_ = referenceTaskIds_;
+      }
       result.bitField0_ |= to_bitField0_;
     }
 
@@ -797,29 +877,29 @@ public Builder mergeFrom(io.a2a.grpc.Message other) {
       if (other.role_ != 0) {
         setRoleValue(other.getRoleValue());
       }
-      if (contentBuilder_ == null) {
-        if (!other.content_.isEmpty()) {
-          if (content_.isEmpty()) {
-            content_ = other.content_;
+      if (partsBuilder_ == null) {
+        if (!other.parts_.isEmpty()) {
+          if (parts_.isEmpty()) {
+            parts_ = other.parts_;
             bitField0_ = (bitField0_ & ~0x00000010);
           } else {
-            ensureContentIsMutable();
-            content_.addAll(other.content_);
+            ensurePartsIsMutable();
+            parts_.addAll(other.parts_);
           }
           onChanged();
         }
       } else {
-        if (!other.content_.isEmpty()) {
-          if (contentBuilder_.isEmpty()) {
-            contentBuilder_.dispose();
-            contentBuilder_ = null;
-            content_ = other.content_;
+        if (!other.parts_.isEmpty()) {
+          if (partsBuilder_.isEmpty()) {
+            partsBuilder_.dispose();
+            partsBuilder_ = null;
+            parts_ = other.parts_;
             bitField0_ = (bitField0_ & ~0x00000010);
-            contentBuilder_ = 
+            partsBuilder_ = 
               com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
-                 internalGetContentFieldBuilder() : null;
+                 internalGetPartsFieldBuilder() : null;
           } else {
-            contentBuilder_.addAllMessages(other.content_);
+            partsBuilder_.addAllMessages(other.parts_);
           }
         }
       }
@@ -836,6 +916,16 @@ public Builder mergeFrom(io.a2a.grpc.Message other) {
         }
         onChanged();
       }
+      if (!other.referenceTaskIds_.isEmpty()) {
+        if (referenceTaskIds_.isEmpty()) {
+          referenceTaskIds_ = other.referenceTaskIds_;
+          bitField0_ |= 0x00000080;
+        } else {
+          ensureReferenceTaskIdsIsMutable();
+          referenceTaskIds_.addAll(other.referenceTaskIds_);
+        }
+        onChanged();
+      }
       this.mergeUnknownFields(other.getUnknownFields());
       onChanged();
       return this;
@@ -887,11 +977,11 @@ public Builder mergeFrom(
                   input.readMessage(
                       io.a2a.grpc.Part.parser(),
                       extensionRegistry);
-              if (contentBuilder_ == null) {
-                ensureContentIsMutable();
-                content_.add(m);
+              if (partsBuilder_ == null) {
+                ensurePartsIsMutable();
+                parts_.add(m);
               } else {
-                contentBuilder_.addMessage(m);
+                partsBuilder_.addMessage(m);
               }
               break;
             } // case 42
@@ -908,6 +998,12 @@ public Builder mergeFrom(
               extensions_.add(s);
               break;
             } // case 58
+            case 66: {
+              java.lang.String s = input.readStringRequireUtf8();
+              ensureReferenceTaskIdsIsMutable();
+              referenceTaskIds_.add(s);
+              break;
+            } // case 66
             default: {
               if (!super.parseUnknownField(input, extensionRegistry, tag)) {
                 done = true; // was an endgroup tag
@@ -928,8 +1024,8 @@ public Builder mergeFrom(
     private java.lang.Object messageId_ = "";
     /**
      * 
-     * The message id of the message. This is required and created by the
-     * message creator.
+     * The unique identifier (e.g. UUID)of the message. This is required and
+     * created by the message creator.
      * 
* * string message_id = 1; @@ -949,8 +1045,8 @@ public java.lang.String getMessageId() { } /** *
-     * The message id of the message. This is required and created by the
-     * message creator.
+     * The unique identifier (e.g. UUID)of the message. This is required and
+     * created by the message creator.
      * 
* * string message_id = 1; @@ -971,8 +1067,8 @@ public java.lang.String getMessageId() { } /** *
-     * The message id of the message. This is required and created by the
-     * message creator.
+     * The unique identifier (e.g. UUID)of the message. This is required and
+     * created by the message creator.
      * 
* * string message_id = 1; @@ -989,8 +1085,8 @@ public Builder setMessageId( } /** *
-     * The message id of the message. This is required and created by the
-     * message creator.
+     * The unique identifier (e.g. UUID)of the message. This is required and
+     * created by the message creator.
      * 
* * string message_id = 1; @@ -1004,8 +1100,8 @@ public Builder clearMessageId() { } /** *
-     * The message id of the message. This is required and created by the
-     * message creator.
+     * The unique identifier (e.g. UUID)of the message. This is required and
+     * created by the message creator.
      * 
* * string message_id = 1; @@ -1287,334 +1383,334 @@ public Builder clearRole() { return this; } - private java.util.List content_ = + private java.util.List parts_ = java.util.Collections.emptyList(); - private void ensureContentIsMutable() { + private void ensurePartsIsMutable() { if (!((bitField0_ & 0x00000010) != 0)) { - content_ = new java.util.ArrayList(content_); + parts_ = new java.util.ArrayList(parts_); bitField0_ |= 0x00000010; } } private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> contentBuilder_; + io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> partsBuilder_; /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public java.util.List getContentList() { - if (contentBuilder_ == null) { - return java.util.Collections.unmodifiableList(content_); + public java.util.List getPartsList() { + if (partsBuilder_ == null) { + return java.util.Collections.unmodifiableList(parts_); } else { - return contentBuilder_.getMessageList(); + return partsBuilder_.getMessageList(); } } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public int getContentCount() { - if (contentBuilder_ == null) { - return content_.size(); + public int getPartsCount() { + if (partsBuilder_ == null) { + return parts_.size(); } else { - return contentBuilder_.getCount(); + return partsBuilder_.getCount(); } } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public io.a2a.grpc.Part getContent(int index) { - if (contentBuilder_ == null) { - return content_.get(index); + public io.a2a.grpc.Part getParts(int index) { + if (partsBuilder_ == null) { + return parts_.get(index); } else { - return contentBuilder_.getMessage(index); + return partsBuilder_.getMessage(index); } } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder setContent( + public Builder setParts( int index, io.a2a.grpc.Part value) { - if (contentBuilder_ == null) { + if (partsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureContentIsMutable(); - content_.set(index, value); + ensurePartsIsMutable(); + parts_.set(index, value); onChanged(); } else { - contentBuilder_.setMessage(index, value); + partsBuilder_.setMessage(index, value); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder setContent( + public Builder setParts( int index, io.a2a.grpc.Part.Builder builderForValue) { - if (contentBuilder_ == null) { - ensureContentIsMutable(); - content_.set(index, builderForValue.build()); + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.set(index, builderForValue.build()); onChanged(); } else { - contentBuilder_.setMessage(index, builderForValue.build()); + partsBuilder_.setMessage(index, builderForValue.build()); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder addContent(io.a2a.grpc.Part value) { - if (contentBuilder_ == null) { + public Builder addParts(io.a2a.grpc.Part value) { + if (partsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureContentIsMutable(); - content_.add(value); + ensurePartsIsMutable(); + parts_.add(value); onChanged(); } else { - contentBuilder_.addMessage(value); + partsBuilder_.addMessage(value); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder addContent( + public Builder addParts( int index, io.a2a.grpc.Part value) { - if (contentBuilder_ == null) { + if (partsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureContentIsMutable(); - content_.add(index, value); + ensurePartsIsMutable(); + parts_.add(index, value); onChanged(); } else { - contentBuilder_.addMessage(index, value); + partsBuilder_.addMessage(index, value); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder addContent( + public Builder addParts( io.a2a.grpc.Part.Builder builderForValue) { - if (contentBuilder_ == null) { - ensureContentIsMutable(); - content_.add(builderForValue.build()); + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.add(builderForValue.build()); onChanged(); } else { - contentBuilder_.addMessage(builderForValue.build()); + partsBuilder_.addMessage(builderForValue.build()); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder addContent( + public Builder addParts( int index, io.a2a.grpc.Part.Builder builderForValue) { - if (contentBuilder_ == null) { - ensureContentIsMutable(); - content_.add(index, builderForValue.build()); + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.add(index, builderForValue.build()); onChanged(); } else { - contentBuilder_.addMessage(index, builderForValue.build()); + partsBuilder_.addMessage(index, builderForValue.build()); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder addAllContent( + public Builder addAllParts( java.lang.Iterable values) { - if (contentBuilder_ == null) { - ensureContentIsMutable(); + if (partsBuilder_ == null) { + ensurePartsIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, content_); + values, parts_); onChanged(); } else { - contentBuilder_.addAllMessages(values); + partsBuilder_.addAllMessages(values); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder clearContent() { - if (contentBuilder_ == null) { - content_ = java.util.Collections.emptyList(); + public Builder clearParts() { + if (partsBuilder_ == null) { + parts_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000010); onChanged(); } else { - contentBuilder_.clear(); + partsBuilder_.clear(); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public Builder removeContent(int index) { - if (contentBuilder_ == null) { - ensureContentIsMutable(); - content_.remove(index); + public Builder removeParts(int index) { + if (partsBuilder_ == null) { + ensurePartsIsMutable(); + parts_.remove(index); onChanged(); } else { - contentBuilder_.remove(index); + partsBuilder_.remove(index); } return this; } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public io.a2a.grpc.Part.Builder getContentBuilder( + public io.a2a.grpc.Part.Builder getPartsBuilder( int index) { - return internalGetContentFieldBuilder().getBuilder(index); + return internalGetPartsFieldBuilder().getBuilder(index); } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public io.a2a.grpc.PartOrBuilder getContentOrBuilder( + public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index) { - if (contentBuilder_ == null) { - return content_.get(index); } else { - return contentBuilder_.getMessageOrBuilder(index); + if (partsBuilder_ == null) { + return parts_.get(index); } else { + return partsBuilder_.getMessageOrBuilder(index); } } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ public java.util.List - getContentOrBuilderList() { - if (contentBuilder_ != null) { - return contentBuilder_.getMessageOrBuilderList(); + getPartsOrBuilderList() { + if (partsBuilder_ != null) { + return partsBuilder_.getMessageOrBuilderList(); } else { - return java.util.Collections.unmodifiableList(content_); + return java.util.Collections.unmodifiableList(parts_); } } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public io.a2a.grpc.Part.Builder addContentBuilder() { - return internalGetContentFieldBuilder().addBuilder( + public io.a2a.grpc.Part.Builder addPartsBuilder() { + return internalGetPartsFieldBuilder().addBuilder( io.a2a.grpc.Part.getDefaultInstance()); } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - public io.a2a.grpc.Part.Builder addContentBuilder( + public io.a2a.grpc.Part.Builder addPartsBuilder( int index) { - return internalGetContentFieldBuilder().addBuilder( + return internalGetPartsFieldBuilder().addBuilder( index, io.a2a.grpc.Part.getDefaultInstance()); } /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Content is the container of the message content.
+     * Parts is the container of the message content.
      * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ public java.util.List - getContentBuilderList() { - return internalGetContentFieldBuilder().getBuilderList(); + getPartsBuilderList() { + return internalGetPartsFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder> - internalGetContentFieldBuilder() { - if (contentBuilder_ == null) { - contentBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + internalGetPartsFieldBuilder() { + if (partsBuilder_ == null) { + partsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.Part, io.a2a.grpc.Part.Builder, io.a2a.grpc.PartOrBuilder>( - content_, + parts_, ((bitField0_ & 0x00000010) != 0), getParentForChildren(), isClean()); - content_ = null; + parts_ = null; } - return contentBuilder_; + return partsBuilder_; } private com.google.protobuf.Struct metadata_; @@ -1930,6 +2026,153 @@ public Builder addExtensionsBytes( return this; } + private com.google.protobuf.LazyStringArrayList referenceTaskIds_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + private void ensureReferenceTaskIdsIsMutable() { + if (!referenceTaskIds_.isModifiable()) { + referenceTaskIds_ = new com.google.protobuf.LazyStringArrayList(referenceTaskIds_); + } + bitField0_ |= 0x00000080; + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @return A list containing the referenceTaskIds. + */ + public com.google.protobuf.ProtocolStringList + getReferenceTaskIdsList() { + referenceTaskIds_.makeImmutable(); + return referenceTaskIds_; + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @return The count of referenceTaskIds. + */ + public int getReferenceTaskIdsCount() { + return referenceTaskIds_.size(); + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index of the element to return. + * @return The referenceTaskIds at the given index. + */ + public java.lang.String getReferenceTaskIds(int index) { + return referenceTaskIds_.get(index); + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index of the value to return. + * @return The bytes of the referenceTaskIds at the given index. + */ + public com.google.protobuf.ByteString + getReferenceTaskIdsBytes(int index) { + return referenceTaskIds_.getByteString(index); + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index to set the value at. + * @param value The referenceTaskIds to set. + * @return This builder for chaining. + */ + public Builder setReferenceTaskIds( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureReferenceTaskIdsIsMutable(); + referenceTaskIds_.set(index, value); + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @param value The referenceTaskIds to add. + * @return This builder for chaining. + */ + public Builder addReferenceTaskIds( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureReferenceTaskIdsIsMutable(); + referenceTaskIds_.add(value); + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @param values The referenceTaskIds to add. + * @return This builder for chaining. + */ + public Builder addAllReferenceTaskIds( + java.lang.Iterable values) { + ensureReferenceTaskIdsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, referenceTaskIds_); + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @return This builder for chaining. + */ + public Builder clearReferenceTaskIds() { + referenceTaskIds_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000080);; + onChanged(); + return this; + } + /** + *
+     * A list of task IDs that this message references for additional context.
+     * 
+ * + * repeated string reference_task_ids = 8; + * @param value The bytes of the referenceTaskIds to add. + * @return This builder for chaining. + */ + public Builder addReferenceTaskIdsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + ensureReferenceTaskIdsIsMutable(); + referenceTaskIds_.add(value); + bitField0_ |= 0x00000080; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.Message) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java index 81727886b..7e8605297 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java @@ -12,8 +12,8 @@ public interface MessageOrBuilder extends /** *
-   * The message id of the message. This is required and created by the
-   * message creator.
+   * The unique identifier (e.g. UUID)of the message. This is required and
+   * created by the message creator.
    * 
* * string message_id = 1; @@ -22,8 +22,8 @@ public interface MessageOrBuilder extends java.lang.String getMessageId(); /** *
-   * The message id of the message. This is required and created by the
-   * message creator.
+   * The unique identifier (e.g. UUID)of the message. This is required and
+   * created by the message creator.
    * 
* * string message_id = 1; @@ -98,50 +98,50 @@ public interface MessageOrBuilder extends /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ java.util.List - getContentList(); + getPartsList(); /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - io.a2a.grpc.Part getContent(int index); + io.a2a.grpc.Part getParts(int index); /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - int getContentCount(); + int getPartsCount(); /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ java.util.List - getContentOrBuilderList(); + getPartsOrBuilderList(); /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Content is the container of the message content.
+   * Parts is the container of the message content.
    * 
* - * repeated .a2a.v1.Part content = 5; + * repeated .a2a.v1.Part parts = 5; */ - io.a2a.grpc.PartOrBuilder getContentOrBuilder( + io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index); /** @@ -214,4 +214,45 @@ io.a2a.grpc.PartOrBuilder getContentOrBuilder( */ com.google.protobuf.ByteString getExtensionsBytes(int index); + + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @return A list containing the referenceTaskIds. + */ + java.util.List + getReferenceTaskIdsList(); + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @return The count of referenceTaskIds. + */ + int getReferenceTaskIdsCount(); + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index of the element to return. + * @return The referenceTaskIds at the given index. + */ + java.lang.String getReferenceTaskIds(int index); + /** + *
+   * A list of task IDs that this message references for additional context.
+   * 
+ * + * repeated string reference_task_ids = 8; + * @param index The index of the value to return. + * @return The bytes of the referenceTaskIds at the given index. + */ + com.google.protobuf.ByteString + getReferenceTaskIdsBytes(int index); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java index 87ef07b99..9e4cf1b9c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:MutualTLSSecurityScheme]
+ * 
+ * * Protobuf type {@code a2a.v1.MutualTlsSecurityScheme} */ @com.google.protobuf.Generated @@ -248,6 +252,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:MutualTLSSecurityScheme]
+   * 
+ * * Protobuf type {@code a2a.v1.MutualTlsSecurityScheme} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java index 9de35587e..2dde8973b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:OAuth2SecurityScheme]
+ * 
+ * * Protobuf type {@code a2a.v1.OAuth2SecurityScheme} */ @com.google.protobuf.Generated @@ -363,6 +367,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:OAuth2SecurityScheme]
+   * 
+ * * Protobuf type {@code a2a.v1.OAuth2SecurityScheme} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java index 4da07493d..c2c2de63f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:OAuthFlows]
+ * 
+ * * Protobuf type {@code a2a.v1.OAuthFlows} */ @com.google.protobuf.Generated @@ -429,6 +433,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:OAuthFlows]
+   * 
+ * * Protobuf type {@code a2a.v1.OAuthFlows} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java index c2f01d139..4f2db6d61 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:OpenIdConnectSecurityScheme]
+ * 
+ * * Protobuf type {@code a2a.v1.OpenIdConnectSecurityScheme} */ @com.google.protobuf.Generated @@ -308,6 +312,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:OpenIdConnectSecurityScheme]
+   * 
+ * * Protobuf type {@code a2a.v1.OpenIdConnectSecurityScheme} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Part.java b/spec-grpc/src/main/java/io/a2a/grpc/Part.java index dbb3015a3..3eee12040 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Part.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Part.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:Part]
  * Part represents a container for a section of communication content.
  * Parts can be purely textual, some sort of file (image, video, etc) or
  * a structured data blob (i.e. JSON).
@@ -49,6 +50,7 @@ private Part() {
             io.a2a.grpc.Part.class, io.a2a.grpc.Part.Builder.class);
   }
 
+  private int bitField0_;
   private int partCase_ = 0;
   @SuppressWarnings("serial")
   private java.lang.Object part_;
@@ -207,6 +209,44 @@ public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() {
     return io.a2a.grpc.DataPart.getDefaultInstance();
   }
 
+  public static final int METADATA_FIELD_NUMBER = 4;
+  private com.google.protobuf.Struct metadata_;
+  /**
+   * 
+   * Optional metadata associated with this part.
+   * 
+ * + * .google.protobuf.Struct metadata = 4; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Optional metadata associated with this part.
+   * 
+ * + * .google.protobuf.Struct metadata = 4; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * Optional metadata associated with this part.
+   * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -230,6 +270,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (partCase_ == 3) { output.writeMessage(3, (io.a2a.grpc.DataPart) part_); } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(4, getMetadata()); + } getUnknownFields().writeTo(output); } @@ -250,6 +293,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, (io.a2a.grpc.DataPart) part_); } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(4, getMetadata()); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -265,6 +312,11 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.Part other = (io.a2a.grpc.Part) obj; + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } if (!getPartCase().equals(other.getPartCase())) return false; switch (partCase_) { case 1: @@ -293,6 +345,10 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } switch (partCase_) { case 1: hash = (37 * hash) + TEXT_FIELD_NUMBER; @@ -408,6 +464,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:Part]
    * Part represents a container for a section of communication content.
    * Parts can be purely textual, some sort of file (image, video, etc) or
    * a structured data blob (i.e. JSON).
@@ -434,13 +491,19 @@ public static final class Builder extends
 
     // Construct using io.a2a.grpc.Part.newBuilder()
     private Builder() {
-
+      maybeForceBuilderInitialization();
     }
 
     private Builder(
         com.google.protobuf.GeneratedMessage.BuilderParent parent) {
       super(parent);
-
+      maybeForceBuilderInitialization();
+    }
+    private void maybeForceBuilderInitialization() {
+      if (com.google.protobuf.GeneratedMessage
+              .alwaysUseFieldBuilders) {
+        internalGetMetadataFieldBuilder();
+      }
     }
     @java.lang.Override
     public Builder clear() {
@@ -452,6 +515,11 @@ public Builder clear() {
       if (dataBuilder_ != null) {
         dataBuilder_.clear();
       }
+      metadata_ = null;
+      if (metadataBuilder_ != null) {
+        metadataBuilder_.dispose();
+        metadataBuilder_ = null;
+      }
       partCase_ = 0;
       part_ = null;
       return this;
@@ -488,6 +556,14 @@ public io.a2a.grpc.Part buildPartial() {
 
     private void buildPartial0(io.a2a.grpc.Part result) {
       int from_bitField0_ = bitField0_;
+      int to_bitField0_ = 0;
+      if (((from_bitField0_ & 0x00000008) != 0)) {
+        result.metadata_ = metadataBuilder_ == null
+            ? metadata_
+            : metadataBuilder_.build();
+        to_bitField0_ |= 0x00000001;
+      }
+      result.bitField0_ |= to_bitField0_;
     }
 
     private void buildPartialOneofs(io.a2a.grpc.Part result) {
@@ -515,6 +591,9 @@ public Builder mergeFrom(com.google.protobuf.Message other) {
 
     public Builder mergeFrom(io.a2a.grpc.Part other) {
       if (other == io.a2a.grpc.Part.getDefaultInstance()) return this;
+      if (other.hasMetadata()) {
+        mergeMetadata(other.getMetadata());
+      }
       switch (other.getPartCase()) {
         case TEXT: {
           partCase_ = 1;
@@ -580,6 +659,13 @@ public Builder mergeFrom(
               partCase_ = 3;
               break;
             } // case 26
+            case 34: {
+              input.readMessage(
+                  internalGetMetadataFieldBuilder().getBuilder(),
+                  extensionRegistry);
+              bitField0_ |= 0x00000008;
+              break;
+            } // case 34
             default: {
               if (!super.parseUnknownField(input, extensionRegistry, tag)) {
                 done = true; // was an endgroup tag
@@ -989,6 +1075,163 @@ public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() {
       return dataBuilder_;
     }
 
+    private com.google.protobuf.Struct metadata_;
+    private com.google.protobuf.SingleFieldBuilder<
+        com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_;
+    /**
+     * 
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } else { + return metadataBuilder_.getMessage(); + } + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } + } else { + metadataBuilder_.mergeFrom(value); + } + if (metadata_ != null) { + bitField0_ |= 0x00000008; + onChanged(); + } + return this; + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000008); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } + onChanged(); + return this; + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000008; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + } + /** + *
+     * Optional metadata associated with this part.
+     * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.Part) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java index 773988036..efcce09b6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java @@ -57,5 +57,32 @@ public interface PartOrBuilder extends */ io.a2a.grpc.DataPartOrBuilder getDataOrBuilder(); + /** + *
+   * Optional metadata associated with this part.
+   * 
+ * + * .google.protobuf.Struct metadata = 4; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * Optional metadata associated with this part.
+   * 
+ * + * .google.protobuf.Struct metadata = 4; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * Optional metadata associated with this part.
+   * 
+ * + * .google.protobuf.Struct metadata = 4; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); + io.a2a.grpc.Part.PartCase getPartCase(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java index c9c1c4b0a..b2d628b53 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:PasswordOAuthFlow]
+ * 
+ * * Protobuf type {@code a2a.v1.PasswordOAuthFlow} */ @com.google.protobuf.Generated @@ -443,6 +447,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:PasswordOAuthFlow]
+   * 
+ * * Protobuf type {@code a2a.v1.PasswordOAuthFlow} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java index 5942397ba..628454eb5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:PushNotificationConfig]
  * Configuration for setting up push notifications for task updates.
  * 
* @@ -56,7 +57,7 @@ private PushNotificationConfig() { private volatile java.lang.Object id_ = ""; /** *
-   * A unique id for this push notification.
+   * A unique identifier (e.g. UUID) for this push notification.
    * 
* * string id = 1; @@ -77,7 +78,7 @@ public java.lang.String getId() { } /** *
-   * A unique id for this push notification.
+   * A unique identifier (e.g. UUID) for this push notification.
    * 
* * string id = 1; @@ -424,6 +425,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:PushNotificationConfig]
    * Configuration for setting up push notifications for task updates.
    * 
* @@ -624,7 +626,7 @@ public Builder mergeFrom( private java.lang.Object id_ = ""; /** *
-     * A unique id for this push notification.
+     * A unique identifier (e.g. UUID) for this push notification.
      * 
* * string id = 1; @@ -644,7 +646,7 @@ public java.lang.String getId() { } /** *
-     * A unique id for this push notification.
+     * A unique identifier (e.g. UUID) for this push notification.
      * 
* * string id = 1; @@ -665,7 +667,7 @@ public java.lang.String getId() { } /** *
-     * A unique id for this push notification.
+     * A unique identifier (e.g. UUID) for this push notification.
      * 
* * string id = 1; @@ -682,7 +684,7 @@ public Builder setId( } /** *
-     * A unique id for this push notification.
+     * A unique identifier (e.g. UUID) for this push notification.
      * 
* * string id = 1; @@ -696,7 +698,7 @@ public Builder clearId() { } /** *
-     * A unique id for this push notification.
+     * A unique identifier (e.g. UUID) for this push notification.
      * 
* * string id = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java index a04532954..287e48afc 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java @@ -12,7 +12,7 @@ public interface PushNotificationConfigOrBuilder extends /** *
-   * A unique id for this push notification.
+   * A unique identifier (e.g. UUID) for this push notification.
    * 
* * string id = 1; @@ -21,7 +21,7 @@ public interface PushNotificationConfigOrBuilder extends java.lang.String getId(); /** *
-   * A unique id for this push notification.
+   * A unique identifier (e.g. UUID) for this push notification.
    * 
* * string id = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java index 90bc8672d..7b2b30ec2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:SecurityScheme]
+ * 
+ * * Protobuf type {@code a2a.v1.SecurityScheme} */ @com.google.protobuf.Generated @@ -477,6 +481,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:SecurityScheme]
+   * 
+ * * Protobuf type {@code a2a.v1.SecurityScheme} */ public static final class Builder extends diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index 75db4a2b8..d7aee9a07 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:MessageSendConfiguration]
  * Configuration of a send message request.
  * 
* @@ -379,6 +380,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:MessageSendConfiguration]
    * Configuration of a send message request.
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index 8882b6d6a..4f306283e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -8,6 +8,7 @@ /** *
  * /////////// Request Messages ///////////
+ * --8<-- [start:MessageSendParams]
  * 
* * Protobuf type {@code a2a.v1.SendMessageRequest} @@ -51,6 +52,10 @@ private SendMessageRequest() { public static final int REQUEST_FIELD_NUMBER = 1; private io.a2a.grpc.Message request_; /** + *
+   * The message to send to the agent.
+   * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ @@ -59,6 +64,10 @@ public boolean hasRequest() { return ((bitField0_ & 0x00000001) != 0); } /** + *
+   * The message to send to the agent.
+   * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return The request. */ @@ -67,6 +76,10 @@ public io.a2a.grpc.Message getRequest() { return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; } /** + *
+   * The message to send to the agent.
+   * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override @@ -77,6 +90,10 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { public static final int CONFIGURATION_FIELD_NUMBER = 2; private io.a2a.grpc.SendMessageConfiguration configuration_; /** + *
+   * Configuration for the send request.
+   * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; * @return Whether the configuration field is set. */ @@ -85,6 +102,10 @@ public boolean hasConfiguration() { return ((bitField0_ & 0x00000002) != 0); } /** + *
+   * Configuration for the send request.
+   * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; * @return The configuration. */ @@ -93,6 +114,10 @@ public io.a2a.grpc.SendMessageConfiguration getConfiguration() { return configuration_ == null ? io.a2a.grpc.SendMessageConfiguration.getDefaultInstance() : configuration_; } /** + *
+   * Configuration for the send request.
+   * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ @java.lang.Override @@ -103,6 +128,10 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() public static final int METADATA_FIELD_NUMBER = 3; private com.google.protobuf.Struct metadata_; /** + *
+   * Optional metadata for the request.
+   * 
+ * * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ @@ -111,6 +140,10 @@ public boolean hasMetadata() { return ((bitField0_ & 0x00000004) != 0); } /** + *
+   * Optional metadata for the request.
+   * 
+ * * .google.protobuf.Struct metadata = 3; * @return The metadata. */ @@ -119,6 +152,10 @@ public com.google.protobuf.Struct getMetadata() { return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; } /** + *
+   * Optional metadata for the request.
+   * 
+ * * .google.protobuf.Struct metadata = 3; */ @java.lang.Override @@ -323,6 +360,7 @@ protected Builder newBuilderForType( /** *
    * /////////// Request Messages ///////////
+   * --8<-- [start:MessageSendParams]
    * 
* * Protobuf type {@code a2a.v1.SendMessageRequest} @@ -525,6 +563,10 @@ public Builder mergeFrom( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> requestBuilder_; /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ @@ -532,6 +574,10 @@ public boolean hasRequest() { return ((bitField0_ & 0x00000001) != 0); } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return The request. */ @@ -543,6 +589,10 @@ public io.a2a.grpc.Message getRequest() { } } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder setRequest(io.a2a.grpc.Message value) { @@ -559,6 +609,10 @@ public Builder setRequest(io.a2a.grpc.Message value) { return this; } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder setRequest( @@ -573,6 +627,10 @@ public Builder setRequest( return this; } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder mergeRequest(io.a2a.grpc.Message value) { @@ -594,6 +652,10 @@ public Builder mergeRequest(io.a2a.grpc.Message value) { return this; } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public Builder clearRequest() { @@ -607,6 +669,10 @@ public Builder clearRequest() { return this; } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Message.Builder getRequestBuilder() { @@ -615,6 +681,10 @@ public io.a2a.grpc.Message.Builder getRequestBuilder() { return internalGetRequestFieldBuilder().getBuilder(); } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { @@ -626,6 +696,10 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { } } /** + *
+     * The message to send to the agent.
+     * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< @@ -646,6 +720,10 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.SendMessageConfiguration, io.a2a.grpc.SendMessageConfiguration.Builder, io.a2a.grpc.SendMessageConfigurationOrBuilder> configurationBuilder_; /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; * @return Whether the configuration field is set. */ @@ -653,6 +731,10 @@ public boolean hasConfiguration() { return ((bitField0_ & 0x00000002) != 0); } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; * @return The configuration. */ @@ -664,6 +746,10 @@ public io.a2a.grpc.SendMessageConfiguration getConfiguration() { } } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder setConfiguration(io.a2a.grpc.SendMessageConfiguration value) { @@ -680,6 +766,10 @@ public Builder setConfiguration(io.a2a.grpc.SendMessageConfiguration value) { return this; } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder setConfiguration( @@ -694,6 +784,10 @@ public Builder setConfiguration( return this; } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) { @@ -715,6 +809,10 @@ public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) { return this; } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ public Builder clearConfiguration() { @@ -728,6 +826,10 @@ public Builder clearConfiguration() { return this; } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ public io.a2a.grpc.SendMessageConfiguration.Builder getConfigurationBuilder() { @@ -736,6 +838,10 @@ public io.a2a.grpc.SendMessageConfiguration.Builder getConfigurationBuilder() { return internalGetConfigurationFieldBuilder().getBuilder(); } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() { @@ -747,6 +853,10 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() } } /** + *
+     * Configuration for the send request.
+     * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ private com.google.protobuf.SingleFieldBuilder< @@ -767,6 +877,10 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ @@ -774,6 +888,10 @@ public boolean hasMetadata() { return ((bitField0_ & 0x00000004) != 0); } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; * @return The metadata. */ @@ -785,6 +903,10 @@ public com.google.protobuf.Struct getMetadata() { } } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ public Builder setMetadata(com.google.protobuf.Struct value) { @@ -801,6 +923,10 @@ public Builder setMetadata(com.google.protobuf.Struct value) { return this; } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ public Builder setMetadata( @@ -815,6 +941,10 @@ public Builder setMetadata( return this; } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { @@ -836,6 +966,10 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { return this; } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ public Builder clearMetadata() { @@ -849,6 +983,10 @@ public Builder clearMetadata() { return this; } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { @@ -857,6 +995,10 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { return internalGetMetadataFieldBuilder().getBuilder(); } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { @@ -868,6 +1010,10 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { } } /** + *
+     * Optional metadata for the request.
+     * 
+ * * .google.protobuf.Struct metadata = 3; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java index 13dae6c0e..8edf78d41 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java @@ -11,46 +11,82 @@ public interface SendMessageRequestOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
+   * The message to send to the agent.
+   * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return Whether the request field is set. */ boolean hasRequest(); /** + *
+   * The message to send to the agent.
+   * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; * @return The request. */ io.a2a.grpc.Message getRequest(); /** + *
+   * The message to send to the agent.
+   * 
+ * * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.MessageOrBuilder getRequestOrBuilder(); /** + *
+   * Configuration for the send request.
+   * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; * @return Whether the configuration field is set. */ boolean hasConfiguration(); /** + *
+   * Configuration for the send request.
+   * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; * @return The configuration. */ io.a2a.grpc.SendMessageConfiguration getConfiguration(); /** + *
+   * Configuration for the send request.
+   * 
+ * * .a2a.v1.SendMessageConfiguration configuration = 2; */ io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder(); /** + *
+   * Optional metadata for the request.
+   * 
+ * * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ boolean hasMetadata(); /** + *
+   * Optional metadata for the request.
+   * 
+ * * .google.protobuf.Struct metadata = 3; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); /** + *
+   * Optional metadata for the request.
+   * 
+ * * .google.protobuf.Struct metadata = 3; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java index 365bbcda2..b2260e057 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java @@ -8,6 +8,7 @@ /** *
  * ////// Response Messages ///////////
+ * --8<-- [start:SendMessageSuccessResponse]
  * 
* * Protobuf type {@code a2a.v1.SendMessageResponse} @@ -339,6 +340,7 @@ protected Builder newBuilderForType( /** *
    * ////// Response Messages ///////////
+   * --8<-- [start:SendMessageSuccessResponse]
    * 
* * Protobuf type {@code a2a.v1.SendMessageResponse} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java index 97291a324..e995b6fa1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:SendStreamingMessageSuccessResponse]
  * The stream response for a message. The stream should be one of the following
  * sequences:
  * If the response is a message, the stream should contain one, and only one,
@@ -442,6 +443,7 @@ protected Builder newBuilderForType(
   }
   /**
    * 
+   * --8<-- [start:SendStreamingMessageSuccessResponse]
    * The stream response for a message. The stream should be one of the following
    * sequences:
    * If the response is a message, the stream should contain one, and only one,
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Task.java b/spec-grpc/src/main/java/io/a2a/grpc/Task.java
index 05a007f63..2e263161d 100644
--- a/spec-grpc/src/main/java/io/a2a/grpc/Task.java
+++ b/spec-grpc/src/main/java/io/a2a/grpc/Task.java
@@ -7,6 +7,7 @@
 
 /**
  * 
+ * --8<-- [start:Task]
  * Task is the core unit of action for A2A. It has a current status
  * and when results are created for the task they are stored in the
  * artifact. If there are multiple turns for a task, these are stored in
@@ -60,7 +61,8 @@ private Task() {
   private volatile java.lang.Object id_ = "";
   /**
    * 
-   * Unique identifier for a task, created by the A2A server.
+   * Unique identifier (e.g. UUID) for the task, generated by the server for a
+   * new task.
    * 
* * string id = 1; @@ -81,7 +83,8 @@ public java.lang.String getId() { } /** *
-   * Unique identifier for a task, created by the A2A server.
+   * Unique identifier (e.g. UUID) for the task, generated by the server for a
+   * new task.
    * 
* * string id = 1; @@ -107,8 +110,8 @@ public java.lang.String getId() { private volatile java.lang.Object contextId_ = ""; /** *
-   * Unique identifier for the contextual collection of interactions (tasks
-   * and messages). Created by the A2A server.
+   * Unique identifier (e.g. UUID) for the contextual collection of interactions
+   * (tasks and messages). Created by the A2A server.
    * 
* * string context_id = 2; @@ -129,8 +132,8 @@ public java.lang.String getContextId() { } /** *
-   * Unique identifier for the contextual collection of interactions (tasks
-   * and messages). Created by the A2A server.
+   * Unique identifier (e.g. UUID) for the contextual collection of interactions
+   * (tasks and messages). Created by the A2A server.
    * 
* * string context_id = 2; @@ -583,6 +586,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:Task]
    * Task is the core unit of action for A2A. It has a current status
    * and when results are created for the task they are stored in the
    * artifact. If there are multiple turns for a task, these are stored in
@@ -910,7 +914,8 @@ public Builder mergeFrom(
     private java.lang.Object id_ = "";
     /**
      * 
-     * Unique identifier for a task, created by the A2A server.
+     * Unique identifier (e.g. UUID) for the task, generated by the server for a
+     * new task.
      * 
* * string id = 1; @@ -930,7 +935,8 @@ public java.lang.String getId() { } /** *
-     * Unique identifier for a task, created by the A2A server.
+     * Unique identifier (e.g. UUID) for the task, generated by the server for a
+     * new task.
      * 
* * string id = 1; @@ -951,7 +957,8 @@ public java.lang.String getId() { } /** *
-     * Unique identifier for a task, created by the A2A server.
+     * Unique identifier (e.g. UUID) for the task, generated by the server for a
+     * new task.
      * 
* * string id = 1; @@ -968,7 +975,8 @@ public Builder setId( } /** *
-     * Unique identifier for a task, created by the A2A server.
+     * Unique identifier (e.g. UUID) for the task, generated by the server for a
+     * new task.
      * 
* * string id = 1; @@ -982,7 +990,8 @@ public Builder clearId() { } /** *
-     * Unique identifier for a task, created by the A2A server.
+     * Unique identifier (e.g. UUID) for the task, generated by the server for a
+     * new task.
      * 
* * string id = 1; @@ -1002,8 +1011,8 @@ public Builder setIdBytes( private java.lang.Object contextId_ = ""; /** *
-     * Unique identifier for the contextual collection of interactions (tasks
-     * and messages). Created by the A2A server.
+     * Unique identifier (e.g. UUID) for the contextual collection of interactions
+     * (tasks and messages). Created by the A2A server.
      * 
* * string context_id = 2; @@ -1023,8 +1032,8 @@ public java.lang.String getContextId() { } /** *
-     * Unique identifier for the contextual collection of interactions (tasks
-     * and messages). Created by the A2A server.
+     * Unique identifier (e.g. UUID) for the contextual collection of interactions
+     * (tasks and messages). Created by the A2A server.
      * 
* * string context_id = 2; @@ -1045,8 +1054,8 @@ public java.lang.String getContextId() { } /** *
-     * Unique identifier for the contextual collection of interactions (tasks
-     * and messages). Created by the A2A server.
+     * Unique identifier (e.g. UUID) for the contextual collection of interactions
+     * (tasks and messages). Created by the A2A server.
      * 
* * string context_id = 2; @@ -1063,8 +1072,8 @@ public Builder setContextId( } /** *
-     * Unique identifier for the contextual collection of interactions (tasks
-     * and messages). Created by the A2A server.
+     * Unique identifier (e.g. UUID) for the contextual collection of interactions
+     * (tasks and messages). Created by the A2A server.
      * 
* * string context_id = 2; @@ -1078,8 +1087,8 @@ public Builder clearContextId() { } /** *
-     * Unique identifier for the contextual collection of interactions (tasks
-     * and messages). Created by the A2A server.
+     * Unique identifier (e.g. UUID) for the contextual collection of interactions
+     * (tasks and messages). Created by the A2A server.
      * 
* * string context_id = 2; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java index 379413bb4..83d102dba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:TaskArtifactUpdateEvent]
  * TaskArtifactUpdateEvent represents a task delta where an artifact has
  * been generated.
  * 
@@ -475,6 +476,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:TaskArtifactUpdateEvent]
    * TaskArtifactUpdateEvent represents a task delta where an artifact has
    * been generated.
    * 
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java index 3bbe9e999..10bfd49ec 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java @@ -12,7 +12,8 @@ public interface TaskOrBuilder extends /** *
-   * Unique identifier for a task, created by the A2A server.
+   * Unique identifier (e.g. UUID) for the task, generated by the server for a
+   * new task.
    * 
* * string id = 1; @@ -21,7 +22,8 @@ public interface TaskOrBuilder extends java.lang.String getId(); /** *
-   * Unique identifier for a task, created by the A2A server.
+   * Unique identifier (e.g. UUID) for the task, generated by the server for a
+   * new task.
    * 
* * string id = 1; @@ -32,8 +34,8 @@ public interface TaskOrBuilder extends /** *
-   * Unique identifier for the contextual collection of interactions (tasks
-   * and messages). Created by the A2A server.
+   * Unique identifier (e.g. UUID) for the contextual collection of interactions
+   * (tasks and messages). Created by the A2A server.
    * 
* * string context_id = 2; @@ -42,8 +44,8 @@ public interface TaskOrBuilder extends java.lang.String getContextId(); /** *
-   * Unique identifier for the contextual collection of interactions (tasks
-   * and messages). Created by the A2A server.
+   * Unique identifier (e.g. UUID) for the contextual collection of interactions
+   * (tasks and messages). Created by the A2A server.
    * 
* * string context_id = 2; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java index b7dabc6e8..95a1b892d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:TaskPushNotificationConfig]
+ * 
+ * * Protobuf type {@code a2a.v1.TaskPushNotificationConfig} */ @com.google.protobuf.Generated @@ -50,7 +54,8 @@ private TaskPushNotificationConfig() { private volatile java.lang.Object name_ = ""; /** *
-   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * The resource name of the config.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -71,7 +76,8 @@ public java.lang.String getName() { } /** *
-   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * The resource name of the config.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -95,6 +101,10 @@ public java.lang.String getName() { public static final int PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER = 2; private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; /** + *
+   * The push notification configuration details.
+   * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return Whether the pushNotificationConfig field is set. */ @@ -103,6 +113,10 @@ public boolean hasPushNotificationConfig() { return ((bitField0_ & 0x00000001) != 0); } /** + *
+   * The push notification configuration details.
+   * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return The pushNotificationConfig. */ @@ -111,6 +125,10 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; } /** + *
+   * The push notification configuration details.
+   * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ @java.lang.Override @@ -291,6 +309,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:TaskPushNotificationConfig]
+   * 
+ * * Protobuf type {@code a2a.v1.TaskPushNotificationConfig} */ public static final class Builder extends @@ -460,7 +482,8 @@ public Builder mergeFrom( private java.lang.Object name_ = ""; /** *
-     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * The resource name of the config.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -480,7 +503,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * The resource name of the config.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -501,7 +525,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * The resource name of the config.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -518,7 +543,8 @@ public Builder setName( } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * The resource name of the config.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -532,7 +558,8 @@ public Builder clearName() { } /** *
-     * name=tasks/{id}/pushNotificationConfigs/{id}
+     * The resource name of the config.
+     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
      * 
* * string name = 1; @@ -553,6 +580,10 @@ public Builder setNameBytes( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationConfigBuilder_; /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return Whether the pushNotificationConfig field is set. */ @@ -560,6 +591,10 @@ public boolean hasPushNotificationConfig() { return ((bitField0_ & 0x00000002) != 0); } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return The pushNotificationConfig. */ @@ -571,6 +606,10 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { } } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { @@ -587,6 +626,10 @@ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig valu return this; } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder setPushNotificationConfig( @@ -601,6 +644,10 @@ public Builder setPushNotificationConfig( return this; } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { @@ -622,6 +669,10 @@ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig va return this; } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public Builder clearPushNotificationConfig() { @@ -635,6 +686,10 @@ public Builder clearPushNotificationConfig() { return this; } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuilder() { @@ -643,6 +698,10 @@ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuild return internalGetPushNotificationConfigFieldBuilder().getBuilder(); } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { @@ -654,6 +713,10 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu } } /** + *
+     * The push notification configuration details.
+     * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java index 660399e6d..f7d80a8e4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java @@ -12,7 +12,8 @@ public interface TaskPushNotificationConfigOrBuilder extends /** *
-   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * The resource name of the config.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -21,7 +22,8 @@ public interface TaskPushNotificationConfigOrBuilder extends java.lang.String getName(); /** *
-   * name=tasks/{id}/pushNotificationConfigs/{id}
+   * The resource name of the config.
+   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    * 
* * string name = 1; @@ -31,16 +33,28 @@ public interface TaskPushNotificationConfigOrBuilder extends getNameBytes(); /** + *
+   * The push notification configuration details.
+   * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return Whether the pushNotificationConfig field is set. */ boolean hasPushNotificationConfig(); /** + *
+   * The push notification configuration details.
+   * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; * @return The pushNotificationConfig. */ io.a2a.grpc.PushNotificationConfig getPushNotificationConfig(); /** + *
+   * The push notification configuration details.
+   * 
+ * * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java index e6c361966..4b0f9cb27 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:TaskState]
  * The set of states a Task can be in.
  * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java index 684be6c4a..0ed32c6ad 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:TaskStatus]
  * A container for the status of a task
  * 
* @@ -344,6 +345,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:TaskStatus]
    * A container for the status of a task
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java index 72d6c57bc..ddf896c3e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java @@ -7,6 +7,7 @@ /** *
+ * --8<-- [start:TaskStatusUpdateEvent]
  * TaskStatusUpdateEvent is a delta even on a task indicating that a task
  * has changed.
  * 
@@ -448,6 +449,7 @@ protected Builder newBuilderForType( } /** *
+   * --8<-- [start:TaskStatusUpdateEvent]
    * TaskStatusUpdateEvent is a delta even on a task indicating that a task
    * has changed.
    * 
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java index fb5317c33..93386e083 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java @@ -6,6 +6,10 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:TaskResubscriptionRequest]
+ * 
+ * * Protobuf type {@code a2a.v1.TaskSubscriptionRequest} */ @com.google.protobuf.Generated @@ -49,7 +53,8 @@ private TaskSubscriptionRequest() { private volatile java.lang.Object name_ = ""; /** *
-   * name=tasks/{id}
+   * The resource name of the task to subscribe to.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; @@ -70,7 +75,8 @@ public java.lang.String getName() { } /** *
-   * name=tasks/{id}
+   * The resource name of the task to subscribe to.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; @@ -248,6 +254,10 @@ protected Builder newBuilderForType( return builder; } /** + *
+   * --8<-- [start:TaskResubscriptionRequest]
+   * 
+ * * Protobuf type {@code a2a.v1.TaskSubscriptionRequest} */ public static final class Builder extends @@ -388,7 +398,8 @@ public Builder mergeFrom( private java.lang.Object name_ = ""; /** *
-     * name=tasks/{id}
+     * The resource name of the task to subscribe to.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -408,7 +419,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task to subscribe to.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -429,7 +441,8 @@ public java.lang.String getName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task to subscribe to.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -446,7 +459,8 @@ public Builder setName( } /** *
-     * name=tasks/{id}
+     * The resource name of the task to subscribe to.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; @@ -460,7 +474,8 @@ public Builder clearName() { } /** *
-     * name=tasks/{id}
+     * The resource name of the task to subscribe to.
+     * Format: tasks/{task_id}
      * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java index db4c6f9a7..07432f4a2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java @@ -12,7 +12,8 @@ public interface TaskSubscriptionRequestOrBuilder extends /** *
-   * name=tasks/{id}
+   * The resource name of the task to subscribe to.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; @@ -21,7 +22,8 @@ public interface TaskSubscriptionRequestOrBuilder extends java.lang.String getName(); /** *
-   * name=tasks/{id}
+   * The resource name of the task to subscribe to.
+   * Format: tasks/{task_id}
    * 
* * string name = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 357016054..7b434dbd8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -132,6 +132,9 @@ public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { if (agentCard.signatures() != null) { builder.addAllSignatures(agentCard.signatures().stream().map(ToProto::agentCardSignature).collect(Collectors.toList())); } + if (agentCard.iconUrl() != null) { + builder.setIconUrl(agentCard.iconUrl()); + } return builder.build(); } @@ -161,9 +164,12 @@ public static io.a2a.grpc.Message message(Message message) { } builder.setRole(role(message.getRole())); if (message.getParts() != null) { - builder.addAllContent(message.getParts().stream().map(ToProto::part).collect(Collectors.toList())); + builder.addAllParts(message.getParts().stream().map(ToProto::part).collect(Collectors.toList())); } builder.setMetadata(struct(message.getMetadata())); + if (message.getReferenceTaskIds() != null && !message.getReferenceTaskIds().isEmpty()) { + builder.addAllReferenceTaskIds(message.getReferenceTaskIds()); + } return builder.build(); } @@ -261,6 +267,9 @@ private static io.a2a.grpc.FilePart filePart(FilePart filePart) { } else if (fileContent instanceof FileWithUri) { builder.setFileWithUri(((FileWithUri) fileContent).uri()); } + if (fileContent.name() != null) { + builder.setName(fileContent.name()); + } return builder.build(); } @@ -362,6 +371,7 @@ private static io.a2a.grpc.AgentCapabilities agentCapabilities(AgentCapabilities io.a2a.grpc.AgentCapabilities.Builder builder = io.a2a.grpc.AgentCapabilities.newBuilder(); builder.setStreaming(agentCapabilities.streaming()); builder.setPushNotifications(agentCapabilities.pushNotifications()); + builder.setStateTransitionHistory(agentCapabilities.stateTransitionHistory()); if (agentCapabilities.extensions() != null) { builder.addAllExtensions(agentCapabilities.extensions().stream().map(ToProto::agentExtension).collect(Collectors.toList())); } @@ -870,11 +880,11 @@ public static Message message(io.a2a.grpc.MessageOrBuilder message) { return new Message( role(message.getRole()), - message.getContentList().stream().map(item -> part(item)).collect(Collectors.toList()), + message.getPartsList().stream().map(item -> part(item)).collect(Collectors.toList()), message.getMessageId().isEmpty() ? null : message.getMessageId(), message.getContextId().isEmpty() ? null : message.getContextId(), message.getTaskId().isEmpty() ? null : message.getTaskId(), - null, // referenceTaskIds is not in grpc message + message.getReferenceTaskIdsList().isEmpty() ? null : message.getReferenceTaskIdsList(), struct(message.getMetadata()), message.getExtensionsList().isEmpty() ? null : message.getExtensionsList() ); @@ -929,9 +939,13 @@ private static TextPart textPart(String text) { private static FilePart filePart(io.a2a.grpc.FilePartOrBuilder filePart) { if (filePart.hasFileWithBytes()) { - return new FilePart(new FileWithBytes(filePart.getMimeType(), null, filePart.getFileWithBytes().toStringUtf8())); + return new FilePart(new FileWithBytes(filePart.getMimeType(), + ! filePart.getName().isEmpty() ? filePart.getName() : null, + filePart.getFileWithBytes().toStringUtf8())); } else if (filePart.hasFileWithUri()) { - return new FilePart(new FileWithUri(filePart.getMimeType(), null, filePart.getFileWithUri())); + return new FilePart(new FileWithUri(filePart.getMimeType(), + ! filePart.getName().isEmpty() ? filePart.getName() : null, + filePart.getFileWithUri())); } throw new InvalidRequestError(); } diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index 8b1230059..55f0bce88 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -9,14 +9,10 @@ import "google/protobuf/empty.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; -// Copied from https://github.com/a2aproject/A2A/blob/v0.3.0/specification/grpc/a2a.proto -// The only change is the 'java_package' update - option csharp_namespace = "A2a.V1"; option go_package = "google.golang.org/a2a/v1"; option java_multiple_files = true; option java_outer_classname = "A2A"; -// Update package from the spec version option java_package = "io.a2a.grpc"; // A2AService defines the gRPC version of the A2A protocol. This has a slightly @@ -29,9 +25,6 @@ option java_package = "io.a2a.grpc"; // - TaskPushNotificationConfig are a resource whose parent is a task. // They have get, list and create methods. // - AgentCard is a static resource with only a get method. -// fields are not present as they don't comply with AIP rules, and the -// optional history_length on the get task method is not present as it also -// violates AIP-127 and AIP-131. service A2AService { // Send a message to the agent. This is a blocking call that will return the // task once it is completed, or a LRO if requested. @@ -57,6 +50,12 @@ service A2AService { }; option (google.api.method_signature) = "name"; } + // List tasks with optional filtering and pagination. + rpc ListTasks(ListTasksRequest) returns (ListTasksResponse) { + option (google.api.http) = { + get: "/v1/tasks" + }; + } // Cancel a task from the agent. If supported one should expect no // more task updates for the task. rpc CancelTask(CancelTaskRequest) returns (Task) { @@ -80,7 +79,7 @@ service A2AService { rpc CreateTaskPushNotificationConfig(CreateTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig) { option (google.api.http) = { - post: "/v1/{parent=task/*/pushNotificationConfigs}" + post: "/v1/{parent=tasks/*/pushNotificationConfigs}" body: "config" }; option (google.api.method_signature) = "parent,config"; @@ -119,6 +118,7 @@ service A2AService { ///////// Data Model //////////// +// --8<-- [start:MessageSendConfiguration] // Configuration of a send message request. message SendMessageConfiguration { // The output modes that the agent is expected to respond with. @@ -134,16 +134,19 @@ message SendMessageConfiguration { // updates. bool blocking = 4; } +// --8<-- [end:MessageSendConfiguration] +// --8<-- [start:Task] // Task is the core unit of action for A2A. It has a current status // and when results are created for the task they are stored in the // artifact. If there are multiple turns for a task, these are stored in // history. message Task { - // Unique identifier for a task, created by the A2A server. + // Unique identifier (e.g. UUID) for the task, generated by the server for a + // new task. string id = 1; - // Unique identifier for the contextual collection of interactions (tasks - // and messages). Created by the A2A server. + // Unique identifier (e.g. UUID) for the contextual collection of interactions + // (tasks and messages). Created by the A2A server. string context_id = 2; // The current status of a Task, including state and a message. TaskStatus status = 3; @@ -156,7 +159,9 @@ message Task { // A key/value object to store custom metadata about a task. google.protobuf.Struct metadata = 6; } +// --8<-- [end:Task] +// --8<-- [start:TaskState] // The set of states a Task can be in. enum TaskState { TASK_STATE_UNSPECIFIED = 0; @@ -183,7 +188,9 @@ enum TaskState { // an interrupted or terminal state. TASK_STATE_AUTH_REQUIRED = 8; } +// --8<-- [end:TaskState] +// --8<-- [start:TaskStatus] // A container for the status of a task message TaskStatus { // The current state of this task @@ -194,7 +201,9 @@ message TaskStatus { // Example: "2023-10-27T10:00:00Z" google.protobuf.Timestamp timestamp = 3; } +// --8<-- [end:TaskStatus] +// --8<-- [start:Part] // Part represents a container for a section of communication content. // Parts can be purely textual, some sort of file (image, video, etc) or // a structured data blob (i.e. JSON). @@ -204,8 +213,12 @@ message Part { FilePart file = 2; DataPart data = 3; } + // Optional metadata associated with this part. + google.protobuf.Struct metadata = 4; } +// --8<-- [end:Part] +// --8<-- [start:FilePart] // FilePart represents the different ways files can be provided. If files are // small, directly feeding the bytes is supported via file_with_bytes. If the // file is large, the agent should read the content as appropriate directly @@ -216,12 +229,16 @@ message FilePart { bytes file_with_bytes = 2; } string mime_type = 3; + string name = 4; } +// --8<-- [end:FilePart] +// --8<-- [start:DataPart] // DataPart represents a structured blob. This is most commonly a JSON payload. message DataPart { google.protobuf.Struct data = 1; } +// --8<-- [end:DataPart] enum Role { ROLE_UNSPECIFIED = 0; @@ -231,6 +248,7 @@ enum Role { ROLE_AGENT = 2; } +// --8<-- [start:Message] // Message is one unit of communication between client and server. It is // associated with a context and optionally a task. Since the server is // responsible for the context definition, it must always provide a context_id @@ -239,8 +257,8 @@ enum Role { // except the server decides if a task is created and whether to include the // task_id. message Message { - // The message id of the message. This is required and created by the - // message creator. + // The unique identifier (e.g. UUID)of the message. This is required and + // created by the message creator. string message_id = 1; // The context id of the message. This is optional and if set, the message // will be associated with the given context. @@ -251,20 +269,25 @@ message Message { // A role for the message. Role role = 4; // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED - // Content is the container of the message content. - repeated Part content = 5; + // Parts is the container of the message content. + repeated Part parts = 5; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED // Any optional metadata to provide along with the message. google.protobuf.Struct metadata = 6; // The URIs of extensions that are present or contributed to this Message. repeated string extensions = 7; + // A list of task IDs that this message references for additional context. + repeated string reference_task_ids = 8; } +// --8<-- [end:Message] +// --8<-- [start:Artifact] // Artifacts are the container for task completed results. These are similar // to Messages but are intended to be the product of a task, as opposed to // point-to-point communication. message Artifact { - // Unique id for the artifact. It must be at least unique within a task. + // Unique identifier (e.g. UUID) for the artifact. It must be at least unique + // within a task. string artifact_id = 1; // A human readable name for the artifact. string name = 3; @@ -277,7 +300,9 @@ message Artifact { // The URIs of extensions that are present or contributed to this Artifact. repeated string extensions = 7; } +// --8<-- [end:Artifact] +// --8<-- [start:TaskStatusUpdateEvent] // TaskStatusUpdateEvent is a delta even on a task indicating that a task // has changed. message TaskStatusUpdateEvent { @@ -292,7 +317,9 @@ message TaskStatusUpdateEvent { // Optional metadata to associate with the task update. google.protobuf.Struct metadata = 5; } +// --8<-- [end:TaskStatusUpdateEvent] +// --8<-- [start:TaskArtifactUpdateEvent] // TaskArtifactUpdateEvent represents a task delta where an artifact has // been generated. message TaskArtifactUpdateEvent { @@ -309,10 +336,12 @@ message TaskArtifactUpdateEvent { // Optional metadata associated with the artifact update. google.protobuf.Struct metadata = 6; } +// --8<-- [end:TaskArtifactUpdateEvent] +// --8<-- [start:PushNotificationConfig] // Configuration for setting up push notifications for task updates. message PushNotificationConfig { - // A unique id for this push notification. + // A unique identifier (e.g. UUID) for this push notification. string id = 1; // Url to send the notification too string url = 2; @@ -321,7 +350,9 @@ message PushNotificationConfig { // Information about the authentication to sent with the notification AuthenticationInfo authentication = 4; } +// --8<-- [end:PushNotificationConfig] +// --8<-- [start:PushNotificationAuthenticationInfo] // Defines authentication details, used for push notifications. message AuthenticationInfo { // Supported authentication schemes - e.g. Basic, Bearer, etc @@ -329,7 +360,9 @@ message AuthenticationInfo { // Optional credentials string credentials = 2; } +// --8<-- [end:PushNotificationAuthenticationInfo] +// --8<-- [start:AgentInterface] // Defines additional transport information for the agent. message AgentInterface { // The url this interface is found at. @@ -339,13 +372,15 @@ message AgentInterface { // supported are JSONRPC, GRPC and HTTP+JSON. string transport = 2; } +// --8<-- [end:AgentInterface] +// --8<-- [start:AgentCard] // AgentCard conveys key information: // - Overall details (version, name, description, uses) // - Skills; a set of actions/solutions the agent can perform // - Default modalities/content types supported by the agent. // - Authentication requirements -// Next ID: 18 +// Next ID: 19 message AgentCard { // The version of the A2A protocol this agent supports. string protocol_version = 16; @@ -405,8 +440,12 @@ message AgentCard { bool supports_authenticated_extended_card = 13; // JSON Web Signatures computed for this AgentCard. repeated AgentCardSignature signatures = 17; + // An optional URL to an icon for the agent. + string icon_url = 18; } +// --8<-- [end:AgentCard] +// --8<-- [start:AgentProvider] // Represents information about the service provider of an agent. message AgentProvider { // The providers reference url @@ -416,7 +455,9 @@ message AgentProvider { // Example: "Google" string organization = 2; } +// --8<-- [end:AgentProvider] +// --8<-- [start:AgentCapabilities] // Defines the A2A feature set supported by the agent message AgentCapabilities { // If the agent will support streaming responses @@ -425,8 +466,12 @@ message AgentCapabilities { bool push_notifications = 2; // Extensions supported by this agent. repeated AgentExtension extensions = 3; + // If the agent provides a history of state transitions for a task. + bool state_transition_history = 4; } +// --8<-- [end:AgentCapabilities] +// --8<-- [start:AgentExtension] // A declaration of an extension supported by an Agent. message AgentExtension { // The URI of the extension. @@ -441,14 +486,16 @@ message AgentExtension { // Optional configuration for the extension. google.protobuf.Struct params = 4; } +// --8<-- [end:AgentExtension] +// --8<-- [start:AgentSkill] // AgentSkill represents a unit of action/solution that the agent can perform. // One can think of this as a type of highly reliable solution that an agent // can be tasked to provide. Agents have the autonomy to choose how and when // to use specific skills, but clients should have confidence that if the // skill is defined that unit of action can be reliably performed. message AgentSkill { - // Unique id of the skill within this agent. + // Unique identifier of the skill within this agent. string id = 1; // A human readable name for the skill. string name = 2; @@ -473,8 +520,11 @@ message AgentSkill { // security requirement objects. Each object is a set of security schemes // that must be used together (a logical AND). repeated Security security = 8; + // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED } +// --8<-- [end:AgentSkill] +// --8<-- [start:AgentCardSignature] // AgentCardSignature represents a JWS signature of an AgentCard. // This follows the JSON format of an RFC 7515 JSON Web Signature (JWS). message AgentCardSignature { @@ -486,12 +536,17 @@ message AgentCardSignature { // The unprotected JWS header values. google.protobuf.Struct header = 3; } +// --8<-- [end:AgentCardSignature] +// --8<-- [start:TaskPushNotificationConfig] message TaskPushNotificationConfig { - // name=tasks/{id}/pushNotificationConfigs/{id} + // The resource name of the config. + // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} string name = 1; + // The push notification configuration details. PushNotificationConfig push_notification_config = 2; } +// --8<-- [end:TaskPushNotificationConfig] // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED message StringList { @@ -503,6 +558,7 @@ message Security { map schemes = 1; } +// --8<-- [start:SecurityScheme] message SecurityScheme { oneof scheme { APIKeySecurityScheme api_key_security_scheme = 1; @@ -512,7 +568,9 @@ message SecurityScheme { MutualTlsSecurityScheme mtls_security_scheme = 5; } } +// --8<-- [end:SecurityScheme] +// --8<-- [start:APIKeySecurityScheme] message APIKeySecurityScheme { // Description of this security scheme. string description = 1; @@ -521,7 +579,9 @@ message APIKeySecurityScheme { // Name of the header, query or cookie parameter to be used. string name = 3; } +// --8<-- [end:APIKeySecurityScheme] +// --8<-- [start:HTTPAuthSecurityScheme] message HTTPAuthSecurityScheme { // Description of this security scheme. string description = 1; @@ -535,7 +595,9 @@ message HTTPAuthSecurityScheme { // this information is primarily for documentation purposes. string bearer_format = 3; } +// --8<-- [end:HTTPAuthSecurityScheme] +// --8<-- [start:OAuth2SecurityScheme] message OAuth2SecurityScheme { // Description of this security scheme. string description = 1; @@ -545,7 +607,9 @@ message OAuth2SecurityScheme { // [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required. string oauth2_metadata_url = 3; } +// --8<-- [end:OAuth2SecurityScheme] +// --8<-- [start:OpenIdConnectSecurityScheme] message OpenIdConnectSecurityScheme { // Description of this security scheme. string description = 1; @@ -553,12 +617,16 @@ message OpenIdConnectSecurityScheme { // metadata. string open_id_connect_url = 2; } +// --8<-- [end:OpenIdConnectSecurityScheme] +// --8<-- [start:MutualTLSSecurityScheme] message MutualTlsSecurityScheme { // Description of this security scheme. string description = 1; } +// --8<-- [end:MutualTLSSecurityScheme] +// --8<-- [start:OAuthFlows] message OAuthFlows { oneof flow { AuthorizationCodeOAuthFlow authorization_code = 1; @@ -567,7 +635,9 @@ message OAuthFlows { PasswordOAuthFlow password = 4; } } +// --8<-- [end:OAuthFlows] +// --8<-- [start:AuthorizationCodeOAuthFlow] message AuthorizationCodeOAuthFlow { // The authorization URL to be used for this flow. This MUST be in the // form of a URL. The OAuth2 standard requires the use of TLS @@ -582,7 +652,9 @@ message AuthorizationCodeOAuthFlow { // scope name and a short description for it. The map MAY be empty. map scopes = 4; } +// --8<-- [end:AuthorizationCodeOAuthFlow] +// --8<-- [start:ClientCredentialsOAuthFlow] message ClientCredentialsOAuthFlow { // The token URL to be used for this flow. This MUST be in the form of a URL. // The OAuth2 standard requires the use of TLS. @@ -594,7 +666,9 @@ message ClientCredentialsOAuthFlow { // scope name and a short description for it. The map MAY be empty. map scopes = 3; } +// --8<-- [end:ClientCredentialsOAuthFlow] +// --8<-- [start:ImplicitOAuthFlow] message ImplicitOAuthFlow { // The authorization URL to be used for this flow. This MUST be in the // form of a URL. The OAuth2 standard requires the use of TLS @@ -606,7 +680,9 @@ message ImplicitOAuthFlow { // scope name and a short description for it. The map MAY be empty. map scopes = 3; } +// --8<-- [end:ImplicitOAuthFlow] +// --8<-- [start:PasswordOAuthFlow] message PasswordOAuthFlow { // The token URL to be used for this flow. This MUST be in the form of a URL. // The OAuth2 standard requires the use of TLS. @@ -618,52 +694,117 @@ message PasswordOAuthFlow { // scope name and a short description for it. The map MAY be empty. map scopes = 3; } +// --8<-- [end:PasswordOAuthFlow] ///////////// Request Messages /////////// +// --8<-- [start:MessageSendParams] message SendMessageRequest { + // The message to send to the agent. Message request = 1 [(google.api.field_behavior) = REQUIRED, json_name = "message"]; + // Configuration for the send request. SendMessageConfiguration configuration = 2; + // Optional metadata for the request. google.protobuf.Struct metadata = 3; } +// --8<-- [end:MessageSendParams] +// --8<-- [start:GetTaskRequest] message GetTaskRequest { - // name=tasks/{id} + // The resource name of the task. + // Format: tasks/{task_id} string name = 1 [(google.api.field_behavior) = REQUIRED]; + // The number of most recent messages from the task's history to retrieve. int32 history_length = 2; } +// --8<-- [end:GetTaskRequest] + +// --8<-- [start:ListTasksRequest] +message ListTasksRequest { + // Filter tasks by context ID to get tasks from a specific conversation + // or session. + string context_id = 1; + // Filter tasks by their current status state. + TaskState status = 2; + // Maximum number of tasks to return. Must be between 1 and 100. + // Defaults to 50 if not specified. + int32 page_size = 3; + // Token for pagination. Use the next_page_token from a previous + // ListTasksResponse. + string page_token = 4; + // Number of recent messages to include in each task's history. + // Must be non-negative. Defaults to 0 if not specified. + int32 history_length = 5; + // Filter tasks updated after this timestamp. Only tasks with a last + // updated time greater than or equal to this value will be returned. + google.protobuf.Timestamp last_updated_time = 6; + // Whether to include artifacts in the returned tasks. + // Defaults to false to reduce payload size. + bool include_artifacts = 7; +} +// --8<-- [end:ListTasksRequest] + +// --8<-- [start:ListTasksResponse] +message ListTasksResponse { + // Array of tasks matching the specified criteria. + repeated Task tasks = 1; + // Token for retrieving the next page of results. + // Empty string if no more results. + string next_page_token = 2; + // Total number of tasks available (before pagination). + int32 total_size = 3; +} +// --8<-- [end:ListTasksResponse] +// --8<-- [start:CancelTaskRequest] message CancelTaskRequest { - // name=tasks/{id} + // The resource name of the task to cancel. + // Format: tasks/{task_id} string name = 1; } +// --8<-- [end:CancelTaskRequest] +// --8<-- [start:GetTaskPushNotificationConfigRequest] message GetTaskPushNotificationConfigRequest { - // name=tasks/{id}/pushNotificationConfigs/{push_id} + // The resource name of the config to retrieve. + // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} string name = 1; } +// --8<-- [end:GetTaskPushNotificationConfigRequest] +// --8<-- [start:DeleteTaskPushNotificationConfigRequest] message DeleteTaskPushNotificationConfigRequest { - // name=tasks/{id}/pushNotificationConfigs/{push_id} + // The resource name of the config to delete. + // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} string name = 1; } +// --8<-- [end:DeleteTaskPushNotificationConfigRequest] +// --8<-- [start:SetTaskPushNotificationConfigRequest] message CreateTaskPushNotificationConfigRequest { - // The task resource for this config. - // Format: tasks/{id} + // The parent task resource for this config. + // Format: tasks/{task_id} string parent = 1 [(google.api.field_behavior) = REQUIRED]; + // The ID for the new config. string config_id = 2 [(google.api.field_behavior) = REQUIRED]; + // The configuration to create. TaskPushNotificationConfig config = 3 [(google.api.field_behavior) = REQUIRED]; } +// --8<-- [end:SetTaskPushNotificationConfigRequest] +// --8<-- [start:TaskResubscriptionRequest] message TaskSubscriptionRequest { - // name=tasks/{id} + // The resource name of the task to subscribe to. + // Format: tasks/{task_id} string name = 1; } +// --8<-- [end:TaskResubscriptionRequest] +// --8<-- [start:ListTaskPushNotificationConfigRequest] message ListTaskPushNotificationConfigRequest { - // parent=tasks/{id} + // The parent task resource. + // Format: tasks/{task_id} string parent = 1; // For AIP-158 these fields are present. Usually not used/needed. // The maximum number of configurations to return. @@ -678,19 +819,25 @@ message ListTaskPushNotificationConfigRequest { // the page token. string page_token = 3; } +// --8<-- [end:ListTaskPushNotificationConfigRequest] +// --8<-- [start:GetAuthenticatedExtendedCardRequest] message GetAgentCardRequest { // Empty. Added to fix linter violation. } +// --8<-- [end:GetAuthenticatedExtendedCardRequest] //////// Response Messages /////////// +// --8<-- [start:SendMessageSuccessResponse] message SendMessageResponse { oneof payload { Task task = 1; Message msg = 2 [json_name = "message"]; } } +// --8<-- [end:SendMessageSuccessResponse] +// --8<-- [start:SendStreamingMessageSuccessResponse] // The stream response for a message. The stream should be one of the following // sequences: // If the response is a message, the stream should contain one, and only one, @@ -708,10 +855,14 @@ message StreamResponse { TaskArtifactUpdateEvent artifact_update = 4; } } +// --8<-- [end:SendStreamingMessageSuccessResponse] +// --8<-- [start:ListTaskPushNotificationConfigSuccessResponse] message ListTaskPushNotificationConfigResponse { + // The list of push notification configurations. repeated TaskPushNotificationConfig configs = 1; // A token, which can be sent as `page_token` to retrieve the next page. // If this field is omitted, there are no subsequent pages. string next_page_token = 2; } +// --8<-- [end:ListTaskPushNotificationConfigSuccessResponse] diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index 67e8463ed..ef3ace765 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -163,10 +163,10 @@ public void convertTask() { assertEquals("context-1234", result.getHistory(0).getContextId()); assertEquals("message-1234", result.getHistory(0).getMessageId()); assertEquals(ROLE_USER, result.getHistory(0).getRole()); - assertEquals(1, result.getHistory(0).getContentCount()); - assertEquals("tell me a joke", result.getHistory(0).getContent(0).getText()); - assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getHistory(0).getContent(0).getFile()); - assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getHistory(0).getContent(0).getData()); + assertEquals(1, result.getHistory(0).getPartsCount()); + assertEquals("tell me a joke", result.getHistory(0).getParts(0).getText()); + assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getHistory(0).getParts(0).getFile()); + assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getHistory(0).getParts(0).getData()); } @Test @@ -175,10 +175,10 @@ public void convertMessage() { assertEquals("context-1234", result.getContextId()); assertEquals("message-1234", result.getMessageId()); assertEquals(ROLE_USER, result.getRole()); - assertEquals(1, result.getContentCount()); - assertEquals("tell me a joke", result.getContent(0).getText()); - assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getContent(0).getFile()); - assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getContent(0).getData()); + assertEquals(1, result.getPartsCount()); + assertEquals("tell me a joke", result.getParts(0).getText()); + assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getParts(0).getFile()); + assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getParts(0).getData()); Message message = new Message.Builder() .role(Message.Role.AGENT) .parts(Collections.singletonList(new TextPart("tell me a joke"))) @@ -188,10 +188,10 @@ public void convertMessage() { assertEquals("", result.getContextId()); assertEquals("message-1234", result.getMessageId()); assertEquals(ROLE_AGENT, result.getRole()); - assertEquals(1, result.getContentCount()); - assertEquals("tell me a joke", result.getContent(0).getText()); - assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getContent(0).getFile()); - assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getContent(0).getData()); + assertEquals(1, result.getPartsCount()); + assertEquals("tell me a joke", result.getParts(0).getText()); + assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getParts(0).getFile()); + assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getParts(0).getData()); } @Test diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index d7a2193bd..e1bff7f41 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -63,7 +63,7 @@ public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) .setMessageId(AbstractA2ARequestHandlerTest.MESSAGE.getMessageId()) .setRole(Role.ROLE_AGENT) - .addContent(Part.newBuilder().setText(((TextPart) AbstractA2ARequestHandlerTest.MESSAGE.getParts().get(0)).getText()).build()) + .addParts(Part.newBuilder().setText(((TextPart) AbstractA2ARequestHandlerTest.MESSAGE.getParts().get(0)).getText()).build()) .setMetadata(Struct.newBuilder().build()) .build(); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 74a505236..eca44a8db 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -63,7 +63,7 @@ public void testSendMessage() throws InvalidProtocolBufferException { "messageId": "message-1234", "contextId": "context-1234", "role": "ROLE_USER", - "content": [{ + "parts": [{ "text": "tell me a joke" }], "metadata": { @@ -103,7 +103,7 @@ public void testSendMessageWrongValueBody() { "messageId": "message-1234", "contextId": "context-1234", "role": "user", - "content": [{ + "parts": [{ "text": "tell me a joke" }], "metadata": { @@ -170,7 +170,7 @@ public void testSendStreamingMessageSuccess() { { "message": { "role": "ROLE_USER", - "content": [ + "parts": [ { "text": "tell me some jokes" } @@ -200,7 +200,7 @@ public void testSendStreamingMessageNotSupported() { { "contextId": "ctx123", "role": "ROLE_USER", - "content": [{ + "parts": [{ "text": "Hello" }] } @@ -341,7 +341,7 @@ public void testStreamingDoesNotBlockMainThread() throws Exception { { "message": { "role": "ROLE_USER", - "content": [ + "parts": [ { "text": "tell me some jokes" } From 204ffad5a2db94663bb186a405646c7e4ee15571 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 6 Nov 2025 17:57:10 +0100 Subject: [PATCH 198/493] fix: Adding support for Jspecify for JSONRPC (#452) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixing issue #335 Fixes #335 🦕 Signed-off-by: Emmanuel Hugonnet --- .../transport/jsonrpc/JSONRPCTransport.java | 37 ++++++++++--------- .../jsonrpc/JSONRPCTransportConfig.java | 5 ++- .../JSONRPCTransportConfigBuilder.java | 4 +- .../jsonrpc/JSONRPCTransportProvider.java | 11 +++--- .../transport/jsonrpc/package-info.java | 5 +++ .../jsonrpc/sse/SSEEventListener.java | 24 ++++++++---- .../transport/jsonrpc/sse/package-info.java | 5 +++ .../interceptors/ClientCallInterceptor.java | 2 +- .../interceptors/auth/AuthInterceptor.java | 2 +- pom.xml | 23 ++++++++++-- .../jsonrpc/handler/JSONRPCHandler.java | 12 +++--- .../jsonrpc/handler/package-info.java | 5 +++ 12 files changed, 88 insertions(+), 47 deletions(-) create mode 100644 client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/package-info.java create mode 100644 client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/package-info.java create mode 100644 transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/package-info.java diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 108c80558..900fcbb6e 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -59,6 +59,7 @@ import java.util.concurrent.atomic.AtomicReference; import io.a2a.util.Utils; +import org.jspecify.annotations.Nullable; public class JSONRPCTransport implements ClientTransport { @@ -73,8 +74,8 @@ public class JSONRPCTransport implements ClientTransport { private final A2AHttpClient httpClient; private final String agentUrl; - private final List interceptors; - private AgentCard agentCard; + private final @Nullable List interceptors; + private @Nullable AgentCard agentCard; private boolean needsExtendedCard = false; public JSONRPCTransport(String agentUrl) { @@ -85,8 +86,8 @@ public JSONRPCTransport(AgentCard agentCard) { this(null, agentCard, agentCard.url(), null); } - public JSONRPCTransport(A2AHttpClient httpClient, AgentCard agentCard, - String agentUrl, List interceptors) { + public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard agentCard, + String agentUrl, @Nullable List interceptors) { this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; this.agentCard = agentCard; this.agentUrl = agentUrl; @@ -95,7 +96,7 @@ public JSONRPCTransport(A2AHttpClient httpClient, AgentCard agentCard, } @Override - public EventKind sendMessage(MessageSendParams request, ClientCallContext context) throws A2AClientException { + public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); SendMessageRequest sendMessageRequest = new SendMessageRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) @@ -119,7 +120,7 @@ public EventKind sendMessage(MessageSendParams request, ClientCallContext contex @Override public void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + @Nullable Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); SendStreamingMessageRequest sendStreamingMessageRequest = new SendStreamingMessageRequest.Builder() @@ -131,7 +132,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer> ref = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { @@ -151,7 +152,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException { + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = new ListTaskPushNotificationConfigRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) @@ -274,7 +275,7 @@ public List listTaskPushNotificationConfigurations( @Override public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, - ClientCallContext context) throws A2AClientException { + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = new DeleteTaskPushNotificationConfigRequest.Builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) @@ -297,7 +298,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC @Override public void resubscribe(TaskIdParams request, Consumer eventConsumer, - Consumer errorConsumer, ClientCallContext context) throws A2AClientException { + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); checkNotNullParam("errorConsumer", errorConsumer); @@ -330,7 +331,7 @@ public void resubscribe(TaskIdParams request, Consumer event } @Override - public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException { + public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException { A2ACardResolver resolver; try { if (agentCard == null) { @@ -370,8 +371,8 @@ public void close() { // no-op } - private PayloadAndHeaders applyInterceptors(String methodName, Object payload, - AgentCard agentCard, ClientCallContext clientCallContext) { + private PayloadAndHeaders applyInterceptors(String methodName, @Nullable Object payload, + @Nullable AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, getHttpHeaders(clientCallContext)); if (interceptors != null && ! interceptors.isEmpty()) { for (ClientCallInterceptor interceptor : interceptors) { @@ -416,7 +417,7 @@ private > T unmarshalResponse(String response, Type return value; } - private Map getHttpHeaders(ClientCallContext context) { + private @Nullable Map getHttpHeaders(@Nullable ClientCallContext context) { return context != null ? context.getHeaders() : null; } } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java index efd3bbdf9..364cc4715 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java @@ -2,10 +2,11 @@ import io.a2a.client.transport.spi.ClientTransportConfig; import io.a2a.client.http.A2AHttpClient; +import org.jspecify.annotations.Nullable; public class JSONRPCTransportConfig extends ClientTransportConfig { - private final A2AHttpClient httpClient; + private final @Nullable A2AHttpClient httpClient; public JSONRPCTransportConfig() { this.httpClient = null; @@ -15,7 +16,7 @@ public JSONRPCTransportConfig(A2AHttpClient httpClient) { this.httpClient = httpClient; } - public A2AHttpClient getHttpClient() { + public @Nullable A2AHttpClient getHttpClient() { return httpClient; } } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java index 64153620f..24ced1242 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java @@ -3,14 +3,14 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.client.transport.spi.ClientTransportConfigBuilder; +import org.jspecify.annotations.Nullable; public class JSONRPCTransportConfigBuilder extends ClientTransportConfigBuilder { - private A2AHttpClient httpClient; + private @Nullable A2AHttpClient httpClient; public JSONRPCTransportConfigBuilder httpClient(A2AHttpClient httpClient) { this.httpClient = httpClient; - return this; } diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java index 97c22866a..6bec0fa21 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -5,16 +5,17 @@ import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.TransportProtocol; +import org.jspecify.annotations.Nullable; public class JSONRPCTransportProvider implements ClientTransportProvider { @Override - public JSONRPCTransport create(JSONRPCTransportConfig clientTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { - if (clientTransportConfig == null) { - clientTransportConfig = new JSONRPCTransportConfig(new JdkA2AHttpClient()); + public JSONRPCTransport create(@Nullable JSONRPCTransportConfig clientTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { + JSONRPCTransportConfig currentClientTransportConfig = clientTransportConfig; + if (currentClientTransportConfig == null) { + currentClientTransportConfig = new JSONRPCTransportConfig(new JdkA2AHttpClient()); } - - return new JSONRPCTransport(clientTransportConfig.getHttpClient(), agentCard, agentUrl, clientTransportConfig.getInterceptors()); + return new JSONRPCTransport(currentClientTransportConfig.getHttpClient(), agentCard, agentUrl, currentClientTransportConfig.getInterceptors()); } @Override diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/package-info.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/package-info.java new file mode 100644 index 000000000..3fc8e35ec --- /dev/null +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.jsonrpc; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index ff4b93f3c..d6bae5b78 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -12,31 +12,37 @@ import static io.a2a.util.Utils.OBJECT_MAPPER; +import org.jspecify.annotations.Nullable; + public class SSEEventListener { + private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); private final Consumer eventHandler; - private final Consumer errorHandler; + private final @Nullable + Consumer errorHandler; private volatile boolean completed = false; public SSEEventListener(Consumer eventHandler, - Consumer errorHandler) { + @Nullable Consumer errorHandler) { this.eventHandler = eventHandler; this.errorHandler = errorHandler; } - public void onMessage(String message, Future completableFuture) { + public void onMessage(String message, @Nullable Future completableFuture) { try { - handleMessage(OBJECT_MAPPER.readTree(message),completableFuture); + handleMessage(OBJECT_MAPPER.readTree(message), completableFuture); } catch (JsonProcessingException e) { log.warning("Failed to parse JSON message: " + message); } } - public void onError(Throwable throwable, Future future) { + public void onError(Throwable throwable, @Nullable Future future) { if (errorHandler != null) { errorHandler.accept(throwable); } - future.cancel(true); // close SSE channel + if (future != null) { + future.cancel(true); // close SSE channel + } } public void onComplete() { @@ -57,7 +63,7 @@ public void onComplete() { } } - private void handleMessage(JsonNode jsonNode, Future future) { + private void handleMessage(JsonNode jsonNode, @Nullable Future future) { try { if (jsonNode.has("error")) { JSONRPCError error = OBJECT_MAPPER.treeToValue(jsonNode.get("error"), JSONRPCError.class); @@ -70,7 +76,9 @@ private void handleMessage(JsonNode jsonNode, Future future) { StreamingEventKind event = OBJECT_MAPPER.treeToValue(result, StreamingEventKind.class); eventHandler.accept(event); if (event instanceof TaskStatusUpdateEvent && ((TaskStatusUpdateEvent) event).isFinal()) { - future.cancel(true); // close SSE channel + if (future != null) { + future.cancel(true); // close SSE channel + } } } else { throw new IllegalArgumentException("Unknown message type"); diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/package-info.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/package-info.java new file mode 100644 index 000000000..956b8992a --- /dev/null +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.jsonrpc.sse; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java index 41141298d..b8a8de797 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/ClientCallInterceptor.java @@ -23,5 +23,5 @@ public abstract class ClientCallInterceptor { * @return the potentially modified payload and headers */ public abstract PayloadAndHeaders intercept(String methodName, @Nullable Object payload, Map headers, - AgentCard agentCard, @Nullable ClientCallContext clientCallContext); + @Nullable AgentCard agentCard, @Nullable ClientCallContext clientCallContext); } diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java index d2f2a5769..8fda4ca49 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java @@ -33,7 +33,7 @@ public AuthInterceptor(final CredentialService credentialService) { @Override public PayloadAndHeaders intercept(String methodName, @Nullable Object payload, Map headers, - AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { + @Nullable AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { Map updatedHeaders = new HashMap<>(headers == null ? new HashMap<>() : headers); if (agentCard == null || agentCard.security() == null || agentCard.securitySchemes() == null) { return new PayloadAndHeaders(payload, updatedHeaders); diff --git a/pom.xml b/pom.xml index 02129d631..8bc48184f 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,9 @@ 5.5.1 2.0.17 1.5.18 - 2.42.0 + 2.43.0 + 0.12.12 + -XDaddTypeAnnotationsToSymbol=true true @@ -321,7 +323,7 @@ -XDcompilePolicy=simple --should-stop=ifError=FLOW -parameters - -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode + ${error-prone.flag}-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode @@ -332,7 +334,7 @@ com.uber.nullaway nullaway - 0.12.10 + ${nullaway.version} + old_jdk_support + + (17, 21] + + + 2.42.0 + 0.12.10 + + + \ No newline at end of file diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 9a9d5eef1..68c7e8856 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -47,21 +47,18 @@ import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.server.util.async.Internal; import mutiny.zero.ZeroPublisher; +import org.jspecify.annotations.Nullable; @ApplicationScoped public class JSONRPCHandler { private AgentCard agentCard; - private Instance extendedAgentCard; + private @Nullable Instance extendedAgentCard; private RequestHandler requestHandler; private final Executor executor; - protected JSONRPCHandler() { - this.executor = null; - } - @Inject - public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, @ExtendedAgentCard Instance extendedAgentCard, + public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, @Nullable @ExtendedAgentCard Instance extendedAgentCard, RequestHandler requestHandler, @Internal Executor executor) { this.agentCard = agentCard; this.extendedAgentCard = extendedAgentCard; @@ -227,7 +224,7 @@ public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig( // TODO: Add authentication (https://github.com/a2aproject/a2a-java/issues/77) public GetAuthenticatedExtendedCardResponse onGetAuthenticatedExtendedCardRequest( GetAuthenticatedExtendedCardRequest request, ServerCallContext context) { - if ( !agentCard.supportsAuthenticatedExtendedCard() || !extendedAgentCard.isResolvable()) { + if (!agentCard.supportsAuthenticatedExtendedCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { return new GetAuthenticatedExtendedCardResponse(request.getId(), new AuthenticatedExtendedCardNotConfiguredError()); } @@ -252,6 +249,7 @@ private Flow.Publisher convertToSendStreamingMessa return ZeroPublisher.create(createTubeConfig(), tube -> { CompletableFuture.runAsync(() -> { publisher.subscribe(new Flow.Subscriber() { + @SuppressWarnings("NullAway") Flow.Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/package-info.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/package-info.java new file mode 100644 index 000000000..20b7a9dde --- /dev/null +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.transport.jsonrpc.handler; + +import org.jspecify.annotations.NullMarked; + From f8f792d4857bf419b0b139e28ef918fbeb8c9a84 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 11 Nov 2025 12:07:17 +0000 Subject: [PATCH 199/493] fix: use main branch of TCK for 0.4.0 features (#456) --- .github/workflows/run-tck.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 375d7f6d3..6cf6e61e7 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -13,9 +13,9 @@ on: workflow_dispatch: env: - # TODO once we have the TCK for 0.4.0 we will need to look at the branch to decide which TCK version to run. + # TODO this is currently running the TCK off the main branch which included changes needed for 0.4.0 # Tag of the TCK - TCK_VERSION: 0.3.0.beta3 + TCK_VERSION: main # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 # SUT_JSONRPC_URL to use for the TCK and the server agent From 2857a072dff915db981f8514d5693b5c53fb04aa Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 11 Nov 2025 13:57:45 +0000 Subject: [PATCH 200/493] chore: Use strict TCK mode (#457) The possibility to do this was introduced in https://github.com/a2aproject/a2a-tck/pull/92. Without this, we would get TCK runs reported as passing, despite failures in the capabilities/features tests. --- .github/workflows/run-tck.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tck.yml b/.github/workflows/run-tck.yml index 6cf6e61e7..3a92c0d4c 100644 --- a/.github/workflows/run-tck.yml +++ b/.github/workflows/run-tck.yml @@ -14,8 +14,12 @@ on: env: # TODO this is currently running the TCK off the main branch which included changes needed for 0.4.0 - # Tag of the TCK + # Tag/branch of the TCK TCK_VERSION: main + # Tell the TCK runner to report failure if the quality tests fail + A2A_TCK_FAIL_ON_QUALITY: 1 + # Tell the TCK runner to report failure if the features tests fail + A2A_TCK_FAIL_ON_FEATURES: 1 # Tells uv to not need a venv, and instead use system UV_SYSTEM_PYTHON: 1 # SUT_JSONRPC_URL to use for the TCK and the server agent From 49d4bec3538131af5388c2961e426c1918e8247c Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 11 Nov 2025 15:46:57 +0000 Subject: [PATCH 201/493] feat: add list tasks functionality (#455) Fixes #359 --- .../java/io/a2a/client/AbstractClient.java | 23 ++ .../src/main/java/io/a2a/client/Client.java | 7 + .../client/transport/grpc/GrpcTransport.java | 48 +++ .../transport/jsonrpc/JSONRPCTransport.java | 26 ++ .../client/transport/rest/RestTransport.java | 89 +++++ .../client/transport/spi/ClientTransport.java | 12 + .../database/jpa/JpaDatabaseTaskStore.java | 127 +++++++ .../taskstore/database/jpa/JpaTask.java | 40 +++ .../jpa/JpaDatabaseTaskStoreTest.java | 340 +++++++++++++++++- .../server/apps/quarkus/A2AServerRoutes.java | 3 + .../server/rest/quarkus/A2AServerRoutes.java | 46 +++ .../DefaultRequestHandler.java | 47 ++- .../requesthandlers/RequestHandler.java | 9 +- .../a2a/server/tasks/InMemoryTaskStore.java | 96 +++++ .../java/io/a2a/server/tasks/TaskStore.java | 10 + .../java/io/a2a/grpc/utils/ProtoUtils.java | 44 ++- .../spec/JSONRPCRequestDeserializerBase.java | 1 + .../java/io/a2a/spec/JSONRPCResponse.java | 5 +- .../java/io/a2a/spec/ListTasksParams.java | 145 ++++++++ .../java/io/a2a/spec/ListTasksRequest.java | 81 +++++ .../java/io/a2a/spec/ListTasksResponse.java | 28 ++ .../java/io/a2a/spec/ListTasksResult.java | 89 +++++ .../a2a/spec/NonStreamingJSONRPCRequest.java | 2 +- ...onStreamingJSONRPCRequestDeserializer.java | 3 + .../apps/common/AbstractA2AServerTest.java | 214 +++++++++++ .../transport/grpc/handler/GrpcHandler.java | 18 + .../jsonrpc/handler/JSONRPCHandler.java | 14 + .../transport/rest/handler/RestHandler.java | 39 ++ 28 files changed, 1581 insertions(+), 25 deletions(-) create mode 100644 spec/src/main/java/io/a2a/spec/ListTasksParams.java create mode 100644 spec/src/main/java/io/a2a/spec/ListTasksRequest.java create mode 100644 spec/src/main/java/io/a2a/spec/ListTasksResponse.java create mode 100644 spec/src/main/java/io/a2a/spec/ListTasksResult.java diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index b6a41a35a..63e88b8e7 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -13,6 +13,8 @@ import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; @@ -172,6 +174,27 @@ public Task getTask(TaskQueryParams request) throws A2AClientException { */ public abstract Task getTask(TaskQueryParams request, @Nullable ClientCallContext context) throws A2AClientException; + /** + * List tasks with optional filtering and pagination. + * + * @param request the list tasks parameters including filters and pagination + * @return the list tasks result containing tasks and pagination information + * @throws A2AClientException if listing tasks fails for any reason + */ + public ListTasksResult listTasks(ListTasksParams request) throws A2AClientException { + return listTasks(request, null); + } + + /** + * List tasks with optional filtering and pagination. + * + * @param request the list tasks parameters including filters and pagination + * @param context optional client call context for the request (may be {@code null}) + * @return the list tasks result containing tasks and pagination information + * @throws A2AClientException if listing tasks fails for any reason + */ + public abstract ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallContext context) throws A2AClientException; + /** * Request the agent to cancel a specific task. * diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index ab2222667..a6b9d6962 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -16,6 +16,8 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -85,6 +87,11 @@ public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context return clientTransport.getTask(request, context); } + @Override + public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallContext context) throws A2AClientException { + return clientTransport.listTasks(request, context); + } + @Override public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.cancelTask(request, context); diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 224433393..4faf40f6e 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -22,6 +22,7 @@ import io.a2a.grpc.GetTaskPushNotificationConfigRequest; import io.a2a.grpc.GetTaskRequest; import io.a2a.grpc.ListTaskPushNotificationConfigRequest; +import io.a2a.grpc.ListTasksRequest; import io.a2a.grpc.SendMessageRequest; import io.a2a.grpc.SendMessageResponse; import io.a2a.grpc.StreamResponse; @@ -34,6 +35,8 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.SendStreamingMessageRequest; import io.a2a.spec.SetTaskPushNotificationConfigRequest; @@ -154,6 +157,51 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context } } + @Override + public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + + ListTasksRequest.Builder requestBuilder = ListTasksRequest.newBuilder(); + if (request.contextId() != null) { + requestBuilder.setContextId(request.contextId()); + } + if (request.status() != null) { + requestBuilder.setStatus(ToProto.taskState(request.status())); + } + if (request.pageSize() != null) { + requestBuilder.setPageSize(request.pageSize()); + } + if (request.pageToken() != null) { + requestBuilder.setPageToken(request.pageToken()); + } + if (request.historyLength() != null) { + requestBuilder.setHistoryLength(request.historyLength()); + } + if (request.includeArtifacts() != null && request.includeArtifacts()) { + requestBuilder.setIncludeArtifacts(true); + } + + ListTasksRequest listTasksRequest = requestBuilder.build(); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.ListTasksRequest.METHOD, listTasksRequest, + agentCard, context); + + try { + A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); + io.a2a.grpc.ListTasksResponse grpcResponse = stubWithMetadata.listTasks(listTasksRequest); + + return new io.a2a.spec.ListTasksResult( + grpcResponse.getTasksList().stream() + .map(FromProto::task) + .collect(Collectors.toList()), + grpcResponse.getTotalSize(), + grpcResponse.getTasksCount(), + grpcResponse.getNextPageToken().isEmpty() ? null : grpcResponse.getNextPageToken() + ); + } catch (StatusRuntimeException e) { + throw GrpcErrorMapper.mapGrpcError(e, "Failed to list tasks: "); + } + } + @Override public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 900fcbb6e..29274db08 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -40,6 +40,10 @@ import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksRequest; +import io.a2a.spec.ListTasksResponse; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.MessageSendParams; @@ -66,6 +70,7 @@ public class JSONRPCTransport implements ClientTransport { private static final TypeReference SEND_MESSAGE_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference GET_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference CANCEL_TASK_RESPONSE_REFERENCE = new TypeReference<>() {}; + private static final TypeReference LIST_TASKS_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {}; @@ -197,6 +202,27 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context } } + @Override + public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + ListTasksRequest listTasksRequest = new ListTasksRequest.Builder() + .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .method(ListTasksRequest.METHOD) + .params(request) + .build(); // id will be randomly generated + + PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTasksRequest.METHOD, listTasksRequest, + agentCard, context); + + try { + String httpResponseBody = sendPostRequest(payloadAndHeaders); + ListTasksResponse response = unmarshalResponse(httpResponseBody, LIST_TASKS_RESPONSE_REFERENCE); + return response.getResult(); + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to list tasks: " + e, e); + } + } + @Override public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 5b67d97e0..fa2875e9f 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -20,6 +20,7 @@ import io.a2a.grpc.GetTaskPushNotificationConfigRequest; import io.a2a.grpc.GetTaskRequest; import io.a2a.grpc.ListTaskPushNotificationConfigRequest; +import io.a2a.grpc.ListTasksRequest; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; @@ -27,6 +28,8 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -38,6 +41,8 @@ import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.util.Utils; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.logging.Logger; @@ -170,6 +175,90 @@ public Task cancelTask(TaskIdParams taskIdParams, @Nullable ClientCallContext co } } + @Override + public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallContext context) throws A2AClientException { + checkNotNullParam("request", request); + ListTasksRequest.Builder builder = ListTasksRequest.newBuilder(); + if (request.contextId() != null) { + builder.setContextId(request.contextId()); + } + if (request.status() != null) { + builder.setStatus(ProtoUtils.ToProto.taskState(request.status())); + } + if (request.pageSize() != null) { + builder.setPageSize(request.pageSize()); + } + if (request.pageToken() != null) { + builder.setPageToken(request.pageToken()); + } + if (request.historyLength() != null) { + builder.setHistoryLength(request.historyLength()); + } + if (request.includeArtifacts() != null && request.includeArtifacts()) { + builder.setIncludeArtifacts(true); + } + + PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.ListTasksRequest.METHOD, builder, + agentCard, context); + + try { + // Build query string + StringBuilder urlBuilder = new StringBuilder(agentUrl).append("/v1/tasks"); + String queryParams = buildListTasksQueryString(request); + if (!queryParams.isEmpty()) { + urlBuilder.append("?").append(queryParams); + } + + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(urlBuilder.toString()); + if (payloadAndHeaders.getHeaders() != null) { + for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { + getBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + A2AHttpResponse response = getBuilder.get(); + if (!response.success()) { + throw RestErrorMapper.mapRestError(response); + } + String httpResponseBody = response.body(); + io.a2a.grpc.ListTasksResponse.Builder responseBuilder = io.a2a.grpc.ListTasksResponse.newBuilder(); + JsonFormat.parser().merge(httpResponseBody, responseBuilder); + + return new ListTasksResult( + responseBuilder.getTasksList().stream() + .map(ProtoUtils.FromProto::task) + .toList(), + responseBuilder.getTotalSize(), + responseBuilder.getTasksCount(), + responseBuilder.getNextPageToken().isEmpty() ? null : responseBuilder.getNextPageToken() + ); + } catch (IOException | InterruptedException e) { + throw new A2AClientException("Failed to list tasks: " + e, e); + } + } + + private String buildListTasksQueryString(ListTasksParams request) { + java.util.List queryParts = new java.util.ArrayList<>(); + if (request.contextId() != null) { + queryParts.add("contextId=" + URLEncoder.encode(request.contextId(), StandardCharsets.UTF_8)); + } + if (request.status() != null) { + queryParts.add("status=" + request.status().asString()); + } + if (request.pageSize() != null) { + queryParts.add("pageSize=" + request.pageSize()); + } + if (request.pageToken() != null) { + queryParts.add("pageToken=" + URLEncoder.encode(request.pageToken(), StandardCharsets.UTF_8)); + } + if (request.historyLength() != null) { + queryParts.add("historyLength=" + request.historyLength()); + } + if (request.includeArtifacts() != null && request.includeArtifacts()) { + queryParts.add("includeArtifacts=true"); + } + return String.join("&", queryParts); + } + @Override public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 56a4067cf..202e80492 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -10,6 +10,8 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -66,6 +68,16 @@ void sendMessageStreaming(MessageSendParams request, Consumer :pageToken"); + } + + // Sort by task ID for consistent pagination + queryBuilder.append(" ORDER BY t.id"); + + // Create and configure the main query + TypedQuery query = em.createQuery(queryBuilder.toString(), JpaTask.class); + + // Set filter parameters + if (params.contextId() != null) { + query.setParameter("contextId", params.contextId()); + } + if (params.status() != null) { + query.setParameter("state", params.status().asString()); + } + if (params.pageToken() != null && !params.pageToken().isEmpty()) { + query.setParameter("pageToken", params.pageToken()); + } + + // Apply page size limit (+1 to check for next page) + int pageSize = params.getEffectivePageSize(); + query.setMaxResults(pageSize + 1); + + // Execute query and deserialize tasks + List jpaTasksPage = query.getResultList(); + + // Determine if there are more results + boolean hasMore = jpaTasksPage.size() > pageSize; + if (hasMore) { + jpaTasksPage = jpaTasksPage.subList(0, pageSize); + } + + // Get total count of matching tasks + TypedQuery countQuery = em.createQuery(countQueryBuilder.toString(), Long.class); + if (params.contextId() != null) { + countQuery.setParameter("contextId", params.contextId()); + } + if (params.status() != null) { + countQuery.setParameter("state", params.status().asString()); + } + int totalSize = countQuery.getSingleResult().intValue(); + + // Deserialize tasks from JSON + List tasks = new ArrayList<>(); + for (JpaTask jpaTask : jpaTasksPage) { + try { + tasks.add(jpaTask.getTask()); + } catch (JsonProcessingException e) { + LOGGER.error("Failed to deserialize task with ID: {}", jpaTask.getId(), e); + throw new RuntimeException("Failed to deserialize task with ID: " + jpaTask.getId(), e); + } + } + + // Determine next page token (ID of last task if there are more results) + String nextPageToken = null; + if (hasMore && !tasks.isEmpty()) { + nextPageToken = tasks.get(tasks.size() - 1).getId(); + } + + // Apply post-processing transformations (history limiting, artifact removal) + int historyLength = params.getEffectiveHistoryLength(); + boolean includeArtifacts = params.shouldIncludeArtifacts(); + + List transformedTasks = tasks.stream() + .map(task -> transformTask(task, historyLength, includeArtifacts)) + .toList(); + + LOGGER.debug("Returning {} tasks out of {} total", transformedTasks.size(), totalSize); + return new ListTasksResult(transformedTasks, totalSize, transformedTasks.size(), nextPageToken); + } + + private Task transformTask(Task task, int historyLength, boolean includeArtifacts) { + // Limit history if needed (keep most recent N messages) + List history = task.getHistory(); + if (historyLength > 0 && history != null && history.size() > historyLength) { + history = history.subList(history.size() - historyLength, history.size()); + } + + // Remove artifacts if not requested + List artifacts = includeArtifacts ? task.getArtifacts() : List.of(); + + // If no transformation needed, return original task + if (history == task.getHistory() && artifacts == task.getArtifacts()) { + return task; + } + + // Build new task with transformed data + return new Task.Builder(task) + .artifacts(artifacts) + .history(history) + .build(); + } } diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java index 2b3fc56f1..ebdb8f2a4 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -19,6 +19,12 @@ public class JpaTask { @Column(name = "task_id") private String id; + @Column(name = "context_id") + private String contextId; + + @Column(name = "state") + private String state; + @Column(name = "task_data", columnDefinition = "TEXT", nullable = false) private String taskJson; @@ -45,6 +51,22 @@ public void setId(String id) { this.id = id; } + public String getContextId() { + return contextId; + } + + public void setContextId(String contextId) { + this.contextId = contextId; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + public String getTaskJson() { return taskJson; } @@ -87,6 +109,7 @@ public void setTask(Task task) throws JsonProcessingException { id = task.getId(); } this.task = task; + updateDenormalizedFields(task); updateFinalizedTimestamp(task); } @@ -94,10 +117,27 @@ static JpaTask createFromTask(Task task) throws JsonProcessingException { String json = Utils.OBJECT_MAPPER.writeValueAsString(task); JpaTask jpaTask = new JpaTask(task.getId(), json); jpaTask.task = task; + jpaTask.updateDenormalizedFields(task); jpaTask.updateFinalizedTimestamp(task); return jpaTask; } + /** + * Updates denormalized fields (contextId, state) from the task object. + * These fields are duplicated from the JSON to enable efficient querying. + * + * @param task the task to extract fields from + */ + private void updateDenormalizedFields(Task task) { + this.contextId = task.getContextId(); + if (task.getStatus() != null) { + io.a2a.spec.TaskState taskState = task.getStatus().state(); + this.state = (taskState != null) ? taskState.asString() : null; + } else { + this.state = null; + } + } + /** * Updates the finalizedAt timestamp if the task is in a final state. * This method is idempotent and only sets the timestamp on first finalization. diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index 8c091f87f..792bf8c0f 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -4,14 +4,20 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.Artifact; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; @@ -270,7 +276,339 @@ public void testIsTaskActiveForFinalTaskBeyondGracePeriod() { public void testIsTaskActiveForNonExistentTask() { JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; boolean isActive = jpaDatabaseTaskStore.isTaskActive("non-existent-task"); - + assertEquals(false, isActive, "Non-existent task should be inactive"); } + + // ===== list() method tests ===== + + @Test + @Transactional + public void testListTasksEmpty() { + // List with specific context that has no tasks + ListTasksParams params = new ListTasksParams.Builder() + .contextId("non-existent-context-12345") + .build(); + ListTasksResult result = taskStore.list(params); + + assertNotNull(result); + assertEquals(0, result.totalSize()); + assertEquals(0, result.pageSize()); + assertTrue(result.tasks().isEmpty()); + assertNull(result.nextPageToken()); + } + + @Test + @Transactional + public void testListTasksFilterByContextId() { + // Create tasks with different context IDs + Task task1 = new Task.Builder() + .id("task-context-1") + .contextId("context-A") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + Task task2 = new Task.Builder() + .id("task-context-2") + .contextId("context-A") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + Task task3 = new Task.Builder() + .id("task-context-3") + .contextId("context-B") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + taskStore.save(task1); + taskStore.save(task2); + taskStore.save(task3); + + // List tasks for context-A + ListTasksParams params = new ListTasksParams.Builder() + .contextId("context-A") + .build(); + ListTasksResult result = taskStore.list(params); + + assertEquals(2, result.totalSize()); + assertEquals(2, result.pageSize()); + assertEquals(2, result.tasks().size()); + assertTrue(result.tasks().stream().allMatch(t -> "context-A".equals(t.getContextId()))); + } + + @Test + @Transactional + public void testListTasksFilterByStatus() { + // Create tasks with different statuses - use unique context + Task task1 = new Task.Builder() + .id("task-status-filter-1") + .contextId("context-status-filter-test") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + Task task2 = new Task.Builder() + .id("task-status-filter-2") + .contextId("context-status-filter-test") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + Task task3 = new Task.Builder() + .id("task-status-filter-3") + .contextId("context-status-filter-test") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + taskStore.save(task1); + taskStore.save(task2); + taskStore.save(task3); + + // List only WORKING tasks in this context + ListTasksParams params = new ListTasksParams.Builder() + .contextId("context-status-filter-test") + .status(TaskState.WORKING) + .build(); + ListTasksResult result = taskStore.list(params); + + assertEquals(1, result.totalSize()); + assertEquals(1, result.pageSize()); + assertEquals(1, result.tasks().size()); + assertEquals(TaskState.WORKING, result.tasks().get(0).getStatus().state()); + } + + @Test + @Transactional + public void testListTasksCombinedFilters() { + // Create tasks with various context IDs and statuses + Task task1 = new Task.Builder() + .id("task-combined-1") + .contextId("context-X") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + Task task2 = new Task.Builder() + .id("task-combined-2") + .contextId("context-X") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + Task task3 = new Task.Builder() + .id("task-combined-3") + .contextId("context-Y") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + + taskStore.save(task1); + taskStore.save(task2); + taskStore.save(task3); + + // List WORKING tasks in context-X + ListTasksParams params = new ListTasksParams.Builder() + .contextId("context-X") + .status(TaskState.WORKING) + .build(); + ListTasksResult result = taskStore.list(params); + + assertEquals(1, result.totalSize()); + assertEquals(1, result.pageSize()); + assertEquals("task-combined-2", result.tasks().get(0).getId()); + assertEquals("context-X", result.tasks().get(0).getContextId()); + assertEquals(TaskState.WORKING, result.tasks().get(0).getStatus().state()); + } + + @Test + @Transactional + public void testListTasksPagination() { + // Create 5 tasks + for (int i = 1; i <= 5; i++) { + Task task = new Task.Builder() + .id("task-page-" + i) + .contextId("context-pagination") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + taskStore.save(task); + } + + // First page: pageSize=2 + ListTasksParams params1 = new ListTasksParams.Builder() + .contextId("context-pagination") + .pageSize(2) + .build(); + ListTasksResult result1 = taskStore.list(params1); + + assertEquals(5, result1.totalSize()); + assertEquals(2, result1.pageSize()); + assertEquals(2, result1.tasks().size()); + assertNotNull(result1.nextPageToken(), "Should have next page token"); + + // Second page: use pageToken from first page + ListTasksParams params2 = new ListTasksParams.Builder() + .contextId("context-pagination") + .pageSize(2) + .pageToken(result1.nextPageToken()) + .build(); + ListTasksResult result2 = taskStore.list(params2); + + assertEquals(5, result2.totalSize()); + assertEquals(2, result2.pageSize()); + assertNotNull(result2.nextPageToken(), "Should have next page token"); + + // Third page: last page + ListTasksParams params3 = new ListTasksParams.Builder() + .contextId("context-pagination") + .pageSize(2) + .pageToken(result2.nextPageToken()) + .build(); + ListTasksResult result3 = taskStore.list(params3); + + assertEquals(5, result3.totalSize()); + assertEquals(1, result3.pageSize()); + assertNull(result3.nextPageToken(), "Last page should have no next page token"); + } + + @Test + @Transactional + public void testListTasksHistoryLimiting() { + // Create messages for history + List longHistory = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + Message message = new Message.Builder() + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("Message " + i))) + .messageId("msg-history-limit-" + i) + .build(); + longHistory.add(message); + } + + // Create task with long history - use unique context + Task task = new Task.Builder() + .id("task-history-limit-unique-1") + .contextId("context-history-limit-unique") + .status(new TaskStatus(TaskState.WORKING)) + .history(longHistory) + .build(); + + taskStore.save(task); + + // List with historyLength=3 (should keep only last 3 messages) - filter by unique context + ListTasksParams params = new ListTasksParams.Builder() + .contextId("context-history-limit-unique") + .historyLength(3) + .build(); + ListTasksResult result = taskStore.list(params); + + assertEquals(1, result.tasks().size()); + Task retrieved = result.tasks().get(0); + assertEquals(3, retrieved.getHistory().size()); + // Should have messages 8, 9, 10 (last 3) + assertEquals("msg-history-limit-8", retrieved.getHistory().get(0).getMessageId()); + assertEquals("msg-history-limit-9", retrieved.getHistory().get(1).getMessageId()); + assertEquals("msg-history-limit-10", retrieved.getHistory().get(2).getMessageId()); + } + + @Test + @Transactional + public void testListTasksArtifactInclusion() { + // Create task with artifacts - use unique context + List artifacts = new ArrayList<>(); + Artifact artifact = new Artifact.Builder() + .artifactId("artifact-unique-1") + .name("test-artifact") + .parts(Collections.singletonList(new TextPart("Artifact content"))) + .build(); + artifacts.add(artifact); + + Task task = new Task.Builder() + .id("task-artifact-unique-1") + .contextId("context-artifact-unique") + .status(new TaskStatus(TaskState.COMPLETED)) + .artifacts(artifacts) + .build(); + + taskStore.save(task); + + // List without artifacts (default) - filter by unique context + ListTasksParams paramsWithoutArtifacts = new ListTasksParams.Builder() + .contextId("context-artifact-unique") + .build(); + ListTasksResult resultWithout = taskStore.list(paramsWithoutArtifacts); + + assertEquals(1, resultWithout.tasks().size()); + assertTrue(resultWithout.tasks().get(0).getArtifacts().isEmpty(), + "By default, artifacts should be excluded"); + + // List with artifacts - filter by unique context + ListTasksParams paramsWithArtifacts = new ListTasksParams.Builder() + .contextId("context-artifact-unique") + .includeArtifacts(true) + .build(); + ListTasksResult resultWith = taskStore.list(paramsWithArtifacts); + + assertEquals(1, resultWith.tasks().size()); + assertEquals(1, resultWith.tasks().get(0).getArtifacts().size(), + "When includeArtifacts=true, artifacts should be included"); + assertEquals("artifact-unique-1", resultWith.tasks().get(0).getArtifacts().get(0).artifactId()); + } + + @Test + @Transactional + public void testListTasksDefaultPageSize() { + // Create 100 tasks (more than default page size of 50) + for (int i = 1; i <= 100; i++) { + Task task = new Task.Builder() + .id("task-default-pagesize-" + String.format("%03d", i)) + .contextId("context-default-pagesize") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + taskStore.save(task); + } + + // List without specifying pageSize (should use default of 50) + ListTasksParams params = new ListTasksParams.Builder() + .contextId("context-default-pagesize") + .build(); + ListTasksResult result = taskStore.list(params); + + assertEquals(100, result.totalSize()); + assertEquals(50, result.pageSize(), "Default page size should be 50"); + assertNotNull(result.nextPageToken(), "Should have next page"); + } + + @Test + @Transactional + public void testListTasksOrderingById() { + // Create tasks with IDs that will sort in specific order + Task task1 = new Task.Builder() + .id("task-order-a") + .contextId("context-order") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + Task task2 = new Task.Builder() + .id("task-order-b") + .contextId("context-order") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + Task task3 = new Task.Builder() + .id("task-order-c") + .contextId("context-order") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + // Save in reverse order + taskStore.save(task3); + taskStore.save(task1); + taskStore.save(task2); + + // List should return in ID order + ListTasksParams params = new ListTasksParams.Builder() + .contextId("context-order") + .build(); + ListTasksResult result = taskStore.list(params); + + assertEquals(3, result.tasks().size()); + assertEquals("task-order-a", result.tasks().get(0).getId()); + assertEquals("task-order-b", result.tasks().get(1).getId()); + assertEquals("task-order-c", result.tasks().get(2).getId()); + } } diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index df726ad88..82c0f0d46 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -46,6 +46,7 @@ import io.a2a.spec.JSONRPCRequest; import io.a2a.spec.JSONRPCResponse; import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTasksRequest; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.MethodNotFoundJsonMappingException; import io.a2a.spec.NonStreamingJSONRPCRequest; @@ -176,6 +177,8 @@ private JSONRPCResponse processNonStreamingRequest( return jsonRpcHandler.onGetTask(req, context); } else if (request instanceof CancelTaskRequest req) { return jsonRpcHandler.onCancelTask(req, context); + } else if (request instanceof ListTasksRequest req) { + return jsonRpcHandler.onListTasks(req, context); } else if (request instanceof SetTaskPushNotificationConfigRequest req) { return jsonRpcHandler.setPushNotificationConfig(req, context); } else if (request instanceof GetTaskPushNotificationConfigRequest req) { diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 4f0173070..75c34f247 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -50,6 +50,7 @@ import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskRequest; import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTasksRequest; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendStreamingMessageRequest; import io.a2a.spec.SetTaskPushNotificationConfigRequest; @@ -113,6 +114,51 @@ public void sendMessageStreaming(@Body String body, RoutingContext rc) { } } + @Route(path = "/v1/tasks", order = 0, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + public void listTasks(RoutingContext rc) { + ServerCallContext context = createCallContext(rc, ListTasksRequest.METHOD); + HTTPRestResponse response = null; + try { + // Extract query parameters + String contextId = rc.request().params().get("contextId"); + String statusStr = rc.request().params().get("status"); + if (statusStr != null && !statusStr.isEmpty()) { + statusStr = statusStr.toUpperCase(); + } + String pageSizeStr = rc.request().params().get("pageSize"); + String pageToken = rc.request().params().get("pageToken"); + String historyLengthStr = rc.request().params().get("historyLength"); + String includeArtifactsStr = rc.request().params().get("includeArtifacts"); + + // Parse optional parameters + Integer pageSize = null; + if (pageSizeStr != null && !pageSizeStr.isEmpty()) { + pageSize = Integer.parseInt(pageSizeStr); + } + + Integer historyLength = null; + if (historyLengthStr != null && !historyLengthStr.isEmpty()) { + historyLength = Integer.parseInt(historyLengthStr); + } + + Boolean includeArtifacts = null; + if (includeArtifactsStr != null && !includeArtifactsStr.isEmpty()) { + includeArtifacts = Boolean.parseBoolean(includeArtifactsStr); + } + + response = jsonRestHandler.listTasks(contextId, statusStr, pageSize, pageToken, + historyLength, includeArtifacts, context); + } catch (NumberFormatException e) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("Invalid number format in parameters")); + } catch (IllegalArgumentException e) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("Invalid parameter value: " + e.getMessage())); + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + sendResponse(rc, response); + } + } + @Route(path = "/v1/tasks/:id", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void getTask(RoutingContext rc) { String taskId = rc.pathParam("id"); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index de62f1a0f..56efc70ce 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -45,6 +45,8 @@ import io.a2a.spec.InternalError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; @@ -136,25 +138,40 @@ public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws LOGGER.debug("No task found for {}. Throwing TaskNotFoundError", params.id()); throw new TaskNotFoundError(); } - if (task.getHistory() != null && params.historyLength() < task.getHistory().size()) { - List history; - if (params.historyLength() <= 0) { - history = task.getHistory(); - } else { - history = task.getHistory().subList( - task.getHistory().size() - params.historyLength(), - task.getHistory().size() - 1); - } - - task = new Task.Builder(task) - .history(history) - .build(); - } - + task = limitTaskHistory(task, params.historyLength()); LOGGER.debug("Task found {}", task); return task; } + /** + * Limits the history of a task to the most recent N messages. + * + * @param task the task to limit + * @param historyLength the maximum number of recent messages to keep (0 or negative = unlimited) + * @return the task with limited history, or the original task if no limiting needed + */ + private static Task limitTaskHistory(Task task, int historyLength) { + if (task.getHistory() == null || historyLength <= 0 || historyLength >= task.getHistory().size()) { + return task; + } + // Keep only the most recent historyLength messages + List limitedHistory = task.getHistory().subList( + task.getHistory().size() - historyLength, + task.getHistory().size()); + return new Task.Builder(task) + .history(limitedHistory) + .build(); + } + + @Override + public ListTasksResult onListTasks(ListTasksParams params, ServerCallContext context) throws JSONRPCError { + LOGGER.debug("onListTasks with contextId={}, status={}, pageSize={}, pageToken={}", + params.contextId(), params.status(), params.pageSize(), params.pageToken()); + ListTasksResult result = taskStore.list(params); + LOGGER.debug("Found {} tasks (total: {})", result.pageSize(), result.totalSize()); + return result; + } + @Override public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws JSONRPCError { Task task = taskStore.get(params.id()); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index 50ba260c0..64ba8eea1 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -9,6 +9,8 @@ import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -20,9 +22,10 @@ public interface RequestHandler { Task onGetTask( TaskQueryParams params, ServerCallContext context) throws JSONRPCError; -// -// List onListTask(ServerCallContext context) -// throws JSONRPCError; + + ListTasksResult onListTasks( + ListTasksParams params, + ServerCallContext context) throws JSONRPCError; Task onCancelTask( TaskIdParams params, diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 0320f8422..0fd604089 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -1,10 +1,19 @@ package io.a2a.server.tasks; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.stream.Stream; import jakarta.enterprise.context.ApplicationScoped; +import io.a2a.spec.Artifact; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; +import io.a2a.spec.Message; import io.a2a.spec.Task; @ApplicationScoped @@ -27,6 +36,93 @@ public void delete(String taskId) { tasks.remove(taskId); } + @Override + public ListTasksResult list(ListTasksParams params) { + Stream taskStream = tasks.values().stream(); + + // Apply filters + if (params.contextId() != null) { + taskStream = taskStream.filter(task -> params.contextId().equals(task.getContextId())); + } + if (params.status() != null) { + taskStream = taskStream.filter(task -> + task.getStatus() != null && params.status().equals(task.getStatus().state()) + ); + } + // Note: lastUpdatedAfter filtering not implemented in InMemoryTaskStore + // as Task doesn't have a lastUpdated timestamp field + + // Sort by task ID for consistent pagination + List allFilteredTasks = taskStream + .sorted(Comparator.comparing(Task::getId)) + .toList(); + + int totalSize = allFilteredTasks.size(); + + // Apply pagination + int pageSize = params.getEffectivePageSize(); + int startIndex = 0; + + // Handle page token (simple cursor: last task ID from previous page) + if (params.pageToken() != null && !params.pageToken().isEmpty()) { + // Use binary search since list is sorted by task ID (O(log N) vs O(N)) + int index = Collections.binarySearch(allFilteredTasks, null, + (t1, t2) -> { + // Handle null key comparisons (binarySearch passes null as one argument) + if (t1 == null && t2 == null) return 0; + if (t1 == null) return params.pageToken().compareTo(t2.getId()); + if (t2 == null) return t1.getId().compareTo(params.pageToken()); + return t1.getId().compareTo(t2.getId()); + }); + if (index >= 0) { + startIndex = index + 1; + } + // If not found (index < 0), startIndex remains 0 (start from beginning) + } + + // Get the page of tasks + int endIndex = Math.min(startIndex + pageSize, allFilteredTasks.size()); + List pageTasks = allFilteredTasks.subList(startIndex, endIndex); + + // Determine next page token + String nextPageToken = null; + if (endIndex < allFilteredTasks.size()) { + nextPageToken = allFilteredTasks.get(endIndex - 1).getId(); + } + + // Transform tasks: limit history and optionally remove artifacts + int historyLength = params.getEffectiveHistoryLength(); + boolean includeArtifacts = params.shouldIncludeArtifacts(); + + List transformedTasks = pageTasks.stream() + .map(task -> transformTask(task, historyLength, includeArtifacts)) + .toList(); + + return new ListTasksResult(transformedTasks, totalSize, transformedTasks.size(), nextPageToken); + } + + private Task transformTask(Task task, int historyLength, boolean includeArtifacts) { + // Limit history if needed (keep most recent N messages) + List history = task.getHistory(); + if (historyLength > 0 && history != null && history.size() > historyLength) { + history = history.subList(history.size() - historyLength, history.size()); + } + + // Remove artifacts if not requested + List artifacts = includeArtifacts ? task.getArtifacts() : List.of(); + + // If no transformation needed, return original task + if (history == task.getHistory() && artifacts == task.getArtifacts()) { + return task; + } + + // Build new task with transformed data + return new Task.Builder(task) + .artifacts(artifacts) + .history(history) + .build(); + } + @Override public boolean isTaskActive(String taskId) { Task task = tasks.get(taskId); diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java index 26f66f023..dafd90360 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java @@ -1,6 +1,8 @@ package io.a2a.server.tasks; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Task; public interface TaskStore { @@ -9,4 +11,12 @@ public interface TaskStore { Task get(String taskId); void delete(String taskId); + + /** + * List tasks with optional filtering and pagination. + * + * @param params the filtering and pagination parameters + * @return the list of tasks matching the criteria with pagination info + */ + ListTasksResult list(ListTasksParams params); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 7b434dbd8..4a69af75e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -39,6 +39,7 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -153,6 +154,18 @@ public static io.a2a.grpc.Task task(Task task) { return builder.build(); } + public static io.a2a.grpc.ListTasksResponse listTasksResult(io.a2a.spec.ListTasksResult result) { + io.a2a.grpc.ListTasksResponse.Builder builder = io.a2a.grpc.ListTasksResponse.newBuilder(); + if (result.tasks() != null) { + builder.addAllTasks(result.tasks().stream().map(ToProto::task).collect(Collectors.toList())); + } + if (result.nextPageToken() != null) { + builder.setNextPageToken(result.nextPageToken()); + } + builder.setTotalSize(result.totalSize()); + return builder.build(); + } + public static io.a2a.grpc.Message message(Message message) { io.a2a.grpc.Message.Builder builder = io.a2a.grpc.Message.newBuilder(); builder.setMessageId(message.getMessageId()); @@ -308,7 +321,7 @@ private static io.a2a.grpc.TaskStatus taskStatus(TaskStatus taskStatus) { return builder.build(); } - private static io.a2a.grpc.TaskState taskState(TaskState taskState) { + public static io.a2a.grpc.TaskState taskState(TaskState taskState) { if (taskState == null) { return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; } @@ -723,6 +736,35 @@ public static TaskQueryParams taskQueryParams(io.a2a.grpc.GetTaskRequestOrBuilde return new TaskQueryParams(id, request.getHistoryLength()); } + public static io.a2a.spec.ListTasksParams listTasksParams(io.a2a.grpc.ListTasksRequestOrBuilder request) { + io.a2a.spec.ListTasksParams.Builder builder = new io.a2a.spec.ListTasksParams.Builder(); + if (!request.getContextId().isEmpty()) { + builder.contextId(request.getContextId()); + } + if (request.getStatus() != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED) { + builder.status(taskState(request.getStatus())); + } + if (request.getPageSize() > 0) { + builder.pageSize(request.getPageSize()); + } + if (!request.getPageToken().isEmpty()) { + builder.pageToken(request.getPageToken()); + } + if (request.getHistoryLength() > 0) { + builder.historyLength(request.getHistoryLength()); + } + if (request.hasLastUpdatedTime()) { + Instant instant = Instant.ofEpochSecond( + request.getLastUpdatedTime().getSeconds(), + request.getLastUpdatedTime().getNanos()); + builder.lastUpdatedAfter(instant); + } + if (request.getIncludeArtifacts()) { + builder.includeArtifacts(true); + } + return builder.build(); + } + public static TaskIdParams taskIdParams(io.a2a.grpc.CancelTaskRequestOrBuilder request) { String name = request.getName(); String id = name.substring(name.lastIndexOf('/') + 1); diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java index 2af7d0513..c3affe4ee 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java @@ -76,6 +76,7 @@ protected Object getIdIfPossible(JsonNode treeNode, JsonParser jsonParser) { protected static boolean isValidMethodName(String methodName) { return methodName != null && (methodName.equals(CancelTaskRequest.METHOD) || methodName.equals(GetTaskRequest.METHOD) + || methodName.equals(ListTasksRequest.METHOD) || methodName.equals(GetTaskPushNotificationConfigRequest.METHOD) || methodName.equals(SetTaskPushNotificationConfigRequest.METHOD) || methodName.equals(TaskResubscriptionRequest.METHOD) diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index be67a1e24..04f6cf4e8 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -12,10 +12,7 @@ */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits SendStreamingMessageResponse, - GetTaskResponse, CancelTaskResponse, SetTaskPushNotificationConfigResponse, GetTaskPushNotificationConfigResponse, - SendMessageResponse, DeleteTaskPushNotificationConfigResponse, ListTaskPushNotificationConfigResponse, JSONRPCErrorResponse, - GetAuthenticatedExtendedCardResponse { +public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { protected String jsonrpc; protected Object id; diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java new file mode 100644 index 000000000..beda51345 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -0,0 +1,145 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import org.jspecify.annotations.Nullable; + +import java.time.Instant; +import java.util.Map; + +/** + * Parameters for listing tasks with optional filtering and pagination. + * + * @param contextId Filter tasks by context ID to get tasks from a specific conversation or session + * @param status Filter tasks by their current status state + * @param pageSize Maximum number of tasks to return (1-100, defaults to 50) + * @param pageToken Token for pagination from a previous ListTasksResult + * @param historyLength Number of recent messages to include in each task's history (defaults to 0) + * @param lastUpdatedAfter Filter tasks updated after this timestamp + * @param includeArtifacts Whether to include artifacts in the returned tasks (defaults to false) + * @param metadata Additional filter properties + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListTasksParams( + @Nullable String contextId, + @Nullable TaskState status, + @Nullable Integer pageSize, + @Nullable String pageToken, + @Nullable Integer historyLength, + @Nullable Instant lastUpdatedAfter, + @Nullable Boolean includeArtifacts, + @Nullable Map metadata +) { + /** + * Default constructor for listing all tasks. + */ + public ListTasksParams() { + this(null, null, null, null, null, null, null, null); + } + + /** + * Constructor with pagination. + * + * @param pageSize Maximum number of tasks to return + * @param pageToken Token for pagination + */ + public ListTasksParams(Integer pageSize, String pageToken) { + this(null, null, pageSize, pageToken, null, null, null, null); + } + + /** + * Validates and returns the effective page size (between 1 and 100, defaults to 50). + * + * @return the effective page size + */ + public int getEffectivePageSize() { + if (pageSize == null) { + return 50; + } + if (pageSize < 1) { + return 1; + } + if (pageSize > 100) { + return 100; + } + return pageSize; + } + + /** + * Returns the effective history length (non-negative, defaults to 0). + * + * @return the effective history length + */ + public int getEffectiveHistoryLength() { + if (historyLength == null || historyLength < 0) { + return 0; + } + return historyLength; + } + + /** + * Returns whether to include artifacts (defaults to false). + * + * @return true if artifacts should be included + */ + public boolean shouldIncludeArtifacts() { + return includeArtifacts != null && includeArtifacts; + } + + public static class Builder { + private String contextId; + private TaskState status; + private Integer pageSize; + private String pageToken; + private Integer historyLength; + private Instant lastUpdatedAfter; + private Boolean includeArtifacts; + private Map metadata; + + public Builder contextId(String contextId) { + this.contextId = contextId; + return this; + } + + public Builder status(TaskState status) { + this.status = status; + return this; + } + + public Builder pageSize(Integer pageSize) { + this.pageSize = pageSize; + return this; + } + + public Builder pageToken(String pageToken) { + this.pageToken = pageToken; + return this; + } + + public Builder historyLength(Integer historyLength) { + this.historyLength = historyLength; + return this; + } + + public Builder lastUpdatedAfter(Instant lastUpdatedAfter) { + this.lastUpdatedAfter = lastUpdatedAfter; + return this; + } + + public Builder includeArtifacts(Boolean includeArtifacts) { + this.includeArtifacts = includeArtifacts; + return this; + } + + public Builder metadata(Map metadata) { + this.metadata = metadata; + return this; + } + + public ListTasksParams build() { + return new ListTasksParams(contextId, status, pageSize, pageToken, historyLength, + lastUpdatedAfter, includeArtifacts, metadata); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java new file mode 100644 index 000000000..f3b7fdf94 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java @@ -0,0 +1,81 @@ +package io.a2a.spec; + +import static io.a2a.util.Utils.defaultIfNull; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.a2a.util.Assert; + +/** + * A list tasks request. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ListTasksRequest extends NonStreamingJSONRPCRequest { + + public static final String METHOD = "tasks/list"; + + @JsonCreator + public ListTasksRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("method") String method, @JsonProperty("params") ListTasksParams params) { + if (jsonrpc == null || jsonrpc.isEmpty()) { + throw new IllegalArgumentException("JSON-RPC protocol version cannot be null or empty"); + } + if (jsonrpc != null && !jsonrpc.equals(JSONRPC_VERSION)) { + throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); + } + Assert.checkNotNullParam("method", method); + if (!method.equals(METHOD)) { + throw new IllegalArgumentException("Invalid ListTasksRequest method"); + } + Assert.checkNotNullParam("params", params); + Assert.isNullOrStringOrInteger(id); + this.jsonrpc = defaultIfNull(jsonrpc, JSONRPC_VERSION); + this.id = id; + this.method = method; + this.params = params; + } + + public ListTasksRequest(Object id, ListTasksParams params) { + this(JSONRPC_VERSION, id, METHOD, params); + } + + public static class Builder { + private String jsonrpc; + private Object id; + private String method; + private ListTasksParams params; + + public Builder jsonrpc(String jsonrpc) { + this.jsonrpc = jsonrpc; + return this; + } + + public Builder id(Object id) { + this.id = id; + return this; + } + + public Builder method(String method) { + this.method = method; + return this; + } + + public Builder params(ListTasksParams params) { + this.params = params; + return this; + } + + public ListTasksRequest build() { + if (id == null) { + id = UUID.randomUUID().toString(); + } + return new ListTasksRequest(jsonrpc, id, method, params); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResponse.java b/spec/src/main/java/io/a2a/spec/ListTasksResponse.java new file mode 100644 index 000000000..d6b6d4e81 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTasksResponse.java @@ -0,0 +1,28 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * The response for a list tasks request. + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public final class ListTasksResponse extends JSONRPCResponse { + + @JsonCreator + public ListTasksResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + @JsonProperty("result") ListTasksResult result, @JsonProperty("error") JSONRPCError error) { + super(jsonrpc, id, result, error, ListTasksResult.class); + } + + public ListTasksResponse(Object id, ListTasksResult result) { + this(null, id, result, null); + } + + public ListTasksResponse(Object id, JSONRPCError error) { + this(null, id, null, error); + } +} diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResult.java b/spec/src/main/java/io/a2a/spec/ListTasksResult.java new file mode 100644 index 000000000..3a02cd2f4 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTasksResult.java @@ -0,0 +1,89 @@ +package io.a2a.spec; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.a2a.util.Assert; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +/** + * Result of a list tasks request containing matching tasks and pagination information. + * + * @param tasks Array of tasks matching the specified criteria + * @param totalSize Total number of tasks available (before pagination) + * @param pageSize Number of tasks returned in this response + * @param nextPageToken Token for retrieving the next page of results (null if no more results) + */ +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ListTasksResult( + List tasks, + int totalSize, + int pageSize, + @Nullable String nextPageToken +) { + public ListTasksResult { + Assert.checkNotNullParam("tasks", tasks); + if (totalSize < 0) { + throw new IllegalArgumentException("totalSize must be non-negative"); + } + if (pageSize < 0) { + throw new IllegalArgumentException("pageSize must be non-negative"); + } + if (pageSize != tasks.size()) { + throw new IllegalArgumentException("pageSize must be equal to the number of tasks in the list. Got " + pageSize + ", but list has " + tasks.size() + " tasks."); + } + // Make defensive copy + tasks = List.copyOf(tasks); + } + + /** + * Constructor for results without pagination. + * + * @param tasks the list of tasks + */ + public ListTasksResult(List tasks) { + this(tasks, tasks.size(), tasks.size(), null); + } + + /** + * Returns whether there are more results available. + * + * @return true if there are more pages of results + */ + public boolean hasMoreResults() { + return nextPageToken != null && !nextPageToken.isEmpty(); + } + + public static class Builder { + private List tasks; + private int totalSize; + private int pageSize; + private String nextPageToken; + + public Builder tasks(List tasks) { + this.tasks = tasks; + return this; + } + + public Builder totalSize(int totalSize) { + this.totalSize = totalSize; + return this; + } + + public Builder pageSize(int pageSize) { + this.pageSize = pageSize; + return this; + } + + public Builder nextPageToken(String nextPageToken) { + this.nextPageToken = nextPageToken; + return this; + } + + public ListTasksResult build() { + return new ListTasksResult(tasks, totalSize, pageSize, nextPageToken); + } + } +} diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java index f3ac7d2cb..41302136c 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java @@ -13,5 +13,5 @@ public abstract sealed class NonStreamingJSONRPCRequest extends JSONRPCRequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest, - GetAuthenticatedExtendedCardRequest { + GetAuthenticatedExtendedCardRequest, ListTasksRequest { } diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java index 37366b9b7..44d7a3d58 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java @@ -33,6 +33,9 @@ public NonStreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserial case CancelTaskRequest.METHOD: return new CancelTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); + case ListTasksRequest.METHOD: + return new ListTasksRequest(jsonrpc, id, method, + getAndValidateParams(paramsNode, jsonParser, treeNode, ListTasksParams.class)); case SetTaskPushNotificationConfigRequest.METHOD: return new SetTaskPushNotificationConfigRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskPushNotificationConfig.class)); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 7fcf32cb4..3a707876b 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -227,6 +227,220 @@ public void testCancelTaskNotFound() { } } + @Test + public void testListTasksSuccess() throws Exception { + // Create multiple tasks with different contexts and states + Task task1 = new Task.Builder() + .id("list-task-1") + .contextId("context-1") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + Task task2 = new Task.Builder() + .id("list-task-2") + .contextId("context-1") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + Task task3 = new Task.Builder() + .id("list-task-3") + .contextId("context-2") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + saveTaskInTaskStore(task1); + saveTaskInTaskStore(task2); + saveTaskInTaskStore(task3); + + try { + // Test listing all tasks (no filters) + io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder().build(); + io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + + assertNotNull(result); + assertNotNull(result.tasks()); + assertTrue(result.tasks().size() >= 3, "Should have at least 3 tasks"); + assertEquals(result.tasks().size(), result.pageSize()); + assertTrue(result.totalSize() >= 3, "Total size should be at least 3"); + } finally { + deleteTaskInTaskStore(task1.getId()); + deleteTaskInTaskStore(task2.getId()); + deleteTaskInTaskStore(task3.getId()); + } + } + + @Test + public void testListTasksFilterByContextId() throws Exception { + Task task1 = new Task.Builder() + .id("list-task-ctx-1") + .contextId("context-filter-1") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + Task task2 = new Task.Builder() + .id("list-task-ctx-2") + .contextId("context-filter-1") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + Task task3 = new Task.Builder() + .id("list-task-ctx-3") + .contextId("context-filter-2") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + saveTaskInTaskStore(task1); + saveTaskInTaskStore(task2); + saveTaskInTaskStore(task3); + + try { + // Filter by contextId + io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder() + .contextId("context-filter-1") + .build(); + io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + + assertNotNull(result); + assertNotNull(result.tasks()); + assertEquals(2, result.tasks().size(), "Should have exactly 2 tasks with context-filter-1"); + assertTrue(result.tasks().stream().allMatch(t -> "context-filter-1".equals(t.getContextId()))); + } finally { + deleteTaskInTaskStore(task1.getId()); + deleteTaskInTaskStore(task2.getId()); + deleteTaskInTaskStore(task3.getId()); + } + } + + @Test + public void testListTasksFilterByStatus() throws Exception { + Task task1 = new Task.Builder() + .id("list-task-status-1") + .contextId("context-status") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + Task task2 = new Task.Builder() + .id("list-task-status-2") + .contextId("context-status") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + Task task3 = new Task.Builder() + .id("list-task-status-3") + .contextId("context-status") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + saveTaskInTaskStore(task1); + saveTaskInTaskStore(task2); + saveTaskInTaskStore(task3); + + try { + // Filter by status WORKING + io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder() + .status(TaskState.WORKING) + .build(); + io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + + assertNotNull(result); + assertNotNull(result.tasks()); + assertTrue(result.tasks().size() >= 2, "Should have at least 2 WORKING tasks"); + assertTrue(result.tasks().stream() + .filter(t -> t.getId().startsWith("list-task-status-")) + .allMatch(t -> TaskState.WORKING.equals(t.getStatus().state()))); + } finally { + deleteTaskInTaskStore(task1.getId()); + deleteTaskInTaskStore(task2.getId()); + deleteTaskInTaskStore(task3.getId()); + } + } + + @Test + public void testListTasksWithPagination() throws Exception { + // Create several tasks + Task task1 = new Task.Builder() + .id("page-task-1") + .contextId("page-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + Task task2 = new Task.Builder() + .id("page-task-2") + .contextId("page-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + Task task3 = new Task.Builder() + .id("page-task-3") + .contextId("page-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + saveTaskInTaskStore(task1); + saveTaskInTaskStore(task2); + saveTaskInTaskStore(task3); + + try { + // Get first page with pageSize=2 + io.a2a.spec.ListTasksParams params1 = new io.a2a.spec.ListTasksParams.Builder() + .contextId("page-context") + .pageSize(2) + .build(); + io.a2a.spec.ListTasksResult result1 = getClient().listTasks(params1); + + assertNotNull(result1); + assertEquals(2, result1.tasks().size(), "First page should have 2 tasks"); + assertNotNull(result1.nextPageToken(), "Should have next page token"); + assertTrue(result1.hasMoreResults()); + + // Get second page using pageToken + io.a2a.spec.ListTasksParams params2 = new io.a2a.spec.ListTasksParams.Builder() + .contextId("page-context") + .pageSize(2) + .pageToken(result1.nextPageToken()) + .build(); + io.a2a.spec.ListTasksResult result2 = getClient().listTasks(params2); + + assertNotNull(result2); + assertTrue(result2.tasks().size() >= 1, "Second page should have at least 1 task"); + } finally { + deleteTaskInTaskStore(task1.getId()); + deleteTaskInTaskStore(task2.getId()); + deleteTaskInTaskStore(task3.getId()); + } + } + + @Test + public void testListTasksWithHistoryLimit() throws Exception { + // Create task with multiple history messages + List history = List.of( + new Message.Builder(MESSAGE).messageId("msg-1").build(), + new Message.Builder(MESSAGE).messageId("msg-2").build(), + new Message.Builder(MESSAGE).messageId("msg-3").build(), + new Message.Builder(MESSAGE).messageId("msg-4").build() + ); + Task taskWithHistory = new Task.Builder() + .id("list-task-history") + .contextId("context-history") + .status(new TaskStatus(TaskState.WORKING)) + .history(history) + .build(); + + saveTaskInTaskStore(taskWithHistory); + + try { + // List with history limited to 2 messages + io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder() + .contextId("context-history") + .historyLength(2) + .build(); + io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + + assertNotNull(result); + assertEquals(1, result.tasks().size()); + Task task = result.tasks().get(0); + assertNotNull(task.getHistory()); + assertEquals(2, task.getHistory().size(), "History should be limited to 2 most recent messages"); + // Verify we get the most recent messages (msg-3 and msg-4) + assertEquals("msg-3", task.getHistory().get(0).getMessageId()); + assertEquals("msg-4", task.getHistory().get(1).getMessageId()); + } finally { + deleteTaskInTaskStore(taskWithHistory.getId()); + } + } + @Test public void testSendMessageNewMessageSuccess() throws Exception { assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 81701c3c1..c5174c4ad 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -111,6 +111,24 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, } } + @Override + public void listTasks(io.a2a.grpc.ListTasksRequest request, + StreamObserver responseObserver) { + try { + ServerCallContext context = createCallContext(responseObserver); + io.a2a.spec.ListTasksParams params = FromProto.listTasksParams(request); + io.a2a.spec.ListTasksResult result = getRequestHandler().onListTasks(params, context); + responseObserver.onNext(ToProto.listTasksResult(result)); + responseObserver.onCompleted(); + } catch (JSONRPCError e) { + handleError(responseObserver, e); + } catch (SecurityException e) { + handleSecurityException(responseObserver, e); + } catch (Throwable t) { + handleInternalError(responseObserver, t); + } + } + @Override public void cancelTask(io.a2a.grpc.CancelTaskRequest request, StreamObserver responseObserver) { diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 68c7e8856..058ebc5f9 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -33,6 +33,9 @@ import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.ListTasksRequest; +import io.a2a.spec.ListTasksResponse; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.PushNotificationNotSupportedError; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; @@ -188,6 +191,17 @@ public GetTaskResponse onGetTask(GetTaskRequest request, ServerCallContext conte } } + public ListTasksResponse onListTasks(ListTasksRequest request, ServerCallContext context) { + try { + ListTasksResult result = requestHandler.onListTasks(request.getParams(), context); + return new ListTasksResponse(request.getId(), result); + } catch (JSONRPCError e) { + return new ListTasksResponse(request.getId(), e); + } catch (Throwable t) { + return new ListTasksResponse(request.getId(), new InternalError(t.getMessage())); + } + } + public ListTaskPushNotificationConfigResponse listPushNotificationConfig( ListTaskPushNotificationConfigRequest request, ServerCallContext context) { if ( !agentCard.capabilities().pushNotifications()) { diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 85d307f53..398a85710 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -30,6 +30,8 @@ import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.PushNotificationNotSupportedError; import io.a2a.spec.StreamingEventKind; @@ -39,6 +41,7 @@ import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; +import io.a2a.spec.TaskState; import io.a2a.spec.UnsupportedOperationError; import io.a2a.server.util.async.Internal; import io.a2a.util.Utils; @@ -177,6 +180,42 @@ public HTTPRestResponse getTask(String taskId, int historyLength, ServerCallCont } } + public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String status, + @Nullable Integer pageSize, @Nullable String pageToken, + @Nullable Integer historyLength, @Nullable Boolean includeArtifacts, + ServerCallContext context) { + try { + // Build params + ListTasksParams.Builder paramsBuilder = new ListTasksParams.Builder(); + if (contextId != null) { + paramsBuilder.contextId(contextId); + } + if (status != null) { + paramsBuilder.status(TaskState.valueOf(status)); + } + if (pageSize != null) { + paramsBuilder.pageSize(pageSize); + } + if (pageToken != null) { + paramsBuilder.pageToken(pageToken); + } + if (historyLength != null) { + paramsBuilder.historyLength(historyLength); + } + if (includeArtifacts != null) { + paramsBuilder.includeArtifacts(includeArtifacts); + } + ListTasksParams params = paramsBuilder.build(); + + ListTasksResult result = requestHandler.onListTasks(params, context); + return createSuccessResponse(200, io.a2a.grpc.ListTasksResponse.newBuilder(ProtoUtils.ToProto.listTasksResult(result))); + } catch (JSONRPCError e) { + return createErrorResponse(e); + } catch (Throwable throwable) { + return createErrorResponse(new InternalError(throwable.getMessage())); + } + } + public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nullable String configId, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { From 4c71032258ea1d3568a3f630fa8f48fb66076574 Mon Sep 17 00:00:00 2001 From: Robby He <448523760@qq.com> Date: Wed, 12 Nov 2025 18:32:03 +0800 Subject: [PATCH 202/493] fix: Update A2A Protocol link in README.md (#454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Robby He (贺俊飞) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f5228e0c0..249a79b88 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

A2A Logo

-

A Java library that helps run agentic applications as A2AServers following the Agent2Agent (A2A) Protocol.

+

A Java library that helps run agentic applications as A2AServers following the Agent2Agent (A2A) Protocol.

## Installation @@ -34,7 +34,7 @@ More examples will be added soon. ## A2A Server -The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A). To run your agentic Java application as an A2A server, simply follow the steps below. +The A2A Java SDK provides a Java server implementation of the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/). To run your agentic Java application as an A2A server, simply follow the steps below. - [Add an A2A Java SDK Server Maven dependency to your project](#1-add-an-a2a-java-sdk-server-maven-dependency-to-your-project) - [Add a class that creates an A2A Agent Card](#2-add-a-class-that-creates-an-a2a-agent-card) @@ -266,7 +266,7 @@ a2a.executor.keep-alive-seconds=60 ## A2A Client -The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://google-a2a.github.io/A2A), allowing communication with A2A servers. The Java client implementation supports the following transports: +The A2A Java SDK provides a Java client implementation of the [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/), allowing communication with A2A servers. The Java client implementation supports the following transports: * JSON-RPC 2.0 * gRPC From 937290a6f946e2d0d559f1faeb8809d5361681c1 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 18 Nov 2025 10:21:50 +0000 Subject: [PATCH 203/493] fix: Improve Kubernetes Example reliability on CI (#435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - exit early once we have the two required pods - increased count until we expect 2 pods Fixes #396 🦕 --- .../workflows/cloud-deployment-example.yml | 3 +- .../examples/cloud/A2ACloudExampleClient.java | 57 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/.github/workflows/cloud-deployment-example.yml b/.github/workflows/cloud-deployment-example.yml index f52ea5111..5c4e1e01a 100644 --- a/.github/workflows/cloud-deployment-example.yml +++ b/.github/workflows/cloud-deployment-example.yml @@ -58,7 +58,8 @@ jobs: mvn test-compile exec:java \ -Dexec.mainClass="io.a2a.examples.cloud.A2ACloudExampleClient" \ -Dexec.classpathScope=test \ - -Dagent.url=http://localhost:8080 + -Dagent.url=http://localhost:8080 \ + -Dci.mode=true - name: Show diagnostics on failure if: failure() diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java index d894d9f10..d84d24896 100644 --- a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -1,5 +1,15 @@ package io.a2a.examples.cloud; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + import io.a2a.A2A; import io.a2a.client.Client; import io.a2a.client.ClientEvent; @@ -9,26 +19,13 @@ import io.a2a.client.config.ClientConfig; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; -import io.a2a.spec.A2AClientError; -import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.Part; -import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TextPart; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - /** * Test client demonstrating multi-pod A2A agent deployment with modernized message protocol. *

@@ -50,8 +47,11 @@ public class A2ACloudExampleClient { private static final String AGENT_URL = System.getProperty("agent.url", "http://localhost:8080"); - private static final int PROCESS_MESSAGE_COUNT = 8; // Number of "process" messages to send - private static final int MESSAGE_INTERVAL_MS = 1500; + private static final int PROCESS_MESSAGE_COUNT = Integer.parseInt(System.getProperty("process.message.count", "8")); // Number of "process" messages to send + private static final int MESSAGE_INTERVAL_MS = Integer.parseInt(System.getProperty("message.interval.ms", "1500")); + private static final boolean CI_MODE = Boolean.parseBoolean(System.getProperty("ci.mode", "false")); // Early exit when 2 pods observed + private static final int MAX_MESSAGES_UNTIL_TWO_PODS = Integer.parseInt(System.getProperty("max.messages.until.two.pods", "20")); // Max messages before giving up + private static final int MIN_PODS_TO_OBSERVE = 2; // Test state private final Map observedPods = Collections.synchronizedMap(new HashMap<>()); @@ -253,11 +253,19 @@ private void handleSubscriptionError(Throwable error) { private void sendProcessMessages() throws InterruptedException { System.out.println(); - System.out.println("Step 3: Sending " + PROCESS_MESSAGE_COUNT + " 'process' messages (interval: " + MESSAGE_INTERVAL_MS + "ms)..."); + if (CI_MODE) { + System.out.println("Step 3: Sending 'process' messages until 2 pods observed (CI mode, max: " + MAX_MESSAGES_UNTIL_TWO_PODS + ", interval: " + MESSAGE_INTERVAL_MS + "ms)..."); + } else { + System.out.println("Step 3: Sending " + PROCESS_MESSAGE_COUNT + " 'process' messages (interval: " + MESSAGE_INTERVAL_MS + "ms)..."); + } System.out.println("--------------------------------------------"); - for (int i = 1; i <= PROCESS_MESSAGE_COUNT; i++) { - final int messageNum = i; + int messageCount = 0; + int maxMessages = CI_MODE ? MAX_MESSAGES_UNTIL_TWO_PODS : PROCESS_MESSAGE_COUNT; + + while (messageCount < maxMessages) { + messageCount++; + final int messageNum = messageCount; // Create a new client for each request to force new HTTP connection Client freshClient = Client.builder(streamingClient.getAgentCard()) @@ -282,8 +290,15 @@ private void sendProcessMessages() throws InterruptedException { }); Thread.sleep(MESSAGE_INTERVAL_MS); + + // In CI mode, check if we've observed 2 pods and can exit early + if (CI_MODE && observedPods.size() >= 2) { + System.out.println(); + System.out.println("✓ CI mode: Successfully observed 2 pods after " + messageNum + " messages. Stopping early."); + break; + } } catch (Exception e) { - System.err.println("✗ Failed to send process message " + i + ": " + e.getMessage()); + System.err.println("✗ Failed to send process message " + messageNum + ": " + e.getMessage()); testFailed.set(true); } } @@ -345,8 +360,8 @@ private void printResults() { if (testFailed.get()) { System.out.println("✗ TEST FAILED - Errors occurred during execution"); System.exit(1); - } else if (observedPods.size() < 2) { - System.out.println("✗ TEST FAILED - Expected at least 2 different pods, but only saw: " + observedPods.size()); + } else if (observedPods.size() < MIN_PODS_TO_OBSERVE) { + System.out.printf("✗ TEST FAILED - Expected at least %d different pods, but only saw: %d\n", MIN_PODS_TO_OBSERVE, observedPods.size()); System.out.println(" This suggests load balancing is not working correctly."); System.exit(1); } else { From 22f5b1df408e482aa7c9570667a8d71c7e58d95a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 18 Nov 2025 15:25:22 +0000 Subject: [PATCH 204/493] feat: Introduce BOMs (#462) There are boms for: * the core sdk classes * the reference classes (includes core sdk) * the extras classes (includes core sdk) Fixes #461 --- boms/README.md | 230 ++++++++++++++++ boms/extras/pom.xml | 105 +++++++ .../it/extras-usage-test/invoker.properties | 2 + boms/extras/src/it/extras-usage-test/pom.xml | 134 +++++++++ .../java/io/a2a/test/ExtrasBomVerifier.java | 35 +++ boms/extras/src/it/settings.xml | 37 +++ boms/reference/pom.xml | 116 ++++++++ .../reference-usage-test/invoker.properties | 2 + .../src/it/reference-usage-test/pom.xml | 131 +++++++++ .../io/a2a/test/ReferenceBomVerifier.java | 33 +++ boms/reference/src/it/settings.xml | 37 +++ boms/sdk/pom.xml | 260 ++++++++++++++++++ .../src/it/sdk-usage-test/invoker.properties | 8 + boms/sdk/src/it/sdk-usage-test/pom.xml | 154 +++++++++++ .../main/java/io/a2a/test/SdkBomVerifier.java | 33 +++ boms/sdk/src/it/settings.xml | 37 +++ boms/test-utils/pom.xml | 35 +++ .../io/a2a/bom/test/DynamicBomVerifier.java | 209 ++++++++++++++ pom.xml | 9 + 19 files changed, 1607 insertions(+) create mode 100644 boms/README.md create mode 100644 boms/extras/pom.xml create mode 100644 boms/extras/src/it/extras-usage-test/invoker.properties create mode 100644 boms/extras/src/it/extras-usage-test/pom.xml create mode 100644 boms/extras/src/it/extras-usage-test/src/main/java/io/a2a/test/ExtrasBomVerifier.java create mode 100644 boms/extras/src/it/settings.xml create mode 100644 boms/reference/pom.xml create mode 100644 boms/reference/src/it/reference-usage-test/invoker.properties create mode 100644 boms/reference/src/it/reference-usage-test/pom.xml create mode 100644 boms/reference/src/it/reference-usage-test/src/main/java/io/a2a/test/ReferenceBomVerifier.java create mode 100644 boms/reference/src/it/settings.xml create mode 100644 boms/sdk/pom.xml create mode 100644 boms/sdk/src/it/sdk-usage-test/invoker.properties create mode 100644 boms/sdk/src/it/sdk-usage-test/pom.xml create mode 100644 boms/sdk/src/it/sdk-usage-test/src/main/java/io/a2a/test/SdkBomVerifier.java create mode 100644 boms/sdk/src/it/settings.xml create mode 100644 boms/test-utils/pom.xml create mode 100644 boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java diff --git a/boms/README.md b/boms/README.md new file mode 100644 index 000000000..79b2639c9 --- /dev/null +++ b/boms/README.md @@ -0,0 +1,230 @@ +# A2A Java SDK - Bill of Materials (BOM) + +This directory contains Bill of Materials (BOM) modules for the A2A Java SDK project, providing dependency management for external users. + +## Overview + +The A2A Java SDK provides three BOMs for different use cases: + +1. **`a2a-java-sdk-bom`** - Core SDK BOM for general A2A agent development +2. **`a2a-java-sdk-extras-bom`** - Extras BOM with server-side enhancements (task stores, queue managers) +3. **`a2a-java-sdk-reference-bom`** - Reference implementations BOM with Quarkus dependencies + +## BOM Modules + +### SDK BOM (`boms/sdk`) + +**Artifact:** `io.github.a2asdk:a2a-java-sdk-bom` + +The SDK BOM includes: +- All A2A SDK core modules (spec, server, client, transport) +- Core third-party dependencies (Jackson, gRPC, SLF4J) +- Jakarta APIs (CDI, Inject, JSON, JAX-RS) +- Test utilities + +**Use this BOM when:** Building A2A agents with any framework (Quarkus, Spring Boot, vanilla Java, etc.) + +### Extras BOM (`boms/extras`) + +**Artifact:** `io.github.a2asdk:a2a-java-sdk-extras-bom` + +The Extras BOM includes: +- Everything from `a2a-java-sdk-bom` (via import) +- Server-side enhancement modules (database persistence, distributed queue management, etc.) + +**Use this BOM when:** Building production A2A servers needing advanced server-side features beyond the core SDK + +### Reference BOM (`boms/reference`) + +**Artifact:** `io.github.a2asdk:a2a-java-sdk-reference-bom` + +The Reference BOM includes: +- Everything from `a2a-java-sdk-bom` (via import) +- Quarkus BOM (complete Quarkus platform) +- A2A reference implementation modules (JSON-RPC, gRPC, REST) +- TCK module for testing + +**Use this BOM when:** Building Quarkus-based A2A agents or reference implementations + +## Usage + +### For SDK Users (Any Framework) + +Add to your project's `pom.xml`: + +```xml + + + + io.github.a2asdk + a2a-java-sdk-bom + ${io.a2a.sdk.version} + pom + import + + + + + + + + io.github.a2asdk + a2a-java-sdk-server-common + + + io.github.a2asdk + a2a-java-sdk-transport-jsonrpc + + +``` + +### For Extras Users (Database Persistence, Distributed Deployments) + +Add to your project's `pom.xml`: + +```xml + + + + io.github.a2asdk + a2a-java-sdk-extras-bom + ${io.a2a.sdk.version} + pom + import + + + + + + + + io.github.a2asdk + a2a-java-sdk-server-common + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + + +``` + +### For Quarkus Reference Implementation Users + +Add to your project's `pom.xml`: + +```xml + + + + io.github.a2asdk + a2a-java-sdk-reference-bom + ${io.a2a.sdk.version} + pom + import + + + + + + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + + + io.quarkus + quarkus-arc + + +``` + +## Internal Project Usage + +**Important:** The A2A Java SDK project itself does **NOT** import these BOMs in the parent `pom.xml`. The BOMs are +for the convenience of external users. + +### Maintenance Strategy + +- Parent `pom.xml` `` is the **single source of truth** for versions +- BOMs are **maintained to match** parent pom versions +- When updating dependencies, update **both** parent pom and relevant BOMs +- BOMs are separate artifacts for **external consumption only** + +## Automated Testing + +All three BOMs include **maven-invoker-plugin** integration tests that automatically verify: +- ✅ BOM can be imported correctly +- ✅ All declared dependencies resolve +- ✅ No missing versions or conflicts +- ✅ Code using the BOM compiles successfully + +### Test Structure + +``` +boms/ +├── sdk/ +│ └── src/it/ +│ ├── settings.xml # Test repository config +│ └── sdk-usage-test/ # Integration test project +│ ├── pom.xml # Imports SDK BOM +│ └── src/main/java/ # Test code using SDK +├── extras/ +│ └── src/it/ +│ └── extras-usage-test/ # Integration test project +│ ├── pom.xml # Imports Extras BOM +│ └── src/main/java/ # Test code using SDK + Extras +└── reference/ + └── src/it/ + └── reference-usage-test/ # Integration test project + ├── pom.xml # Imports Reference BOM + └── src/main/java/ # Test code using Quarkus + SDK +``` + +### Running Tests + +Tests run automatically during `mvn install`: + +```bash +# Test all BOMs +mvn clean install -DskipTests -pl boms/sdk,boms/extras,boms/reference + +# Test individual BOM +mvn clean install -DskipTests -pl boms/sdk +``` + +**What happens:** +1. BOM is installed to `target/local-repo/` +2. Test project builds using the BOM +3. If compilation succeeds → BOM is valid ✅ +4. If dependencies missing → Build fails ❌ + +## Maintenance Guidelines + +When updating dependencies in the project: + +1. **Update parent `pom.xml`** - Change version properties and dependencyManagement +2. **Update SDK BOM** (`boms/sdk/pom.xml`) - Sync core dependency versions +3. **Update Extras BOM** (`boms/extras/pom.xml`) - Sync if extras modules changed +4. **Update Reference BOM** (`boms/reference/pom.xml`) - Sync if Quarkus or reference modules changed +5. **Run automated tests** - Integration tests will catch any missing dependencies: + ```bash + mvn clean install -DskipTests -pl boms/sdk,boms/extras,boms/reference + ``` + +### Adding New Dependencies to BOMs + +When adding new SDK modules or dependencies: + +1. Add to appropriate BOM's `` +2. Add usage example in `src/it/*/src/main/java/` test code +3. Run tests to verify compilation +4. Tests will fail if versions are missing or incorrect + +## Version Alignment + +The BOMs use `${project.version}` for all A2A SDK modules, ensuring: +- BOMs always reference the correct SDK version +- Version updates only need to change parent pom +- No version drift between BOMs and SDK modules + +Maven's reactor automatically orders them correctly based on their `` declarations, regardless of their position in the parent pom's `` section. \ No newline at end of file diff --git a/boms/extras/pom.xml b/boms/extras/pom.xml new file mode 100644 index 000000000..78f962fe6 --- /dev/null +++ b/boms/extras/pom.xml @@ -0,0 +1,105 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.4.0.Alpha1-SNAPSHOT + ../../pom.xml + + + a2a-java-sdk-extras-bom + pom + + A2A Java SDK - Extras BOM + Bill of Materials for A2A SDK Extras modules (task stores, queue managers, etc.) + + + + + + ${project.groupId} + a2a-java-sdk-bom + ${project.version} + pom + import + + + + + ${project.groupId} + a2a-java-extras-common + ${project.version} + + + ${project.groupId} + a2a-java-extras-task-store-database-jpa + ${project.version} + + + ${project.groupId} + a2a-java-extras-push-notification-config-store-database-jpa + ${project.version} + + + ${project.groupId} + a2a-java-queue-manager-replicated-core + ${project.version} + + + ${project.groupId} + a2a-java-queue-manager-replication-mp-reactive + ${project.version} + + + + + + + + ${project.groupId} + a2a-java-sdk-bom + ${project.version} + pom + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.8.0 + + ${project.build.directory}/it + ${project.build.directory}/local-repo + src/it/settings.xml + + clean + verify + + false + true + invoker.properties + + + io.github.a2asdk:a2a-java-bom-test-utils:${project.version}:jar + + + + + integration-test + + install + run + + + + + + + + diff --git a/boms/extras/src/it/extras-usage-test/invoker.properties b/boms/extras/src/it/extras-usage-test/invoker.properties new file mode 100644 index 000000000..ff2d26065 --- /dev/null +++ b/boms/extras/src/it/extras-usage-test/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals=clean verify +invoker.buildResult=success diff --git a/boms/extras/src/it/extras-usage-test/pom.xml b/boms/extras/src/it/extras-usage-test/pom.xml new file mode 100644 index 000000000..e794ec9ae --- /dev/null +++ b/boms/extras/src/it/extras-usage-test/pom.xml @@ -0,0 +1,134 @@ + + + 4.0.0 + + io.github.a2asdk.it + extras-bom-usage-test + 1.0-SNAPSHOT + jar + + Extras BOM Usage Test + Integration test to verify Extras BOM completeness + + + 17 + 17 + UTF-8 + + + + + + + io.github.a2asdk + a2a-java-sdk-extras-bom + @project.version@ + pom + import + + + + + + + + io.github.a2asdk + a2a-java-bom-test-utils + @project.version@ + + + + + io.github.a2asdk + a2a-java-extras-common + + + io.github.a2asdk + a2a-java-extras-task-store-database-jpa + + + io.github.a2asdk + a2a-java-extras-push-notification-config-store-database-jpa + + + io.github.a2asdk + a2a-java-queue-manager-replicated-core + + + io.github.a2asdk + a2a-java-queue-manager-replication-mp-reactive + + + + + io.github.a2asdk + a2a-java-sdk-spec + + + io.github.a2asdk + a2a-java-sdk-spec-grpc + + + io.github.a2asdk + a2a-java-sdk-server-common + + + io.github.a2asdk + a2a-java-sdk-client + + + io.github.a2asdk + a2a-java-sdk-client-transport-grpc + + + io.github.a2asdk + a2a-java-sdk-client-transport-jsonrpc + + + io.github.a2asdk + a2a-java-sdk-client-transport-rest + + + io.github.a2asdk + a2a-java-sdk-transport-jsonrpc + + + io.github.a2asdk + a2a-java-sdk-transport-grpc + + + io.github.a2asdk + a2a-java-sdk-transport-rest + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + verify-bom-completeness + verify + + java + + + io.a2a.test.ExtrasBomVerifier + + + + + + + diff --git a/boms/extras/src/it/extras-usage-test/src/main/java/io/a2a/test/ExtrasBomVerifier.java b/boms/extras/src/it/extras-usage-test/src/main/java/io/a2a/test/ExtrasBomVerifier.java new file mode 100644 index 000000000..9f7fd092f --- /dev/null +++ b/boms/extras/src/it/extras-usage-test/src/main/java/io/a2a/test/ExtrasBomVerifier.java @@ -0,0 +1,35 @@ +package io.a2a.test; + +import io.a2a.bom.test.DynamicBomVerifier; + +import java.util.Set; + +/** + * Verifies Extras BOM completeness by attempting to load all discovered classes. + * - Includes SDK modules + Extras modules (task stores, queue managers, etc.) + * - Forbids reference/ to prove BOM doesn't leak reference implementation dependencies + */ +public class ExtrasBomVerifier extends DynamicBomVerifier { + + private static final Set EXTRAS_EXCLUSIONS = Set.of( + "boms/", // BOM test modules themselves + "examples/", // Example applications + "tck/", // TCK test suite + "tests/", // Integration tests + "extras/queue-manager-replicated/tests-multi-instance/", // Test harness applications + "extras/queue-manager-replicated/tests-single-instance/" // Test harness applications + // Note: extras/ production modules are NOT in this list - we want to verify those classes load + ); + + private static final Set EXTRAS_FORBIDDEN = Set.of( + "reference/" // Reference implementations (separate BOM) - must NOT be loadable + ); + + public ExtrasBomVerifier() { + super(EXTRAS_EXCLUSIONS, EXTRAS_FORBIDDEN); + } + + public static void main(String[] args) throws Exception { + new ExtrasBomVerifier().verify(); + } +} diff --git a/boms/extras/src/it/settings.xml b/boms/extras/src/it/settings.xml new file mode 100644 index 000000000..c06ed6333 --- /dev/null +++ b/boms/extras/src/it/settings.xml @@ -0,0 +1,37 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/boms/reference/pom.xml b/boms/reference/pom.xml new file mode 100644 index 000000000..e143bd967 --- /dev/null +++ b/boms/reference/pom.xml @@ -0,0 +1,116 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.4.0.Alpha1-SNAPSHOT + ../../pom.xml + + + a2a-java-sdk-reference-bom + pom + + A2A Java SDK - Reference Implementations BOM + Bill of Materials (BOM) for A2A Java SDK reference implementations with Quarkus dependencies + + + + + + ${project.groupId} + a2a-java-sdk-bom + ${project.version} + pom + import + + + + + io.quarkus + quarkus-bom + ${quarkus.platform.version} + pom + import + + + + + ${project.groupId} + a2a-java-sdk-reference-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-reference-rest + ${project.version} + + + + + ${project.groupId} + a2a-java-sdk-tck + ${project.version} + + + + + + + + ${project.groupId} + a2a-java-sdk-bom + ${project.version} + pom + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.8.0 + + ${project.build.directory}/it + ${project.build.directory}/local-repo + src/it/settings.xml + + clean + verify + + false + true + invoker.properties + + + io.github.a2asdk:a2a-java-bom-test-utils:${project.version}:jar + + + + + integration-test + + install + run + + + + + + + + diff --git a/boms/reference/src/it/reference-usage-test/invoker.properties b/boms/reference/src/it/reference-usage-test/invoker.properties new file mode 100644 index 000000000..ff2d26065 --- /dev/null +++ b/boms/reference/src/it/reference-usage-test/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals=clean verify +invoker.buildResult=success diff --git a/boms/reference/src/it/reference-usage-test/pom.xml b/boms/reference/src/it/reference-usage-test/pom.xml new file mode 100644 index 000000000..c09a3cc3b --- /dev/null +++ b/boms/reference/src/it/reference-usage-test/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + + io.github.a2asdk.it + reference-bom-usage-test + 1.0-SNAPSHOT + jar + + Reference BOM Usage Test + Integration test to verify Reference BOM completeness including Quarkus + + + 17 + 17 + UTF-8 + + + + + + io.github.a2asdk + a2a-java-sdk-reference-bom + @project.version@ + pom + import + + + + + + + + io.github.a2asdk + a2a-java-bom-test-utils + @project.version@ + + + + + io.github.a2asdk + a2a-java-sdk-reference-common + + + io.github.a2asdk + a2a-java-sdk-reference-jsonrpc + + + io.github.a2asdk + a2a-java-sdk-reference-grpc + + + io.github.a2asdk + a2a-java-sdk-reference-rest + + + + + io.github.a2asdk + a2a-java-sdk-spec + + + io.github.a2asdk + a2a-java-sdk-spec-grpc + + + io.github.a2asdk + a2a-java-sdk-server-common + + + io.github.a2asdk + a2a-java-sdk-client + + + io.github.a2asdk + a2a-java-sdk-client-transport-grpc + + + io.github.a2asdk + a2a-java-sdk-client-transport-rest + + + io.github.a2asdk + a2a-java-sdk-transport-jsonrpc + + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-grpc + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + verify-bom-completeness + verify + + java + + + io.a2a.test.ReferenceBomVerifier + + + + + + + diff --git a/boms/reference/src/it/reference-usage-test/src/main/java/io/a2a/test/ReferenceBomVerifier.java b/boms/reference/src/it/reference-usage-test/src/main/java/io/a2a/test/ReferenceBomVerifier.java new file mode 100644 index 000000000..af73ded93 --- /dev/null +++ b/boms/reference/src/it/reference-usage-test/src/main/java/io/a2a/test/ReferenceBomVerifier.java @@ -0,0 +1,33 @@ +package io.a2a.test; + +import io.a2a.bom.test.DynamicBomVerifier; + +import java.util.Set; + +/** + * Verifies Reference BOM completeness by attempting to load all discovered classes. + * - Includes SDK modules + Reference implementation modules + * - Forbids extras/ to prove BOM doesn't leak extras dependencies + */ +public class ReferenceBomVerifier extends DynamicBomVerifier { + + private static final Set REFERENCE_EXCLUSIONS = Set.of( + "boms/", // BOM test modules themselves + "examples/", // Example applications + "tck/", // TCK test suite + "tests/" // Integration tests + // Note: reference/ is NOT in this list - we want to verify those classes load + ); + + private static final Set REFERENCE_FORBIDDEN = Set.of( + "extras/" // Extras modules (separate BOM) - must NOT be loadable + ); + + public ReferenceBomVerifier() { + super(REFERENCE_EXCLUSIONS, REFERENCE_FORBIDDEN); + } + + public static void main(String[] args) throws Exception { + new ReferenceBomVerifier().verify(); + } +} diff --git a/boms/reference/src/it/settings.xml b/boms/reference/src/it/settings.xml new file mode 100644 index 000000000..c06ed6333 --- /dev/null +++ b/boms/reference/src/it/settings.xml @@ -0,0 +1,37 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml new file mode 100644 index 000000000..9aed2b1d1 --- /dev/null +++ b/boms/sdk/pom.xml @@ -0,0 +1,260 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.4.0.Alpha1-SNAPSHOT + ../../pom.xml + + + a2a-java-sdk-bom + pom + + A2A Java SDK - BOM + Bill of Materials (BOM) for A2A Java SDK core modules and dependencies + + + + + + + + ${project.groupId} + a2a-java-sdk-spec + ${project.version} + + + ${project.groupId} + a2a-java-sdk-spec-grpc + ${project.version} + + + + + ${project.groupId} + a2a-java-sdk-common + ${project.version} + + + ${project.groupId} + a2a-java-sdk-http-client + ${project.version} + + + + + ${project.groupId} + a2a-java-sdk-server-common + ${project.version} + + + + + ${project.groupId} + a2a-java-sdk-client + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-spi + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-client-transport-rest + ${project.version} + + + + + ${project.groupId} + a2a-java-sdk-transport-grpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-jsonrpc + ${project.version} + + + ${project.groupId} + a2a-java-sdk-transport-rest + ${project.version} + + + + + ${project.groupId} + a2a-java-sdk-tests-server-common + ${project.version} + test + + + ${project.groupId} + a2a-java-sdk-tests-server-common + test-jar + test + ${project.version} + + + ${project.groupId} + a2a-java-sdk-server-common + test-jar + test + ${project.version} + + + + + io.grpc + grpc-bom + ${grpc.version} + pom + import + + + org.slf4j + slf4j-bom + ${slf4j.version} + pom + import + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + io.smallrye.reactive + mutiny-zero + ${mutiny-zero.version} + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + ${jakarta.enterprise.cdi-api.version} + + + jakarta.inject + jakarta.inject-api + ${jakarta.inject.jakarta.inject-api.version} + + + jakarta.json + jakarta.json-api + ${jakarta.json-api.version} + provided + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jakarta.ws.rs-api.version} + provided + + + + + org.jspecify + jspecify + 1.0.0 + provided + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + org.mockito + mockito-core + ${mockito-core.version} + test + + + org.mock-server + mockserver-netty + ${mockserver.version} + test + + + ch.qos.logback + logback-classic + ${logback.version} + test + + + + + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.8.0 + + ${project.build.directory}/it + ${project.build.directory}/local-repo + src/it/settings.xml + + clean + verify + + false + true + invoker.properties + + + io.github.a2asdk:a2a-java-bom-test-utils:${project.version}:jar + + + + + integration-test + + install + run + + + + + + + + diff --git a/boms/sdk/src/it/sdk-usage-test/invoker.properties b/boms/sdk/src/it/sdk-usage-test/invoker.properties new file mode 100644 index 000000000..dfd24e59e --- /dev/null +++ b/boms/sdk/src/it/sdk-usage-test/invoker.properties @@ -0,0 +1,8 @@ +# Integration test configuration for maven-invoker-plugin +# This test verifies that the SDK BOM can be imported and used successfully + +# Expected goals to succeed +invoker.goals=clean verify + +# This test should pass +invoker.buildResult=success diff --git a/boms/sdk/src/it/sdk-usage-test/pom.xml b/boms/sdk/src/it/sdk-usage-test/pom.xml new file mode 100644 index 000000000..c5bd4ff21 --- /dev/null +++ b/boms/sdk/src/it/sdk-usage-test/pom.xml @@ -0,0 +1,154 @@ + + + 4.0.0 + + io.github.a2asdk.it + sdk-bom-usage-test + 1.0-SNAPSHOT + jar + + SDK BOM Usage Test + Integration test to verify SDK BOM completeness + + + 17 + 17 + UTF-8 + + + + + + + io.github.a2asdk + a2a-java-sdk-bom + @project.version@ + pom + import + + + + + + + + io.github.a2asdk + a2a-java-bom-test-utils + @project.version@ + + + + + io.github.a2asdk + a2a-java-sdk-spec + + + io.github.a2asdk + a2a-java-sdk-spec-grpc + + + + + io.github.a2asdk + a2a-java-sdk-common + + + + + io.github.a2asdk + a2a-java-sdk-server-common + + + + + io.github.a2asdk + a2a-java-sdk-client + + + io.github.a2asdk + a2a-java-sdk-client-transport-spi + + + io.github.a2asdk + a2a-java-sdk-client-transport-jsonrpc + + + io.github.a2asdk + a2a-java-sdk-client-transport-grpc + + + io.github.a2asdk + a2a-java-sdk-client-transport-rest + + + + + io.github.a2asdk + a2a-java-sdk-transport-jsonrpc + + + io.github.a2asdk + a2a-java-sdk-transport-grpc + + + io.github.a2asdk + a2a-java-sdk-transport-rest + + + + + com.fasterxml.jackson.core + jackson-databind + + + org.slf4j + slf4j-api + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + io.smallrye.reactive + mutiny-zero + + + com.google.protobuf + protobuf-java + + + io.grpc + grpc-stub + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + verify-bom-completeness + verify + + java + + + io.a2a.test.SdkBomVerifier + + + + + + + diff --git a/boms/sdk/src/it/sdk-usage-test/src/main/java/io/a2a/test/SdkBomVerifier.java b/boms/sdk/src/it/sdk-usage-test/src/main/java/io/a2a/test/SdkBomVerifier.java new file mode 100644 index 000000000..036626c3f --- /dev/null +++ b/boms/sdk/src/it/sdk-usage-test/src/main/java/io/a2a/test/SdkBomVerifier.java @@ -0,0 +1,33 @@ +package io.a2a.test; + +import io.a2a.bom.test.DynamicBomVerifier; + +import java.util.Set; + +/** + * Verifies SDK BOM completeness by attempting to load all discovered classes. + * - Excludes paths not tested at all (boms/, examples/, tck/, tests/) + * - Forbids paths that must NOT be loadable (extras/, reference/) to prove BOM doesn't leak dependencies + */ +public class SdkBomVerifier extends DynamicBomVerifier { + + private static final Set SDK_EXCLUSIONS = Set.of( + "boms/", // BOM test modules themselves + "examples/", // Example applications + "tck/", // TCK test suite + "tests/" // Integration tests + ); + + private static final Set SDK_FORBIDDEN = Set.of( + "extras/", // Extras modules (separate BOM) - must NOT be loadable + "reference/" // Reference implementations (separate BOM) - must NOT be loadable + ); + + public SdkBomVerifier() { + super(SDK_EXCLUSIONS, SDK_FORBIDDEN); + } + + public static void main(String[] args) throws Exception { + new SdkBomVerifier().verify(); + } +} diff --git a/boms/sdk/src/it/settings.xml b/boms/sdk/src/it/settings.xml new file mode 100644 index 000000000..c06ed6333 --- /dev/null +++ b/boms/sdk/src/it/settings.xml @@ -0,0 +1,37 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/boms/test-utils/pom.xml b/boms/test-utils/pom.xml new file mode 100644 index 000000000..febf8dec1 --- /dev/null +++ b/boms/test-utils/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.4.0.Alpha1-SNAPSHOT + ../../pom.xml + + + a2a-java-bom-test-utils + jar + + A2A Java SDK - BOM Test Utilities + Shared utilities for BOM integration tests + + + 17 + 17 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + diff --git a/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java b/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java new file mode 100644 index 000000000..d0686e70f --- /dev/null +++ b/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java @@ -0,0 +1,209 @@ +package io.a2a.bom.test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Base class for dynamically discovering and verifying all classes in a BOM can be loaded. + * Subclass this and pass BOM-specific exclusions and forbidden paths to the constructor. + * + * - Excluded paths: Not tested at all (e.g., boms/, examples/, tck/, tests/) + * - Forbidden paths: Must NOT be loadable (proves BOM doesn't include wrong dependencies) + * - Required paths: Must be loadable (everything else) + */ +public abstract class DynamicBomVerifier { + + private final Set excludedPaths; + private final Set forbiddenPaths; + private static final Pattern PACKAGE_PATTERN = Pattern.compile("^package\\s+([a-zA-Z0-9_.]+);"); + + protected DynamicBomVerifier(Set excludedPaths, Set forbiddenPaths) { + this.excludedPaths = excludedPaths; + this.forbiddenPaths = forbiddenPaths; + } + + // Backwards compatibility constructor for verifiers without forbidden paths + protected DynamicBomVerifier(Set excludedPaths) { + this(excludedPaths, Set.of()); + } + + public void verify() throws Exception { + Path projectRoot = null; + Path current = Paths.get("").toAbsolutePath(); + while (current != null) { + if (Files.isDirectory(current)) { + if (Files.exists(current.resolve("pom.xml")) && Files.exists(current.resolve("boms"))) { + projectRoot = current; + break; + } + } + current = current.getParent(); + } + if (projectRoot == null) { + throw new IllegalStateException("Could not find project root directory."); + } + //Path projectRoot = Paths.get("").toAbsolutePath().getParent().getParent().getParent().getParent().getParent(); + System.out.println("Scanning project root: " + projectRoot); + + Set requiredClasses = discoverRequiredClasses(projectRoot); + Set forbiddenClasses = discoverForbiddenClasses(projectRoot); + + System.out.println("Discovered " + requiredClasses.size() + " required classes to verify"); + System.out.println("Discovered " + forbiddenClasses.size() + " forbidden classes to verify"); + + sanityCheckDiscovery(requiredClasses, forbiddenClasses); + + List failures = new ArrayList<>(); + int successful = 0; + + // Test required classes - must be loadable + for (String className : requiredClasses) { + try { + Class.forName(className); + successful++; + } catch (ClassNotFoundException e) { + failures.add("[REQUIRED] " + className + " - NOT FOUND"); + } catch (NoClassDefFoundError e) { + failures.add("[REQUIRED] " + className + " - MISSING DEPENDENCY: " + e.getMessage()); + } catch (Exception e) { + failures.add("[REQUIRED] " + className + " - ERROR: " + e.getMessage()); + } + } + + // Test forbidden classes - must NOT be loadable + int correctlyForbidden = 0; + for (String className : forbiddenClasses) { + try { + Class.forName(className); + failures.add("[FORBIDDEN] " + className + " - INCORRECTLY LOADABLE (BOM includes dependency it shouldn't!)"); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + // Expected - class should not be loadable + correctlyForbidden++; + } catch (Exception e) { + failures.add("[FORBIDDEN] " + className + " - UNEXPECTED ERROR: " + e.getMessage()); + } + } + + System.out.println("\n=== BOM Verification Results ==="); + System.out.println("Required classes successfully loaded: " + successful + "/" + requiredClasses.size()); + System.out.println("Forbidden classes correctly not loadable: " + correctlyForbidden + "/" + forbiddenClasses.size()); + System.out.println("Total failures: " + failures.size()); + + if (!failures.isEmpty()) { + System.err.println("\n=== FAILURES ==="); + failures.forEach(System.err::println); + System.err.println("\nBOM verification FAILED!"); + System.exit(1); + } + + System.out.println("\n✅ BOM is COMPLETE - all required classes loaded, all forbidden classes not loadable!"); + } + + private void sanityCheckDiscovery(Set requiredClasses, Set forbiddenClasses) { + // Do some sanity checks for some classes from both top-level and nested modules to make sure the + // discovery mechanism worked properly + sanityCheckDiscovery("io.a2a.spec.AgentCard", requiredClasses, forbiddenClasses); + sanityCheckDiscovery("io.a2a.server.events.EventConsumer", requiredClasses, forbiddenClasses); + sanityCheckDiscovery("io.a2a.client.transport.spi.ClientTransport", requiredClasses, forbiddenClasses); + + sanityCheckDiscovery("io.a2a.server.common.quarkus.DefaultProducers", requiredClasses, forbiddenClasses); + sanityCheckDiscovery("io.a2a.extras.common.events.TaskFinalizedEvent", requiredClasses, forbiddenClasses); + sanityCheckDiscovery("io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem", requiredClasses, forbiddenClasses); + + // Make sure that the required and forbidden sets don't contain the same classes + Set intersection = new HashSet<>(requiredClasses); + intersection.retainAll(forbiddenClasses); + if (!intersection.isEmpty()) { + System.err.println("The following classes appear in both the required and forbidden sets: " + intersection); + System.exit(1); + } + } + + private void sanityCheckDiscovery(String className, Set requiredClasses, Set forbiddenClasses) { + if (!requiredClasses.contains(className) && !forbiddenClasses.contains(className)) { + System.err.println("Class expected to be on the classpath was not discovered: " + className); + System.exit(1); + } + } + + /** + * Discover classes that MUST be loadable (not excluded, not forbidden) + */ + private Set discoverRequiredClasses(Path projectRoot) throws IOException { + return discoverClasses(projectRoot, relativePath -> !isExcluded(relativePath) && !isForbidden(relativePath)); + } + + /** + * Discover classes that must NOT be loadable (forbidden but not excluded) + */ + private Set discoverForbiddenClasses(Path projectRoot) throws IOException { + return discoverClasses(projectRoot, relativePath -> !isExcluded(relativePath) && isForbidden(relativePath)); + } + + /** + * Common discovery logic with custom filter + */ + private Set discoverClasses(Path projectRoot, java.util.function.Predicate pathFilter) throws IOException { + Set classes = new TreeSet<>(); + + try (Stream paths = Files.walk(projectRoot)) { + paths.filter(Files::isRegularFile) + .filter(p -> p.toString().endsWith(".java")) + .filter(p -> p.toString().contains("/src/main/java/")) + .filter(p -> pathFilter.test(projectRoot.relativize(p).toString())) + .forEach(javaFile -> { + try { + String className = extractClassName(javaFile); + if (className != null) { + classes.add(className); + } + } catch (IOException e) { + System.err.println("Failed to parse: " + javaFile + " - " + e.getMessage()); + } + }); + } + + return classes; + } + + private boolean isExcluded(String relativePath) { + return excludedPaths.stream().anyMatch(relativePath::startsWith); + } + + private boolean isForbidden(String relativePath) { + return forbiddenPaths.stream().anyMatch(relativePath::startsWith); + } + + private static String extractClassName(Path javaFile) throws IOException { + // Extract simple class name from filename + String fileName = javaFile.getFileName().toString(); + if (!fileName.endsWith(".java")) { + return null; + } + String simpleClassName = fileName.substring(0, fileName.length() - 5); // Remove ".java" + + // Extract package name from file content + String packageName = null; + try (Stream lines = Files.lines(javaFile)) { + packageName = lines.map(String::trim) + .map(PACKAGE_PATTERN::matcher) + .filter(Matcher::matches) + .map(m -> m.group(1)) + .findFirst() + .orElse(null); + } + + if (packageName != null) { + return packageName + "." + simpleClassName; + } + + return null; + } +} diff --git a/pom.xml b/pom.xml index 8bc48184f..e3cca6229 100644 --- a/pom.xml +++ b/pom.xml @@ -434,6 +434,10 @@ + + boms/test-utils + + client/base client/transport/grpc client/transport/jsonrpc @@ -459,6 +463,11 @@ transport/jsonrpc transport/grpc transport/rest + + + boms/extras + boms/reference + boms/sdk From 870b82c24d25609cf6fd8487aea855fc673ce95d Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 19 Nov 2025 10:43:31 +0000 Subject: [PATCH 205/493] feat: update tasks/list implementation to match A2A 0.4.0 spec (#459) - Sort tasks by status.timestamp DESC (most recent first), then ID ASC - Add lastUpdatedAfter parameter filtering - Update InMemoryTaskStore and JpaDatabaseTaskStore sorting logic - Add timestamp denormalized column to JpaTask for efficient querying - Update all transport handlers (JSON-RPC, gRPC, REST) Follows up on #455 and #359 --- .../database/jpa/JpaDatabaseTaskStore.java | 57 +++++- .../taskstore/database/jpa/JpaTask.java | 19 +- .../jpa/JpaDatabaseTaskStoreTest.java | 179 +++++++++++++++++- .../server/rest/quarkus/A2AServerRoutes.java | 5 +- .../DefaultRequestHandler.java | 21 +- .../a2a/server/tasks/InMemoryTaskStore.java | 95 ++++++---- .../transport/rest/handler/RestHandler.java | 17 +- 7 files changed, 341 insertions(+), 52 deletions(-) diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 24dd3648d..f7a652d60 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -227,13 +227,29 @@ public ListTasksResult list(ListTasksParams params) { countQueryBuilder.append(" AND t.state = :state"); } - // Apply pagination cursor (tasks after pageToken) + // Apply lastUpdatedAfter filter using denormalized timestamp column + if (params.lastUpdatedAfter() != null) { + queryBuilder.append(" AND t.statusTimestamp > :lastUpdatedAfter"); + countQueryBuilder.append(" AND t.statusTimestamp > :lastUpdatedAfter"); + } + + // Apply pagination cursor using keyset pagination for composite sort (timestamp DESC, id ASC) + // PageToken format: "timestamp_millis:taskId" (e.g., "1699999999000:task-123") if (params.pageToken() != null && !params.pageToken().isEmpty()) { - queryBuilder.append(" AND t.id > :pageToken"); + String[] tokenParts = params.pageToken().split(":", 2); + if (tokenParts.length == 2) { + // Keyset pagination: get tasks where timestamp < tokenTimestamp OR (timestamp = tokenTimestamp AND id > tokenId) + // All tasks have timestamps (TaskStatus canonical constructor ensures this) + queryBuilder.append(" AND (t.statusTimestamp < :tokenTimestamp OR (t.statusTimestamp = :tokenTimestamp AND t.id > :tokenId))"); + } else { + // Legacy ID-only pageToken format is not supported with timestamp-based sorting + // Throw error to prevent incorrect pagination results + throw new io.a2a.spec.InvalidParamsError(null, "Invalid pageToken format: expected 'timestamp:id'", null); + } } - // Sort by task ID for consistent pagination - queryBuilder.append(" ORDER BY t.id"); + // Sort by status timestamp descending (most recent first), then by ID for stable ordering + queryBuilder.append(" ORDER BY t.statusTimestamp DESC, t.id ASC"); // Create and configure the main query TypedQuery query = em.createQuery(queryBuilder.toString(), JpaTask.class); @@ -245,8 +261,28 @@ public ListTasksResult list(ListTasksParams params) { if (params.status() != null) { query.setParameter("state", params.status().asString()); } + if (params.lastUpdatedAfter() != null) { + query.setParameter("lastUpdatedAfter", params.lastUpdatedAfter()); + } if (params.pageToken() != null && !params.pageToken().isEmpty()) { - query.setParameter("pageToken", params.pageToken()); + String[] tokenParts = params.pageToken().split(":", 2); + if (tokenParts.length == 2) { + // Parse keyset pagination parameters + try { + long timestampMillis = Long.parseLong(tokenParts[0]); + String tokenId = tokenParts[1]; + + // All tasks have timestamps (TaskStatus canonical constructor ensures this) + Instant tokenTimestamp = Instant.ofEpochMilli(timestampMillis); + query.setParameter("tokenTimestamp", tokenTimestamp); + query.setParameter("tokenId", tokenId); + } catch (NumberFormatException e) { + // Malformed timestamp in pageToken + throw new io.a2a.spec.InvalidParamsError(null, + "Invalid pageToken format: timestamp must be numeric milliseconds", null); + } + } + // Note: Legacy ID-only format already rejected in query building phase } // Apply page size limit (+1 to check for next page) @@ -270,6 +306,9 @@ public ListTasksResult list(ListTasksParams params) { if (params.status() != null) { countQuery.setParameter("state", params.status().asString()); } + if (params.lastUpdatedAfter() != null) { + countQuery.setParameter("lastUpdatedAfter", params.lastUpdatedAfter()); + } int totalSize = countQuery.getSingleResult().intValue(); // Deserialize tasks from JSON @@ -283,10 +322,14 @@ public ListTasksResult list(ListTasksParams params) { } } - // Determine next page token (ID of last task if there are more results) + // Determine next page token (timestamp:ID of last task if there are more results) + // Format: "timestamp_millis:taskId" for keyset pagination String nextPageToken = null; if (hasMore && !tasks.isEmpty()) { - nextPageToken = tasks.get(tasks.size() - 1).getId(); + Task lastTask = tasks.get(tasks.size() - 1); + // All tasks have timestamps (TaskStatus canonical constructor ensures this) + long timestampMillis = lastTask.getStatus().timestamp().toInstant().toEpochMilli(); + nextPageToken = timestampMillis + ":" + lastTask.getId(); } // Apply post-processing transformations (history limiting, artifact removal) diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java index ebdb8f2a4..9f38dee41 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -25,6 +25,9 @@ public class JpaTask { @Column(name = "state") private String state; + @Column(name = "status_timestamp") + private Instant statusTimestamp; + @Column(name = "task_data", columnDefinition = "TEXT", nullable = false) private String taskJson; @@ -67,6 +70,14 @@ public void setState(String state) { this.state = state; } + public Instant getStatusTimestamp() { + return statusTimestamp; + } + + public void setStatusTimestamp(Instant statusTimestamp) { + this.statusTimestamp = statusTimestamp; + } + public String getTaskJson() { return taskJson; } @@ -123,7 +134,7 @@ static JpaTask createFromTask(Task task) throws JsonProcessingException { } /** - * Updates denormalized fields (contextId, state) from the task object. + * Updates denormalized fields (contextId, state, statusTimestamp) from the task object. * These fields are duplicated from the JSON to enable efficient querying. * * @param task the task to extract fields from @@ -133,8 +144,14 @@ private void updateDenormalizedFields(Task task) { if (task.getStatus() != null) { io.a2a.spec.TaskState taskState = task.getStatus().state(); this.state = (taskState != null) ? taskState.asString() : null; + // Extract status timestamp for efficient querying and sorting + // Truncate to milliseconds for keyset pagination consistency (pageToken uses millis) + this.statusTimestamp = (task.getStatus().timestamp() != null) + ? task.getStatus().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) + : null; } else { this.state = null; + this.statusTimestamp = null; } } diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index 792bf8c0f..a7cb8d79d 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -418,12 +419,14 @@ public void testListTasksCombinedFilters() { @Test @Transactional public void testListTasksPagination() { - // Create 5 tasks + // Create 5 tasks with same timestamp to ensure ID-based pagination works + // (With timestamp DESC sorting, same timestamps allow ID ASC tie-breaking) + OffsetDateTime sameTimestamp = OffsetDateTime.now(java.time.ZoneOffset.UTC); for (int i = 1; i <= 5; i++) { Task task = new Task.Builder() .id("task-page-" + i) .contextId("context-pagination") - .status(new TaskStatus(TaskState.SUBMITTED)) + .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); taskStore.save(task); } @@ -465,6 +468,122 @@ public void testListTasksPagination() { assertNull(result3.nextPageToken(), "Last page should have no next page token"); } + @Test + @Transactional + public void testListTasksPaginationWithDifferentTimestamps() { + // Create tasks with different timestamps to verify keyset pagination + // with composite sort (timestamp DESC, id ASC) + OffsetDateTime now = OffsetDateTime.now(java.time.ZoneOffset.UTC); + + // Task 1: 10 minutes ago, ID="task-diff-a" + Task task1 = new Task.Builder() + .id("task-diff-a") + .contextId("context-diff-timestamps") + .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(10))) + .build(); + taskStore.save(task1); + + // Task 2: 5 minutes ago, ID="task-diff-b" + Task task2 = new Task.Builder() + .id("task-diff-b") + .contextId("context-diff-timestamps") + .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(5))) + .build(); + taskStore.save(task2); + + // Task 3: 5 minutes ago, ID="task-diff-c" (same timestamp as task2, tests ID tie-breaker) + Task task3 = new Task.Builder() + .id("task-diff-c") + .contextId("context-diff-timestamps") + .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(5))) + .build(); + taskStore.save(task3); + + // Task 4: Now, ID="task-diff-d" + Task task4 = new Task.Builder() + .id("task-diff-d") + .contextId("context-diff-timestamps") + .status(new TaskStatus(TaskState.WORKING, null, now)) + .build(); + taskStore.save(task4); + + // Task 5: 1 minute ago, ID="task-diff-e" + Task task5 = new Task.Builder() + .id("task-diff-e") + .contextId("context-diff-timestamps") + .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(1))) + .build(); + taskStore.save(task5); + + // Expected order (timestamp DESC, id ASC): + // 1. task-diff-d (now) + // 2. task-diff-e (1 min ago) + // 3. task-diff-b (5 min ago, ID 'b') + // 4. task-diff-c (5 min ago, ID 'c') + // 5. task-diff-a (10 min ago) + + // Page 1: Get first 2 tasks + ListTasksParams params1 = new ListTasksParams.Builder() + .contextId("context-diff-timestamps") + .pageSize(2) + .build(); + ListTasksResult result1 = taskStore.list(params1); + + assertEquals(5, result1.totalSize()); + assertEquals(2, result1.pageSize()); + assertNotNull(result1.nextPageToken(), "Should have next page token"); + + // Verify first page order + assertEquals("task-diff-d", result1.tasks().get(0).getId(), "First task should be most recent"); + assertEquals("task-diff-e", result1.tasks().get(1).getId(), "Second task should be 1 min ago"); + + // Verify pageToken format: "timestamp_millis:taskId" + assertTrue(result1.nextPageToken().contains(":"), "PageToken should have format timestamp:id"); + String[] tokenParts = result1.nextPageToken().split(":", 2); + assertEquals(2, tokenParts.length, "PageToken should have exactly 2 parts"); + assertEquals("task-diff-e", tokenParts[1], "PageToken should contain last task ID"); + + // Page 2: Get next 2 tasks + ListTasksParams params2 = new ListTasksParams.Builder() + .contextId("context-diff-timestamps") + .pageSize(2) + .pageToken(result1.nextPageToken()) + .build(); + ListTasksResult result2 = taskStore.list(params2); + + assertEquals(5, result2.totalSize()); + assertEquals(2, result2.pageSize()); + assertNotNull(result2.nextPageToken(), "Should have next page token"); + + // Verify second page order (tasks with same timestamp, sorted by ID) + assertEquals("task-diff-b", result2.tasks().get(0).getId(), "Third task should be 5 min ago, ID 'b'"); + assertEquals("task-diff-c", result2.tasks().get(1).getId(), "Fourth task should be 5 min ago, ID 'c'"); + + // Page 3: Get last task + ListTasksParams params3 = new ListTasksParams.Builder() + .contextId("context-diff-timestamps") + .pageSize(2) + .pageToken(result2.nextPageToken()) + .build(); + ListTasksResult result3 = taskStore.list(params3); + + assertEquals(5, result3.totalSize()); + assertEquals(1, result3.pageSize()); + assertNull(result3.nextPageToken(), "Last page should have no next page token"); + + // Verify last task + assertEquals("task-diff-a", result3.tasks().get(0).getId(), "Last task should be oldest"); + + // Verify no duplicates across all pages + List allTaskIds = new ArrayList<>(); + allTaskIds.addAll(result1.tasks().stream().map(Task::getId).toList()); + allTaskIds.addAll(result2.tasks().stream().map(Task::getId).toList()); + allTaskIds.addAll(result3.tasks().stream().map(Task::getId).toList()); + + assertEquals(5, allTaskIds.size(), "Should have exactly 5 tasks across all pages"); + assertEquals(5, allTaskIds.stream().distinct().count(), "Should have no duplicate tasks"); + } + @Test @Transactional public void testListTasksHistoryLimiting() { @@ -573,26 +692,72 @@ public void testListTasksDefaultPageSize() { assertNotNull(result.nextPageToken(), "Should have next page"); } + @Test + @Transactional + public void testListTasksInvalidPageTokenFormat() { + // Create a task + Task task = new Task.Builder() + .id("task-invalid-token") + .contextId("context-invalid-token") + .status(new TaskStatus(TaskState.WORKING)) + .build(); + taskStore.save(task); + + // Test 1: Legacy ID-only pageToken should throw InvalidParamsError + ListTasksParams params1 = new ListTasksParams.Builder() + .contextId("context-invalid-token") + .pageToken("task-invalid-token") // ID-only format (legacy) + .build(); + + try { + taskStore.list(params1); + throw new AssertionError("Expected InvalidParamsError for legacy ID-only pageToken"); + } catch (io.a2a.spec.InvalidParamsError e) { + // Expected - legacy format not supported + assertTrue(e.getMessage().contains("Invalid pageToken format"), + "Error message should mention invalid format"); + } + + // Test 2: Malformed timestamp in pageToken should throw InvalidParamsError + ListTasksParams params2 = new ListTasksParams.Builder() + .contextId("context-invalid-token") + .pageToken("not-a-number:task-id") // Invalid timestamp + .build(); + + try { + taskStore.list(params2); + throw new AssertionError("Expected InvalidParamsError for malformed timestamp"); + } catch (io.a2a.spec.InvalidParamsError e) { + // Expected - malformed timestamp + assertTrue(e.getMessage().contains("timestamp must be numeric"), + "Error message should mention numeric timestamp requirement"); + } + } + + @Test @Transactional public void testListTasksOrderingById() { - // Create tasks with IDs that will sort in specific order + // Create tasks with same timestamp to test ID-based tie-breaking + // (spec requires sorting by timestamp DESC, then ID ASC) + OffsetDateTime sameTimestamp = OffsetDateTime.now(java.time.ZoneOffset.UTC); + Task task1 = new Task.Builder() .id("task-order-a") .contextId("context-order") - .status(new TaskStatus(TaskState.SUBMITTED)) + .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); Task task2 = new Task.Builder() .id("task-order-b") .contextId("context-order") - .status(new TaskStatus(TaskState.SUBMITTED)) + .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); Task task3 = new Task.Builder() .id("task-order-c") .contextId("context-order") - .status(new TaskStatus(TaskState.SUBMITTED)) + .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); // Save in reverse order @@ -600,7 +765,7 @@ public void testListTasksOrderingById() { taskStore.save(task1); taskStore.save(task2); - // List should return in ID order + // List should return sorted by timestamp DESC (all same), then by ID ASC ListTasksParams params = new ListTasksParams.Builder() .contextId("context-order") .build(); diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 75c34f247..88f2eac61 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -128,6 +128,7 @@ public void listTasks(RoutingContext rc) { String pageSizeStr = rc.request().params().get("pageSize"); String pageToken = rc.request().params().get("pageToken"); String historyLengthStr = rc.request().params().get("historyLength"); + String lastUpdatedAfter = rc.request().params().get("lastUpdatedAfter"); String includeArtifactsStr = rc.request().params().get("includeArtifacts"); // Parse optional parameters @@ -143,11 +144,11 @@ public void listTasks(RoutingContext rc) { Boolean includeArtifacts = null; if (includeArtifactsStr != null && !includeArtifactsStr.isEmpty()) { - includeArtifacts = Boolean.parseBoolean(includeArtifactsStr); + includeArtifacts = Boolean.valueOf(includeArtifactsStr); } response = jsonRestHandler.listTasks(contextId, statusStr, pageSize, pageToken, - historyLength, includeArtifacts, context); + historyLength, lastUpdatedAfter, includeArtifacts, context); } catch (NumberFormatException e) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("Invalid number format in parameters")); } catch (IllegalArgumentException e) { diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 56efc70ce..0671ce883 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -5,8 +5,11 @@ import static io.a2a.server.util.async.AsyncUtils.processor; import static java.util.concurrent.TimeUnit.*; +import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -43,6 +46,7 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; +import io.a2a.spec.InvalidParamsError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTasksParams; @@ -165,8 +169,21 @@ private static Task limitTaskHistory(Task task, int historyLength) { @Override public ListTasksResult onListTasks(ListTasksParams params, ServerCallContext context) throws JSONRPCError { - LOGGER.debug("onListTasks with contextId={}, status={}, pageSize={}, pageToken={}", - params.contextId(), params.status(), params.pageSize(), params.pageToken()); + LOGGER.debug("onListTasks with contextId={}, status={}, pageSize={}, pageToken={}, lastUpdatedAfter={}", + params.contextId(), params.status(), params.pageSize(), params.pageToken(), params.lastUpdatedAfter()); + + // Validate lastUpdatedAfter timestamp if provided + if (params.lastUpdatedAfter() != null) { + // Check if timestamp is in the future (optional validation per spec) + Instant now = Instant.now(); + if (params.lastUpdatedAfter().isAfter(now)) { + Map errorData = new HashMap<>(); + errorData.put("parameter", "lastUpdatedAfter"); + errorData.put("reason", "Timestamp cannot be in the future"); + throw new InvalidParamsError(null, "Invalid params", errorData); + } + } + ListTasksResult result = taskStore.list(params); LOGGER.debug("Found {} tasks (total: {})", result.pageSize(), result.totalSize()); return result; diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 0fd604089..ba50c6cd6 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -38,23 +38,22 @@ public void delete(String taskId) { @Override public ListTasksResult list(ListTasksParams params) { - Stream taskStream = tasks.values().stream(); - - // Apply filters - if (params.contextId() != null) { - taskStream = taskStream.filter(task -> params.contextId().equals(task.getContextId())); - } - if (params.status() != null) { - taskStream = taskStream.filter(task -> - task.getStatus() != null && params.status().equals(task.getStatus().state()) - ); - } - // Note: lastUpdatedAfter filtering not implemented in InMemoryTaskStore - // as Task doesn't have a lastUpdated timestamp field - - // Sort by task ID for consistent pagination - List allFilteredTasks = taskStream - .sorted(Comparator.comparing(Task::getId)) + // Filter and sort tasks in a single stream pipeline + List allFilteredTasks = tasks.values().stream() + .filter(task -> params.contextId() == null || params.contextId().equals(task.getContextId())) + .filter(task -> params.status() == null || + (task.getStatus() != null && params.status().equals(task.getStatus().state()))) + .filter(task -> params.lastUpdatedAfter() == null || + (task.getStatus() != null && + task.getStatus().timestamp() != null && + task.getStatus().timestamp().toInstant().isAfter(params.lastUpdatedAfter()))) + .sorted(Comparator.comparing( + (Task t) -> (t.getStatus() != null && t.getStatus().timestamp() != null) + // Truncate to milliseconds for consistency with pageToken precision + ? t.getStatus().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) + : null, + Comparator.nullsLast(Comparator.reverseOrder())) + .thenComparing(Task::getId)) .toList(); int totalSize = allFilteredTasks.size(); @@ -63,31 +62,63 @@ public ListTasksResult list(ListTasksParams params) { int pageSize = params.getEffectivePageSize(); int startIndex = 0; - // Handle page token (simple cursor: last task ID from previous page) + // Handle page token using keyset pagination (format: "timestamp_millis:taskId") + // Use binary search to efficiently find the first task after the pageToken position (O(log N)) if (params.pageToken() != null && !params.pageToken().isEmpty()) { - // Use binary search since list is sorted by task ID (O(log N) vs O(N)) - int index = Collections.binarySearch(allFilteredTasks, null, - (t1, t2) -> { - // Handle null key comparisons (binarySearch passes null as one argument) - if (t1 == null && t2 == null) return 0; - if (t1 == null) return params.pageToken().compareTo(t2.getId()); - if (t2 == null) return t1.getId().compareTo(params.pageToken()); - return t1.getId().compareTo(t2.getId()); - }); - if (index >= 0) { - startIndex = index + 1; + String[] tokenParts = params.pageToken().split(":", 2); + if (tokenParts.length == 2) { + try { + long tokenTimestampMillis = Long.parseLong(tokenParts[0]); + java.time.Instant tokenTimestamp = java.time.Instant.ofEpochMilli(tokenTimestampMillis); + String tokenId = tokenParts[1]; + + // Binary search for first task where: timestamp < tokenTimestamp OR (timestamp == tokenTimestamp AND id > tokenId) + // Since list is sorted (timestamp DESC, id ASC), we search for the insertion point + int left = 0; + int right = allFilteredTasks.size(); + + while (left < right) { + int mid = left + (right - left) / 2; + Task task = allFilteredTasks.get(mid); + + // All tasks have timestamps (TaskStatus canonical constructor ensures this) + // Truncate to milliseconds for consistency with pageToken precision + java.time.Instant taskTimestamp = task.getStatus().timestamp().toInstant() + .truncatedTo(java.time.temporal.ChronoUnit.MILLIS); + int timestampCompare = taskTimestamp.compareTo(tokenTimestamp); + + if (timestampCompare < 0 || (timestampCompare == 0 && task.getId().compareTo(tokenId) > 0)) { + // This task is after the token, search left half + right = mid; + } else { + // This task is before or equal to token, search right half + left = mid + 1; + } + } + startIndex = left; + } catch (NumberFormatException e) { + // Malformed timestamp in pageToken + throw new io.a2a.spec.InvalidParamsError(null, + "Invalid pageToken format: timestamp must be numeric milliseconds", null); + } + } else { + // Legacy ID-only pageToken format is not supported with timestamp-based sorting + // Throw error to prevent incorrect pagination results + throw new io.a2a.spec.InvalidParamsError(null, "Invalid pageToken format: expected 'timestamp:id'", null); } - // If not found (index < 0), startIndex remains 0 (start from beginning) } // Get the page of tasks int endIndex = Math.min(startIndex + pageSize, allFilteredTasks.size()); List pageTasks = allFilteredTasks.subList(startIndex, endIndex); - // Determine next page token + // Determine next page token (format: "timestamp_millis:taskId") String nextPageToken = null; if (endIndex < allFilteredTasks.size()) { - nextPageToken = allFilteredTasks.get(endIndex - 1).getId(); + Task lastTask = allFilteredTasks.get(endIndex - 1); + // All tasks have timestamps (TaskStatus canonical constructor ensures this) + long timestampMillis = lastTask.getStatus().timestamp().toInstant().toEpochMilli(); + nextPageToken = timestampMillis + ":" + lastTask.getId(); } // Transform tasks: limit history and optionally remove artifacts diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 398a85710..105831262 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -11,7 +11,11 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Flow; import io.a2a.server.PublicAgentCard; @@ -182,7 +186,8 @@ public HTTPRestResponse getTask(String taskId, int historyLength, ServerCallCont public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String status, @Nullable Integer pageSize, @Nullable String pageToken, - @Nullable Integer historyLength, @Nullable Boolean includeArtifacts, + @Nullable Integer historyLength, @Nullable String lastUpdatedAfter, + @Nullable Boolean includeArtifacts, ServerCallContext context) { try { // Build params @@ -202,6 +207,16 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s if (historyLength != null) { paramsBuilder.historyLength(historyLength); } + if (lastUpdatedAfter != null) { + try { + paramsBuilder.lastUpdatedAfter(Instant.parse(lastUpdatedAfter)); + } catch (DateTimeParseException e) { + Map errorData = new HashMap<>(); + errorData.put("parameter", "lastUpdatedAfter"); + errorData.put("reason", "Must be valid ISO-8601 timestamp"); + throw new InvalidParamsError(null, "Invalid params", errorData); + } + } if (includeArtifacts != null) { paramsBuilder.includeArtifacts(includeArtifacts); } From bfdee139e249d5e4695ecb68c2697874ab6135c7 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 20 Nov 2025 11:43:29 +0000 Subject: [PATCH 206/493] feat!: Remove hard dependency on MicroProfile Config from the core SDK (#468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default values will now be used by default. You can supply your own by providing a CDI bean with a higher priority. Also, there is a new 2a-java-sdk-microprofile-config with the previous MicroProfile Config capabilities. If used, this will allow MicroProfile Config configurations of the properties. The reference implementations use this new module Fixes #467 🦕 --- README.md | 40 +++-- boms/extras/src/it/extras-usage-test/pom.xml | 10 ++ .../src/it/reference-usage-test/pom.xml | 10 ++ boms/sdk/pom.xml | 7 + boms/sdk/src/it/sdk-usage-test/pom.xml | 10 ++ .../database/jpa/JpaDatabaseTaskStore.java | 20 ++- .../META-INF/a2a-defaults.properties | 6 + integrations/microprofile-config/README.md | 148 ++++++++++++++++++ integrations/microprofile-config/pom.xml | 59 +++++++ .../MicroProfileConfigProvider.java | 78 +++++++++ .../MicroProfileConfigProviderTest.java | 102 ++++++++++++ .../src/test/resources/application.properties | 14 ++ pom.xml | 6 + reference/common/pom.xml | 4 + server-common/pom.xml | 4 - .../a2a/server/config/A2AConfigProvider.java | 35 +++++ .../config/DefaultValuesConfigProvider.java | 96 ++++++++++++ .../DefaultRequestHandler.java | 32 ++-- .../util/async/AsyncExecutorProducer.java | 36 ++++- .../META-INF/a2a-defaults.properties | 21 +++ 20 files changed, 703 insertions(+), 35 deletions(-) create mode 100644 extras/task-store-database-jpa/src/main/resources/META-INF/a2a-defaults.properties create mode 100644 integrations/microprofile-config/README.md create mode 100644 integrations/microprofile-config/pom.xml create mode 100644 integrations/microprofile-config/src/main/java/io/a2a/integrations/microprofile/MicroProfileConfigProvider.java create mode 100644 integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java create mode 100644 integrations/microprofile-config/src/test/resources/application.properties create mode 100644 server-common/src/main/java/io/a2a/server/config/A2AConfigProvider.java create mode 100644 server-common/src/main/java/io/a2a/server/config/DefaultValuesConfigProvider.java create mode 100644 server-common/src/main/resources/META-INF/a2a-defaults.properties diff --git a/README.md b/README.md index 249a79b88..a185bc21e 100644 --- a/README.md +++ b/README.md @@ -232,11 +232,22 @@ public class WeatherAgentExecutorProducer { } ``` -### 4. Configure Executor Settings (Optional) +### 4. Configuration System -The A2A Java SDK uses a dedicated executor for handling asynchronous operations like streaming subscriptions. By default, this executor is configured with a core pool size of 5 threads and a maximum pool size of 50 threads, optimized for I/O-bound operations. +The A2A Java SDK uses a flexible configuration system that works across different frameworks. -You can customize the executor settings in your `application.properties`: +**Default behavior:** Configuration values come from `META-INF/a2a-defaults.properties` files on the classpath (provided by core modules and extras). These defaults work out of the box without any additional setup. + +**Customizing configuration:** +- **Quarkus/MicroProfile Config users**: Add the [`microprofile-config`](integrations/microprofile-config/README.md) integration to override defaults via `application.properties`, environment variables, or system properties +- **Spring/other frameworks**: See the [integration module README](integrations/microprofile-config/README.md#custom-config-providers) for how to implement a custom `A2AConfigProvider` +- **Reference implementations**: Already include the MicroProfile Config integration + +#### Configuration Properties + +**Executor Settings** (Optional) + +The SDK uses a dedicated executor for async operations like streaming. Default: 5 core threads, 50 max threads. ```properties # Core thread pool size for the @Internal executor (default: 5) @@ -249,20 +260,23 @@ a2a.executor.max-pool-size=50 a2a.executor.keep-alive-seconds=60 ``` -**Why this matters:** -- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load. -- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool used by other async tasks. -- **Concurrency**: In production environments with high concurrent streaming requests, increase the pool sizes accordingly. +**Blocking Call Timeouts** (Optional) -**Default Configuration:** ```properties -# These are the defaults - no need to set unless you want different values -a2a.executor.core-pool-size=5 -a2a.executor.max-pool-size=50 -a2a.executor.keep-alive-seconds=60 +# Timeout for agent execution in blocking calls (default: 30 seconds) +a2a.blocking.agent.timeout.seconds=30 + +# Timeout for event consumption in blocking calls (default: 5 seconds) +a2a.blocking.consumption.timeout.seconds=5 ``` -**Note:** The reference server implementations automatically configure this executor. If you're creating a custom server integration, ensure you provide an `@Internal Executor` bean for optimal streaming performance. +**Why this matters:** +- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load. +- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool. +- **Concurrency**: In production with high concurrent streaming, increase pool sizes accordingly. +- **Agent Timeouts**: LLM-based agents may need longer timeouts (60-120s) compared to simple agents. + +**Note:** The reference server implementations (Quarkus-based) automatically include the MicroProfile Config integration, so properties work out of the box in `application.properties`. ## A2A Client diff --git a/boms/extras/src/it/extras-usage-test/pom.xml b/boms/extras/src/it/extras-usage-test/pom.xml index e794ec9ae..5beeb50ea 100644 --- a/boms/extras/src/it/extras-usage-test/pom.xml +++ b/boms/extras/src/it/extras-usage-test/pom.xml @@ -74,6 +74,10 @@ io.github.a2asdk a2a-java-sdk-server-common + + io.github.a2asdk + a2a-java-sdk-microprofile-config + io.github.a2asdk a2a-java-sdk-client @@ -102,6 +106,12 @@ io.github.a2asdk a2a-java-sdk-transport-rest + + + + org.eclipse.microprofile.config + microprofile-config-api + diff --git a/boms/reference/src/it/reference-usage-test/pom.xml b/boms/reference/src/it/reference-usage-test/pom.xml index c09a3cc3b..2e000a8a8 100644 --- a/boms/reference/src/it/reference-usage-test/pom.xml +++ b/boms/reference/src/it/reference-usage-test/pom.xml @@ -69,6 +69,10 @@ io.github.a2asdk a2a-java-sdk-server-common + + io.github.a2asdk + a2a-java-sdk-microprofile-config + io.github.a2asdk a2a-java-sdk-client @@ -86,6 +90,12 @@ a2a-java-sdk-transport-jsonrpc + + + org.eclipse.microprofile.config + microprofile-config-api + + io.quarkus diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml index 9aed2b1d1..fd3bd07e4 100644 --- a/boms/sdk/pom.xml +++ b/boms/sdk/pom.xml @@ -52,6 +52,13 @@ ${project.version} + + + ${project.groupId} + a2a-java-sdk-microprofile-config + ${project.version} + + ${project.groupId} diff --git a/boms/sdk/src/it/sdk-usage-test/pom.xml b/boms/sdk/src/it/sdk-usage-test/pom.xml index c5bd4ff21..0468502e7 100644 --- a/boms/sdk/src/it/sdk-usage-test/pom.xml +++ b/boms/sdk/src/it/sdk-usage-test/pom.xml @@ -61,6 +61,12 @@ a2a-java-sdk-server-common + + + io.github.a2asdk + a2a-java-sdk-microprofile-config + + io.github.a2asdk @@ -122,6 +128,10 @@ io.grpc grpc-stub + + org.eclipse.microprofile.config + microprofile-config-api + diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index f7a652d60..6e63b9f5d 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.stream.Stream; +import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; @@ -19,6 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.a2a.extras.common.events.TaskFinalizedEvent; +import io.a2a.server.config.A2AConfigProvider; import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.tasks.TaskStore; import io.a2a.spec.Artifact; @@ -26,7 +28,6 @@ import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.Task; -import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,9 +45,24 @@ public class JpaDatabaseTaskStore implements TaskStore, TaskStateProvider { Event taskFinalizedEvent; @Inject - @ConfigProperty(name = "a2a.replication.grace-period-seconds", defaultValue = "15") + A2AConfigProvider configProvider; + + /** + * Grace period for task finalization in replicated scenarios (seconds). + * After a task reaches a final state, this is the minimum time to wait before cleanup + * to allow replicated events to arrive and be processed. + *

+ * Property: {@code a2a.replication.grace-period-seconds}
+ * Default: 15
+ * Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath. + */ long gracePeriodSeconds; + @PostConstruct + void initConfig() { + gracePeriodSeconds = Long.parseLong(configProvider.getValue("a2a.replication.grace-period-seconds")); + } + @Transactional @Override public void save(Task task) { diff --git a/extras/task-store-database-jpa/src/main/resources/META-INF/a2a-defaults.properties b/extras/task-store-database-jpa/src/main/resources/META-INF/a2a-defaults.properties new file mode 100644 index 000000000..c01c5e60a --- /dev/null +++ b/extras/task-store-database-jpa/src/main/resources/META-INF/a2a-defaults.properties @@ -0,0 +1,6 @@ +# A2A JPA Database Task Store Default Configuration + +# Grace period for task finalization in replicated scenarios (seconds) +# After a task reaches a final state, this is the minimum time to wait before cleanup +# to allow replicated events to arrive and be processed +a2a.replication.grace-period-seconds=15 diff --git a/integrations/microprofile-config/README.md b/integrations/microprofile-config/README.md new file mode 100644 index 000000000..501a0dd78 --- /dev/null +++ b/integrations/microprofile-config/README.md @@ -0,0 +1,148 @@ +# A2A Java SDK - MicroProfile Config Integration + +This optional integration module provides MicroProfile Config support for the A2A Java SDK configuration system. + +## Overview + +The A2A Java SDK core uses the `A2AConfigProvider` interface for configuration, with default values loaded from `META-INF/a2a-defaults.properties` files on the classpath. + +This module provides `MicroProfileConfigProvider`, which integrates with MicroProfile Config to allow configuration via: +- `application.properties` +- Environment variables +- System properties (`-D` flags) +- Custom ConfigSources + +## Quick Start + +### 1. Add Dependency + +```xml + + io.github.a2asdk + a2a-java-sdk-microprofile-config + ${io.a2a.sdk.version} + +``` + +### 2. Configure Properties + +Once the dependency is added, you can override any A2A configuration property: + +**application.properties:** +```properties +# Executor configuration +a2a.executor.core-pool-size=10 +a2a.executor.max-pool-size=100 + +# Timeout configuration +a2a.blocking.agent.timeout.seconds=60 +a2a.blocking.consumption.timeout.seconds=10 +``` + +**Environment variables:** +```bash +export A2A_EXECUTOR_CORE_POOL_SIZE=10 +export A2A_BLOCKING_AGENT_TIMEOUT_SECONDS=60 +``` + +**System properties:** +```bash +java -Da2a.executor.core-pool-size=10 -jar your-app.jar +``` + +## How It Works + +The `MicroProfileConfigProvider` implementation: + +1. **First tries MicroProfile Config** - Checks `application.properties`, environment variables, system properties, and custom ConfigSources +2. **Falls back to defaults** - If not found, uses values from `META-INF/a2a-defaults.properties` provided by core modules and extras +3. **Priority 50** - Can be overridden by custom providers with higher priority + +## Configuration Fallback Chain + +``` +MicroProfile Config Sources (application.properties, env vars, -D flags) + ↓ (not found?) +DefaultValuesConfigProvider + → Scans classpath for ALL META-INF/a2a-defaults.properties files + → Merges all discovered properties together + → Throws exception if duplicate keys found + ↓ (property exists?) +Return merged default value + ↓ (not found?) +IllegalArgumentException +``` + +**Note**: All `META-INF/a2a-defaults.properties` files (from server-common, extras modules, etc.) are loaded and merged together by `DefaultValuesConfigProvider` at startup. This is not a sequential fallback chain, but a single merged set of defaults. + +## Available Configuration Properties + +See the [main README](../../README.md#configuration-system) for a complete list of configuration properties. + +## Framework Compatibility + +This module works with any MicroProfile Config implementation: + +- **Quarkus** - Built-in MicroProfile Config support +- **Helidon** - Built-in MicroProfile Config support +- **Open Liberty** - Built-in MicroProfile Config support +- **WildFly/JBoss EAP** - Add `smallrye-config` dependency +- **Other Jakarta EE servers** - Add MicroProfile Config implementation + +## Custom Config Providers + +If you're using a different framework (Spring, Micronaut, etc.), you can implement your own `A2AConfigProvider`: + +```java +import io.a2a.server.config.A2AConfigProvider; +import io.a2a.server.config.DefaultValuesConfigProvider; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.annotation.Priority; +import jakarta.inject.Inject; + +@ApplicationScoped +@Alternative +@Priority(100) // Higher than MicroProfileConfigProvider's priority of 50 +public class OtherEnvironmentConfigProvider implements A2AConfigProvider { + + @Inject + Environment env; + + @Inject + DefaultValuesConfigProvider defaultValues; + + @Override + public String getValue(String name) { + String value = env.getProperty(name); + if (value != null) { + return value; + } + // Fallback to defaults + return defaultValues.getValue(name); + } + + @Override + public Optional getOptionalValue(String name) { + String value = env.getProperty(name); + if (value != null) { + return Optional.of(value); + } + return defaultValues.getOptionalValue(name); + } +} +``` + +## Implementation Details + +- **Package**: `io.a2a.integrations.microprofile` +- **Class**: `MicroProfileConfigProvider` +- **Priority**: 50 (can be overridden) +- **Scope**: `@ApplicationScoped` +- **Dependencies**: MicroProfile Config API, A2A SDK server-common + +## Reference Implementations + +The A2A Java SDK reference implementations (Quarkus-based) automatically include this integration module, so MicroProfile Config properties work out of the box. + +If you're building a custom server implementation, add this dependency to enable property-based configuration. diff --git a/integrations/microprofile-config/pom.xml b/integrations/microprofile-config/pom.xml new file mode 100644 index 000000000..f5435bdce --- /dev/null +++ b/integrations/microprofile-config/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.4.0.Alpha1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-microprofile-config + + jar + + A2A Java SDK - MicroProfile Config Integration + MicroProfile Config integration for A2A Java SDK - provides A2AConfigProvider implementation + + + + ${project.groupId} + a2a-java-sdk-server-common + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.inject + jakarta.inject-api + + + org.eclipse.microprofile.config + microprofile-config-api + + + org.slf4j + slf4j-api + + + + + io.quarkus + quarkus-arc + test + + + io.quarkus + quarkus-junit5 + test + + + org.junit.jupiter + junit-jupiter-api + test + + + diff --git a/integrations/microprofile-config/src/main/java/io/a2a/integrations/microprofile/MicroProfileConfigProvider.java b/integrations/microprofile-config/src/main/java/io/a2a/integrations/microprofile/MicroProfileConfigProvider.java new file mode 100644 index 000000000..666c2d612 --- /dev/null +++ b/integrations/microprofile-config/src/main/java/io/a2a/integrations/microprofile/MicroProfileConfigProvider.java @@ -0,0 +1,78 @@ +package io.a2a.integrations.microprofile; + +import java.util.Optional; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Alternative; +import jakarta.annotation.Priority; +import jakarta.inject.Inject; + +import io.a2a.server.config.A2AConfigProvider; +import io.a2a.server.config.DefaultValuesConfigProvider; +import org.eclipse.microprofile.config.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * MicroProfile Config-based implementation of {@link A2AConfigProvider}. + *

+ * This provider integrates with MicroProfile Config (used by Quarkus and other Jakarta EE runtimes) + * to allow configuration via standard sources: + *

    + *
  • System properties (-D flags)
  • + *
  • Environment variables
  • + *
  • application.properties
  • + *
  • Custom ConfigSources
  • + *
+ *

+ * Falls back to {@link DefaultValuesConfigProvider} when a configuration value is not found + * in MicroProfile Config, ensuring that default values from {@code META-INF/a2a-defaults.properties} + * are always available. + *

+ * This provider is automatically enabled with {@code @Priority(50)}, but can be overridden by + * custom providers with higher priority. + *

+ * To use this provider, add the {@code a2a-java-sdk-microprofile-config} dependency to your project. + */ +@ApplicationScoped +@Alternative +@Priority(50) +public class MicroProfileConfigProvider implements A2AConfigProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(MicroProfileConfigProvider.class); + + @Inject + Config mpConfig; + + @Inject + DefaultValuesConfigProvider defaultValues; + + @Override + public String getValue(String name) { + Optional value = mpConfig.getOptionalValue(name, String.class); + if (value.isPresent()) { + LOGGER.trace("Config value '{}' = '{}' (from MicroProfile Config)", name, value.get()); + return value.get(); + } + + // Fallback to defaults + String defaultValue = defaultValues.getValue(name); + LOGGER.trace("Config value '{}' = '{}' (from DefaultValuesConfigProvider)", name, defaultValue); + return defaultValue; + } + + @Override + public Optional getOptionalValue(String name) { + Optional value = mpConfig.getOptionalValue(name, String.class); + if (value.isPresent()) { + LOGGER.trace("Optional config value '{}' = '{}' (from MicroProfile Config)", name, value.get()); + return value; + } + + // Fallback to defaults + Optional defaultValue = defaultValues.getOptionalValue(name); + LOGGER.trace("Optional config value '{}' = '{}' (from DefaultValuesConfigProvider)", + name, defaultValue.orElse("")); + return defaultValue; + } +} diff --git a/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java b/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java new file mode 100644 index 000000000..1c245baf7 --- /dev/null +++ b/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java @@ -0,0 +1,102 @@ +package io.a2a.integrations.microprofile; + +import io.a2a.server.config.A2AConfigProvider; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * CDI-based test to verify that MicroProfileConfigProvider is properly selected + * and works correctly with MicroProfile Config and fallback to defaults. + */ +@QuarkusTest +public class MicroProfileConfigProviderTest { + + @Inject + A2AConfigProvider configProvider; + + @Test + public void testIsMicroProfileConfigProvider() { + // Verify that when microprofile-config module is on classpath, + // the injected A2AConfigProvider is the MicroProfile implementation + assertInstanceOf(MicroProfileConfigProvider.class, configProvider, + "A2AConfigProvider should be MicroProfileConfigProvider when module is present"); + } + + @Test + public void testGetValueFromMicroProfileConfig() { + // Test that values from application.properties override defaults + // The test application.properties sets a2a.executor.core-pool-size=15 + String value = configProvider.getValue("a2a.executor.core-pool-size"); + assertEquals("15", value, "Should get value from MicroProfile Config (application.properties)"); + } + + @Test + public void testGetValueFallbackToDefaults() { + // Test that values not in application.properties fall back to META-INF/a2a-defaults.properties + // a2a.executor.max-pool-size is not in test application.properties, so should use default + String value = configProvider.getValue("a2a.executor.max-pool-size"); + assertEquals("50", value, "Should fall back to default value from META-INF/a2a-defaults.properties"); + } + + @Test + public void testGetValueAnotherDefault() { + // Test another default property to ensure fallback works + String value = configProvider.getValue("a2a.executor.keep-alive-seconds"); + assertEquals("60", value, "Should fall back to default value"); + } + + @Test + public void testGetOptionalValueFromMicroProfileConfig() { + // Test optional value that exists in application.properties + Optional value = configProvider.getOptionalValue("a2a.executor.core-pool-size"); + assertTrue(value.isPresent(), "Optional value should be present"); + assertEquals("15", value.get(), "Should get overridden value from MicroProfile Config"); + } + + @Test + public void testGetOptionalValueFallbackToDefaults() { + // Test optional value that falls back to defaults + Optional value = configProvider.getOptionalValue("a2a.executor.max-pool-size"); + assertTrue(value.isPresent(), "Optional value should be present from defaults"); + assertEquals("50", value.get(), "Should get default value"); + } + + @Test + public void testGetOptionalValueNotFound() { + // Test optional value that doesn't exist anywhere + Optional value = configProvider.getOptionalValue("non.existent.property"); + assertFalse(value.isPresent(), "Optional value should be empty for non-existent property"); + } + + @Test + public void testGetValueThrowsForNonExistent() { + // Test that required getValue() throws for non-existent property + assertThrows(IllegalArgumentException.class, + () -> configProvider.getValue("non.existent.property"), + "Should throw IllegalArgumentException for non-existent required property"); + } + + @Test + public void testSystemPropertyOverride() { + // System properties should have higher priority than application.properties + // Set a system property and verify it's used + String originalValue = System.getProperty("a2a.test.system.property"); + try { + System.setProperty("a2a.test.system.property", "from-system-property"); + String value = configProvider.getValue("a2a.test.system.property"); + assertEquals("from-system-property", value, + "System property should override application.properties"); + } finally { + if (originalValue != null) { + System.setProperty("a2a.test.system.property", originalValue); + } else { + System.clearProperty("a2a.test.system.property"); + } + } + } +} diff --git a/integrations/microprofile-config/src/test/resources/application.properties b/integrations/microprofile-config/src/test/resources/application.properties new file mode 100644 index 000000000..a79c9b843 --- /dev/null +++ b/integrations/microprofile-config/src/test/resources/application.properties @@ -0,0 +1,14 @@ +# Test configuration for MicroProfileConfigProviderTest +# This overrides the default value to verify MicroProfile Config integration works + +# Override default value (default is 5) +a2a.executor.core-pool-size=15 + +# Note: a2a.executor.max-pool-size is NOT set here to test fallback to defaults +# Default value should be 50 from META-INF/a2a-defaults.properties + +# Exclude beans that aren't needed for config testing +quarkus.arc.exclude-types=io.a2a.server.requesthandlers.*,io.a2a.server.agentexecution.*,io.a2a.server.tasks.*,io.a2a.server.events.*,io.a2a.server.util.* + +# Property that will be overridden by a system property +a2a.test.system.property=from-application-properties \ No newline at end of file diff --git a/pom.xml b/pom.xml index e3cca6229..5442bd2ac 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,11 @@ a2a-java-sdk-server-common ${project.version} + + ${project.groupId} + a2a-java-sdk-microprofile-config + ${project.version} + ${project.groupId} a2a-java-extras-common @@ -451,6 +456,7 @@ extras/push-notification-config-store-database-jpa extras/queue-manager-replicated http-client + integrations/microprofile-config reference/common reference/grpc reference/jsonrpc diff --git a/reference/common/pom.xml b/reference/common/pom.xml index f93374a16..0a9c57123 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -49,6 +49,10 @@ org.slf4j slf4j-api + + ${project.groupId} + a2a-java-sdk-microprofile-config + io.quarkus quarkus-junit5 diff --git a/server-common/pom.xml b/server-common/pom.xml index 2165790ed..5d00552d8 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -91,10 +91,6 @@ logback-classic test - - org.eclipse.microprofile.config - microprofile-config-api - diff --git a/server-common/src/main/java/io/a2a/server/config/A2AConfigProvider.java b/server-common/src/main/java/io/a2a/server/config/A2AConfigProvider.java new file mode 100644 index 000000000..ccd442e1c --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/config/A2AConfigProvider.java @@ -0,0 +1,35 @@ +package io.a2a.server.config; + +import java.util.Optional; + +/** + * Configuration provider interface for A2A SDK configuration values. + *

+ * Implementations can obtain configuration from various sources: + *

    + *
  • {@link DefaultValuesConfigProvider} - Loads from META-INF/a2a-defaults.properties on classpath
  • + *
  • MicroProfileConfigProvider - Delegates to MicroProfile Config (reference implementations)
  • + *
  • Custom implementations - Can integrate with any configuration system
  • + *
+ *

+ * All configuration values are returned as strings. Consumers are responsible for type conversion. + */ +public interface A2AConfigProvider { + + /** + * Get a required configuration value. + * + * @param name the configuration property name + * @return the configuration value + * @throws IllegalArgumentException if the configuration value is not found + */ + String getValue(String name); + + /** + * Get an optional configuration value. + * + * @param name the configuration property name + * @return an Optional containing the value if present, empty otherwise + */ + Optional getOptionalValue(String name); +} diff --git a/server-common/src/main/java/io/a2a/server/config/DefaultValuesConfigProvider.java b/server-common/src/main/java/io/a2a/server/config/DefaultValuesConfigProvider.java new file mode 100644 index 000000000..f2375ae57 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/config/DefaultValuesConfigProvider.java @@ -0,0 +1,96 @@ +package io.a2a.server.config; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Default configuration provider that loads values from {@code META-INF/a2a-defaults.properties} + * files on the classpath. + *

+ * Each module (server-common, extras, etc.) can contribute a {@code META-INF/a2a-defaults.properties} + * file with default configuration values. All files are discovered and merged at startup. + *

+ * If duplicate keys are found across different properties files, initialization will fail with + * an exception to prevent ambiguous configuration. + */ +@ApplicationScoped +public class DefaultValuesConfigProvider implements A2AConfigProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultValuesConfigProvider.class); + private static final String DEFAULTS_RESOURCE = "META-INF/a2a-defaults.properties"; + + private final Map defaults = new HashMap<>(); + + @PostConstruct + void init() { + loadDefaultsFromClasspath(); + } + + private void loadDefaultsFromClasspath() { + try { + Enumeration resources = Thread.currentThread() + .getContextClassLoader() + .getResources(DEFAULTS_RESOURCE); + + Map sourceTracker = new HashMap<>(); // Track which file each key came from + + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + LOGGER.debug("Loading A2A defaults from: {}", url); + + Properties props = new Properties(); + try (InputStream is = url.openStream()) { + props.load(is); + + // Check for duplicates and merge + for (String key : props.stringPropertyNames()) { + String value = props.getProperty(key); + String existingSource = sourceTracker.get(key); + + if (existingSource != null) { + throw new IllegalStateException(String.format( + "Duplicate configuration key '%s' found in multiple a2a-defaults.properties files: %s and %s", + key, existingSource, url)); + } + + defaults.put(key, value); + sourceTracker.put(key, url.toString()); + LOGGER.trace("Loaded default: {} = {}", key, value); + } + } + } + + LOGGER.info("Loaded {} A2A default configuration values from {} resource(s)", + defaults.size(), sourceTracker.values().stream().distinct().count()); + + } catch (IOException e) { + throw new RuntimeException("Failed to load A2A default configuration from classpath", e); + } + } + + @Override + public String getValue(String name) { + String value = defaults.get(name); + if (value == null) { + throw new IllegalArgumentException("No default configuration value found for: " + name); + } + return value; + } + + @Override + public Optional getOptionalValue(String name) { + return Optional.ofNullable(defaults.get(name)); + } +} diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 0671ce883..f29aff575 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -63,7 +63,8 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.UnsupportedOperationError; -import org.eclipse.microprofile.config.inject.ConfigProperty; +import io.a2a.server.config.A2AConfigProvider; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,24 +73,29 @@ public class DefaultRequestHandler implements RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRequestHandler.class); + @Inject + A2AConfigProvider configProvider; + /** * Timeout in seconds to wait for agent execution to complete in blocking calls. * This allows slow agents (LLM-based, data processing, external APIs) sufficient time. - * Configurable via: a2a.blocking.agent.timeout.seconds - * Default: 30 seconds + *

+ * Property: {@code a2a.blocking.agent.timeout.seconds}
+ * Default: 30 seconds
+ * Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath + * (e.g., MicroProfileConfigProvider in reference implementations). */ - @Inject - @ConfigProperty(name = "a2a.blocking.agent.timeout.seconds", defaultValue = "30") int agentCompletionTimeoutSeconds; /** * Timeout in seconds to wait for event consumption to complete in blocking calls. * This ensures all events are processed and persisted before returning to client. - * Configurable via: a2a.blocking.consumption.timeout.seconds - * Default: 5 seconds + *

+ * Property: {@code a2a.blocking.consumption.timeout.seconds}
+ * Default: 5 seconds
+ * Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath + * (e.g., MicroProfileConfigProvider in reference implementations). */ - @Inject - @ConfigProperty(name = "a2a.blocking.consumption.timeout.seconds", defaultValue = "5") int consumptionCompletionTimeoutSeconds; private final AgentExecutor agentExecutor; @@ -121,6 +127,14 @@ public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, this.requestContextBuilder = () -> new SimpleRequestContextBuilder(taskStore, false); } + @PostConstruct + void initConfig() { + agentCompletionTimeoutSeconds = Integer.parseInt( + configProvider.getValue("a2a.blocking.agent.timeout.seconds")); + consumptionCompletionTimeoutSeconds = Integer.parseInt( + configProvider.getValue("a2a.blocking.consumption.timeout.seconds")); + } + /** * For testing */ diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index 49e69f99e..d85cd4de3 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -14,7 +14,7 @@ import jakarta.enterprise.inject.Produces; import jakarta.inject.Inject; -import org.eclipse.microprofile.config.inject.ConfigProperty; +import io.a2a.server.config.A2AConfigProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,22 +23,44 @@ public class AsyncExecutorProducer { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncExecutorProducer.class); - @Inject // Needed to work in standard Jakarta runtimes (Quarkus skips this) - @ConfigProperty(name = "a2a.executor.core-pool-size", defaultValue = "5") + @Inject + A2AConfigProvider configProvider; + + /** + * Core pool size for async agent execution thread pool. + *

+ * Property: {@code a2a.executor.core-pool-size}
+ * Default: 5
+ * Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath. + */ int corePoolSize; - @Inject // Needed to work in standard Jakarta runtimes (Quarkus skips this) - @ConfigProperty(name = "a2a.executor.max-pool-size", defaultValue = "50") + /** + * Maximum pool size for async agent execution thread pool. + *

+ * Property: {@code a2a.executor.max-pool-size}
+ * Default: 50
+ * Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath. + */ int maxPoolSize; - @Inject // Needed to work in standard Jakarta runtimes (Quarkus skips this) - @ConfigProperty(name = "a2a.executor.keep-alive-seconds", defaultValue = "60") + /** + * Keep-alive time for idle threads (seconds). + *

+ * Property: {@code a2a.executor.keep-alive-seconds}
+ * Default: 60
+ * Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath. + */ long keepAliveSeconds; private ExecutorService executor; @PostConstruct public void init() { + corePoolSize = Integer.parseInt(configProvider.getValue("a2a.executor.core-pool-size")); + maxPoolSize = Integer.parseInt(configProvider.getValue("a2a.executor.max-pool-size")); + keepAliveSeconds = Long.parseLong(configProvider.getValue("a2a.executor.keep-alive-seconds")); + LOGGER.info("Initializing async executor: corePoolSize={}, maxPoolSize={}, keepAliveSeconds={}", corePoolSize, maxPoolSize, keepAliveSeconds); diff --git a/server-common/src/main/resources/META-INF/a2a-defaults.properties b/server-common/src/main/resources/META-INF/a2a-defaults.properties new file mode 100644 index 000000000..280fd943b --- /dev/null +++ b/server-common/src/main/resources/META-INF/a2a-defaults.properties @@ -0,0 +1,21 @@ +# A2A SDK Default Configuration Values +# These values are used when no other configuration source provides them + +# DefaultRequestHandler - Blocking call timeouts +# Timeout for agent execution to complete (seconds) +# Increase for slow agents: LLM-based, data processing, external APIs +a2a.blocking.agent.timeout.seconds=30 + +# Timeout for event consumption/persistence to complete (seconds) +# Ensures TaskStore is fully updated before returning to client +a2a.blocking.consumption.timeout.seconds=5 + +# AsyncExecutorProducer - Thread pool configuration +# Core pool size for async agent execution +a2a.executor.core-pool-size=5 + +# Maximum pool size for async agent execution +a2a.executor.max-pool-size=50 + +# Keep-alive time for idle threads (seconds) +a2a.executor.keep-alive-seconds=60 From 9cfda0f266f02d033023addda5c7ae45fbe4ee38 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 20 Nov 2025 18:49:50 +0100 Subject: [PATCH 207/493] fix: Making the event queue overridable (#473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes # 🦕 Signed-off-by: Emmanuel Hugonnet --- .../main/java/io/a2a/client/MessageEvent.java | 17 +++++++++++++++-- .../java/io/a2a/server/events/EventQueue.java | 12 ++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/client/base/src/main/java/io/a2a/client/MessageEvent.java b/client/base/src/main/java/io/a2a/client/MessageEvent.java index b5970ab78..94c8a1058 100644 --- a/client/base/src/main/java/io/a2a/client/MessageEvent.java +++ b/client/base/src/main/java/io/a2a/client/MessageEvent.java @@ -21,6 +21,19 @@ public MessageEvent(Message message) { public Message getMessage() { return message; } -} - + @Override + public String toString() { + String messageAsString = "{" + + "role=" + message.getRole() + + ", parts=" + message.getParts() + + ", messageId=" + message.getMessageId() + + ", contextId=" + message.getContextId() + + ", taskId=" + message.getTaskId() + + ", metadata=" + message.getMetadata() + + ", kind=" + message.getKind() + + ", referenceTaskIds=" + message.getReferenceTaskIds() + + ", extensions=" + message.getExtensions() + '}'; + return "MessageEvent{" + "message=" + messageAsString + '}'; + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index d590d8890..6a8a154ac 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -96,7 +96,7 @@ public int getQueueSize() { public abstract void awaitQueuePollerStart() throws InterruptedException ; - abstract void signalQueuePollerStarted(); + public abstract void signalQueuePollerStarted(); public void enqueueEvent(Event event) { enqueueItem(new LocalEventQueueItem(event)); @@ -119,7 +119,7 @@ public void enqueueItem(EventQueueItem item) { LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); } - abstract EventQueue tap(); + public abstract EventQueue tap(); /** * Dequeues an EventQueueItem from the queue. @@ -265,7 +265,7 @@ static class MainQueue extends EventQueue { taskId, onCloseCallbacks.size(), taskStateProvider != null); } - EventQueue tap() { + public EventQueue tap() { ChildQueue child = new ChildQueue(this); children.add(child); return child; @@ -310,7 +310,7 @@ public void awaitQueuePollerStart() throws InterruptedException { } @Override - void signalQueuePollerStarted() { + public void signalQueuePollerStarted() { if (pollingStarted.get()) { return; } @@ -415,7 +415,7 @@ private void internalEnqueueItem(EventQueueItem item) { } @Override - EventQueue tap() { + public EventQueue tap() { throw new IllegalStateException("Can only tap the main queue"); } @@ -425,7 +425,7 @@ public void awaitQueuePollerStart() throws InterruptedException { } @Override - void signalQueuePollerStarted() { + public void signalQueuePollerStarted() { parent.signalQueuePollerStarted(); } From f7530b7b7743b60038e137eeca9f9d2223fc4224 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 20 Nov 2025 20:40:06 +0000 Subject: [PATCH 208/493] fix: Constants to the confg properties (#470) This came up in a Gemini review on the backport to 0.3.x This is a follow up for PR #468 and issue #467 --- .../database/jpa/JpaDatabaseTaskStore.java | 3 +- .../MicroProfileConfigProviderTest.java | 29 ++++++++++++------- .../DefaultRequestHandler.java | 7 +++-- .../util/async/AsyncExecutorProducer.java | 9 ++++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 6e63b9f5d..d38991a63 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -37,6 +37,7 @@ public class JpaDatabaseTaskStore implements TaskStore, TaskStateProvider { private static final Logger LOGGER = LoggerFactory.getLogger(JpaDatabaseTaskStore.class); + private static final String A2A_REPLICATION_GRACE_PERIOD_SECONDS = "a2a.replication.grace-period-seconds"; @PersistenceContext(unitName = "a2a-java") EntityManager em; @@ -60,7 +61,7 @@ public class JpaDatabaseTaskStore implements TaskStore, TaskStateProvider { @PostConstruct void initConfig() { - gracePeriodSeconds = Long.parseLong(configProvider.getValue("a2a.replication.grace-period-seconds")); + gracePeriodSeconds = Long.parseLong(configProvider.getValue(A2A_REPLICATION_GRACE_PERIOD_SECONDS)); } @Transactional diff --git a/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java b/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java index 1c245baf7..6ad911145 100644 --- a/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java +++ b/integrations/microprofile-config/src/test/java/io/a2a/integrations/microprofile/MicroProfileConfigProviderTest.java @@ -1,13 +1,18 @@ package io.a2a.integrations.microprofile; -import io.a2a.server.config.A2AConfigProvider; -import io.quarkus.test.junit.QuarkusTest; -import jakarta.inject.Inject; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import jakarta.inject.Inject; + +import io.a2a.server.config.A2AConfigProvider; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; /** * CDI-based test to verify that MicroProfileConfigProvider is properly selected @@ -16,6 +21,10 @@ @QuarkusTest public class MicroProfileConfigProviderTest { + private static final String A2A_EXECUTOR_CORE_POOL_SIZE = "a2a.executor.core-pool-size"; + private static final String A2A_EXECUTOR_MAX_POOL_SIZE = "a2a.executor.max-pool-size"; + private static final String A2A_EXECUTOR_KEEP_ALIVE_SECONDS = "a2a.executor.keep-alive-seconds"; + @Inject A2AConfigProvider configProvider; @@ -31,7 +40,7 @@ public void testIsMicroProfileConfigProvider() { public void testGetValueFromMicroProfileConfig() { // Test that values from application.properties override defaults // The test application.properties sets a2a.executor.core-pool-size=15 - String value = configProvider.getValue("a2a.executor.core-pool-size"); + String value = configProvider.getValue(A2A_EXECUTOR_CORE_POOL_SIZE); assertEquals("15", value, "Should get value from MicroProfile Config (application.properties)"); } @@ -39,21 +48,21 @@ public void testGetValueFromMicroProfileConfig() { public void testGetValueFallbackToDefaults() { // Test that values not in application.properties fall back to META-INF/a2a-defaults.properties // a2a.executor.max-pool-size is not in test application.properties, so should use default - String value = configProvider.getValue("a2a.executor.max-pool-size"); + String value = configProvider.getValue(A2A_EXECUTOR_MAX_POOL_SIZE); assertEquals("50", value, "Should fall back to default value from META-INF/a2a-defaults.properties"); } @Test public void testGetValueAnotherDefault() { // Test another default property to ensure fallback works - String value = configProvider.getValue("a2a.executor.keep-alive-seconds"); + String value = configProvider.getValue(A2A_EXECUTOR_KEEP_ALIVE_SECONDS); assertEquals("60", value, "Should fall back to default value"); } @Test public void testGetOptionalValueFromMicroProfileConfig() { // Test optional value that exists in application.properties - Optional value = configProvider.getOptionalValue("a2a.executor.core-pool-size"); + Optional value = configProvider.getOptionalValue(A2A_EXECUTOR_CORE_POOL_SIZE); assertTrue(value.isPresent(), "Optional value should be present"); assertEquals("15", value.get(), "Should get overridden value from MicroProfile Config"); } @@ -61,7 +70,7 @@ public void testGetOptionalValueFromMicroProfileConfig() { @Test public void testGetOptionalValueFallbackToDefaults() { // Test optional value that falls back to defaults - Optional value = configProvider.getOptionalValue("a2a.executor.max-pool-size"); + Optional value = configProvider.getOptionalValue(A2A_EXECUTOR_MAX_POOL_SIZE); assertTrue(value.isPresent(), "Optional value should be present from defaults"); assertEquals("50", value.get(), "Should get default value"); } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index f29aff575..1e8e1af94 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -73,6 +73,9 @@ public class DefaultRequestHandler implements RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRequestHandler.class); + private static final String A2A_BLOCKING_AGENT_TIMEOUT_SECONDS = "a2a.blocking.agent.timeout.seconds"; + private static final String A2A_BLOCKING_CONSUMPTION_TIMEOUT_SECONDS = "a2a.blocking.consumption.timeout.seconds"; + @Inject A2AConfigProvider configProvider; @@ -130,9 +133,9 @@ public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, @PostConstruct void initConfig() { agentCompletionTimeoutSeconds = Integer.parseInt( - configProvider.getValue("a2a.blocking.agent.timeout.seconds")); + configProvider.getValue(A2A_BLOCKING_AGENT_TIMEOUT_SECONDS)); consumptionCompletionTimeoutSeconds = Integer.parseInt( - configProvider.getValue("a2a.blocking.consumption.timeout.seconds")); + configProvider.getValue(A2A_BLOCKING_CONSUMPTION_TIMEOUT_SECONDS)); } /** diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index d85cd4de3..3d7ed0915 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -22,6 +22,9 @@ public class AsyncExecutorProducer { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncExecutorProducer.class); + private static final String A2A_EXECUTOR_CORE_POOL_SIZE = "a2a.executor.core-pool-size"; + private static final String A2A_EXECUTOR_MAX_POOL_SIZE = "a2a.executor.max-pool-size"; + private static final String A2A_EXECUTOR_KEEP_ALIVE_SECONDS = "a2a.executor.keep-alive-seconds"; @Inject A2AConfigProvider configProvider; @@ -57,9 +60,9 @@ public class AsyncExecutorProducer { @PostConstruct public void init() { - corePoolSize = Integer.parseInt(configProvider.getValue("a2a.executor.core-pool-size")); - maxPoolSize = Integer.parseInt(configProvider.getValue("a2a.executor.max-pool-size")); - keepAliveSeconds = Long.parseLong(configProvider.getValue("a2a.executor.keep-alive-seconds")); + corePoolSize = Integer.parseInt(configProvider.getValue(A2A_EXECUTOR_CORE_POOL_SIZE)); + maxPoolSize = Integer.parseInt(configProvider.getValue(A2A_EXECUTOR_MAX_POOL_SIZE)); + keepAliveSeconds = Long.parseLong(configProvider.getValue(A2A_EXECUTOR_KEEP_ALIVE_SECONDS)); LOGGER.info("Initializing async executor: corePoolSize={}, maxPoolSize={}, keepAliveSeconds={}", corePoolSize, maxPoolSize, keepAliveSeconds); From 3d6f7620e6de887ace6b89cf2046ce3bbf3d1631 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 21 Nov 2025 12:05:28 +0000 Subject: [PATCH 209/493] chore: Remove unused imports (#465) --- .../java/io/a2a/bom/test/DynamicBomVerifier.java | 1 - client/base/src/main/java/io/a2a/A2A.java | 1 - .../transport/rest/sse/RestSSEEventListener.java | 5 ----- .../a2a/examples/helloworld/HelloWorldClient.java | 1 - .../database/jpa/JpaDatabaseTaskStore.java | 2 -- .../java/io/a2a/server/TransportMetadata.java | 1 - .../io/a2a/server/extensions/A2AExtensions.java | 1 - .../io/a2a/server/tasks/InMemoryTaskStore.java | 3 --- .../io/a2a/server/tasks/ResultAggregator.java | 1 - .../java/io/a2a/server/tasks/TaskManager.java | 2 -- .../io/a2a/server/tasks/ResultAggregatorTest.java | 1 - .../java/io/a2a/server/tasks/TaskManagerTest.java | 15 +++++++-------- .../main/java/io/a2a/grpc/utils/ProtoUtils.java | 4 +--- .../java/io/a2a/spec/SendMessageResponse.java | 3 --- 14 files changed, 8 insertions(+), 33 deletions(-) diff --git a/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java b/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java index d0686e70f..e4ca498e7 100644 --- a/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java +++ b/boms/test-utils/src/main/java/io/a2a/bom/test/DynamicBomVerifier.java @@ -7,7 +7,6 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; /** diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index d64cdaa27..9056b661c 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -3,7 +3,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.UUID; import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java index d0b130eee..58a1a57f0 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java @@ -1,10 +1,5 @@ package io.a2a.client.transport.rest.sse; -import static io.a2a.grpc.StreamResponse.PayloadCase.ARTIFACT_UPDATE; -import static io.a2a.grpc.StreamResponse.PayloadCase.MSG; -import static io.a2a.grpc.StreamResponse.PayloadCase.STATUS_UPDATE; -import static io.a2a.grpc.StreamResponse.PayloadCase.TASK; - import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.logging.Logger; diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index a82438a35..8192346cd 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -12,7 +12,6 @@ import io.a2a.A2A; import io.a2a.client.Client; -import io.a2a.client.ClientBuilder; import io.a2a.client.ClientEvent; import io.a2a.client.MessageEvent; import io.a2a.client.http.A2ACardResolver; diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index d38991a63..4245cb391 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -3,9 +3,7 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; -import java.util.stream.Stream; import jakarta.annotation.PostConstruct; import jakarta.annotation.Priority; diff --git a/server-common/src/main/java/io/a2a/server/TransportMetadata.java b/server-common/src/main/java/io/a2a/server/TransportMetadata.java index 8fd2dbc1e..207ebcc67 100644 --- a/server-common/src/main/java/io/a2a/server/TransportMetadata.java +++ b/server-common/src/main/java/io/a2a/server/TransportMetadata.java @@ -1,6 +1,5 @@ package io.a2a.server; -import io.a2a.spec.TransportProtocol; /** * Interface for transport endpoint implementations to provide metadata about their transport. diff --git a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java index fec151366..0020d2cbc 100644 --- a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java +++ b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Set; -import io.a2a.common.A2AHeaders; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentExtension; diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index ba50c6cd6..c9f2f8271 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -1,12 +1,9 @@ package io.a2a.server.tasks; -import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.stream.Stream; import jakarta.enterprise.context.ApplicationScoped; diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index 26767a90b..27de1defb 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -16,7 +16,6 @@ import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.EventKind; -import io.a2a.spec.InternalError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 4b1112e22..c01c8b680 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -10,11 +10,9 @@ import java.util.Map; import io.a2a.spec.A2AServerException; -import io.a2a.spec.Artifact; import io.a2a.spec.Event; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.Message; -import io.a2a.spec.Part; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatus; diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java index 06e12e425..0db54c373 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atMost; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 1a4850bda..2edbebaed 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -1,20 +1,17 @@ package io.a2a.server.tasks; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; @@ -26,6 +23,8 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.util.Utils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class TaskManagerTest { private static final String TASK_JSON = """ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 4a69af75e..d2263a111 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -3,8 +3,8 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.time.ZoneOffset; import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,7 +13,6 @@ import com.google.protobuf.ByteString; import com.google.protobuf.Struct; import com.google.protobuf.Value; - import io.a2a.grpc.StreamResponse; import io.a2a.spec.APIKeySecurityScheme; import io.a2a.spec.AgentCapabilities; @@ -39,7 +38,6 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; diff --git a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java index 901beba90..78d7a9141 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java @@ -1,12 +1,9 @@ package io.a2a.spec; -import static io.a2a.util.Utils.defaultIfNull; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import io.a2a.util.Assert; /** * The response after receiving a send message request. From 7dd1f596f7bc2f0cfa78708ca82ff554cfc3add4 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 21 Nov 2025 16:09:49 +0000 Subject: [PATCH 210/493] feat: Add Javadoc for the spec module (#466) Fixes: #475 --- .../main/java/io/a2a/spec/A2AClientError.java | 34 ++- .../java/io/a2a/spec/A2AClientException.java | 36 ++- .../java/io/a2a/spec/A2AClientHTTPError.java | 27 ++ .../a2a/spec/A2AClientInvalidArgsError.java | 25 ++ .../a2a/spec/A2AClientInvalidStateError.java | 24 ++ .../java/io/a2a/spec/A2AClientJSONError.java | 25 ++ spec/src/main/java/io/a2a/spec/A2AError.java | 25 ++ .../main/java/io/a2a/spec/A2AException.java | 16 +- .../java/io/a2a/spec/A2AServerException.java | 16 +- .../io/a2a/spec/APIKeySecurityScheme.java | 11 +- .../java/io/a2a/spec/AgentCapabilities.java | 85 +++++- spec/src/main/java/io/a2a/spec/AgentCard.java | 262 +++++++++++++++++- .../java/io/a2a/spec/AgentCardSignature.java | 65 ++++- .../main/java/io/a2a/spec/AgentExtension.java | 61 +++- .../main/java/io/a2a/spec/AgentInterface.java | 17 +- .../main/java/io/a2a/spec/AgentProvider.java | 16 +- .../src/main/java/io/a2a/spec/AgentSkill.java | 134 ++++++++- spec/src/main/java/io/a2a/spec/Artifact.java | 96 ++++++- ...ticatedExtendedCardNotConfiguredError.java | 25 +- .../java/io/a2a/spec/AuthenticationInfo.java | 15 +- .../a2a/spec/AuthorizationCodeOAuthFlow.java | 19 +- .../java/io/a2a/spec/CancelTaskRequest.java | 16 +- .../java/io/a2a/spec/CancelTaskResponse.java | 14 +- .../a2a/spec/ClientCredentialsOAuthFlow.java | 19 +- .../spec/ContentTypeNotSupportedError.java | 33 ++- spec/src/main/java/io/a2a/spec/DataPart.java | 29 +- ...eleteTaskPushNotificationConfigParams.java | 11 +- ...leteTaskPushNotificationConfigRequest.java | 12 +- ...eteTaskPushNotificationConfigResponse.java | 10 +- spec/src/main/java/io/a2a/spec/Event.java | 25 ++ spec/src/main/java/io/a2a/spec/EventKind.java | 24 ++ .../main/java/io/a2a/spec/FileContent.java | 34 +++ spec/src/main/java/io/a2a/spec/FilePart.java | 31 ++- .../main/java/io/a2a/spec/FileWithBytes.java | 23 +- .../main/java/io/a2a/spec/FileWithUri.java | 27 +- .../GetAuthenticatedExtendedCardRequest.java | 23 +- .../GetAuthenticatedExtendedCardResponse.java | 15 +- .../GetTaskPushNotificationConfigParams.java | 12 +- .../GetTaskPushNotificationConfigRequest.java | 12 +- ...GetTaskPushNotificationConfigResponse.java | 12 +- .../main/java/io/a2a/spec/GetTaskRequest.java | 13 +- .../java/io/a2a/spec/GetTaskResponse.java | 13 +- .../io/a2a/spec/HTTPAuthSecurityScheme.java | 59 +++- .../java/io/a2a/spec/ImplicitOAuthFlow.java | 18 +- .../java/io/a2a/spec/IntegerJsonrpcId.java | 20 +- .../main/java/io/a2a/spec/InternalError.java | 22 +- .../a2a/spec/InvalidAgentResponseError.java | 33 ++- .../java/io/a2a/spec/InvalidParamsError.java | 26 +- .../java/io/a2a/spec/InvalidRequestError.java | 26 +- .../java/io/a2a/spec/JSONErrorResponse.java | 15 + .../main/java/io/a2a/spec/JSONParseError.java | 22 +- .../main/java/io/a2a/spec/JSONRPCError.java | 59 +++- .../io/a2a/spec/JSONRPCErrorResponse.java | 44 ++- .../main/java/io/a2a/spec/JSONRPCMessage.java | 36 ++- .../main/java/io/a2a/spec/JSONRPCRequest.java | 52 +++- spec/src/main/java/io/a2a/spec/JsonrpcId.java | 20 ++ .../ListTaskPushNotificationConfigParams.java | 11 +- ...ListTaskPushNotificationConfigRequest.java | 12 +- ...istTaskPushNotificationConfigResponse.java | 11 +- spec/src/main/java/io/a2a/spec/Message.java | 123 +++++++- .../io/a2a/spec/MessageSendConfiguration.java | 50 +++- .../java/io/a2a/spec/MessageSendParams.java | 48 +++- .../java/io/a2a/spec/MethodNotFoundError.java | 18 +- .../io/a2a/spec/MutualTLSSecurityScheme.java | 22 +- .../io/a2a/spec/OAuth2SecurityScheme.java | 13 +- .../src/main/java/io/a2a/spec/OAuthFlows.java | 15 +- .../a2a/spec/OpenIdConnectSecurityScheme.java | 47 +++- spec/src/main/java/io/a2a/spec/Part.java | 50 +++- .../java/io/a2a/spec/PasswordOAuthFlow.java | 18 +- .../PushNotificationAuthenticationInfo.java | 15 +- .../io/a2a/spec/PushNotificationConfig.java | 64 ++++- .../PushNotificationNotSupportedError.java | 24 +- .../main/java/io/a2a/spec/SecurityScheme.java | 20 +- .../java/io/a2a/spec/SendMessageRequest.java | 59 +++- .../a2a/spec/SendStreamingMessageRequest.java | 51 +++- .../spec/SendStreamingMessageResponse.java | 16 +- .../SetTaskPushNotificationConfigRequest.java | 13 +- ...SetTaskPushNotificationConfigResponse.java | 14 +- .../java/io/a2a/spec/StreamingEventKind.java | 34 +++ .../io/a2a/spec/StreamingJSONRPCRequest.java | 26 +- .../java/io/a2a/spec/StringJsonrpcId.java | 20 +- spec/src/main/java/io/a2a/spec/Task.java | 130 ++++++++- .../io/a2a/spec/TaskArtifactUpdateEvent.java | 58 +++- .../main/java/io/a2a/spec/TaskIdParams.java | 12 +- .../io/a2a/spec/TaskNotCancelableError.java | 26 +- .../java/io/a2a/spec/TaskNotFoundError.java | 29 +- .../a2a/spec/TaskPushNotificationConfig.java | 16 +- .../a2a/spec/TaskResubscriptionRequest.java | 19 +- spec/src/main/java/io/a2a/spec/TaskState.java | 75 ++++- .../src/main/java/io/a2a/spec/TaskStatus.java | 49 +++- spec/src/main/java/io/a2a/spec/TextPart.java | 18 +- .../java/io/a2a/spec/TransportProtocol.java | 63 ++++- .../a2a/spec/UnsupportedOperationError.java | 27 +- .../main/java/io/a2a/spec/UpdateEvent.java | 20 ++ spec/src/main/java/io/a2a/util/Utils.java | 97 +++++++ 95 files changed, 3215 insertions(+), 112 deletions(-) diff --git a/spec/src/main/java/io/a2a/spec/A2AClientError.java b/spec/src/main/java/io/a2a/spec/A2AClientError.java index 60065557a..018c468a5 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientError.java @@ -1,16 +1,48 @@ package io.a2a.spec; /** - * Base exception for A2A Client errors. + * Base exception for A2A client-specific error conditions. + *

+ * This is a specialized exception hierarchy for client-side errors, distinct from + * {@link A2AClientException}. It is used for errors that occur during client SDK + * operations such as validation, state management, and protocol handling. + *

+ * Specialized subclasses: + *

    + *
  • {@link A2AClientHTTPError} - HTTP transport errors with status codes
  • + *
  • {@link A2AClientJSONError} - JSON serialization/deserialization errors
  • + *
  • {@link A2AClientInvalidStateError} - Invalid client state errors
  • + *
  • {@link A2AClientInvalidArgsError} - Invalid argument errors
  • + *
+ * + * @see A2AClientException for general client exceptions + * @see A2AClientHTTPError for HTTP-specific errors + * @see A2AClientJSONError for JSON-specific errors + * @see A2AClientInvalidStateError for invalid state errors + * @see A2AClientInvalidArgsError for invalid argument errors */ public class A2AClientError extends RuntimeException { + /** + * Constructs a new A2AClientError with no detail message. + */ public A2AClientError() { } + /** + * Constructs a new A2AClientError with the specified detail message. + * + * @param message the detail message + */ public A2AClientError(String message) { super(message); } + /** + * Constructs a new A2AClientError with the specified detail message and cause. + * + * @param message the detail message + * @param cause the cause of this exception + */ public A2AClientError(String message, Throwable cause) { super(message, cause); } diff --git a/spec/src/main/java/io/a2a/spec/A2AClientException.java b/spec/src/main/java/io/a2a/spec/A2AClientException.java index 17ff073d5..72eeebd47 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientException.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientException.java @@ -1,22 +1,56 @@ package io.a2a.spec; /** - * Exception to indicate a general failure related to an A2A client. + * Exception indicating a client-side failure in A2A Protocol operations. + *

+ * This exception is thrown by A2A client implementations when encountering errors + * during communication with agents, response validation, or client-side processing. + *

+ * Common scenarios: + *

    + *
  • Network communication failures
  • + *
  • Invalid agent responses ({@link A2AClientError})
  • + *
  • HTTP errors ({@link A2AClientHTTPError})
  • + *
  • JSON parsing errors ({@link A2AClientJSONError})
  • + *
+ * + * @see A2AException for the base exception class + * @see A2AServerException for server-side errors + * @see A2AClientError for more specific client errors */ public class A2AClientException extends A2AException { + /** + * Constructs a new A2AClientException with no detail message or cause. + */ public A2AClientException() { super(); } + /** + * Constructs a new A2AClientException with the specified detail message. + * + * @param msg the detail message + */ public A2AClientException(final String msg) { super(msg); } + /** + * Constructs a new A2AClientException with the specified cause. + * + * @param cause the cause of this exception + */ public A2AClientException(final Throwable cause) { super(cause); } + /** + * Constructs a new A2AClientException with the specified detail message and cause. + * + * @param msg the detail message + * @param cause the cause of this exception + */ public A2AClientException(final String msg, final Throwable cause) { super(msg, cause); } diff --git a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java index eab7aecf7..abaf9e263 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java @@ -2,6 +2,33 @@ import io.a2a.util.Assert; +/** + * Client exception indicating an HTTP transport error with a specific status code. + *

+ * This exception is thrown when HTTP communication with an A2A agent fails, + * capturing both the HTTP status code and error message. It is used for non-2xx + * HTTP responses that don't contain valid A2A Protocol error responses. + *

+ * Common HTTP status codes: + *

    + *
  • 4xx - Client errors (400 Bad Request, 401 Unauthorized, 404 Not Found, etc.)
  • + *
  • 5xx - Server errors (500 Internal Server Error, 503 Service Unavailable, etc.)
  • + *
+ *

+ * Usage example: + *

{@code
+ * if (response.statusCode() >= 400) {
+ *     throw new A2AClientHTTPError(
+ *         response.statusCode(),
+ *         "HTTP error: " + response.statusMessage(),
+ *         response.body()
+ *     );
+ * }
+ * }
+ * + * @see A2AClientError for the base client error class + * @see HTTP Status Codes + */ public class A2AClientHTTPError extends A2AClientError { private final int code; private final String message; diff --git a/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java b/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java index c39c53350..20ad84c09 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java @@ -1,5 +1,30 @@ package io.a2a.spec; +/** + * Client exception indicating invalid arguments provided to a client operation. + *

+ * This exception is thrown when parameters passed to A2A client SDK methods fail + * validation. This is distinct from {@link InvalidParamsError}, which represents + * server-side parameter validation errors. + *

+ * Common scenarios: + *

    + *
  • Null values for required parameters
  • + *
  • Invalid parameter combinations
  • + *
  • Parameter values outside acceptable ranges
  • + *
  • Malformed URIs or identifiers
  • + *
+ *

+ * Usage example: + *

{@code
+ * if (agentUrl == null || agentUrl.isEmpty()) {
+ *     throw new A2AClientInvalidArgsError("agentUrl cannot be null or empty");
+ * }
+ * }
+ * + * @see A2AClientError for the base client error class + * @see InvalidParamsError for server-side parameter errors + */ public class A2AClientInvalidArgsError extends A2AClientError { public A2AClientInvalidArgsError() { diff --git a/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java b/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java index e828fe95d..d08e18942 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java @@ -1,5 +1,29 @@ package io.a2a.spec; +/** + * Client exception indicating an invalid state for the requested operation. + *

+ * This exception is thrown when a client operation is attempted while the client + * is in a state that doesn't permit that operation. This ensures proper sequencing + * and lifecycle management of client operations. + *

+ * Common scenarios: + *

    + *
  • Attempting to send messages before client initialization
  • + *
  • Trying to cancel a task after the client connection is closed
  • + *
  • Performing operations on a client that has been shut down
  • + *
  • Violating client state machine transitions
  • + *
+ *

+ * Usage example: + *

{@code
+ * if (!client.isConnected()) {
+ *     throw new A2AClientInvalidStateError("Client not connected");
+ * }
+ * }
+ * + * @see A2AClientError for the base client error class + */ public class A2AClientInvalidStateError extends A2AClientError { public A2AClientInvalidStateError() { diff --git a/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java index 75988da1c..e558d672a 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java @@ -1,5 +1,30 @@ package io.a2a.spec; +/** + * Client exception indicating a JSON serialization or deserialization error. + *

+ * This exception is thrown when the A2A client SDK encounters errors while + * parsing JSON responses from agents or serializing requests. This typically + * indicates: + *

    + *
  • Malformed JSON in agent responses
  • + *
  • Unexpected JSON structure or field types
  • + *
  • Missing required JSON fields
  • + *
  • JSON encoding/decoding errors
  • + *
+ *

+ * Usage example: + *

{@code
+ * try {
+ *     AgentCard card = objectMapper.readValue(json, AgentCard.class);
+ * } catch (JsonProcessingException e) {
+ *     throw new A2AClientJSONError("Failed to parse agent card", e);
+ * }
+ * }
+ * + * @see A2AClientError for the base client error class + * @see JSONParseError for protocol-level JSON errors + */ public class A2AClientJSONError extends A2AClientError { public A2AClientJSONError() { diff --git a/spec/src/main/java/io/a2a/spec/A2AError.java b/spec/src/main/java/io/a2a/spec/A2AError.java index 4c9951df2..122b26a0d 100644 --- a/spec/src/main/java/io/a2a/spec/A2AError.java +++ b/spec/src/main/java/io/a2a/spec/A2AError.java @@ -1,4 +1,29 @@ package io.a2a.spec; +/** + * Marker interface for A2A Protocol error events. + *

+ * This interface extends {@link Event} to allow errors to be transmitted as events + * in the A2A Protocol's event stream. All protocol-level errors implement this interface, + * enabling uniform error handling across both streaming and non-streaming communication. + *

+ * A2A errors typically extend {@link JSONRPCError} to provide JSON-RPC 2.0 compliant + * error responses with standard error codes, messages, and optional additional data. + *

+ * Common implementations include: + *

    + *
  • {@link InvalidParamsError} - Invalid method parameters
  • + *
  • {@link InvalidRequestError} - Malformed request
  • + *
  • {@link MethodNotFoundError} - Unknown method
  • + *
  • {@link InternalError} - Server-side error
  • + *
  • {@link TaskNotFoundError} - Task does not exist
  • + *
  • And others for specific protocol error conditions
  • + *
+ * + * @see Event for the base event interface + * @see JSONRPCError for the base error implementation + * @see JSON-RPC 2.0 Error Object + * @see A2A Protocol Specification + */ public interface A2AError extends Event { } diff --git a/spec/src/main/java/io/a2a/spec/A2AException.java b/spec/src/main/java/io/a2a/spec/A2AException.java index a97bbd570..e48b5fc81 100644 --- a/spec/src/main/java/io/a2a/spec/A2AException.java +++ b/spec/src/main/java/io/a2a/spec/A2AException.java @@ -1,7 +1,21 @@ package io.a2a.spec; /** - * Exception to indicate a general failure related to the A2A protocol. + * Base exception for A2A Protocol-related failures. + *

+ * This is the root exception class for all A2A Protocol exceptions, providing a common + * base type for exception handling. It extends {@link RuntimeException} to allow unchecked + * exception propagation throughout A2A client and server implementations. + *

+ * Specialized subclasses: + *

    + *
  • {@link A2AServerException} - Server-side failures
  • + *
  • {@link A2AClientException} - Client-side failures
  • + *
+ * + * @see A2AServerException for server-side errors + * @see A2AClientException for client-side errors + * @see A2A Protocol Specification */ public class A2AException extends RuntimeException { diff --git a/spec/src/main/java/io/a2a/spec/A2AServerException.java b/spec/src/main/java/io/a2a/spec/A2AServerException.java index ca2611c2f..594502776 100644 --- a/spec/src/main/java/io/a2a/spec/A2AServerException.java +++ b/spec/src/main/java/io/a2a/spec/A2AServerException.java @@ -1,7 +1,21 @@ package io.a2a.spec; /** - * Exception to indicate a general failure related to an A2A server. + * Exception indicating a server-side failure in A2A Protocol operations. + *

+ * This exception is thrown by A2A server implementations when encountering errors + * during request processing, task execution, or other server-side operations. + *

+ * Common scenarios: + *

    + *
  • Agent execution failures
  • + *
  • Task store persistence errors
  • + *
  • Resource exhaustion or limits exceeded
  • + *
  • Configuration errors
  • + *
+ * + * @see A2AException for the base exception class + * @see A2AClientException for client-side errors */ public class A2AServerException extends A2AException { diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index 028fea303..bf814bd40 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -12,7 +12,16 @@ import static io.a2a.spec.APIKeySecurityScheme.API_KEY; /** - * Defines a security scheme using an API key. + * API key security scheme for agent authentication. + *

+ * This security scheme uses an API key that can be sent in a header, query parameter, + * or cookie to authenticate requests to the agent. + *

+ * Corresponds to the OpenAPI "apiKey" security scheme type. + * + * @see SecurityScheme for the base interface + * @see OpenAPI Security Scheme + * @see A2A Protocol Specification */ @JsonTypeName(API_KEY) @JsonInclude(JsonInclude.Include.NON_ABSENT) diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java index 1c6fdf1b2..c35cd39f5 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java @@ -6,13 +6,54 @@ import com.fasterxml.jackson.annotation.JsonInclude; /** - * Defines optional capabilities supported by an agent. + * Defines optional capabilities supported by an agent in the A2A Protocol. + *

+ * AgentCapabilities advertises which advanced features an agent supports beyond the + * basic request-response pattern. Clients can inspect these capabilities to determine + * how to interact with the agent and what features are available. + *

+ * Core Capabilities: + *

    + *
  • streaming: Agent can produce incremental results via streaming responses, + * allowing clients to receive partial artifacts as they are generated rather than + * waiting for task completion
  • + *
  • pushNotifications: Agent can send proactive notifications to clients + * when task state changes, eliminating the need for polling
  • + *
  • stateTransitionHistory: Agent maintains and provides a complete history + * of all state transitions for tasks, useful for debugging and auditing
  • + *
+ *

+ * Capabilities are declared in the {@link AgentCard} and are immutable for the lifetime + * of the agent instance. This class uses the Builder pattern for construction. + * + * @param streaming whether the agent supports streaming responses with incremental artifacts + * @param pushNotifications whether the agent supports push notifications for state changes + * @param stateTransitionHistory whether the agent maintains state transition history + * @param extensions list of custom extensions supported by the agent (optional) + * @see AgentCard + * @see AgentExtension + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record AgentCapabilities(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory, List extensions) { + /** + * Builder for constructing immutable {@link AgentCapabilities} instances. + *

+ * The Builder pattern provides a fluent API for setting capability flags. + * All capabilities default to false if not explicitly set. + *

+ * Example usage: + *

{@code
+     * AgentCapabilities capabilities = new AgentCapabilities.Builder()
+     *     .streaming(true)
+     *     .pushNotifications(false)
+     *     .stateTransitionHistory(false)
+     *     .build();
+     * }
+ */ public static class Builder { private boolean streaming; @@ -20,26 +61,68 @@ public static class Builder { private boolean stateTransitionHistory; private List extensions; + /** + * Sets whether the agent supports streaming responses. + *

+ * When enabled, clients can subscribe to task updates and receive + * incremental artifacts as the agent produces them. + * + * @param streaming true if streaming is supported, false otherwise + * @return this builder for method chaining + */ public Builder streaming(boolean streaming) { this.streaming = streaming; return this; } + /** + * Sets whether the agent supports push notifications. + *

+ * When enabled, the agent can proactively notify clients of task + * state changes, eliminating the need for polling. + * + * @param pushNotifications true if push notifications are supported, false otherwise + * @return this builder for method chaining + */ public Builder pushNotifications(boolean pushNotifications) { this.pushNotifications = pushNotifications; return this; } + /** + * Sets whether the agent maintains state transition history. + *

+ * When enabled, the agent tracks and provides a complete history + * of all state transitions for each task. + * + * @param stateTransitionHistory true if state history is maintained, false otherwise + * @return this builder for method chaining + */ public Builder stateTransitionHistory(boolean stateTransitionHistory) { this.stateTransitionHistory = stateTransitionHistory; return this; } + /** + * Sets the list of custom extensions supported by the agent. + *

+ * Extensions allow agents to advertise proprietary or experimental + * capabilities beyond the core A2A Protocol. + * + * @param extensions list of agent extensions (optional) + * @return this builder for method chaining + * @see AgentExtension + */ public Builder extensions(List extensions) { this.extensions = extensions; return this; } + /** + * Builds an immutable {@link AgentCapabilities} from the current builder state. + * + * @return a new AgentCapabilities instance + */ public AgentCapabilities build() { return new AgentCapabilities(streaming, pushNotifications, stateTransitionHistory, extensions); } diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index b59a9403b..80df1df7f 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -9,22 +9,70 @@ import io.a2a.util.Assert; /** - * The AgentCard is a self-describing manifest for an agent. It provides essential - * metadata including the agent's identity, capabilities, skills, supported - * communication methods, and security requirements. + * The AgentCard is a self-describing manifest for an agent in the A2A Protocol. + *

+ * An AgentCard provides essential metadata about an agent, including its identity, capabilities, + * supported skills, communication methods, and security requirements. It serves as the primary + * discovery mechanism for clients to understand what an agent can do and how to interact with it. + *

+ * The AgentCard corresponds to the {@code AgentCard} type in the A2A Protocol specification, + * defining the contract between clients and agents for capability advertisement. + *

+ * This class is immutable and uses the Builder pattern for construction to handle the mix of + * required and optional fields defined by the specification. + * + * @param name the human-readable name of the agent (required) + * @param description a brief description of the agent's purpose and functionality (required) + * @param url the base URL where the agent can be accessed (required) + * @param provider information about the organization or entity providing the agent (optional) + * @param version the version of the agent implementation (required) + * @param documentationUrl URL to human-readable documentation for the agent (optional) + * @param capabilities the capabilities supported by this agent (required) + * @param defaultInputModes list of supported input modes, e.g., "text", "audio" (required) + * @param defaultOutputModes list of supported output modes, e.g., "text", "audio" (required) + * @param skills list of skills that this agent can perform (required) + * @param supportsAuthenticatedExtendedCard whether the agent supports authenticated extended card retrieval (optional, defaults to false) + * @param securitySchemes map of security scheme names to their definitions (optional) + * @param security list of security requirements for accessing the agent (optional) + * @param iconUrl URL to an icon representing the agent (optional) + * @param additionalInterfaces list of additional transport interfaces beyond the primary url/transport (optional) + * @param preferredTransport the preferred transport protocol, e.g., "jsonrpc", "grpc" (defaults to "jsonrpc") + * @param protocolVersion the version of the A2A Protocol this agent implements (defaults to {@link #DEFAULT_PROTOCOL_VERSION}) + * @param signatures digital signatures verifying the authenticity of the agent card (optional) + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record AgentCard(String name, String description, String url, AgentProvider provider, - String version, String documentationUrl, AgentCapabilities capabilities, - List defaultInputModes, List defaultOutputModes, List skills, - boolean supportsAuthenticatedExtendedCard, Map securitySchemes, - List>> security, String iconUrl, List additionalInterfaces, - String preferredTransport, String protocolVersion, List signatures) { - - private static final String DEFAULT_PROTOCOL_VERSION = "0.3.0"; +public record AgentCard( + String name, + String description, + String url, + AgentProvider provider, + String version, + String documentationUrl, + AgentCapabilities capabilities, + List defaultInputModes, + List defaultOutputModes, + List skills, + boolean supportsAuthenticatedExtendedCard, + Map securitySchemes, + List>> security, + String iconUrl, + List additionalInterfaces, + String preferredTransport, + String protocolVersion, + List signatures) { + + /** The default A2A Protocol version used when not explicitly specified. */ + public static final String DEFAULT_PROTOCOL_VERSION = "0.3.0"; + private static final TransportProtocol DEFAULT_TRANSPORT = TransportProtocol.JSONRPC; + /** + * Compact constructor that validates required fields and sets defaults. + * + * @throws IllegalArgumentException if any required field is null + */ public AgentCard { Assert.checkNotNullParam("capabilities", capabilities); Assert.checkNotNullParam("defaultInputModes", defaultInputModes); @@ -42,6 +90,35 @@ public record AgentCard(String name, String description, String url, AgentProvid } } + /** + * Builder for constructing immutable {@link AgentCard} instances. + *

+ * The Builder pattern is used to enforce immutability of AgentCard objects while providing + * a fluent API for setting required and optional fields. This approach ensures that once + * an AgentCard is created, its state cannot be modified, which is important for thread-safety + * and protocol correctness. + *

+ * Example usage: + *

{@code
+     * AgentCard card = new AgentCard.Builder()
+     *     .name("Weather Agent")
+     *     .description("Provides weather information")
+     *     .url("http://localhost:9999")
+     *     .version("1.0.0")
+     *     .capabilities(new AgentCapabilities.Builder()
+     *         .streaming(true)
+     *         .build())
+     *     .defaultInputModes(List.of("text"))
+     *     .defaultOutputModes(List.of("text"))
+     *     .skills(List.of(
+     *         new AgentSkill.Builder()
+     *             .id("weather_query")
+     *             .name("Weather Queries")
+     *             .build()
+     *     ))
+     *     .build();
+     * }
+ */ public static class Builder { private String name; private String description; @@ -63,16 +140,19 @@ public static class Builder { private List signatures; /** - * Creates a new Builder. + * Creates a new Builder with all fields unset. */ public Builder() { } /** - * Creates a new Builder as a copy of an existing AgentCard. + * Creates a new Builder initialized with values from an existing AgentCard. + *

+ * This constructor creates defensive copies of mutable collections to ensure + * that modifications to the builder do not affect the original AgentCard. * - * @param card the AgentCard to copy + * @param card the AgentCard to copy values from */ public Builder(AgentCard card) { this.name = card.name; @@ -95,96 +175,250 @@ public Builder(AgentCard card) { this.signatures = card.signatures != null ? new ArrayList<>(card.signatures) : null; } + /** + * Sets the human-readable name of the agent. + * + * @param name the agent name (required) + * @return this builder for method chaining + */ public Builder name(String name) { this.name = name; return this; } + /** + * Sets a brief description of the agent's purpose and functionality. + * + * @param description the agent description (required) + * @return this builder for method chaining + */ public Builder description(String description) { this.description = description; return this; } + /** + * Sets the base URL where the agent can be accessed. + * + * @param url the agent URL (required) + * @return this builder for method chaining + */ public Builder url(String url) { this.url = url; return this; } + /** + * Sets information about the organization or entity providing the agent. + * + * @param provider the agent provider (optional) + * @return this builder for method chaining + */ public Builder provider(AgentProvider provider) { this.provider = provider; return this; } + /** + * Sets the version of the agent implementation. + * + * @param version the agent version (required) + * @return this builder for method chaining + */ public Builder version(String version) { this.version = version; return this; } + /** + * Sets the URL to human-readable documentation for the agent. + * + * @param documentationUrl the documentation URL (optional) + * @return this builder for method chaining + */ public Builder documentationUrl(String documentationUrl) { this.documentationUrl = documentationUrl; return this; } + /** + * Sets the capabilities supported by this agent. + *

+ * Capabilities define optional features such as streaming responses, + * push notifications, and state transition history. + * + * @param capabilities the agent capabilities (required) + * @return this builder for method chaining + * @see AgentCapabilities + */ public Builder capabilities(AgentCapabilities capabilities) { this.capabilities = capabilities; return this; } + /** + * Sets the list of supported input modes. + *

+ * Input modes define the formats the agent can accept, such as "text", "audio", or "image". + * + * @param defaultInputModes the list of input modes (required, must not be empty) + * @return this builder for method chaining + */ public Builder defaultInputModes(List defaultInputModes) { this.defaultInputModes = defaultInputModes; return this; } + /** + * Sets the list of supported output modes. + *

+ * Output modes define the formats the agent can produce, such as "text", "audio", or "image". + * + * @param defaultOutputModes the list of output modes (required, must not be empty) + * @return this builder for method chaining + */ public Builder defaultOutputModes(List defaultOutputModes) { this.defaultOutputModes = defaultOutputModes; return this; } + /** + * Sets the list of skills that this agent can perform. + *

+ * Skills represent distinct capabilities or operations the agent can execute, + * such as "weather_query" or "language_translation". + * + * @param skills the list of agent skills (required, must not be empty) + * @return this builder for method chaining + * @see AgentSkill + */ public Builder skills(List skills) { this.skills = skills; return this; } + /** + * Sets whether the agent supports authenticated extended card retrieval. + *

+ * When true, the agent can provide additional information through the + * {@code GetAuthenticatedExtendedCard} method, which may include private + * or user-specific details not in the public card. + * + * @param supportsAuthenticatedExtendedCard true if supported, false otherwise + * @return this builder for method chaining + */ public Builder supportsAuthenticatedExtendedCard(boolean supportsAuthenticatedExtendedCard) { this.supportsAuthenticatedExtendedCard = supportsAuthenticatedExtendedCard; return this; } + /** + * Sets the map of security scheme definitions. + *

+ * Security schemes define authentication and authorization methods supported + * by the agent, such as OAuth2, API keys, or HTTP authentication. + * + * @param securitySchemes map of scheme names to definitions (optional) + * @return this builder for method chaining + * @see SecurityScheme + */ public Builder securitySchemes(Map securitySchemes) { this.securitySchemes = securitySchemes; return this; } + /** + * Sets the list of security requirements for accessing the agent. + *

+ * Each entry in the list represents an alternative security requirement, + * where each map contains scheme names and their required scopes. + * + * @param security the list of security requirements (optional) + * @return this builder for method chaining + */ public Builder security(List>> security) { this.security = security; return this; } + /** + * Sets the URL to an icon representing the agent. + * + * @param iconUrl the icon URL (optional) + * @return this builder for method chaining + */ public Builder iconUrl(String iconUrl) { this.iconUrl = iconUrl; return this; } + /** + * Sets the list of additional transport interfaces. + *

+ * Additional interfaces allow the agent to be accessed through multiple + * transport protocols beyond the primary url/preferredTransport combination. + * + * @param additionalInterfaces the list of additional interfaces (optional) + * @return this builder for method chaining + * @see AgentInterface + */ public Builder additionalInterfaces(List additionalInterfaces) { this.additionalInterfaces = additionalInterfaces; return this; } + /** + * Sets the preferred transport protocol. + *

+ * Valid values include "jsonrpc", "grpc", and "rest". If not set, defaults to "jsonrpc". + * + * @param preferredTransport the preferred transport protocol name + * @return this builder for method chaining + * @see TransportProtocol + */ public Builder preferredTransport(String preferredTransport) { this.preferredTransport = preferredTransport; return this; } + /** + * Sets the version of the A2A Protocol this agent implements. + *

+ * If not set, defaults to {@link AgentCard#DEFAULT_PROTOCOL_VERSION}. + * + * @param protocolVersion the protocol version string + * @return this builder for method chaining + */ public Builder protocolVersion(String protocolVersion) { this.protocolVersion = protocolVersion; return this; } + /** + * Sets the digital signatures verifying the authenticity of the agent card. + *

+ * Signatures provide cryptographic proof that the agent card was issued by + * a trusted authority and has not been tampered with. + * + * @param signatures the list of signatures (optional) + * @return this builder for method chaining + * @see AgentCardSignature + */ public Builder signatures(List signatures) { this.signatures = signatures; return this; } + /** + * Builds an immutable {@link AgentCard} from the current builder state. + *

+ * This method applies default values for optional fields and creates an entry + * in additionalInterfaces matching the primary url and preferredTransport if + * additionalInterfaces was not explicitly set. + * + * @return a new AgentCard instance + * @throws IllegalArgumentException if any required field is null + */ public AgentCard build() { if (preferredTransport == null) { preferredTransport = DEFAULT_TRANSPORT.asString(); diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java index 4e383d998..5a5275a65 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -9,8 +9,30 @@ import io.a2a.util.Assert; /** - * Represents a JWS signature of an AgentCard. - * This follows the JSON format of an RFC 7515 JSON Web Signature (JWS). + * Represents a digital signature for an {@link AgentCard} using JSON Web Signature (JWS) format. + *

+ * AgentCardSignature provides cryptographic proof that an AgentCard was issued by a trusted + * authority and has not been tampered with. This enables verification of agent authenticity + * and integrity in security-sensitive scenarios. + *

+ * The signature follows RFC 7515 JSON Web Signature (JWS) specification, consisting of: + *

    + *
  • A protected header (Base64URL-encoded JSON) containing algorithm and key information
  • + *
  • An optional unprotected header with additional metadata
  • + *
  • The signature value itself
  • + *
+ *

+ * Multiple signatures can be included in an AgentCard to support different verification + * authorities or key algorithms. + *

+ * This class is immutable. Use the {@link Builder} for construction. + * + * @param header optional unprotected header with additional metadata (optional) + * @param protectedHeader Base64URL-encoded protected header containing algorithm and key info (required) + * @param signature the Base64URL-encoded signature value (required) + * @see AgentCard + * @see RFC 7515 - JSON Web Signature + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -22,26 +44,65 @@ public record AgentCardSignature(Map header, @JsonProperty("prot Assert.checkNotNullParam("signature", signature); } + /** + * Builder for constructing immutable {@link AgentCardSignature} instances. + *

+ * Example usage: + *

{@code
+     * AgentCardSignature sig = new AgentCardSignature.Builder()
+     *     .protectedHeader("eyJhbGciOiJFUzI1NiJ9")
+     *     .signature("DtEhU3ljbEg8L38VWAfUAqOyKAM6...")
+     *     .header(Map.of("kid", "2024-01"))
+     *     .build();
+     * }
+ */ public static class Builder { private Map header; String protectedHeader; String signature; + /** + * Sets the optional unprotected header with additional metadata. + * + * @param header map of header parameters (optional) + * @return this builder for method chaining + */ public Builder header(Map header) { this.header = header; return this; } + /** + * Sets the Base64URL-encoded protected header. + *

+ * The protected header typically contains the algorithm ("alg") and may include + * key identification ("kid") or other parameters that need integrity protection. + * + * @param protectedHeader the Base64URL-encoded protected header (required) + * @return this builder for method chaining + */ public Builder protectedHeader(String protectedHeader) { this.protectedHeader = protectedHeader; return this; } + /** + * Sets the Base64URL-encoded signature value. + * + * @param signature the Base64URL-encoded signature (required) + * @return this builder for method chaining + */ public Builder signature(String signature) { this.signature = signature; return this; } + /** + * Builds a new immutable {@link AgentCardSignature} from the current builder state. + * + * @return a new AgentCardSignature instance + * @throws IllegalArgumentException if protectedHeader or signature is null + */ public AgentCardSignature build() { return new AgentCardSignature(header, protectedHeader, signature); } diff --git a/spec/src/main/java/io/a2a/spec/AgentExtension.java b/spec/src/main/java/io/a2a/spec/AgentExtension.java index 053855976..8bb5dade3 100644 --- a/spec/src/main/java/io/a2a/spec/AgentExtension.java +++ b/spec/src/main/java/io/a2a/spec/AgentExtension.java @@ -5,7 +5,23 @@ import io.a2a.util.Assert; /** - * A declaration of a protocol extension supported by an Agent. + * Represents a protocol extension supported by an agent. + *

+ * AgentExtension declares optional or required capabilities beyond the core A2A Protocol + * specification. Extensions allow agents to advertise support for additional features, + * behaviors, or custom protocol enhancements identified by a unique URI. + *

+ * Extensions may include parameters for configuration and can be marked as required, + * indicating that clients must support the extension to interact with the agent successfully. + *

+ * This class is immutable. Use the {@link Builder} for construction. + * + * @param description a human-readable description of the extension's purpose (optional) + * @param params configuration parameters for the extension (optional) + * @param required whether support for this extension is mandatory for clients (defaults to false) + * @param uri the unique identifier URI for this extension (required) + * @see AgentCard + * @see A2A Protocol Specification */ public record AgentExtension (String description, Map params, boolean required, String uri) { @@ -13,32 +29,75 @@ public record AgentExtension (String description, Map params, bo Assert.checkNotNullParam("uri", uri); } + /** + * Builder for constructing immutable {@link AgentExtension} instances. + *

+ * Example usage: + *

{@code
+     * AgentExtension ext = new AgentExtension.Builder()
+     *     .uri("https://example.com/extensions/custom-auth")
+     *     .description("Custom authentication extension")
+     *     .required(true)
+     *     .params(Map.of("authType", "bearer"))
+     *     .build();
+     * }
+ */ public static class Builder { String description; Map params; boolean required; String uri; + /** + * Sets a human-readable description of the extension's purpose. + * + * @param description the extension description (optional) + * @return this builder for method chaining + */ public Builder description(String description) { this.description = description; return this; } + /** + * Sets configuration parameters for the extension. + * + * @param params map of parameter key-value pairs (optional) + * @return this builder for method chaining + */ public Builder params(Map params) { this.params = params; return this; } + /** + * Sets whether support for this extension is mandatory. + * + * @param required true if clients must support this extension (defaults to false) + * @return this builder for method chaining + */ public Builder required(boolean required) { this.required = required; return this; } + /** + * Sets the unique identifier URI for this extension. + * + * @param uri the extension URI (required) + * @return this builder for method chaining + */ public Builder uri(String uri) { this.uri = uri; return this; } + /** + * Builds a new immutable {@link AgentExtension} from the current builder state. + * + * @return a new AgentExtension instance + * @throws IllegalArgumentException if uri is null + */ public AgentExtension build() { return new AgentExtension(description, params, required, uri); } diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index db81ce8f0..dd19ed4b5 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -5,7 +5,22 @@ import io.a2a.util.Assert; /** - * Declares a combination of a target URL and a transport protocol for interacting with the agent. + * Declares a combination of a target URL and transport protocol for accessing an agent. + *

+ * AgentInterface defines how clients can connect to and communicate with an agent using + * a specific transport mechanism (such as JSON-RPC, gRPC, or REST) at a particular endpoint. + *

+ * Agents may support multiple interfaces to allow flexibility in how clients communicate. + * The {@link AgentCard} includes a primary interface (url + preferredTransport) and may + * list additional interfaces for alternative access methods. + *

+ * This class is immutable. + * + * @param transport the transport protocol name (e.g., "jsonrpc", "grpc", "rest") (required) + * @param url the endpoint URL for this transport interface (required) + * @see AgentCard + * @see TransportProtocol + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AgentProvider.java b/spec/src/main/java/io/a2a/spec/AgentProvider.java index fa57b4478..2ee796b7d 100644 --- a/spec/src/main/java/io/a2a/spec/AgentProvider.java +++ b/spec/src/main/java/io/a2a/spec/AgentProvider.java @@ -5,7 +5,21 @@ import io.a2a.util.Assert; /** - * Represents the service provider of an agent. + * Represents information about the organization or entity providing an agent. + *

+ * AgentProvider contains metadata about who is responsible for operating and maintaining + * an agent. This information helps users understand the source and trustworthiness of + * the agent, and provides contact or documentation references. + *

+ * Provider information is included in the {@link AgentCard} to identify the organization + * behind the agent service. + *

+ * This class is immutable. + * + * @param organization the name of the organization providing the agent (required) + * @param url the URL to the provider's website or information page (required) + * @see AgentCard + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java index 3802b3418..be701dc6f 100644 --- a/spec/src/main/java/io/a2a/spec/AgentSkill.java +++ b/spec/src/main/java/io/a2a/spec/AgentSkill.java @@ -8,7 +8,38 @@ import io.a2a.util.Assert; /** - * The set of skills, or distinct capabilities, that the agent can perform. + * Represents a distinct skill or capability that an agent can perform in the A2A Protocol. + *

+ * An AgentSkill defines a specific operation or category of operations that the agent supports. + * Skills provide a structured way to advertise what an agent can do, helping clients discover + * and invoke appropriate functionality. Each skill is uniquely identified and includes metadata + * about supported input/output modes, examples, and security requirements. + *

+ * Key Components: + *

    + *
  • Identity: Unique ID and human-readable name for discovery and invocation
  • + *
  • Documentation: Description, examples, and tags for understanding usage
  • + *
  • Modes: Supported input/output formats (text, audio, image, etc.)
  • + *
  • Security: Specific authentication/authorization requirements for this skill
  • + *
+ *

+ * Skills are declared in the {@link AgentCard} and represent the agent's advertised capabilities. + * Clients can query available skills to understand what operations are supported and how to + * invoke them. If inputModes/outputModes are not specified, the skill inherits the defaults + * from the AgentCard. + *

+ * This class is immutable and uses the Builder pattern for construction. + * + * @param id unique identifier for the skill (required, e.g., "weather_query", "translate_text") + * @param name human-readable name of the skill (required, e.g., "Weather Queries") + * @param description detailed explanation of what the skill does and how to use it (required) + * @param tags categorization tags for discovery and filtering (required, may be empty list) + * @param examples example queries or use cases demonstrating the skill (optional) + * @param inputModes supported input formats for this skill (optional, inherits from AgentCard if not set) + * @param outputModes supported output formats for this skill (optional, inherits from AgentCard if not set) + * @param security security requirements specific to this skill (optional) + * @see AgentCard + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -23,6 +54,29 @@ public record AgentSkill(String id, String name, String description, List + * The Builder pattern provides a fluent API for setting skill properties. + * This approach ensures that once an AgentSkill is created, its state cannot + * be modified, which is important for protocol correctness and thread-safety. + *

+ * Example usage: + *

{@code
+     * AgentSkill skill = new AgentSkill.Builder()
+     *     .id("weather_query")
+     *     .name("Weather Queries")
+     *     .description("Get current weather conditions for any location")
+     *     .tags(List.of("weather", "information"))
+     *     .examples(List.of(
+     *         "What's the weather in Tokyo?",
+     *         "Current temperature in London"
+     *     ))
+     *     .inputModes(List.of("text"))
+     *     .outputModes(List.of("text"))
+     *     .build();
+     * }
+ */ public static class Builder { private String id; @@ -34,46 +88,124 @@ public static class Builder { private List outputModes; private List>> security; + /** + * Sets the unique identifier for the skill. + *

+ * The ID should be a stable identifier that doesn't change across versions, + * typically in snake_case format (e.g., "weather_query", "translate_text"). + * + * @param id the skill ID (required) + * @return this builder for method chaining + */ public Builder id(String id) { this.id = id; return this; } + /** + * Sets the human-readable name of the skill. + *

+ * The name is displayed to users and should clearly describe the skill's purpose. + * + * @param name the skill name (required) + * @return this builder for method chaining + */ public Builder name(String name) { this.name = name; return this; } + /** + * Sets a detailed description of what the skill does and how to use it. + *

+ * The description should provide sufficient information for users to understand + * when and how to invoke the skill. + * + * @param description the skill description (required) + * @return this builder for method chaining + */ public Builder description(String description) { this.description = description; return this; } + /** + * Sets categorization tags for discovery and filtering. + *

+ * Tags help organize skills and enable clients to filter or search for + * specific categories of functionality. + * + * @param tags list of tags (required, may be empty) + * @return this builder for method chaining + */ public Builder tags(List tags) { this.tags = tags; return this; } + /** + * Sets example queries or use cases demonstrating the skill. + *

+ * Examples help users understand how to phrase requests to effectively + * invoke the skill. + * + * @param examples list of example queries (optional) + * @return this builder for method chaining + */ public Builder examples(List examples) { this.examples = examples; return this; } + /** + * Sets the supported input formats for this skill. + *

+ * If not specified, the skill inherits the default input modes from the AgentCard. + * Common values include "text", "audio", "image", "video". + * + * @param inputModes list of supported input formats (optional) + * @return this builder for method chaining + */ public Builder inputModes(List inputModes) { this.inputModes = inputModes; return this; } + /** + * Sets the supported output formats for this skill. + *

+ * If not specified, the skill inherits the default output modes from the AgentCard. + * Common values include "text", "audio", "image", "video". + * + * @param outputModes list of supported output formats (optional) + * @return this builder for method chaining + */ public Builder outputModes(List outputModes) { this.outputModes = outputModes; return this; } + /** + * Sets security requirements specific to this skill. + *

+ * Security requirements override or supplement the agent-level security + * defined in the AgentCard. Each entry represents an alternative security + * requirement, where each map contains scheme names and their required scopes. + * + * @param security list of security requirements (optional) + * @return this builder for method chaining + */ public Builder security(List>> security) { this.security = security; return this; } + /** + * Builds an immutable {@link AgentSkill} from the current builder state. + * + * @return a new AgentSkill instance + * @throws IllegalArgumentException if any required field (id, name, description, tags) is null + */ public AgentSkill build() { return new AgentSkill(id, name, description, tags, examples, inputModes, outputModes, security); } diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java index 798ac5823..763ad37c7 100644 --- a/spec/src/main/java/io/a2a/spec/Artifact.java +++ b/spec/src/main/java/io/a2a/spec/Artifact.java @@ -8,7 +8,28 @@ import io.a2a.util.Assert; /** - * Represents a file, data structure, or other resource generated by an agent during a task. + * Represents a file, data structure, or other resource generated by an agent during task execution. + *

+ * Artifacts are outputs created by agents in response to user requests. They can represent various + * types of content including documents, data structures, analysis results, generated files, or any + * other structured output from agent operations. + *

+ * Each artifact has a unique identifier and contains content through {@link Part}s, which allow + * for multi-modal output (text, files, structured data). Artifacts can include metadata for + * additional context and support protocol extensions. + *

+ * Artifacts are typically delivered to clients through {@link Task} responses or + * {@link TaskArtifactUpdateEvent} events in streaming scenarios. + *

+ * This class is immutable. Use the {@link Builder} for construction. + * + * @param artifactId the unique identifier for this artifact (required) + * @param name the human-readable name of the artifact (optional) + * @param description a brief description of the artifact's purpose or content (optional) + * @param parts the content parts comprising the artifact (required, must not be empty) + * @param metadata additional key-value metadata for the artifact (optional) + * @param extensions protocol extensions used in this artifact (optional) + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -23,6 +44,22 @@ public record Artifact(String artifactId, String name, String description, List< } } + /** + * Builder for constructing immutable {@link Artifact} instances. + *

+ * The Builder provides a fluent API for creating artifacts with required and optional fields. + * Artifacts must have an artifactId and non-empty parts list. + *

+ * Example usage: + *

{@code
+     * Artifact result = new Artifact.Builder()
+     *     .artifactId("artifact-123")
+     *     .name("Analysis Report")
+     *     .description("Detailed analysis of user data")
+     *     .parts(List.of(new TextPart("Report content...", null)))
+     *     .build();
+     * }
+ */ public static class Builder { private String artifactId; private String name; @@ -31,9 +68,17 @@ public static class Builder { private Map metadata; private List extensions; + /** + * Creates a new Builder with all fields unset. + */ public Builder(){ } + /** + * Creates a new Builder initialized with values from an existing Artifact. + * + * @param existingArtifact the Artifact to copy values from + */ public Builder(Artifact existingArtifact) { artifactId = existingArtifact.artifactId; name = existingArtifact.name; @@ -43,42 +88,89 @@ public Builder(Artifact existingArtifact) { extensions = existingArtifact.extensions; } + /** + * Sets the unique identifier for this artifact. + * + * @param artifactId the artifact identifier (required) + * @return this builder for method chaining + */ public Builder artifactId(String artifactId) { this.artifactId = artifactId; return this; } - + /** + * Sets the human-readable name of the artifact. + * + * @param name the artifact name (optional) + * @return this builder for method chaining + */ public Builder name(String name) { this.name = name; return this; } + /** + * Sets a brief description of the artifact's purpose or content. + * + * @param description the artifact description (optional) + * @return this builder for method chaining + */ public Builder description(String description) { this.description = description; return this; } + /** + * Sets the content parts comprising the artifact. + * + * @param parts the list of content parts (required, must not be empty) + * @return this builder for method chaining + */ public Builder parts(List> parts) { this.parts = parts; return this; } + /** + * Sets the content parts from varargs. + * + * @param parts the content parts (required, must not be empty) + * @return this builder for method chaining + */ public Builder parts(Part... parts) { this.parts = List.of(parts); return this; } + /** + * Sets additional metadata for the artifact. + * + * @param metadata map of metadata key-value pairs (optional) + * @return this builder for method chaining + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Sets the list of protocol extensions used in this artifact. + * + * @param extensions the list of extension identifiers (optional) + * @return this builder for method chaining + */ public Builder extensions(List extensions) { this.extensions = (extensions == null) ? null : List.copyOf(extensions); return this; } + /** + * Builds a new immutable {@link Artifact} from the current builder state. + * + * @return a new Artifact instance + * @throws IllegalArgumentException if required fields are missing or parts is empty + */ public Artifact build() { return new Artifact(artifactId, name, description, parts, metadata, extensions); } diff --git a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java index 323cd147d..a318bdd3d 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java @@ -8,8 +8,29 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An A2A-specific error indicating that the agent does not have an - * Authenticated Extended Card configured + * A2A Protocol error indicating that the agent does not have an authenticated extended card configured. + *

+ * This error is returned when a client attempts to retrieve an authenticated extended agent card + * via {@link GetAuthenticatedExtendedCardRequest}, but the agent has not configured authentication-protected + * extended card information. + *

+ * Extended cards may contain additional agent metadata, capabilities, or configuration that + * should only be accessible to authenticated clients. Agents that don't implement this feature + * will return this error. + *

+ * Corresponds to A2A-specific error code {@code -32007}. + *

+ * Usage example: + *

{@code
+ * // In agent implementation
+ * if (authenticatedExtendedCard == null) {
+ *     throw new AuthenticatedExtendedCardNotConfiguredError();
+ * }
+ * }
+ * + * @see GetAuthenticatedExtendedCardRequest for retrieving authenticated extended cards + * @see AgentCard for the base agent card structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java index d28a1e173..a1cd3c293 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java @@ -7,7 +7,20 @@ import io.a2a.util.Assert; /** - * The authentication info for an agent. + * Authentication information for accessing an agent's authenticated endpoints. + *

+ * This record encapsulates the authentication schemes supported by an agent and + * optionally provides credentials for authentication. It is used when clients need + * to authenticate to access protected agent resources. + *

+ * The {@code schemes} list identifies which security schemes from the agent's + * {@link AgentCard#securitySchemes()} should be used for authentication. + * + * @param schemes list of security scheme names that should be used for authentication (required) + * @param credentials optional credentials string for authentication (format depends on scheme) + * @see AgentCard#securitySchemes() for available security schemes + * @see SecurityScheme for security scheme definitions + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index afa6fed72..f3435be96 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -8,7 +8,24 @@ import io.a2a.util.Assert; /** - * Defines configuration details for the OAuth 2.0 Authorization Code flow. + * Configuration for the OAuth 2.0 Authorization Code flow. + *

+ * The authorization code flow is the most secure OAuth 2.0 flow, recommended for + * server-side applications. It involves redirecting the user to an authorization + * server, obtaining an authorization code, and then exchanging that code for an + * access token. + *

+ * This flow is suitable when the client can securely store client credentials + * and the authorization code can be exchanged server-side. + * + * @param authorizationUrl URL for the authorization endpoint where users authenticate (required) + * @param refreshUrl URL for obtaining refresh tokens (optional) + * @param scopes map of available OAuth scopes to their descriptions (required) + * @param tokenUrl URL for the token endpoint where codes are exchanged for tokens (required) + * @see OAuthFlows for the container of all supported OAuth flows + * @see OAuth2SecurityScheme for the security scheme using these flows + * @see RFC 6749 - Authorization Code Grant + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java index 39c370ae3..2e0db609a 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java @@ -12,7 +12,21 @@ import io.a2a.util.Assert; /** - * A request that can be used to cancel a task. + * JSON-RPC request to cancel an in-progress task. + *

+ * This request instructs the agent to cancel execution of a specific task identified by ID. + * The agent should stop processing, clean up resources, and transition the task to + * {@link TaskState#CANCELED} state if cancellation is possible. + *

+ * Not all tasks can be canceled (e.g., already completed tasks), which may result in + * a {@link TaskNotCancelableError}. + *

+ * This class implements the JSON-RPC {@code tasks/cancel} method as specified in the A2A Protocol. + * + * @see CancelTaskResponse for the corresponding response + * @see TaskIdParams for the parameter structure + * @see TaskNotCancelableError for the error when cancellation is not possible + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java index 9ef775118..05b7215e6 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java @@ -6,7 +6,19 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * A response to a cancel task request. + * JSON-RPC response for task cancellation requests. + *

+ * This response contains the updated {@link Task} object after cancellation, typically + * showing {@link TaskState#CANCELED} status if the cancellation was successful. + *

+ * If the task cannot be canceled (e.g., already completed) or is not found, the error + * field will contain a {@link JSONRPCError} such as {@link TaskNotCancelableError} or + * {@link TaskNotFoundError}. + * + * @see CancelTaskRequest for the corresponding request + * @see Task for the task structure + * @see TaskNotCancelableError for the error when cancellation fails + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java index 372b57d78..dc5ad21cd 100644 --- a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java @@ -9,7 +9,24 @@ import io.a2a.util.Assert; /** - * Defines configuration details for the OAuth 2.0 Client Credentials flow. + * Configuration for the OAuth 2.0 Client Credentials flow. + *

+ * The client credentials flow is designed for machine-to-machine authentication + * where the client application authenticates using its own credentials (client ID + * and secret) rather than on behalf of a user. This is suitable for server-to-server + * communication and backend services. + *

+ * This flow is appropriate when the client is acting on its own behalf, not + * representing a user, such as accessing its own resources or performing + * administrative operations. + * + * @param refreshUrl URL for obtaining refresh tokens (optional, rarely used in client credentials flow) + * @param scopes map of available OAuth scopes to their descriptions (required) + * @param tokenUrl URL for the token endpoint where client credentials are exchanged for tokens (required) + * @see OAuthFlows for the container of all supported OAuth flows + * @see OAuth2SecurityScheme for the security scheme using these flows + * @see RFC 6749 - Client Credentials Grant + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index 3aa245ca2..a73b5c038 100644 --- a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -8,8 +8,37 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An A2A-specific error indicating an incompatibility between the requested - * content types and the agent's capabilities. + * A2A Protocol error indicating incompatibility between requested content types and agent capabilities. + *

+ * This error is returned when the input or output modes requested by a client are not supported + * by the agent. Agents declare their supported content types via {@link AgentCard#defaultInputModes()} + * and {@link AgentCard#defaultOutputModes()}, and clients can request specific modes via + * {@link MessageSendConfiguration}. + *

+ * Common scenarios: + *

    + *
  • Client requests audio input but agent only supports text
  • + *
  • Client requires video output but agent only produces text and images
  • + *
  • Incompatible combinations of input and output modes
  • + *
+ *

+ * Corresponds to A2A-specific error code {@code -32005}. + *

+ * Usage example: + *

{@code
+ * if (!agentCard.defaultInputModes().contains(requestedInputMode)) {
+ *     throw new ContentTypeNotSupportedError(
+ *         null,
+ *         "Input mode " + requestedInputMode + " not supported",
+ *         null
+ *     );
+ * }
+ * }
+ * + * @see AgentCard#defaultInputModes() for agent input capabilities + * @see AgentCard#defaultOutputModes() for agent output capabilities + * @see MessageSendConfiguration for client content type preferences + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 7ac244263..79fdec7e0 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -12,7 +12,34 @@ import static io.a2a.spec.DataPart.DATA; /** - * Represents a structured data segment (e.g., JSON) within a message or artifact. + * Represents a structured data content part within a {@link Message} or {@link Artifact}. + *

+ * DataPart contains structured data (typically JSON objects) for machine-to-machine communication. + * It is used when content needs to be processed programmatically rather than displayed as text, + * such as API responses, configuration data, analysis results, or structured metadata. + *

+ * The data is represented as a Map of key-value pairs, which can contain nested structures + * including lists, maps, and primitive values. + *

+ * Example usage: + *

{@code
+ * // Simple structured data
+ * DataPart result = new DataPart(Map.of(
+ *     "status", "success",
+ *     "count", 42,
+ *     "items", List.of("item1", "item2")
+ * ));
+ *
+ * // With metadata
+ * DataPart withMeta = new DataPart(
+ *     Map.of("temperature", 72.5, "unit", "F"),
+ *     Map.of("source", "weather-api", "timestamp", "2024-01-20T12:00:00Z")
+ * );
+ * }
+ * + * @see Part + * @see Message + * @see Artifact */ @JsonTypeName(DATA) @JsonInclude(JsonInclude.Include.NON_ABSENT) diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index a64421a4c..510b16aad 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -8,7 +8,16 @@ import io.a2a.util.Assert; /** - * Parameters for removing pushNotificationConfiguration associated with a Task. + * Parameters for deleting a push notification configuration from a task. + *

+ * This record specifies which task and which specific push notification configuration + * to remove, allowing cleanup of notification endpoints that are no longer needed. + * + * @param id the task identifier (required) + * @param pushNotificationConfigId the specific configuration ID to delete (required) + * @param metadata optional arbitrary key-value metadata for the request + * @see DeleteTaskPushNotificationConfigRequest for the request using these parameters + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java index 99f50ebfd..b261a36ef 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -11,7 +11,17 @@ import io.a2a.util.Utils; /** - * A delete task push notification config request. + * JSON-RPC request to delete a push notification configuration from a task. + *

+ * This request removes a specific push notification endpoint configuration from a task, + * stopping future notifications to that endpoint. The task will continue execution, but + * no longer send updates to the deleted notification URL. + *

+ * This class implements the JSON-RPC {@code tasks/pushNotificationConfig/delete} method. + * + * @see DeleteTaskPushNotificationConfigResponse for the response + * @see DeleteTaskPushNotificationConfigParams for the parameter structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java index 0f65b5ad5..35bbd05df 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java @@ -7,7 +7,15 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** - * A response for a delete task push notification config request. + * JSON-RPC response confirming deletion of a task's push notification configuration. + *

+ * This response confirms successful deletion of the push notification configuration. + * The result is void (no content) on success. + *

+ * If an error occurs during deletion, the error field will contain a {@link JSONRPCError}. + * + * @see DeleteTaskPushNotificationConfigRequest for the corresponding request + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/Event.java b/spec/src/main/java/io/a2a/spec/Event.java index 4d0daa531..2be6a757f 100644 --- a/spec/src/main/java/io/a2a/spec/Event.java +++ b/spec/src/main/java/io/a2a/spec/Event.java @@ -1,4 +1,29 @@ package io.a2a.spec; +/** + * Marker interface for all event types in the A2A Protocol. + *

+ * Events are the fundamental communication mechanism for agent task execution and state updates. + * They enable both synchronous request-response patterns and asynchronous streaming updates. + *

+ * The Event hierarchy includes: + *

    + *
  • {@link EventKind} - Events that can be returned from non-streaming operations
  • + *
  • {@link StreamingEventKind} - Events that can be emitted during streaming responses
  • + *
  • {@link UpdateEvent} - Incremental update events for task progress
  • + *
+ *

+ * Common event types include: + *

    + *
  • {@link Task} - Complete task state
  • + *
  • {@link Message} - Message exchange
  • + *
  • {@link TaskStatusUpdateEvent} - Task status changes
  • + *
  • {@link TaskArtifactUpdateEvent} - Artifact creation/updates
  • + *
+ * + * @see EventKind + * @see StreamingEventKind + * @see UpdateEvent + */ public interface Event { } diff --git a/spec/src/main/java/io/a2a/spec/EventKind.java b/spec/src/main/java/io/a2a/spec/EventKind.java index a1ed7ef31..b16e9cb45 100644 --- a/spec/src/main/java/io/a2a/spec/EventKind.java +++ b/spec/src/main/java/io/a2a/spec/EventKind.java @@ -6,6 +6,25 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +/** + * Interface for events that can be returned from non-streaming A2A Protocol operations. + *

+ * EventKind represents events that are suitable for synchronous request-response patterns. + * These events provide complete state information and are typically returned as the final + * result of an operation. + *

+ * EventKind implementations use polymorphic JSON serialization with the "kind" discriminator + * to determine the concrete type during deserialization. + *

+ * Permitted implementations: + *

    + *
  • {@link Task} - Complete task state with status and artifacts
  • + *
  • {@link Message} - Full message with all content parts
  • + *
+ * + * @see StreamingEventKind + * @see Event + */ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, @@ -18,5 +37,10 @@ }) public interface EventKind { + /** + * Returns the kind identifier for this event. + * + * @return the event kind string (e.g., "task", "message") + */ String getKind(); } diff --git a/spec/src/main/java/io/a2a/spec/FileContent.java b/spec/src/main/java/io/a2a/spec/FileContent.java index f9609fb8b..8b87bf5f9 100644 --- a/spec/src/main/java/io/a2a/spec/FileContent.java +++ b/spec/src/main/java/io/a2a/spec/FileContent.java @@ -2,10 +2,44 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +/** + * Sealed interface representing file content in the A2A Protocol. + *

+ * FileContent provides a polymorphic abstraction for file data, allowing files to be + * represented either as embedded binary content or as URI references. This flexibility + * enables different strategies for file transmission based on size, security, and + * accessibility requirements. + *

+ * The sealed interface permits only two implementations: + *

    + *
  • {@link FileWithBytes} - File content embedded as base64-encoded bytes (for small files or inline data)
  • + *
  • {@link FileWithUri} - File content referenced by URI (for large files or external resources)
  • + *
+ *

+ * Both implementations must provide: + *

    + *
  • MIME type - Describes the file format (e.g., "image/png", "application/pdf")
  • + *
  • File name - The original or display name for the file
  • + *
+ * + * @see FilePart + * @see FileWithBytes + * @see FileWithUri + */ @JsonDeserialize(using = FileContentDeserializer.class) public sealed interface FileContent permits FileWithBytes, FileWithUri { + /** + * Returns the MIME type of the file content. + * + * @return the MIME type (e.g., "image/png", "text/plain", "application/json") + */ String mimeType(); + /** + * Returns the file name. + * + * @return the file name (e.g., "document.pdf", "image.jpg") + */ String name(); } diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 79bd2e5e8..9e44a0f49 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -12,8 +12,35 @@ import static io.a2a.spec.FilePart.FILE; /** - * Represents a file segment within a message or artifact. The file content can be - * provided either directly as bytes or as a URI. + * Represents a file content part within a {@link Message} or {@link Artifact}. + *

+ * FilePart contains file data that can be provided in two ways: + *

    + *
  • {@link FileWithBytes} - File content embedded as base64-encoded bytes
  • + *
  • {@link FileWithUri} - File content referenced by URI
  • + *
+ *

+ * File parts are used to exchange binary data, documents, images, or any file-based content + * between users and agents. The choice between bytes and URI depends on file size, accessibility, + * and security requirements. + *

+ * Example usage: + *

{@code
+ * // File with embedded bytes
+ * FilePart imageBytes = new FilePart(
+ *     new FileWithBytes("image/png", "diagram.png", "iVBORw0KGgoAAAANS...")
+ * );
+ *
+ * // File with URI reference
+ * FilePart imageUri = new FilePart(
+ *     new FileWithUri("image/png", "photo.png", "https://example.com/photo.png")
+ * );
+ * }
+ * + * @see Part + * @see FileContent + * @see FileWithBytes + * @see FileWithUri */ @JsonTypeName(FILE) @JsonInclude(JsonInclude.Include.NON_ABSENT) diff --git a/spec/src/main/java/io/a2a/spec/FileWithBytes.java b/spec/src/main/java/io/a2a/spec/FileWithBytes.java index 782bc2c02..eeeea9375 100644 --- a/spec/src/main/java/io/a2a/spec/FileWithBytes.java +++ b/spec/src/main/java/io/a2a/spec/FileWithBytes.java @@ -4,7 +4,28 @@ import com.fasterxml.jackson.annotation.JsonInclude; /** - * Represents a file with its content provided directly as a base64-encoded string. + * Represents file content embedded directly as base64-encoded bytes. + *

+ * FileWithBytes is used when file content needs to be transmitted inline with the message or + * artifact, rather than requiring a separate download. This is appropriate for: + *

    + *
  • Small files that fit comfortably in a JSON payload
  • + *
  • Generated content that doesn't exist as a standalone file
  • + *
  • Content that must be preserved exactly as created
  • + *
  • Scenarios where URI accessibility is uncertain
  • + *
+ *

+ * The bytes field contains the base64-encoded file content. Decoders should handle the base64 + * encoding/decoding transparently. + *

+ * This class is immutable. + * + * @param mimeType the MIME type of the file (e.g., "image/png", "application/pdf") (required) + * @param name the file name (e.g., "report.pdf", "diagram.png") (required) + * @param bytes the base64-encoded file content (required) + * @see FileContent + * @see FilePart + * @see FileWithUri */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/FileWithUri.java b/spec/src/main/java/io/a2a/spec/FileWithUri.java index afb3a87d8..54f53c80b 100644 --- a/spec/src/main/java/io/a2a/spec/FileWithUri.java +++ b/spec/src/main/java/io/a2a/spec/FileWithUri.java @@ -4,7 +4,32 @@ import com.fasterxml.jackson.annotation.JsonInclude; /** - * Represents a file with its content located at a specific URI. + * Represents file content referenced by a URI location. + *

+ * FileWithUri is used when file content is stored externally and accessed via a URI reference. + * This is appropriate for: + *

    + *
  • Large files that would be impractical to embed in JSON
  • + *
  • Publicly accessible resources (HTTP/HTTPS URLs)
  • + *
  • Files stored in object storage (S3, Azure Blob, etc.)
  • + *
  • Content that may be accessed multiple times or by multiple clients
  • + *
+ *

+ * The URI should be accessible to the receiving party. Considerations include: + *

    + *
  • Authentication requirements for private resources
  • + *
  • URI expiration for temporary access (signed URLs)
  • + *
  • Network accessibility and firewall rules
  • + *
+ *

+ * This class is immutable. + * + * @param mimeType the MIME type of the file (e.g., "image/png", "application/pdf") (required) + * @param name the file name (e.g., "report.pdf", "photo.jpg") (required) + * @param uri the URI where the file content can be accessed (e.g., "https://example.com/file.pdf") (required) + * @see FileContent + * @see FilePart + * @see FileWithBytes */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java index 9d561c2d2..8730d89f0 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -11,7 +11,28 @@ import io.a2a.util.Utils; /** - * Represents a JSON-RPC request for the `agent/getAuthenticatedExtendedCard` method. + * JSON-RPC request to retrieve an agent's extended card with authenticated details. + *

+ * This request fetches an extended version of the {@link AgentCard} that may contain + * additional information only available to authenticated clients, such as: + *

    + *
  • Additional security scheme details
  • + *
  • Extended capability information
  • + *
  • Authenticated-only skills or interfaces
  • + *
  • Premium or restricted features
  • + *
+ *

+ * The agent must support authenticated extended cards (indicated by + * {@link AgentCard#supportsAuthenticatedExtendedCard()}) and the client must provide + * valid authentication credentials for this request to succeed. + *

+ * This class implements the JSON-RPC {@code agent/getAuthenticatedExtendedCard} method + * as specified in the A2A Protocol. + * + * @see GetAuthenticatedExtendedCardResponse for the corresponding response + * @see AgentCard for the card structure + * @see AuthenticatedExtendedCardNotConfiguredError for the error when unsupported + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java index ec624e77d..2802d5648 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java @@ -6,7 +6,20 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * A response for the `agent/getAuthenticatedExtendedCard` method. + * JSON-RPC response containing an agent's extended card with authenticated details. + *

+ * This response returns an {@link AgentCard} with additional information only available + * to authenticated clients. The extended card may include premium features, detailed + * security configurations, or other authenticated-only capabilities. + *

+ * If the agent doesn't support authenticated extended cards or authentication fails, + * the error field will contain a {@link JSONRPCError} such as + * {@link AuthenticatedExtendedCardNotConfiguredError}. + * + * @see GetAuthenticatedExtendedCardRequest for the corresponding request + * @see AgentCard for the card structure + * @see AuthenticatedExtendedCardNotConfiguredError for the error when unsupported + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index 627feff34..f95e83436 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -9,7 +9,17 @@ import org.jspecify.annotations.Nullable; /** - * Parameters for fetching a pushNotificationConfiguration associated with a Task. + * Parameters for retrieving push notification configuration for a specific task. + *

+ * This record specifies which task's push notification configuration to retrieve, with + * an optional filter by configuration ID if multiple configurations exist for the task. + * + * @param id the task identifier (required) + * @param pushNotificationConfigId optional specific configuration ID to retrieve + * @param metadata optional arbitrary key-value metadata for the request + * @see GetTaskPushNotificationConfigRequest for the request using these parameters + * @see TaskPushNotificationConfig for the returned configuration structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index b353e0cc8..bea3b1246 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -10,7 +10,17 @@ import java.util.UUID; /** - * A get task push notification request. + * JSON-RPC request to retrieve push notification configuration for a task. + *

+ * This request retrieves the currently configured push notification endpoint and settings + * for a specific task, allowing clients to verify or inspect the notification configuration. + *

+ * This class implements the JSON-RPC {@code tasks/pushNotificationConfig/get} method. + * + * @see GetTaskPushNotificationConfigResponse for the response + * @see GetTaskPushNotificationConfigParams for the parameter structure + * @see TaskPushNotificationConfig for the returned configuration + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java index 116799a9e..76b411c1a 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java @@ -6,7 +6,17 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * A response for a get task push notification request. + * JSON-RPC response containing a task's push notification configuration. + *

+ * This response returns the {@link TaskPushNotificationConfig} for the requested task, + * showing the current push notification endpoint and authentication settings. + *

+ * If no configuration exists or an error occurs, the error field will contain a + * {@link JSONRPCError}. + * + * @see GetTaskPushNotificationConfigRequest for the corresponding request + * @see TaskPushNotificationConfig for the configuration structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java index f31237af0..6c85bd6ea 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java @@ -12,7 +12,18 @@ import io.a2a.util.Assert; /** - * A get task request. + * JSON-RPC request to retrieve task information by ID. + *

+ * This request queries the agent for the current state of a specific task, including its + * status, artifacts, messages, and other task metadata. Clients use this to check task + * progress or retrieve completed task results. + *

+ * This class implements the JSON-RPC {@code tasks/get} method as specified in the A2A Protocol. + * + * @see GetTaskResponse for the corresponding response + * @see TaskQueryParams for the parameter structure + * @see Task for the returned task structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java index 0d27a8e68..81166b5af 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java @@ -6,7 +6,18 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * The response for a get task request. + * JSON-RPC response containing requested task information. + *

+ * This response returns the full {@link Task} object for a task ID queried via + * {@link GetTaskRequest}, including all task metadata, status, artifacts, and messages. + *

+ * If the task is not found or an error occurs, the error field will be populated with + * a {@link JSONRPCError} (typically {@link TaskNotFoundError}) instead of a result. + * + * @see GetTaskRequest for the corresponding request + * @see Task for the task structure + * @see TaskNotFoundError for the common error case + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 408fd2605..69a8f0728 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -11,7 +11,34 @@ import static io.a2a.spec.HTTPAuthSecurityScheme.HTTP; /** - * Defines a security scheme using HTTP authentication. + * HTTP authentication security scheme for agent authentication. + *

+ * This security scheme uses HTTP authentication mechanisms, supporting both basic authentication + * and bearer token authentication (e.g., JWT). The {@code scheme} parameter specifies the + * HTTP authentication scheme name as defined in RFC 7235. + *

+ * Common schemes: + *

    + *
  • {@code basic} - HTTP Basic authentication (RFC 7617)
  • + *
  • {@code bearer} - Bearer token authentication (RFC 6750), typically used with OAuth 2.0
  • + *
+ *

+ * For bearer tokens, the {@code bearerFormat} field can provide additional information about + * the token format (e.g., "JWT"). + *

+ * Example usage: + *

{@code
+ * HTTPAuthSecurityScheme scheme = new HTTPAuthSecurityScheme.Builder()
+ *     .scheme("bearer")
+ *     .bearerFormat("JWT")
+ *     .description("JWT bearer token authentication")
+ *     .build();
+ * }
+ * + * @see SecurityScheme for the base interface + * @see OpenAPI Security Scheme + * @see RFC 7235 - HTTP Authentication + * @see A2A Protocol Specification */ @JsonTypeName(HTTP) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -59,26 +86,56 @@ public String getType() { return type; } + /** + * Builder for constructing {@link HTTPAuthSecurityScheme} instances. + *

+ * Provides a fluent API for creating HTTP authentication security schemes. + * The {@code scheme} parameter is required and must be set before calling {@code build()}. + */ public static class Builder { private String bearerFormat; private String scheme; private String description; + /** + * Sets the bearer token format hint. + * + * @param bearerFormat hint to the client about the format of bearer tokens (e.g., "JWT") + * @return this builder instance + */ public Builder bearerFormat(String bearerFormat) { this.bearerFormat = bearerFormat; return this; } + /** + * Sets the HTTP authentication scheme name. + * + * @param scheme the scheme name (required, e.g., "basic" or "bearer") + * @return this builder instance + */ public Builder scheme(String scheme) { this.scheme = scheme; return this; } + /** + * Sets an optional description of the security scheme. + * + * @param description human-readable description + * @return this builder instance + */ public Builder description(String description) { this.description = description; return this; } + /** + * Builds the {@link HTTPAuthSecurityScheme} instance. + * + * @return a new immutable HTTPAuthSecurityScheme + * @throws IllegalArgumentException if required fields are missing + */ public HTTPAuthSecurityScheme build() { return new HTTPAuthSecurityScheme(bearerFormat, scheme, description); } diff --git a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java index 8e2d529ea..a63336ef1 100644 --- a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java @@ -8,7 +8,23 @@ import io.a2a.util.Assert; /** - * Defines configuration details for the OAuth 2.0 Implicit flow. + * Configuration for the OAuth 2.0 Implicit flow. + *

+ * The implicit flow is designed for browser-based applications where the client + * cannot securely store credentials. The access token is returned directly from + * the authorization endpoint without an intermediate authorization code. + *

+ * Note: The implicit flow is considered less secure than the + * authorization code flow and is deprecated in OAuth 2.1. It should only be used + * for legacy applications or when the authorization code flow with PKCE is not feasible. + * + * @param authorizationUrl URL for the authorization endpoint where users authenticate (required) + * @param refreshUrl URL for obtaining refresh tokens (optional, rarely used in implicit flow) + * @param scopes map of available OAuth scopes to their descriptions (required) + * @see OAuthFlows for the container of all supported OAuth flows + * @see OAuth2SecurityScheme for the security scheme using these flows + * @see RFC 6749 - Implicit Grant + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java b/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java index 2a01c1f50..386a59118 100644 --- a/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java +++ b/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java @@ -1,4 +1,22 @@ package io.a2a.spec; -public class IntegerJsonrpcId { +/** + * Integer-based implementation of JSON-RPC request/response correlation identifier. + *

+ * This class represents a JSON-RPC ID that uses an Integer value for correlation + * between requests and responses. According to the JSON-RPC 2.0 specification, + * Number IDs are one of the valid identifier types (along with String and NULL). + *

+ * Integer IDs are commonly used when: + *

    + *
  • Sequential request numbering is desired
  • + *
  • Memory efficiency is important (integers are smaller than strings)
  • + *
  • Simple monotonic correlation is sufficient
  • + *
+ * + * @see JsonrpcId + * @see StringJsonrpcId + * @see JSON-RPC 2.0 Request Object + */ +public class IntegerJsonrpcId implements JsonrpcId { } diff --git a/spec/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java index ae52ecb70..3d1b7b5ca 100644 --- a/spec/src/main/java/io/a2a/spec/InternalError.java +++ b/spec/src/main/java/io/a2a/spec/InternalError.java @@ -8,7 +8,27 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An error indicating an internal error on the server. + * JSON-RPC error indicating an internal error occurred on the server. + *

+ * This error represents unexpected server-side failures such as unhandled exceptions, + * resource exhaustion, or other internal issues that prevent the server from processing + * a request. This is a catch-all error for server problems not covered by more specific + * error types. + *

+ * Corresponds to JSON-RPC 2.0 error code {@code -32603}. + *

+ * Usage example: + *

{@code
+ * try {
+ *     // Server processing
+ * } catch (Exception e) {
+ *     throw new InternalError("Failed to process request: " + e.getMessage());
+ * }
+ * }
+ * + * @see JSONRPCError for the base error class + * @see A2AError for the error marker interface + * @see JSON-RPC 2.0 Error Codes */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index faab71a96..d3c23e657 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -8,8 +8,37 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An A2A-specific error indicating that the agent returned a response that - * does not conform to the specification for the current method. + * A2A Protocol error indicating that an agent returned a response not conforming to protocol specifications. + *

+ * This error is typically raised by client implementations when validating agent responses. + * It indicates that the agent's response structure, content, or format violates the A2A Protocol + * requirements for the invoked method. + *

+ * Common violations: + *

    + *
  • Missing required fields in response objects
  • + *
  • Invalid field types or values
  • + *
  • Malformed event stream data
  • + *
  • Response doesn't match declared agent capabilities
  • + *
+ *

+ * Corresponds to A2A-specific error code {@code -32006}. + *

+ * Usage example: + *

{@code
+ * SendMessageResponse response = client.sendMessage(request);
+ * if (response.task() == null) {
+ *     throw new InvalidAgentResponseError(
+ *         null,
+ *         "Response missing required 'task' field",
+ *         null
+ *     );
+ * }
+ * }
+ * + * @see JSONRPCResponse for response structure + * @see SendMessageResponse for message send response + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java index c71ea14bf..8a3b6b81f 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -8,7 +8,31 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An error indicating that the method parameters are invalid. + * JSON-RPC error indicating that method parameters are invalid or missing required fields. + *

+ * This error is returned when a JSON-RPC method is called with parameters that fail validation. + * Common causes include: + *

    + *
  • Missing required parameters
  • + *
  • Parameters of incorrect type
  • + *
  • Parameter values outside acceptable ranges
  • + *
  • Malformed parameter structures
  • + *
+ *

+ * Corresponds to JSON-RPC 2.0 error code {@code -32602}. + *

+ * Usage example: + *

{@code
+ * // Default error with standard message
+ * throw new InvalidParamsError();
+ *
+ * // Custom error message
+ * throw new InvalidParamsError("taskId parameter is required");
+ * }
+ * + * @see JSONRPCError for the base error class + * @see A2AError for the error marker interface + * @see JSON-RPC 2.0 Error Codes */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java index 4d9e50779..77c9bd6de 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -8,7 +8,31 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An error indicating that the JSON sent is not a valid Request object. + * JSON-RPC error indicating that the request payload is not a valid JSON-RPC Request object. + *

+ * This error is returned when the JSON-RPC request fails structural validation. + * Common causes include: + *

    + *
  • Missing required JSON-RPC fields (jsonrpc, method, id)
  • + *
  • Invalid JSON-RPC version (must be "2.0")
  • + *
  • Malformed request structure
  • + *
  • Type mismatches in required fields
  • + *
+ *

+ * Corresponds to JSON-RPC 2.0 error code {@code -32600}. + *

+ * Usage example: + *

{@code
+ * // Default error with standard message
+ * throw new InvalidRequestError();
+ *
+ * // Custom error message
+ * throw new InvalidRequestError("Missing 'method' field in request");
+ * }
+ * + * @see JSONRPCError for the base error class + * @see A2AError for the error marker interface + * @see JSON-RPC 2.0 Error Codes */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java index 3029f5394..ea8aac7ec 100644 --- a/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java @@ -3,6 +3,21 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +/** + * A simplified error response wrapper for non-JSON-RPC error scenarios. + *

+ * This record provides a lightweight error response format for cases where + * a full JSON-RPC error structure is not appropriate, such as HTTP-level + * errors or transport-layer failures. + *

+ * Unlike {@link JSONRPCErrorResponse}, this is not part of the JSON-RPC 2.0 + * specification but serves as a utility for simpler error reporting in the + * A2A Java SDK implementation. + * + * @param error a human-readable error message + * @see JSONRPCErrorResponse + * @see JSONRPCError + */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public record JSONErrorResponse(String error) { diff --git a/spec/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java index d596c0701..d7716a753 100644 --- a/spec/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -8,7 +8,27 @@ import static io.a2a.util.Utils.defaultIfNull; /** - * An error indicating that the server received invalid JSON. + * JSON-RPC error indicating that the server received invalid JSON that could not be parsed. + *

+ * This error is returned when the request payload is not valid JSON, such as malformed syntax, + * unexpected tokens, or encoding issues. This is distinct from {@link InvalidRequestError}, + * which indicates structurally valid JSON that doesn't conform to the JSON-RPC specification. + *

+ * Corresponds to JSON-RPC 2.0 error code {@code -32700}. + *

+ * Usage example: + *

{@code
+ * try {
+ *     objectMapper.readValue(payload, JSONRPCRequest.class);
+ * } catch (JsonProcessingException e) {
+ *     throw new JSONParseError("Malformed JSON: " + e.getMessage());
+ * }
+ * }
+ * + * @see JSONRPCError for the base error class + * @see A2AError for the error marker interface + * @see InvalidRequestError for structurally valid but invalid requests + * @see JSON-RPC 2.0 Error Codes */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCError.java b/spec/src/main/java/io/a2a/spec/JSONRPCError.java index 776b9c8c6..53ae0b05b 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCError.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCError.java @@ -10,7 +10,34 @@ import io.a2a.util.Assert; /** - * Represents a JSON-RPC 2.0 Error object, included in an error response. + * Represents a JSON-RPC 2.0 error object as defined in the JSON-RPC 2.0 specification. + *

+ * This class encapsulates error information returned in JSON-RPC error responses. + * According to the JSON-RPC 2.0 specification, an error object must contain: + *

    + *
  • {@code code} - A number indicating the error type (required)
  • + *
  • {@code message} - A short description of the error (required)
  • + *
  • {@code data} - Additional information about the error (optional)
  • + *
+ *

+ * This class implements {@link Event} to allow errors to be streamed to clients, + * and {@link A2AError} to integrate with the A2A Protocol's error handling system. + * It extends {@link Error} to provide standard Java error semantics with a message. + *

+ * Standard error codes are defined in the JSON-RPC 2.0 specification: + *

    + *
  • -32700: Parse error
  • + *
  • -32600: Invalid Request
  • + *
  • -32601: Method not found
  • + *
  • -32602: Invalid params
  • + *
  • -32603: Internal error
  • + *
  • -32000 to -32099: Server error (implementation-defined)
  • + *
+ * + * @see Event + * @see A2AError + * @see JSONRPCErrorResponse + * @see JSON-RPC 2.0 Error Object */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonDeserialize(using = JSONRPCErrorDeserializer.class) @@ -21,6 +48,16 @@ public class JSONRPCError extends Error implements Event, A2AError { private final Integer code; private final Object data; + /** + * Constructs a JSON-RPC error with the specified code, message, and optional data. + *

+ * This constructor is used by Jackson for JSON deserialization. + * + * @param code the numeric error code (required, see JSON-RPC 2.0 spec for standard codes) + * @param message the human-readable error message (required) + * @param data additional error information, structure defined by the error code (optional) + * @throws IllegalArgumentException if code or message is null + */ @JsonCreator public JSONRPCError( @JsonProperty("code") Integer code, @@ -34,7 +71,17 @@ public JSONRPCError( } /** - * Gets the error code + * Gets the numeric error code indicating the error type. + *

+ * Standard JSON-RPC 2.0 error codes: + *

    + *
  • -32700: Parse error
  • + *
  • -32600: Invalid Request
  • + *
  • -32601: Method not found
  • + *
  • -32602: Invalid params
  • + *
  • -32603: Internal error
  • + *
  • -32000 to -32099: Server error (implementation-defined)
  • + *
* * @return the error code */ @@ -43,9 +90,13 @@ public Integer getCode() { } /** - * Gets the data associated with the error. + * Gets additional information about the error. + *

+ * The structure and type of the data field is defined by the specific error code. + * It may contain detailed debugging information, validation errors, or other + * context-specific data to help diagnose the error. * - * @return the data. May be {@code null} + * @return the error data, or null if not provided */ public Object getData() { return data; diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java index ea7846655..18fbd12ae 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java @@ -8,12 +8,38 @@ import io.a2a.util.Assert; /** - * A JSON RPC error response. + * Represents a JSON-RPC 2.0 error response. + *

+ * An error response is returned when a request cannot be processed successfully. + * According to the JSON-RPC 2.0 specification, an error response must contain: + *

    + *
  • {@code jsonrpc} - Always "2.0"
  • + *
  • {@code error} - A {@link JSONRPCError} object describing the error
  • + *
  • {@code id} - The request ID, or null if the ID could not be determined
  • + *
+ *

+ * The {@code result} field must be absent or null in error responses. This class + * enforces that constraint by fixing the result type parameter to {@code Void}. + * + * @see JSONRPCError + * @see JSONRPCResponse + * @see JSON-RPC 2.0 Response Object */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class JSONRPCErrorResponse extends JSONRPCResponse { + /** + * Constructs a JSON-RPC error response with all fields. + *

+ * This constructor is used by Jackson for JSON deserialization. + * + * @param jsonrpc the JSON-RPC version (must be "2.0") + * @param id the request ID, or null if the ID could not be determined from the request + * @param result must be null for error responses + * @param error the error object describing what went wrong (required) + * @throws IllegalArgumentException if error is null + */ @JsonCreator public JSONRPCErrorResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("result") Void result, @JsonProperty("error") JSONRPCError error) { @@ -21,10 +47,26 @@ public JSONRPCErrorResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProper Assert.checkNotNullParam("error", error); } + /** + * Constructs a JSON-RPC error response with a request ID and error. + *

+ * The jsonrpc field defaults to "2.0". + * + * @param id the request ID + * @param error the error object (required) + */ public JSONRPCErrorResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs a JSON-RPC error response without a request ID. + *

+ * Use this constructor when the request ID could not be determined, + * typically for parse errors or malformed requests. + * + * @param error the error object (required) + */ public JSONRPCErrorResponse(JSONRPCError error) { this(null, null, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java b/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java index 68e2d16fd..f3b8ed5fa 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java @@ -1,13 +1,47 @@ package io.a2a.spec; /** - * Defines the base structure for any JSON-RPC 2.0 request, response, or notification. + * Base interface for all JSON-RPC 2.0 protocol messages in the A2A Protocol. + *

+ * This sealed interface defines the fundamental structure shared by all JSON-RPC 2.0 + * messages used in the A2A Protocol's JSON-RPC transport layer. It ensures type safety + * and exhaustiveness checking by permitting only {@link JSONRPCRequest} and {@link JSONRPCResponse} + * as implementing types. + *

+ * According to the JSON-RPC 2.0 specification, all messages must include a {@code jsonrpc} + * version field set to "2.0", and may optionally include an {@code id} field for correlation + * between requests and responses. + * + * @see JSONRPCRequest + * @see JSONRPCResponse + * @see JSON-RPC 2.0 Specification + * @see A2A Protocol Specification */ public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCResponse { + /** + * The JSON-RPC protocol version string as defined by the JSON-RPC 2.0 specification. + * All messages must have their {@code jsonrpc} field set to this value. + */ String JSONRPC_VERSION = "2.0"; + /** + * Gets the JSON-RPC version for this message. + *

+ * According to the JSON-RPC 2.0 specification, this must be exactly "2.0". + * + * @return the JSON-RPC version string, always {@value #JSONRPC_VERSION} + */ String getJsonrpc(); + + /** + * Gets the request/response correlation identifier. + *

+ * The ID is used to match responses with their corresponding requests. It may be + * a String, Integer, or null for notification-style requests that do not expect a response. + * + * @return the correlation ID, or null for notifications + */ Object getId(); } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java index a88de90f1..90a7c3b7a 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java @@ -8,7 +8,29 @@ import io.a2a.util.Assert; /** - * Represents a JSONRPC request. + * Base class for JSON-RPC 2.0 requests in the A2A Protocol. + *

+ * This sealed class represents a JSON-RPC 2.0 request message with parameterized support + * for different request parameter types. It enforces the JSON-RPC 2.0 specification + * requirements while providing type safety through generic parameters. + *

+ * A JSON-RPC request consists of: + *

    + *
  • {@code jsonrpc} - The protocol version (always "2.0")
  • + *
  • {@code method} - The name of the method to invoke
  • + *
  • {@code params} - Method parameters (optional, type varies by method)
  • + *
  • {@code id} - Correlation identifier for matching with responses (optional for notifications)
  • + *
+ *

+ * This class is sealed to ensure only {@link NonStreamingJSONRPCRequest} and + * {@link StreamingJSONRPCRequest} can extend it, providing compile-time guarantees + * about request types. + * + * @param the type of the params object for this request + * @see NonStreamingJSONRPCRequest + * @see StreamingJSONRPCRequest + * @see JSON-RPC 2.0 Specification + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -19,9 +41,21 @@ public abstract sealed class JSONRPCRequest implements JSONRPCMessage permits protected String method; protected T params; + /** + * Default constructor for JSON deserialization. + */ public JSONRPCRequest() { } + /** + * Constructs a JSON-RPC request with the specified parameters. + * + * @param jsonrpc the JSON-RPC version (defaults to {@value JSONRPCMessage#JSONRPC_VERSION} if null) + * @param id the correlation identifier (must be String, Integer, or null) + * @param method the method name to invoke (required) + * @param params the method parameters (optional) + * @throws IllegalArgumentException if jsonrpc or method is null, or if id is not String/Integer/null + */ public JSONRPCRequest(String jsonrpc, Object id, String method, T params) { Assert.checkNotNullParam("jsonrpc", jsonrpc); Assert.checkNotNullParam("method", method); @@ -32,20 +66,36 @@ public JSONRPCRequest(String jsonrpc, Object id, String method, T params) { this.params = params; } + /** + * {@inheritDoc} + */ @Override public String getJsonrpc() { return this.jsonrpc; } + /** + * {@inheritDoc} + */ @Override public Object getId() { return this.id; } + /** + * Gets the name of the method to be invoked. + * + * @return the method name + */ public String getMethod() { return this.method; } + /** + * Gets the parameters to be passed to the method. + * + * @return the method parameters, or null if none + */ public T getParams() { return this.params; } diff --git a/spec/src/main/java/io/a2a/spec/JsonrpcId.java b/spec/src/main/java/io/a2a/spec/JsonrpcId.java index e4db4b458..152c2a5b1 100644 --- a/spec/src/main/java/io/a2a/spec/JsonrpcId.java +++ b/spec/src/main/java/io/a2a/spec/JsonrpcId.java @@ -1,4 +1,24 @@ package io.a2a.spec; +/** + * Marker interface for JSON-RPC request/response correlation identifiers. + *

+ * According to the JSON-RPC 2.0 specification, the {@code id} field must be + * a String, Number, or NULL value. This interface serves as a type marker + * for implementations that represent valid JSON-RPC IDs. + *

+ * The A2A Java SDK provides two concrete implementations: + *

    + *
  • {@link StringJsonrpcId} - For string-based identifiers
  • + *
  • {@link IntegerJsonrpcId} - For numeric identifiers
  • + *
+ *

+ * Null IDs are also valid and represent notifications (requests that do not + * expect a response). + * + * @see StringJsonrpcId + * @see IntegerJsonrpcId + * @see JSON-RPC 2.0 Request Object + */ public interface JsonrpcId { } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index 5ebb12f76..ed65ebde6 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -8,7 +8,16 @@ import io.a2a.util.Assert; /** - * Parameters for getting list of pushNotificationConfigurations associated with a Task. + * Parameters for listing all push notification configurations for a task. + *

+ * This record specifies which task's push notification configurations to list, returning + * all configured notification endpoints for that task. + * + * @param id the task identifier (required) + * @param metadata optional arbitrary key-value metadata for the request + * @see ListTaskPushNotificationConfigRequest for the request using these parameters + * @see TaskPushNotificationConfig for the configuration structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java index 90ba0f1f5..03f58874c 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java @@ -11,7 +11,17 @@ import io.a2a.util.Utils; /** - * A list task push notification config request. + * JSON-RPC request to list all push notification configurations for a task. + *

+ * This request retrieves all configured push notification endpoints for a specific task, + * allowing clients to enumerate all active notification subscriptions. + *

+ * This class implements the JSON-RPC {@code tasks/pushNotificationConfig/list} method. + * + * @see ListTaskPushNotificationConfigResponse for the response + * @see ListTaskPushNotificationConfigParams for the parameter structure + * @see TaskPushNotificationConfig for the configuration structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java index cc610416e..7d0eb8611 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java @@ -8,7 +8,16 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * A response for a list task push notification config request. + * JSON-RPC response containing all push notification configurations for a task. + *

+ * This response returns a list of all {@link TaskPushNotificationConfig} entries + * configured for the requested task, showing all active notification endpoints. + *

+ * If an error occurs, the error field will contain a {@link JSONRPCError}. + * + * @see ListTaskPushNotificationConfigRequest for the corresponding request + * @see TaskPushNotificationConfig for the configuration structure + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index dd7e860a5..19843d26c 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -16,7 +16,23 @@ import static io.a2a.spec.Message.MESSAGE; /** - * Represents a single message in the conversation between a user and an agent. + * Represents a single message in the conversation between a user and an agent in the A2A Protocol. + *

+ * A Message encapsulates communication content exchanged during agent interactions. It contains the + * message role (user or agent), content parts (text, files, or data), and contextual metadata for + * message threading and correlation. + *

+ * Messages are fundamental to the A2A Protocol's conversational model, enabling rich multi-modal + * communication between users and agents. Each message has a unique identifier and can reference + * related tasks and contexts. + *

+ * Messages implement both {@link EventKind} and {@link StreamingEventKind}, meaning they can be + * sent as standalone events or as part of a streaming response sequence. + *

+ * This class is mutable (allows setting taskId and contextId) to support post-construction correlation + * with tasks and conversation contexts. Use the {@link Builder} for construction. + * + * @see A2A Protocol Specification */ @JsonTypeName(MESSAGE) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -113,8 +129,21 @@ public String getKind() { return kind; } + /** + * Defines the role of the message sender in the conversation. + *

+ * The role determines who originated the message and how it should be processed + * within the conversational context. + */ public enum Role { + /** + * Message originated from the user (client side). + */ USER("user"), + + /** + * Message originated from the agent (server side). + */ AGENT("agent"); private final String role; @@ -123,12 +152,32 @@ public enum Role { this.role = role; } + /** + * Returns the string representation of the role for JSON serialization. + * + * @return the role as a string ("user" or "agent") + */ @JsonValue public String asString() { return this.role; } } + /** + * Builder for constructing {@link Message} instances with fluent API. + *

+ * The Builder provides a convenient way to construct messages with required and optional fields. + * If messageId is not provided, a random UUID will be generated automatically. + *

+ * Example usage: + *

{@code
+     * Message userMessage = new Message.Builder()
+     *     .role(Message.Role.USER)
+     *     .parts(List.of(new TextPart("Hello, agent!", null)))
+     *     .contextId("conv-123")
+     *     .build();
+     * }
+ */ public static class Builder { private Role role; @@ -140,9 +189,17 @@ public static class Builder { private Map metadata; private List extensions; + /** + * Creates a new Builder with all fields unset. + */ public Builder() { } + /** + * Creates a new Builder initialized with values from an existing Message. + * + * @param message the Message to copy values from + */ public Builder(Message message) { role = message.role; parts = message.parts; @@ -154,51 +211,115 @@ public Builder(Message message) { extensions = message.extensions; } + /** + * Sets the role of the message sender. + * + * @param role the message role (required) + * @return this builder for method chaining + */ public Builder role(Role role) { this.role = role; return this; } + /** + * Sets the content parts of the message. + * + * @param parts the list of message parts (required, must not be empty) + * @return this builder for method chaining + */ public Builder parts(List> parts) { this.parts = parts; return this; } + /** + * Sets the content parts of the message from varargs. + * + * @param parts the message parts (required, must not be empty) + * @return this builder for method chaining + */ public Builder parts(Part...parts) { this.parts = List.of(parts); return this; } + /** + * Sets the unique identifier for this message. + *

+ * If not provided, a random UUID will be generated when {@link #build()} is called. + * + * @param messageId the message identifier (optional) + * @return this builder for method chaining + */ public Builder messageId(String messageId) { this.messageId = messageId; return this; } + /** + * Sets the conversation context identifier. + * + * @param contextId the context identifier (optional) + * @return this builder for method chaining + */ public Builder contextId(String contextId) { this.contextId = contextId; return this; } + /** + * Sets the task identifier this message is associated with. + * + * @param taskId the task identifier (optional) + * @return this builder for method chaining + */ public Builder taskId(String taskId) { this.taskId = taskId; return this; } + /** + * Sets the list of reference task identifiers this message relates to. + * + * @param referenceTaskIds the list of reference task IDs (optional) + * @return this builder for method chaining + */ public Builder referenceTaskIds(List referenceTaskIds) { this.referenceTaskIds = referenceTaskIds; return this; } + /** + * Sets additional metadata for the message. + * + * @param metadata map of metadata key-value pairs (optional) + * @return this builder for method chaining + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Sets the list of protocol extensions used in this message. + * + * @param extensions the list of extension identifiers (optional) + * @return this builder for method chaining + */ public Builder extensions(List extensions) { this.extensions = (extensions == null) ? null : List.copyOf(extensions); return this; } + /** + * Builds a new {@link Message} from the current builder state. + *

+ * If messageId was not set, a random UUID will be generated. + * + * @return a new Message instance + * @throws IllegalArgumentException if required fields are missing or invalid + */ public Message build() { return new Message(role, parts, messageId == null ? UUID.randomUUID().toString() : messageId, contextId, taskId, referenceTaskIds, metadata, extensions); diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index cbe72cfb1..0142addb6 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -8,7 +8,20 @@ import org.jspecify.annotations.Nullable; /** - * Defines configuration options for a `message/send` or `message/stream` request. + * Configuration options for {@code message/send} and {@code message/stream} requests. + *

+ * This record defines how the agent should process a message request, including output format + * preferences, conversation history context, push notification settings, and blocking behavior. + *

+ * All fields are optional and have sensible defaults when not specified. + * + * @param acceptedOutputModes list of output modes the client can handle (e.g., "text", "audio") + * @param historyLength number of previous messages to include in conversation context (must be non-negative) + * @param pushNotificationConfig configuration for asynchronous push notifications when task state changes + * @param blocking whether the request should block until task completion (defaults to true) + * @see MessageSendParams for the parameters that use this configuration + * @see PushNotificationConfig for push notification options + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -21,6 +34,11 @@ public record MessageSendConfiguration(List acceptedOutputModes, Integer } } + /** + * Builder for constructing {@link MessageSendConfiguration} instances. + *

+ * Provides a fluent API for configuring message send behavior with sensible defaults. + */ public static class Builder { List acceptedOutputModes; @@ -28,16 +46,35 @@ public static class Builder { PushNotificationConfig pushNotificationConfig; Boolean blocking = true; + /** + * Sets the accepted output modes. + * + * @param acceptedOutputModes list of output modes the client can handle + * @return this builder + */ public Builder acceptedOutputModes(List acceptedOutputModes) { this.acceptedOutputModes = acceptedOutputModes; return this; } + /** + * Sets the push notification configuration. + * + * @param pushNotificationConfig configuration for push notifications + * @return this builder + */ public Builder pushNotificationConfig(@Nullable PushNotificationConfig pushNotificationConfig) { this.pushNotificationConfig = pushNotificationConfig; return this; } + /** + * Sets the conversation history length. + * + * @param historyLength number of previous messages to include (must be non-negative) + * @return this builder + * @throws IllegalArgumentException if historyLength is negative + */ public Builder historyLength(@Nullable Integer historyLength) { if (historyLength != null && historyLength < 0) { throw new IllegalArgumentException("Invalid history length"); @@ -46,11 +83,22 @@ public Builder historyLength(@Nullable Integer historyLength) { return this; } + /** + * Sets whether the request should block until completion. + * + * @param blocking true to block until task completes, false for fire-and-forget + * @return this builder + */ public Builder blocking(@NonNull Boolean blocking) { this.blocking = blocking; return this; } + /** + * Builds the {@link MessageSendConfiguration}. + * + * @return a new message send configuration instance + */ public MessageSendConfiguration build() { return new MessageSendConfiguration(acceptedOutputModes, historyLength, pushNotificationConfig, blocking); } diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index 7cba6f1ae..106c65b18 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -7,8 +7,22 @@ import io.a2a.util.Assert; /** - * Defines the parameters for a request to send a message to an agent. This can be used - * to create a new task, continue an existing one, or restart a task. + * Parameters for sending a message to an agent in the A2A Protocol. + *

+ * This record encapsulates the message content, optional configuration, and metadata for + * agent task requests. It is used by both {@link SendMessageRequest} and + * {@link SendStreamingMessageRequest} to define what message to send and how to process it. + *

+ * The message can create a new task, continue an existing task (if it contains a task ID), + * or restart a task depending on the agent's implementation and the message context. + * + * @param message the message to send to the agent (required) + * @param configuration optional configuration for message processing behavior + * @param metadata optional arbitrary key-value metadata for the request + * @see SendMessageRequest for non-streaming message delivery + * @see SendStreamingMessageRequest for streaming message delivery + * @see MessageSendConfiguration for available configuration options + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -19,26 +33,56 @@ public record MessageSendParams(Message message, MessageSendConfiguration config Assert.checkNotNullParam("message", message); } + /** + * Builder for constructing {@link MessageSendParams} instances. + *

+ * Provides a fluent API for building message send parameters with optional + * configuration and metadata. + */ public static class Builder { Message message; MessageSendConfiguration configuration; Map metadata; + /** + * Sets the message to send to the agent. + * + * @param message the message (required) + * @return this builder + */ public Builder message(Message message) { this.message = message; return this; } + /** + * Sets the optional configuration for message processing. + * + * @param configuration the message send configuration + * @return this builder + */ public Builder configuration(MessageSendConfiguration configuration) { this.configuration = configuration; return this; } + /** + * Sets optional metadata for the request. + * + * @param metadata arbitrary key-value metadata + * @return this builder + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Builds the {@link MessageSendParams}. + * + * @return a new message send parameters instance + * @throws IllegalArgumentException if message is null + */ public MessageSendParams build() { return new MessageSendParams(message, configuration, metadata); } diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java index 5a46b336f..764b8beca 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -8,7 +8,23 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An error indicating that the requested method does not exist or is not available. + * JSON-RPC error indicating that the requested method does not exist or is not available. + *

+ * This error is returned when a client attempts to invoke a JSON-RPC method that is not + * implemented by the agent. In the A2A Protocol context, this typically means calling + * an unsupported protocol method. + *

+ * Corresponds to JSON-RPC 2.0 error code {@code -32601}. + *

+ * Usage example: + *

{@code
+ * // Standard error for unknown method
+ * throw new MethodNotFoundError();
+ * }
+ * + * @see JSONRPCError for the base error class + * @see A2AError for the error marker interface + * @see JSON-RPC 2.0 Error Codes */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index 37f5bd755..88589bb7e 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -10,7 +10,27 @@ import static io.a2a.spec.MutualTLSSecurityScheme.MUTUAL_TLS; /** - * Defines a security scheme using mTLS authentication. + * Mutual TLS (mTLS) security scheme for agent authentication. + *

+ * This security scheme uses mutual TLS authentication, where both the client and server + * present X.509 certificates to authenticate each other. This provides strong, + * certificate-based authentication at the transport layer. + *

+ * Unlike other authentication schemes that operate at the HTTP layer, mTLS authentication + * occurs during the TLS handshake before any HTTP communication begins. This makes it + * particularly suitable for high-security environments and machine-to-machine communication. + *

+ * Example usage: + *

{@code
+ * MutualTLSSecurityScheme scheme = new MutualTLSSecurityScheme(
+ *     "Client certificate authentication required"
+ * );
+ * }
+ * + * @see SecurityScheme for the base interface + * @see OpenAPI Security Scheme + * @see RFC 8446 - TLS 1.3 Client Authentication + * @see A2A Protocol Specification */ @JsonTypeName(MUTUAL_TLS) @JsonInclude(JsonInclude.Include.NON_ABSENT) diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 9094eee7c..7a1d972b0 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -11,7 +11,18 @@ import static io.a2a.spec.OAuth2SecurityScheme.OAUTH2; /** - * Defines a security scheme using OAuth 2.0. + * OAuth 2.0 security scheme for agent authentication. + *

+ * This security scheme uses OAuth 2.0 authorization flows to authenticate requests. + * Supports authorization code, client credentials, implicit, and password flows + * via the {@link OAuthFlows} configuration. + *

+ * Corresponds to the OpenAPI "oauth2" security scheme type. + * + * @see SecurityScheme for the base interface + * @see OAuthFlows for flow configuration + * @see OpenAPI Security Scheme + * @see A2A Protocol Specification */ @JsonTypeName(OAUTH2) @JsonInclude(JsonInclude.Include.NON_ABSENT) diff --git a/spec/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java index 8c5015893..6aa704db7 100644 --- a/spec/src/main/java/io/a2a/spec/OAuthFlows.java +++ b/spec/src/main/java/io/a2a/spec/OAuthFlows.java @@ -4,7 +4,20 @@ import com.fasterxml.jackson.annotation.JsonInclude; /** - * Defines the configuration for the supported OAuth 2.0 flows. + * Configuration for supported OAuth 2.0 authorization flows. + *

+ * This record specifies which OAuth 2.0 flows the agent supports and their configurations, + * including authorization and token endpoints, scopes, and refresh URLs. + *

+ * All fields are optional; only the flows supported by the agent should be specified. + * + * @param authorizationCode OAuth 2.0 authorization code flow configuration + * @param clientCredentials OAuth 2.0 client credentials flow configuration + * @param implicit OAuth 2.0 implicit flow configuration + * @param password OAuth 2.0 resource owner password credentials flow configuration + * @see OAuth2SecurityScheme for the security scheme using these flows + * @see OpenAPI OAuth Flows Object + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 01726d2ea..01906bd52 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -10,7 +10,28 @@ import static io.a2a.spec.OpenIdConnectSecurityScheme.OPENID_CONNECT; /** - * Defines a security scheme using OpenID Connect. + * OpenID Connect security scheme for agent authentication. + *

+ * This security scheme uses OpenID Connect Discovery to automatically configure + * authentication. The {@code openIdConnectUrl} must point to an OpenID Connect + * Discovery document that describes the provider's configuration, including + * authorization and token endpoints. + *

+ * OpenID Connect builds on OAuth 2.0 to provide identity layer functionality, + * enabling clients to verify user identity and obtain basic profile information. + *

+ * Example usage: + *

{@code
+ * OpenIdConnectSecurityScheme scheme = new OpenIdConnectSecurityScheme.Builder()
+ *     .openIdConnectUrl("https://example.com/.well-known/openid-configuration")
+ *     .description("OpenID Connect authentication")
+ *     .build();
+ * }
+ * + * @see SecurityScheme for the base interface + * @see OpenAPI Security Scheme + * @see OpenID Connect Discovery + * @see A2A Protocol Specification */ @JsonTypeName(OPENID_CONNECT) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -52,20 +73,44 @@ public String getType() { return type; } + /** + * Builder for constructing {@link OpenIdConnectSecurityScheme} instances. + *

+ * Provides a fluent API for creating OpenID Connect security schemes. + * The {@code openIdConnectUrl} parameter is required. + */ public static class Builder { private String openIdConnectUrl; private String description; + /** + * Sets the OpenID Connect Discovery URL. + * + * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) + * @return this builder instance + */ public Builder openIdConnectUrl(String openIdConnectUrl) { this.openIdConnectUrl = openIdConnectUrl; return this; } + /** + * Sets an optional description of the security scheme. + * + * @param description human-readable description + * @return this builder instance + */ public Builder description(String description) { this.description = description; return this; } + /** + * Builds the {@link OpenIdConnectSecurityScheme} instance. + * + * @return a new immutable OpenIdConnectSecurityScheme + * @throws IllegalArgumentException if openIdConnectUrl is null + */ public OpenIdConnectSecurityScheme build() { return new OpenIdConnectSecurityScheme(openIdConnectUrl, description); } diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index 2bdfb69d1..93297a047 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -7,8 +7,25 @@ import com.fasterxml.jackson.annotation.JsonValue; /** - * A fundamental unit with a Message or Artifact. - * @param the type of unit + * Base class for content parts within {@link Message}s and {@link Artifact}s. + *

+ * Parts represent the fundamental content units in the A2A Protocol, allowing multi-modal + * communication through different content types. A Part can be: + *

    + *
  • {@link TextPart} - Plain text content
  • + *
  • {@link FilePart} - File content (as bytes or URI reference)
  • + *
  • {@link DataPart} - Structured data (JSON objects)
  • + *
+ *

+ * Parts use polymorphic JSON serialization with the "kind" discriminator property to + * determine the concrete type during deserialization. + *

+ * Each Part can include optional metadata for additional context about the content. + * + * @param the type of content contained in this part + * @see Message + * @see Artifact + * @see A2A Protocol Specification */ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -22,9 +39,23 @@ @JsonSubTypes.Type(value = DataPart.class, name = DataPart.DATA) }) public abstract class Part { + /** + * Enum defining the different types of content parts. + */ public enum Kind { + /** + * Plain text content part. + */ TEXT("text"), + + /** + * File content part (bytes or URI). + */ FILE("file"), + + /** + * Structured data content part (JSON). + */ DATA("data"); private final String kind; @@ -33,14 +64,29 @@ public enum Kind { this.kind = kind; } + /** + * Returns the string representation of the kind for JSON serialization. + * + * @return the kind as a string + */ @JsonValue public String asString() { return this.kind; } } + /** + * Returns the kind of this part. + * + * @return the Part.Kind indicating the content type + */ public abstract Kind getKind(); + /** + * Returns optional metadata associated with this part. + * + * @return map of metadata key-value pairs, or null if no metadata + */ public abstract Map getMetadata(); } \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java index 424a4817f..55554bbb3 100644 --- a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java @@ -8,7 +8,23 @@ import io.a2a.util.Assert; /** - * Defines configuration details for the OAuth 2.0 Resource Owner Password flow. + * Configuration for the OAuth 2.0 Resource Owner Password Credentials flow. + *

+ * The password flow allows the client to exchange a user's credentials (username and password) + * directly for an access token. This flow should only be used when there is a high degree of + * trust between the user and the client application. + *

+ * Note: This flow is generally discouraged and deprecated in OAuth 2.1 + * because it exposes user credentials to the client. Use the authorization code flow + * with PKCE instead whenever possible. + * + * @param refreshUrl URL for obtaining refresh tokens (optional) + * @param scopes map of available OAuth scopes to their descriptions (required) + * @param tokenUrl URL for the token endpoint where credentials are exchanged for tokens (required) + * @see OAuthFlows for the container of all supported OAuth flows + * @see OAuth2SecurityScheme for the security scheme using these flows + * @see RFC 6749 - Resource Owner Password Credentials Grant + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java index 1ef29ccd4..86342374f 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java @@ -7,7 +7,20 @@ import io.a2a.util.Assert; /** - * Defines authentication details for a push notification endpoint. + * Authentication details for push notification endpoints. + *

+ * This record specifies the authentication schemes and credentials required for the agent + * to authenticate when POSTing task updates to the push notification endpoint. Supports + * various authentication methods including HTTP Basic, Bearer tokens, API keys, and OAuth. + *

+ * The {@code schemes} field lists supported authentication types (e.g., "Basic", "Bearer"), + * while {@code credentials} contains the actual authentication data (e.g., base64-encoded + * credentials, tokens). + * + * @param schemes list of supported authentication scheme names (required) + * @param credentials the authentication credentials or tokens (optional) + * @see PushNotificationConfig for the full push notification configuration + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index fcffa2c7f..100c75e76 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -6,7 +6,27 @@ import io.a2a.util.Assert; /** - * Defines the configuration for setting up push notifications for task updates. + * Configuration for asynchronous push notifications of task updates. + *

+ * This record defines the endpoint and authentication details for receiving task event + * notifications. When configured, the agent will POST task updates (status changes, + * artifact additions, completions) to the specified URL as they occur, enabling + * asynchronous workflows without polling. + *

+ * Authentication can be provided via either: + *

    + *
  • Simple bearer token in the {@code token} field
  • + *
  • More complex authentication via {@link PushNotificationAuthenticationInfo}
  • + *
+ * + * @param url the HTTP/HTTPS endpoint URL to receive push notifications (required) + * @param token optional bearer token for simple authentication + * @param authentication optional complex authentication configuration + * @param id optional client-provided identifier for this configuration + * @see PushNotificationAuthenticationInfo for authentication details + * @see TaskPushNotificationConfig for task-specific bindings + * @see MessageSendConfiguration for configuring push notifications on message send + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -17,15 +37,27 @@ public record PushNotificationConfig(String url, String token, PushNotificationA Assert.checkNotNullParam("url", url); } + /** + * Builder for constructing {@link PushNotificationConfig} instances. + *

+ * Provides a fluent API for building push notification configurations with optional + * authentication and identification. + */ public static class Builder { private String url; private String token; private PushNotificationAuthenticationInfo authentication; private String id; + /** Creates an empty builder. */ public Builder() { } + /** + * Creates a builder initialized from an existing configuration. + * + * @param notificationConfig the configuration to copy + */ public Builder(PushNotificationConfig notificationConfig) { this.url = notificationConfig.url; this.token = notificationConfig.token; @@ -33,26 +65,56 @@ public Builder(PushNotificationConfig notificationConfig) { this.id = notificationConfig.id; } + /** + * Sets the push notification endpoint URL. + * + * @param url the HTTP/HTTPS endpoint (required) + * @return this builder + */ public Builder url(String url) { this.url = url; return this; } + /** + * Sets the bearer token for simple authentication. + * + * @param token the bearer token + * @return this builder + */ public Builder token(String token) { this.token = token; return this; } + /** + * Sets complex authentication information. + * + * @param authenticationInfo the authentication configuration + * @return this builder + */ public Builder authenticationInfo(PushNotificationAuthenticationInfo authenticationInfo) { this.authentication = authenticationInfo; return this; } + /** + * Sets the client-provided configuration identifier. + * + * @param id the configuration ID + * @return this builder + */ public Builder id(String id) { this.id = id; return this; } + /** + * Builds the {@link PushNotificationConfig}. + * + * @return a new push notification configuration + * @throws IllegalArgumentException if url is null + */ public PushNotificationConfig build() { return new PushNotificationConfig(url, token, authentication, id); } diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index c618e2b78..a2a676063 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -8,7 +8,29 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An A2A-specific error indicating that the agent does not support push notifications. + * A2A Protocol error indicating that the agent does not support push notifications. + *

+ * This error is returned when a client attempts push notification operations + * (such as {@link SetTaskPushNotificationConfigRequest}) on an agent that has + * {@link AgentCapabilities#pushNotifications()} set to {@code false}. + *

+ * Push notifications allow agents to proactively send task updates to clients via + * HTTP callbacks. Agents that don't support this capability will return this error + * for all push notification-related methods. + *

+ * Corresponds to A2A-specific error code {@code -32003}. + *

+ * Usage example: + *

{@code
+ * if (!agentCard.capabilities().pushNotifications()) {
+ *     throw new PushNotificationNotSupportedError();
+ * }
+ * }
+ * + * @see AgentCapabilities#pushNotifications() for push notification capability + * @see SetTaskPushNotificationConfigRequest for configuring push notifications + * @see TaskPushNotificationConfig for push notification configuration + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index b3cc104e3..afc617ed2 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -19,8 +19,24 @@ @JsonSubTypes.Type(value = MutualTLSSecurityScheme.class, name = MutualTLSSecurityScheme.MUTUAL_TLS) }) /** - * Defines a security scheme that can be used to secure an agent's endpoints. - * This is a discriminated union type based on the OpenAPI 3.0 Security Scheme Object. + * Base interface for security schemes used to authenticate access to agent endpoints. + *

+ * This sealed interface defines a discriminated union of authentication mechanisms based on + * the OpenAPI 3.0 Security Scheme Object specification. Each implementation represents a + * different authentication strategy that can be declared in an {@link AgentCard}. + *

+ * Supported security schemes: + *

    + *
  • {@link APIKeySecurityScheme} - API key in header, query, or cookie
  • + *
  • {@link HTTPAuthSecurityScheme} - HTTP Basic or Bearer authentication
  • + *
  • {@link OAuth2SecurityScheme} - OAuth 2.0 flows
  • + *
  • {@link OpenIdConnectSecurityScheme} - OpenID Connect discovery
  • + *
  • {@link MutualTLSSecurityScheme} - Client certificate authentication
  • + *
+ * + * @see AgentCard#securitySchemes() for security scheme declarations + * @see OpenAPI Security Scheme Object + * @see A2A Protocol Specification */ public sealed interface SecurityScheme permits APIKeySecurityScheme, HTTPAuthSecurityScheme, OAuth2SecurityScheme, OpenIdConnectSecurityScheme, MutualTLSSecurityScheme { diff --git a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java index d31f364e4..dd4faefcb 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java @@ -12,14 +12,50 @@ import io.a2a.util.Assert; /** - * Used to send a message request. + * JSON-RPC request for the {@code message/send} method in the A2A Protocol. + *

+ * This request initiates a new task or continues an existing task by sending a message + * to an agent. The request returns a single response containing the final {@link Task} + * state once processing completes. + *

+ * The {@code message/send} method is used for: + *

    + *
  • Creating new tasks with an initial user message
  • + *
  • Continuing existing tasks with additional messages
  • + *
  • Restarting tasks with new context
  • + *
+ *

+ * For real-time event streaming during task execution, use {@link SendStreamingMessageRequest} + * with the {@code message/stream} method instead. + *

+ * This class includes a fluent {@link Builder} for convenient request construction. + * + * @see SendStreamingMessageRequest + * @see MessageSendParams + * @see Task + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) public final class SendMessageRequest extends NonStreamingJSONRPCRequest { + /** + * The JSON-RPC method name for sending a message: "message/send". + */ public static final String METHOD = "message/send"; + /** + * Constructs a SendMessageRequest with the specified JSON-RPC fields. + *

+ * This constructor is used by Jackson for JSON deserialization and validates + * that the method name is exactly "message/send". + * + * @param jsonrpc the JSON-RPC version (must be "2.0") + * @param id the request correlation identifier (String, Integer, or null) + * @param method the method name (must be {@value #METHOD}) + * @param params the message send parameters (required) + * @throws IllegalArgumentException if validation fails + */ @JsonCreator public SendMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") MessageSendParams params) { @@ -41,36 +77,57 @@ public SendMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty this.params = params; } + /** + * Constructs a SendMessageRequest with default JSON-RPC version. + * + * @param id the request correlation identifier + * @param params the message send parameters (required) + */ public SendMessageRequest(Object id, MessageSendParams params) { this(JSONRPC_VERSION, id, METHOD, params); } + /** + * Builder for constructing {@link SendMessageRequest} instances with a fluent API. + *

+ * If no ID is provided, a UUID will be auto-generated when {@link #build()} is called. + */ public static class Builder { private String jsonrpc; private Object id; private String method; private MessageSendParams params; + /** Sets the JSON-RPC version. */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** Sets the request correlation ID. */ public Builder id(Object id) { this.id = id; return this; } + /** Sets the method name. */ public Builder method(String method) { this.method = method; return this; } + /** Sets the message send parameters (required). */ public Builder params(MessageSendParams params) { this.params = params; return this; } + /** + * Builds the SendMessageRequest. + * Auto-generates a UUID for the ID if not set. + * + * @return the constructed request + */ public SendMessageRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java index c4ebe8315..34489af42 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java @@ -11,7 +11,21 @@ import java.util.UUID; /** - * Used to initiate a task with streaming. + * JSON-RPC request to initiate a new task with streaming event delivery. + *

+ * This request starts agent processing for a message and immediately returns a stream of events + * representing the agent's progress and responses. Unlike {@link SendMessageRequest}, this enables + * real-time progress updates as the agent processes the request. + *

+ * The streaming response provides {@link StreamingEventKind} events such as task updates, + * artifact additions, and status changes as they occur, rather than waiting for final completion. + *

+ * This class implements the JSON-RPC {@code message/stream} method as specified in the A2A Protocol. + * + * @see SendMessageRequest for non-streaming message delivery + * @see MessageSendParams for parameter structure + * @see StreamingEventKind for event types in streaming responses + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -41,32 +55,67 @@ public SendStreamingMessageRequest(Object id, MessageSendParams params) { this(null, id, METHOD, params); } + /** + * Builder for constructing {@link SendStreamingMessageRequest} instances. + *

+ * Provides a fluent API for building streaming message requests with optional customization + * of JSON-RPC protocol fields. + */ public static class Builder { private String jsonrpc; private Object id; private String method = METHOD; private MessageSendParams params; + /** + * Sets the JSON-RPC protocol version. + * + * @param jsonrpc the JSON-RPC version (defaults to "2.0") + * @return this builder + */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the JSON-RPC request ID. + * + * @param id the request identifier (auto-generated UUID if null) + * @return this builder + */ public Builder id(Object id) { this.id = id; return this; } + /** + * Sets the JSON-RPC method name. + * + * @param method the method name (defaults to "message/stream") + * @return this builder + */ public Builder method(String method) { this.method = method; return this; } + /** + * Sets the message send parameters. + * + * @param params the parameters containing the message and configuration + * @return this builder + */ public Builder params(MessageSendParams params) { this.params = params; return this; } + /** + * Builds the {@link SendStreamingMessageRequest}. + * + * @return a new streaming message request instance + */ public SendStreamingMessageRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java index 0ed6ca80d..f9e4013ea 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java @@ -7,7 +7,21 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * The response after receiving a request to initiate a task with streaming. + * JSON-RPC response for streaming message initiation requests. + *

+ * This response is sent after receiving a {@link SendStreamingMessageRequest} and contains + * a stream of {@link StreamingEventKind} events representing the agent's processing progress. + * Unlike non-streaming responses, this provides real-time updates as the agent works. + *

+ * The result field contains events such as {@link Task}, {@link TaskStatusUpdateEvent}, + * {@link TaskArtifactUpdateEvent}, and {@link Message} as they are produced by the agent. + *

+ * If an error occurs during request processing, the error field will be populated with + * a {@link JSONRPCError} instead of streaming events. + * + * @see SendStreamingMessageRequest for the corresponding request + * @see StreamingEventKind for the types of events that can be streamed + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java index 7d53083a1..edc9ae6cb 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java @@ -12,7 +12,18 @@ import io.a2a.util.Assert; /** - * Used to set a task push notification request. + * JSON-RPC request to configure push notifications for a specific task. + *

+ * This request registers or updates the push notification endpoint for a task, enabling + * the agent to send asynchronous updates (status changes, artifact additions) to the + * specified URL without requiring client polling. + *

+ * This class implements the JSON-RPC {@code tasks/pushNotificationConfig/set} method. + * + * @see SetTaskPushNotificationConfigResponse for the response + * @see TaskPushNotificationConfig for the parameter structure + * @see PushNotificationConfig for notification endpoint details + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java index c40f18f18..a8d302106 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java @@ -6,7 +6,19 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * The response after receiving a set task push notification request. + * JSON-RPC response confirming push notification configuration for a task. + *

+ * This response confirms that the push notification configuration has been successfully + * registered for the task. The result contains the full {@link TaskPushNotificationConfig} + * as stored by the agent. + *

+ * If push notifications are not supported or an error occurs, the error field will contain + * a {@link JSONRPCError} (e.g., {@link PushNotificationNotSupportedError}). + * + * @see SetTaskPushNotificationConfigRequest for the corresponding request + * @see TaskPushNotificationConfig for the configuration structure + * @see PushNotificationNotSupportedError for the error when unsupported + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java index b8c16b00a..dcd497211 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java +++ b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java @@ -8,6 +8,35 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +/** + * Sealed interface for events that can be emitted during streaming A2A Protocol operations. + *

+ * StreamingEventKind represents events suitable for asynchronous, progressive updates during + * agent task execution. Streaming allows agents to provide incremental feedback as work progresses, + * rather than waiting until task completion. + *

+ * Streaming events use polymorphic JSON serialization with the "kind" discriminator to determine + * the concrete type during deserialization. + *

+ * Permitted implementations: + *

    + *
  • {@link Task} - Complete task state (typically the final event in a stream)
  • + *
  • {@link Message} - Full message (complete message in the stream)
  • + *
  • {@link TaskStatusUpdateEvent} - Incremental status updates (e.g., SUBMITTED → WORKING → COMPLETED)
  • + *
  • {@link TaskArtifactUpdateEvent} - Incremental artifact updates (partial results, chunks)
  • + *
+ *

+ * Streaming events enable patterns like: + *

    + *
  • Progressive text generation (chunks of response as generated)
  • + *
  • Status notifications (task state transitions)
  • + *
  • Partial results (early artifacts before task completion)
  • + *
+ * + * @see Event + * @see EventKind + * @see UpdateEvent + */ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, @@ -22,5 +51,10 @@ }) public sealed interface StreamingEventKind extends Event permits Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent { + /** + * Returns the kind identifier for this streaming event. + * + * @return the event kind string (e.g., "task", "message", "status-update", "artifact-update") + */ String getKind(); } diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java index 7642c41d2..f0dd5f244 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java @@ -5,7 +5,31 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; /** - * Represents a streaming JSON-RPC request. + * Base class for JSON-RPC requests that support streaming responses in the A2A Protocol. + *

+ * This sealed class extends {@link JSONRPCRequest} to provide specialized support for + * A2A Protocol methods that return streaming responses. Streaming requests enable + * server-sent events and real-time updates to be pushed to clients as they occur, + * rather than waiting for a complete response. + *

+ * The A2A Protocol defines two primary streaming operations: + *

    + *
  • {@link SendStreamingMessageRequest} - Stream task execution events in real-time
  • + *
  • {@link TaskResubscriptionRequest} - Resubscribe to events from an existing task
  • + *
+ *

+ * Streaming requests follow the JSON-RPC 2.0 specification structure but the response + * is delivered as a stream of {@link StreamingEventKind} objects rather than a single + * response message. + *

+ * This class uses a custom deserializer to properly handle polymorphic deserialization + * of streaming request types. + * + * @param the type of the params object for this streaming request + * @see SendStreamingMessageRequest + * @see TaskResubscriptionRequest + * @see StreamingEventKind + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java b/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java index 74a28272f..9a033a979 100644 --- a/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java +++ b/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java @@ -1,4 +1,22 @@ package io.a2a.spec; -public class StringJsonrpcId { +/** + * String-based implementation of JSON-RPC request/response correlation identifier. + *

+ * This class represents a JSON-RPC ID that uses a String value for correlation + * between requests and responses. According to the JSON-RPC 2.0 specification, + * String IDs are one of the valid identifier types (along with Number and NULL). + *

+ * String IDs are commonly used when: + *

    + *
  • Client-generated correlation requires UUID or similar format
  • + *
  • IDs need to encode additional context or metadata
  • + *
  • Compatibility with systems that use string-based correlation
  • + *
+ * + * @see JsonrpcId + * @see IntegerJsonrpcId + * @see JSON-RPC 2.0 Request Object + */ +public class StringJsonrpcId implements JsonrpcId { } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index 873f23431..c2fd23d10 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -14,7 +14,34 @@ import static io.a2a.spec.Task.TASK; /** - * Represents a single, stateful operation or conversation between a client and an agent. + * Represents a single, stateful operation or conversation between a client and an agent in the A2A Protocol. + *

+ * A Task encapsulates the complete lifecycle of an agent interaction, from submission through completion, + * cancellation, or failure. It maintains the current state, accumulated artifacts (responses), conversation + * history, and metadata associated with the operation. + *

+ * Tasks are the fundamental unit of work in the A2A Protocol. When a client sends a message to an agent, + * a Task is created to track the operation. The agent updates the Task's state as it processes the request, + * and may add artifacts containing partial or final responses. The Task's status transitions through + * various states (SUBMITTED, WORKING, COMPLETED, etc.) until reaching a final state. + *

+ * Tasks support both blocking and streaming patterns: + *

    + *
  • Blocking: Client sends a message and waits for the Task to reach a final state
  • + *
  • Streaming: Client subscribes to Task updates and receives incremental artifacts as they are produced
  • + *
+ *

+ * Tasks are immutable once created and use the Builder pattern for construction. Updates to a Task's + * state are communicated via new Task instances or TaskStatusUpdateEvent/TaskArtifactUpdateEvent objects. + *

+ * This class implements {@link EventKind} and {@link StreamingEventKind}, allowing Task instances to + * be transmitted as events in both blocking and streaming scenarios. + * + * @see TaskStatus + * @see TaskState + * @see Artifact + * @see Message + * @see A2A Protocol Specification */ @JsonTypeName(TASK) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -86,6 +113,26 @@ public String getKind() { return kind; } + /** + * Builder for constructing immutable {@link Task} instances. + *

+ * The Builder pattern is used to enforce immutability of Task objects while providing + * a fluent API for setting required and optional fields. This approach ensures that once + * a Task is created, its state cannot be modified directly, which is important for + * thread-safety and protocol correctness. + *

+ * Example usage: + *

{@code
+     * Task task = new Task.Builder()
+     *     .id("task-123")
+     *     .contextId("context-456")
+     *     .status(new TaskStatus(TaskState.WORKING))
+     *     .artifacts(List.of(new Artifact(...)))
+     *     .history(List.of(userMessage))
+     *     .metadata(Map.of("key", "value"))
+     *     .build();
+     * }
+ */ public static class Builder { private String id; private String contextId; @@ -94,10 +141,21 @@ public static class Builder { private List history; private Map metadata; + /** + * Creates a new Builder with all fields unset. + */ public Builder() { } + /** + * Creates a new Builder initialized with values from an existing Task. + *

+ * This constructor allows for creating a modified copy of an existing Task + * by copying all fields and then selectively updating specific values. + * + * @param task the Task to copy values from + */ public Builder(Task task) { id = task.id; contextId = task.contextId; @@ -108,41 +166,111 @@ public Builder(Task task) { } + /** + * Sets the unique identifier for this task. + * + * @param id the task ID (required) + * @return this builder for method chaining + */ public Builder id(String id) { this.id = id; return this; } + /** + * Sets the context identifier associating this task with a conversation or session. + *

+ * Multiple tasks may share the same contextId if they are part of a multi-turn + * conversation or related workflow. + * + * @param contextId the context ID (required) + * @return this builder for method chaining + */ public Builder contextId(String contextId) { this.contextId = contextId; return this; } + /** + * Sets the current status of the task. + *

+ * The status includes the state (SUBMITTED, WORKING, COMPLETED, etc.), + * an optional message, and a timestamp. + * + * @param status the task status (required) + * @return this builder for method chaining + * @see TaskStatus + */ public Builder status(TaskStatus status) { this.status = status; return this; } + /** + * Sets the list of artifacts produced by the agent during task execution. + *

+ * Artifacts represent the agent's responses or output, which may include + * text, files, data, or other content types. Artifacts accumulate over the + * lifetime of the task, especially in streaming scenarios. + * + * @param artifacts the list of artifacts (optional) + * @return this builder for method chaining + * @see Artifact + */ public Builder artifacts(List artifacts) { this.artifacts = artifacts; return this; } + /** + * Sets the conversation history for this task. + *

+ * The history contains all messages exchanged between the client and agent + * as part of this task, providing context for multi-turn interactions. + * + * @param history the list of messages (optional) + * @return this builder for method chaining + * @see Message + */ public Builder history(List history) { this.history = history; return this; } + /** + * Sets the conversation history using a varargs array of messages. + *

+ * This is a convenience method for setting history without creating a List explicitly. + * + * @param history the messages to include in the history + * @return this builder for method chaining + * @see Message + */ public Builder history(Message... history) { this.history = List.of(history); return this; } + /** + * Sets arbitrary metadata associated with the task. + *

+ * Metadata can be used to store custom information about the task, + * such as client identifiers, routing information, or application-specific data. + * + * @param metadata map of metadata key-value pairs (optional) + * @return this builder for method chaining + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Builds an immutable {@link Task} from the current builder state. + * + * @return a new Task instance + * @throws IllegalArgumentException if any required field (id, contextId, status) is null + */ public Task build() { return new Task(id, contextId, status, artifacts, history, metadata); } diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 1e1cad947..6cd073eb6 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -12,8 +12,32 @@ import static io.a2a.spec.TaskArtifactUpdateEvent.ARTIFACT_UPDATE; /** - * An event sent by the agent to notify the client that an artifact has been - * generated or updated. This is typically used in streaming models. + * Event notifying that a task artifact has been created, modified, or appended to. + *

+ * TaskArtifactUpdateEvent is emitted during streaming operations to deliver partial or complete + * artifacts as they become available. This enables progressive result delivery and real-time + * feedback for long-running operations. + *

+ * The event supports two primary patterns: + *

    + *
  • Complete artifacts - New artifacts added to the task (append=false or null)
  • + *
  • Incremental chunks - Content appended to existing artifacts (append=true)
  • + *
+ *

+ * Use cases include: + *

    + *
  • Streaming text generation (progressive LLM responses)
  • + *
  • Incremental file generation (large documents built over time)
  • + *
  • Partial results (early outputs before complete analysis)
  • + *
+ *

+ * The {@code lastChunk} flag indicates whether this is the final update for an artifact, + * allowing clients to distinguish between intermediate and final states. + * + * @see UpdateEvent + * @see StreamingEventKind + * @see Artifact + * @see Task */ @JsonTypeName(ARTIFACT_UPDATE) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -85,6 +109,35 @@ public String getKind() { return kind; } + /** + * Builder for constructing {@link TaskArtifactUpdateEvent} instances. + *

+ * Example for complete artifact: + *

{@code
+     * TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder()
+     *     .taskId("task-123")
+     *     .contextId("ctx-456")
+     *     .artifact(new Artifact.Builder()
+     *         .artifactId("artifact-789")
+     *         .parts(List.of(new TextPart("Analysis complete")))
+     *         .build())
+     *     .build();
+     * }
+ *

+ * Example for incremental chunk: + *

{@code
+     * TaskArtifactUpdateEvent chunk = new TaskArtifactUpdateEvent.Builder()
+     *     .taskId("task-123")
+     *     .contextId("ctx-456")
+     *     .artifact(new Artifact.Builder()
+     *         .artifactId("artifact-789")
+     *         .parts(List.of(new TextPart("more text...")))
+     *         .build())
+     *     .append(true)
+     *     .lastChunk(false)
+     *     .build();
+     * }
+ */ public static class Builder { private String taskId; @@ -131,7 +184,6 @@ public Builder lastChunk(Boolean lastChunk) { return this; } - public Builder metadata(Map metadata) { this.metadata = metadata; return this; diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index 237fe727a..fd7ef9980 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -7,7 +7,17 @@ import io.a2a.util.Assert; /** - * Defines parameters containing a task ID, used for simple task operations. + * Parameters containing a task identifier for task-related operations. + *

+ * This simple parameter record is used by operations that only need a task ID, + * such as {@link CancelTaskRequest}, {@link TaskResubscriptionRequest}, and similar + * task-specific requests. It optionally includes metadata for additional context. + * + * @param id the unique task identifier (required) + * @param metadata optional arbitrary key-value metadata for the request + * @see CancelTaskRequest for task cancellation + * @see TaskResubscriptionRequest for task resubscription + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index 50d32c385..c567959b1 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -8,7 +8,31 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An A2A-specific error indicating that the task is in a state where it cannot be canceled. + * A2A Protocol error indicating that a task cannot be canceled in its current state. + *

+ * This error is returned when a client attempts to cancel a task via {@link CancelTaskRequest} + * but the task is in a terminal state ({@link TaskState#COMPLETED}, {@link TaskState#FAILED}, + * {@link TaskState#CANCELED}) where cancellation is not applicable. + *

+ * Tasks can only be canceled when they are in non-terminal states such as {@link TaskState#SUBMITTED} + * or {@link TaskState#WORKING}. + *

+ * Corresponds to A2A-specific error code {@code -32002}. + *

+ * Usage example: + *

{@code
+ * Task task = taskStore.getTask(taskId);
+ * if (task.status().state().isFinal()) {
+ *     throw new TaskNotCancelableError(
+ *         "Task in " + task.status().state() + " state cannot be canceled"
+ *     );
+ * }
+ * }
+ * + * @see CancelTaskRequest for task cancellation + * @see TaskState for task state definitions + * @see TaskStatus#state() for current task state + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index c21998d2d..81c95ed05 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -8,7 +8,34 @@ import static io.a2a.util.Utils.defaultIfNull; /** - * An A2A-specific error indicating that the requested task ID was not found. + * A2A Protocol error indicating that the requested task ID does not exist. + *

+ * This error is returned when a client attempts to perform operations on a task (such as + * {@link GetTaskRequest}, {@link CancelTaskRequest}, or push notification operations) using + * a task ID that is not found in the server's task store. + *

+ * Common causes: + *

    + *
  • Task ID was never created
  • + *
  • Task has been removed from the task store (expired or deleted)
  • + *
  • Task ID typo or incorrect value
  • + *
  • Task belongs to a different agent or server instance
  • + *
+ *

+ * Corresponds to A2A-specific error code {@code -32001}. + *

+ * Usage example: + *

{@code
+ * Task task = taskStore.getTask(taskId);
+ * if (task == null) {
+ *     throw new TaskNotFoundError();
+ * }
+ * }
+ * + * @see Task for task object definition + * @see GetTaskRequest for task retrieval + * @see CancelTaskRequest for task cancellation + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index 4cda7a8f3..89b42d210 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -5,7 +5,21 @@ import io.a2a.util.Assert; /** - * A container associating a push notification configuration with a specific task. + * Associates a push notification configuration with a specific task. + *

+ * This record binds a {@link PushNotificationConfig} to a particular task ID, enabling + * the agent to send asynchronous updates about task progress to the configured endpoint. + * When task events occur (status changes, artifact updates), the agent will POST updates + * to the specified notification URL. + *

+ * Used for managing task-specific push notification settings via the push notification + * management methods ({@code tasks/pushNotificationConfig/set}, {@code tasks/pushNotificationConfig/get}, etc.). + * + * @param taskId the unique identifier of the task to receive push notifications for (required) + * @param pushNotificationConfig the push notification endpoint and authentication configuration (required) + * @see PushNotificationConfig for notification endpoint details + * @see SetTaskPushNotificationConfigRequest for setting push notifications + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java b/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java index de0c88c62..b9e5589da 100644 --- a/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java +++ b/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java @@ -11,7 +11,24 @@ import java.util.UUID; /** - * Used to resubscribe to a task. + * JSON-RPC request to resubscribe to an ongoing or completed task's event stream. + *

+ * This request allows clients to reconnect to a task and receive its events, enabling + * recovery from disconnections or retrieval of missed updates. The agent will stream + * events for the task starting from its current state. + *

+ * Resubscription is particularly useful for: + *

    + *
  • Recovering from network interruptions without losing task context
  • + *
  • Multiple clients observing the same task
  • + *
  • Retrieving final results for completed tasks
  • + *
+ *

+ * This class implements the JSON-RPC {@code tasks/resubscribe} method as specified in the A2A Protocol. + * + * @see TaskIdParams for the parameter structure + * @see StreamingEventKind for the types of events that can be streamed + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/TaskState.java b/spec/src/main/java/io/a2a/spec/TaskState.java index 655d20919..6d7647b4f 100644 --- a/spec/src/main/java/io/a2a/spec/TaskState.java +++ b/spec/src/main/java/io/a2a/spec/TaskState.java @@ -4,17 +4,62 @@ import com.fasterxml.jackson.annotation.JsonValue; /** - * Defines the lifecycle states of a Task. + * Defines the lifecycle states of a {@link Task} in the A2A Protocol. + *

+ * TaskState represents the discrete states a task can be in during its execution lifecycle. + * States are categorized as either transitional (non-final) or terminal (final), where + * terminal states indicate that the task has reached its end state and will not transition further. + *

+ * Transitional States: + *

    + *
  • SUBMITTED: Task has been received by the agent and is queued for processing
  • + *
  • WORKING: Agent is actively processing the task and may produce incremental results
  • + *
  • INPUT_REQUIRED: Agent needs additional input from the user to continue
  • + *
  • AUTH_REQUIRED: Agent requires authentication or authorization before proceeding
  • + *
+ *

+ * Terminal States: + *

    + *
  • COMPLETED: Task finished successfully with all requested work done
  • + *
  • CANCELED: Task was explicitly canceled by the user or system
  • + *
  • FAILED: Task failed due to an error during execution
  • + *
  • REJECTED: Task was rejected by the agent (e.g., invalid request, policy violation)
  • + *
  • UNKNOWN: Task state cannot be determined (error recovery state)
  • + *
+ *

+ * The {@link #isFinal()} method can be used to determine if a state is terminal, which is + * important for event queue management and client polling logic. + * + * @see TaskStatus + * @see Task + * @see A2A Protocol Specification */ public enum TaskState { + /** Task has been received and is queued for processing (transitional state). */ SUBMITTED("submitted"), + + /** Agent is actively processing the task (transitional state). */ WORKING("working"), + + /** Agent requires additional input from the user to continue (transitional state). */ INPUT_REQUIRED("input-required"), + + /** Agent requires authentication or authorization to proceed (transitional state). */ AUTH_REQUIRED("auth-required"), + + /** Task completed successfully (terminal state). */ COMPLETED("completed", true), + + /** Task was canceled by user or system (terminal state). */ CANCELED("canceled", true), + + /** Task failed due to an error (terminal state). */ FAILED("failed", true), + + /** Task was rejected by the agent (terminal state). */ REJECTED("rejected", true), + + /** Task state is unknown or cannot be determined (terminal state). */ UNKNOWN("unknown", true); private final String state; @@ -29,15 +74,43 @@ public enum TaskState { this.isFinal = isFinal; } + /** + * Returns the string representation of this task state for JSON serialization. + *

+ * This method is used by Jackson to serialize TaskState values to their + * wire format (e.g., "working", "completed"). + * + * @return the string representation of this state + */ @JsonValue public String asString() { return state; } + /** + * Determines whether this state is a terminal (final) state. + *

+ * Terminal states indicate that the task has completed its lifecycle and will + * not transition to any other state. This is used by the event queue system + * to determine when to close queues and by clients to know when to stop polling. + * + * @return true if this is a terminal state (COMPLETED, CANCELED, FAILED, REJECTED, UNKNOWN), + * false for transitional states (SUBMITTED, WORKING, INPUT_REQUIRED, AUTH_REQUIRED) + */ public boolean isFinal(){ return isFinal; } + /** + * Deserializes a string value into a TaskState enum constant. + *

+ * This method is used by Jackson to deserialize TaskState values from their + * wire format during JSON parsing. + * + * @param state the string representation of the state + * @return the corresponding TaskState enum constant + * @throws IllegalArgumentException if the state string is not recognized + */ @JsonCreator public static TaskState fromString(String state) { return switch (state) { diff --git a/spec/src/main/java/io/a2a/spec/TaskStatus.java b/spec/src/main/java/io/a2a/spec/TaskStatus.java index 0b9bed654..a31c9e067 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatus.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatus.java @@ -10,7 +10,36 @@ import io.a2a.util.Assert; /** - * Represents the status of a task at a specific point in time. + * Represents the status of a task at a specific point in time in the A2A Protocol. + *

+ * TaskStatus encapsulates the current state of a task along with an optional message + * providing additional context and a timestamp indicating when the status was created. + * This information is essential for tracking task lifecycle and communicating progress + * to clients. + *

+ * The status transitions through various states as the agent processes the task: + *

    + *
  • SUBMITTED: Task has been received and queued
  • + *
  • WORKING: Agent is actively processing the task
  • + *
  • INPUT_REQUIRED: Agent needs additional input from the user
  • + *
  • AUTH_REQUIRED: Agent requires authentication or authorization
  • + *
  • COMPLETED: Task finished successfully (final state)
  • + *
  • CANCELED: Task was canceled by the user or system (final state)
  • + *
  • FAILED: Task failed due to an error (final state)
  • + *
  • REJECTED: Task was rejected by the agent (final state)
  • + *
  • UNKNOWN: Task state cannot be determined (final state)
  • + *
+ *

+ * TaskStatus is immutable and automatically generates a UTC timestamp if none is provided. + * This class is used within {@link Task} objects and {@link TaskStatusUpdateEvent} instances + * to communicate state changes. + * + * @param state the current state of the task (required) + * @param message an optional message providing additional context about the status (optional) + * @param timestamp the time when this status was created, in UTC (defaults to current time if not provided) + * @see TaskState + * @see Task + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @@ -22,14 +51,26 @@ public record TaskStatus(TaskState state, Message message, timestamp = timestamp == null ? OffsetDateTime.now(ZoneOffset.UTC) : timestamp; } + /** + * Creates a TaskStatus with only a state, using the current UTC time as the timestamp. + *

+ * This is a convenience constructor for creating status updates without + * an accompanying message. + * + * @param state the task state (required) + */ public TaskStatus(TaskState state) { this(state, null, null); } /** - * Constructor for testing purposes. - * @param state the task state - * @param timestamp timestamp generation + * Creates a TaskStatus with a specific timestamp, primarily for testing purposes. + *

+ * This package-private constructor allows tests to create TaskStatus instances + * with deterministic timestamps for assertions. + * + * @param state the task state (required) + * @param timestamp the timestamp to use (if null, current UTC time is used) */ TaskStatus(TaskState state, OffsetDateTime timestamp) { this(state, null, timestamp); diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index ade2047ee..ddf461d69 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -12,7 +12,23 @@ import static io.a2a.spec.TextPart.TEXT; /** - * Represents a text segment within a message or artifact. + * Represents a plain text content part within a {@link Message} or {@link Artifact}. + *

+ * TextPart is the most common part type, containing textual content such as user messages, + * agent responses, descriptions, or any other human-readable text. + *

+ * The text content is required and must be non-null. Optional metadata can provide additional + * context about the text (such as language, encoding, or formatting hints). + *

+ * Example usage: + *

{@code
+ * TextPart greeting = new TextPart("Hello, how can I help you?");
+ * TextPart withMetadata = new TextPart("Bonjour!", Map.of("language", "fr"));
+ * }
+ * + * @see Part + * @see Message + * @see Artifact */ @JsonTypeName(TEXT) @JsonInclude(JsonInclude.Include.NON_ABSENT) diff --git a/spec/src/main/java/io/a2a/spec/TransportProtocol.java b/spec/src/main/java/io/a2a/spec/TransportProtocol.java index d6c2922d2..24fecefcf 100644 --- a/spec/src/main/java/io/a2a/spec/TransportProtocol.java +++ b/spec/src/main/java/io/a2a/spec/TransportProtocol.java @@ -4,11 +4,56 @@ import com.fasterxml.jackson.annotation.JsonValue; /** - * Supported A2A transport protocols. + * Enumeration of supported transport protocols for A2A Protocol communication. + *

+ * This enum defines the communication mechanisms that A2A agents and clients can use + * to exchange messages. Each transport protocol has different characteristics, advantages, + * and use cases. + *

+ * Supported transport protocols: + *

    + *
  • {@link #JSONRPC} - JSON-RPC 2.0 over HTTP (most common, human-readable)
  • + *
  • {@link #GRPC} - gRPC binary protocol (high performance, streaming)
  • + *
  • {@link #HTTP_JSON} - RESTful HTTP with JSON (REST-style endpoints)
  • + *
+ *

+ * Usage example: + *

{@code
+ * TransportProtocol protocol = TransportProtocol.JSONRPC;
+ * String protocolName = protocol.asString();  // "JSONRPC"
+ * TransportProtocol parsed = TransportProtocol.fromString("JSONRPC");
+ * }
+ * + * @see JSON-RPC 2.0 Specification + * @see gRPC + * @see A2A Protocol Specification */ public enum TransportProtocol { + /** + * JSON-RPC 2.0 transport protocol over HTTP. + *

+ * This is the primary and most widely supported transport for the A2A Protocol. + * It uses JSON-RPC 2.0 request/response semantics with JSON encoding, making it + * human-readable and easy to debug. Supports both synchronous and streaming communication. + */ JSONRPC("JSONRPC"), + + /** + * gRPC binary transport protocol. + *

+ * High-performance binary protocol using Protocol Buffers for serialization. + * Provides efficient streaming, strong typing, and cross-language support. + * Suitable for high-throughput scenarios and service-to-service communication. + */ GRPC("GRPC"), + + /** + * HTTP+JSON RESTful transport protocol. + *

+ * REST-style HTTP endpoints with JSON payloads. Provides a more traditional + * web API approach compared to JSON-RPC, using HTTP methods (GET, POST, etc.) + * and resource-oriented URLs. + */ HTTP_JSON("HTTP+JSON"); private final String transport; @@ -17,11 +62,27 @@ public enum TransportProtocol { this.transport = transport; } + /** + * Returns the string representation of this transport protocol. + *

+ * Used for JSON serialization via Jackson's {@code @JsonValue} annotation. + * + * @return the transport protocol name as a string + */ @JsonValue public String asString() { return transport; } + /** + * Parses a string into a {@link TransportProtocol} enum constant. + *

+ * Used for JSON deserialization via Jackson's {@code @JsonCreator} annotation. + * + * @param transport the transport protocol string (e.g., "JSONRPC", "GRPC", "HTTP+JSON") + * @return the corresponding TransportProtocol enum constant + * @throws IllegalArgumentException if the transport string is not recognized + */ @JsonCreator public static TransportProtocol fromString(String transport) { return switch (transport) { diff --git a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java index 9fe055e9c..9e4fded80 100644 --- a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -8,7 +8,32 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * An A2A-specific error indicating that the requested operation is not supported by the agent. + * A2A Protocol error indicating that the requested operation is not supported by the agent. + *

+ * This error is returned when a client attempts an operation that is valid according to the + * A2A Protocol but not implemented or enabled by the specific agent. This is distinct from + * {@link MethodNotFoundError}, which indicates an unknown JSON-RPC method. + *

+ * Common scenarios: + *

    + *
  • Calling streaming methods when {@link AgentCapabilities#streaming()} is false
  • + *
  • Attempting push notification operations when {@link AgentCapabilities#pushNotifications()} is false
  • + *
  • Using optional protocol features not implemented by the agent
  • + *
  • Agent-specific operations disabled by configuration
  • + *
+ *

+ * Corresponds to A2A-specific error code {@code -32004}. + *

+ * Usage example: + *

{@code
+ * if (!agentCard.capabilities().streaming()) {
+ *     throw new UnsupportedOperationError();
+ * }
+ * }
+ * + * @see AgentCapabilities for agent capability declarations + * @see MethodNotFoundError for unknown method errors + * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/spec/src/main/java/io/a2a/spec/UpdateEvent.java b/spec/src/main/java/io/a2a/spec/UpdateEvent.java index 81060c8eb..996977498 100644 --- a/spec/src/main/java/io/a2a/spec/UpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/UpdateEvent.java @@ -1,4 +1,24 @@ package io.a2a.spec; +/** + * Sealed interface for incremental update events during task execution. + *

+ * UpdateEvent represents partial updates to task state or content that can be emitted during + * streaming operations. These events enable real-time progress tracking and incremental result + * delivery without waiting for complete task finalization. + *

+ * Permitted implementations: + *

    + *
  • {@link TaskStatusUpdateEvent} - Updates to task status and state transitions
  • + *
  • {@link TaskArtifactUpdateEvent} - Updates to task artifacts (creation, modification, appending)
  • + *
+ *

+ * Update events are always part of {@link StreamingEventKind} and provide granular visibility + * into agent progress during long-running operations. + * + * @see StreamingEventKind + * @see TaskStatusUpdateEvent + * @see TaskArtifactUpdateEvent + */ public sealed interface UpdateEvent permits TaskStatusUpdateEvent, TaskArtifactUpdateEvent { } diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 07143ab6a..59db4fb67 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -14,19 +14,72 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; +/** + * Utility class providing common helper methods for A2A Protocol operations. + *

+ * This class contains static utility methods for JSON serialization/deserialization, + * null-safe operations, artifact management, and other common tasks used throughout + * the A2A Java SDK. + *

+ * Key capabilities: + *

    + *
  • JSON processing with pre-configured {@link ObjectMapper}
  • + *
  • Null-safe value defaults via {@link #defaultIfNull(Object, Object)}
  • + *
  • Artifact streaming support via {@link #appendArtifactToTask(Task, TaskArtifactUpdateEvent, String)}
  • + *
  • Type-safe exception rethrowing via {@link #rethrow(Throwable)}
  • + *
+ * + * @see ObjectMapper for JSON processing + * @see TaskArtifactUpdateEvent for streaming artifact updates + */ public class Utils { + /** + * Pre-configured Jackson {@link ObjectMapper} for JSON operations. + *

+ * This mapper is configured with: + *

    + *
  • {@link JavaTimeModule} for Java 8 date/time type support
  • + *
+ *

+ * Used throughout the SDK for consistent JSON serialization and deserialization. + */ public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final Logger log = Logger.getLogger(Utils.class.getName()); + static { // needed for date/time types OBJECT_MAPPER.registerModule(new JavaTimeModule()); } + /** + * Deserializes JSON string into a typed object using Jackson. + *

+ * This method uses the pre-configured {@link #OBJECT_MAPPER} to parse JSON + * with support for generic types via {@link TypeReference}. + * + * @param the target type + * @param data JSON string to deserialize + * @param typeRef type reference specifying the target type (for generic types) + * @return deserialized object of type T + * @throws JsonProcessingException if JSON parsing fails + */ public static T unmarshalFrom(String data, TypeReference typeRef) throws JsonProcessingException { return OBJECT_MAPPER.readValue(data, typeRef); } + /** + * Returns the provided value if non-null, otherwise returns the default value. + *

+ * This is a null-safe utility for providing default values when a parameter + * might be null. + * + * @param the value type + * @param value the value to check + * @param defaultValue the default value to return if value is null + * @return value if non-null, otherwise defaultValue + */ public static T defaultIfNull(T value, T defaultValue) { if (value == null) { return defaultValue; @@ -34,10 +87,44 @@ public static T defaultIfNull(T value, T defaultValue) { return value; } + /** + * Rethrows a checked exception as an unchecked exception. + *

+ * This method uses type erasure to bypass checked exception handling, + * allowing checked exceptions to be thrown without explicit declaration. + * Use with caution as it bypasses Java's compile-time exception checking. + * + * @param the throwable type + * @param t the throwable to rethrow + * @throws T the rethrown exception + */ public static void rethrow(Throwable t) throws T { throw (T) t; } + /** + * Appends or updates an artifact in a task based on a {@link TaskArtifactUpdateEvent}. + *

+ * This method handles streaming artifact updates, supporting both: + *

    + *
  • Adding new artifacts to the task
  • + *
  • Replacing existing artifacts (when {@code append=false})
  • + *
  • Appending parts to existing artifacts (when {@code append=true})
  • + *
+ *

+ * The {@code append} flag in the event determines the behavior: + *

    + *
  • {@code false} or {@code null}: Replace/add the entire artifact
  • + *
  • {@code true}: Append the new artifact's parts to an existing artifact with matching {@code artifactId}
  • + *
+ * + * @param task the current task to update + * @param event the artifact update event containing the new/updated artifact + * @param taskId the task ID (for logging purposes) + * @return a new Task instance with the updated artifacts list + * @see TaskArtifactUpdateEvent for streaming artifact updates + * @see Artifact for artifact structure + */ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event, String taskId) { // Append artifacts List artifacts = task.getArtifacts() == null ? new ArrayList<>() : new ArrayList<>(task.getArtifacts()); @@ -94,6 +181,16 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event } + /** + * Serializes an object to a JSON string using Jackson. + *

+ * This method uses the pre-configured {@link #OBJECT_MAPPER} to produce + * JSON representation of the provided object. + * + * @param o the object to serialize + * @return JSON string representation of the object + * @throws RuntimeException if JSON serialization fails (wraps {@link JsonProcessingException}) + */ public static String toJsonString(Object o) { try { return OBJECT_MAPPER.writeValueAsString(o); From 8ecb546464e5d3ba8d6b884a4483a4b29af72840 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 21 Nov 2025 16:11:22 +0000 Subject: [PATCH 211/493] fix: Rename push_notification to push_notification_config (#482) Fixes: #383 Needs https://github.com/a2aproject/a2a-tck/pull/94 --- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 418 +++++++++--------- .../io/a2a/grpc/SendMessageConfiguration.java | 168 +++---- .../SendMessageConfigurationOrBuilder.java | 16 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 4 +- spec-grpc/src/main/proto/a2a.proto | 2 +- 5 files changed, 304 insertions(+), 304 deletions(-) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index f6ec5eb57..e509c936d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -305,214 +305,214 @@ public static void registerAllExtensions( "le/api/field_behavior.proto\032\033google/prot" + "obuf/empty.proto\032\034google/protobuf/struct" + ".proto\032\037google/protobuf/timestamp.proto\"" + - "\236\001\n\030SendMessageConfiguration\022\035\n\025accepted" + - "_output_modes\030\001 \003(\t\0229\n\021push_notification" + - "\030\002 \001(\0132\036.a2a.v1.PushNotificationConfig\022\026" + - "\n\016history_length\030\003 \001(\005\022\020\n\010blocking\030\004 \001(\010" + - "\"\274\001\n\004Task\022\n\n\002id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(" + - "\t\022\"\n\006status\030\003 \001(\0132\022.a2a.v1.TaskStatus\022#\n" + - "\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifact\022 \n\007hi" + - "story\030\005 \003(\0132\017.a2a.v1.Message\022)\n\010metadata" + - "\030\006 \001(\0132\027.google.protobuf.Struct\"\207\001\n\nTask" + - "Status\022 \n\005state\030\001 \001(\0162\021.a2a.v1.TaskState" + - "\022(\n\006update\030\002 \001(\0132\017.a2a.v1.MessageR\007messa" + - "ge\022-\n\ttimestamp\030\003 \001(\0132\032.google.protobuf." + - "Timestamp\"\215\001\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022 \n\004fi" + - "le\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004data\030\003 \001" + - "(\0132\020.a2a.v1.DataPartH\000\022)\n\010metadata\030\004 \001(\013" + - "2\027.google.protobuf.StructB\006\n\004part\"g\n\010Fil" + - "ePart\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n\017file_w" + - "ith_bytes\030\002 \001(\014H\000\022\021\n\tmime_type\030\003 \001(\t\022\014\n\004" + - "name\030\004 \001(\tB\006\n\004file\"1\n\010DataPart\022%\n\004data\030\001" + - " \001(\0132\027.google.protobuf.Struct\"\326\001\n\007Messag" + - "e\022\022\n\nmessage_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(" + - "\t\022\017\n\007task_id\030\003 \001(\t\022\032\n\004role\030\004 \001(\0162\014.a2a.v" + - "1.Role\022\033\n\005parts\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010m" + - "etadata\030\006 \001(\0132\027.google.protobuf.Struct\022\022" + - "\n\nextensions\030\007 \003(\t\022\032\n\022reference_task_ids" + - "\030\010 \003(\t\"\236\001\n\010Artifact\022\023\n\013artifact_id\030\001 \001(\t" + - "\022\014\n\004name\030\003 \001(\t\022\023\n\013description\030\004 \001(\t\022\033\n\005p" + - "arts\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010metadata\030\006 \001" + - "(\0132\027.google.protobuf.Struct\022\022\n\nextension" + - "s\030\007 \003(\t\"\232\001\n\025TaskStatusUpdateEvent\022\017\n\007tas" + - "k_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\006status" + - "\030\003 \001(\0132\022.a2a.v1.TaskStatus\022\r\n\005final\030\004 \001(" + - "\010\022)\n\010metadata\030\005 \001(\0132\027.google.protobuf.St" + - "ruct\"\261\001\n\027TaskArtifactUpdateEvent\022\017\n\007task" + - "_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\010artifac" + - "t\030\003 \001(\0132\020.a2a.v1.Artifact\022\016\n\006append\030\004 \001(" + - "\010\022\022\n\nlast_chunk\030\005 \001(\010\022)\n\010metadata\030\006 \001(\0132" + - "\027.google.protobuf.Struct\"t\n\026PushNotifica" + - "tionConfig\022\n\n\002id\030\001 \001(\t\022\013\n\003url\030\002 \001(\t\022\r\n\005t" + - "oken\030\003 \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a" + - ".v1.AuthenticationInfo\":\n\022Authentication" + - "Info\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credentials\030\002 \001" + - "(\t\"0\n\016AgentInterface\022\013\n\003url\030\001 \001(\t\022\021\n\ttra" + - "nsport\030\002 \001(\t\"\264\005\n\tAgentCard\022\030\n\020protocol_v" + - "ersion\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013descriptio" + - "n\030\002 \001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred_transp" + - "ort\030\016 \001(\t\0225\n\025additional_interfaces\030\017 \003(\013" + - "2\026.a2a.v1.AgentInterface\022\'\n\010provider\030\004 \001" + - "(\0132\025.a2a.v1.AgentProvider\022\017\n\007version\030\005 \001" + - "(\t\022\031\n\021documentation_url\030\006 \001(\t\022/\n\014capabil" + - "ities\030\007 \001(\0132\031.a2a.v1.AgentCapabilities\022@" + - "\n\020security_schemes\030\010 \003(\0132&.a2a.v1.AgentC" + - "ard.SecuritySchemesEntry\022\"\n\010security\030\t \003" + - "(\0132\020.a2a.v1.Security\022\033\n\023default_input_mo" + - "des\030\n \003(\t\022\034\n\024default_output_modes\030\013 \003(\t\022" + - "\"\n\006skills\030\014 \003(\0132\022.a2a.v1.AgentSkill\022,\n$s" + - "upports_authenticated_extended_card\030\r \001(" + - "\010\022.\n\nsignatures\030\021 \003(\0132\032.a2a.v1.AgentCard" + - "Signature\022\020\n\010icon_url\030\022 \001(\t\032N\n\024SecurityS" + - "chemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132" + - "\026.a2a.v1.SecurityScheme:\0028\001\"2\n\rAgentProv" + - "ider\022\013\n\003url\030\001 \001(\t\022\024\n\014organization\030\002 \001(\t\"" + - "\220\001\n\021AgentCapabilities\022\021\n\tstreaming\030\001 \001(\010" + - "\022\032\n\022push_notifications\030\002 \001(\010\022*\n\nextensio" + - "ns\030\003 \003(\0132\026.a2a.v1.AgentExtension\022 \n\030stat" + - "e_transition_history\030\004 \001(\010\"m\n\016AgentExten" + - "sion\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030\002 \001(\t\022\020" + - "\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132\027.googl" + - "e.protobuf.Struct\"\252\001\n\nAgentSkill\022\n\n\002id\030\001" + - " \001(\t\022\014\n\004name\030\002 \001(\t\022\023\n\013description\030\003 \001(\t\022" + - "\014\n\004tags\030\004 \003(\t\022\020\n\010examples\030\005 \003(\t\022\023\n\013input" + - "_modes\030\006 \003(\t\022\024\n\014output_modes\030\007 \003(\t\022\"\n\010se" + - "curity\030\010 \003(\0132\020.a2a.v1.Security\"m\n\022AgentC" + - "ardSignature\022\026\n\tprotected\030\001 \001(\tB\003\340A\002\022\026\n\t" + - "signature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\0132\027.g" + - "oogle.protobuf.Struct\"l\n\032TaskPushNotific" + - "ationConfig\022\014\n\004name\030\001 \001(\t\022@\n\030push_notifi" + - "cation_config\030\002 \001(\0132\036.a2a.v1.PushNotific" + - "ationConfig\"\032\n\nStringList\022\014\n\004list\030\001 \003(\t\"" + - "~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035.a2a.v1.Se" + - "curity.SchemesEntry\032B\n\014SchemesEntry\022\013\n\003k" + - "ey\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1.StringL" + - "ist:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027api_key_se" + - "curity_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySecur" + - "itySchemeH\000\022C\n\031http_auth_security_scheme" + - "\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecuritySchemeH\000" + - "\022>\n\026oauth2_security_scheme\030\003 \001(\0132\034.a2a.v" + - "1.OAuth2SecuritySchemeH\000\022N\n\037open_id_conn" + - "ect_security_scheme\030\004 \001(\0132#.a2a.v1.OpenI" + - "dConnectSecuritySchemeH\000\022?\n\024mtls_securit" + - "y_scheme\030\005 \001(\0132\037.a2a.v1.MutualTlsSecurit" + - "ySchemeH\000B\010\n\006scheme\"K\n\024APIKeySecuritySch" + - "eme\022\023\n\013description\030\001 \001(\t\022\020\n\010location\030\002 \001" + - "(\t\022\014\n\004name\030\003 \001(\t\"T\n\026HTTPAuthSecuritySche" + - "me\022\023\n\013description\030\001 \001(\t\022\016\n\006scheme\030\002 \001(\t\022" + - "\025\n\rbearer_format\030\003 \001(\t\"k\n\024OAuth2Security" + - "Scheme\022\023\n\013description\030\001 \001(\t\022!\n\005flows\030\002 \001" + - "(\0132\022.a2a.v1.OAuthFlows\022\033\n\023oauth2_metadat" + - "a_url\030\003 \001(\t\"O\n\033OpenIdConnectSecuritySche" + - "me\022\023\n\013description\030\001 \001(\t\022\033\n\023open_id_conne" + - "ct_url\030\002 \001(\t\".\n\027MutualTlsSecurityScheme\022" + - "\023\n\013description\030\001 \001(\t\"\366\001\n\nOAuthFlows\022@\n\022a" + - "uthorization_code\030\001 \001(\0132\".a2a.v1.Authori" + - "zationCodeOAuthFlowH\000\022@\n\022client_credenti" + - "als\030\002 \001(\0132\".a2a.v1.ClientCredentialsOAut" + - "hFlowH\000\022-\n\010implicit\030\003 \001(\0132\031.a2a.v1.Impli" + - "citOAuthFlowH\000\022-\n\010password\030\004 \001(\0132\031.a2a.v" + - "1.PasswordOAuthFlowH\000B\006\n\004flow\"\316\001\n\032Author" + - "izationCodeOAuthFlow\022\031\n\021authorization_ur" + - "l\030\001 \001(\t\022\021\n\ttoken_url\030\002 \001(\t\022\023\n\013refresh_ur" + - "l\030\003 \001(\t\022>\n\006scopes\030\004 \003(\0132..a2a.v1.Authori" + - "zationCodeOAuthFlow.ScopesEntry\032-\n\013Scope" + - "sEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\263" + - "\001\n\032ClientCredentialsOAuthFlow\022\021\n\ttoken_u" + - "rl\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\022>\n\006scopes\030" + - "\003 \003(\0132..a2a.v1.ClientCredentialsOAuthFlo" + - "w.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(" + - "\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021ImplicitOAuthFl" + - "ow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013refresh" + - "_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a.v1.Impl" + - "icitOAuthFlow.ScopesEntry\032-\n\013ScopesEntry" + - "\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\241\001\n\021Pas" + - "swordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t\022\023\n\013ref" + - "resh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a.v1." + - "PasswordOAuthFlow.ScopesEntry\032-\n\013ScopesE" + - "ntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\250\001\n" + - "\022SendMessageRequest\022.\n\007request\030\001 \001(\0132\017.a" + - "2a.v1.MessageB\003\340A\002R\007message\0227\n\rconfigura" + - "tion\030\002 \001(\0132 .a2a.v1.SendMessageConfigura" + - "tion\022)\n\010metadata\030\003 \001(\0132\027.google.protobuf" + - ".Struct\";\n\016GetTaskRequest\022\021\n\004name\030\001 \001(\tB" + - "\003\340A\002\022\026\n\016history_length\030\002 \001(\005\"\332\001\n\020ListTas" + - "ksRequest\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006status\030" + - "\002 \001(\0162\021.a2a.v1.TaskState\022\021\n\tpage_size\030\003 " + - "\001(\005\022\022\n\npage_token\030\004 \001(\t\022\026\n\016history_lengt" + - "h\030\005 \001(\005\0225\n\021last_updated_time\030\006 \001(\0132\032.goo" + - "gle.protobuf.Timestamp\022\031\n\021include_artifa" + - "cts\030\007 \001(\010\"]\n\021ListTasksResponse\022\033\n\005tasks\030" + - "\001 \003(\0132\014.a2a.v1.Task\022\027\n\017next_page_token\030\002" + - " \001(\t\022\022\n\ntotal_size\030\003 \001(\005\"!\n\021CancelTaskRe" + - "quest\022\014\n\004name\030\001 \001(\t\"4\n$GetTaskPushNotifi" + - "cationConfigRequest\022\014\n\004name\030\001 \001(\t\"7\n\'Del" + - "eteTaskPushNotificationConfigRequest\022\014\n\004" + - "name\030\001 \001(\t\"\217\001\n\'CreateTaskPushNotificatio" + - "nConfigRequest\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\tc" + - "onfig_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a2" + - "a.v1.TaskPushNotificationConfigB\003\340A\002\"\'\n\027" + - "TaskSubscriptionRequest\022\014\n\004name\030\001 \001(\t\"^\n" + - "%ListTaskPushNotificationConfigRequest\022\016" + - "\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npag" + - "e_token\030\003 \001(\t\"\025\n\023GetAgentCardRequest\"g\n\023" + - "SendMessageResponse\022\034\n\004task\030\001 \001(\0132\014.a2a." + - "v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.MessageH" + - "\000R\007messageB\t\n\007payload\"\326\001\n\016StreamResponse" + - "\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 " + - "\001(\0132\017.a2a.v1.MessageH\000R\007message\0226\n\rstatu" + - "s_update\030\003 \001(\0132\035.a2a.v1.TaskStatusUpdate" + - "EventH\000\022:\n\017artifact_update\030\004 \001(\0132\037.a2a.v" + - "1.TaskArtifactUpdateEventH\000B\t\n\007payload\"v" + - "\n&ListTaskPushNotificationConfigResponse" + - "\0223\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPushNotif" + - "icationConfig\022\027\n\017next_page_token\030\002 \001(\t*\372" + - "\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020\000" + - "\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STATE" + - "_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n\021" + - "TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCEL" + - "LED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n\023" + - "TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUTH" + - "_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020" + - "\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\220\013\n\nA2A" + - "Service\022c\n\013SendMessage\022\032.a2a.v1.SendMess" + - "ageRequest\032\033.a2a.v1.SendMessageResponse\"" + - "\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024SendStre" + - "amingMessage\022\032.a2a.v1.SendMessageRequest" + - "\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"\022/v1/me" + - "ssage:stream:\001*0\001\022R\n\007GetTask\022\026.a2a.v1.Ge" + - "tTaskRequest\032\014.a2a.v1.Task\"!\332A\004name\202\323\344\223\002" + - "\024\022\022/v1/{name=tasks/*}\022S\n\tListTasks\022\030.a2a" + - ".v1.ListTasksRequest\032\031.a2a.v1.ListTasksR" + - "esponse\"\021\202\323\344\223\002\013\022\t/v1/tasks\022[\n\nCancelTask" + - "\022\031.a2a.v1.CancelTaskRequest\032\014.a2a.v1.Tas" + - "k\"$\202\323\344\223\002\036\"\031/v1/{name=tasks/*}:cancel:\001*\022" + - "s\n\020TaskSubscription\022\037.a2a.v1.TaskSubscri" + - "ptionRequest\032\026.a2a.v1.StreamResponse\"$\202\323" + - "\344\223\002\036\022\034/v1/{name=tasks/*}:subscribe0\001\022\305\001\n" + - " CreateTaskPushNotificationConfig\022/.a2a." + - "v1.CreateTaskPushNotificationConfigReque" + - "st\032\".a2a.v1.TaskPushNotificationConfig\"L" + - "\332A\rparent,config\202\323\344\223\0026\",/v1/{parent=task" + - "s/*/pushNotificationConfigs}:\006config\022\256\001\n" + - "\035GetTaskPushNotificationConfig\022,.a2a.v1." + - "GetTaskPushNotificationConfigRequest\032\".a" + - "2a.v1.TaskPushNotificationConfig\";\332A\004nam" + - "e\202\323\344\223\002.\022,/v1/{name=tasks/*/pushNotificat" + - "ionConfigs/*}\022\276\001\n\036ListTaskPushNotificati" + - "onConfig\022-.a2a.v1.ListTaskPushNotificati" + - "onConfigRequest\032..a2a.v1.ListTaskPushNot" + - "ificationConfigResponse\"=\332A\006parent\202\323\344\223\002." + - "\022,/v1/{parent=tasks/*}/pushNotificationC" + - "onfigs\022P\n\014GetAgentCard\022\033.a2a.v1.GetAgent" + - "CardRequest\032\021.a2a.v1.AgentCard\"\020\202\323\344\223\002\n\022\010" + - "/v1/card\022\250\001\n DeleteTaskPushNotificationC" + - "onfig\022/.a2a.v1.DeleteTaskPushNotificatio" + - "nConfigRequest\032\026.google.protobuf.Empty\";" + - "\332A\004name\202\323\344\223\002.*,/v1/{name=tasks/*/pushNot" + - "ificationConfigs/*}B7\n\013io.a2a.grpcB\003A2AP" + - "\001Z\030google.golang.org/a2a/v1\252\002\006A2a.V1b\006pr" + - "oto3" + "\245\001\n\030SendMessageConfiguration\022\035\n\025accepted" + + "_output_modes\030\001 \003(\t\022@\n\030push_notification" + + "_config\030\002 \001(\0132\036.a2a.v1.PushNotificationC" + + "onfig\022\026\n\016history_length\030\003 \001(\005\022\020\n\010blockin" + + "g\030\004 \001(\010\"\274\001\n\004Task\022\n\n\002id\030\001 \001(\t\022\022\n\ncontext_" + + "id\030\002 \001(\t\022\"\n\006status\030\003 \001(\0132\022.a2a.v1.TaskSt" + + "atus\022#\n\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifac" + + "t\022 \n\007history\030\005 \003(\0132\017.a2a.v1.Message\022)\n\010m" + + "etadata\030\006 \001(\0132\027.google.protobuf.Struct\"\207" + + "\001\n\nTaskStatus\022 \n\005state\030\001 \001(\0162\021.a2a.v1.Ta" + + "skState\022(\n\006update\030\002 \001(\0132\017.a2a.v1.Message" + + "R\007message\022-\n\ttimestamp\030\003 \001(\0132\032.google.pr" + + "otobuf.Timestamp\"\215\001\n\004Part\022\016\n\004text\030\001 \001(\tH" + + "\000\022 \n\004file\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004d" + + "ata\030\003 \001(\0132\020.a2a.v1.DataPartH\000\022)\n\010metadat" + + "a\030\004 \001(\0132\027.google.protobuf.StructB\006\n\004part" + + "\"g\n\010FilePart\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n" + + "\017file_with_bytes\030\002 \001(\014H\000\022\021\n\tmime_type\030\003 " + + "\001(\t\022\014\n\004name\030\004 \001(\tB\006\n\004file\"1\n\010DataPart\022%\n" + + "\004data\030\001 \001(\0132\027.google.protobuf.Struct\"\326\001\n" + + "\007Message\022\022\n\nmessage_id\030\001 \001(\t\022\022\n\ncontext_" + + "id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t\022\032\n\004role\030\004 \001(\0162" + + "\014.a2a.v1.Role\022\033\n\005parts\030\005 \003(\0132\014.a2a.v1.Pa" + + "rt\022)\n\010metadata\030\006 \001(\0132\027.google.protobuf.S" + + "truct\022\022\n\nextensions\030\007 \003(\t\022\032\n\022reference_t" + + "ask_ids\030\010 \003(\t\"\236\001\n\010Artifact\022\023\n\013artifact_i" + + "d\030\001 \001(\t\022\014\n\004name\030\003 \001(\t\022\023\n\013description\030\004 \001" + + "(\t\022\033\n\005parts\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010metad" + + "ata\030\006 \001(\0132\027.google.protobuf.Struct\022\022\n\nex" + + "tensions\030\007 \003(\t\"\232\001\n\025TaskStatusUpdateEvent" + + "\022\017\n\007task_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n" + + "\006status\030\003 \001(\0132\022.a2a.v1.TaskStatus\022\r\n\005fin" + + "al\030\004 \001(\010\022)\n\010metadata\030\005 \001(\0132\027.google.prot" + + "obuf.Struct\"\261\001\n\027TaskArtifactUpdateEvent\022" + + "\017\n\007task_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\010" + + "artifact\030\003 \001(\0132\020.a2a.v1.Artifact\022\016\n\006appe" + + "nd\030\004 \001(\010\022\022\n\nlast_chunk\030\005 \001(\010\022)\n\010metadata" + + "\030\006 \001(\0132\027.google.protobuf.Struct\"t\n\026PushN" + + "otificationConfig\022\n\n\002id\030\001 \001(\t\022\013\n\003url\030\002 \001" + + "(\t\022\r\n\005token\030\003 \001(\t\0222\n\016authentication\030\004 \001(" + + "\0132\032.a2a.v1.AuthenticationInfo\":\n\022Authent" + + "icationInfo\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credenti" + + "als\030\002 \001(\t\"0\n\016AgentInterface\022\013\n\003url\030\001 \001(\t" + + "\022\021\n\ttransport\030\002 \001(\t\"\264\005\n\tAgentCard\022\030\n\020pro" + + "tocol_version\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013des" + + "cription\030\002 \001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred" + + "_transport\030\016 \001(\t\0225\n\025additional_interface" + + "s\030\017 \003(\0132\026.a2a.v1.AgentInterface\022\'\n\010provi" + + "der\030\004 \001(\0132\025.a2a.v1.AgentProvider\022\017\n\007vers" + + "ion\030\005 \001(\t\022\031\n\021documentation_url\030\006 \001(\t\022/\n\014" + + "capabilities\030\007 \001(\0132\031.a2a.v1.AgentCapabil" + + "ities\022@\n\020security_schemes\030\010 \003(\0132&.a2a.v1" + + ".AgentCard.SecuritySchemesEntry\022\"\n\010secur" + + "ity\030\t \003(\0132\020.a2a.v1.Security\022\033\n\023default_i" + + "nput_modes\030\n \003(\t\022\034\n\024default_output_modes" + + "\030\013 \003(\t\022\"\n\006skills\030\014 \003(\0132\022.a2a.v1.AgentSki" + + "ll\022,\n$supports_authenticated_extended_ca" + + "rd\030\r \001(\010\022.\n\nsignatures\030\021 \003(\0132\032.a2a.v1.Ag" + + "entCardSignature\022\020\n\010icon_url\030\022 \001(\t\032N\n\024Se" + + "curitySchemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value" + + "\030\002 \001(\0132\026.a2a.v1.SecurityScheme:\0028\001\"2\n\rAg" + + "entProvider\022\013\n\003url\030\001 \001(\t\022\024\n\014organization" + + "\030\002 \001(\t\"\220\001\n\021AgentCapabilities\022\021\n\tstreamin" + + "g\030\001 \001(\010\022\032\n\022push_notifications\030\002 \001(\010\022*\n\ne" + + "xtensions\030\003 \003(\0132\026.a2a.v1.AgentExtension\022" + + " \n\030state_transition_history\030\004 \001(\010\"m\n\016Age" + + "ntExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030" + + "\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132" + + "\027.google.protobuf.Struct\"\252\001\n\nAgentSkill\022" + + "\n\n\002id\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\022\023\n\013description" + + "\030\003 \001(\t\022\014\n\004tags\030\004 \003(\t\022\020\n\010examples\030\005 \003(\t\022\023" + + "\n\013input_modes\030\006 \003(\t\022\024\n\014output_modes\030\007 \003(" + + "\t\022\"\n\010security\030\010 \003(\0132\020.a2a.v1.Security\"m\n" + + "\022AgentCardSignature\022\026\n\tprotected\030\001 \001(\tB\003" + + "\340A\002\022\026\n\tsignature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 " + + "\001(\0132\027.google.protobuf.Struct\"l\n\032TaskPush" + + "NotificationConfig\022\014\n\004name\030\001 \001(\t\022@\n\030push" + + "_notification_config\030\002 \001(\0132\036.a2a.v1.Push" + + "NotificationConfig\"\032\n\nStringList\022\014\n\004list" + + "\030\001 \003(\t\"~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035.a2" + + "a.v1.Security.SchemesEntry\032B\n\014SchemesEnt" + + "ry\022\013\n\003key\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1." + + "StringList:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027api" + + "_key_security_scheme\030\001 \001(\0132\034.a2a.v1.APIK" + + "eySecuritySchemeH\000\022C\n\031http_auth_security" + + "_scheme\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecurityS" + + "chemeH\000\022>\n\026oauth2_security_scheme\030\003 \001(\0132" + + "\034.a2a.v1.OAuth2SecuritySchemeH\000\022N\n\037open_" + + "id_connect_security_scheme\030\004 \001(\0132#.a2a.v" + + "1.OpenIdConnectSecuritySchemeH\000\022?\n\024mtls_" + + "security_scheme\030\005 \001(\0132\037.a2a.v1.MutualTls" + + "SecuritySchemeH\000B\010\n\006scheme\"K\n\024APIKeySecu" + + "rityScheme\022\023\n\013description\030\001 \001(\t\022\020\n\010locat" + + "ion\030\002 \001(\t\022\014\n\004name\030\003 \001(\t\"T\n\026HTTPAuthSecur" + + "ityScheme\022\023\n\013description\030\001 \001(\t\022\016\n\006scheme" + + "\030\002 \001(\t\022\025\n\rbearer_format\030\003 \001(\t\"k\n\024OAuth2S" + + "ecurityScheme\022\023\n\013description\030\001 \001(\t\022!\n\005fl" + + "ows\030\002 \001(\0132\022.a2a.v1.OAuthFlows\022\033\n\023oauth2_" + + "metadata_url\030\003 \001(\t\"O\n\033OpenIdConnectSecur" + + "ityScheme\022\023\n\013description\030\001 \001(\t\022\033\n\023open_i" + + "d_connect_url\030\002 \001(\t\".\n\027MutualTlsSecurity" + + "Scheme\022\023\n\013description\030\001 \001(\t\"\366\001\n\nOAuthFlo" + + "ws\022@\n\022authorization_code\030\001 \001(\0132\".a2a.v1." + + "AuthorizationCodeOAuthFlowH\000\022@\n\022client_c" + + "redentials\030\002 \001(\0132\".a2a.v1.ClientCredenti" + + "alsOAuthFlowH\000\022-\n\010implicit\030\003 \001(\0132\031.a2a.v" + + "1.ImplicitOAuthFlowH\000\022-\n\010password\030\004 \001(\0132" + + "\031.a2a.v1.PasswordOAuthFlowH\000B\006\n\004flow\"\316\001\n" + + "\032AuthorizationCodeOAuthFlow\022\031\n\021authoriza" + + "tion_url\030\001 \001(\t\022\021\n\ttoken_url\030\002 \001(\t\022\023\n\013ref" + + "resh_url\030\003 \001(\t\022>\n\006scopes\030\004 \003(\0132..a2a.v1." + + "AuthorizationCodeOAuthFlow.ScopesEntry\032-" + + "\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(" + + "\t:\0028\001\"\263\001\n\032ClientCredentialsOAuthFlow\022\021\n\t" + + "token_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\022>\n\006" + + "scopes\030\003 \003(\0132..a2a.v1.ClientCredentialsO" + + "AuthFlow.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003k" + + "ey\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021Implicit" + + "OAuthFlow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013" + + "refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a." + + "v1.ImplicitOAuthFlow.ScopesEntry\032-\n\013Scop" + + "esEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"" + + "\241\001\n\021PasswordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t" + + "\022\023\n\013refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%." + + "a2a.v1.PasswordOAuthFlow.ScopesEntry\032-\n\013" + + "ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:" + + "\0028\001\"\250\001\n\022SendMessageRequest\022.\n\007request\030\001 " + + "\001(\0132\017.a2a.v1.MessageB\003\340A\002R\007message\0227\n\rco" + + "nfiguration\030\002 \001(\0132 .a2a.v1.SendMessageCo" + + "nfiguration\022)\n\010metadata\030\003 \001(\0132\027.google.p" + + "rotobuf.Struct\";\n\016GetTaskRequest\022\021\n\004name" + + "\030\001 \001(\tB\003\340A\002\022\026\n\016history_length\030\002 \001(\005\"\332\001\n\020" + + "ListTasksRequest\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006" + + "status\030\002 \001(\0162\021.a2a.v1.TaskState\022\021\n\tpage_" + + "size\030\003 \001(\005\022\022\n\npage_token\030\004 \001(\t\022\026\n\016histor" + + "y_length\030\005 \001(\005\0225\n\021last_updated_time\030\006 \001(" + + "\0132\032.google.protobuf.Timestamp\022\031\n\021include" + + "_artifacts\030\007 \001(\010\"]\n\021ListTasksResponse\022\033\n" + + "\005tasks\030\001 \003(\0132\014.a2a.v1.Task\022\027\n\017next_page_" + + "token\030\002 \001(\t\022\022\n\ntotal_size\030\003 \001(\005\"!\n\021Cance" + + "lTaskRequest\022\014\n\004name\030\001 \001(\t\"4\n$GetTaskPus" + + "hNotificationConfigRequest\022\014\n\004name\030\001 \001(\t" + + "\"7\n\'DeleteTaskPushNotificationConfigRequ" + + "est\022\014\n\004name\030\001 \001(\t\"\217\001\n\'CreateTaskPushNoti" + + "ficationConfigRequest\022\023\n\006parent\030\001 \001(\tB\003\340" + + "A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001" + + "(\0132\".a2a.v1.TaskPushNotificationConfigB\003" + + "\340A\002\"\'\n\027TaskSubscriptionRequest\022\014\n\004name\030\001" + + " \001(\t\"^\n%ListTaskPushNotificationConfigRe" + + "quest\022\016\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005" + + "\022\022\n\npage_token\030\003 \001(\t\"\025\n\023GetAgentCardRequ" + + "est\"g\n\023SendMessageResponse\022\034\n\004task\030\001 \001(\013" + + "2\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.M" + + "essageH\000R\007messageB\t\n\007payload\"\326\001\n\016StreamR" + + "esponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n" + + "\003msg\030\002 \001(\0132\017.a2a.v1.MessageH\000R\007message\0226" + + "\n\rstatus_update\030\003 \001(\0132\035.a2a.v1.TaskStatu" + + "sUpdateEventH\000\022:\n\017artifact_update\030\004 \001(\0132" + + "\037.a2a.v1.TaskArtifactUpdateEventH\000B\t\n\007pa" + + "yload\"v\n&ListTaskPushNotificationConfigR" + + "esponse\0223\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPu" + + "shNotificationConfig\022\027\n\017next_page_token\030" + + "\002 \001(\t*\372\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPEC" + + "IFIED\020\000\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TAS" + + "K_STATE_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETE" + + "D\020\003\022\025\n\021TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE" + + "_CANCELLED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRE" + + "D\020\006\022\027\n\023TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STA" + + "TE_AUTH_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPE" + + "CIFIED\020\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022" + + "\220\013\n\nA2AService\022c\n\013SendMessage\022\032.a2a.v1.S" + + "endMessageRequest\032\033.a2a.v1.SendMessageRe" + + "sponse\"\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024S" + + "endStreamingMessage\022\032.a2a.v1.SendMessage" + + "Request\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"" + + "\022/v1/message:stream:\001*0\001\022R\n\007GetTask\022\026.a2" + + "a.v1.GetTaskRequest\032\014.a2a.v1.Task\"!\332A\004na" + + "me\202\323\344\223\002\024\022\022/v1/{name=tasks/*}\022S\n\tListTask" + + "s\022\030.a2a.v1.ListTasksRequest\032\031.a2a.v1.Lis" + + "tTasksResponse\"\021\202\323\344\223\002\013\022\t/v1/tasks\022[\n\nCan" + + "celTask\022\031.a2a.v1.CancelTaskRequest\032\014.a2a" + + ".v1.Task\"$\202\323\344\223\002\036\"\031/v1/{name=tasks/*}:can" + + "cel:\001*\022s\n\020TaskSubscription\022\037.a2a.v1.Task" + + "SubscriptionRequest\032\026.a2a.v1.StreamRespo" + + "nse\"$\202\323\344\223\002\036\022\034/v1/{name=tasks/*}:subscrib" + + "e0\001\022\305\001\n CreateTaskPushNotificationConfig" + + "\022/.a2a.v1.CreateTaskPushNotificationConf" + + "igRequest\032\".a2a.v1.TaskPushNotificationC" + + "onfig\"L\332A\rparent,config\202\323\344\223\0026\",/v1/{pare" + + "nt=tasks/*/pushNotificationConfigs}:\006con" + + "fig\022\256\001\n\035GetTaskPushNotificationConfig\022,." + + "a2a.v1.GetTaskPushNotificationConfigRequ" + + "est\032\".a2a.v1.TaskPushNotificationConfig\"" + + ";\332A\004name\202\323\344\223\002.\022,/v1/{name=tasks/*/pushNo" + + "tificationConfigs/*}\022\276\001\n\036ListTaskPushNot" + + "ificationConfig\022-.a2a.v1.ListTaskPushNot" + + "ificationConfigRequest\032..a2a.v1.ListTask" + + "PushNotificationConfigResponse\"=\332A\006paren" + + "t\202\323\344\223\002.\022,/v1/{parent=tasks/*}/pushNotifi" + + "cationConfigs\022P\n\014GetAgentCard\022\033.a2a.v1.G" + + "etAgentCardRequest\032\021.a2a.v1.AgentCard\"\020\202" + + "\323\344\223\002\n\022\010/v1/card\022\250\001\n DeleteTaskPushNotifi" + + "cationConfig\022/.a2a.v1.DeleteTaskPushNoti" + + "ficationConfigRequest\032\026.google.protobuf." + + "Empty\";\332A\004name\202\323\344\223\002.*,/v1/{name=tasks/*/" + + "pushNotificationConfigs/*}B7\n\013io.a2a.grp" + + "cB\003A2AP\001Z\030google.golang.org/a2a/v1\252\002\006A2a" + + ".V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -529,7 +529,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_SendMessageConfiguration_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SendMessageConfiguration_descriptor, - new java.lang.String[] { "AcceptedOutputModes", "PushNotification", "HistoryLength", "Blocking", }); + new java.lang.String[] { "AcceptedOutputModes", "PushNotificationConfig", "HistoryLength", "Blocking", }); internal_static_a2a_v1_Task_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_a2a_v1_Task_fieldAccessorTable = new diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index d7aee9a07..aa00a6b94 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -104,18 +104,18 @@ public java.lang.String getAcceptedOutputModes(int index) { return acceptedOutputModes_.getByteString(index); } - public static final int PUSH_NOTIFICATION_FIELD_NUMBER = 2; - private io.a2a.grpc.PushNotificationConfig pushNotification_; + public static final int PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER = 2; + private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; /** *

    * A configuration of a webhook that can be used to receive updates
    * 
* - * .a2a.v1.PushNotificationConfig push_notification = 2; - * @return Whether the pushNotification field is set. + * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * @return Whether the pushNotificationConfig field is set. */ @java.lang.Override - public boolean hasPushNotification() { + public boolean hasPushNotificationConfig() { return ((bitField0_ & 0x00000001) != 0); } /** @@ -123,23 +123,23 @@ public boolean hasPushNotification() { * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; - * @return The pushNotification. + * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * @return The pushNotificationConfig. */ @java.lang.Override - public io.a2a.grpc.PushNotificationConfig getPushNotification() { - return pushNotification_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { + return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; } /** *
    * A configuration of a webhook that can be used to receive updates
    * 
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ @java.lang.Override - public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder() { - return pushNotification_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { + return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; } public static final int HISTORY_LENGTH_FIELD_NUMBER = 3; @@ -194,7 +194,7 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) com.google.protobuf.GeneratedMessage.writeString(output, 1, acceptedOutputModes_.getRaw(i)); } if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(2, getPushNotification()); + output.writeMessage(2, getPushNotificationConfig()); } if (historyLength_ != 0) { output.writeInt32(3, historyLength_); @@ -221,7 +221,7 @@ public int getSerializedSize() { } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, getPushNotification()); + .computeMessageSize(2, getPushNotificationConfig()); } if (historyLength_ != 0) { size += com.google.protobuf.CodedOutputStream @@ -248,10 +248,10 @@ public boolean equals(final java.lang.Object obj) { if (!getAcceptedOutputModesList() .equals(other.getAcceptedOutputModesList())) return false; - if (hasPushNotification() != other.hasPushNotification()) return false; - if (hasPushNotification()) { - if (!getPushNotification() - .equals(other.getPushNotification())) return false; + if (hasPushNotificationConfig() != other.hasPushNotificationConfig()) return false; + if (hasPushNotificationConfig()) { + if (!getPushNotificationConfig() + .equals(other.getPushNotificationConfig())) return false; } if (getHistoryLength() != other.getHistoryLength()) return false; @@ -272,9 +272,9 @@ public int hashCode() { hash = (37 * hash) + ACCEPTED_OUTPUT_MODES_FIELD_NUMBER; hash = (53 * hash) + getAcceptedOutputModesList().hashCode(); } - if (hasPushNotification()) { - hash = (37 * hash) + PUSH_NOTIFICATION_FIELD_NUMBER; - hash = (53 * hash) + getPushNotification().hashCode(); + if (hasPushNotificationConfig()) { + hash = (37 * hash) + PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER; + hash = (53 * hash) + getPushNotificationConfig().hashCode(); } hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; hash = (53 * hash) + getHistoryLength(); @@ -416,7 +416,7 @@ private Builder( private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage .alwaysUseFieldBuilders) { - internalGetPushNotificationFieldBuilder(); + internalGetPushNotificationConfigFieldBuilder(); } } @java.lang.Override @@ -425,10 +425,10 @@ public Builder clear() { bitField0_ = 0; acceptedOutputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); - pushNotification_ = null; - if (pushNotificationBuilder_ != null) { - pushNotificationBuilder_.dispose(); - pushNotificationBuilder_ = null; + pushNotificationConfig_ = null; + if (pushNotificationConfigBuilder_ != null) { + pushNotificationConfigBuilder_.dispose(); + pushNotificationConfigBuilder_ = null; } historyLength_ = 0; blocking_ = false; @@ -471,9 +471,9 @@ private void buildPartial0(io.a2a.grpc.SendMessageConfiguration result) { } int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000002) != 0)) { - result.pushNotification_ = pushNotificationBuilder_ == null - ? pushNotification_ - : pushNotificationBuilder_.build(); + result.pushNotificationConfig_ = pushNotificationConfigBuilder_ == null + ? pushNotificationConfig_ + : pushNotificationConfigBuilder_.build(); to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000004) != 0)) { @@ -507,8 +507,8 @@ public Builder mergeFrom(io.a2a.grpc.SendMessageConfiguration other) { } onChanged(); } - if (other.hasPushNotification()) { - mergePushNotification(other.getPushNotification()); + if (other.hasPushNotificationConfig()) { + mergePushNotificationConfig(other.getPushNotificationConfig()); } if (other.getHistoryLength() != 0) { setHistoryLength(other.getHistoryLength()); @@ -550,7 +550,7 @@ public Builder mergeFrom( } // case 10 case 18: { input.readMessage( - internalGetPushNotificationFieldBuilder().getBuilder(), + internalGetPushNotificationConfigFieldBuilder().getBuilder(), extensionRegistry); bitField0_ |= 0x00000002; break; @@ -729,18 +729,18 @@ public Builder addAcceptedOutputModesBytes( return this; } - private io.a2a.grpc.PushNotificationConfig pushNotification_; + private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationBuilder_; + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationConfigBuilder_; /** *
      * A configuration of a webhook that can be used to receive updates
      * 
* - * .a2a.v1.PushNotificationConfig push_notification = 2; - * @return Whether the pushNotification field is set. + * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * @return Whether the pushNotificationConfig field is set. */ - public boolean hasPushNotification() { + public boolean hasPushNotificationConfig() { return ((bitField0_ & 0x00000002) != 0); } /** @@ -748,14 +748,14 @@ public boolean hasPushNotification() { * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; - * @return The pushNotification. + * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * @return The pushNotificationConfig. */ - public io.a2a.grpc.PushNotificationConfig getPushNotification() { - if (pushNotificationBuilder_ == null) { - return pushNotification_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { + if (pushNotificationConfigBuilder_ == null) { + return pushNotificationConfig_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; } else { - return pushNotificationBuilder_.getMessage(); + return pushNotificationConfigBuilder_.getMessage(); } } /** @@ -763,16 +763,16 @@ public io.a2a.grpc.PushNotificationConfig getPushNotification() { * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - public Builder setPushNotification(io.a2a.grpc.PushNotificationConfig value) { - if (pushNotificationBuilder_ == null) { + public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { + if (pushNotificationConfigBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - pushNotification_ = value; + pushNotificationConfig_ = value; } else { - pushNotificationBuilder_.setMessage(value); + pushNotificationConfigBuilder_.setMessage(value); } bitField0_ |= 0x00000002; onChanged(); @@ -783,14 +783,14 @@ public Builder setPushNotification(io.a2a.grpc.PushNotificationConfig value) { * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - public Builder setPushNotification( + public Builder setPushNotificationConfig( io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { - if (pushNotificationBuilder_ == null) { - pushNotification_ = builderForValue.build(); + if (pushNotificationConfigBuilder_ == null) { + pushNotificationConfig_ = builderForValue.build(); } else { - pushNotificationBuilder_.setMessage(builderForValue.build()); + pushNotificationConfigBuilder_.setMessage(builderForValue.build()); } bitField0_ |= 0x00000002; onChanged(); @@ -801,21 +801,21 @@ public Builder setPushNotification( * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - public Builder mergePushNotification(io.a2a.grpc.PushNotificationConfig value) { - if (pushNotificationBuilder_ == null) { + public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { + if (pushNotificationConfigBuilder_ == null) { if (((bitField0_ & 0x00000002) != 0) && - pushNotification_ != null && - pushNotification_ != io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) { - getPushNotificationBuilder().mergeFrom(value); + pushNotificationConfig_ != null && + pushNotificationConfig_ != io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) { + getPushNotificationConfigBuilder().mergeFrom(value); } else { - pushNotification_ = value; + pushNotificationConfig_ = value; } } else { - pushNotificationBuilder_.mergeFrom(value); + pushNotificationConfigBuilder_.mergeFrom(value); } - if (pushNotification_ != null) { + if (pushNotificationConfig_ != null) { bitField0_ |= 0x00000002; onChanged(); } @@ -826,14 +826,14 @@ public Builder mergePushNotification(io.a2a.grpc.PushNotificationConfig value) { * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - public Builder clearPushNotification() { + public Builder clearPushNotificationConfig() { bitField0_ = (bitField0_ & ~0x00000002); - pushNotification_ = null; - if (pushNotificationBuilder_ != null) { - pushNotificationBuilder_.dispose(); - pushNotificationBuilder_ = null; + pushNotificationConfig_ = null; + if (pushNotificationConfigBuilder_ != null) { + pushNotificationConfigBuilder_.dispose(); + pushNotificationConfigBuilder_ = null; } onChanged(); return this; @@ -843,26 +843,26 @@ public Builder clearPushNotification() { * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationBuilder() { + public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuilder() { bitField0_ |= 0x00000002; onChanged(); - return internalGetPushNotificationFieldBuilder().getBuilder(); + return internalGetPushNotificationConfigFieldBuilder().getBuilder(); } /** *
      * A configuration of a webhook that can be used to receive updates
      * 
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder() { - if (pushNotificationBuilder_ != null) { - return pushNotificationBuilder_.getMessageOrBuilder(); + public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { + if (pushNotificationConfigBuilder_ != null) { + return pushNotificationConfigBuilder_.getMessageOrBuilder(); } else { - return pushNotification_ == null ? - io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotification_; + return pushNotificationConfig_ == null ? + io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : pushNotificationConfig_; } } /** @@ -870,20 +870,20 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder( * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> - internalGetPushNotificationFieldBuilder() { - if (pushNotificationBuilder_ == null) { - pushNotificationBuilder_ = new com.google.protobuf.SingleFieldBuilder< + internalGetPushNotificationConfigFieldBuilder() { + if (pushNotificationConfigBuilder_ == null) { + pushNotificationConfigBuilder_ = new com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder>( - getPushNotification(), + getPushNotificationConfig(), getParentForChildren(), isClean()); - pushNotification_ = null; + pushNotificationConfig_ = null; } - return pushNotificationBuilder_; + return pushNotificationConfigBuilder_; } private int historyLength_ ; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java index ff22f3f2a..d69a9755e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -56,27 +56,27 @@ public interface SendMessageConfigurationOrBuilder extends * A configuration of a webhook that can be used to receive updates *
* - * .a2a.v1.PushNotificationConfig push_notification = 2; - * @return Whether the pushNotification field is set. + * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * @return Whether the pushNotificationConfig field is set. */ - boolean hasPushNotification(); + boolean hasPushNotificationConfig(); /** *
    * A configuration of a webhook that can be used to receive updates
    * 
* - * .a2a.v1.PushNotificationConfig push_notification = 2; - * @return The pushNotification. + * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * @return The pushNotificationConfig. */ - io.a2a.grpc.PushNotificationConfig getPushNotification(); + io.a2a.grpc.PushNotificationConfig getPushNotificationConfig(); /** *
    * A configuration of a webhook that can be used to receive updates
    * 
* - * .a2a.v1.PushNotificationConfig push_notification = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2; */ - io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationOrBuilder(); + io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder(); /** *
diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java
index d2263a111..3589f7ae2 100644
--- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java
+++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java
@@ -365,7 +365,7 @@ public static io.a2a.grpc.SendMessageConfiguration messageSendConfiguration(Mess
                 builder.setHistoryLength(messageSendConfiguration.historyLength());
             }
             if (messageSendConfiguration.pushNotificationConfig() != null) {
-                builder.setPushNotification(pushNotificationConfig(messageSendConfiguration.pushNotificationConfig()));
+                builder.setPushNotificationConfig(pushNotificationConfig(messageSendConfiguration.pushNotificationConfig()));
             }
             builder.setBlocking(messageSendConfiguration.blocking());
             return builder.build();
@@ -874,7 +874,7 @@ private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.Sen
                     sendMessageConfiguration.getAcceptedOutputModesList().isEmpty() ? null :
                             new ArrayList<>(sendMessageConfiguration.getAcceptedOutputModesList()),
                     sendMessageConfiguration.getHistoryLength(),
-                    pushNotification(sendMessageConfiguration.getPushNotification()),
+                    pushNotification(sendMessageConfiguration.getPushNotificationConfig()),
                     sendMessageConfiguration.getBlocking()
             );
         }
diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto
index 55f0bce88..6904a918a 100644
--- a/spec-grpc/src/main/proto/a2a.proto
+++ b/spec-grpc/src/main/proto/a2a.proto
@@ -124,7 +124,7 @@ message SendMessageConfiguration {
   // The output modes that the agent is expected to respond with.
   repeated string accepted_output_modes = 1;
   // A configuration of a webhook that can be used to receive updates
-  PushNotificationConfig push_notification = 2;
+  PushNotificationConfig push_notification_config = 2;
   // The maximum number of messages to include in the history. if 0, the
   // history will be unlimited.
   int32 history_length = 3;

From fcc02ecbb5fd2f386b9f28df32943028b289a8cf Mon Sep 17 00:00:00 2001
From: Kabir Khan 
Date: Mon, 24 Nov 2025 15:18:44 +0000
Subject: [PATCH 212/493] fix: Store push notification config for newly created
 tasks (#483)

This was missing in the non-streaming message sends

Follows up on #383 and #482

The TCK is not testing this, but it is introduced in
https://github.com/a2aproject/a2a-tck/pull/94
---
 .../DefaultRequestHandler.java                |   8 +
 .../DefaultRequestHandlerTest.java            | 158 ++++++++++++++++++
 2 files changed, 166 insertions(+)

diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
index 1e8e1af94..b59a9aedb 100644
--- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
+++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
@@ -306,6 +306,14 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte
             // We do this HERE (not in ResultAggregator) to avoid blocking Vert.x worker threads
             // during the consumption loop itself.
             kind = etai.eventType();
+
+            // Store push notification config for newly created tasks (mirrors streaming logic)
+            // Only for NEW tasks - existing tasks are handled by initMessageSend()
+            if (mss.task() == null && kind instanceof Task createdTask && shouldAddPushInfo(params)) {
+                LOGGER.debug("Storing push notification config for new task {}", createdTask.getId());
+                pushConfigStore.setInfo(createdTask.getId(), params.configuration().pushNotificationConfig());
+            }
+
             if (blocking && interruptedOrNonBlocking) {
                 // For blocking calls: ensure all events are processed before returning
                 // Order of operations is critical to avoid circular dependency:
diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java
index 4fa3a2a15..acaa531ad 100644
--- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java
+++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java
@@ -4,9 +4,11 @@
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -18,12 +20,14 @@
 import io.a2a.server.auth.UnauthenticatedUser;
 import io.a2a.server.events.EventQueue;
 import io.a2a.server.events.InMemoryQueueManager;
+import io.a2a.server.tasks.InMemoryPushNotificationConfigStore;
 import io.a2a.server.tasks.InMemoryTaskStore;
 import io.a2a.server.tasks.TaskUpdater;
 import io.a2a.spec.JSONRPCError;
 import io.a2a.spec.Message;
 import io.a2a.spec.MessageSendConfiguration;
 import io.a2a.spec.MessageSendParams;
+import io.a2a.spec.PushNotificationConfig;
 import io.a2a.spec.Task;
 import io.a2a.spec.TaskState;
 import io.a2a.spec.TaskStatus;
@@ -794,6 +798,160 @@ void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception {
             returnedTask.getArtifacts().size());
     }
 
+    /**
+     * Test that pushNotificationConfig from SendMessageConfiguration is stored for NEW tasks
+     * in non-streaming (blocking) mode. This reproduces the bug from issue #84.
+     *
+     * Expected behavior:
+     * 1. Client sends message with pushNotificationConfig in SendMessageConfiguration
+     * 2. Agent creates a new task
+     * 3. pushNotificationConfig should be stored in PushNotificationConfigStore
+     * 4. Config should be retrievable via getInfo()
+     */
+    @Test
+    @Timeout(10)
+    void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exception {
+        String taskId = "push-config-blocking-new-task";
+        String contextId = "push-config-ctx";
+
+        // Create test config store
+        InMemoryPushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore();
+
+        // Re-create request handler with pushConfigStore
+        requestHandler = DefaultRequestHandler.create(
+            agentExecutor,
+            taskStore,
+            queueManager,
+            pushConfigStore, // Add push config store
+            null, // pushSender
+            Executors.newCachedThreadPool()
+        );
+
+        // Create push notification config
+        PushNotificationConfig pushConfig = new PushNotificationConfig.Builder()
+            .id("config-1")
+            .url("https://example.com/webhook")
+            .token("test-token-123")
+            .build();
+
+        // Create message with pushNotificationConfig
+        Message message = new Message.Builder()
+            .messageId("msg-push-config")
+            .role(Message.Role.USER)
+            .parts(new TextPart("test message"))
+            .taskId(taskId)
+            .contextId(contextId)
+            .build();
+
+        MessageSendConfiguration config = new MessageSendConfiguration.Builder()
+            .blocking(true)
+            .pushNotificationConfig(pushConfig)
+            .build();
+
+        MessageSendParams params = new MessageSendParams(message, config, null);
+
+        // Agent creates a new task
+        agentExecutor.setExecuteCallback((context, queue) -> {
+            TaskUpdater updater = new TaskUpdater(context, queue);
+            updater.submit();  // Creates new task in SUBMITTED state
+            updater.complete();
+        });
+
+        // Call blocking onMessageSend
+        Object result = requestHandler.onMessageSend(params, serverCallContext);
+
+        // Verify result is a task
+        assertTrue(result instanceof Task, "Result should be a Task");
+        Task returnedTask = (Task) result;
+        assertEquals(taskId, returnedTask.getId());
+
+        // THE KEY ASSERTION: Verify pushNotificationConfig was stored
+        List storedConfigs = pushConfigStore.getInfo(taskId);
+        assertNotNull(storedConfigs, "Push notification config should be stored for new task");
+        assertEquals(1, storedConfigs.size(),
+            "Should have exactly 1 push config stored");
+        assertEquals("config-1", storedConfigs.get(0).id());
+        assertEquals("https://example.com/webhook", storedConfigs.get(0).url());
+    }
+
+    /**
+     * Test that pushNotificationConfig is stored for EXISTING tasks.
+     * This verifies the initMessageSend logic works correctly.
+     */
+    @Test
+    @Timeout(10)
+    void testMessageStoresPushNotificationConfigForExistingTask() throws Exception {
+        String taskId = "push-config-existing-task";
+        String contextId = "push-config-existing-ctx";
+
+        // Create test config store
+        InMemoryPushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore();
+
+        // Re-create request handler with pushConfigStore
+        requestHandler = DefaultRequestHandler.create(
+            agentExecutor,
+            taskStore,
+            queueManager,
+            pushConfigStore, // Add push config store
+            null, // pushSender
+            Executors.newCachedThreadPool()
+        );
+
+        // Create EXISTING task in store
+        Task existingTask = new Task.Builder()
+            .id(taskId)
+            .contextId(contextId)
+            .status(new TaskStatus(TaskState.WORKING))
+            .build();
+        taskStore.save(existingTask);
+
+        // Create push notification config
+        PushNotificationConfig pushConfig = new PushNotificationConfig.Builder()
+            .id("config-existing-1")
+            .url("https://example.com/existing-webhook")
+            .token("existing-token-789")
+            .build();
+
+        Message message = new Message.Builder()
+            .messageId("msg-push-existing")
+            .role(Message.Role.USER)
+            .parts(new TextPart("update existing task"))
+            .taskId(taskId)
+            .contextId(contextId)
+            .build();
+
+        MessageSendConfiguration config = new MessageSendConfiguration.Builder()
+            .blocking(true)
+            .pushNotificationConfig(pushConfig)
+            .build();
+
+        MessageSendParams params = new MessageSendParams(message, config, null);
+
+        // Agent updates the existing task
+        agentExecutor.setExecuteCallback((context, queue) -> {
+            TaskUpdater updater = new TaskUpdater(context, queue);
+            updater.addArtifact(
+                List.of(new TextPart("update artifact", null)),
+                "artifact-1", "Update", null);
+            updater.complete();
+        });
+
+        // Call blocking onMessageSend
+        Object result = requestHandler.onMessageSend(params, serverCallContext);
+
+        // Verify result
+        assertTrue(result instanceof Task, "Result should be a Task");
+
+        // Verify pushNotificationConfig was stored (initMessageSend path)
+        List storedConfigs = pushConfigStore.getInfo(taskId);
+        assertNotNull(storedConfigs,
+            "Push notification config should be stored for existing task");
+        assertEquals(1, storedConfigs.size(),
+            "Should have exactly 1 push config stored");
+        assertEquals("config-existing-1", storedConfigs.get(0).id());
+        assertEquals("https://example.com/existing-webhook", storedConfigs.get(0).url());
+    }
+
     /**
      * Simple test agent executor that allows controlling execution timing
      */

From 686e40a7363823be1e3614d62899c25e56fc09fb Mon Sep 17 00:00:00 2001
From: Jeff Mesnil 
Date: Thu, 27 Nov 2025 11:20:09 +0100
Subject: [PATCH 213/493] fix: Add Content-Type header to push notification
 requests (#489)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Set `Content-Type: application/json` header when sending push
notifications
to prevent `415 Unsupported Media Type` errors. The request was
previously
defaulting to `application/octet-stream`.

Updates tests to verify that the `Content-Type` header is always present
in push notification requests, regardless of whether authentication
token is included.

Fixes: https://github.com/a2aproject/a2a-java/issues/486
Signed-off-by: Jeff Mesnil 

- [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md).
- [X] Make your Pull Request title in the
 specification.
- Important Prefixes for
[release-please](https://github.com/googleapis/release-please):
- `fix:` which represents bug fixes, and correlates to a
[SemVer](https://semver.org/) patch.
- `feat:` represents a new feature, and correlates to a SemVer minor.
- `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking
change (indicated by the `!`) and will result in a SemVer major.
- [X] Ensure the tests pass
- [X] Appropriate READMEs were updated (if necessary)

Fixes #486 🦕

Signed-off-by: Jeff Mesnil 
---
 .../JpaPushNotificationConfigStoreTest.java   |  4 ++++
 .../io/a2a/client/http/A2AHttpClient.java     |  3 +++
 .../tasks/BasePushNotificationSender.java     |  3 +++
 ...MemoryPushNotificationConfigStoreTest.java |  9 ++++-----
 .../tasks/PushNotificationSenderTest.java     | 19 ++++++++++++++++---
 5 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java
index 70f9d1e5f..f8be8eea0 100644
--- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java
+++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java
@@ -1,5 +1,7 @@
 package io.a2a.extras.pushnotificationconfigstore.database.jpa;
 
+import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
+import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -235,6 +237,7 @@ public void testSendNotificationSuccess() throws Exception {
         // Mock successful HTTP response
         when(mockHttpClient.createPost()).thenReturn(mockPostBuilder);
         when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder);
+        when(mockPostBuilder.addHeader(CONTENT_TYPE, APPLICATION_JSON)).thenReturn(mockPostBuilder);
         when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder);
         when(mockPostBuilder.post()).thenReturn(mockHttpResponse);
         when(mockHttpResponse.success()).thenReturn(true);
@@ -245,6 +248,7 @@ public void testSendNotificationSuccess() throws Exception {
         ArgumentCaptor bodyCaptor = ArgumentCaptor.forClass(String.class);
         verify(mockHttpClient).createPost();
         verify(mockPostBuilder).url(config.url());
+        verify(mockPostBuilder).addHeader(CONTENT_TYPE, APPLICATION_JSON);
         verify(mockPostBuilder).body(bodyCaptor.capture());
         verify(mockPostBuilder).post();
 
diff --git a/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java
index 52c252a8f..8d7f2d0f9 100644
--- a/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java
+++ b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java
@@ -7,6 +7,9 @@
 
 public interface A2AHttpClient {
 
+    String CONTENT_TYPE= "Content-Type";
+    String APPLICATION_JSON= "application/json";
+
     GetBuilder createGet();
 
     PostBuilder createPost();
diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java
index 4afaf3b4e..e400b7ba0 100644
--- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java
+++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java
@@ -1,5 +1,7 @@
 package io.a2a.server.tasks;
 
+import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
+import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
 import static io.a2a.common.A2AHeaders.X_A2A_NOTIFICATION_TOKEN;
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
@@ -90,6 +92,7 @@ private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo)
         try {
             postBuilder
                     .url(url)
+                    .addHeader(CONTENT_TYPE, APPLICATION_JSON)
                     .body(body)
                     .post();
         } catch (IOException | InterruptedException e) {
diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java
index 9156f78b2..bc984ae68 100644
--- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java
+++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java
@@ -1,5 +1,7 @@
 package io.a2a.server.tasks;
 
+import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
+import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -50,6 +52,7 @@ public void setUp() {
     private void setupBasicMockHttpResponse() throws Exception {
         when(mockHttpClient.createPost()).thenReturn(mockPostBuilder);
         when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder);
+        when(mockPostBuilder.addHeader(CONTENT_TYPE, APPLICATION_JSON)).thenReturn(mockPostBuilder);
         when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder);
         when(mockPostBuilder.post()).thenReturn(mockHttpResponse);
         when(mockHttpResponse.success()).thenReturn(true);
@@ -230,11 +233,7 @@ public void testSendNotificationSuccess() throws Exception {
         configStore.setInfo(taskId, config);
 
         // Mock successful HTTP response
-        when(mockHttpClient.createPost()).thenReturn(mockPostBuilder);
-        when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder);
-        when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder);
-        when(mockPostBuilder.post()).thenReturn(mockHttpResponse);
-        when(mockHttpResponse.success()).thenReturn(true);
+        setupBasicMockHttpResponse();
 
         notificationSender.sendNotification(task);
 
diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java
index 2ab974edb..c52a4cf20 100644
--- a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java
+++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java
@@ -1,6 +1,9 @@
 package io.a2a.server.tasks;
 
+import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
+import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
@@ -158,7 +161,12 @@ private void testSendNotificationWithInvalidToken(String token, String testName)
         // Verify that no authentication header was sent (invalid token should not add header)
         assertEquals(1, testHttpClient.headers.size());
         Map sentHeaders = testHttpClient.headers.get(0);
-        assertTrue(sentHeaders.isEmpty(), "No headers should be sent when token is invalid");
+        assertEquals(1, sentHeaders.size());
+        assertFalse(sentHeaders.containsKey(A2AHeaders.X_A2A_NOTIFICATION_TOKEN),
+                "X-A2A-Notification-Token header should not be sent when token is invalid");
+        // Content-Type header should always be present
+        assertTrue(sentHeaders.containsKey(CONTENT_TYPE));
+        assertEquals(APPLICATION_JSON, sentHeaders.get(CONTENT_TYPE));
     }
 
     private Task createSampleTask(String taskId, TaskState state) {
@@ -227,8 +235,13 @@ public void testSendNotificationWithTokenSuccess() throws InterruptedException {
         // Verify that the X-A2A-Notification-Token header is sent with the correct token
         assertEquals(1, testHttpClient.headers.size());
         Map sentHeaders = testHttpClient.headers.get(0);
+        assertEquals(2, sentHeaders.size());
         assertTrue(sentHeaders.containsKey(A2AHeaders.X_A2A_NOTIFICATION_TOKEN));
         assertEquals(config.token(), sentHeaders.get(A2AHeaders.X_A2A_NOTIFICATION_TOKEN));
+        // Content-Type header should always be present
+        assertTrue(sentHeaders.containsKey(CONTENT_TYPE));
+        assertEquals(APPLICATION_JSON, sentHeaders.get(CONTENT_TYPE));
+
     }
 
     @Test
@@ -290,10 +303,10 @@ public void testSendNotificationHttpError() {
         String taskId = "task_send_http_err";
         Task taskData = createSampleTask(taskId, TaskState.COMPLETED);
         PushNotificationConfig config = createSamplePushConfig("http://notify.me/http_error", "cfg1", null);
-        
+
         // Set up the configuration in the store
         configStore.setInfo(taskId, config);
-        
+
         // Configure the test client to throw an exception
         testHttpClient.shouldThrowException = true;
 

From cb084ec517f5511c45c0457850337b4b74431b10 Mon Sep 17 00:00:00 2001
From: Emmanuel Hugonnet 
Date: Wed, 3 Dec 2025 14:14:01 +0100
Subject: [PATCH 214/493] feat!: Updating to newest proto to align with 1.0
 Draft specifications (#484)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

[Feat]: Use protobuf as the serialization mechanism

Fixes # 🦕

---------

Signed-off-by: Emmanuel Hugonnet 
Co-authored-by: Kabir Khan 
---
 README.md                                     |    2 +-
 boms/sdk/pom.xml                              |    2 +-
 .../java/io/a2a/client/ClientBuilder.java     |   18 +-
 .../AuthenticationAuthorizationTest.java      |    3 +-
 .../java/io/a2a/client/ClientBuilderTest.java |    2 +-
 .../client/transport/grpc/GrpcTransport.java  |  102 +-
 client/transport/jsonrpc/pom.xml              |   19 +
 .../transport/jsonrpc/JSONRPCTransport.java   |  194 +-
 .../jsonrpc/sse/SSEEventListener.java         |   41 +-
 .../jsonrpc/JSONRPCTransportTest.java         |   26 +-
 .../transport/jsonrpc/JsonMessages.java       |  771 +++----
 .../jsonrpc/JsonStreamingMessages.java        |  147 +-
 .../jsonrpc/sse/SSEEventListenerTest.java     |    2 +-
 .../client/transport/rest/RestTransport.java  |   82 +-
 .../transport/rest/JsonRestMessages.java      |  277 +--
 .../transport/rest/RestTransportTest.java     |   25 +-
 .../auth/AuthInterceptorTest.java             |   23 +-
 .../cloud/CloudAgentCardProducer.java         |    4 +-
 .../examples/cloud/A2ACloudExampleClient.java |    7 +-
 .../helloworld/AgentCardProducer.java         |    4 +-
 ...ationConfigStoreTestAgentCardProducer.java |    7 +-
 .../MultiInstanceReplicationAgentCards.java   |    5 +-
 .../MultiInstanceReplicationTest.java         |   11 +-
 .../ReplicationTestAgentCardProducer.java     |    5 +-
 ...atabaseTaskStoreTestAgentCardProducer.java |    7 +-
 http-client/pom.xml                           |    8 +
 .../io/a2a/client/http/A2ACardResolver.java   |   14 +-
 .../a2a/client/http/A2ACardResolverTest.java  |   24 +-
 .../java/io/a2a/client/http/JsonMessages.java |   28 +-
 pom.xml                                       |   31 +-
 .../a2a-requesthandler-test.properties        |    1 +
 .../server/apps/quarkus/A2AServerRoutes.java  |  126 +-
 .../apps/quarkus/A2AServerRoutesTest.java     |  208 +-
 .../a2a-requesthandler-test.properties        |    1 +
 .../server/rest/quarkus/A2AServerRoutes.java  |    4 +-
 .../rest/quarkus/A2AServerRoutesTest.java     |    4 +-
 .../a2a-requesthandler-test.properties        |    1 +
 server-common/pom.xml                         |    4 +
 .../io/a2a/server/AgentCardValidator.java     |   37 +-
 .../io/a2a/server/AgentCardValidatorTest.java |   51 +-
 .../server/extensions/A2AExtensionsTest.java  |    7 +-
 .../AbstractA2ARequestHandlerTest.java        |    4 +-
 spec-grpc/pom.xml                             |  114 +-
 spec-grpc/src/main/java/io/a2a/grpc/A2A.java  |  455 ++--
 .../main/java/io/a2a/grpc/A2AServiceGrpc.java |  347 ++--
 .../io/a2a/grpc/APIKeySecurityScheme.java     |   72 +-
 .../grpc/APIKeySecuritySchemeOrBuilder.java   |   20 +-
 .../java/io/a2a/grpc/AgentCapabilities.java   |  239 ++-
 .../a2a/grpc/AgentCapabilitiesOrBuilder.java  |   49 +-
 .../src/main/java/io/a2a/grpc/AgentCard.java  | 1832 ++++++++++-------
 .../java/io/a2a/grpc/AgentCardOrBuilder.java  |  318 +--
 .../main/java/io/a2a/grpc/AgentExtension.java |   82 +-
 .../io/a2a/grpc/AgentExtensionOrBuilder.java  |   21 +-
 .../main/java/io/a2a/grpc/AgentInterface.java |  172 +-
 .../io/a2a/grpc/AgentInterfaceOrBuilder.java  |   32 +-
 .../main/java/io/a2a/grpc/AgentProvider.java  |   60 +-
 .../io/a2a/grpc/AgentProviderOrBuilder.java   |   16 +-
 .../src/main/java/io/a2a/grpc/AgentSkill.java |  400 ++--
 .../java/io/a2a/grpc/AgentSkillOrBuilder.java |  107 +-
 .../src/main/java/io/a2a/grpc/Artifact.java   |   68 +-
 .../java/io/a2a/grpc/ArtifactOrBuilder.java   |   14 +-
 .../java/io/a2a/grpc/AuthenticationInfo.java  |   52 +-
 .../a2a/grpc/AuthenticationInfoOrBuilder.java |   16 +-
 .../a2a/grpc/AuthorizationCodeOAuthFlow.java  |  148 +-
 .../AuthorizationCodeOAuthFlowOrBuilder.java  |   51 +-
 .../java/io/a2a/grpc/CancelTaskRequest.java   |    2 +
 .../a2a/grpc/ClientCredentialsOAuthFlow.java  |  113 +-
 .../ClientCredentialsOAuthFlowOrBuilder.java  |   41 +-
 .../src/main/java/io/a2a/grpc/DataPart.java   |   72 +-
 .../java/io/a2a/grpc/DataPartOrBuilder.java   |   18 +-
 ...leteTaskPushNotificationConfigRequest.java |    2 +
 .../src/main/java/io/a2a/grpc/FilePart.java   |  212 +-
 .../java/io/a2a/grpc/FilePartOrBuilder.java   |   48 +-
 ....java => GetExtendedAgentCardRequest.java} |  114 +-
 ...GetExtendedAgentCardRequestOrBuilder.java} |    4 +-
 .../main/java/io/a2a/grpc/GetTaskRequest.java |   65 +-
 .../io/a2a/grpc/GetTaskRequestOrBuilder.java  |   13 +-
 .../io/a2a/grpc/HTTPAuthSecurityScheme.java   |  114 +-
 .../grpc/HTTPAuthSecuritySchemeOrBuilder.java |   32 +-
 .../java/io/a2a/grpc/ImplicitOAuthFlow.java   |  113 +-
 .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java  |   41 +-
 ...ListTaskPushNotificationConfigRequest.java |   57 +-
 ...ushNotificationConfigRequestOrBuilder.java |   16 +-
 ...istTaskPushNotificationConfigResponse.java |    8 +-
 .../java/io/a2a/grpc/ListTasksRequest.java    |  655 +++---
 .../a2a/grpc/ListTasksRequestOrBuilder.java   |   94 +-
 .../java/io/a2a/grpc/ListTasksResponse.java   |  185 +-
 .../a2a/grpc/ListTasksResponseOrBuilder.java  |   32 +-
 .../src/main/java/io/a2a/grpc/Message.java    |  102 +-
 .../java/io/a2a/grpc/MessageOrBuilder.java    |   26 +-
 .../io/a2a/grpc/MutualTlsSecurityScheme.java  |   16 +-
 .../MutualTlsSecuritySchemeOrBuilder.java     |    4 +-
 .../io/a2a/grpc/OAuth2SecurityScheme.java     |   78 +-
 .../grpc/OAuth2SecuritySchemeOrBuilder.java   |   20 +-
 .../src/main/java/io/a2a/grpc/OAuthFlows.java |  194 ++
 .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java |   48 +
 .../a2a/grpc/OpenIdConnectSecurityScheme.java |   58 +-
 .../OpenIdConnectSecuritySchemeOrBuilder.java |   16 +-
 spec-grpc/src/main/java/io/a2a/grpc/Part.java |  132 ++
 .../main/java/io/a2a/grpc/PartOrBuilder.java  |   36 +
 .../java/io/a2a/grpc/PasswordOAuthFlow.java   |  113 +-
 .../a2a/grpc/PasswordOAuthFlowOrBuilder.java  |   41 +-
 .../io/a2a/grpc/PushNotificationConfig.java   |   14 +-
 .../grpc/PushNotificationConfigOrBuilder.java |    4 +-
 spec-grpc/src/main/java/io/a2a/grpc/Role.java |    5 +
 .../main/java/io/a2a/grpc/SecurityScheme.java |    6 +
 .../io/a2a/grpc/SendMessageConfiguration.java |   88 +-
 .../SendMessageConfigurationOrBuilder.java    |   19 +-
 .../java/io/a2a/grpc/SendMessageRequest.java  |    6 +-
 .../java/io/a2a/grpc/SendMessageResponse.java |    4 +-
 ...SetTaskPushNotificationConfigRequest.java} |  114 +-
 ...shNotificationConfigRequestOrBuilder.java} |    4 +-
 .../main/java/io/a2a/grpc/StreamResponse.java |   24 +-
 ...quest.java => SubscribeToTaskRequest.java} |  116 +-
 ...a => SubscribeToTaskRequestOrBuilder.java} |    4 +-
 spec-grpc/src/main/java/io/a2a/grpc/Task.java |   76 +-
 .../io/a2a/grpc/TaskArtifactUpdateEvent.java  |  124 +-
 .../TaskArtifactUpdateEventOrBuilder.java     |   33 +-
 .../main/java/io/a2a/grpc/TaskOrBuilder.java  |   20 +-
 .../a2a/grpc/TaskPushNotificationConfig.java  |   42 +-
 .../TaskPushNotificationConfigOrBuilder.java  |   10 +-
 .../src/main/java/io/a2a/grpc/TaskStatus.java |  220 +-
 .../java/io/a2a/grpc/TaskStatusOrBuilder.java |   30 +-
 .../io/a2a/grpc/TaskStatusUpdateEvent.java    |   76 +-
 .../grpc/TaskStatusUpdateEventOrBuilder.java  |   18 +-
 .../a2a/grpc/mapper/A2ACommonFieldMapper.java |  351 ++++
 .../java/io/a2a/grpc/mapper/A2AMappers.java   |   27 +
 .../a2a/grpc/mapper/A2AProtoMapperConfig.java |  195 ++
 .../mapper/APIKeySecuritySchemeMapper.java    |   31 +
 .../grpc/mapper/AgentCapabilitiesMapper.java  |   19 +
 .../io/a2a/grpc/mapper/AgentCardMapper.java   |   37 +
 .../grpc/mapper/AgentCardSignatureMapper.java |   36 +
 .../a2a/grpc/mapper/AgentExtensionMapper.java |   34 +
 .../a2a/grpc/mapper/AgentInterfaceMapper.java |   18 +
 .../a2a/grpc/mapper/AgentProviderMapper.java  |   18 +
 .../io/a2a/grpc/mapper/AgentSkillMapper.java  |   19 +
 .../io/a2a/grpc/mapper/ArtifactMapper.java    |   41 +
 .../grpc/mapper/AuthenticationInfoMapper.java |   20 +
 .../AuthorizationCodeOAuthFlowMapper.java     |   18 +
 .../ClientCredentialsOAuthFlowMapper.java     |   18 +
 .../io/a2a/grpc/mapper/DataPartMapper.java    |   54 +
 ...askPushNotificationConfigParamsMapper.java |   35 +
 .../io/a2a/grpc/mapper/FilePartMapper.java    |   80 +
 ...askPushNotificationConfigParamsMapper.java |   39 +
 .../mapper/HTTPAuthSecuritySchemeMapper.java  |   30 +
 .../grpc/mapper/ImplicitOAuthFlowMapper.java  |   18 +
 ...askPushNotificationConfigParamsMapper.java |   36 +
 .../grpc/mapper/ListTasksParamsMapper.java    |   49 +
 .../grpc/mapper/ListTasksResultMapper.java    |   29 +
 .../io/a2a/grpc/mapper/MessageMapper.java     |   46 +
 .../MessageSendConfigurationMapper.java       |   36 +
 .../grpc/mapper/MessageSendParamsMapper.java  |   37 +
 .../mapper/MutualTLSSecuritySchemeMapper.java |   28 +
 .../mapper/OAuth2SecuritySchemeMapper.java    |   32 +
 .../io/a2a/grpc/mapper/OAuthFlowsMapper.java  |   24 +
 .../OpenIdConnectSecuritySchemeMapper.java    |   28 +
 .../java/io/a2a/grpc/mapper/PartMapper.java   |   69 +
 .../grpc/mapper/PasswordOAuthFlowMapper.java  |   18 +
 .../mapper/PushNotificationConfigMapper.java  |   31 +
 .../a2a/grpc/mapper/ResourceNameParser.java   |  106 +
 .../java/io/a2a/grpc/mapper/RoleMapper.java   |   52 +
 .../io/a2a/grpc/mapper/SecurityMapper.java    |  110 +
 .../a2a/grpc/mapper/SecuritySchemeMapper.java |  104 +
 .../SetTaskPushNotificationConfigMapper.java  |  136 ++
 .../a2a/grpc/mapper/StreamResponseMapper.java |   90 +
 .../mapper/SubscribeToTaskRequestMapper.java  |   54 +
 .../mapper/TaskArtifactUpdateEventMapper.java |   35 +
 .../a2a/grpc/mapper/TaskIdParamsMapper.java   |   53 +
 .../java/io/a2a/grpc/mapper/TaskMapper.java   |   42 +
 .../TaskPushNotificationConfigMapper.java     |  115 ++
 .../grpc/mapper/TaskQueryParamsMapper.java    |   33 +
 .../io/a2a/grpc/mapper/TaskStateMapper.java   |   68 +
 .../io/a2a/grpc/mapper/TaskStatusMapper.java  |   37 +
 .../mapper/TaskStatusUpdateEventMapper.java   |   37 +
 .../java/io/a2a/grpc/utils/JSONRPCUtils.java  |  553 +++++
 .../java/io/a2a/grpc/utils/ProtoUtils.java    | 1056 ++--------
 spec-grpc/src/main/proto/a2a.proto            |  519 +++--
 .../grpc/mapper/StreamResponseMapperTest.java |  274 +++
 .../io/a2a/grpc/utils/JSONRPCUtilsTest.java   |  247 +++
 .../java/io/a2a/grpc/utils/ToProtoTest.java   |   69 +-
 .../main/java/io/a2a/spec/A2AErrorCodes.java  |   26 +
 .../io/a2a/spec/APIKeySecurityScheme.java     |   25 +-
 spec/src/main/java/io/a2a/spec/AgentCard.java |   93 +-
 .../main/java/io/a2a/spec/AgentInterface.java |   15 +-
 ...ticatedExtendedCardNotConfiguredError.java |    5 +-
 .../java/io/a2a/spec/AuthenticationInfo.java  |   20 +-
 .../java/io/a2a/spec/CancelTaskRequest.java   |    2 +-
 .../spec/ContentTypeNotSupportedError.java    |    7 +-
 ...leteTaskPushNotificationConfigRequest.java |    2 +-
 .../GetAuthenticatedExtendedCardRequest.java  |    2 +-
 .../GetTaskPushNotificationConfigRequest.java |    2 +-
 .../main/java/io/a2a/spec/GetTaskRequest.java |    4 +-
 .../main/java/io/a2a/spec/InternalError.java  |    5 +-
 .../a2a/spec/InvalidAgentResponseError.java   |    5 +-
 .../java/io/a2a/spec/InvalidParamsError.java  |    5 +-
 .../java/io/a2a/spec/InvalidRequestError.java |    5 +-
 .../main/java/io/a2a/spec/JSONParseError.java |    6 +-
 .../io/a2a/spec/JSONRPCErrorDeserializer.java |   35 +-
 .../spec/JSONRPCRequestDeserializerBase.java  |    2 +-
 ...ListTaskPushNotificationConfigRequest.java |    2 +-
 .../java/io/a2a/spec/ListTasksRequest.java    |    2 +-
 spec/src/main/java/io/a2a/spec/Message.java   |    9 +
 .../java/io/a2a/spec/MethodNotFoundError.java |    7 +-
 .../PushNotificationAuthenticationInfo.java   |   32 -
 .../io/a2a/spec/PushNotificationConfig.java   |   10 +-
 .../PushNotificationNotSupportedError.java    |    5 +-
 .../java/io/a2a/spec/SendMessageRequest.java  |    6 +-
 .../a2a/spec/SendStreamingMessageRequest.java |    4 +-
 .../SetTaskPushNotificationConfigRequest.java |    2 +-
 .../io/a2a/spec/StreamingJSONRPCRequest.java  |    2 +-
 .../StreamingJSONRPCRequestDeserializer.java  |    4 +-
 ...quest.java => SubscribeToTaskRequest.java} |   22 +-
 spec/src/main/java/io/a2a/spec/Task.java      |    9 +
 .../io/a2a/spec/TaskArtifactUpdateEvent.java  |    9 +
 .../io/a2a/spec/TaskNotCancelableError.java   |    5 +-
 .../java/io/a2a/spec/TaskNotFoundError.java   |    6 +-
 .../a2a/spec/TaskPushNotificationConfig.java  |    1 +
 .../io/a2a/spec/TaskStatusUpdateEvent.java    |    9 +
 .../a2a/spec/UnsupportedOperationError.java   |    5 +-
 spec/src/main/java/io/a2a/util/Utils.java     |   15 +
 .../spec/JSONRPCErrorSerializationTest.java   |   35 +-
 .../io/a2a/spec/SubTypeSerializationTest.java |    2 +-
 .../io/a2a/tck/server/AgentCardProducer.java  |    9 +-
 .../apps/common/AbstractA2AServerTest.java    |  246 +--
 .../server/apps/common/AgentCardProducer.java |   13 +-
 .../transport/grpc/handler/GrpcHandler.java   |    8 +-
 .../grpc/handler/GrpcHandlerTest.java         |   27 +-
 .../a2a-requesthandler-test.properties        |    0
 transport/jsonrpc/pom.xml                     |   15 +-
 .../jsonrpc/handler/JSONRPCHandler.java       |    7 +-
 .../jsonrpc/handler/JSONRPCHandlerTest.java   |   32 +-
 .../transport/rest/handler/RestHandler.java   |   10 +-
 .../rest/handler/RestHandlerTest.java         |    6 +-
 .../a2a-requesthandler-test.properties        |    0
 234 files changed, 10655 insertions(+), 6594 deletions(-)
 create mode 100644 reference/grpc/src/test/resources/a2a-requesthandler-test.properties
 create mode 100644 reference/jsonrpc/src/test/resources/a2a-requesthandler-test.properties
 create mode 100644 reference/rest/src/test/resources/a2a-requesthandler-test.properties
 rename spec-grpc/src/main/java/io/a2a/grpc/{GetAgentCardRequest.java => GetExtendedAgentCardRequest.java} (66%)
 rename spec-grpc/src/main/java/io/a2a/grpc/{GetAgentCardRequestOrBuilder.java => GetExtendedAgentCardRequestOrBuilder.java} (62%)
 rename spec-grpc/src/main/java/io/a2a/grpc/{CreateTaskPushNotificationConfigRequest.java => SetTaskPushNotificationConfigRequest.java} (84%)
 rename spec-grpc/src/main/java/io/a2a/grpc/{CreateTaskPushNotificationConfigRequestOrBuilder.java => SetTaskPushNotificationConfigRequestOrBuilder.java} (92%)
 rename spec-grpc/src/main/java/io/a2a/grpc/{TaskSubscriptionRequest.java => SubscribeToTaskRequest.java} (76%)
 rename spec-grpc/src/main/java/io/a2a/grpc/{TaskSubscriptionRequestOrBuilder.java => SubscribeToTaskRequestOrBuilder.java} (83%)
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AMappers.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AProtoMapperConfig.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardSignatureMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentExtensionMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentInterfaceMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentProviderMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentSkillMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ArtifactMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthenticationInfoMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ClientCredentialsOAuthFlowMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendConfigurationMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/PushNotificationConfigMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ResourceNameParser.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/RoleMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/SecurityMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/SecuritySchemeMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStateMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java
 create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java
 create mode 100644 spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java
 create mode 100644 spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java
 create mode 100644 spec/src/main/java/io/a2a/spec/A2AErrorCodes.java
 delete mode 100644 spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java
 rename spec/src/main/java/io/a2a/spec/{TaskResubscriptionRequest.java => SubscribeToTaskRequest.java} (75%)
 rename transport/grpc/src/{main => test}/resources/a2a-requesthandler-test.properties (100%)
 rename transport/rest/src/{main => test}/resources/a2a-requesthandler-test.properties (100%)

diff --git a/README.md b/README.md
index a185bc21e..6245c8001 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ We copy https://github.com/a2aproject/A2A/blob/main/specification/grpc/a2a.proto
 ```
 option java_package = "io.a2a.grpc";
 ```
-Then build the `spec-grpc` module with `mvn clean install -Pproto-compile` to regenerate the gRPC classes in the `io.a2a.grpc` package.
+Then build the `spec-grpc` module with `mvn clean install  -Dskip.protobuf.generate=false` to regenerate the gRPC classes in the `io.a2a.grpc` package.
 
 ## Examples
 
diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml
index fd3bd07e4..38246898e 100644
--- a/boms/sdk/pom.xml
+++ b/boms/sdk/pom.xml
@@ -155,7 +155,7 @@
             
                 com.google.protobuf
                 protobuf-java
-                ${protobuf.version}
+                ${protobuf-java.version}
             
             
                 io.smallrye.reactive
diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java
index 5765da204..c62b49ca6 100644
--- a/client/base/src/main/java/io/a2a/client/ClientBuilder.java
+++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java
@@ -92,9 +92,9 @@ private ClientTransport buildClientTransport() throws A2AClientException {
         AgentInterface agentInterface = findBestClientTransport();
 
         // Get the transport provider associated with the protocol
-        ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.transport());
+        ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.protocolBinding());
         if (clientTransportProvider == null) {
-            throw new A2AClientException("No client available for " + agentInterface.transport());
+            throw new A2AClientException("No client available for " + agentInterface.protocolBinding());
         }
         Class transportProtocolClass = clientTransportProvider.getTransportProtocolClass();
 
@@ -102,19 +102,19 @@ private ClientTransport buildClientTransport() throws A2AClientException {
         ClientTransportConfig clientTransportConfig = clientTransports.get(transportProtocolClass);
 
         if (clientTransportConfig == null) {
-            throw new A2AClientException("Missing required TransportConfig for " + agentInterface.transport());
+            throw new A2AClientException("Missing required TransportConfig for " + agentInterface.protocolBinding());
         }
 
         return clientTransportProvider.create(clientTransportConfig, agentCard, agentInterface.url());
     }
 
-    private Map getServerPreferredTransports() {
+    private Map getServerPreferredTransports() throws A2AClientException {
         Map serverPreferredTransports = new LinkedHashMap<>();
-        serverPreferredTransports.put(agentCard.preferredTransport(), agentCard.url());
-        if (agentCard.additionalInterfaces() != null) {
-            for (AgentInterface agentInterface : agentCard.additionalInterfaces()) {
-                serverPreferredTransports.putIfAbsent(agentInterface.transport(), agentInterface.url());
-            }
+        if(agentCard.supportedInterfaces() == null || agentCard.supportedInterfaces().isEmpty()) {
+            throw new A2AClientException("No server interface available in the AgentCard");
+        }
+        for (AgentInterface agentInterface : agentCard.supportedInterfaces()) {
+            serverPreferredTransports.putIfAbsent(agentInterface.protocolBinding(), agentInterface.url());
         }
         return serverPreferredTransports;
     }
diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java
index 295205748..b06d0f49f 100644
--- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java
+++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java
@@ -77,7 +77,6 @@ public void setUp() {
         agentCard = new AgentCard.Builder()
                 .name("Test Agent")
                 .description("Test agent for auth tests")
-                .url(AGENT_URL)
                 .version("1.0.0")
                 .capabilities(new AgentCapabilities.Builder()
                         .streaming(true)  // Support streaming for all tests
@@ -91,7 +90,7 @@ public void setUp() {
                         .tags(Collections.singletonList("test"))
                         .build()))
                 .protocolVersion("0.3.0")
-                .additionalInterfaces(java.util.Arrays.asList(
+                .supportedInterfaces(java.util.Arrays.asList(
                         new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL),
                         new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL),
                         new AgentInterface(TransportProtocol.GRPC.asString(), grpcServerName)))
diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java
index 1c7ed38a5..4c92307e1 100644
--- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java
+++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java
@@ -24,7 +24,7 @@ public class ClientBuilderTest {
     private AgentCard card = new AgentCard.Builder()
             .name("Hello World Agent")
                 .description("Just a hello world agent")
-                .url("http://localhost:9999")
+                .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999")))
                 .version("1.0.0")
                 .documentationUrl("http://example.com/docs")
                 .capabilities(new AgentCapabilities.Builder()
diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java
index 4faf40f6e..1f6dc19a4 100644
--- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java
+++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java
@@ -16,36 +16,32 @@
 import io.a2a.grpc.A2AServiceGrpc;
 import io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub;
 import io.a2a.grpc.A2AServiceGrpc.A2AServiceStub;
-import io.a2a.grpc.CancelTaskRequest;
-import io.a2a.grpc.CreateTaskPushNotificationConfigRequest;
-import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest;
-import io.a2a.grpc.GetTaskPushNotificationConfigRequest;
-import io.a2a.grpc.GetTaskRequest;
-import io.a2a.grpc.ListTaskPushNotificationConfigRequest;
-import io.a2a.grpc.ListTasksRequest;
-import io.a2a.grpc.SendMessageRequest;
-import io.a2a.grpc.SendMessageResponse;
-import io.a2a.grpc.StreamResponse;
-import io.a2a.grpc.TaskSubscriptionRequest;
 import io.a2a.grpc.utils.ProtoUtils.FromProto;
 import io.a2a.grpc.utils.ProtoUtils.ToProto;
 import io.a2a.spec.A2AClientException;
 import io.a2a.spec.AgentCard;
+import io.a2a.spec.CancelTaskRequest;
 import io.a2a.spec.DeleteTaskPushNotificationConfigParams;
+import io.a2a.spec.DeleteTaskPushNotificationConfigRequest;
 import io.a2a.spec.EventKind;
 import io.a2a.spec.GetTaskPushNotificationConfigParams;
+import io.a2a.spec.GetTaskPushNotificationConfigRequest;
+import io.a2a.spec.GetTaskRequest;
 import io.a2a.spec.ListTaskPushNotificationConfigParams;
+import io.a2a.spec.ListTaskPushNotificationConfigRequest;
 import io.a2a.spec.ListTasksParams;
+import io.a2a.spec.ListTasksRequest;
 import io.a2a.spec.ListTasksResult;
 import io.a2a.spec.MessageSendParams;
+import io.a2a.spec.SendMessageRequest;
 import io.a2a.spec.SendStreamingMessageRequest;
 import io.a2a.spec.SetTaskPushNotificationConfigRequest;
 import io.a2a.spec.StreamingEventKind;
+import io.a2a.spec.SubscribeToTaskRequest;
 import io.a2a.spec.Task;
 import io.a2a.spec.TaskIdParams;
 import io.a2a.spec.TaskPushNotificationConfig;
 import io.a2a.spec.TaskQueryParams;
-import io.a2a.spec.TaskResubscriptionRequest;
 import io.grpc.Channel;
 import io.grpc.Metadata;
 import io.grpc.StatusRuntimeException;
@@ -83,13 +79,13 @@ public GrpcTransport(Channel channel, AgentCard agentCard, @Nullable List errorConsumer, @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
         checkNotNullParam("eventConsumer", eventConsumer);
-        SendMessageRequest grpcRequest = createGrpcSendMessageRequest(request, context);
+        io.a2a.grpc.SendMessageRequest grpcRequest = createGrpcSendMessageRequest(request, context);
         PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD,
                 grpcRequest, agentCard, context);
-        StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer);
+        StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer);
 
         try {
             A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context, payloadAndHeaders);
@@ -124,11 +120,11 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations(
             @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
 
-        ListTaskPushNotificationConfigRequest grpcRequest = ListTaskPushNotificationConfigRequest.newBuilder()
+        io.a2a.grpc.ListTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder()
                 .setParent("tasks/" + request.id())
                 .build();
-        PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.ListTaskPushNotificationConfigRequest.METHOD,
+        PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD,
                 grpcRequest, agentCard, context);
 
         try {
@@ -271,10 +269,10 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC
                                                          @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
 
-        DeleteTaskPushNotificationConfigRequest grpcRequest = DeleteTaskPushNotificationConfigRequest.newBuilder()
+        io.a2a.grpc.DeleteTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder()
                 .setName(getTaskPushNotificationConfigName(request.id(), request.pushNotificationConfigId()))
                 .build();
-        PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.DeleteTaskPushNotificationConfigRequest.METHOD,
+        PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD,
                 grpcRequest, agentCard, context);
 
         try {
@@ -291,17 +289,17 @@ public void resubscribe(TaskIdParams request, Consumer event
         checkNotNullParam("request", request);
         checkNotNullParam("eventConsumer", eventConsumer);
 
-        TaskSubscriptionRequest grpcRequest = TaskSubscriptionRequest.newBuilder()
+        io.a2a.grpc.SubscribeToTaskRequest grpcRequest = io.a2a.grpc.SubscribeToTaskRequest.newBuilder()
                 .setName("tasks/" + request.id())
                 .build();
-        PayloadAndHeaders payloadAndHeaders = applyInterceptors(TaskResubscriptionRequest.METHOD,
+        PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD,
                 grpcRequest, agentCard, context);
 
-        StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer);
+        StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer);
 
         try {
             A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context, payloadAndHeaders);
-            stubWithMetadata.taskSubscription(grpcRequest, streamObserver);
+            stubWithMetadata.subscribeToTask(grpcRequest, streamObserver);
         } catch (StatusRuntimeException e) {
             throw GrpcErrorMapper.mapGrpcError(e, "Failed to resubscribe task push notification config: ");
         }
@@ -317,16 +315,8 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli
     public void close() {
     }
 
-    private SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messageSendParams, @Nullable ClientCallContext context) {
-        SendMessageRequest.Builder builder = SendMessageRequest.newBuilder();
-        builder.setRequest(ToProto.message(messageSendParams.message()));
-        if (messageSendParams.configuration() != null) {
-            builder.setConfiguration(ToProto.messageSendConfiguration(messageSendParams.configuration()));
-        }
-        if (messageSendParams.metadata() != null) {
-            builder.setMetadata(ToProto.struct(messageSendParams.metadata()));
-        }
-        return builder.build();
+    private io.a2a.grpc.SendMessageRequest createGrpcSendMessageRequest(MessageSendParams messageSendParams, @Nullable ClientCallContext context) {
+        return ToProto.sendMessageRequest(messageSendParams);
     }
 
     /**
diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml
index 278e4fb96..98cd051bf 100644
--- a/client/transport/jsonrpc/pom.xml
+++ b/client/transport/jsonrpc/pom.xml
@@ -33,6 +33,25 @@
             ${project.groupId}
             a2a-java-sdk-spec
         
+        
+            ${project.groupId}
+            a2a-java-sdk-spec-grpc
+        
+        
+            com.google.code.gson
+            gson
+            provided
+        
+        
+            com.google.protobuf
+            protobuf-java-util
+            provided
+        
+        
+            com.google.protobuf
+            protobuf-java
+            provided
+        
         
             org.junit.jupiter
             junit-jupiter-api
diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java
index 29274db08..005fafd62 100644
--- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java
+++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java
@@ -5,26 +5,31 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.type.TypeReference;
-
+import com.google.protobuf.MessageOrBuilder;
 import io.a2a.client.http.A2ACardResolver;
-import io.a2a.client.transport.spi.interceptors.ClientCallContext;
-import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor;
-import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders;
 import io.a2a.client.http.A2AHttpClient;
 import io.a2a.client.http.A2AHttpResponse;
 import io.a2a.client.http.JdkA2AHttpClient;
+import io.a2a.client.transport.jsonrpc.sse.SSEEventListener;
 import io.a2a.client.transport.spi.ClientTransport;
+import io.a2a.client.transport.spi.interceptors.ClientCallContext;
+import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor;
+import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders;
+import io.a2a.grpc.utils.JSONRPCUtils;
+import io.a2a.grpc.utils.ProtoUtils;
 import io.a2a.spec.A2AClientError;
 import io.a2a.spec.A2AClientException;
 import io.a2a.spec.AgentCard;
 import io.a2a.spec.CancelTaskRequest;
 import io.a2a.spec.CancelTaskResponse;
-
 import io.a2a.spec.DeleteTaskPushNotificationConfigParams;
+import io.a2a.spec.DeleteTaskPushNotificationConfigRequest;
+import io.a2a.spec.DeleteTaskPushNotificationConfigResponse;
 import io.a2a.spec.EventKind;
 import io.a2a.spec.GetAuthenticatedExtendedCardRequest;
 import io.a2a.spec.GetAuthenticatedExtendedCardResponse;
@@ -36,7 +41,6 @@
 import io.a2a.spec.JSONRPCError;
 import io.a2a.spec.JSONRPCMessage;
 import io.a2a.spec.JSONRPCResponse;
-
 import io.a2a.spec.ListTaskPushNotificationConfigParams;
 import io.a2a.spec.ListTaskPushNotificationConfigRequest;
 import io.a2a.spec.ListTaskPushNotificationConfigResponse;
@@ -44,8 +48,6 @@
 import io.a2a.spec.ListTasksRequest;
 import io.a2a.spec.ListTasksResponse;
 import io.a2a.spec.ListTasksResult;
-import io.a2a.spec.DeleteTaskPushNotificationConfigRequest;
-import io.a2a.spec.DeleteTaskPushNotificationConfigResponse;
 import io.a2a.spec.MessageSendParams;
 import io.a2a.spec.SendMessageRequest;
 import io.a2a.spec.SendMessageResponse;
@@ -53,30 +55,16 @@
 import io.a2a.spec.SetTaskPushNotificationConfigRequest;
 import io.a2a.spec.SetTaskPushNotificationConfigResponse;
 import io.a2a.spec.StreamingEventKind;
+import io.a2a.spec.SubscribeToTaskRequest;
 import io.a2a.spec.Task;
 import io.a2a.spec.TaskIdParams;
 import io.a2a.spec.TaskPushNotificationConfig;
 import io.a2a.spec.TaskQueryParams;
-import io.a2a.spec.TaskResubscriptionRequest;
-import io.a2a.client.transport.jsonrpc.sse.SSEEventListener;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicReference;
-
 import io.a2a.util.Utils;
 import org.jspecify.annotations.Nullable;
 
 public class JSONRPCTransport implements ClientTransport {
 
-    private static final TypeReference SEND_MESSAGE_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference GET_TASK_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference CANCEL_TASK_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference LIST_TASKS_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference GET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference SET_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE = new TypeReference<>() {};
-    private static final TypeReference GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE = new TypeReference<>() {};
-
     private final A2AHttpClient httpClient;
     private final String agentUrl;
     private final @Nullable List interceptors;
@@ -88,7 +76,7 @@ public JSONRPCTransport(String agentUrl) {
     }
 
     public JSONRPCTransport(AgentCard agentCard) {
-        this(null, agentCard, agentCard.url(), null);
+        this(null, agentCard, Utils.getFavoriteInterface(agentCard), null);
     }
 
     public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard agentCard,
@@ -103,18 +91,12 @@ public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard
     @Override
     public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
-        SendMessageRequest sendMessageRequest = new SendMessageRequest.Builder()
-                .jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
-                .method(SendMessageRequest.METHOD)
-                .params(request)
-                .build(); // id will be randomly generated
-
-        PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, sendMessageRequest,
+        PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, ProtoUtils.ToProto.sendMessageRequest(request),
                 agentCard, context);
 
         try {
-            String httpResponseBody = sendPostRequest(payloadAndHeaders);
-            SendMessageResponse response = unmarshalResponse(httpResponseBody, SEND_MESSAGE_RESPONSE_REFERENCE);
+            String httpResponseBody = sendPostRequest(payloadAndHeaders, SendMessageRequest.METHOD);
+            SendMessageResponse response = unmarshalResponse(httpResponseBody, SendMessageRequest.METHOD);
             return response.getResult();
         } catch (A2AClientException e) {
             throw e;
@@ -128,20 +110,14 @@ public void sendMessageStreaming(MessageSendParams request, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
         checkNotNullParam("eventConsumer", eventConsumer);
-        SendStreamingMessageRequest sendStreamingMessageRequest = new SendStreamingMessageRequest.Builder()
-                .jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
-                .method(SendStreamingMessageRequest.METHOD)
-                .params(request)
-                .build(); // id will be randomly generated
-
         PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD,
-                sendStreamingMessageRequest, agentCard, context);
+                ProtoUtils.ToProto.sendMessageRequest(request), agentCard, context);
 
         final AtomicReference> ref = new AtomicReference<>();
         SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer);
 
         try {
-            A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders);
+            A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders, SendStreamingMessageRequest.METHOD);
             ref.set(builder.postAsyncSSE(
                     msg -> sseEventListener.onMessage(msg, ref.get()),
                     throwable -> sseEventListener.onError(throwable, ref.get()),
@@ -159,18 +135,12 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations(
             ListTaskPushNotificationConfigParams request,
             @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
-        ListTaskPushNotificationConfigRequest listTaskPushNotificationRequest = new ListTaskPushNotificationConfigRequest.Builder()
-                .jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
-                .method(ListTaskPushNotificationConfigRequest.METHOD)
-                .params(request)
-                .build(); // id will be randomly generated
-
         PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD,
-                listTaskPushNotificationRequest, agentCard, context);
+                ProtoUtils.ToProto.listTaskPushNotificationConfigRequest(request), agentCard, context);
 
         try {
-            String httpResponseBody = sendPostRequest(payloadAndHeaders);
+            String httpResponseBody = sendPostRequest(payloadAndHeaders, ListTaskPushNotificationConfigRequest.METHOD);
             ListTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody,
-                    LIST_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE);
+                    ListTaskPushNotificationConfigRequest.METHOD);
             return response.getResult();
         } catch (A2AClientException e) {
             throw e;
@@ -303,18 +242,13 @@ public List listTaskPushNotificationConfigurations(
     public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request,
                                                          @Nullable ClientCallContext context) throws A2AClientException {
         checkNotNullParam("request", request);
-        DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationRequest = new DeleteTaskPushNotificationConfigRequest.Builder()
-                .jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
-                .method(DeleteTaskPushNotificationConfigRequest.METHOD)
-                .params(request)
-                .build(); // id will be randomly generated
-
         PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD,
-                deleteTaskPushNotificationRequest, agentCard, context);
+                ProtoUtils.ToProto.deleteTaskPushNotificationConfigRequest(request), agentCard, context);
 
         try {
-            String httpResponseBody = sendPostRequest(payloadAndHeaders);
-            unmarshalResponse(httpResponseBody, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_RESPONSE_REFERENCE);
+            String httpResponseBody = sendPostRequest(payloadAndHeaders,DeleteTaskPushNotificationConfigRequest.METHOD);
+            DeleteTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, DeleteTaskPushNotificationConfigRequest.METHOD);
+            // Response validated (no error), but no result to return
         } catch (A2AClientException e) {
             throw e;
         } catch (IOException | InterruptedException e) {
@@ -328,20 +262,14 @@ public void resubscribe(TaskIdParams request, Consumer event
         checkNotNullParam("request", request);
         checkNotNullParam("eventConsumer", eventConsumer);
         checkNotNullParam("errorConsumer", errorConsumer);
-        TaskResubscriptionRequest taskResubscriptionRequest = new TaskResubscriptionRequest.Builder()
-                .jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
-                .method(TaskResubscriptionRequest.METHOD)
-                .params(request)
-                .build(); // id will be randomly generated
-
-        PayloadAndHeaders payloadAndHeaders = applyInterceptors(TaskResubscriptionRequest.METHOD,
-                taskResubscriptionRequest, agentCard, context);
+        PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD,
+                ProtoUtils.ToProto.subscribeToTaskRequest(request), agentCard, context);
 
         AtomicReference> ref = new AtomicReference<>();
         SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer);
 
         try {
-            A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders);
+            A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders,SubscribeToTaskRequest.METHOD);
             ref.set(builder.postAsyncSSE(
                     msg -> sseEventListener.onMessage(msg, ref.get()),
                     throwable -> sseEventListener.onError(throwable, ref.get()),
@@ -375,12 +303,12 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli
                     .build(); // id will be randomly generated
 
             PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetAuthenticatedExtendedCardRequest.METHOD,
-                    getExtendedAgentCardRequest, agentCard, context);
+                    ProtoUtils.ToProto.extendedAgentCard(getExtendedAgentCardRequest), agentCard, context);
 
             try {
-                String httpResponseBody = sendPostRequest(payloadAndHeaders);
+                String httpResponseBody = sendPostRequest(payloadAndHeaders,GetAuthenticatedExtendedCardRequest.METHOD);
                 GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody,
-                        GET_AUTHENTICATED_EXTENDED_CARD_RESPONSE_REFERENCE);
+                        GetAuthenticatedExtendedCardRequest.METHOD);
                 agentCard = response.getResult();
                 needsExtendedCard = false;
                 return agentCard;
@@ -409,8 +337,8 @@ private PayloadAndHeaders applyInterceptors(String methodName, @Nullable Object
         return payloadAndHeaders;
     }
 
-    private String sendPostRequest(PayloadAndHeaders payloadAndHeaders) throws IOException, InterruptedException {
-        A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders);
+    private String sendPostRequest(PayloadAndHeaders payloadAndHeaders, String method) throws IOException, InterruptedException {
+        A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders,method);
         A2AHttpResponse response = builder.post();
         if (!response.success()) {
             throw new IOException("Request failed " + response.status());
@@ -418,11 +346,11 @@ private String sendPostRequest(PayloadAndHeaders payloadAndHeaders) throws IOExc
         return response.body();
     }
 
-    private A2AHttpClient.PostBuilder createPostBuilder(PayloadAndHeaders payloadAndHeaders) throws JsonProcessingException {
+    private A2AHttpClient.PostBuilder createPostBuilder(PayloadAndHeaders payloadAndHeaders, String method) throws JsonProcessingException {
         A2AHttpClient.PostBuilder postBuilder = httpClient.createPost()
                 .url(agentUrl)
                 .addHeader("Content-Type", "application/json")
-                .body(Utils.OBJECT_MAPPER.writeValueAsString(payloadAndHeaders.getPayload()));
+                .body(JSONRPCUtils.toJsonRPCRequest(null, method, (MessageOrBuilder) payloadAndHeaders.getPayload()));
 
         if (payloadAndHeaders.getHeaders() != null) {
             for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) {
@@ -433,14 +361,30 @@ private A2AHttpClient.PostBuilder createPostBuilder(PayloadAndHeaders payloadAnd
         return postBuilder;
     }
 
-    private > T unmarshalResponse(String response, TypeReference typeReference)
+    /**
+     * Unmarshals a JSON-RPC response string into a type-safe response object.
+     * 

+ * This method parses the JSON-RPC response body and returns the appropriate + * response type based on the method parameter. If the response contains an error, + * an A2AClientException is thrown. + * + * @param the expected response type, must extend JSONRPCResponse + * @param response the JSON-RPC response body as a string + * @param method the method name used to determine the response type + * @return the parsed response object of type T + * @throws A2AClientException if the response contains an error or parsing fails + * @throws JsonProcessingException if the JSON cannot be processed + */ + @SuppressWarnings("unchecked") + private > T unmarshalResponse(String response, String method) throws A2AClientException, JsonProcessingException { - T value = Utils.unmarshalFrom(response, typeReference); + JSONRPCResponse value = JSONRPCUtils.parseResponseBody(response, method); JSONRPCError error = value.getError(); if (error != null) { throw new A2AClientException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); } - return value; + // Safe cast: JSONRPCUtils.parseResponseBody returns the correct concrete type based on method + return (T) value; } private @Nullable Map getHttpHeaders(@Nullable ClientCallContext context) { diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index d6bae5b78..8cee952f1 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -1,7 +1,6 @@ package io.a2a.client.transport.jsonrpc.sse; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import io.a2a.spec.JSONRPCError; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskStatusUpdateEvent; @@ -10,8 +9,9 @@ import java.util.function.Consumer; import java.util.logging.Logger; -import static io.a2a.util.Utils.OBJECT_MAPPER; - +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.utils.JSONRPCUtils; +import io.a2a.grpc.utils.ProtoUtils; import org.jspecify.annotations.Nullable; public class SSEEventListener { @@ -29,11 +29,7 @@ public SSEEventListener(Consumer eventHandler, } public void onMessage(String message, @Nullable Future completableFuture) { - try { - handleMessage(OBJECT_MAPPER.readTree(message), completableFuture); - } catch (JsonProcessingException e) { - log.warning("Failed to parse JSON message: " + message); - } + handleMessage(message, completableFuture); } public void onError(Throwable throwable, @Nullable Future future) { @@ -63,25 +59,20 @@ public void onComplete() { } } - private void handleMessage(JsonNode jsonNode, @Nullable Future future) { + private void handleMessage(String message, @Nullable Future future) { try { - if (jsonNode.has("error")) { - JSONRPCError error = OBJECT_MAPPER.treeToValue(jsonNode.get("error"), JSONRPCError.class); - if (errorHandler != null) { - errorHandler.accept(error); - } - } else if (jsonNode.has("result")) { - // result can be a Task, Message, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent - JsonNode result = jsonNode.path("result"); - StreamingEventKind event = OBJECT_MAPPER.treeToValue(result, StreamingEventKind.class); - eventHandler.accept(event); - if (event instanceof TaskStatusUpdateEvent && ((TaskStatusUpdateEvent) event).isFinal()) { - if (future != null) { - future.cancel(true); // close SSE channel - } + StreamResponse response = JSONRPCUtils.parseResponseEvent(message); + + StreamingEventKind event = ProtoUtils.FromProto.streamingEventKind(response); + eventHandler.accept(event); + if (event instanceof TaskStatusUpdateEvent && ((TaskStatusUpdateEvent) event).isFinal()) { + if (future != null) { + future.cancel(true); // close SSE channel } - } else { - throw new IllegalArgumentException("Unknown message type"); + } + } catch (JSONRPCError error) { + if (errorHandler != null) { + errorHandler.accept(error); } } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 25de32947..dcfa47c2e 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -55,7 +55,7 @@ import io.a2a.spec.MessageSendParams; import io.a2a.spec.OpenIdConnectSecurityScheme; import io.a2a.spec.Part; -import io.a2a.spec.PushNotificationAuthenticationInfo; +import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SecurityScheme; import io.a2a.spec.Task; @@ -65,6 +65,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; +import io.a2a.util.Utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -212,7 +213,7 @@ public void testA2AClientSendMessageWithError() throws Exception { client.sendMessage(params, null); fail(); // should not reach here } catch (A2AClientException e) { - assertTrue(e.getMessage().contains("Invalid parameters: Hello world")); + assertTrue(e.getMessage().contains("Invalid parameters: \"Hello world\""),e.getMessage()); } } @@ -310,12 +311,12 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception { JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); TaskPushNotificationConfig taskPushNotificationConfig = client.getTaskPushNotificationConfiguration( - new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", null, + new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "c295ea44-7543-4f78-b524-7a38915ad6e4", new HashMap<>()), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); - PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); assertTrue(authenticationInfo.schemes().size() == 1); assertEquals("jwt", authenticationInfo.schemes().get(0)); } @@ -339,14 +340,15 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration( new TaskPushNotificationConfig("de38c76d-d54c-436c-8b9f-4c2703648d64", new PushNotificationConfig.Builder() + .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("https://example.com/callback") - .authenticationInfo(new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), + .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build()), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); - PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); assertEquals(1, authenticationInfo.schemes().size()); assertEquals("jwt", authenticationInfo.schemes().get(0)); } @@ -369,7 +371,7 @@ public void testA2AClientGetAgentCard() throws Exception { AgentCard agentCard = client.getAgentCard(null); assertEquals("GeoSpatial Route Planner Agent", agentCard.name()); assertEquals("Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", agentCard.description()); - assertEquals("https://georoute-agent.example.com/a2a/v1", agentCard.url()); + assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard)); assertEquals("Example Geo Services Inc.", agentCard.provider().organization()); assertEquals("https://www.examplegeoservices.com", agentCard.provider().url()); assertEquals("1.2.0", agentCard.version()); @@ -419,8 +421,8 @@ public void testA2AClientGetAgentCard() throws Exception { assertFalse(agentCard.supportsAuthenticatedExtendedCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); assertEquals("0.2.9", agentCard.protocolVersion()); - assertEquals("JSONRPC", agentCard.preferredTransport()); - List additionalInterfaces = agentCard.additionalInterfaces(); + assertEquals("JSONRPC", agentCard.supportedInterfaces().get(0).protocolBinding()); + List additionalInterfaces = agentCard.supportedInterfaces(); assertEquals(3, additionalInterfaces.size()); AgentInterface jsonrpc = new AgentInterface(TransportProtocol.JSONRPC.asString(), "https://georoute-agent.example.com/a2a/v1"); AgentInterface grpc = new AgentInterface(TransportProtocol.GRPC.asString(), "https://georoute-agent.example.com/a2a/grpc"); @@ -458,7 +460,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { AgentCard agentCard = client.getAgentCard(null); assertEquals("GeoSpatial Route Planner Agent Extended", agentCard.name()); assertEquals("Extended description", agentCard.description()); - assertEquals("https://georoute-agent.example.com/a2a/v1", agentCard.url()); + assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard)); assertEquals("Example Geo Services Inc.", agentCard.provider().organization()); assertEquals("https://www.examplegeoservices.com", agentCard.provider().url()); assertEquals("1.2.0", agentCard.version()); @@ -562,7 +564,9 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); assertEquals("This is an image of a cat sitting on a windowsill.", ((TextPart) part).getText()); - assertTrue(task.getMetadata().isEmpty()); + assertFalse(task.getMetadata().isEmpty()); + assertEquals(1, task.getMetadata().size()); + assertEquals("metadata-test", task.getMetadata().get("test")); } @Test diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index 59838012c..912d2a598 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -11,12 +11,10 @@ public class JsonMessages { "protocolVersion": "0.2.9", "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", - "preferredTransport": "JSONRPC", - "additionalInterfaces" : [ - {"url": "https://georoute-agent.example.com/a2a/v1", "transport": "JSONRPC"}, - {"url": "https://georoute-agent.example.com/a2a/grpc", "transport": "GRPC"}, - {"url": "https://georoute-agent.example.com/a2a/json", "transport": "HTTP+JSON"} + "supportedInterfaces" : [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"}, + {"url": "https://georoute-agent.example.com/a2a/grpc", "protocolBinding": "GRPC"}, + {"url": "https://georoute-agent.example.com/a2a/json", "protocolBinding": "HTTP+JSON"} ], "provider": { "organization": "Example Geo Services Inc.", @@ -32,11 +30,12 @@ public class JsonMessages { }, "securitySchemes": { "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + "openIdConnectSecurityScheme": { + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -87,7 +86,9 @@ public class JsonMessages { { "name": "GeoSpatial Route Planner Agent Extended", "description": "Extended description", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -102,11 +103,12 @@ public class JsonMessages { }, "securitySchemes": { "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + "openIdConnectSecurityScheme": { + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -161,124 +163,138 @@ public class JsonMessages { }"""; static final String SEND_MESSAGE_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" + { + "jsonrpc":"2.0", + "method":"SendMessage", + "params":{ + "message":{ + "messageId":"message-1234", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ + { + "text":"tell me a joke" + } + ], + "metadata":{ + + } } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } + } }"""; static final String SEND_MESSAGE_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "joke", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ] - } - ], - "metadata": {}, - "kind": "task" - } + { + "jsonrpc":"2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "result":{ + "task":{ + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "status":{ + "state":"TASK_STATE_COMPLETED" + }, + "artifacts":[ + { + "artifactId":"artifact-1", + "name":"joke", + "parts":[ + { + "text":"Why did the chicken cross the road? To get to the other side!" + } + ] + } + ], + "metadata":{ + + } + } + } }"""; static final String SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ + { + "jsonrpc":"2.0", + "method":"SendMessage", + "params":{ + "message":{ + "messageId":"message-1234", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ { - "kind": "text", - "text": "tell me a joke" + "text":"tell me a joke" } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } - }"""; + ], + "metadata":{ + } + }, + "configuration":{ + "acceptedOutputModes":[ + "text" + ], + "blocking":true + }, + "metadata":{ + } + } + }"""; static final String SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE = """ { - "jsonrpc": "2.0", - "id": 1, - "result": { - "role": "agent", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ], - "messageId": "msg-456", - "kind": "message" - } + "jsonrpc":"2.0", + "id":1, + "result":{ + "message": { + "messageId":"msg-456", + "contextId":"context-1234", + "role":"ROLE_AGENT", + "parts":[ + { + "text":"Why did the chicken cross the road? To get to the other side!" + } + ], + "metadata":{ + } + } + } }"""; static final String SEND_MESSAGE_WITH_ERROR_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" + "jsonrpc":"2.0", + "method":"SendMessage", + "params":{ + "message":{ + "messageId":"message-1234", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ + { + "text":"tell me a joke" + } + ], + "metadata":{ + + } + }, + "configuration":{ + "acceptedOutputModes":[ + "text" + ], + "blocking":true + }, + "metadata":{ + } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } + } }"""; static final String SEND_MESSAGE_ERROR_TEST_RESPONSE = """ { "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "error": { "code": -32702, "message": "Invalid parameters", @@ -288,110 +304,107 @@ public class JsonMessages { static final String GET_TASK_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "tasks/get", - "params": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "historyLength": 10 - } + "jsonrpc":"2.0", + "method":"GetTask", + "params":{ + "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength":10 + } } """; static final String GET_TASK_TEST_RESPONSE = """ { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ] - } - ], - "history": [ - { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - }, - { - "kind": "file", - "file": { - "uri": "file:///path/to/file.txt", - "mimeType": "text/plain" + "jsonrpc":"2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "result":{ + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "status":{ + "state":"TASK_STATE_COMPLETED" + }, + "artifacts":[ + { + "artifactId":"artifact-1", + "parts":[ + { + "text":"Why did the chicken cross the road? To get to the other side!" + } + ] } - }, - { - "kind": "file", - "file": { - "bytes": "aGVsbG8=", - "name": "hello.txt" + ], + "history":[ + { + "role":"ROLE_USER", + "parts":[ + { + "text":"tell me a joke" + }, + { + "file":{ + "file_with_uri":"file:///path/to/file.txt", + "mediaType":"text/plain" + } + }, + { + "file":{ + "file_with_bytes":"aGVsbG8=", + "name":"hello.txt" + } + } + ], + "messageId":"message-123" } - } ], - "messageId": "message-123", - "kind": "message" - } - ], - "metadata": {}, - "kind": "task" - } + "metadata":{ + + } + } } """; static final String CANCEL_TASK_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "tasks/cancel", - "params": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "metadata": {} - } + "jsonrpc":"2.0", + "method":"CancelTask", + "params":{ + "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + } } """; static final String CANCEL_TASK_TEST_RESPONSE = """ { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "canceled" - }, - "metadata": {}, - "kind" : "task" - } + "jsonrpc":"2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "result":{ + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "status":{ + "state":"TASK_STATE_CANCELLED" + }, + "metadata":{ + + } + } } """; static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "tasks/pushNotificationConfig/get", - "params": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "metadata": {}, - } - } - """; + "jsonrpc":"2.0", + "method":"GetTaskPushNotificationConfig", + "params":{ + "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4" + } + }"""; static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ { "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "result": { - "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { @@ -403,25 +416,32 @@ public class JsonMessages { """; static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "tasks/pushNotificationConfig/set", - "params": { - "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "pushNotificationConfig": { - "url": "https://example.com/callback", - "authentication": { - "schemes": ["jwt"] - } + { + "jsonrpc":"2.0", + "method":"SetTaskPushNotificationConfig", + "params":{ + "parent":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "configId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "config":{ + "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4", + "pushNotificationConfig":{ + "url":"https://example.com/callback", + "authentication":{ + "schemes":[ + "jwt" + ] + } + } + } } - } }"""; static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ { "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "result": { - "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { @@ -433,191 +453,218 @@ public class JsonMessages { """; static final String SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "analyze this image" - }, - { - "kind": "file", - "file": { - "uri": "file:///path/to/image.jpg", - "mimeType": "image/jpeg" + { + "jsonrpc":"2.0", + "method":"SendMessage", + "params":{ + "message":{ + "messageId":"message-1234-with-file", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ + { + "text":"analyze this image" + }, + { + "file":{ + "fileWithUri":"file:///path/to/image.jpg", + "mediaType":"image/jpeg" + } + } + ], + "metadata":{ + + } + }, + "configuration":{ + "acceptedOutputModes":[ + "text" + ], + "blocking":true + }, + "metadata":{ + } - } - ], - "messageId": "message-1234-with-file", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; + } + }"""; static final String SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE = """ { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "image-analysis", - "parts": [ - { - "kind": "text", - "text": "This is an image of a cat sitting on a windowsill." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } + "jsonrpc":"2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "result":{ + "task":{ + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "status":{ + "state":"TASK_STATE_COMPLETED" + }, + "artifacts":[ + { + "artifactId":"artifact-1", + "name":"image-analysis", + "parts":[ + { + "text":"This is an image of a cat sitting on a windowsill." + } + ] + } + ], + "metadata":{ + "test":"metadata-test" + } + } + } }"""; static final String SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "process this data" + "jsonrpc":"2.0", + "method":"SendMessage", + "params":{ + "message":{ + "messageId":"message-1234-with-data", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ + { + "text":"process this data" + }, + { + "data":{ + "data":{ + "temperature":25.5, + "humidity":60.2, + "location":"San Francisco", + "timestamp":"2024-01-15T10:30:00Z" + } + } + } + ], + "metadata":{ + + } }, - { - "kind": "data", - "data": { - "temperature": 25.5, - "humidity": 60.2, - "location": "San Francisco", - "timestamp": "2024-01-15T10:30:00Z" - } + "configuration":{ + "acceptedOutputModes":[ + "text" + ], + "blocking":true + }, + "metadata":{ + } - ], - "messageId": "message-1234-with-data", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true } - } }"""; static final String SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE = """ { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "data-analysis", - "parts": [ - { - "kind": "text", - "text": "Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } + "jsonrpc":"2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "result":{ + "task":{ + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "status":{ + "state":"TASK_STATE_COMPLETED" + }, + "artifacts":[ + { + "artifactId":"artifact-1", + "name":"data-analysis", + "parts":[ + { + "text":"Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." + } + ] + } + ], + "metadata":{ + + } + } + } }"""; static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "analyze this data and image" + "jsonrpc":"2.0", + "method":"SendMessage", + "params":{ + "message":{ + "messageId":"message-1234-with-mixed", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ + { + "text":"analyze this data and image" + }, + { + "file":{ + "fileWithBytes":"aGVsbG8=", + "mediaType":"image/png", + "name":"chart.png" + } + }, + { + "data":{ + "data":{ + "chartType":"bar", + "dataPoints":[10.0, 20.0, 30.0, 40.0], + "labels":["Q1", "Q2", "Q3", "Q4"] + } + } + } + ], + "metadata":{ + + } }, - { - "kind": "file", - "file": { - "bytes": "aGVsbG8=", - "name": "chart.png", - "mimeType": "image/png" - } + "configuration":{ + "acceptedOutputModes":[ + "text" + ], + "blocking":true }, - { - "kind": "data", - "data": { - "chartType": "bar", - "dataPoints": [10, 20, 30, 40], - "labels": ["Q1", "Q2", "Q3", "Q4"] - } + "metadata":{ + } - ], - "messageId": "message-1234-with-mixed", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true } - } }"""; static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "mixed-analysis", - "parts": [ - { - "kind": "text", - "text": "Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } + { + "jsonrpc":"2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "result":{ + "task":{ + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", + "status":{ + "state":"TASK_STATE_COMPLETED" + }, + "artifacts":[ + { + "artifactId":"artifact-1", + "name":"mixed-analysis", + "parts":[ + { + "text":"Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." + } + ] + } + ], + "metadata":{ + + } + } + } }"""; static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST = """ { "jsonrpc": "2.0", - "method": "agent/getAuthenticatedExtendedCard" + "method": "GetExtendedAgentCard" } """; @@ -628,7 +675,9 @@ public class JsonMessages { "result": { "name": "GeoSpatial Route Planner Agent Extended", "description": "Extended description", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -643,11 +692,12 @@ public class JsonMessages { }, "securitySchemes": { "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + "openIdConnectSecurityScheme": { + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -706,7 +756,9 @@ public class JsonMessages { { "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -721,11 +773,12 @@ public class JsonMessages { }, "securitySchemes": { "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + "openIdConnectSecurityScheme": { + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -764,6 +817,6 @@ public class JsonMessages { } ], "supportsAuthenticatedExtendedCard": true, - "protocolVersion": "0.2.5" + "protocolVersion": "1.0.0" }"""; } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java index 909955e81..021c6c7f6 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java @@ -10,11 +10,12 @@ public class JsonStreamingMessages { "jsonrpc": "2.0", "id": "1234", "result": { - "kind": "task", - "id": "task-123", - "contextId": "context-456", - "status": { - "state": "working" + "task": { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "TASK_STATE_WORKING" + } } } } @@ -26,16 +27,16 @@ public class JsonStreamingMessages { "jsonrpc": "2.0", "id": "1234", "result": { - "kind": "message", - "role": "agent", - "messageId": "msg-123", - "contextId": "context-456", - "parts": [ - { - "kind": "text", - "text": "Hello, world!" - } - ] + "message": { + "role": "ROLE_AGENT", + "messageId": "msg-123", + "contextId": "context-456", + "parts": [ + { + "text": "Hello, world!" + } + ] + } } }"""; @@ -44,13 +45,14 @@ public class JsonStreamingMessages { "jsonrpc": "2.0", "id": "1234", "result": { - "taskId": "1", - "contextId": "2", - "status": { - "state": "submitted" - }, - "final": false, - "kind": "status-update" + "statusUpdate": { + "taskId": "1", + "contextId": "2", + "status": { + "state": "TASK_STATE_SUBMITTED" + }, + "final": false + } } }"""; @@ -59,13 +61,14 @@ public class JsonStreamingMessages { "jsonrpc": "2.0", "id": "1234", "result": { - "taskId": "1", - "contextId": "2", - "status": { - "state": "completed" - }, - "final": true, - "kind": "status-update" + "statusUpdate": { + "taskId": "1", + "contextId": "2", + "status": { + "state": "TASK_STATE_COMPLETED" + }, + "final": true + } } }"""; @@ -74,22 +77,21 @@ public class JsonStreamingMessages { "jsonrpc": "2.0", "id": "1234", "result": { - "kind": "artifact-update", - "taskId": "1", - "contextId": "2", - "append": false, - "lastChunk": true, - "artifact": { + "artifactUpdate": { + "taskId": "1", + "contextId": "2", + "append": false, + "lastChunk": true, + "artifact": { "artifactId": "artifact-1", "parts": [ - { - "kind": "text", + { "text": "Why did the chicken cross the road? To get to the other side!" - } + } ] + } } } - } }"""; public static final String STREAMING_ERROR_EVENT = """ @@ -105,42 +107,53 @@ public class JsonStreamingMessages { public static final String SEND_MESSAGE_STREAMING_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "message/stream", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me some jokes" + "jsonrpc":"2.0", + "method":"SendStreamingMessage", + "params":{ + "message":{ + "messageId":"message-1234", + "contextId":"context-1234", + "role":"ROLE_USER", + "parts":[ + { + "text":"tell me some jokes" + } + ], + "metadata":{ + + } + }, + "configuration":{ + "acceptedOutputModes":[ + "text" + ] + }, + "metadata":{ + } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": false - }, - } + } }"""; static final String SEND_MESSAGE_STREAMING_TEST_RESPONSE = - "event: message\n" + - "data: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"completed\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"kind\":\"text\",\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{},\"kind\":\"task\"}}\n\n"; + """ + event: message + data: {"jsonrpc":"2.0","id":1,"result":{"task":{"id":"2","contextId":"context-1234","status":{"state":"TASK_STATE_COMPLETED"},"artifacts":[{"artifactId":"artifact-1","name":"joke","parts":[{"text":"Why did the chicken cross the road? To get to the other side!"}]}],"metadata":{}}}} + + """; static final String TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE = - "event: message\n" + - "data: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"id\":\"2\",\"contextId\":\"context-1234\",\"status\":{\"state\":\"completed\"},\"artifacts\":[{\"artifactId\":\"artifact-1\",\"name\":\"joke\",\"parts\":[{\"kind\":\"text\",\"text\":\"Why did the chicken cross the road? To get to the other side!\"}]}],\"metadata\":{},\"kind\":\"task\"}}\n\n"; + """ + event: message + data: {"jsonrpc":"2.0","id":1,"result":{"task":{"id":"2","contextId":"context-1234","status":{"state":"TASK_STATE_COMPLETED"},"artifacts":[{"artifactId":"artifact-1","name":"joke","parts":[{"text":"Why did the chicken cross the road? To get to the other side!"}]}],"metadata":{}}}} + + """; public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ { - "jsonrpc": "2.0", - "method": "tasks/resubscribe", - "params": { - "id": "task-1234" - } + "jsonrpc":"2.0", + "method":"SubscribeToTask", + "params":{ + "name":"tasks/task-1234" + } }"""; } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 8c4c1495e..da87cb985 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -162,7 +162,7 @@ public void testOnEventWithError() throws Exception { JSONRPCError jsonrpcError = (JSONRPCError) receivedError.get(); assertEquals(-32602, jsonrpcError.getCode()); assertEquals("Invalid parameters", jsonrpcError.getMessage()); - assertEquals("Missing required field", jsonrpcError.getData()); + assertEquals("\"Missing required field\"", jsonrpcError.getData()); } @Test diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index fa2875e9f..32fd26133 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -2,6 +2,17 @@ import static io.a2a.util.Assert.checkNotNullParam; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.logging.Logger; + import com.fasterxml.jackson.core.JsonProcessingException; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageOrBuilder; @@ -15,41 +26,33 @@ import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; -import io.a2a.grpc.CancelTaskRequest; -import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskRequest; -import io.a2a.grpc.ListTaskPushNotificationConfigRequest; -import io.a2a.grpc.ListTasksRequest; -import io.a2a.spec.TaskPushNotificationConfig; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskRequest; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksRequest; import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; -import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.spec.A2AClientError; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.util.Utils; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; import org.jspecify.annotations.Nullable; public class RestTransport implements ClientTransport { @@ -62,7 +65,7 @@ public class RestTransport implements ClientTransport { private boolean needsExtendedCard = false; public RestTransport(AgentCard agentCard) { - this(null, agentCard, agentCard.url(), null); + this(null, agentCard, Utils.getFavoriteInterface(agentCard), null); } public RestTransport(@Nullable A2AHttpClient httpClient, AgentCard agentCard, @@ -77,7 +80,7 @@ public RestTransport(@Nullable A2AHttpClient httpClient, AgentCard agentCard, public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("messageSendParams", messageSendParams); io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.SendMessageRequest.METHOD, builder, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, builder, agentCard, context); try { String httpResponseBody = sendPostRequest(agentUrl + "/v1/message:send", payloadAndHeaders); io.a2a.grpc.SendMessageResponse.Builder responseBuilder = io.a2a.grpc.SendMessageResponse.newBuilder(); @@ -124,9 +127,9 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); - ListTaskPushNotificationConfigRequest.Builder builder = ListTaskPushNotificationConfigRequest.newBuilder(); + io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder = + io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder(); builder.setParent(String.format("/tasks/%1s/pushNotificationConfigs", request.id())); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.ListTaskPushNotificationConfigRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs", request.id()); @@ -345,7 +351,7 @@ public List listTaskPushNotificationConfigurations(L public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.DeleteTaskPushNotificationConfigRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); @@ -370,9 +376,9 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC public void resubscribe(TaskIdParams request, Consumer eventConsumer, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); - io.a2a.grpc.TaskSubscriptionRequest.Builder builder = io.a2a.grpc.TaskSubscriptionRequest.newBuilder(); + io.a2a.grpc.SubscribeToTaskRequest.Builder builder = io.a2a.grpc.SubscribeToTaskRequest.newBuilder(); builder.setName("tasks/" + request.id()); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.TaskResubscriptionRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD, builder, agentCard, context); AtomicReference> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); @@ -404,7 +410,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli if (!needsExtendedCard) { return agentCard; } - PayloadAndHeaders payloadAndHeaders = applyInterceptors(io.a2a.spec.GetTaskRequest.METHOD, null, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, null, agentCard, context); String url = agentUrl + String.format("/v1/card"); A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java index 8842fe2b8..28e59e6e8 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java @@ -88,13 +88,13 @@ public class JsonRestMessages { { "file": { "file_with_uri": "file:///path/to/file.txt", - "mimeType": "text/plain" + "mediaType": "text/plain" } }, { "file": { "file_with_bytes": "aGVsbG8=", - "mimeType": "text/plain" + "mediaType": "text/plain" } } ], @@ -109,7 +109,9 @@ public class JsonRestMessages { { "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "HTTP+JSON"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -174,7 +176,9 @@ public class JsonRestMessages { { "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "HTTP+JSON"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -239,7 +243,9 @@ public class JsonRestMessages { { "name": "GeoSpatial Route Planner Agent Extended", "description": "Extended description", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "HTTP+JSON"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -306,81 +312,6 @@ public class JsonRestMessages { "protocolVersion": "0.2.5" }"""; - static final String SEND_MESSAGE_TEST_REQUEST_WITH_MESSAGE_RESPONSE = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } - }"""; - - static final String SEND_MESSAGE_TEST_RESPONSE_WITH_MESSAGE_RESPONSE = """ - { - "jsonrpc": "2.0", - "id": 1, - "result": { - "role": "agent", - "parts": [ - { - "kind": "text", - "text": "Why did the chicken cross the road? To get to the other side!" - } - ], - "messageId": "msg-456", - "kind": "message" - } - }"""; - - static final String SEND_MESSAGE_WITH_ERROR_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "tell me a joke" - } - ], - "messageId": "message-1234", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - }, - } - }"""; - - static final String SEND_MESSAGE_ERROR_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "error": { - "code": -32702, - "message": "Invalid parameters", - "data": "Hello world" - } - }"""; - static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ { "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", @@ -416,8 +347,9 @@ public class JsonRestMessages { static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ { "parent": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "configId": "default-config-id", "config": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs", + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/default-config-id", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { @@ -438,187 +370,6 @@ public class JsonRestMessages { } }"""; - static final String SEND_MESSAGE_WITH_FILE_PART_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "analyze this image" - }, - { - "kind": "file", - "file": { - "uri": "file:///path/to/image.jpg", - "mimeType": "image/jpeg" - } - } - ], - "messageId": "message-1234-with-file", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; - - static final String SEND_MESSAGE_WITH_FILE_PART_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "image-analysis", - "parts": [ - { - "kind": "text", - "text": "This is an image of a cat sitting on a windowsill." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; - - static final String SEND_MESSAGE_WITH_DATA_PART_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "process this data" - }, - { - "kind": "data", - "data": { - "temperature": 25.5, - "humidity": 60.2, - "location": "San Francisco", - "timestamp": "2024-01-15T10:30:00Z" - } - } - ], - "messageId": "message-1234-with-data", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; - - static final String SEND_MESSAGE_WITH_DATA_PART_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "data-analysis", - "parts": [ - { - "kind": "text", - "text": "Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; - - static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_REQUEST = """ - { - "jsonrpc": "2.0", - "method": "message/send", - "params": { - "message": { - "role": "user", - "parts": [ - { - "kind": "text", - "text": "analyze this data and image" - }, - { - "kind": "file", - "file": { - "bytes": "aGVsbG8=", - "name": "chart.png", - "mimeType": "image/png" - } - }, - { - "kind": "data", - "data": { - "chartType": "bar", - "dataPoints": [10, 20, 30, 40], - "labels": ["Q1", "Q2", "Q3", "Q4"] - } - } - ], - "messageId": "message-1234-with-mixed", - "contextId": "context-1234", - "kind": "message" - }, - "configuration": { - "acceptedOutputModes": ["text"], - "blocking": true - } - } - }"""; - - static final String SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE = """ - { - "jsonrpc": "2.0", - "result": { - "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", - "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", - "status": { - "state": "completed" - }, - "artifacts": [ - { - "artifactId": "artifact-1", - "name": "mixed-analysis", - "parts": [ - { - "kind": "text", - "text": "Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40]." - } - ] - } - ], - "metadata": {}, - "kind": "task" - } - }"""; public static final String SEND_MESSAGE_STREAMING_TEST_REQUEST = """ { @@ -646,7 +397,7 @@ public class JsonRestMessages { public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ { "jsonrpc": "2.0", - "method": "tasks/resubscribe", + "method": "SubscribeToTask", "params": { "id": "task-1234" } diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index a296553c3..a1c79d9af 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -38,7 +38,7 @@ import io.a2a.spec.MessageSendParams; import io.a2a.spec.Part; import io.a2a.spec.Part.Kind; -import io.a2a.spec.PushNotificationAuthenticationInfo; +import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -70,7 +70,7 @@ public class RestTransportTest { private static final AgentCard CARD = new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") - .url("http://localhost:4001") + .supportedInterfaces(Collections.singletonList(new io.a2a.spec.AgentInterface("HTTP+JSON", "http://localhost:4001"))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() @@ -149,7 +149,7 @@ public void testSendMessage() throws Exception { assertEquals(1, history.getParts().size()); assertEquals(Kind.TEXT, history.getParts().get(0).getKind()); assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).getText()); - assertNull(history.getMetadata()); + assertNull(task.getMetadata()); assertNull(history.getReferenceTaskIds()); } @@ -176,7 +176,8 @@ public void testCancelTask() throws Exception { assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); assertEquals(TaskState.CANCELED, task.getStatus().state()); assertNull(task.getStatus().message()); - assertNull(task.getMetadata()); + assertNotNull(task.getMetadata()); + assertTrue(task.getMetadata().isEmpty()); } /** @@ -201,12 +202,13 @@ public void testGetTask() throws Exception { assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); assertEquals(TaskState.COMPLETED, task.getStatus().state()); assertNull(task.getStatus().message()); - assertNull(task.getMetadata()); + assertNotNull(task.getMetadata()); + assertTrue(task.getMetadata().isEmpty()); assertEquals(false, task.getArtifacts().isEmpty()); assertEquals(1, task.getArtifacts().size()); Artifact artifact = task.getArtifacts().get(0); assertEquals("artifact-1", artifact.artifactId()); - assertEquals("", artifact.name()); + assertNull(artifact.name()); assertEquals(false, artifact.parts().isEmpty()); assertEquals(Kind.TEXT, artifact.parts().get(0).getKind()); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).getText()); @@ -225,7 +227,7 @@ public void testGetTask() throws Exception { part = (FilePart) history.getParts().get(2); assertEquals(Kind.FILE, part.getKind()); assertEquals("text/plain", part.getFile().mimeType()); - assertEquals("hello", ((FileWithBytes) part.getFile()).bytes()); + assertEquals("aGVsbG8=", ((FileWithBytes) part.getFile()).bytes()); assertNull(history.getMetadata()); assertNull(history.getReferenceTaskIds()); } @@ -302,15 +304,16 @@ public void testSetTaskPushNotificationConfiguration() throws Exception { TaskPushNotificationConfig pushedConfig = new TaskPushNotificationConfig( "de38c76d-d54c-436c-8b9f-4c2703648d64", new PushNotificationConfig.Builder() + .id("default-config-id") .url("https://example.com/callback") .authenticationInfo( - new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), null)) + new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build()); TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration(pushedConfig, null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); - PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); assertEquals(1, authenticationInfo.schemes().size()); assertEquals("jwt", authenticationInfo.schemes().get(0)); } @@ -338,7 +341,7 @@ public void testGetTaskPushNotificationConfiguration() throws Exception { PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); - PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); assertTrue(authenticationInfo.schemes().size() == 1); assertEquals("jwt", authenticationInfo.schemes().get(0)); } @@ -367,7 +370,7 @@ public void testListTaskPushNotificationConfigurations() throws Exception { assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); assertEquals("10", pushNotificationConfig.id()); - PushNotificationAuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); + AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); assertTrue(authenticationInfo.schemes().size() == 1); assertEquals("jwt", authenticationInfo.schemes().get(0)); assertEquals("", authenticationInfo.credentials()); diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java index 53319c4b5..e8807c594 100644 --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java @@ -13,6 +13,7 @@ import io.a2a.spec.APIKeySecurityScheme; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.HTTPAuthSecurityScheme; import io.a2a.spec.OAuth2SecurityScheme; import io.a2a.spec.OAuthFlows; @@ -79,7 +80,7 @@ public void testAPIKeySecurityScheme() { "session-id", APIKeySecurityScheme.API_KEY, "secret-api-key", - new APIKeySecurityScheme("header", "x-api-key", "API Key authentication"), + new APIKeySecurityScheme(APIKeySecurityScheme.Location.HEADER, "x-api-key", "API Key authentication"), "x-api-key", "secret-api-key" ); @@ -137,7 +138,7 @@ private void testSecurityScheme(AuthTestCase authTestCase) { ClientCallContext context = new ClientCallContext(Map.of("sessionId", authTestCase.sessionId), Map.of()); PayloadAndHeaders result = authInterceptor.intercept( - "message/send", + "SendMessage", requestPayload, headers, agentCard, @@ -154,7 +155,7 @@ void testAuthInterceptorWithoutAgentCard() { Map headers = Map.of("foo", "bar"); PayloadAndHeaders result = authInterceptor.intercept( - "message/send", + "SendMessage", requestPayload, headers, null, // no agent card @@ -206,7 +207,7 @@ void testCustomInterceptor() { Map headers = Map.of(); PayloadAndHeaders result = interceptor.intercept( - "message/send", + "SendMessage", payload, headers, null, @@ -229,7 +230,7 @@ void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() { AgentCard agentCard = new AgentCard.Builder() .name("missing") .description("Uses missing scheme definition") - .url("http://agent.com/rpc") + .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc"))) .version("1.0") .capabilities(new AgentCapabilities.Builder().build()) .defaultInputModes(List.of("text")) @@ -244,7 +245,7 @@ void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() { ClientCallContext context = new ClientCallContext(Map.of("sessionId", sessionId), Map.of()); PayloadAndHeaders result = authInterceptor.intercept( - "message/send", + "SendMessage", requestPayload, headers, agentCard, @@ -258,7 +259,7 @@ void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() { @Test void testNoCredentialAvailable() { String schemeName = "apikey"; - SecurityScheme securityScheme = new APIKeySecurityScheme("header", "X-API-Key", "API Key authentication"); + SecurityScheme securityScheme = new APIKeySecurityScheme(APIKeySecurityScheme.Location.HEADER, "X-API-Key", "API Key authentication"); AgentCard agentCard = createAgentCard(schemeName, securityScheme); Map requestPayload = Map.of("test", "payload"); @@ -266,7 +267,7 @@ void testNoCredentialAvailable() { ClientCallContext context = new ClientCallContext(Map.of("sessionId", "session-id"), Map.of()); PayloadAndHeaders result = authInterceptor.intercept( - "message/send", + "SendMessage", requestPayload, headers, agentCard, @@ -283,7 +284,7 @@ void testNoAgentCardSecuritySpecified() { AgentCard agentCard = new AgentCard.Builder() .name("nosecuritybot") .description("A bot with no security requirements") - .url("http://agent.com/rpc") + .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc"))) .version("1.0") .capabilities(new AgentCapabilities.Builder().build()) .defaultInputModes(List.of("text")) @@ -297,7 +298,7 @@ void testNoAgentCardSecuritySpecified() { ClientCallContext context = new ClientCallContext(Map.of("sessionId", "session-id"), Map.of()); PayloadAndHeaders result = authInterceptor.intercept( - "message/send", + "SendMessage", requestPayload, headers, agentCard, @@ -315,7 +316,7 @@ private AgentCard createAgentCard(String schemeName, SecurityScheme securitySche return new AgentCard.Builder() .name(schemeName + "bot") .description("A bot that uses " + schemeName) - .url("http://agent.com/rpc") + .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc"))) .version("1.0") .capabilities(new AgentCapabilities.Builder().build()) .defaultInputModes(List.of("text")) diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java index 2894c84a9..acca34fd8 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -3,6 +3,7 @@ import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; @@ -30,7 +31,8 @@ public AgentCard agentCard() { .name("Cloud Deployment Demo Agent") .description("Demonstrates A2A multi-pod deployment with Kafka event replication, " + "PostgreSQL persistence, and round-robin load balancing across Kubernetes pods") - .url(agentUrl) + .supportedInterfaces(Collections.singletonList( + new AgentInterface("JSONRPC", agentUrl))) .version("1.0.0") .capabilities(new AgentCapabilities.Builder() .streaming(true) diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java index d84d24896..c02549a67 100644 --- a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -20,11 +20,13 @@ import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.Message; import io.a2a.spec.Part; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TextPart; +import io.a2a.spec.TransportProtocol; /** * Test client demonstrating multi-pod A2A agent deployment with modernized message protocol. @@ -106,7 +108,10 @@ private AgentCard fetchAndConfigureAgentCard() { // Override agent card URL to use the port-forwarded URL instead of internal K8s service URL AgentCard agentCard = new AgentCard.Builder(fetchedCard) - .url(AGENT_URL) // Use localhost URL for port-forwarded connection + .supportedInterfaces( + Collections.singletonList( + // Use localhost URL for port-forwarded connection + new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL))) .build(); System.out.println(); return agentCard; diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 9e34aef0a..ffe610311 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -9,6 +9,7 @@ import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; @ApplicationScoped @@ -22,7 +23,8 @@ public AgentCard agentCard() { return new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") - .url("http://localhost:9999") + .supportedInterfaces(Collections.singletonList( + new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java index a795bfcd6..2060ead00 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java @@ -1,5 +1,6 @@ package io.a2a.extras.pushnotificationconfigstore.database.jpa; +import java.util.Collections; import java.util.List; import jakarta.enterprise.context.ApplicationScoped; @@ -8,6 +9,7 @@ import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; import io.quarkus.arc.profile.IfBuildProfile; @@ -26,8 +28,9 @@ public AgentCard agentCard() { .name("JPA PushNotificationConfigStore Integration Test Agent") .description("Test agent for verifying JPA PushNotificationConfigStore integration") .version("1.0.0") - .url("http://localhost:8081") // Port is managed by QuarkusTest - .preferredTransport(TransportProtocol.JSONRPC.asString()) + .supportedInterfaces( + Collections.singletonList( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) // Port is managed by QuarkusTest .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .capabilities(new AgentCapabilities.Builder() diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java index bc1994cdb..54287c9df 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java @@ -30,7 +30,8 @@ public static AgentCard createAgentCard(int instanceNumber, int port) { return new AgentCard.Builder() .name("Multi-Instance Test Agent " + instanceNumber) .description("Test agent for multi-instance replication testing - Instance " + instanceNumber) - .url(url) + .supportedInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), url))) .version("1.0.0") .capabilities(new AgentCapabilities.Builder() .streaming(true) @@ -45,8 +46,6 @@ public static AgentCard createAgentCard(int instanceNumber, int port) { .description("Fire-and-forget agent for testing replication") .tags(Collections.singletonList("test")) .build())) - .additionalInterfaces(List.of( - new AgentInterface(TransportProtocol.JSONRPC.asString(), url))) .protocolVersion("0.3.0") .build(); } diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java index f7fd652bb..282425fa7 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.IOException; import java.time.Duration; +import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -22,11 +23,13 @@ import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; +import io.a2a.spec.TransportProtocol; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -145,11 +148,15 @@ public static void setup() { // Rebuild AgentCards with correct URLs (mapped ports, not container ports) app1Card = new AgentCard.Builder(originalApp1Card) - .url(app1Url) + .supportedInterfaces(Collections.singletonList( + new AgentInterface(TransportProtocol.JSONRPC.asString(), app1Url) + )) .build(); app2Card = new AgentCard.Builder(originalApp2Card) - .url(app2Url) + .supportedInterfaces(Collections.singletonList( + new AgentInterface(TransportProtocol.JSONRPC.asString(), app2Url) + )) .build(); } catch (Exception e) { System.err.println("=== Failed to get AgentCards ==="); diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java index a8596f05c..734e64700 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java @@ -25,10 +25,8 @@ public AgentCard agentCard() { return new AgentCard.Builder() .name("replication-test-agent") .description("Test agent for replicated queue manager integration testing") - .url("http://localhost:8081") .version("1.0.0") .documentationUrl("http://localhost:8081/docs") - .preferredTransport(TransportProtocol.JSONRPC.asString()) .capabilities(new AgentCapabilities.Builder() .streaming(true) .pushNotifications(true) @@ -37,7 +35,8 @@ public AgentCard agentCard() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .additionalInterfaces(List.of(new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) + .supportedInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) .protocolVersion("0.2.5") .build(); } diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java index 291519131..65d31020f 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java @@ -1,5 +1,6 @@ package io.a2a.extras.taskstore.database.jpa; +import java.util.Collections; import java.util.List; import jakarta.enterprise.context.ApplicationScoped; @@ -8,6 +9,7 @@ import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; import io.quarkus.arc.profile.IfBuildProfile; @@ -25,12 +27,13 @@ public AgentCard agentCard() { .name("JPA TaskStore Integration Test Agent") .description("Test agent for verifying JPA TaskStore integration") .version("1.0.0") - .url("http://localhost:8081") - .preferredTransport(TransportProtocol.JSONRPC.asString()) .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .capabilities(new AgentCapabilities.Builder().build()) .skills(List.of()) + .supportedInterfaces(Collections.singletonList( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081") + )) .build(); } } diff --git a/http-client/pom.xml b/http-client/pom.xml index 98640e8f2..06d7a6ceb 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -21,6 +21,14 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-spec-grpc + + + com.google.protobuf + protobuf-java-util + org.junit.jupiter diff --git a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java index 539f3a741..f7060e050 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java +++ b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java @@ -1,17 +1,17 @@ package io.a2a.client.http; -import static io.a2a.util.Utils.unmarshalFrom; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Map; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; +import io.a2a.grpc.utils.JSONRPCUtils; +import io.a2a.grpc.utils.ProtoUtils; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; +import io.a2a.spec.JSONRPCError; import org.jspecify.annotations.Nullable; public class A2ACardResolver { @@ -21,8 +21,6 @@ public class A2ACardResolver { private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent-card.json"; - private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; - /** * Get the agent card for an A2A agent. * The {@code JdkA2AHttpClient} will be used to fetch the agent card. @@ -106,8 +104,10 @@ public AgentCard getAgentCard() throws A2AClientError, A2AClientJSONError { } try { - return unmarshalFrom(body, AGENT_CARD_TYPE_REFERENCE); - } catch (JsonProcessingException e) { + io.a2a.grpc.AgentCard.Builder agentCardBuilder = io.a2a.grpc.AgentCard.newBuilder(); + JSONRPCUtils.parseJsonString(body, agentCardBuilder); + return ProtoUtils.FromProto.agentCard(agentCardBuilder); + } catch (JSONRPCError e) { throw new A2AClientJSONError("Could not unmarshal agent card response", e); } diff --git a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java index 99d26adad..68cc86af9 100644 --- a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java +++ b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java @@ -1,7 +1,5 @@ package io.a2a.client.http; -import static io.a2a.util.Utils.OBJECT_MAPPER; -import static io.a2a.util.Utils.unmarshalFrom; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -10,7 +8,10 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import com.fasterxml.jackson.core.type.TypeReference; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.a2a.grpc.utils.JSONRPCUtils; +import io.a2a.grpc.utils.ProtoUtils; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; @@ -20,7 +21,6 @@ public class A2ACardResolverTest { private static final String AGENT_CARD_PATH = "/.well-known/agent-card.json"; - private static final TypeReference AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {}; @Test public void testConstructorStripsSlashes() throws Exception { @@ -72,13 +72,23 @@ public void testGetAgentCardSuccess() throws Exception { A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/"); AgentCard card = resolver.getAgentCard(); - AgentCard expectedCard = unmarshalFrom(JsonMessages.AGENT_CARD, AGENT_CARD_TYPE_REFERENCE); - String expected = OBJECT_MAPPER.writeValueAsString(expectedCard); + AgentCard expectedCard = unmarshalFrom(JsonMessages.AGENT_CARD); + String expected = printAgentCard(expectedCard); - String requestCardString = OBJECT_MAPPER.writeValueAsString(card); + String requestCardString = printAgentCard(card); assertEquals(expected, requestCardString); } + private AgentCard unmarshalFrom(String body) { + io.a2a.grpc.AgentCard.Builder agentCardBuilder = io.a2a.grpc.AgentCard.newBuilder(); + JSONRPCUtils.parseJsonString(body, agentCardBuilder); + return ProtoUtils.FromProto.agentCard(agentCardBuilder); + } + + private String printAgentCard(AgentCard agentCard) throws InvalidProtocolBufferException { + return JsonFormat.printer().print(ProtoUtils.ToProto.agentCard(agentCard)); + } + @Test public void testGetAgentCardJsonDecodeError() throws Exception { TestHttpClient client = new TestHttpClient(); diff --git a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java index f5fd0426a..242f66311 100644 --- a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java +++ b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java @@ -11,12 +11,10 @@ public class JsonMessages { "protocolVersion": "0.2.9", "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", - "url": "https://georoute-agent.example.com/a2a/v1", - "preferredTransport": "JSONRPC", - "additionalInterfaces" : [ - {"url": "https://georoute-agent.example.com/a2a/v1", "transport": "JSONRPC"}, - {"url": "https://georoute-agent.example.com/a2a/grpc", "transport": "GRPC"}, - {"url": "https://georoute-agent.example.com/a2a/json", "transport": "HTTP+JSON"} + "supportedInterfaces" : [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"}, + {"url": "https://georoute-agent.example.com/a2a/grpc", "protocolBinding": "GRPC"}, + {"url": "https://georoute-agent.example.com/a2a/json", "protocolBinding": "HTTP+JSON"} ], "provider": { "organization": "Example Geo Services Inc.", @@ -32,11 +30,12 @@ public class JsonMessages { }, "securitySchemes": { "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + "openIdConnectSecurityScheme": { + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -87,7 +86,9 @@ public class JsonMessages { { "name": "GeoSpatial Route Planner Agent Extended", "description": "Extended description", - "url": "https://georoute-agent.example.com/a2a/v1", + "supportedInterfaces": [ + {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"} + ], "provider": { "organization": "Example Geo Services Inc.", "url": "https://www.examplegeoservices.com" @@ -102,11 +103,12 @@ public class JsonMessages { }, "securitySchemes": { "google": { - "type": "openIdConnect", - "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + "openIdConnectSecurityScheme": { + "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" + } } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ diff --git a/pom.xml b/pom.xml index 5442bd2ac..e4bc77a17 100644 --- a/pom.xml +++ b/pom.xml @@ -54,11 +54,13 @@ 2.1.3 3.1.0 5.13.4 + 1.6.3 + 1.52.0 5.17.0 5.15.0 1.1.1 1.7.1 - 4.31.1 + 4.31.1 0.6.1 3.28.2 5.5.1 @@ -70,6 +72,7 @@ true + --add-opens=java.base/java.lang=ALL-UNNAMED false @@ -206,7 +209,7 @@ com.google.protobuf protobuf-java - ${protobuf.version} + ${protobuf-java.version} io.smallrye.reactive @@ -223,6 +226,16 @@ jakarta.inject-api ${jakarta.inject.jakarta.inject-api.version} + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + jakarta.json jakarta.json-api @@ -341,6 +354,16 @@ nullaway ${nullaway.version} + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + no.entur.mapstruct.spi + protobuf-spi-impl + ${mapstruct-spi-protobuf.version} + + + org.mapstruct + mapstruct + provided + javax.annotation javax.annotation-api provided + org.junit.jupiter junit-jupiter-api @@ -69,78 +79,42 @@ junit-jupiter-engine test + + com.google.protobuf + protobuf-java-util + provided + - - - - kr.motd.maven - os-maven-plugin - ${os-maven-plugin.version} - - + + + + io.github.ascopes + protobuf-maven-plugin + 4.0.2 + + ${skip.protobuf.generate} + true + ${protobuf-java.version} + ${project.basedir}/src/main/java/ + + + io.grpc + protoc-gen-grpc-java + ${grpc.version} + + + + + + generate-sources + + generate + + + + + - - - - proto-compile - - - - - org.apache.maven.plugins - maven-clean-plugin - ${maven-clean-plugin.version} - - - remove-generated-files - initialize - - clean - - - true - - - ${project.basedir}/src/main/java/io/a2a/grpc - false - - *.java - - - - - - - - - - org.xolstice.maven.plugins - protobuf-maven-plugin - ${protobuf-maven-plugin.version} - - com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} - grpc-java - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} - src/main/java - false - - - - - compile - compile-custom - - - - - - - - - diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index e509c936d..40ecc5080 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -257,25 +257,25 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable; + internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + internal_static_a2a_v1_SubscribeToTaskRequest_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable; + internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_GetAgentCardRequest_descriptor; + internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable; + internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_SendMessageResponse_descriptor; static final @@ -305,208 +305,227 @@ public static void registerAllExtensions( "le/api/field_behavior.proto\032\033google/prot" + "obuf/empty.proto\032\034google/protobuf/struct" + ".proto\032\037google/protobuf/timestamp.proto\"" + - "\245\001\n\030SendMessageConfiguration\022\035\n\025accepted" + + "\275\001\n\030SendMessageConfiguration\022\035\n\025accepted" + "_output_modes\030\001 \003(\t\022@\n\030push_notification" + "_config\030\002 \001(\0132\036.a2a.v1.PushNotificationC" + - "onfig\022\026\n\016history_length\030\003 \001(\005\022\020\n\010blockin" + - "g\030\004 \001(\010\"\274\001\n\004Task\022\n\n\002id\030\001 \001(\t\022\022\n\ncontext_" + - "id\030\002 \001(\t\022\"\n\006status\030\003 \001(\0132\022.a2a.v1.TaskSt" + - "atus\022#\n\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifac" + - "t\022 \n\007history\030\005 \003(\0132\017.a2a.v1.Message\022)\n\010m" + - "etadata\030\006 \001(\0132\027.google.protobuf.Struct\"\207" + - "\001\n\nTaskStatus\022 \n\005state\030\001 \001(\0162\021.a2a.v1.Ta" + - "skState\022(\n\006update\030\002 \001(\0132\017.a2a.v1.Message" + - "R\007message\022-\n\ttimestamp\030\003 \001(\0132\032.google.pr" + - "otobuf.Timestamp\"\215\001\n\004Part\022\016\n\004text\030\001 \001(\tH" + - "\000\022 \n\004file\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004d" + - "ata\030\003 \001(\0132\020.a2a.v1.DataPartH\000\022)\n\010metadat" + - "a\030\004 \001(\0132\027.google.protobuf.StructB\006\n\004part" + - "\"g\n\010FilePart\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n" + - "\017file_with_bytes\030\002 \001(\014H\000\022\021\n\tmime_type\030\003 " + - "\001(\t\022\014\n\004name\030\004 \001(\tB\006\n\004file\"1\n\010DataPart\022%\n" + - "\004data\030\001 \001(\0132\027.google.protobuf.Struct\"\326\001\n" + - "\007Message\022\022\n\nmessage_id\030\001 \001(\t\022\022\n\ncontext_" + - "id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t\022\032\n\004role\030\004 \001(\0162" + - "\014.a2a.v1.Role\022\033\n\005parts\030\005 \003(\0132\014.a2a.v1.Pa" + - "rt\022)\n\010metadata\030\006 \001(\0132\027.google.protobuf.S" + - "truct\022\022\n\nextensions\030\007 \003(\t\022\032\n\022reference_t" + - "ask_ids\030\010 \003(\t\"\236\001\n\010Artifact\022\023\n\013artifact_i" + - "d\030\001 \001(\t\022\014\n\004name\030\003 \001(\t\022\023\n\013description\030\004 \001" + - "(\t\022\033\n\005parts\030\005 \003(\0132\014.a2a.v1.Part\022)\n\010metad" + - "ata\030\006 \001(\0132\027.google.protobuf.Struct\022\022\n\nex" + - "tensions\030\007 \003(\t\"\232\001\n\025TaskStatusUpdateEvent" + - "\022\017\n\007task_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n" + - "\006status\030\003 \001(\0132\022.a2a.v1.TaskStatus\022\r\n\005fin" + - "al\030\004 \001(\010\022)\n\010metadata\030\005 \001(\0132\027.google.prot" + - "obuf.Struct\"\261\001\n\027TaskArtifactUpdateEvent\022" + - "\017\n\007task_id\030\001 \001(\t\022\022\n\ncontext_id\030\002 \001(\t\022\"\n\010" + - "artifact\030\003 \001(\0132\020.a2a.v1.Artifact\022\016\n\006appe" + - "nd\030\004 \001(\010\022\022\n\nlast_chunk\030\005 \001(\010\022)\n\010metadata" + - "\030\006 \001(\0132\027.google.protobuf.Struct\"t\n\026PushN" + - "otificationConfig\022\n\n\002id\030\001 \001(\t\022\013\n\003url\030\002 \001" + - "(\t\022\r\n\005token\030\003 \001(\t\0222\n\016authentication\030\004 \001(" + - "\0132\032.a2a.v1.AuthenticationInfo\":\n\022Authent" + - "icationInfo\022\017\n\007schemes\030\001 \003(\t\022\023\n\013credenti" + - "als\030\002 \001(\t\"0\n\016AgentInterface\022\013\n\003url\030\001 \001(\t" + - "\022\021\n\ttransport\030\002 \001(\t\"\264\005\n\tAgentCard\022\030\n\020pro" + - "tocol_version\030\020 \001(\t\022\014\n\004name\030\001 \001(\t\022\023\n\013des" + - "cription\030\002 \001(\t\022\013\n\003url\030\003 \001(\t\022\033\n\023preferred" + - "_transport\030\016 \001(\t\0225\n\025additional_interface" + - "s\030\017 \003(\0132\026.a2a.v1.AgentInterface\022\'\n\010provi" + - "der\030\004 \001(\0132\025.a2a.v1.AgentProvider\022\017\n\007vers" + - "ion\030\005 \001(\t\022\031\n\021documentation_url\030\006 \001(\t\022/\n\014" + - "capabilities\030\007 \001(\0132\031.a2a.v1.AgentCapabil" + - "ities\022@\n\020security_schemes\030\010 \003(\0132&.a2a.v1" + + "onfig\022\033\n\016history_length\030\003 \001(\005H\000\210\001\001\022\020\n\010bl" + + "ocking\030\004 \001(\010B\021\n\017_history_length\"\313\001\n\004Task" + + "\022\017\n\002id\030\001 \001(\tB\003\340A\002\022\027\n\ncontext_id\030\002 \001(\tB\003\340" + + "A\002\022\'\n\006status\030\003 \001(\0132\022.a2a.v1.TaskStatusB\003" + + "\340A\002\022#\n\tartifacts\030\004 \003(\0132\020.a2a.v1.Artifact" + + "\022 \n\007history\030\005 \003(\0132\017.a2a.v1.Message\022)\n\010me" + + "tadata\030\006 \001(\0132\027.google.protobuf.Struct\"\204\001" + + "\n\nTaskStatus\022%\n\005state\030\001 \001(\0162\021.a2a.v1.Tas" + + "kStateB\003\340A\002\022 \n\007message\030\002 \001(\0132\017.a2a.v1.Me" + + "ssage\022-\n\ttimestamp\030\003 \001(\0132\032.google.protob" + + "uf.Timestamp\"\215\001\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022 \n" + + "\004file\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004data\030" + + "\003 \001(\0132\020.a2a.v1.DataPartH\000\022)\n\010metadata\030\004 " + + "\001(\0132\027.google.protobuf.StructB\006\n\004part\"h\n\010" + + "FilePart\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n\017fil" + + "e_with_bytes\030\002 \001(\014H\000\022\022\n\nmedia_type\030\003 \001(\t" + + "\022\014\n\004name\030\004 \001(\tB\006\n\004file\"6\n\010DataPart\022*\n\004da" + + "ta\030\001 \001(\0132\027.google.protobuf.StructB\003\340A\002\"\345" + + "\001\n\007Message\022\027\n\nmessage_id\030\001 \001(\tB\003\340A\002\022\022\n\nc" + + "ontext_id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t\022\037\n\004role" + + "\030\004 \001(\0162\014.a2a.v1.RoleB\003\340A\002\022 \n\005parts\030\005 \003(\013" + + "2\014.a2a.v1.PartB\003\340A\002\022)\n\010metadata\030\006 \001(\0132\027." + + "google.protobuf.Struct\022\022\n\nextensions\030\007 \003" + + "(\t\022\032\n\022reference_task_ids\030\010 \003(\t\"\250\001\n\010Artif" + + "act\022\030\n\013artifact_id\030\001 \001(\tB\003\340A\002\022\014\n\004name\030\003 " + + "\001(\t\022\023\n\013description\030\004 \001(\t\022 \n\005parts\030\005 \003(\0132" + + "\014.a2a.v1.PartB\003\340A\002\022)\n\010metadata\030\006 \001(\0132\027.g" + + "oogle.protobuf.Struct\022\022\n\nextensions\030\007 \003(" + + "\t\"\256\001\n\025TaskStatusUpdateEvent\022\024\n\007task_id\030\001" + + " \001(\tB\003\340A\002\022\027\n\ncontext_id\030\002 \001(\tB\003\340A\002\022\'\n\006st" + + "atus\030\003 \001(\0132\022.a2a.v1.TaskStatusB\003\340A\002\022\022\n\005f" + + "inal\030\004 \001(\010B\003\340A\002\022)\n\010metadata\030\005 \001(\0132\027.goog" + + "le.protobuf.Struct\"\300\001\n\027TaskArtifactUpdat" + + "eEvent\022\024\n\007task_id\030\001 \001(\tB\003\340A\002\022\027\n\ncontext_" + + "id\030\002 \001(\tB\003\340A\002\022\'\n\010artifact\030\003 \001(\0132\020.a2a.v1" + + ".ArtifactB\003\340A\002\022\016\n\006append\030\004 \001(\010\022\022\n\nlast_c" + + "hunk\030\005 \001(\010\022)\n\010metadata\030\006 \001(\0132\027.google.pr" + + "otobuf.Struct\"y\n\026PushNotificationConfig\022" + + "\n\n\002id\030\001 \001(\t\022\020\n\003url\030\002 \001(\tB\003\340A\002\022\r\n\005token\030\003" + + " \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a.v1.Au" + + "thenticationInfo\"?\n\022AuthenticationInfo\022\024" + + "\n\007schemes\030\001 \003(\tB\003\340A\002\022\023\n\013credentials\030\002 \001(" + + "\t\"A\n\016AgentInterface\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\035\n" + + "\020protocol_binding\030\002 \001(\tB\003\340A\002\"\275\007\n\tAgentCa" + + "rd\022\"\n\020protocol_version\030\020 \001(\tB\003\340A\002H\000\210\001\001\022\021" + + "\n\004name\030\001 \001(\tB\003\340A\002\022\030\n\013description\030\002 \001(\tB\003" + + "\340A\002\0224\n\024supported_interfaces\030\023 \003(\0132\026.a2a." + + "v1.AgentInterface\022\024\n\003url\030\003 \001(\tB\002\030\001H\001\210\001\001\022" + + "$\n\023preferred_transport\030\016 \001(\tB\002\030\001H\002\210\001\001\0229\n" + + "\025additional_interfaces\030\017 \003(\0132\026.a2a.v1.Ag" + + "entInterfaceB\002\030\001\022\'\n\010provider\030\004 \001(\0132\025.a2a" + + ".v1.AgentProvider\022\024\n\007version\030\005 \001(\tB\003\340A\002\022" + + "\036\n\021documentation_url\030\006 \001(\tH\003\210\001\001\0224\n\014capab" + + "ilities\030\007 \001(\0132\031.a2a.v1.AgentCapabilities" + + "B\003\340A\002\022@\n\020security_schemes\030\010 \003(\0132&.a2a.v1" + ".AgentCard.SecuritySchemesEntry\022\"\n\010secur" + - "ity\030\t \003(\0132\020.a2a.v1.Security\022\033\n\023default_i" + - "nput_modes\030\n \003(\t\022\034\n\024default_output_modes" + - "\030\013 \003(\t\022\"\n\006skills\030\014 \003(\0132\022.a2a.v1.AgentSki" + - "ll\022,\n$supports_authenticated_extended_ca" + - "rd\030\r \001(\010\022.\n\nsignatures\030\021 \003(\0132\032.a2a.v1.Ag" + - "entCardSignature\022\020\n\010icon_url\030\022 \001(\t\032N\n\024Se" + - "curitySchemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value" + - "\030\002 \001(\0132\026.a2a.v1.SecurityScheme:\0028\001\"2\n\rAg" + - "entProvider\022\013\n\003url\030\001 \001(\t\022\024\n\014organization" + - "\030\002 \001(\t\"\220\001\n\021AgentCapabilities\022\021\n\tstreamin" + - "g\030\001 \001(\010\022\032\n\022push_notifications\030\002 \001(\010\022*\n\ne" + - "xtensions\030\003 \003(\0132\026.a2a.v1.AgentExtension\022" + - " \n\030state_transition_history\030\004 \001(\010\"m\n\016Age" + - "ntExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030" + - "\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132" + - "\027.google.protobuf.Struct\"\252\001\n\nAgentSkill\022" + - "\n\n\002id\030\001 \001(\t\022\014\n\004name\030\002 \001(\t\022\023\n\013description" + - "\030\003 \001(\t\022\014\n\004tags\030\004 \003(\t\022\020\n\010examples\030\005 \003(\t\022\023" + - "\n\013input_modes\030\006 \003(\t\022\024\n\014output_modes\030\007 \003(" + - "\t\022\"\n\010security\030\010 \003(\0132\020.a2a.v1.Security\"m\n" + - "\022AgentCardSignature\022\026\n\tprotected\030\001 \001(\tB\003" + - "\340A\002\022\026\n\tsignature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 " + - "\001(\0132\027.google.protobuf.Struct\"l\n\032TaskPush" + - "NotificationConfig\022\014\n\004name\030\001 \001(\t\022@\n\030push" + - "_notification_config\030\002 \001(\0132\036.a2a.v1.Push" + - "NotificationConfig\"\032\n\nStringList\022\014\n\004list" + - "\030\001 \003(\t\"~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035.a2" + - "a.v1.Security.SchemesEntry\032B\n\014SchemesEnt" + - "ry\022\013\n\003key\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1." + - "StringList:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027api" + - "_key_security_scheme\030\001 \001(\0132\034.a2a.v1.APIK" + - "eySecuritySchemeH\000\022C\n\031http_auth_security" + - "_scheme\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecurityS" + - "chemeH\000\022>\n\026oauth2_security_scheme\030\003 \001(\0132" + - "\034.a2a.v1.OAuth2SecuritySchemeH\000\022N\n\037open_" + - "id_connect_security_scheme\030\004 \001(\0132#.a2a.v" + - "1.OpenIdConnectSecuritySchemeH\000\022?\n\024mtls_" + - "security_scheme\030\005 \001(\0132\037.a2a.v1.MutualTls" + - "SecuritySchemeH\000B\010\n\006scheme\"K\n\024APIKeySecu" + - "rityScheme\022\023\n\013description\030\001 \001(\t\022\020\n\010locat" + - "ion\030\002 \001(\t\022\014\n\004name\030\003 \001(\t\"T\n\026HTTPAuthSecur" + - "ityScheme\022\023\n\013description\030\001 \001(\t\022\016\n\006scheme" + - "\030\002 \001(\t\022\025\n\rbearer_format\030\003 \001(\t\"k\n\024OAuth2S" + - "ecurityScheme\022\023\n\013description\030\001 \001(\t\022!\n\005fl" + - "ows\030\002 \001(\0132\022.a2a.v1.OAuthFlows\022\033\n\023oauth2_" + - "metadata_url\030\003 \001(\t\"O\n\033OpenIdConnectSecur" + - "ityScheme\022\023\n\013description\030\001 \001(\t\022\033\n\023open_i" + - "d_connect_url\030\002 \001(\t\".\n\027MutualTlsSecurity" + - "Scheme\022\023\n\013description\030\001 \001(\t\"\366\001\n\nOAuthFlo" + - "ws\022@\n\022authorization_code\030\001 \001(\0132\".a2a.v1." + - "AuthorizationCodeOAuthFlowH\000\022@\n\022client_c" + - "redentials\030\002 \001(\0132\".a2a.v1.ClientCredenti" + - "alsOAuthFlowH\000\022-\n\010implicit\030\003 \001(\0132\031.a2a.v" + - "1.ImplicitOAuthFlowH\000\022-\n\010password\030\004 \001(\0132" + - "\031.a2a.v1.PasswordOAuthFlowH\000B\006\n\004flow\"\316\001\n" + - "\032AuthorizationCodeOAuthFlow\022\031\n\021authoriza" + - "tion_url\030\001 \001(\t\022\021\n\ttoken_url\030\002 \001(\t\022\023\n\013ref" + - "resh_url\030\003 \001(\t\022>\n\006scopes\030\004 \003(\0132..a2a.v1." + - "AuthorizationCodeOAuthFlow.ScopesEntry\032-" + - "\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(" + - "\t:\0028\001\"\263\001\n\032ClientCredentialsOAuthFlow\022\021\n\t" + - "token_url\030\001 \001(\t\022\023\n\013refresh_url\030\002 \001(\t\022>\n\006" + + "ity\030\t \003(\0132\020.a2a.v1.Security\022 \n\023default_i" + + "nput_modes\030\n \003(\tB\003\340A\002\022!\n\024default_output_" + + "modes\030\013 \003(\tB\003\340A\002\022\'\n\006skills\030\014 \003(\0132\022.a2a.v" + + "1.AgentSkillB\003\340A\002\0221\n$supports_authentica" + + "ted_extended_card\030\r \001(\010H\004\210\001\001\022.\n\nsignatur" + + "es\030\021 \003(\0132\032.a2a.v1.AgentCardSignature\022\025\n\010" + + "icon_url\030\022 \001(\tH\005\210\001\001\032N\n\024SecuritySchemesEn" + + "try\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132\026.a2a.v1" + + ".SecurityScheme:\0028\001B\023\n\021_protocol_version" + + "B\006\n\004_urlB\026\n\024_preferred_transportB\024\n\022_doc" + + "umentation_urlB\'\n%_supports_authenticate" + + "d_extended_cardB\013\n\t_icon_url\"<\n\rAgentPro" + + "vider\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\031\n\014organization\030" + + "\002 \001(\tB\003\340A\002\"\341\001\n\021AgentCapabilities\022\026\n\tstre" + + "aming\030\001 \001(\010H\000\210\001\001\022\037\n\022push_notifications\030\002" + + " \001(\010H\001\210\001\001\022*\n\nextensions\030\003 \003(\0132\026.a2a.v1.A" + + "gentExtension\022%\n\030state_transition_histor" + + "y\030\004 \001(\010H\002\210\001\001B\014\n\n_streamingB\025\n\023_push_noti" + + "ficationsB\033\n\031_state_transition_history\"m" + + "\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013descrip" + + "tion\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006params\030\004" + + " \001(\0132\027.google.protobuf.Struct\"\276\001\n\nAgentS" + + "kill\022\017\n\002id\030\001 \001(\tB\003\340A\002\022\021\n\004name\030\002 \001(\tB\003\340A\002" + + "\022\030\n\013description\030\003 \001(\tB\003\340A\002\022\021\n\004tags\030\004 \003(\t" + + "B\003\340A\002\022\020\n\010examples\030\005 \003(\t\022\023\n\013input_modes\030\006" + + " \003(\t\022\024\n\014output_modes\030\007 \003(\t\022\"\n\010security\030\010" + + " \003(\0132\020.a2a.v1.Security\"m\n\022AgentCardSigna" + + "ture\022\026\n\tprotected\030\001 \001(\tB\003\340A\002\022\026\n\tsignatur" + + "e\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\0132\027.google.pr" + + "otobuf.Struct\"v\n\032TaskPushNotificationCon" + + "fig\022\021\n\004name\030\001 \001(\tB\003\340A\002\022E\n\030push_notificat" + + "ion_config\030\002 \001(\0132\036.a2a.v1.PushNotificati" + + "onConfigB\003\340A\002\"\032\n\nStringList\022\014\n\004list\030\001 \003(" + + "\t\"~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035.a2a.v1." + + "Security.SchemesEntry\032B\n\014SchemesEntry\022\013\n" + + "\003key\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1.Strin" + + "gList:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027api_key_" + + "security_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySec" + + "uritySchemeH\000\022C\n\031http_auth_security_sche" + + "me\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecurityScheme" + + "H\000\022>\n\026oauth2_security_scheme\030\003 \001(\0132\034.a2a" + + ".v1.OAuth2SecuritySchemeH\000\022N\n\037open_id_co" + + "nnect_security_scheme\030\004 \001(\0132#.a2a.v1.Ope" + + "nIdConnectSecuritySchemeH\000\022?\n\024mtls_secur" + + "ity_scheme\030\005 \001(\0132\037.a2a.v1.MutualTlsSecur" + + "itySchemeH\000B\010\n\006scheme\"U\n\024APIKeySecurityS" + + "cheme\022\023\n\013description\030\001 \001(\t\022\025\n\010location\030\002" + + " \001(\tB\003\340A\002\022\021\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026HTTPAuth" + + "SecurityScheme\022\023\n\013description\030\001 \001(\t\022\023\n\006s" + + "cheme\030\002 \001(\tB\003\340A\002\022\025\n\rbearer_format\030\003 \001(\t\"" + + "p\n\024OAuth2SecurityScheme\022\023\n\013description\030\001" + + " \001(\t\022&\n\005flows\030\002 \001(\0132\022.a2a.v1.OAuthFlowsB" + + "\003\340A\002\022\033\n\023oauth2_metadata_url\030\003 \001(\t\"T\n\033Ope" + + "nIdConnectSecurityScheme\022\023\n\013description\030" + + "\001 \001(\t\022 \n\023open_id_connect_url\030\002 \001(\tB\003\340A\002\"" + + ".\n\027MutualTlsSecurityScheme\022\023\n\013descriptio" + + "n\030\001 \001(\t\"\366\001\n\nOAuthFlows\022@\n\022authorization_" + + "code\030\001 \001(\0132\".a2a.v1.AuthorizationCodeOAu" + + "thFlowH\000\022@\n\022client_credentials\030\002 \001(\0132\".a" + + "2a.v1.ClientCredentialsOAuthFlowH\000\022-\n\010im" + + "plicit\030\003 \001(\0132\031.a2a.v1.ImplicitOAuthFlowH" + + "\000\022-\n\010password\030\004 \001(\0132\031.a2a.v1.PasswordOAu" + + "thFlowH\000B\006\n\004flow\"\335\001\n\032AuthorizationCodeOA" + + "uthFlow\022\036\n\021authorization_url\030\001 \001(\tB\003\340A\002\022" + + "\026\n\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003" + + " \001(\t\022C\n\006scopes\030\004 \003(\0132..a2a.v1.Authorizat" + + "ionCodeOAuthFlow.ScopesEntryB\003\340A\002\032-\n\013Sco" + + "pesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001" + + "\"\275\001\n\032ClientCredentialsOAuthFlow\022\026\n\ttoken" + + "_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022C\n\006" + "scopes\030\003 \003(\0132..a2a.v1.ClientCredentialsO" + - "AuthFlow.ScopesEntry\032-\n\013ScopesEntry\022\013\n\003k" + - "ey\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021Implicit" + - "OAuthFlow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013" + - "refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a." + - "v1.ImplicitOAuthFlow.ScopesEntry\032-\n\013Scop" + - "esEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"" + - "\241\001\n\021PasswordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t" + - "\022\023\n\013refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%." + - "a2a.v1.PasswordOAuthFlow.ScopesEntry\032-\n\013" + - "ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:" + - "\0028\001\"\250\001\n\022SendMessageRequest\022.\n\007request\030\001 " + - "\001(\0132\017.a2a.v1.MessageB\003\340A\002R\007message\0227\n\rco" + - "nfiguration\030\002 \001(\0132 .a2a.v1.SendMessageCo" + - "nfiguration\022)\n\010metadata\030\003 \001(\0132\027.google.p" + - "rotobuf.Struct\";\n\016GetTaskRequest\022\021\n\004name" + - "\030\001 \001(\tB\003\340A\002\022\026\n\016history_length\030\002 \001(\005\"\332\001\n\020" + - "ListTasksRequest\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006" + - "status\030\002 \001(\0162\021.a2a.v1.TaskState\022\021\n\tpage_" + - "size\030\003 \001(\005\022\022\n\npage_token\030\004 \001(\t\022\026\n\016histor" + - "y_length\030\005 \001(\005\0225\n\021last_updated_time\030\006 \001(" + - "\0132\032.google.protobuf.Timestamp\022\031\n\021include" + - "_artifacts\030\007 \001(\010\"]\n\021ListTasksResponse\022\033\n" + - "\005tasks\030\001 \003(\0132\014.a2a.v1.Task\022\027\n\017next_page_" + - "token\030\002 \001(\t\022\022\n\ntotal_size\030\003 \001(\005\"!\n\021Cance" + - "lTaskRequest\022\014\n\004name\030\001 \001(\t\"4\n$GetTaskPus" + - "hNotificationConfigRequest\022\014\n\004name\030\001 \001(\t" + - "\"7\n\'DeleteTaskPushNotificationConfigRequ" + - "est\022\014\n\004name\030\001 \001(\t\"\217\001\n\'CreateTaskPushNoti" + - "ficationConfigRequest\022\023\n\006parent\030\001 \001(\tB\003\340" + - "A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001" + - "(\0132\".a2a.v1.TaskPushNotificationConfigB\003" + - "\340A\002\"\'\n\027TaskSubscriptionRequest\022\014\n\004name\030\001" + - " \001(\t\"^\n%ListTaskPushNotificationConfigRe" + - "quest\022\016\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005" + - "\022\022\n\npage_token\030\003 \001(\t\"\025\n\023GetAgentCardRequ" + - "est\"g\n\023SendMessageResponse\022\034\n\004task\030\001 \001(\013" + + "AuthFlow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry" + + "\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\263\001\n\021Imp" + + "licitOAuthFlow\022\036\n\021authorization_url\030\001 \001(" + + "\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022:\n\006scopes\030\003 " + + "\003(\0132%.a2a.v1.ImplicitOAuthFlow.ScopesEnt" + + "ryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005v" + + "alue\030\002 \001(\t:\0028\001\"\253\001\n\021PasswordOAuthFlow\022\026\n\t" + + "token_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(" + + "\t\022:\n\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuth" + + "Flow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003" + + "key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\250\001\n\022SendMes" + + "sageRequest\022.\n\007request\030\001 \001(\0132\017.a2a.v1.Me" + + "ssageB\003\340A\002R\007message\0227\n\rconfiguration\030\002 \001" + + "(\0132 .a2a.v1.SendMessageConfiguration\022)\n\010" + + "metadata\030\003 \001(\0132\027.google.protobuf.Struct\"" + + "S\n\016GetTaskRequest\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\033\n\016" + + "history_length\030\002 \001(\005H\000\210\001\001B\021\n\017_history_le" + + "ngth\"\260\002\n\020ListTasksRequest\022\022\n\ncontext_id\030" + + "\001 \001(\t\022!\n\006status\030\002 \001(\0162\021.a2a.v1.TaskState" + + "\022\026\n\tpage_size\030\003 \001(\005H\000\210\001\001\022\022\n\npage_token\030\004" + + " \001(\t\022\033\n\016history_length\030\005 \001(\005H\001\210\001\001\022\032\n\022las" + + "t_updated_after\030\006 \001(\003\022\036\n\021include_artifac" + + "ts\030\007 \001(\010H\002\210\001\001\022)\n\010metadata\030\010 \001(\0132\027.google" + + ".protobuf.StructB\014\n\n_page_sizeB\021\n\017_histo" + + "ry_lengthB\024\n\022_include_artifacts\"\204\001\n\021List" + + "TasksResponse\022 \n\005tasks\030\001 \003(\0132\014.a2a.v1.Ta" + + "skB\003\340A\002\022\034\n\017next_page_token\030\002 \001(\tB\003\340A\002\022\026\n" + + "\tpage_size\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_size\030\004 \001(" + + "\005B\003\340A\002\"!\n\021CancelTaskRequest\022\014\n\004name\030\001 \001(" + + "\t\"4\n$GetTaskPushNotificationConfigReques" + + "t\022\014\n\004name\030\001 \001(\t\"7\n\'DeleteTaskPushNotific" + + "ationConfigRequest\022\014\n\004name\030\001 \001(\t\"\214\001\n$Set" + + "TaskPushNotificationConfigRequest\022\023\n\006par" + + "ent\030\001 \001(\tB\003\340A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227" + + "\n\006config\030\003 \001(\0132\".a2a.v1.TaskPushNotifica" + + "tionConfigB\003\340A\002\"&\n\026SubscribeToTaskReques" + + "t\022\014\n\004name\030\001 \001(\t\"^\n%ListTaskPushNotificat" + + "ionConfigRequest\022\016\n\006parent\030\001 \001(\t\022\021\n\tpage" + + "_size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t\"\035\n\033GetEx" + + "tendedAgentCardRequest\"g\n\023SendMessageRes" + + "ponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003m" + + "sg\030\002 \001(\0132\017.a2a.v1.MessageH\000R\007messageB\t\n\007" + + "payload\"\326\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\013" + "2\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.M" + - "essageH\000R\007messageB\t\n\007payload\"\326\001\n\016StreamR" + - "esponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n" + - "\003msg\030\002 \001(\0132\017.a2a.v1.MessageH\000R\007message\0226" + - "\n\rstatus_update\030\003 \001(\0132\035.a2a.v1.TaskStatu" + - "sUpdateEventH\000\022:\n\017artifact_update\030\004 \001(\0132" + - "\037.a2a.v1.TaskArtifactUpdateEventH\000B\t\n\007pa" + - "yload\"v\n&ListTaskPushNotificationConfigR" + - "esponse\0223\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPu" + - "shNotificationConfig\022\027\n\017next_page_token\030" + - "\002 \001(\t*\372\001\n\tTaskState\022\032\n\026TASK_STATE_UNSPEC" + - "IFIED\020\000\022\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TAS" + - "K_STATE_WORKING\020\002\022\030\n\024TASK_STATE_COMPLETE" + - "D\020\003\022\025\n\021TASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE" + - "_CANCELLED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRE" + - "D\020\006\022\027\n\023TASK_STATE_REJECTED\020\007\022\034\n\030TASK_STA" + - "TE_AUTH_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPE" + - "CIFIED\020\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022" + - "\220\013\n\nA2AService\022c\n\013SendMessage\022\032.a2a.v1.S" + - "endMessageRequest\032\033.a2a.v1.SendMessageRe" + - "sponse\"\033\202\323\344\223\002\025\"\020/v1/message:send:\001*\022k\n\024S" + - "endStreamingMessage\022\032.a2a.v1.SendMessage" + - "Request\032\026.a2a.v1.StreamResponse\"\035\202\323\344\223\002\027\"" + - "\022/v1/message:stream:\001*0\001\022R\n\007GetTask\022\026.a2" + - "a.v1.GetTaskRequest\032\014.a2a.v1.Task\"!\332A\004na" + - "me\202\323\344\223\002\024\022\022/v1/{name=tasks/*}\022S\n\tListTask" + - "s\022\030.a2a.v1.ListTasksRequest\032\031.a2a.v1.Lis" + - "tTasksResponse\"\021\202\323\344\223\002\013\022\t/v1/tasks\022[\n\nCan" + - "celTask\022\031.a2a.v1.CancelTaskRequest\032\014.a2a" + - ".v1.Task\"$\202\323\344\223\002\036\"\031/v1/{name=tasks/*}:can" + - "cel:\001*\022s\n\020TaskSubscription\022\037.a2a.v1.Task" + - "SubscriptionRequest\032\026.a2a.v1.StreamRespo" + - "nse\"$\202\323\344\223\002\036\022\034/v1/{name=tasks/*}:subscrib" + - "e0\001\022\305\001\n CreateTaskPushNotificationConfig" + - "\022/.a2a.v1.CreateTaskPushNotificationConf" + - "igRequest\032\".a2a.v1.TaskPushNotificationC" + - "onfig\"L\332A\rparent,config\202\323\344\223\0026\",/v1/{pare" + - "nt=tasks/*/pushNotificationConfigs}:\006con" + - "fig\022\256\001\n\035GetTaskPushNotificationConfig\022,." + - "a2a.v1.GetTaskPushNotificationConfigRequ" + - "est\032\".a2a.v1.TaskPushNotificationConfig\"" + - ";\332A\004name\202\323\344\223\002.\022,/v1/{name=tasks/*/pushNo" + - "tificationConfigs/*}\022\276\001\n\036ListTaskPushNot" + - "ificationConfig\022-.a2a.v1.ListTaskPushNot" + - "ificationConfigRequest\032..a2a.v1.ListTask" + - "PushNotificationConfigResponse\"=\332A\006paren" + - "t\202\323\344\223\002.\022,/v1/{parent=tasks/*}/pushNotifi" + - "cationConfigs\022P\n\014GetAgentCard\022\033.a2a.v1.G" + - "etAgentCardRequest\032\021.a2a.v1.AgentCard\"\020\202" + - "\323\344\223\002\n\022\010/v1/card\022\250\001\n DeleteTaskPushNotifi" + + "essageH\000R\007message\0226\n\rstatus_update\030\003 \001(\013" + + "2\035.a2a.v1.TaskStatusUpdateEventH\000\022:\n\017art" + + "ifact_update\030\004 \001(\0132\037.a2a.v1.TaskArtifact" + + "UpdateEventH\000B\t\n\007payload\"v\n&ListTaskPush" + + "NotificationConfigResponse\0223\n\007configs\030\001 " + + "\003(\0132\".a2a.v1.TaskPushNotificationConfig\022" + + "\027\n\017next_page_token\030\002 \001(\t*\372\001\n\tTaskState\022\032" + + "\n\026TASK_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STATE" + + "_SUBMITTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024" + + "TASK_STATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FAI" + + "LED\020\004\022\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK_" + + "STATE_INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_REJ" + + "ECTED\020\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;\n" + + "\004Role\022\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USER" + + "\020\001\022\016\n\nROLE_AGENT\020\0022\245\013\n\nA2AService\022c\n\013Sen" + + "dMessage\022\032.a2a.v1.SendMessageRequest\032\033.a" + + "2a.v1.SendMessageResponse\"\033\202\323\344\223\002\025\"\020/v1/m" + + "essage:send:\001*\022k\n\024SendStreamingMessage\022\032" + + ".a2a.v1.SendMessageRequest\032\026.a2a.v1.Stre" + + "amResponse\"\035\202\323\344\223\002\027\"\022/v1/message:stream:\001" + + "*0\001\022R\n\007GetTask\022\026.a2a.v1.GetTaskRequest\032\014" + + ".a2a.v1.Task\"!\332A\004name\202\323\344\223\002\024\022\022/v1/{name=t" + + "asks/*}\022S\n\tListTasks\022\030.a2a.v1.ListTasksR" + + "equest\032\031.a2a.v1.ListTasksResponse\"\021\202\323\344\223\002" + + "\013\022\t/v1/tasks\022[\n\nCancelTask\022\031.a2a.v1.Canc" + + "elTaskRequest\032\014.a2a.v1.Task\"$\202\323\344\223\002\036\"\031/v1" + + "/{name=tasks/*}:cancel:\001*\022q\n\017SubscribeTo" + + "Task\022\036.a2a.v1.SubscribeToTaskRequest\032\026.a" + + "2a.v1.StreamResponse\"$\202\323\344\223\002\036\022\034/v1/{name=" + + "tasks/*}:subscribe0\001\022\277\001\n\035SetTaskPushNoti" + + "ficationConfig\022,.a2a.v1.SetTaskPushNotif" + + "icationConfigRequest\032\".a2a.v1.TaskPushNo" + + "tificationConfig\"L\332A\rparent,config\202\323\344\223\0026" + + "\",/v1/{parent=tasks/*/pushNotificationCo" + + "nfigs}:\006config\022\256\001\n\035GetTaskPushNotificati" + + "onConfig\022,.a2a.v1.GetTaskPushNotificatio" + + "nConfigRequest\032\".a2a.v1.TaskPushNotifica" + + "tionConfig\";\332A\004name\202\323\344\223\002.\022,/v1/{name=tas" + + "ks/*/pushNotificationConfigs/*}\022\276\001\n\036List" + + "TaskPushNotificationConfig\022-.a2a.v1.List" + + "TaskPushNotificationConfigRequest\032..a2a." + + "v1.ListTaskPushNotificationConfigRespons" + + "e\"=\332A\006parent\202\323\344\223\002.\022,/v1/{parent=tasks/*}" + + "/pushNotificationConfigs\022m\n\024GetExtendedA" + + "gentCard\022#.a2a.v1.GetExtendedAgentCardRe" + + "quest\032\021.a2a.v1.AgentCard\"\035\202\323\344\223\002\027\022\025/v1/ex" + + "tendedAgentCard\022\250\001\n DeleteTaskPushNotifi" + "cationConfig\022/.a2a.v1.DeleteTaskPushNoti" + "ficationConfigRequest\032\026.google.protobuf." + "Empty\";\332A\004name\202\323\344\223\002.*,/v1/{name=tasks/*/" + @@ -541,7 +560,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_TaskStatus_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskStatus_descriptor, - new java.lang.String[] { "State", "Update", "Timestamp", }); + new java.lang.String[] { "State", "Message", "Timestamp", }); internal_static_a2a_v1_Part_descriptor = getDescriptor().getMessageTypes().get(3); internal_static_a2a_v1_Part_fieldAccessorTable = new @@ -553,7 +572,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_FilePart_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_FilePart_descriptor, - new java.lang.String[] { "FileWithUri", "FileWithBytes", "MimeType", "Name", "File", }); + new java.lang.String[] { "FileWithUri", "FileWithBytes", "MediaType", "Name", "File", }); internal_static_a2a_v1_DataPart_descriptor = getDescriptor().getMessageTypes().get(5); internal_static_a2a_v1_DataPart_fieldAccessorTable = new @@ -601,13 +620,13 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentInterface_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentInterface_descriptor, - new java.lang.String[] { "Url", "Transport", }); + new java.lang.String[] { "Url", "ProtocolBinding", }); internal_static_a2a_v1_AgentCard_descriptor = getDescriptor().getMessageTypes().get(13); internal_static_a2a_v1_AgentCard_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCard_descriptor, - new java.lang.String[] { "ProtocolVersion", "Name", "Description", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", "Signatures", "IconUrl", }); + new java.lang.String[] { "ProtocolVersion", "Name", "Description", "SupportedInterfaces", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", "Signatures", "IconUrl", }); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor = internal_static_a2a_v1_AgentCard_descriptor.getNestedTypes().get(0); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new @@ -775,13 +794,13 @@ public static void registerAllExtensions( internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTasksRequest_descriptor, - new java.lang.String[] { "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "LastUpdatedTime", "IncludeArtifacts", }); + new java.lang.String[] { "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "LastUpdatedAfter", "IncludeArtifacts", "Metadata", }); internal_static_a2a_v1_ListTasksResponse_descriptor = getDescriptor().getMessageTypes().get(36); internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTasksResponse_descriptor, - new java.lang.String[] { "Tasks", "NextPageToken", "TotalSize", }); + new java.lang.String[] { "Tasks", "NextPageToken", "PageSize", "TotalSize", }); internal_static_a2a_v1_CancelTaskRequest_descriptor = getDescriptor().getMessageTypes().get(37); internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new @@ -800,17 +819,17 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Name", }); - internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor = + internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageTypes().get(40); - internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable = new + internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor, + internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Parent", "ConfigId", "Config", }); - internal_static_a2a_v1_TaskSubscriptionRequest_descriptor = + internal_static_a2a_v1_SubscribeToTaskRequest_descriptor = getDescriptor().getMessageTypes().get(41); - internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable = new + internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_TaskSubscriptionRequest_descriptor, + internal_static_a2a_v1_SubscribeToTaskRequest_descriptor, new java.lang.String[] { "Name", }); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageTypes().get(42); @@ -818,11 +837,11 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Parent", "PageSize", "PageToken", }); - internal_static_a2a_v1_GetAgentCardRequest_descriptor = + internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor = getDescriptor().getMessageTypes().get(43); - internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable = new + internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_GetAgentCardRequest_descriptor, + internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor, new java.lang.String[] { }); internal_static_a2a_v1_SendMessageResponse_descriptor = getDescriptor().getMessageTypes().get(44); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java index 3b1dbefcd..a20922afe 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -4,16 +4,7 @@ /** *

- * A2AService defines the gRPC version of the A2A protocol. This has a slightly
- * different shape than the JSONRPC version to better conform to AIP-127,
- * where appropriate. The nouns are AgentCard, Message, Task and
- * TaskPushNotificationConfig.
- * - Messages are not a standard resource so there is no get/delete/update/list
- *   interface, only a send and stream custom methods.
- * - Tasks have a get interface and custom cancel and subscribe methods.
- * - TaskPushNotificationConfig are a resource whose parent is a task.
- *   They have get, list and create methods.
- * - AgentCard is a static resource with only a get method.
+ * A2AService defines the operations of the A2A protocol.
  * 
*/ @javax.annotation.Generated( @@ -182,66 +173,66 @@ io.a2a.grpc.Task> getCancelTaskMethod() { return getCancelTaskMethod; } - private static volatile io.grpc.MethodDescriptor getTaskSubscriptionMethod; + private static volatile io.grpc.MethodDescriptor getSubscribeToTaskMethod; @io.grpc.stub.annotations.RpcMethod( - fullMethodName = SERVICE_NAME + '/' + "TaskSubscription", - requestType = io.a2a.grpc.TaskSubscriptionRequest.class, + fullMethodName = SERVICE_NAME + '/' + "SubscribeToTask", + requestType = io.a2a.grpc.SubscribeToTaskRequest.class, responseType = io.a2a.grpc.StreamResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) - public static io.grpc.MethodDescriptor getTaskSubscriptionMethod() { - io.grpc.MethodDescriptor getTaskSubscriptionMethod; - if ((getTaskSubscriptionMethod = A2AServiceGrpc.getTaskSubscriptionMethod) == null) { + public static io.grpc.MethodDescriptor getSubscribeToTaskMethod() { + io.grpc.MethodDescriptor getSubscribeToTaskMethod; + if ((getSubscribeToTaskMethod = A2AServiceGrpc.getSubscribeToTaskMethod) == null) { synchronized (A2AServiceGrpc.class) { - if ((getTaskSubscriptionMethod = A2AServiceGrpc.getTaskSubscriptionMethod) == null) { - A2AServiceGrpc.getTaskSubscriptionMethod = getTaskSubscriptionMethod = - io.grpc.MethodDescriptor.newBuilder() + if ((getSubscribeToTaskMethod = A2AServiceGrpc.getSubscribeToTaskMethod) == null) { + A2AServiceGrpc.getSubscribeToTaskMethod = getSubscribeToTaskMethod = + io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "TaskSubscription")) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SubscribeToTask")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.a2a.grpc.TaskSubscriptionRequest.getDefaultInstance())) + io.a2a.grpc.SubscribeToTaskRequest.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( io.a2a.grpc.StreamResponse.getDefaultInstance())) - .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("TaskSubscription")) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("SubscribeToTask")) .build(); } } } - return getTaskSubscriptionMethod; + return getSubscribeToTaskMethod; } - private static volatile io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod; + private static volatile io.grpc.MethodDescriptor getSetTaskPushNotificationConfigMethod; @io.grpc.stub.annotations.RpcMethod( - fullMethodName = SERVICE_NAME + '/' + "CreateTaskPushNotificationConfig", - requestType = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, + fullMethodName = SERVICE_NAME + '/' + "SetTaskPushNotificationConfig", + requestType = io.a2a.grpc.SetTaskPushNotificationConfigRequest.class, responseType = io.a2a.grpc.TaskPushNotificationConfig.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) - public static io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod() { - io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod; - if ((getCreateTaskPushNotificationConfigMethod = A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod) == null) { + public static io.grpc.MethodDescriptor getSetTaskPushNotificationConfigMethod() { + io.grpc.MethodDescriptor getSetTaskPushNotificationConfigMethod; + if ((getSetTaskPushNotificationConfigMethod = A2AServiceGrpc.getSetTaskPushNotificationConfigMethod) == null) { synchronized (A2AServiceGrpc.class) { - if ((getCreateTaskPushNotificationConfigMethod = A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod) == null) { - A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod = getCreateTaskPushNotificationConfigMethod = - io.grpc.MethodDescriptor.newBuilder() + if ((getSetTaskPushNotificationConfigMethod = A2AServiceGrpc.getSetTaskPushNotificationConfigMethod) == null) { + A2AServiceGrpc.getSetTaskPushNotificationConfigMethod = getSetTaskPushNotificationConfigMethod = + io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.UNARY) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "CreateTaskPushNotificationConfig")) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SetTaskPushNotificationConfig")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance())) + io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance())) - .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("CreateTaskPushNotificationConfig")) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("SetTaskPushNotificationConfig")) .build(); } } } - return getCreateTaskPushNotificationConfigMethod; + return getSetTaskPushNotificationConfigMethod; } private static volatile io.grpc.MethodDescriptor getListTaskPushNotificationC return getListTaskPushNotificationConfigMethod; } - private static volatile io.grpc.MethodDescriptor getGetAgentCardMethod; + private static volatile io.grpc.MethodDescriptor getGetExtendedAgentCardMethod; @io.grpc.stub.annotations.RpcMethod( - fullMethodName = SERVICE_NAME + '/' + "GetAgentCard", - requestType = io.a2a.grpc.GetAgentCardRequest.class, + fullMethodName = SERVICE_NAME + '/' + "GetExtendedAgentCard", + requestType = io.a2a.grpc.GetExtendedAgentCardRequest.class, responseType = io.a2a.grpc.AgentCard.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) - public static io.grpc.MethodDescriptor getGetAgentCardMethod() { - io.grpc.MethodDescriptor getGetAgentCardMethod; - if ((getGetAgentCardMethod = A2AServiceGrpc.getGetAgentCardMethod) == null) { + public static io.grpc.MethodDescriptor getGetExtendedAgentCardMethod() { + io.grpc.MethodDescriptor getGetExtendedAgentCardMethod; + if ((getGetExtendedAgentCardMethod = A2AServiceGrpc.getGetExtendedAgentCardMethod) == null) { synchronized (A2AServiceGrpc.class) { - if ((getGetAgentCardMethod = A2AServiceGrpc.getGetAgentCardMethod) == null) { - A2AServiceGrpc.getGetAgentCardMethod = getGetAgentCardMethod = - io.grpc.MethodDescriptor.newBuilder() + if ((getGetExtendedAgentCardMethod = A2AServiceGrpc.getGetExtendedAgentCardMethod) == null) { + A2AServiceGrpc.getGetExtendedAgentCardMethod = getGetExtendedAgentCardMethod = + io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.UNARY) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetAgentCard")) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetExtendedAgentCard")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.a2a.grpc.GetAgentCardRequest.getDefaultInstance())) + io.a2a.grpc.GetExtendedAgentCardRequest.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( io.a2a.grpc.AgentCard.getDefaultInstance())) - .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("GetAgentCard")) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("GetExtendedAgentCard")) .build(); } } } - return getGetAgentCardMethod; + return getGetExtendedAgentCardMethod; } private static volatile io.grpc.MethodDescriptor - * A2AService defines the gRPC version of the A2A protocol. This has a slightly - * different shape than the JSONRPC version to better conform to AIP-127, - * where appropriate. The nouns are AgentCard, Message, Task and - * TaskPushNotificationConfig. - * - Messages are not a standard resource so there is no get/delete/update/list - * interface, only a send and stream custom methods. - * - Tasks have a get interface and custom cancel and subscribe methods. - * - TaskPushNotificationConfig are a resource whose parent is a task. - * They have get, list and create methods. - * - AgentCard is a static resource with only a get method. + * A2AService defines the operations of the A2A protocol. *
*/ public interface AsyncService { /** *
-     * Send a message to the agent. This is a blocking call that will return the
-     * task once it is completed, or a LRO if requested.
+     * Send a message to the agent.
      * 
*/ default void sendMessage(io.a2a.grpc.SendMessageRequest request, @@ -456,8 +437,7 @@ default void sendMessage(io.a2a.grpc.SendMessageRequest request, /** *
-     * SendStreamingMessage is a streaming call that will return a stream of
-     * task update events until the Task is in an interrupted or terminal state.
+     * SendStreamingMessage is a streaming version of SendMessage.
      * 
*/ default void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, @@ -487,8 +467,7 @@ default void listTasks(io.a2a.grpc.ListTasksRequest request, /** *
-     * Cancel a task from the agent. If supported one should expect no
-     * more task updates for the task.
+     * Cancel a task.
      * 
*/ default void cancelTask(io.a2a.grpc.CancelTaskRequest request, @@ -498,15 +477,13 @@ default void cancelTask(io.a2a.grpc.CancelTaskRequest request, /** *
-     * TaskSubscription is a streaming call that will return a stream of task
-     * update events. This attaches the stream to an existing in process task.
-     * If the task is complete the stream will return the completed task (like
-     * GetTask) and close the stream.
+     * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
+     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
      * 
*/ - default void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, + default void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getTaskSubscriptionMethod(), responseObserver); + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSubscribeToTaskMethod(), responseObserver); } /** @@ -514,9 +491,9 @@ default void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, * Set a push notification config for a task. *
*/ - default void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, + default void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request, io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCreateTaskPushNotificationConfigMethod(), responseObserver); + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSetTaskPushNotificationConfigMethod(), responseObserver); } /** @@ -541,12 +518,12 @@ default void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotification /** *
-     * GetAgentCard returns the agent card for the agent.
+     * GetExtendedAgentCard returns the extended agent card for authenticated agents.
      * 
*/ - default void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, + default void getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request, io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetAgentCardMethod(), responseObserver); + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetExtendedAgentCardMethod(), responseObserver); } /** @@ -563,16 +540,7 @@ default void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotifica /** * Base class for the server implementation of the service A2AService. *
-   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
-   * different shape than the JSONRPC version to better conform to AIP-127,
-   * where appropriate. The nouns are AgentCard, Message, Task and
-   * TaskPushNotificationConfig.
-   * - Messages are not a standard resource so there is no get/delete/update/list
-   *   interface, only a send and stream custom methods.
-   * - Tasks have a get interface and custom cancel and subscribe methods.
-   * - TaskPushNotificationConfig are a resource whose parent is a task.
-   *   They have get, list and create methods.
-   * - AgentCard is a static resource with only a get method.
+   * A2AService defines the operations of the A2A protocol.
    * 
*/ public static abstract class A2AServiceImplBase @@ -586,16 +554,7 @@ public static abstract class A2AServiceImplBase /** * A stub to allow clients to do asynchronous rpc calls to service A2AService. *
-   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
-   * different shape than the JSONRPC version to better conform to AIP-127,
-   * where appropriate. The nouns are AgentCard, Message, Task and
-   * TaskPushNotificationConfig.
-   * - Messages are not a standard resource so there is no get/delete/update/list
-   *   interface, only a send and stream custom methods.
-   * - Tasks have a get interface and custom cancel and subscribe methods.
-   * - TaskPushNotificationConfig are a resource whose parent is a task.
-   *   They have get, list and create methods.
-   * - AgentCard is a static resource with only a get method.
+   * A2AService defines the operations of the A2A protocol.
    * 
*/ public static final class A2AServiceStub @@ -613,8 +572,7 @@ protected A2AServiceStub build( /** *
-     * Send a message to the agent. This is a blocking call that will return the
-     * task once it is completed, or a LRO if requested.
+     * Send a message to the agent.
      * 
*/ public void sendMessage(io.a2a.grpc.SendMessageRequest request, @@ -625,8 +583,7 @@ public void sendMessage(io.a2a.grpc.SendMessageRequest request, /** *
-     * SendStreamingMessage is a streaming call that will return a stream of
-     * task update events until the Task is in an interrupted or terminal state.
+     * SendStreamingMessage is a streaming version of SendMessage.
      * 
*/ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, @@ -659,8 +616,7 @@ public void listTasks(io.a2a.grpc.ListTasksRequest request, /** *
-     * Cancel a task from the agent. If supported one should expect no
-     * more task updates for the task.
+     * Cancel a task.
      * 
*/ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, @@ -671,16 +627,14 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, /** *
-     * TaskSubscription is a streaming call that will return a stream of task
-     * update events. This attaches the stream to an existing in process task.
-     * If the task is complete the stream will return the completed task (like
-     * GetTask) and close the stream.
+     * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
+     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
      * 
*/ - public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, + public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, io.grpc.stub.StreamObserver responseObserver) { io.grpc.stub.ClientCalls.asyncServerStreamingCall( - getChannel().newCall(getTaskSubscriptionMethod(), getCallOptions()), request, responseObserver); + getChannel().newCall(getSubscribeToTaskMethod(), getCallOptions()), request, responseObserver); } /** @@ -688,10 +642,10 @@ public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, * Set a push notification config for a task. *
*/ - public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, + public void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request, io.grpc.stub.StreamObserver responseObserver) { io.grpc.stub.ClientCalls.asyncUnaryCall( - getChannel().newCall(getCreateTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); + getChannel().newCall(getSetTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); } /** @@ -718,13 +672,13 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC /** *
-     * GetAgentCard returns the agent card for the agent.
+     * GetExtendedAgentCard returns the extended agent card for authenticated agents.
      * 
*/ - public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, + public void getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request, io.grpc.stub.StreamObserver responseObserver) { io.grpc.stub.ClientCalls.asyncUnaryCall( - getChannel().newCall(getGetAgentCardMethod(), getCallOptions()), request, responseObserver); + getChannel().newCall(getGetExtendedAgentCardMethod(), getCallOptions()), request, responseObserver); } /** @@ -742,16 +696,7 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat /** * A stub to allow clients to do synchronous rpc calls to service A2AService. *
-   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
-   * different shape than the JSONRPC version to better conform to AIP-127,
-   * where appropriate. The nouns are AgentCard, Message, Task and
-   * TaskPushNotificationConfig.
-   * - Messages are not a standard resource so there is no get/delete/update/list
-   *   interface, only a send and stream custom methods.
-   * - Tasks have a get interface and custom cancel and subscribe methods.
-   * - TaskPushNotificationConfig are a resource whose parent is a task.
-   *   They have get, list and create methods.
-   * - AgentCard is a static resource with only a get method.
+   * A2AService defines the operations of the A2A protocol.
    * 
*/ public static final class A2AServiceBlockingV2Stub @@ -769,8 +714,7 @@ protected A2AServiceBlockingV2Stub build( /** *
-     * Send a message to the agent. This is a blocking call that will return the
-     * task once it is completed, or a LRO if requested.
+     * Send a message to the agent.
      * 
*/ public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageRequest request) { @@ -780,8 +724,7 @@ public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageReques /** *
-     * SendStreamingMessage is a streaming call that will return a stream of
-     * task update events until the Task is in an interrupted or terminal state.
+     * SendStreamingMessage is a streaming version of SendMessage.
      * 
*/ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918") @@ -813,8 +756,7 @@ public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest requ /** *
-     * Cancel a task from the agent. If supported one should expect no
-     * more task updates for the task.
+     * Cancel a task.
      * 
*/ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { @@ -824,17 +766,15 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { /** *
-     * TaskSubscription is a streaming call that will return a stream of task
-     * update events. This attaches the stream to an existing in process task.
-     * If the task is complete the stream will return the completed task (like
-     * GetTask) and close the stream.
+     * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
+     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
      * 
*/ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918") public io.grpc.stub.BlockingClientCall - taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request) { + subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request) { return io.grpc.stub.ClientCalls.blockingV2ServerStreamingCall( - getChannel(), getTaskSubscriptionMethod(), getCallOptions(), request); + getChannel(), getSubscribeToTaskMethod(), getCallOptions(), request); } /** @@ -842,9 +782,9 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { * Set a push notification config for a task. *
*/ - public io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request) { return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getCreateTaskPushNotificationConfigMethod(), getCallOptions(), request); + getChannel(), getSetTaskPushNotificationConfigMethod(), getCallOptions(), request); } /** @@ -869,12 +809,12 @@ public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificati /** *
-     * GetAgentCard returns the agent card for the agent.
+     * GetExtendedAgentCard returns the extended agent card for authenticated agents.
      * 
*/ - public io.a2a.grpc.AgentCard getAgentCard(io.a2a.grpc.GetAgentCardRequest request) { + public io.a2a.grpc.AgentCard getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request) { return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getGetAgentCardMethod(), getCallOptions(), request); + getChannel(), getGetExtendedAgentCardMethod(), getCallOptions(), request); } /** @@ -891,16 +831,7 @@ public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.De /** * A stub to allow clients to do limited synchronous rpc calls to service A2AService. *
-   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
-   * different shape than the JSONRPC version to better conform to AIP-127,
-   * where appropriate. The nouns are AgentCard, Message, Task and
-   * TaskPushNotificationConfig.
-   * - Messages are not a standard resource so there is no get/delete/update/list
-   *   interface, only a send and stream custom methods.
-   * - Tasks have a get interface and custom cancel and subscribe methods.
-   * - TaskPushNotificationConfig are a resource whose parent is a task.
-   *   They have get, list and create methods.
-   * - AgentCard is a static resource with only a get method.
+   * A2AService defines the operations of the A2A protocol.
    * 
*/ public static final class A2AServiceBlockingStub @@ -918,8 +849,7 @@ protected A2AServiceBlockingStub build( /** *
-     * Send a message to the agent. This is a blocking call that will return the
-     * task once it is completed, or a LRO if requested.
+     * Send a message to the agent.
      * 
*/ public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageRequest request) { @@ -929,8 +859,7 @@ public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageReques /** *
-     * SendStreamingMessage is a streaming call that will return a stream of
-     * task update events until the Task is in an interrupted or terminal state.
+     * SendStreamingMessage is a streaming version of SendMessage.
      * 
*/ public java.util.Iterator sendStreamingMessage( @@ -961,8 +890,7 @@ public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest requ /** *
-     * Cancel a task from the agent. If supported one should expect no
-     * more task updates for the task.
+     * Cancel a task.
      * 
*/ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { @@ -972,16 +900,14 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { /** *
-     * TaskSubscription is a streaming call that will return a stream of task
-     * update events. This attaches the stream to an existing in process task.
-     * If the task is complete the stream will return the completed task (like
-     * GetTask) and close the stream.
+     * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
+     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
      * 
*/ - public java.util.Iterator taskSubscription( - io.a2a.grpc.TaskSubscriptionRequest request) { + public java.util.Iterator subscribeToTask( + io.a2a.grpc.SubscribeToTaskRequest request) { return io.grpc.stub.ClientCalls.blockingServerStreamingCall( - getChannel(), getTaskSubscriptionMethod(), getCallOptions(), request); + getChannel(), getSubscribeToTaskMethod(), getCallOptions(), request); } /** @@ -989,9 +915,9 @@ public java.util.Iterator taskSubscription( * Set a push notification config for a task. *
*/ - public io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request) { return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getCreateTaskPushNotificationConfigMethod(), getCallOptions(), request); + getChannel(), getSetTaskPushNotificationConfigMethod(), getCallOptions(), request); } /** @@ -1016,12 +942,12 @@ public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificati /** *
-     * GetAgentCard returns the agent card for the agent.
+     * GetExtendedAgentCard returns the extended agent card for authenticated agents.
      * 
*/ - public io.a2a.grpc.AgentCard getAgentCard(io.a2a.grpc.GetAgentCardRequest request) { + public io.a2a.grpc.AgentCard getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request) { return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getGetAgentCardMethod(), getCallOptions(), request); + getChannel(), getGetExtendedAgentCardMethod(), getCallOptions(), request); } /** @@ -1038,16 +964,7 @@ public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.De /** * A stub to allow clients to do ListenableFuture-style rpc calls to service A2AService. *
-   * A2AService defines the gRPC version of the A2A protocol. This has a slightly
-   * different shape than the JSONRPC version to better conform to AIP-127,
-   * where appropriate. The nouns are AgentCard, Message, Task and
-   * TaskPushNotificationConfig.
-   * - Messages are not a standard resource so there is no get/delete/update/list
-   *   interface, only a send and stream custom methods.
-   * - Tasks have a get interface and custom cancel and subscribe methods.
-   * - TaskPushNotificationConfig are a resource whose parent is a task.
-   *   They have get, list and create methods.
-   * - AgentCard is a static resource with only a get method.
+   * A2AService defines the operations of the A2A protocol.
    * 
*/ public static final class A2AServiceFutureStub @@ -1065,8 +982,7 @@ protected A2AServiceFutureStub build( /** *
-     * Send a message to the agent. This is a blocking call that will return the
-     * task once it is completed, or a LRO if requested.
+     * Send a message to the agent.
      * 
*/ public com.google.common.util.concurrent.ListenableFuture sendMessage( @@ -1099,8 +1015,7 @@ public com.google.common.util.concurrent.ListenableFuture - * Cancel a task from the agent. If supported one should expect no - * more task updates for the task. + * Cancel a task. *
*/ public com.google.common.util.concurrent.ListenableFuture cancelTask( @@ -1114,10 +1029,10 @@ public com.google.common.util.concurrent.ListenableFuture canc * Set a push notification config for a task. *
*/ - public com.google.common.util.concurrent.ListenableFuture createTaskPushNotificationConfig( - io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { + public com.google.common.util.concurrent.ListenableFuture setTaskPushNotificationConfig( + io.a2a.grpc.SetTaskPushNotificationConfigRequest request) { return io.grpc.stub.ClientCalls.futureUnaryCall( - getChannel().newCall(getCreateTaskPushNotificationConfigMethod(), getCallOptions()), request); + getChannel().newCall(getSetTaskPushNotificationConfigMethod(), getCallOptions()), request); } /** @@ -1144,13 +1059,13 @@ public com.google.common.util.concurrent.ListenableFuture - * GetAgentCard returns the agent card for the agent. + * GetExtendedAgentCard returns the extended agent card for authenticated agents. * */ - public com.google.common.util.concurrent.ListenableFuture getAgentCard( - io.a2a.grpc.GetAgentCardRequest request) { + public com.google.common.util.concurrent.ListenableFuture getExtendedAgentCard( + io.a2a.grpc.GetExtendedAgentCardRequest request) { return io.grpc.stub.ClientCalls.futureUnaryCall( - getChannel().newCall(getGetAgentCardMethod(), getCallOptions()), request); + getChannel().newCall(getGetExtendedAgentCardMethod(), getCallOptions()), request); } /** @@ -1170,11 +1085,11 @@ public com.google.common.util.concurrent.ListenableFuture implements @@ -1214,12 +1129,12 @@ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserv serviceImpl.cancelTask((io.a2a.grpc.CancelTaskRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; - case METHODID_TASK_SUBSCRIPTION: - serviceImpl.taskSubscription((io.a2a.grpc.TaskSubscriptionRequest) request, + case METHODID_SUBSCRIBE_TO_TASK: + serviceImpl.subscribeToTask((io.a2a.grpc.SubscribeToTaskRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; - case METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG: - serviceImpl.createTaskPushNotificationConfig((io.a2a.grpc.CreateTaskPushNotificationConfigRequest) request, + case METHODID_SET_TASK_PUSH_NOTIFICATION_CONFIG: + serviceImpl.setTaskPushNotificationConfig((io.a2a.grpc.SetTaskPushNotificationConfigRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; case METHODID_GET_TASK_PUSH_NOTIFICATION_CONFIG: @@ -1230,8 +1145,8 @@ public void invoke(Req request, io.grpc.stub.StreamObserver responseObserv serviceImpl.listTaskPushNotificationConfig((io.a2a.grpc.ListTaskPushNotificationConfigRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; - case METHODID_GET_AGENT_CARD: - serviceImpl.getAgentCard((io.a2a.grpc.GetAgentCardRequest) request, + case METHODID_GET_EXTENDED_AGENT_CARD: + serviceImpl.getExtendedAgentCard((io.a2a.grpc.GetExtendedAgentCardRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; case METHODID_DELETE_TASK_PUSH_NOTIFICATION_CONFIG: @@ -1292,19 +1207,19 @@ public static final io.grpc.ServerServiceDefinition bindService(AsyncService ser io.a2a.grpc.Task>( service, METHODID_CANCEL_TASK))) .addMethod( - getTaskSubscriptionMethod(), + getSubscribeToTaskMethod(), io.grpc.stub.ServerCalls.asyncServerStreamingCall( new MethodHandlers< - io.a2a.grpc.TaskSubscriptionRequest, + io.a2a.grpc.SubscribeToTaskRequest, io.a2a.grpc.StreamResponse>( - service, METHODID_TASK_SUBSCRIPTION))) + service, METHODID_SUBSCRIBE_TO_TASK))) .addMethod( - getCreateTaskPushNotificationConfigMethod(), + getSetTaskPushNotificationConfigMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( new MethodHandlers< - io.a2a.grpc.CreateTaskPushNotificationConfigRequest, + io.a2a.grpc.SetTaskPushNotificationConfigRequest, io.a2a.grpc.TaskPushNotificationConfig>( - service, METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG))) + service, METHODID_SET_TASK_PUSH_NOTIFICATION_CONFIG))) .addMethod( getGetTaskPushNotificationConfigMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( @@ -1320,12 +1235,12 @@ public static final io.grpc.ServerServiceDefinition bindService(AsyncService ser io.a2a.grpc.ListTaskPushNotificationConfigResponse>( service, METHODID_LIST_TASK_PUSH_NOTIFICATION_CONFIG))) .addMethod( - getGetAgentCardMethod(), + getGetExtendedAgentCardMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( new MethodHandlers< - io.a2a.grpc.GetAgentCardRequest, + io.a2a.grpc.GetExtendedAgentCardRequest, io.a2a.grpc.AgentCard>( - service, METHODID_GET_AGENT_CARD))) + service, METHODID_GET_EXTENDED_AGENT_CARD))) .addMethod( getDeleteTaskPushNotificationConfigMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( @@ -1386,11 +1301,11 @@ public static io.grpc.ServiceDescriptor getServiceDescriptor() { .addMethod(getGetTaskMethod()) .addMethod(getListTasksMethod()) .addMethod(getCancelTaskMethod()) - .addMethod(getTaskSubscriptionMethod()) - .addMethod(getCreateTaskPushNotificationConfigMethod()) + .addMethod(getSubscribeToTaskMethod()) + .addMethod(getSetTaskPushNotificationConfigMethod()) .addMethod(getGetTaskPushNotificationConfigMethod()) .addMethod(getListTaskPushNotificationConfigMethod()) - .addMethod(getGetAgentCardMethod()) + .addMethod(getGetExtendedAgentCardMethod()) .addMethod(getDeleteTaskPushNotificationConfigMethod()) .build(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java index 118d0947e..eaefa966d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:APIKeySecurityScheme]
+ * Defines a security scheme using an API key.
  * 
* * Protobuf type {@code a2a.v1.APIKeySecurityScheme} @@ -55,7 +56,7 @@ private APIKeySecurityScheme() { private volatile java.lang.Object description_ = ""; /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -76,7 +77,7 @@ public java.lang.String getDescription() { } /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -102,10 +103,10 @@ public java.lang.String getDescription() { private volatile java.lang.Object location_ = ""; /** *
-   * Location of the API key, valid values are "query", "header", or "cookie"
+   * The location of the API key. Valid values are "query", "header", or "cookie".
    * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The location. */ @java.lang.Override @@ -123,10 +124,10 @@ public java.lang.String getLocation() { } /** *
-   * Location of the API key, valid values are "query", "header", or "cookie"
+   * The location of the API key. Valid values are "query", "header", or "cookie".
    * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for location. */ @java.lang.Override @@ -149,10 +150,10 @@ public java.lang.String getLocation() { private volatile java.lang.Object name_ = ""; /** *
-   * Name of the header, query or cookie parameter to be used.
+   * The name of the header, query, or cookie parameter to be used.
    * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ @java.lang.Override @@ -170,10 +171,10 @@ public java.lang.String getName() { } /** *
-   * Name of the header, query or cookie parameter to be used.
+   * The name of the header, query, or cookie parameter to be used.
    * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ @java.lang.Override @@ -370,6 +371,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:APIKeySecurityScheme]
+   * Defines a security scheme using an API key.
    * 
* * Protobuf type {@code a2a.v1.APIKeySecurityScheme} @@ -540,7 +542,7 @@ public Builder mergeFrom( private java.lang.Object description_ = ""; /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -560,7 +562,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -581,7 +583,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -598,7 +600,7 @@ public Builder setDescription( } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -612,7 +614,7 @@ public Builder clearDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -632,10 +634,10 @@ public Builder setDescriptionBytes( private java.lang.Object location_ = ""; /** *
-     * Location of the API key, valid values are "query", "header", or "cookie"
+     * The location of the API key. Valid values are "query", "header", or "cookie".
      * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The location. */ public java.lang.String getLocation() { @@ -652,10 +654,10 @@ public java.lang.String getLocation() { } /** *
-     * Location of the API key, valid values are "query", "header", or "cookie"
+     * The location of the API key. Valid values are "query", "header", or "cookie".
      * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for location. */ public com.google.protobuf.ByteString @@ -673,10 +675,10 @@ public java.lang.String getLocation() { } /** *
-     * Location of the API key, valid values are "query", "header", or "cookie"
+     * The location of the API key. Valid values are "query", "header", or "cookie".
      * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The location to set. * @return This builder for chaining. */ @@ -690,10 +692,10 @@ public Builder setLocation( } /** *
-     * Location of the API key, valid values are "query", "header", or "cookie"
+     * The location of the API key. Valid values are "query", "header", or "cookie".
      * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearLocation() { @@ -704,10 +706,10 @@ public Builder clearLocation() { } /** *
-     * Location of the API key, valid values are "query", "header", or "cookie"
+     * The location of the API key. Valid values are "query", "header", or "cookie".
      * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for location to set. * @return This builder for chaining. */ @@ -724,10 +726,10 @@ public Builder setLocationBytes( private java.lang.Object name_ = ""; /** *
-     * Name of the header, query or cookie parameter to be used.
+     * The name of the header, query, or cookie parameter to be used.
      * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ public java.lang.String getName() { @@ -744,10 +746,10 @@ public java.lang.String getName() { } /** *
-     * Name of the header, query or cookie parameter to be used.
+     * The name of the header, query, or cookie parameter to be used.
      * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -765,10 +767,10 @@ public java.lang.String getName() { } /** *
-     * Name of the header, query or cookie parameter to be used.
+     * The name of the header, query, or cookie parameter to be used.
      * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @param value The name to set. * @return This builder for chaining. */ @@ -782,10 +784,10 @@ public Builder setName( } /** *
-     * Name of the header, query or cookie parameter to be used.
+     * The name of the header, query, or cookie parameter to be used.
      * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearName() { @@ -796,10 +798,10 @@ public Builder clearName() { } /** *
-     * Name of the header, query or cookie parameter to be used.
+     * The name of the header, query, or cookie parameter to be used.
      * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for name to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java index f3e505c6a..3444d5fc3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java @@ -12,7 +12,7 @@ public interface APIKeySecuritySchemeOrBuilder extends /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -21,7 +21,7 @@ public interface APIKeySecuritySchemeOrBuilder extends java.lang.String getDescription(); /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -32,19 +32,19 @@ public interface APIKeySecuritySchemeOrBuilder extends /** *
-   * Location of the API key, valid values are "query", "header", or "cookie"
+   * The location of the API key. Valid values are "query", "header", or "cookie".
    * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The location. */ java.lang.String getLocation(); /** *
-   * Location of the API key, valid values are "query", "header", or "cookie"
+   * The location of the API key. Valid values are "query", "header", or "cookie".
    * 
* - * string location = 2; + * string location = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for location. */ com.google.protobuf.ByteString @@ -52,19 +52,19 @@ public interface APIKeySecuritySchemeOrBuilder extends /** *
-   * Name of the header, query or cookie parameter to be used.
+   * The name of the header, query, or cookie parameter to be used.
    * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ java.lang.String getName(); /** *
-   * Name of the header, query or cookie parameter to be used.
+   * The name of the header, query, or cookie parameter to be used.
    * 
* - * string name = 3; + * string name = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java index a01a1734b..de58e060e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -8,7 +8,7 @@ /** *
  * --8<-- [start:AgentCapabilities]
- * Defines the A2A feature set supported by the agent
+ * Defines optional capabilities supported by an agent.
  * 
* * Protobuf type {@code a2a.v1.AgentCapabilities} @@ -49,14 +49,27 @@ private AgentCapabilities() { io.a2a.grpc.AgentCapabilities.class, io.a2a.grpc.AgentCapabilities.Builder.class); } + private int bitField0_; public static final int STREAMING_FIELD_NUMBER = 1; private boolean streaming_ = false; /** *
-   * If the agent will support streaming responses
+   * Indicates if the agent supports streaming responses.
    * 
* - * bool streaming = 1; + * optional bool streaming = 1; + * @return Whether the streaming field is set. + */ + @java.lang.Override + public boolean hasStreaming() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Indicates if the agent supports streaming responses.
+   * 
+ * + * optional bool streaming = 1; * @return The streaming. */ @java.lang.Override @@ -68,10 +81,22 @@ public boolean getStreaming() { private boolean pushNotifications_ = false; /** *
-   * If the agent can send push notifications to the clients webhook
+   * Indicates if the agent supports sending push notifications for asynchronous task updates.
+   * 
+ * + * optional bool push_notifications = 2; + * @return Whether the pushNotifications field is set. + */ + @java.lang.Override + public boolean hasPushNotifications() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * Indicates if the agent supports sending push notifications for asynchronous task updates.
    * 
* - * bool push_notifications = 2; + * optional bool push_notifications = 2; * @return The pushNotifications. */ @java.lang.Override @@ -84,7 +109,7 @@ public boolean getPushNotifications() { private java.util.List extensions_; /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -95,7 +120,7 @@ public java.util.List getExtensionsList() { } /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -107,7 +132,7 @@ public java.util.List getExtensionsList() { } /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -118,7 +143,7 @@ public int getExtensionsCount() { } /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -129,7 +154,7 @@ public io.a2a.grpc.AgentExtension getExtensions(int index) { } /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -144,10 +169,22 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( private boolean stateTransitionHistory_ = false; /** *
-   * If the agent provides a history of state transitions for a task.
+   * Indicates if the agent provides a history of state transitions for a task.
+   * 
+ * + * optional bool state_transition_history = 4; + * @return Whether the stateTransitionHistory field is set. + */ + @java.lang.Override + public boolean hasStateTransitionHistory() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+   * Indicates if the agent provides a history of state transitions for a task.
    * 
* - * bool state_transition_history = 4; + * optional bool state_transition_history = 4; * @return The stateTransitionHistory. */ @java.lang.Override @@ -169,16 +206,16 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (streaming_ != false) { + if (((bitField0_ & 0x00000001) != 0)) { output.writeBool(1, streaming_); } - if (pushNotifications_ != false) { + if (((bitField0_ & 0x00000002) != 0)) { output.writeBool(2, pushNotifications_); } for (int i = 0; i < extensions_.size(); i++) { output.writeMessage(3, extensions_.get(i)); } - if (stateTransitionHistory_ != false) { + if (((bitField0_ & 0x00000004) != 0)) { output.writeBool(4, stateTransitionHistory_); } getUnknownFields().writeTo(output); @@ -190,11 +227,11 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (streaming_ != false) { + if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream .computeBoolSize(1, streaming_); } - if (pushNotifications_ != false) { + if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream .computeBoolSize(2, pushNotifications_); } @@ -202,7 +239,7 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, extensions_.get(i)); } - if (stateTransitionHistory_ != false) { + if (((bitField0_ & 0x00000004) != 0)) { size += com.google.protobuf.CodedOutputStream .computeBoolSize(4, stateTransitionHistory_); } @@ -221,14 +258,23 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.AgentCapabilities other = (io.a2a.grpc.AgentCapabilities) obj; - if (getStreaming() - != other.getStreaming()) return false; - if (getPushNotifications() - != other.getPushNotifications()) return false; + if (hasStreaming() != other.hasStreaming()) return false; + if (hasStreaming()) { + if (getStreaming() + != other.getStreaming()) return false; + } + if (hasPushNotifications() != other.hasPushNotifications()) return false; + if (hasPushNotifications()) { + if (getPushNotifications() + != other.getPushNotifications()) return false; + } if (!getExtensionsList() .equals(other.getExtensionsList())) return false; - if (getStateTransitionHistory() - != other.getStateTransitionHistory()) return false; + if (hasStateTransitionHistory() != other.hasStateTransitionHistory()) return false; + if (hasStateTransitionHistory()) { + if (getStateTransitionHistory() + != other.getStateTransitionHistory()) return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -240,19 +286,25 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + STREAMING_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getStreaming()); - hash = (37 * hash) + PUSH_NOTIFICATIONS_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getPushNotifications()); + if (hasStreaming()) { + hash = (37 * hash) + STREAMING_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getStreaming()); + } + if (hasPushNotifications()) { + hash = (37 * hash) + PUSH_NOTIFICATIONS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getPushNotifications()); + } if (getExtensionsCount() > 0) { hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER; hash = (53 * hash) + getExtensionsList().hashCode(); } - hash = (37 * hash) + STATE_TRANSITION_HISTORY_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getStateTransitionHistory()); + if (hasStateTransitionHistory()) { + hash = (37 * hash) + STATE_TRANSITION_HISTORY_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getStateTransitionHistory()); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -353,7 +405,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AgentCapabilities]
-   * Defines the A2A feature set supported by the agent
+   * Defines optional capabilities supported by an agent.
    * 
* * Protobuf type {@code a2a.v1.AgentCapabilities} @@ -445,15 +497,20 @@ private void buildPartialRepeatedFields(io.a2a.grpc.AgentCapabilities result) { private void buildPartial0(io.a2a.grpc.AgentCapabilities result) { int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) != 0)) { result.streaming_ = streaming_; + to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000002) != 0)) { result.pushNotifications_ = pushNotifications_; + to_bitField0_ |= 0x00000002; } if (((from_bitField0_ & 0x00000008) != 0)) { result.stateTransitionHistory_ = stateTransitionHistory_; + to_bitField0_ |= 0x00000004; } + result.bitField0_ |= to_bitField0_; } @java.lang.Override @@ -468,10 +525,10 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.AgentCapabilities other) { if (other == io.a2a.grpc.AgentCapabilities.getDefaultInstance()) return this; - if (other.getStreaming() != false) { + if (other.hasStreaming()) { setStreaming(other.getStreaming()); } - if (other.getPushNotifications() != false) { + if (other.hasPushNotifications()) { setPushNotifications(other.getPushNotifications()); } if (extensionsBuilder_ == null) { @@ -500,7 +557,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCapabilities other) { } } } - if (other.getStateTransitionHistory() != false) { + if (other.hasStateTransitionHistory()) { setStateTransitionHistory(other.getStateTransitionHistory()); } this.mergeUnknownFields(other.getUnknownFields()); @@ -577,10 +634,22 @@ public Builder mergeFrom( private boolean streaming_ ; /** *
-     * If the agent will support streaming responses
+     * Indicates if the agent supports streaming responses.
+     * 
+ * + * optional bool streaming = 1; + * @return Whether the streaming field is set. + */ + @java.lang.Override + public boolean hasStreaming() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+     * Indicates if the agent supports streaming responses.
      * 
* - * bool streaming = 1; + * optional bool streaming = 1; * @return The streaming. */ @java.lang.Override @@ -589,10 +658,10 @@ public boolean getStreaming() { } /** *
-     * If the agent will support streaming responses
+     * Indicates if the agent supports streaming responses.
      * 
* - * bool streaming = 1; + * optional bool streaming = 1; * @param value The streaming to set. * @return This builder for chaining. */ @@ -605,10 +674,10 @@ public Builder setStreaming(boolean value) { } /** *
-     * If the agent will support streaming responses
+     * Indicates if the agent supports streaming responses.
      * 
* - * bool streaming = 1; + * optional bool streaming = 1; * @return This builder for chaining. */ public Builder clearStreaming() { @@ -621,10 +690,22 @@ public Builder clearStreaming() { private boolean pushNotifications_ ; /** *
-     * If the agent can send push notifications to the clients webhook
+     * Indicates if the agent supports sending push notifications for asynchronous task updates.
+     * 
+ * + * optional bool push_notifications = 2; + * @return Whether the pushNotifications field is set. + */ + @java.lang.Override + public boolean hasPushNotifications() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+     * Indicates if the agent supports sending push notifications for asynchronous task updates.
      * 
* - * bool push_notifications = 2; + * optional bool push_notifications = 2; * @return The pushNotifications. */ @java.lang.Override @@ -633,10 +714,10 @@ public boolean getPushNotifications() { } /** *
-     * If the agent can send push notifications to the clients webhook
+     * Indicates if the agent supports sending push notifications for asynchronous task updates.
      * 
* - * bool push_notifications = 2; + * optional bool push_notifications = 2; * @param value The pushNotifications to set. * @return This builder for chaining. */ @@ -649,10 +730,10 @@ public Builder setPushNotifications(boolean value) { } /** *
-     * If the agent can send push notifications to the clients webhook
+     * Indicates if the agent supports sending push notifications for asynchronous task updates.
      * 
* - * bool push_notifications = 2; + * optional bool push_notifications = 2; * @return This builder for chaining. */ public Builder clearPushNotifications() { @@ -676,7 +757,7 @@ private void ensureExtensionsIsMutable() { /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -690,7 +771,7 @@ public java.util.List getExtensionsList() { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -704,7 +785,7 @@ public int getExtensionsCount() { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -718,7 +799,7 @@ public io.a2a.grpc.AgentExtension getExtensions(int index) { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -739,7 +820,7 @@ public Builder setExtensions( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -757,7 +838,7 @@ public Builder setExtensions( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -777,7 +858,7 @@ public Builder addExtensions(io.a2a.grpc.AgentExtension value) { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -798,7 +879,7 @@ public Builder addExtensions( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -816,7 +897,7 @@ public Builder addExtensions( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -834,7 +915,7 @@ public Builder addExtensions( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -853,7 +934,7 @@ public Builder addAllExtensions( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -870,7 +951,7 @@ public Builder clearExtensions() { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -887,7 +968,7 @@ public Builder removeExtensions(int index) { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -898,7 +979,7 @@ public io.a2a.grpc.AgentExtension.Builder getExtensionsBuilder( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -912,7 +993,7 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -927,7 +1008,7 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -938,7 +1019,7 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder() { } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -950,7 +1031,7 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder( } /** *
-     * Extensions supported by this agent.
+     * A list of protocol extensions supported by the agent.
      * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -977,10 +1058,22 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder( private boolean stateTransitionHistory_ ; /** *
-     * If the agent provides a history of state transitions for a task.
+     * Indicates if the agent provides a history of state transitions for a task.
+     * 
+ * + * optional bool state_transition_history = 4; + * @return Whether the stateTransitionHistory field is set. + */ + @java.lang.Override + public boolean hasStateTransitionHistory() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + *
+     * Indicates if the agent provides a history of state transitions for a task.
      * 
* - * bool state_transition_history = 4; + * optional bool state_transition_history = 4; * @return The stateTransitionHistory. */ @java.lang.Override @@ -989,10 +1082,10 @@ public boolean getStateTransitionHistory() { } /** *
-     * If the agent provides a history of state transitions for a task.
+     * Indicates if the agent provides a history of state transitions for a task.
      * 
* - * bool state_transition_history = 4; + * optional bool state_transition_history = 4; * @param value The stateTransitionHistory to set. * @return This builder for chaining. */ @@ -1005,10 +1098,10 @@ public Builder setStateTransitionHistory(boolean value) { } /** *
-     * If the agent provides a history of state transitions for a task.
+     * Indicates if the agent provides a history of state transitions for a task.
      * 
* - * bool state_transition_history = 4; + * optional bool state_transition_history = 4; * @return This builder for chaining. */ public Builder clearStateTransitionHistory() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java index df1a236ac..32214c333 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java @@ -12,27 +12,45 @@ public interface AgentCapabilitiesOrBuilder extends /** *
-   * If the agent will support streaming responses
+   * Indicates if the agent supports streaming responses.
    * 
* - * bool streaming = 1; + * optional bool streaming = 1; + * @return Whether the streaming field is set. + */ + boolean hasStreaming(); + /** + *
+   * Indicates if the agent supports streaming responses.
+   * 
+ * + * optional bool streaming = 1; * @return The streaming. */ boolean getStreaming(); /** *
-   * If the agent can send push notifications to the clients webhook
+   * Indicates if the agent supports sending push notifications for asynchronous task updates.
    * 
* - * bool push_notifications = 2; + * optional bool push_notifications = 2; + * @return Whether the pushNotifications field is set. + */ + boolean hasPushNotifications(); + /** + *
+   * Indicates if the agent supports sending push notifications for asynchronous task updates.
+   * 
+ * + * optional bool push_notifications = 2; * @return The pushNotifications. */ boolean getPushNotifications(); /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -41,7 +59,7 @@ public interface AgentCapabilitiesOrBuilder extends getExtensionsList(); /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -49,7 +67,7 @@ public interface AgentCapabilitiesOrBuilder extends io.a2a.grpc.AgentExtension getExtensions(int index); /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -57,7 +75,7 @@ public interface AgentCapabilitiesOrBuilder extends int getExtensionsCount(); /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -66,7 +84,7 @@ public interface AgentCapabilitiesOrBuilder extends getExtensionsOrBuilderList(); /** *
-   * Extensions supported by this agent.
+   * A list of protocol extensions supported by the agent.
    * 
* * repeated .a2a.v1.AgentExtension extensions = 3; @@ -76,10 +94,19 @@ io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( /** *
-   * If the agent provides a history of state transitions for a task.
+   * Indicates if the agent provides a history of state transitions for a task.
+   * 
+ * + * optional bool state_transition_history = 4; + * @return Whether the stateTransitionHistory field is set. + */ + boolean hasStateTransitionHistory(); + /** + *
+   * Indicates if the agent provides a history of state transitions for a task.
    * 
* - * bool state_transition_history = 4; + * optional bool state_transition_history = 4; * @return The stateTransitionHistory. */ boolean getStateTransitionHistory(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index 6a001ac2c..cf214725b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -8,12 +8,15 @@ /** *
  * --8<-- [start:AgentCard]
+ * AgentCard is a self-describing manifest for an agent. It provides essential
+ * metadata including the agent's identity, capabilities, skills, supported
+ * communication methods, and security requirements.
  * AgentCard conveys key information:
  * - Overall details (version, name, description, uses)
  * - Skills; a set of actions/solutions the agent can perform
  * - Default modalities/content types supported by the agent.
  * - Authentication requirements
- * Next ID: 19
+ * Next ID: 20
  * 
* * Protobuf type {@code a2a.v1.AgentCard} @@ -41,6 +44,7 @@ private AgentCard() { protocolVersion_ = ""; name_ = ""; description_ = ""; + supportedInterfaces_ = java.util.Collections.emptyList(); url_ = ""; preferredTransport_ = ""; additionalInterfaces_ = java.util.Collections.emptyList(); @@ -88,9 +92,23 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl /** *
    * The version of the A2A protocol this agent supports.
+   * Default: "1.0"
    * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return Whether the protocolVersion field is set. + */ + @java.lang.Override + public boolean hasProtocolVersion() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The version of the A2A protocol this agent supports.
+   * Default: "1.0"
+   * 
+ * + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return The protocolVersion. */ @java.lang.Override @@ -109,9 +127,10 @@ public java.lang.String getProtocolVersion() { /** *
    * The version of the A2A protocol this agent supports.
+   * Default: "1.0"
    * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for protocolVersion. */ @java.lang.Override @@ -138,7 +157,7 @@ public java.lang.String getProtocolVersion() { * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ @java.lang.Override @@ -160,7 +179,7 @@ public java.lang.String getName() { * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ @java.lang.Override @@ -183,11 +202,12 @@ public java.lang.String getName() { private volatile java.lang.Object description_ = ""; /** *
-   * A description of the agent's domain of action/solution space.
+   * A human-readable description of the agent, assisting users and other agents
+   * in understanding its purpose.
    * Example: "Agent that helps users with recipes and cooking."
    * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The description. */ @java.lang.Override @@ -205,11 +225,12 @@ public java.lang.String getDescription() { } /** *
-   * A description of the agent's domain of action/solution space.
+   * A human-readable description of the agent, assisting users and other agents
+   * in understanding its purpose.
    * Example: "Agent that helps users with recipes and cooking."
    * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for description. */ @java.lang.Override @@ -227,20 +248,96 @@ public java.lang.String getDescription() { } } + public static final int SUPPORTED_INTERFACES_FIELD_NUMBER = 19; + @SuppressWarnings("serial") + private java.util.List supportedInterfaces_; + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + @java.lang.Override + public java.util.List getSupportedInterfacesList() { + return supportedInterfaces_; + } + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + @java.lang.Override + public java.util.List + getSupportedInterfacesOrBuilderList() { + return supportedInterfaces_; + } + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + @java.lang.Override + public int getSupportedInterfacesCount() { + return supportedInterfaces_.size(); + } + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + @java.lang.Override + public io.a2a.grpc.AgentInterface getSupportedInterfaces(int index) { + return supportedInterfaces_.get(index); + } + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + @java.lang.Override + public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( + int index) { + return supportedInterfaces_.get(index); + } + public static final int URL_FIELD_NUMBER = 3; @SuppressWarnings("serial") private volatile java.lang.Object url_ = ""; /** *
-   * A URL to the address the agent is hosted at. This represents the
-   * preferred endpoint as declared by the agent.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 + * @return Whether the url field is set. + */ + @java.lang.Override + @java.lang.Deprecated public boolean hasUrl() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * DEPRECATED: Use 'supported_interfaces' instead.
+   * 
+ * + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return The url. */ @java.lang.Override - public java.lang.String getUrl() { + @java.lang.Deprecated public java.lang.String getUrl() { java.lang.Object ref = url_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; @@ -254,15 +351,16 @@ public java.lang.String getUrl() { } /** *
-   * A URL to the address the agent is hosted at. This represents the
-   * preferred endpoint as declared by the agent.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return The bytes for url. */ @java.lang.Override - public com.google.protobuf.ByteString + @java.lang.Deprecated public com.google.protobuf.ByteString getUrlBytes() { java.lang.Object ref = url_; if (ref instanceof java.lang.String) { @@ -281,14 +379,30 @@ public java.lang.String getUrl() { private volatile java.lang.Object preferredTransport_ = ""; /** *
-   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * DEPRECATED: Use 'supported_interfaces' instead.
+   * 
+ * + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 + * @return Whether the preferredTransport field is set. + */ + @java.lang.Override + @java.lang.Deprecated public boolean hasPreferredTransport() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return The preferredTransport. */ @java.lang.Override - public java.lang.String getPreferredTransport() { + @java.lang.Deprecated public java.lang.String getPreferredTransport() { java.lang.Object ref = preferredTransport_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; @@ -302,14 +416,16 @@ public java.lang.String getPreferredTransport() { } /** *
-   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return The bytes for preferredTransport. */ @java.lang.Override - public com.google.protobuf.ByteString + @java.lang.Deprecated public com.google.protobuf.ByteString getPreferredTransportBytes() { java.lang.Object ref = preferredTransport_; if (ref instanceof java.lang.String) { @@ -328,63 +444,58 @@ public java.lang.String getPreferredTransport() { private java.util.List additionalInterfaces_; /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ @java.lang.Override - public java.util.List getAdditionalInterfacesList() { + @java.lang.Deprecated public java.util.List getAdditionalInterfacesList() { return additionalInterfaces_; } /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ @java.lang.Override - public java.util.List + @java.lang.Deprecated public java.util.List getAdditionalInterfacesOrBuilderList() { return additionalInterfaces_; } /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ @java.lang.Override - public int getAdditionalInterfacesCount() { + @java.lang.Deprecated public int getAdditionalInterfacesCount() { return additionalInterfaces_.size(); } /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ @java.lang.Override - public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { + @java.lang.Deprecated public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { return additionalInterfaces_.get(index); } /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ @java.lang.Override - public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + @java.lang.Deprecated public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( int index) { return additionalInterfaces_.get(index); } @@ -401,7 +512,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( */ @java.lang.Override public boolean hasProvider() { - return ((bitField0_ & 0x00000001) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** *
@@ -436,7 +547,7 @@ public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() {
    * Example: "1.0.0"
    * 
* - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The version. */ @java.lang.Override @@ -458,7 +569,7 @@ public java.lang.String getVersion() { * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for version. */ @java.lang.Override @@ -484,7 +595,19 @@ public java.lang.String getVersion() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; + * @return Whether the documentationUrl field is set. + */ + @java.lang.Override + public boolean hasDocumentationUrl() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + *
+   * A url to provide additional documentation about the agent.
+   * 
+ * + * optional string documentation_url = 6; * @return The documentationUrl. */ @java.lang.Override @@ -505,7 +628,7 @@ public java.lang.String getDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; * @return The bytes for documentationUrl. */ @java.lang.Override @@ -530,19 +653,19 @@ public java.lang.String getDocumentationUrl() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the capabilities field is set. */ @java.lang.Override public boolean hasCapabilities() { - return ((bitField0_ & 0x00000002) != 0); + return ((bitField0_ & 0x00000020) != 0); } /** *
    * A2A Capability set supported by the agent.
    * 
* - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; * @return The capabilities. */ @java.lang.Override @@ -554,7 +677,7 @@ public io.a2a.grpc.AgentCapabilities getCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { @@ -663,18 +786,6 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -687,18 +798,6 @@ public java.util.List getSecurityList() { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -712,18 +811,6 @@ public java.util.List getSecurityList() { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -736,18 +823,6 @@ public int getSecurityCount() { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -760,18 +835,6 @@ public io.a2a.grpc.Security getSecurity(int index) { *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -790,10 +853,10 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the defaultInputModes. */ public com.google.protobuf.ProtocolStringList @@ -804,10 +867,10 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return The count of defaultInputModes. */ public int getDefaultInputModesCount() { @@ -817,10 +880,10 @@ public int getDefaultInputModesCount() { *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The defaultInputModes at the given index. */ @@ -831,10 +894,10 @@ public java.lang.String getDefaultInputModes(int index) { *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the defaultInputModes at the given index. */ @@ -849,10 +912,10 @@ public java.lang.String getDefaultInputModes(int index) { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the defaultOutputModes. */ public com.google.protobuf.ProtocolStringList @@ -861,10 +924,10 @@ public java.lang.String getDefaultInputModes(int index) { } /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return The count of defaultOutputModes. */ public int getDefaultOutputModesCount() { @@ -872,10 +935,10 @@ public int getDefaultOutputModesCount() { } /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The defaultOutputModes at the given index. */ @@ -884,10 +947,10 @@ public java.lang.String getDefaultOutputModes(int index) { } /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the defaultOutputModes at the given index. */ @@ -906,7 +969,7 @@ public java.lang.String getDefaultOutputModes(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List getSkillsList() { @@ -919,7 +982,7 @@ public java.util.List getSkillsList() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List @@ -933,7 +996,7 @@ public java.util.List getSkillsList() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public int getSkillsCount() { @@ -946,7 +1009,7 @@ public int getSkillsCount() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.AgentSkill getSkills(int index) { @@ -959,7 +1022,7 @@ public io.a2a.grpc.AgentSkill getSkills(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( @@ -971,12 +1034,22 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( private boolean supportsAuthenticatedExtendedCard_ = false; /** *
-   * Whether the agent supports providing an extended agent card when
-   * the user is authenticated, i.e. is the card from .well-known
-   * different than the card from GetAgentCard.
+   * Whether the agent supports providing an extended agent card when authenticated.
+   * 
+ * + * optional bool supports_authenticated_extended_card = 13; + * @return Whether the supportsAuthenticatedExtendedCard field is set. + */ + @java.lang.Override + public boolean hasSupportsAuthenticatedExtendedCard() { + return ((bitField0_ & 0x00000040) != 0); + } + /** + *
+   * Whether the agent supports providing an extended agent card when authenticated.
    * 
* - * bool supports_authenticated_extended_card = 13; + * optional bool supports_authenticated_extended_card = 13; * @return The supportsAuthenticatedExtendedCard. */ @java.lang.Override @@ -1053,7 +1126,19 @@ public io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; + * @return Whether the iconUrl field is set. + */ + @java.lang.Override + public boolean hasIconUrl() { + return ((bitField0_ & 0x00000080) != 0); + } + /** + *
+   * An optional URL to an icon for the agent.
+   * 
+ * + * optional string icon_url = 18; * @return The iconUrl. */ @java.lang.Override @@ -1074,7 +1159,7 @@ public java.lang.String getIconUrl() { * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; * @return The bytes for iconUrl. */ @java.lang.Override @@ -1112,19 +1197,19 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, description_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + if (((bitField0_ & 0x00000002) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 3, url_); } - if (((bitField0_ & 0x00000001) != 0)) { + if (((bitField0_ & 0x00000008) != 0)) { output.writeMessage(4, getProvider()); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(version_)) { com.google.protobuf.GeneratedMessage.writeString(output, 5, version_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(documentationUrl_)) { + if (((bitField0_ & 0x00000010) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 6, documentationUrl_); } - if (((bitField0_ & 0x00000002) != 0)) { + if (((bitField0_ & 0x00000020) != 0)) { output.writeMessage(7, getCapabilities()); } com.google.protobuf.GeneratedMessage @@ -1145,24 +1230,27 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < skills_.size(); i++) { output.writeMessage(12, skills_.get(i)); } - if (supportsAuthenticatedExtendedCard_ != false) { + if (((bitField0_ & 0x00000040) != 0)) { output.writeBool(13, supportsAuthenticatedExtendedCard_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(preferredTransport_)) { + if (((bitField0_ & 0x00000004) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 14, preferredTransport_); } for (int i = 0; i < additionalInterfaces_.size(); i++) { output.writeMessage(15, additionalInterfaces_.get(i)); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { + if (((bitField0_ & 0x00000001) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 16, protocolVersion_); } for (int i = 0; i < signatures_.size(); i++) { output.writeMessage(17, signatures_.get(i)); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(iconUrl_)) { + if (((bitField0_ & 0x00000080) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 18, iconUrl_); } + for (int i = 0; i < supportedInterfaces_.size(); i++) { + output.writeMessage(19, supportedInterfaces_.get(i)); + } getUnknownFields().writeTo(output); } @@ -1178,20 +1266,20 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, description_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { + if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(3, url_); } - if (((bitField0_ & 0x00000001) != 0)) { + if (((bitField0_ & 0x00000008) != 0)) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(4, getProvider()); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(version_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(5, version_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(documentationUrl_)) { + if (((bitField0_ & 0x00000010) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(6, documentationUrl_); } - if (((bitField0_ & 0x00000002) != 0)) { + if (((bitField0_ & 0x00000020) != 0)) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(7, getCapabilities()); } @@ -1229,27 +1317,31 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(12, skills_.get(i)); } - if (supportsAuthenticatedExtendedCard_ != false) { + if (((bitField0_ & 0x00000040) != 0)) { size += com.google.protobuf.CodedOutputStream .computeBoolSize(13, supportsAuthenticatedExtendedCard_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(preferredTransport_)) { + if (((bitField0_ & 0x00000004) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(14, preferredTransport_); } for (int i = 0; i < additionalInterfaces_.size(); i++) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(15, additionalInterfaces_.get(i)); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { + if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(16, protocolVersion_); } for (int i = 0; i < signatures_.size(); i++) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(17, signatures_.get(i)); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(iconUrl_)) { + if (((bitField0_ & 0x00000080) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(18, iconUrl_); } + for (int i = 0; i < supportedInterfaces_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(19, supportedInterfaces_.get(i)); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -1265,16 +1357,27 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.AgentCard other = (io.a2a.grpc.AgentCard) obj; - if (!getProtocolVersion() - .equals(other.getProtocolVersion())) return false; + if (hasProtocolVersion() != other.hasProtocolVersion()) return false; + if (hasProtocolVersion()) { + if (!getProtocolVersion() + .equals(other.getProtocolVersion())) return false; + } if (!getName() .equals(other.getName())) return false; if (!getDescription() .equals(other.getDescription())) return false; - if (!getUrl() - .equals(other.getUrl())) return false; - if (!getPreferredTransport() - .equals(other.getPreferredTransport())) return false; + if (!getSupportedInterfacesList() + .equals(other.getSupportedInterfacesList())) return false; + if (hasUrl() != other.hasUrl()) return false; + if (hasUrl()) { + if (!getUrl() + .equals(other.getUrl())) return false; + } + if (hasPreferredTransport() != other.hasPreferredTransport()) return false; + if (hasPreferredTransport()) { + if (!getPreferredTransport() + .equals(other.getPreferredTransport())) return false; + } if (!getAdditionalInterfacesList() .equals(other.getAdditionalInterfacesList())) return false; if (hasProvider() != other.hasProvider()) return false; @@ -1284,8 +1387,11 @@ public boolean equals(final java.lang.Object obj) { } if (!getVersion() .equals(other.getVersion())) return false; - if (!getDocumentationUrl() - .equals(other.getDocumentationUrl())) return false; + if (hasDocumentationUrl() != other.hasDocumentationUrl()) return false; + if (hasDocumentationUrl()) { + if (!getDocumentationUrl() + .equals(other.getDocumentationUrl())) return false; + } if (hasCapabilities() != other.hasCapabilities()) return false; if (hasCapabilities()) { if (!getCapabilities() @@ -1301,12 +1407,18 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getDefaultOutputModesList())) return false; if (!getSkillsList() .equals(other.getSkillsList())) return false; - if (getSupportsAuthenticatedExtendedCard() - != other.getSupportsAuthenticatedExtendedCard()) return false; + if (hasSupportsAuthenticatedExtendedCard() != other.hasSupportsAuthenticatedExtendedCard()) return false; + if (hasSupportsAuthenticatedExtendedCard()) { + if (getSupportsAuthenticatedExtendedCard() + != other.getSupportsAuthenticatedExtendedCard()) return false; + } if (!getSignaturesList() .equals(other.getSignaturesList())) return false; - if (!getIconUrl() - .equals(other.getIconUrl())) return false; + if (hasIconUrl() != other.hasIconUrl()) return false; + if (hasIconUrl()) { + if (!getIconUrl() + .equals(other.getIconUrl())) return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -1318,16 +1430,26 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + PROTOCOL_VERSION_FIELD_NUMBER; - hash = (53 * hash) + getProtocolVersion().hashCode(); + if (hasProtocolVersion()) { + hash = (37 * hash) + PROTOCOL_VERSION_FIELD_NUMBER; + hash = (53 * hash) + getProtocolVersion().hashCode(); + } hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; hash = (53 * hash) + getDescription().hashCode(); - hash = (37 * hash) + URL_FIELD_NUMBER; - hash = (53 * hash) + getUrl().hashCode(); - hash = (37 * hash) + PREFERRED_TRANSPORT_FIELD_NUMBER; - hash = (53 * hash) + getPreferredTransport().hashCode(); + if (getSupportedInterfacesCount() > 0) { + hash = (37 * hash) + SUPPORTED_INTERFACES_FIELD_NUMBER; + hash = (53 * hash) + getSupportedInterfacesList().hashCode(); + } + if (hasUrl()) { + hash = (37 * hash) + URL_FIELD_NUMBER; + hash = (53 * hash) + getUrl().hashCode(); + } + if (hasPreferredTransport()) { + hash = (37 * hash) + PREFERRED_TRANSPORT_FIELD_NUMBER; + hash = (53 * hash) + getPreferredTransport().hashCode(); + } if (getAdditionalInterfacesCount() > 0) { hash = (37 * hash) + ADDITIONAL_INTERFACES_FIELD_NUMBER; hash = (53 * hash) + getAdditionalInterfacesList().hashCode(); @@ -1338,8 +1460,10 @@ public int hashCode() { } hash = (37 * hash) + VERSION_FIELD_NUMBER; hash = (53 * hash) + getVersion().hashCode(); - hash = (37 * hash) + DOCUMENTATION_URL_FIELD_NUMBER; - hash = (53 * hash) + getDocumentationUrl().hashCode(); + if (hasDocumentationUrl()) { + hash = (37 * hash) + DOCUMENTATION_URL_FIELD_NUMBER; + hash = (53 * hash) + getDocumentationUrl().hashCode(); + } if (hasCapabilities()) { hash = (37 * hash) + CAPABILITIES_FIELD_NUMBER; hash = (53 * hash) + getCapabilities().hashCode(); @@ -1364,15 +1488,19 @@ public int hashCode() { hash = (37 * hash) + SKILLS_FIELD_NUMBER; hash = (53 * hash) + getSkillsList().hashCode(); } - hash = (37 * hash) + SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getSupportsAuthenticatedExtendedCard()); + if (hasSupportsAuthenticatedExtendedCard()) { + hash = (37 * hash) + SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getSupportsAuthenticatedExtendedCard()); + } if (getSignaturesCount() > 0) { hash = (37 * hash) + SIGNATURES_FIELD_NUMBER; hash = (53 * hash) + getSignaturesList().hashCode(); } - hash = (37 * hash) + ICON_URL_FIELD_NUMBER; - hash = (53 * hash) + getIconUrl().hashCode(); + if (hasIconUrl()) { + hash = (37 * hash) + ICON_URL_FIELD_NUMBER; + hash = (53 * hash) + getIconUrl().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -1473,12 +1601,15 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AgentCard]
+   * AgentCard is a self-describing manifest for an agent. It provides essential
+   * metadata including the agent's identity, capabilities, skills, supported
+   * communication methods, and security requirements.
    * AgentCard conveys key information:
    * - Overall details (version, name, description, uses)
    * - Skills; a set of actions/solutions the agent can perform
    * - Default modalities/content types supported by the agent.
    * - Authentication requirements
-   * Next ID: 19
+   * Next ID: 20
    * 
* * Protobuf type {@code a2a.v1.AgentCard} @@ -1535,6 +1666,7 @@ private Builder( private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage .alwaysUseFieldBuilders) { + internalGetSupportedInterfacesFieldBuilder(); internalGetAdditionalInterfacesFieldBuilder(); internalGetProviderFieldBuilder(); internalGetCapabilitiesFieldBuilder(); @@ -1550,6 +1682,13 @@ public Builder clear() { protocolVersion_ = ""; name_ = ""; description_ = ""; + if (supportedInterfacesBuilder_ == null) { + supportedInterfaces_ = java.util.Collections.emptyList(); + } else { + supportedInterfaces_ = null; + supportedInterfacesBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000008); url_ = ""; preferredTransport_ = ""; if (additionalInterfacesBuilder_ == null) { @@ -1558,7 +1697,7 @@ public Builder clear() { additionalInterfaces_ = null; additionalInterfacesBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); provider_ = null; if (providerBuilder_ != null) { providerBuilder_.dispose(); @@ -1578,7 +1717,7 @@ public Builder clear() { security_ = null; securityBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00000800); + bitField0_ = (bitField0_ & ~0x00001000); defaultInputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); defaultOutputModes_ = @@ -1589,7 +1728,7 @@ public Builder clear() { skills_ = null; skillsBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00004000); + bitField0_ = (bitField0_ & ~0x00008000); supportsAuthenticatedExtendedCard_ = false; if (signaturesBuilder_ == null) { signatures_ = java.util.Collections.emptyList(); @@ -1597,7 +1736,7 @@ public Builder clear() { signatures_ = null; signaturesBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00010000); + bitField0_ = (bitField0_ & ~0x00020000); iconUrl_ = ""; return this; } @@ -1632,37 +1771,46 @@ public io.a2a.grpc.AgentCard buildPartial() { } private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) { + if (supportedInterfacesBuilder_ == null) { + if (((bitField0_ & 0x00000008) != 0)) { + supportedInterfaces_ = java.util.Collections.unmodifiableList(supportedInterfaces_); + bitField0_ = (bitField0_ & ~0x00000008); + } + result.supportedInterfaces_ = supportedInterfaces_; + } else { + result.supportedInterfaces_ = supportedInterfacesBuilder_.build(); + } if (additionalInterfacesBuilder_ == null) { - if (((bitField0_ & 0x00000020) != 0)) { + if (((bitField0_ & 0x00000040) != 0)) { additionalInterfaces_ = java.util.Collections.unmodifiableList(additionalInterfaces_); - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); } result.additionalInterfaces_ = additionalInterfaces_; } else { result.additionalInterfaces_ = additionalInterfacesBuilder_.build(); } if (securityBuilder_ == null) { - if (((bitField0_ & 0x00000800) != 0)) { + if (((bitField0_ & 0x00001000) != 0)) { security_ = java.util.Collections.unmodifiableList(security_); - bitField0_ = (bitField0_ & ~0x00000800); + bitField0_ = (bitField0_ & ~0x00001000); } result.security_ = security_; } else { result.security_ = securityBuilder_.build(); } if (skillsBuilder_ == null) { - if (((bitField0_ & 0x00004000) != 0)) { + if (((bitField0_ & 0x00008000) != 0)) { skills_ = java.util.Collections.unmodifiableList(skills_); - bitField0_ = (bitField0_ & ~0x00004000); + bitField0_ = (bitField0_ & ~0x00008000); } result.skills_ = skills_; } else { result.skills_ = skillsBuilder_.build(); } if (signaturesBuilder_ == null) { - if (((bitField0_ & 0x00010000) != 0)) { + if (((bitField0_ & 0x00020000) != 0)) { signatures_ = java.util.Collections.unmodifiableList(signatures_); - bitField0_ = (bitField0_ & ~0x00010000); + bitField0_ = (bitField0_ & ~0x00020000); } result.signatures_ = signatures_; } else { @@ -1672,8 +1820,10 @@ private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) { private void buildPartial0(io.a2a.grpc.AgentCard result) { int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) != 0)) { result.protocolVersion_ = protocolVersion_; + to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; @@ -1681,47 +1831,51 @@ private void buildPartial0(io.a2a.grpc.AgentCard result) { if (((from_bitField0_ & 0x00000004) != 0)) { result.description_ = description_; } - if (((from_bitField0_ & 0x00000008) != 0)) { + if (((from_bitField0_ & 0x00000010) != 0)) { result.url_ = url_; + to_bitField0_ |= 0x00000002; } - if (((from_bitField0_ & 0x00000010) != 0)) { + if (((from_bitField0_ & 0x00000020) != 0)) { result.preferredTransport_ = preferredTransport_; + to_bitField0_ |= 0x00000004; } - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000040) != 0)) { + if (((from_bitField0_ & 0x00000080) != 0)) { result.provider_ = providerBuilder_ == null ? provider_ : providerBuilder_.build(); - to_bitField0_ |= 0x00000001; + to_bitField0_ |= 0x00000008; } - if (((from_bitField0_ & 0x00000080) != 0)) { + if (((from_bitField0_ & 0x00000100) != 0)) { result.version_ = version_; } - if (((from_bitField0_ & 0x00000100) != 0)) { + if (((from_bitField0_ & 0x00000200) != 0)) { result.documentationUrl_ = documentationUrl_; + to_bitField0_ |= 0x00000010; } - if (((from_bitField0_ & 0x00000200) != 0)) { + if (((from_bitField0_ & 0x00000400) != 0)) { result.capabilities_ = capabilitiesBuilder_ == null ? capabilities_ : capabilitiesBuilder_.build(); - to_bitField0_ |= 0x00000002; + to_bitField0_ |= 0x00000020; } - if (((from_bitField0_ & 0x00000400) != 0)) { + if (((from_bitField0_ & 0x00000800) != 0)) { result.securitySchemes_ = internalGetSecuritySchemes().build(SecuritySchemesDefaultEntryHolder.defaultEntry); } - if (((from_bitField0_ & 0x00001000) != 0)) { + if (((from_bitField0_ & 0x00002000) != 0)) { defaultInputModes_.makeImmutable(); result.defaultInputModes_ = defaultInputModes_; } - if (((from_bitField0_ & 0x00002000) != 0)) { + if (((from_bitField0_ & 0x00004000) != 0)) { defaultOutputModes_.makeImmutable(); result.defaultOutputModes_ = defaultOutputModes_; } - if (((from_bitField0_ & 0x00008000) != 0)) { + if (((from_bitField0_ & 0x00010000) != 0)) { result.supportsAuthenticatedExtendedCard_ = supportsAuthenticatedExtendedCard_; + to_bitField0_ |= 0x00000040; } - if (((from_bitField0_ & 0x00020000) != 0)) { + if (((from_bitField0_ & 0x00040000) != 0)) { result.iconUrl_ = iconUrl_; + to_bitField0_ |= 0x00000080; } result.bitField0_ |= to_bitField0_; } @@ -1738,7 +1892,7 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (other == io.a2a.grpc.AgentCard.getDefaultInstance()) return this; - if (!other.getProtocolVersion().isEmpty()) { + if (other.hasProtocolVersion()) { protocolVersion_ = other.protocolVersion_; bitField0_ |= 0x00000001; onChanged(); @@ -1753,21 +1907,47 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { bitField0_ |= 0x00000004; onChanged(); } - if (!other.getUrl().isEmpty()) { + if (supportedInterfacesBuilder_ == null) { + if (!other.supportedInterfaces_.isEmpty()) { + if (supportedInterfaces_.isEmpty()) { + supportedInterfaces_ = other.supportedInterfaces_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.addAll(other.supportedInterfaces_); + } + onChanged(); + } + } else { + if (!other.supportedInterfaces_.isEmpty()) { + if (supportedInterfacesBuilder_.isEmpty()) { + supportedInterfacesBuilder_.dispose(); + supportedInterfacesBuilder_ = null; + supportedInterfaces_ = other.supportedInterfaces_; + bitField0_ = (bitField0_ & ~0x00000008); + supportedInterfacesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + internalGetSupportedInterfacesFieldBuilder() : null; + } else { + supportedInterfacesBuilder_.addAllMessages(other.supportedInterfaces_); + } + } + } + if (other.hasUrl()) { url_ = other.url_; - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); } - if (!other.getPreferredTransport().isEmpty()) { + if (other.hasPreferredTransport()) { preferredTransport_ = other.preferredTransport_; - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; onChanged(); } if (additionalInterfacesBuilder_ == null) { if (!other.additionalInterfaces_.isEmpty()) { if (additionalInterfaces_.isEmpty()) { additionalInterfaces_ = other.additionalInterfaces_; - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); } else { ensureAdditionalInterfacesIsMutable(); additionalInterfaces_.addAll(other.additionalInterfaces_); @@ -1780,7 +1960,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { additionalInterfacesBuilder_.dispose(); additionalInterfacesBuilder_ = null; additionalInterfaces_ = other.additionalInterfaces_; - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); additionalInterfacesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetAdditionalInterfacesFieldBuilder() : null; @@ -1794,12 +1974,12 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } if (!other.getVersion().isEmpty()) { version_ = other.version_; - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000100; onChanged(); } - if (!other.getDocumentationUrl().isEmpty()) { + if (other.hasDocumentationUrl()) { documentationUrl_ = other.documentationUrl_; - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); } if (other.hasCapabilities()) { @@ -1807,12 +1987,12 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } internalGetMutableSecuritySchemes().mergeFrom( other.internalGetSecuritySchemes()); - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; if (securityBuilder_ == null) { if (!other.security_.isEmpty()) { if (security_.isEmpty()) { security_ = other.security_; - bitField0_ = (bitField0_ & ~0x00000800); + bitField0_ = (bitField0_ & ~0x00001000); } else { ensureSecurityIsMutable(); security_.addAll(other.security_); @@ -1825,7 +2005,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { securityBuilder_.dispose(); securityBuilder_ = null; security_ = other.security_; - bitField0_ = (bitField0_ & ~0x00000800); + bitField0_ = (bitField0_ & ~0x00001000); securityBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetSecurityFieldBuilder() : null; @@ -1837,7 +2017,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (!other.defaultInputModes_.isEmpty()) { if (defaultInputModes_.isEmpty()) { defaultInputModes_ = other.defaultInputModes_; - bitField0_ |= 0x00001000; + bitField0_ |= 0x00002000; } else { ensureDefaultInputModesIsMutable(); defaultInputModes_.addAll(other.defaultInputModes_); @@ -1847,7 +2027,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (!other.defaultOutputModes_.isEmpty()) { if (defaultOutputModes_.isEmpty()) { defaultOutputModes_ = other.defaultOutputModes_; - bitField0_ |= 0x00002000; + bitField0_ |= 0x00004000; } else { ensureDefaultOutputModesIsMutable(); defaultOutputModes_.addAll(other.defaultOutputModes_); @@ -1858,7 +2038,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (!other.skills_.isEmpty()) { if (skills_.isEmpty()) { skills_ = other.skills_; - bitField0_ = (bitField0_ & ~0x00004000); + bitField0_ = (bitField0_ & ~0x00008000); } else { ensureSkillsIsMutable(); skills_.addAll(other.skills_); @@ -1871,7 +2051,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { skillsBuilder_.dispose(); skillsBuilder_ = null; skills_ = other.skills_; - bitField0_ = (bitField0_ & ~0x00004000); + bitField0_ = (bitField0_ & ~0x00008000); skillsBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetSkillsFieldBuilder() : null; @@ -1880,14 +2060,14 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } } } - if (other.getSupportsAuthenticatedExtendedCard() != false) { + if (other.hasSupportsAuthenticatedExtendedCard()) { setSupportsAuthenticatedExtendedCard(other.getSupportsAuthenticatedExtendedCard()); } if (signaturesBuilder_ == null) { if (!other.signatures_.isEmpty()) { if (signatures_.isEmpty()) { signatures_ = other.signatures_; - bitField0_ = (bitField0_ & ~0x00010000); + bitField0_ = (bitField0_ & ~0x00020000); } else { ensureSignaturesIsMutable(); signatures_.addAll(other.signatures_); @@ -1900,7 +2080,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { signaturesBuilder_.dispose(); signaturesBuilder_ = null; signatures_ = other.signatures_; - bitField0_ = (bitField0_ & ~0x00010000); + bitField0_ = (bitField0_ & ~0x00020000); signaturesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetSignaturesFieldBuilder() : null; @@ -1909,9 +2089,9 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } } } - if (!other.getIconUrl().isEmpty()) { + if (other.hasIconUrl()) { iconUrl_ = other.iconUrl_; - bitField0_ |= 0x00020000; + bitField0_ |= 0x00040000; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -1952,31 +2132,31 @@ public Builder mergeFrom( } // case 18 case 26: { url_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; break; } // case 26 case 34: { input.readMessage( internalGetProviderFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; break; } // case 34 case 42: { version_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000100; break; } // case 42 case 50: { documentationUrl_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; break; } // case 50 case 58: { input.readMessage( internalGetCapabilitiesFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; break; } // case 58 case 66: { @@ -1985,7 +2165,7 @@ public Builder mergeFrom( SecuritySchemesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); internalGetMutableSecuritySchemes().ensureBuilderMap().put( securitySchemes__.getKey(), securitySchemes__.getValue()); - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; break; } // case 66 case 74: { @@ -2028,12 +2208,12 @@ public Builder mergeFrom( } // case 98 case 104: { supportsAuthenticatedExtendedCard_ = input.readBool(); - bitField0_ |= 0x00008000; + bitField0_ |= 0x00010000; break; } // case 104 case 114: { preferredTransport_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; break; } // case 114 case 122: { @@ -2069,9 +2249,22 @@ public Builder mergeFrom( } // case 138 case 146: { iconUrl_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00020000; + bitField0_ |= 0x00040000; break; } // case 146 + case 154: { + io.a2a.grpc.AgentInterface m = + input.readMessage( + io.a2a.grpc.AgentInterface.parser(), + extensionRegistry); + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(m); + } else { + supportedInterfacesBuilder_.addMessage(m); + } + break; + } // case 154 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -2093,9 +2286,22 @@ public Builder mergeFrom( /** *
      * The version of the A2A protocol this agent supports.
+     * Default: "1.0"
      * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return Whether the protocolVersion field is set. + */ + public boolean hasProtocolVersion() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+     * The version of the A2A protocol this agent supports.
+     * Default: "1.0"
+     * 
+ * + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return The protocolVersion. */ public java.lang.String getProtocolVersion() { @@ -2113,9 +2319,10 @@ public java.lang.String getProtocolVersion() { /** *
      * The version of the A2A protocol this agent supports.
+     * Default: "1.0"
      * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for protocolVersion. */ public com.google.protobuf.ByteString @@ -2134,9 +2341,10 @@ public java.lang.String getProtocolVersion() { /** *
      * The version of the A2A protocol this agent supports.
+     * Default: "1.0"
      * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @param value The protocolVersion to set. * @return This builder for chaining. */ @@ -2151,9 +2359,10 @@ public Builder setProtocolVersion( /** *
      * The version of the A2A protocol this agent supports.
+     * Default: "1.0"
      * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearProtocolVersion() { @@ -2165,9 +2374,10 @@ public Builder clearProtocolVersion() { /** *
      * The version of the A2A protocol this agent supports.
+     * Default: "1.0"
      * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for protocolVersion to set. * @return This builder for chaining. */ @@ -2188,7 +2398,7 @@ public Builder setProtocolVersionBytes( * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ public java.lang.String getName() { @@ -2209,7 +2419,7 @@ public java.lang.String getName() { * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -2231,7 +2441,7 @@ public java.lang.String getName() { * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The name to set. * @return This builder for chaining. */ @@ -2249,7 +2459,7 @@ public Builder setName( * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearName() { @@ -2264,7 +2474,7 @@ public Builder clearName() { * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -2281,11 +2491,12 @@ public Builder setNameBytes( private java.lang.Object description_ = ""; /** *
-     * A description of the agent's domain of action/solution space.
+     * A human-readable description of the agent, assisting users and other agents
+     * in understanding its purpose.
      * Example: "Agent that helps users with recipes and cooking."
      * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The description. */ public java.lang.String getDescription() { @@ -2302,11 +2513,12 @@ public java.lang.String getDescription() { } /** *
-     * A description of the agent's domain of action/solution space.
+     * A human-readable description of the agent, assisting users and other agents
+     * in understanding its purpose.
      * Example: "Agent that helps users with recipes and cooking."
      * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -2324,11 +2536,12 @@ public java.lang.String getDescription() { } /** *
-     * A description of the agent's domain of action/solution space.
+     * A human-readable description of the agent, assisting users and other agents
+     * in understanding its purpose.
      * Example: "Agent that helps users with recipes and cooking."
      * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The description to set. * @return This builder for chaining. */ @@ -2342,11 +2555,12 @@ public Builder setDescription( } /** *
-     * A description of the agent's domain of action/solution space.
+     * A human-readable description of the agent, assisting users and other agents
+     * in understanding its purpose.
      * Example: "Agent that helps users with recipes and cooking."
      * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearDescription() { @@ -2357,11 +2571,12 @@ public Builder clearDescription() { } /** *
-     * A description of the agent's domain of action/solution space.
+     * A human-readable description of the agent, assisting users and other agents
+     * in understanding its purpose.
      * Example: "Agent that helps users with recipes and cooking."
      * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -2375,17 +2590,343 @@ public Builder setDescriptionBytes( return this; } + private java.util.List supportedInterfaces_ = + java.util.Collections.emptyList(); + private void ensureSupportedInterfacesIsMutable() { + if (!((bitField0_ & 0x00000008) != 0)) { + supportedInterfaces_ = new java.util.ArrayList(supportedInterfaces_); + bitField0_ |= 0x00000008; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> supportedInterfacesBuilder_; + + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public java.util.List getSupportedInterfacesList() { + if (supportedInterfacesBuilder_ == null) { + return java.util.Collections.unmodifiableList(supportedInterfaces_); + } else { + return supportedInterfacesBuilder_.getMessageList(); + } + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public int getSupportedInterfacesCount() { + if (supportedInterfacesBuilder_ == null) { + return supportedInterfaces_.size(); + } else { + return supportedInterfacesBuilder_.getCount(); + } + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public io.a2a.grpc.AgentInterface getSupportedInterfaces(int index) { + if (supportedInterfacesBuilder_ == null) { + return supportedInterfaces_.get(index); + } else { + return supportedInterfacesBuilder_.getMessage(index); + } + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder setSupportedInterfaces( + int index, io.a2a.grpc.AgentInterface value) { + if (supportedInterfacesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.set(index, value); + onChanged(); + } else { + supportedInterfacesBuilder_.setMessage(index, value); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder setSupportedInterfaces( + int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.set(index, builderForValue.build()); + onChanged(); + } else { + supportedInterfacesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder addSupportedInterfaces(io.a2a.grpc.AgentInterface value) { + if (supportedInterfacesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(value); + onChanged(); + } else { + supportedInterfacesBuilder_.addMessage(value); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder addSupportedInterfaces( + int index, io.a2a.grpc.AgentInterface value) { + if (supportedInterfacesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(index, value); + onChanged(); + } else { + supportedInterfacesBuilder_.addMessage(index, value); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder addSupportedInterfaces( + io.a2a.grpc.AgentInterface.Builder builderForValue) { + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(builderForValue.build()); + onChanged(); + } else { + supportedInterfacesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder addSupportedInterfaces( + int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(index, builderForValue.build()); + onChanged(); + } else { + supportedInterfacesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder addAllSupportedInterfaces( + java.lang.Iterable values) { + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, supportedInterfaces_); + onChanged(); + } else { + supportedInterfacesBuilder_.addAllMessages(values); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder clearSupportedInterfaces() { + if (supportedInterfacesBuilder_ == null) { + supportedInterfaces_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + } else { + supportedInterfacesBuilder_.clear(); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public Builder removeSupportedInterfaces(int index) { + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.remove(index); + onChanged(); + } else { + supportedInterfacesBuilder_.remove(index); + } + return this; + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public io.a2a.grpc.AgentInterface.Builder getSupportedInterfacesBuilder( + int index) { + return internalGetSupportedInterfacesFieldBuilder().getBuilder(index); + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( + int index) { + if (supportedInterfacesBuilder_ == null) { + return supportedInterfaces_.get(index); } else { + return supportedInterfacesBuilder_.getMessageOrBuilder(index); + } + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public java.util.List + getSupportedInterfacesOrBuilderList() { + if (supportedInterfacesBuilder_ != null) { + return supportedInterfacesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(supportedInterfaces_); + } + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder() { + return internalGetSupportedInterfacesFieldBuilder().addBuilder( + io.a2a.grpc.AgentInterface.getDefaultInstance()); + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( + int index) { + return internalGetSupportedInterfacesFieldBuilder().addBuilder( + index, io.a2a.grpc.AgentInterface.getDefaultInstance()); + } + /** + *
+     * Ordered list of supported interfaces. First entry is preferred.
+     * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + public java.util.List + getSupportedInterfacesBuilderList() { + return internalGetSupportedInterfacesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> + internalGetSupportedInterfacesFieldBuilder() { + if (supportedInterfacesBuilder_ == null) { + supportedInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder>( + supportedInterfaces_, + ((bitField0_ & 0x00000008) != 0), + getParentForChildren(), + isClean()); + supportedInterfaces_ = null; + } + return supportedInterfacesBuilder_; + } + private java.lang.Object url_ = ""; /** *
-     * A URL to the address the agent is hosted at. This represents the
-     * preferred endpoint as declared by the agent.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 + * @return Whether the url field is set. + */ + @java.lang.Deprecated public boolean hasUrl() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + *
+     * DEPRECATED: Use 'supported_interfaces' instead.
+     * 
+ * + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return The url. */ - public java.lang.String getUrl() { + @java.lang.Deprecated public java.lang.String getUrl() { java.lang.Object ref = url_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = @@ -2399,14 +2940,15 @@ public java.lang.String getUrl() { } /** *
-     * A URL to the address the agent is hosted at. This represents the
-     * preferred endpoint as declared by the agent.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return The bytes for url. */ - public com.google.protobuf.ByteString + @java.lang.Deprecated public com.google.protobuf.ByteString getUrlBytes() { java.lang.Object ref = url_; if (ref instanceof String) { @@ -2421,53 +2963,56 @@ public java.lang.String getUrl() { } /** *
-     * A URL to the address the agent is hosted at. This represents the
-     * preferred endpoint as declared by the agent.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @param value The url to set. * @return This builder for chaining. */ - public Builder setUrl( + @java.lang.Deprecated public Builder setUrl( java.lang.String value) { if (value == null) { throw new NullPointerException(); } url_ = value; - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); return this; } /** *
-     * A URL to the address the agent is hosted at. This represents the
-     * preferred endpoint as declared by the agent.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return This builder for chaining. */ - public Builder clearUrl() { + @java.lang.Deprecated public Builder clearUrl() { url_ = getDefaultInstance().getUrl(); - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); onChanged(); return this; } /** *
-     * A URL to the address the agent is hosted at. This represents the
-     * preferred endpoint as declared by the agent.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @param value The bytes for url to set. * @return This builder for chaining. */ - public Builder setUrlBytes( + @java.lang.Deprecated public Builder setUrlBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); url_ = value; - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); return this; } @@ -2475,13 +3020,28 @@ public Builder setUrlBytes( private java.lang.Object preferredTransport_ = ""; /** *
-     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * DEPRECATED: Use 'supported_interfaces' instead.
+     * 
+ * + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 + * @return Whether the preferredTransport field is set. + */ + @java.lang.Deprecated public boolean hasPreferredTransport() { + return ((bitField0_ & 0x00000020) != 0); + } + /** + *
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return The preferredTransport. */ - public java.lang.String getPreferredTransport() { + @java.lang.Deprecated public java.lang.String getPreferredTransport() { java.lang.Object ref = preferredTransport_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = @@ -2495,13 +3055,15 @@ public java.lang.String getPreferredTransport() { } /** *
-     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return The bytes for preferredTransport. */ - public com.google.protobuf.ByteString + @java.lang.Deprecated public com.google.protobuf.ByteString getPreferredTransportBytes() { java.lang.Object ref = preferredTransport_; if (ref instanceof String) { @@ -2516,50 +3078,56 @@ public java.lang.String getPreferredTransport() { } /** *
-     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @param value The preferredTransport to set. * @return This builder for chaining. */ - public Builder setPreferredTransport( + @java.lang.Deprecated public Builder setPreferredTransport( java.lang.String value) { if (value == null) { throw new NullPointerException(); } preferredTransport_ = value; - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; onChanged(); return this; } /** *
-     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return This builder for chaining. */ - public Builder clearPreferredTransport() { + @java.lang.Deprecated public Builder clearPreferredTransport() { preferredTransport_ = getDefaultInstance().getPreferredTransport(); - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000020); onChanged(); return this; } /** *
-     * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @param value The bytes for preferredTransport to set. * @return This builder for chaining. */ - public Builder setPreferredTransportBytes( + @java.lang.Deprecated public Builder setPreferredTransportBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); preferredTransport_ = value; - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; onChanged(); return this; } @@ -2567,9 +3135,9 @@ public Builder setPreferredTransportBytes( private java.util.List additionalInterfaces_ = java.util.Collections.emptyList(); private void ensureAdditionalInterfacesIsMutable() { - if (!((bitField0_ & 0x00000020) != 0)) { + if (!((bitField0_ & 0x00000040) != 0)) { additionalInterfaces_ = new java.util.ArrayList(additionalInterfaces_); - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000040; } } @@ -2578,13 +3146,12 @@ private void ensureAdditionalInterfacesIsMutable() { /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public java.util.List getAdditionalInterfacesList() { + @java.lang.Deprecated public java.util.List getAdditionalInterfacesList() { if (additionalInterfacesBuilder_ == null) { return java.util.Collections.unmodifiableList(additionalInterfaces_); } else { @@ -2593,13 +3160,12 @@ public java.util.List getAdditionalInterfacesList() } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public int getAdditionalInterfacesCount() { + @java.lang.Deprecated public int getAdditionalInterfacesCount() { if (additionalInterfacesBuilder_ == null) { return additionalInterfaces_.size(); } else { @@ -2608,13 +3174,12 @@ public int getAdditionalInterfacesCount() { } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { + @java.lang.Deprecated public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { if (additionalInterfacesBuilder_ == null) { return additionalInterfaces_.get(index); } else { @@ -2623,13 +3188,12 @@ public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder setAdditionalInterfaces( + @java.lang.Deprecated public Builder setAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface value) { if (additionalInterfacesBuilder_ == null) { if (value == null) { @@ -2645,13 +3209,12 @@ public Builder setAdditionalInterfaces( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder setAdditionalInterfaces( + @java.lang.Deprecated public Builder setAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { if (additionalInterfacesBuilder_ == null) { ensureAdditionalInterfacesIsMutable(); @@ -2664,13 +3227,12 @@ public Builder setAdditionalInterfaces( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { + @java.lang.Deprecated public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { if (additionalInterfacesBuilder_ == null) { if (value == null) { throw new NullPointerException(); @@ -2685,13 +3247,12 @@ public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder addAdditionalInterfaces( + @java.lang.Deprecated public Builder addAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface value) { if (additionalInterfacesBuilder_ == null) { if (value == null) { @@ -2707,13 +3268,12 @@ public Builder addAdditionalInterfaces( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder addAdditionalInterfaces( + @java.lang.Deprecated public Builder addAdditionalInterfaces( io.a2a.grpc.AgentInterface.Builder builderForValue) { if (additionalInterfacesBuilder_ == null) { ensureAdditionalInterfacesIsMutable(); @@ -2726,13 +3286,12 @@ public Builder addAdditionalInterfaces( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder addAdditionalInterfaces( + @java.lang.Deprecated public Builder addAdditionalInterfaces( int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { if (additionalInterfacesBuilder_ == null) { ensureAdditionalInterfacesIsMutable(); @@ -2745,13 +3304,12 @@ public Builder addAdditionalInterfaces( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder addAllAdditionalInterfaces( + @java.lang.Deprecated public Builder addAllAdditionalInterfaces( java.lang.Iterable values) { if (additionalInterfacesBuilder_ == null) { ensureAdditionalInterfacesIsMutable(); @@ -2765,16 +3323,15 @@ public Builder addAllAdditionalInterfaces( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder clearAdditionalInterfaces() { + @java.lang.Deprecated public Builder clearAdditionalInterfaces() { if (additionalInterfacesBuilder_ == null) { additionalInterfaces_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000020); + bitField0_ = (bitField0_ & ~0x00000040); onChanged(); } else { additionalInterfacesBuilder_.clear(); @@ -2783,13 +3340,12 @@ public Builder clearAdditionalInterfaces() { } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public Builder removeAdditionalInterfaces(int index) { + @java.lang.Deprecated public Builder removeAdditionalInterfaces(int index) { if (additionalInterfacesBuilder_ == null) { ensureAdditionalInterfacesIsMutable(); additionalInterfaces_.remove(index); @@ -2801,25 +3357,23 @@ public Builder removeAdditionalInterfaces(int index) { } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public io.a2a.grpc.AgentInterface.Builder getAdditionalInterfacesBuilder( + @java.lang.Deprecated public io.a2a.grpc.AgentInterface.Builder getAdditionalInterfacesBuilder( int index) { return internalGetAdditionalInterfacesFieldBuilder().getBuilder(index); } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + @java.lang.Deprecated public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( int index) { if (additionalInterfacesBuilder_ == null) { return additionalInterfaces_.get(index); } else { @@ -2828,13 +3382,12 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public java.util.List + @java.lang.Deprecated public java.util.List getAdditionalInterfacesOrBuilderList() { if (additionalInterfacesBuilder_ != null) { return additionalInterfacesBuilder_.getMessageOrBuilderList(); @@ -2844,38 +3397,35 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder() { + @java.lang.Deprecated public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder() { return internalGetAdditionalInterfacesFieldBuilder().addBuilder( io.a2a.grpc.AgentInterface.getDefaultInstance()); } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( + @java.lang.Deprecated public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( int index) { return internalGetAdditionalInterfacesFieldBuilder().addBuilder( index, io.a2a.grpc.AgentInterface.getDefaultInstance()); } /** *
-     * Announcement of additional supported transports. Client can use any of
-     * the supported transports.
+     * DEPRECATED: Use 'supported_interfaces' instead.
      * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - public java.util.List + @java.lang.Deprecated public java.util.List getAdditionalInterfacesBuilderList() { return internalGetAdditionalInterfacesFieldBuilder().getBuilderList(); } @@ -2886,7 +3436,7 @@ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( additionalInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder>( additionalInterfaces_, - ((bitField0_ & 0x00000020) != 0), + ((bitField0_ & 0x00000040) != 0), getParentForChildren(), isClean()); additionalInterfaces_ = null; @@ -2906,7 +3456,7 @@ public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( * @return Whether the provider field is set. */ public boolean hasProvider() { - return ((bitField0_ & 0x00000040) != 0); + return ((bitField0_ & 0x00000080) != 0); } /** *
@@ -2939,7 +3489,7 @@ public Builder setProvider(io.a2a.grpc.AgentProvider value) {
       } else {
         providerBuilder_.setMessage(value);
       }
-      bitField0_ |= 0x00000040;
+      bitField0_ |= 0x00000080;
       onChanged();
       return this;
     }
@@ -2957,7 +3507,7 @@ public Builder setProvider(
       } else {
         providerBuilder_.setMessage(builderForValue.build());
       }
-      bitField0_ |= 0x00000040;
+      bitField0_ |= 0x00000080;
       onChanged();
       return this;
     }
@@ -2970,7 +3520,7 @@ public Builder setProvider(
      */
     public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
       if (providerBuilder_ == null) {
-        if (((bitField0_ & 0x00000040) != 0) &&
+        if (((bitField0_ & 0x00000080) != 0) &&
           provider_ != null &&
           provider_ != io.a2a.grpc.AgentProvider.getDefaultInstance()) {
           getProviderBuilder().mergeFrom(value);
@@ -2981,7 +3531,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
         providerBuilder_.mergeFrom(value);
       }
       if (provider_ != null) {
-        bitField0_ |= 0x00000040;
+        bitField0_ |= 0x00000080;
         onChanged();
       }
       return this;
@@ -2994,7 +3544,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
      * .a2a.v1.AgentProvider provider = 4;
      */
     public Builder clearProvider() {
-      bitField0_ = (bitField0_ & ~0x00000040);
+      bitField0_ = (bitField0_ & ~0x00000080);
       provider_ = null;
       if (providerBuilder_ != null) {
         providerBuilder_.dispose();
@@ -3011,7 +3561,7 @@ public Builder clearProvider() {
      * .a2a.v1.AgentProvider provider = 4;
      */
     public io.a2a.grpc.AgentProvider.Builder getProviderBuilder() {
-      bitField0_ |= 0x00000040;
+      bitField0_ |= 0x00000080;
       onChanged();
       return internalGetProviderFieldBuilder().getBuilder();
     }
@@ -3058,7 +3608,7 @@ public io.a2a.grpc.AgentProviderOrBuilder getProviderOrBuilder() {
      * Example: "1.0.0"
      * 
* - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The version. */ public java.lang.String getVersion() { @@ -3079,7 +3629,7 @@ public java.lang.String getVersion() { * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for version. */ public com.google.protobuf.ByteString @@ -3101,7 +3651,7 @@ public java.lang.String getVersion() { * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @param value The version to set. * @return This builder for chaining. */ @@ -3109,7 +3659,7 @@ public Builder setVersion( java.lang.String value) { if (value == null) { throw new NullPointerException(); } version_ = value; - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000100; onChanged(); return this; } @@ -3119,12 +3669,12 @@ public Builder setVersion( * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearVersion() { version_ = getDefaultInstance().getVersion(); - bitField0_ = (bitField0_ & ~0x00000080); + bitField0_ = (bitField0_ & ~0x00000100); onChanged(); return this; } @@ -3134,7 +3684,7 @@ public Builder clearVersion() { * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for version to set. * @return This builder for chaining. */ @@ -3143,7 +3693,7 @@ public Builder setVersionBytes( if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); version_ = value; - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000100; onChanged(); return this; } @@ -3154,7 +3704,18 @@ public Builder setVersionBytes( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; + * @return Whether the documentationUrl field is set. + */ + public boolean hasDocumentationUrl() { + return ((bitField0_ & 0x00000200) != 0); + } + /** + *
+     * A url to provide additional documentation about the agent.
+     * 
+ * + * optional string documentation_url = 6; * @return The documentationUrl. */ public java.lang.String getDocumentationUrl() { @@ -3174,7 +3735,7 @@ public java.lang.String getDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; * @return The bytes for documentationUrl. */ public com.google.protobuf.ByteString @@ -3195,7 +3756,7 @@ public java.lang.String getDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; * @param value The documentationUrl to set. * @return This builder for chaining. */ @@ -3203,7 +3764,7 @@ public Builder setDocumentationUrl( java.lang.String value) { if (value == null) { throw new NullPointerException(); } documentationUrl_ = value; - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); return this; } @@ -3212,12 +3773,12 @@ public Builder setDocumentationUrl( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; * @return This builder for chaining. */ public Builder clearDocumentationUrl() { documentationUrl_ = getDefaultInstance().getDocumentationUrl(); - bitField0_ = (bitField0_ & ~0x00000100); + bitField0_ = (bitField0_ & ~0x00000200); onChanged(); return this; } @@ -3226,7 +3787,7 @@ public Builder clearDocumentationUrl() { * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; * @param value The bytes for documentationUrl to set. * @return This builder for chaining. */ @@ -3235,7 +3796,7 @@ public Builder setDocumentationUrlBytes( if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); documentationUrl_ = value; - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000200; onChanged(); return this; } @@ -3248,18 +3809,18 @@ public Builder setDocumentationUrlBytes( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the capabilities field is set. */ public boolean hasCapabilities() { - return ((bitField0_ & 0x00000200) != 0); + return ((bitField0_ & 0x00000400) != 0); } /** *
      * A2A Capability set supported by the agent.
      * 
* - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; * @return The capabilities. */ public io.a2a.grpc.AgentCapabilities getCapabilities() { @@ -3274,7 +3835,7 @@ public io.a2a.grpc.AgentCapabilities getCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) { if (capabilitiesBuilder_ == null) { @@ -3285,7 +3846,7 @@ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) { } else { capabilitiesBuilder_.setMessage(value); } - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; onChanged(); return this; } @@ -3294,7 +3855,7 @@ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setCapabilities( io.a2a.grpc.AgentCapabilities.Builder builderForValue) { @@ -3303,7 +3864,7 @@ public Builder setCapabilities( } else { capabilitiesBuilder_.setMessage(builderForValue.build()); } - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; onChanged(); return this; } @@ -3312,11 +3873,11 @@ public Builder setCapabilities( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) { if (capabilitiesBuilder_ == null) { - if (((bitField0_ & 0x00000200) != 0) && + if (((bitField0_ & 0x00000400) != 0) && capabilities_ != null && capabilities_ != io.a2a.grpc.AgentCapabilities.getDefaultInstance()) { getCapabilitiesBuilder().mergeFrom(value); @@ -3327,7 +3888,7 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) { capabilitiesBuilder_.mergeFrom(value); } if (capabilities_ != null) { - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; onChanged(); } return this; @@ -3337,10 +3898,10 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearCapabilities() { - bitField0_ = (bitField0_ & ~0x00000200); + bitField0_ = (bitField0_ & ~0x00000400); capabilities_ = null; if (capabilitiesBuilder_ != null) { capabilitiesBuilder_.dispose(); @@ -3354,10 +3915,10 @@ public Builder clearCapabilities() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() { - bitField0_ |= 0x00000200; + bitField0_ |= 0x00000400; onChanged(); return internalGetCapabilitiesFieldBuilder().getBuilder(); } @@ -3366,7 +3927,7 @@ public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { if (capabilitiesBuilder_ != null) { @@ -3381,7 +3942,7 @@ public io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder() { * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AgentCapabilities, io.a2a.grpc.AgentCapabilities.Builder, io.a2a.grpc.AgentCapabilitiesOrBuilder> @@ -3425,7 +3986,7 @@ public com.google.protobuf.MapEntry(securitySchemesConverter); } - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; onChanged(); return securitySchemes_; } @@ -3499,7 +4060,7 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( return securitySchemesConverter.build(map.get(key)); } public Builder clearSecuritySchemes() { - bitField0_ = (bitField0_ & ~0x00000400); + bitField0_ = (bitField0_ & ~0x00000800); internalGetMutableSecuritySchemes().clear(); return this; } @@ -3523,7 +4084,7 @@ public Builder removeSecuritySchemes( @java.lang.Deprecated public java.util.Map getMutableSecuritySchemes() { - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; return internalGetMutableSecuritySchemes().ensureMessageMap(); } /** @@ -3540,7 +4101,7 @@ public Builder putSecuritySchemes( if (value == null) { throw new NullPointerException("map value"); } internalGetMutableSecuritySchemes().ensureBuilderMap() .put(key, value); - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; return this; } /** @@ -3559,7 +4120,7 @@ public Builder putAllSecuritySchemes( } internalGetMutableSecuritySchemes().ensureBuilderMap() .putAll(values); - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000800; return this; } /** @@ -3587,9 +4148,9 @@ public io.a2a.grpc.SecurityScheme.Builder putSecuritySchemesBuilderIfAbsent( private java.util.List security_ = java.util.Collections.emptyList(); private void ensureSecurityIsMutable() { - if (!((bitField0_ & 0x00000800) != 0)) { + if (!((bitField0_ & 0x00001000) != 0)) { security_ = new java.util.ArrayList(security_); - bitField0_ |= 0x00000800; + bitField0_ |= 0x00001000; } } @@ -3600,18 +4161,6 @@ private void ensureSecurityIsMutable() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3627,18 +4176,6 @@ public java.util.List getSecurityList() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3654,18 +4191,6 @@ public int getSecurityCount() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3681,18 +4206,6 @@ public io.a2a.grpc.Security getSecurity(int index) { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3715,18 +4228,6 @@ public Builder setSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3746,18 +4247,6 @@ public Builder setSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3779,18 +4268,6 @@ public Builder addSecurity(io.a2a.grpc.Security value) { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3813,18 +4290,6 @@ public Builder addSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3844,18 +4309,6 @@ public Builder addSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3875,18 +4328,6 @@ public Builder addSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3907,18 +4348,6 @@ public Builder addAllSecurity( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3926,7 +4355,7 @@ public Builder addAllSecurity( public Builder clearSecurity() { if (securityBuilder_ == null) { security_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000800); + bitField0_ = (bitField0_ & ~0x00001000); onChanged(); } else { securityBuilder_.clear(); @@ -3937,18 +4366,6 @@ public Builder clearSecurity() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3967,18 +4384,6 @@ public Builder removeSecurity(int index) { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -3991,18 +4396,6 @@ public io.a2a.grpc.Security.Builder getSecurityBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -4018,18 +4411,6 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -4046,18 +4427,6 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -4070,18 +4439,6 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder() { *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -4095,18 +4452,6 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder( *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
      * Security requirements for contacting the agent.
-     * This list can be seen as an OR of ANDs. Each object in the list describes
-     * one possible set of security requirements that must be present on a
-     * request. This allows specifying, for example, "callers must either use
-     * OAuth OR an API Key AND mTLS."
-     * Example:
-     * security {
-     * schemes { key: "oauth" value { list: ["read"] } }
-     * }
-     * security {
-     * schemes { key: "api-key" }
-     * schemes { key: "mtls" }
-     * }
      * 
* * repeated .a2a.v1.Security security = 9; @@ -4122,7 +4467,7 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder( securityBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder>( security_, - ((bitField0_ & 0x00000800) != 0), + ((bitField0_ & 0x00001000) != 0), getParentForChildren(), isClean()); security_ = null; @@ -4136,16 +4481,16 @@ private void ensureDefaultInputModesIsMutable() { if (!defaultInputModes_.isModifiable()) { defaultInputModes_ = new com.google.protobuf.LazyStringArrayList(defaultInputModes_); } - bitField0_ |= 0x00001000; + bitField0_ |= 0x00002000; } /** *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the defaultInputModes. */ public com.google.protobuf.ProtocolStringList @@ -4157,10 +4502,10 @@ private void ensureDefaultInputModesIsMutable() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return The count of defaultInputModes. */ public int getDefaultInputModesCount() { @@ -4170,10 +4515,10 @@ public int getDefaultInputModesCount() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The defaultInputModes at the given index. */ @@ -4184,10 +4529,10 @@ public java.lang.String getDefaultInputModes(int index) { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the defaultInputModes at the given index. */ @@ -4199,10 +4544,10 @@ public java.lang.String getDefaultInputModes(int index) { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index to set the value at. * @param value The defaultInputModes to set. * @return This builder for chaining. @@ -4212,7 +4557,7 @@ public Builder setDefaultInputModes( if (value == null) { throw new NullPointerException(); } ensureDefaultInputModesIsMutable(); defaultInputModes_.set(index, value); - bitField0_ |= 0x00001000; + bitField0_ |= 0x00002000; onChanged(); return this; } @@ -4220,10 +4565,10 @@ public Builder setDefaultInputModes( *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param value The defaultInputModes to add. * @return This builder for chaining. */ @@ -4232,7 +4577,7 @@ public Builder addDefaultInputModes( if (value == null) { throw new NullPointerException(); } ensureDefaultInputModesIsMutable(); defaultInputModes_.add(value); - bitField0_ |= 0x00001000; + bitField0_ |= 0x00002000; onChanged(); return this; } @@ -4240,10 +4585,10 @@ public Builder addDefaultInputModes( *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param values The defaultInputModes to add. * @return This builder for chaining. */ @@ -4252,7 +4597,7 @@ public Builder addAllDefaultInputModes( ensureDefaultInputModesIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( values, defaultInputModes_); - bitField0_ |= 0x00001000; + bitField0_ |= 0x00002000; onChanged(); return this; } @@ -4260,16 +4605,16 @@ public Builder addAllDefaultInputModes( *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearDefaultInputModes() { defaultInputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); - bitField0_ = (bitField0_ & ~0x00001000);; + bitField0_ = (bitField0_ & ~0x00002000);; onChanged(); return this; } @@ -4277,10 +4622,10 @@ public Builder clearDefaultInputModes() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * The set of interaction modes that the agent supports across all skills.
-     * This can be overridden per skill. Defined as mime types.
+     * This can be overridden per skill. Defined as media types.
      * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes of the defaultInputModes to add. * @return This builder for chaining. */ @@ -4290,7 +4635,7 @@ public Builder addDefaultInputModesBytes( checkByteStringIsUtf8(value); ensureDefaultInputModesIsMutable(); defaultInputModes_.add(value); - bitField0_ |= 0x00001000; + bitField0_ |= 0x00002000; onChanged(); return this; } @@ -4301,14 +4646,14 @@ private void ensureDefaultOutputModesIsMutable() { if (!defaultOutputModes_.isModifiable()) { defaultOutputModes_ = new com.google.protobuf.LazyStringArrayList(defaultOutputModes_); } - bitField0_ |= 0x00002000; + bitField0_ |= 0x00004000; } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the defaultOutputModes. */ public com.google.protobuf.ProtocolStringList @@ -4318,10 +4663,10 @@ private void ensureDefaultOutputModesIsMutable() { } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return The count of defaultOutputModes. */ public int getDefaultOutputModesCount() { @@ -4329,10 +4674,10 @@ public int getDefaultOutputModesCount() { } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The defaultOutputModes at the given index. */ @@ -4341,10 +4686,10 @@ public java.lang.String getDefaultOutputModes(int index) { } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the defaultOutputModes at the given index. */ @@ -4354,10 +4699,10 @@ public java.lang.String getDefaultOutputModes(int index) { } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index to set the value at. * @param value The defaultOutputModes to set. * @return This builder for chaining. @@ -4367,16 +4712,16 @@ public Builder setDefaultOutputModes( if (value == null) { throw new NullPointerException(); } ensureDefaultOutputModesIsMutable(); defaultOutputModes_.set(index, value); - bitField0_ |= 0x00002000; + bitField0_ |= 0x00004000; onChanged(); return this; } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param value The defaultOutputModes to add. * @return This builder for chaining. */ @@ -4385,16 +4730,16 @@ public Builder addDefaultOutputModes( if (value == null) { throw new NullPointerException(); } ensureDefaultOutputModesIsMutable(); defaultOutputModes_.add(value); - bitField0_ |= 0x00002000; + bitField0_ |= 0x00004000; onChanged(); return this; } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param values The defaultOutputModes to add. * @return This builder for chaining. */ @@ -4403,31 +4748,31 @@ public Builder addAllDefaultOutputModes( ensureDefaultOutputModesIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( values, defaultOutputModes_); - bitField0_ |= 0x00002000; + bitField0_ |= 0x00004000; onChanged(); return this; } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearDefaultOutputModes() { defaultOutputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); - bitField0_ = (bitField0_ & ~0x00002000);; + bitField0_ = (bitField0_ & ~0x00004000);; onChanged(); return this; } /** *
-     * The mime types supported as outputs from this agent.
+     * The media types supported as outputs from this agent.
      * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes of the defaultOutputModes to add. * @return This builder for chaining. */ @@ -4437,7 +4782,7 @@ public Builder addDefaultOutputModesBytes( checkByteStringIsUtf8(value); ensureDefaultOutputModesIsMutable(); defaultOutputModes_.add(value); - bitField0_ |= 0x00002000; + bitField0_ |= 0x00004000; onChanged(); return this; } @@ -4445,9 +4790,9 @@ public Builder addDefaultOutputModesBytes( private java.util.List skills_ = java.util.Collections.emptyList(); private void ensureSkillsIsMutable() { - if (!((bitField0_ & 0x00004000) != 0)) { + if (!((bitField0_ & 0x00008000) != 0)) { skills_ = new java.util.ArrayList(skills_); - bitField0_ |= 0x00004000; + bitField0_ |= 0x00008000; } } @@ -4461,7 +4806,7 @@ private void ensureSkillsIsMutable() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getSkillsList() { if (skillsBuilder_ == null) { @@ -4477,7 +4822,7 @@ public java.util.List getSkillsList() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public int getSkillsCount() { if (skillsBuilder_ == null) { @@ -4493,7 +4838,7 @@ public int getSkillsCount() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentSkill getSkills(int index) { if (skillsBuilder_ == null) { @@ -4509,7 +4854,7 @@ public io.a2a.grpc.AgentSkill getSkills(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setSkills( int index, io.a2a.grpc.AgentSkill value) { @@ -4532,7 +4877,7 @@ public Builder setSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setSkills( int index, io.a2a.grpc.AgentSkill.Builder builderForValue) { @@ -4552,7 +4897,7 @@ public Builder setSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addSkills(io.a2a.grpc.AgentSkill value) { if (skillsBuilder_ == null) { @@ -4574,7 +4919,7 @@ public Builder addSkills(io.a2a.grpc.AgentSkill value) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addSkills( int index, io.a2a.grpc.AgentSkill value) { @@ -4597,7 +4942,7 @@ public Builder addSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addSkills( io.a2a.grpc.AgentSkill.Builder builderForValue) { @@ -4617,7 +4962,7 @@ public Builder addSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addSkills( int index, io.a2a.grpc.AgentSkill.Builder builderForValue) { @@ -4637,7 +4982,7 @@ public Builder addSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addAllSkills( java.lang.Iterable values) { @@ -4658,12 +5003,12 @@ public Builder addAllSkills( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearSkills() { if (skillsBuilder_ == null) { skills_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00004000); + bitField0_ = (bitField0_ & ~0x00008000); onChanged(); } else { skillsBuilder_.clear(); @@ -4677,7 +5022,7 @@ public Builder clearSkills() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeSkills(int index) { if (skillsBuilder_ == null) { @@ -4696,7 +5041,7 @@ public Builder removeSkills(int index) { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentSkill.Builder getSkillsBuilder( int index) { @@ -4709,7 +5054,7 @@ public io.a2a.grpc.AgentSkill.Builder getSkillsBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( int index) { @@ -4725,7 +5070,7 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getSkillsOrBuilderList() { @@ -4742,7 +5087,7 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder() { return internalGetSkillsFieldBuilder().addBuilder( @@ -4755,7 +5100,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder() { * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( int index) { @@ -4769,7 +5114,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getSkillsBuilderList() { @@ -4782,7 +5127,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( skillsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.AgentSkill, io.a2a.grpc.AgentSkill.Builder, io.a2a.grpc.AgentSkillOrBuilder>( skills_, - ((bitField0_ & 0x00004000) != 0), + ((bitField0_ & 0x00008000) != 0), getParentForChildren(), isClean()); skills_ = null; @@ -4793,12 +5138,22 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( private boolean supportsAuthenticatedExtendedCard_ ; /** *
-     * Whether the agent supports providing an extended agent card when
-     * the user is authenticated, i.e. is the card from .well-known
-     * different than the card from GetAgentCard.
+     * Whether the agent supports providing an extended agent card when authenticated.
+     * 
+ * + * optional bool supports_authenticated_extended_card = 13; + * @return Whether the supportsAuthenticatedExtendedCard field is set. + */ + @java.lang.Override + public boolean hasSupportsAuthenticatedExtendedCard() { + return ((bitField0_ & 0x00010000) != 0); + } + /** + *
+     * Whether the agent supports providing an extended agent card when authenticated.
      * 
* - * bool supports_authenticated_extended_card = 13; + * optional bool supports_authenticated_extended_card = 13; * @return The supportsAuthenticatedExtendedCard. */ @java.lang.Override @@ -4807,34 +5162,30 @@ public boolean getSupportsAuthenticatedExtendedCard() { } /** *
-     * Whether the agent supports providing an extended agent card when
-     * the user is authenticated, i.e. is the card from .well-known
-     * different than the card from GetAgentCard.
+     * Whether the agent supports providing an extended agent card when authenticated.
      * 
* - * bool supports_authenticated_extended_card = 13; + * optional bool supports_authenticated_extended_card = 13; * @param value The supportsAuthenticatedExtendedCard to set. * @return This builder for chaining. */ public Builder setSupportsAuthenticatedExtendedCard(boolean value) { supportsAuthenticatedExtendedCard_ = value; - bitField0_ |= 0x00008000; + bitField0_ |= 0x00010000; onChanged(); return this; } /** *
-     * Whether the agent supports providing an extended agent card when
-     * the user is authenticated, i.e. is the card from .well-known
-     * different than the card from GetAgentCard.
+     * Whether the agent supports providing an extended agent card when authenticated.
      * 
* - * bool supports_authenticated_extended_card = 13; + * optional bool supports_authenticated_extended_card = 13; * @return This builder for chaining. */ public Builder clearSupportsAuthenticatedExtendedCard() { - bitField0_ = (bitField0_ & ~0x00008000); + bitField0_ = (bitField0_ & ~0x00010000); supportsAuthenticatedExtendedCard_ = false; onChanged(); return this; @@ -4843,9 +5194,9 @@ public Builder clearSupportsAuthenticatedExtendedCard() { private java.util.List signatures_ = java.util.Collections.emptyList(); private void ensureSignaturesIsMutable() { - if (!((bitField0_ & 0x00010000) != 0)) { + if (!((bitField0_ & 0x00020000) != 0)) { signatures_ = new java.util.ArrayList(signatures_); - bitField0_ |= 0x00010000; + bitField0_ |= 0x00020000; } } @@ -5039,7 +5390,7 @@ public Builder addAllSignatures( public Builder clearSignatures() { if (signaturesBuilder_ == null) { signatures_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00010000); + bitField0_ = (bitField0_ & ~0x00020000); onChanged(); } else { signaturesBuilder_.clear(); @@ -5144,7 +5495,7 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder( signaturesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.AgentCardSignature, io.a2a.grpc.AgentCardSignature.Builder, io.a2a.grpc.AgentCardSignatureOrBuilder>( signatures_, - ((bitField0_ & 0x00010000) != 0), + ((bitField0_ & 0x00020000) != 0), getParentForChildren(), isClean()); signatures_ = null; @@ -5158,7 +5509,18 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder( * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; + * @return Whether the iconUrl field is set. + */ + public boolean hasIconUrl() { + return ((bitField0_ & 0x00040000) != 0); + } + /** + *
+     * An optional URL to an icon for the agent.
+     * 
+ * + * optional string icon_url = 18; * @return The iconUrl. */ public java.lang.String getIconUrl() { @@ -5178,7 +5540,7 @@ public java.lang.String getIconUrl() { * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; * @return The bytes for iconUrl. */ public com.google.protobuf.ByteString @@ -5199,7 +5561,7 @@ public java.lang.String getIconUrl() { * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; * @param value The iconUrl to set. * @return This builder for chaining. */ @@ -5207,7 +5569,7 @@ public Builder setIconUrl( java.lang.String value) { if (value == null) { throw new NullPointerException(); } iconUrl_ = value; - bitField0_ |= 0x00020000; + bitField0_ |= 0x00040000; onChanged(); return this; } @@ -5216,12 +5578,12 @@ public Builder setIconUrl( * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; * @return This builder for chaining. */ public Builder clearIconUrl() { iconUrl_ = getDefaultInstance().getIconUrl(); - bitField0_ = (bitField0_ & ~0x00020000); + bitField0_ = (bitField0_ & ~0x00040000); onChanged(); return this; } @@ -5230,7 +5592,7 @@ public Builder clearIconUrl() { * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; * @param value The bytes for iconUrl to set. * @return This builder for chaining. */ @@ -5239,7 +5601,7 @@ public Builder setIconUrlBytes( if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); iconUrl_ = value; - bitField0_ |= 0x00020000; + bitField0_ |= 0x00040000; onChanged(); return this; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java index da51cf2d8..d07f03e2a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java @@ -13,18 +13,30 @@ public interface AgentCardOrBuilder extends /** *
    * The version of the A2A protocol this agent supports.
+   * Default: "1.0"
    * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return Whether the protocolVersion field is set. + */ + boolean hasProtocolVersion(); + /** + *
+   * The version of the A2A protocol this agent supports.
+   * Default: "1.0"
+   * 
+ * + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return The protocolVersion. */ java.lang.String getProtocolVersion(); /** *
    * The version of the A2A protocol this agent supports.
+   * Default: "1.0"
    * 
* - * string protocol_version = 16; + * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for protocolVersion. */ com.google.protobuf.ByteString @@ -36,7 +48,7 @@ public interface AgentCardOrBuilder extends * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ java.lang.String getName(); @@ -46,7 +58,7 @@ public interface AgentCardOrBuilder extends * Example: "Recipe Agent" * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ com.google.protobuf.ByteString @@ -54,21 +66,23 @@ public interface AgentCardOrBuilder extends /** *
-   * A description of the agent's domain of action/solution space.
+   * A human-readable description of the agent, assisting users and other agents
+   * in understanding its purpose.
    * Example: "Agent that helps users with recipes and cooking."
    * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The description. */ java.lang.String getDescription(); /** *
-   * A description of the agent's domain of action/solution space.
+   * A human-readable description of the agent, assisting users and other agents
+   * in understanding its purpose.
    * Example: "Agent that helps users with recipes and cooking."
    * 
* - * string description = 2; + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -76,93 +90,160 @@ public interface AgentCardOrBuilder extends /** *
-   * A URL to the address the agent is hosted at. This represents the
-   * preferred endpoint as declared by the agent.
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + java.util.List + getSupportedInterfacesList(); + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + io.a2a.grpc.AgentInterface getSupportedInterfaces(int index); + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + int getSupportedInterfacesCount(); + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + java.util.List + getSupportedInterfacesOrBuilderList(); + /** + *
+   * Ordered list of supported interfaces. First entry is preferred.
+   * 
+ * + * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + */ + io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( + int index); + + /** + *
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 + * @return Whether the url field is set. + */ + @java.lang.Deprecated boolean hasUrl(); + /** + *
+   * DEPRECATED: Use 'supported_interfaces' instead.
+   * 
+ * + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return The url. */ - java.lang.String getUrl(); + @java.lang.Deprecated java.lang.String getUrl(); /** *
-   * A URL to the address the agent is hosted at. This represents the
-   * preferred endpoint as declared by the agent.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string url = 3; + * optional string url = 3 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.url is deprecated. + * See a2a.proto;l=397 * @return The bytes for url. */ - com.google.protobuf.ByteString + @java.lang.Deprecated com.google.protobuf.ByteString getUrlBytes(); /** *
-   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 + * @return Whether the preferredTransport field is set. + */ + @java.lang.Deprecated boolean hasPreferredTransport(); + /** + *
+   * DEPRECATED: Use 'supported_interfaces' instead.
+   * 
+ * + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return The preferredTransport. */ - java.lang.String getPreferredTransport(); + @java.lang.Deprecated java.lang.String getPreferredTransport(); /** *
-   * The transport of the preferred endpoint. If empty, defaults to JSONRPC.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * string preferred_transport = 14; + * optional string preferred_transport = 14 [deprecated = true]; + * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. + * See a2a.proto;l=399 * @return The bytes for preferredTransport. */ - com.google.protobuf.ByteString + @java.lang.Deprecated com.google.protobuf.ByteString getPreferredTransportBytes(); /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - java.util.List + @java.lang.Deprecated java.util.List getAdditionalInterfacesList(); /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index); + @java.lang.Deprecated io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index); /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - int getAdditionalInterfacesCount(); + @java.lang.Deprecated int getAdditionalInterfacesCount(); /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - java.util.List + @java.lang.Deprecated java.util.List getAdditionalInterfacesOrBuilderList(); /** *
-   * Announcement of additional supported transports. Client can use any of
-   * the supported transports.
+   * DEPRECATED: Use 'supported_interfaces' instead.
    * 
* - * repeated .a2a.v1.AgentInterface additional_interfaces = 15; + * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; */ - io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + @java.lang.Deprecated io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( int index); /** @@ -198,7 +279,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The version. */ java.lang.String getVersion(); @@ -208,7 +289,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * Example: "1.0.0" * * - * string version = 5; + * string version = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for version. */ com.google.protobuf.ByteString @@ -219,7 +300,16 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; + * @return Whether the documentationUrl field is set. + */ + boolean hasDocumentationUrl(); + /** + *
+   * A url to provide additional documentation about the agent.
+   * 
+ * + * optional string documentation_url = 6; * @return The documentationUrl. */ java.lang.String getDocumentationUrl(); @@ -228,7 +318,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A url to provide additional documentation about the agent. * * - * string documentation_url = 6; + * optional string documentation_url = 6; * @return The bytes for documentationUrl. */ com.google.protobuf.ByteString @@ -239,7 +329,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the capabilities field is set. */ boolean hasCapabilities(); @@ -248,7 +338,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; * @return The capabilities. */ io.a2a.grpc.AgentCapabilities getCapabilities(); @@ -257,7 +347,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( * A2A Capability set supported by the agent. * * - * .a2a.v1.AgentCapabilities capabilities = 7; + * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.AgentCapabilitiesOrBuilder getCapabilitiesOrBuilder(); @@ -319,18 +409,6 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -341,18 +419,6 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -362,18 +428,6 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -383,18 +437,6 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -405,18 +447,6 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
    * Security requirements for contacting the agent.
-   * This list can be seen as an OR of ANDs. Each object in the list describes
-   * one possible set of security requirements that must be present on a
-   * request. This allows specifying, for example, "callers must either use
-   * OAuth OR an API Key AND mTLS."
-   * Example:
-   * security {
-   * schemes { key: "oauth" value { list: ["read"] } }
-   * }
-   * security {
-   * schemes { key: "api-key" }
-   * schemes { key: "mtls" }
-   * }
    * 
* * repeated .a2a.v1.Security security = 9; @@ -428,10 +458,10 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the defaultInputModes. */ java.util.List @@ -440,10 +470,10 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @return The count of defaultInputModes. */ int getDefaultInputModesCount(); @@ -451,10 +481,10 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The defaultInputModes at the given index. */ @@ -463,10 +493,10 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * The set of interaction modes that the agent supports across all skills.
-   * This can be overridden per skill. Defined as mime types.
+   * This can be overridden per skill. Defined as media types.
    * 
* - * repeated string default_input_modes = 10; + * repeated string default_input_modes = 10 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the defaultInputModes at the given index. */ @@ -475,39 +505,39 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the defaultOutputModes. */ java.util.List getDefaultOutputModesList(); /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @return The count of defaultOutputModes. */ int getDefaultOutputModesCount(); /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The defaultOutputModes at the given index. */ java.lang.String getDefaultOutputModes(int index); /** *
-   * The mime types supported as outputs from this agent.
+   * The media types supported as outputs from this agent.
    * 
* - * repeated string default_output_modes = 11; + * repeated string default_output_modes = 11 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the defaultOutputModes at the given index. */ @@ -521,7 +551,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getSkillsList(); @@ -532,7 +562,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.AgentSkill getSkills(int index); /** @@ -542,7 +572,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ int getSkillsCount(); /** @@ -552,7 +582,7 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getSkillsOrBuilderList(); @@ -563,19 +593,26 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( * agent is highly likely to succeed at. * * - * repeated .a2a.v1.AgentSkill skills = 12; + * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( int index); /** *
-   * Whether the agent supports providing an extended agent card when
-   * the user is authenticated, i.e. is the card from .well-known
-   * different than the card from GetAgentCard.
+   * Whether the agent supports providing an extended agent card when authenticated.
+   * 
+ * + * optional bool supports_authenticated_extended_card = 13; + * @return Whether the supportsAuthenticatedExtendedCard field is set. + */ + boolean hasSupportsAuthenticatedExtendedCard(); + /** + *
+   * Whether the agent supports providing an extended agent card when authenticated.
    * 
* - * bool supports_authenticated_extended_card = 13; + * optional bool supports_authenticated_extended_card = 13; * @return The supportsAuthenticatedExtendedCard. */ boolean getSupportsAuthenticatedExtendedCard(); @@ -629,7 +666,16 @@ io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; + * @return Whether the iconUrl field is set. + */ + boolean hasIconUrl(); + /** + *
+   * An optional URL to an icon for the agent.
+   * 
+ * + * optional string icon_url = 18; * @return The iconUrl. */ java.lang.String getIconUrl(); @@ -638,7 +684,7 @@ io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( * An optional URL to an icon for the agent. * * - * string icon_url = 18; + * optional string icon_url = 18; * @return The bytes for iconUrl. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java index 99a651f90..a6920fdb1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java @@ -8,7 +8,7 @@ /** *
  * --8<-- [start:AgentExtension]
- * A declaration of an extension supported by an Agent.
+ * A declaration of a protocol extension supported by an Agent.
  * 
* * Protobuf type {@code a2a.v1.AgentExtension} @@ -56,8 +56,7 @@ private AgentExtension() { private volatile java.lang.Object uri_ = ""; /** *
-   * The URI of the extension.
-   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * The unique URI identifying the extension.
    * 
* * string uri = 1; @@ -78,8 +77,7 @@ public java.lang.String getUri() { } /** *
-   * The URI of the extension.
-   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * The unique URI identifying the extension.
    * 
* * string uri = 1; @@ -105,8 +103,7 @@ public java.lang.String getUri() { private volatile java.lang.Object description_ = ""; /** *
-   * A description of how this agent uses this extension.
-   * Example: "Google OAuth 2.0 authentication"
+   * A human-readable description of how this agent uses the extension.
    * 
* * string description = 2; @@ -127,8 +124,7 @@ public java.lang.String getDescription() { } /** *
-   * A description of how this agent uses this extension.
-   * Example: "Google OAuth 2.0 authentication"
+   * A human-readable description of how this agent uses the extension.
    * 
* * string description = 2; @@ -153,8 +149,7 @@ public java.lang.String getDescription() { private boolean required_ = false; /** *
-   * Whether the client must follow specific requirements of the extension.
-   * Example: false
+   * If true, the client must understand and comply with the extension's requirements.
    * 
* * bool required = 3; @@ -169,7 +164,7 @@ public boolean getRequired() { private com.google.protobuf.Struct params_; /** *
-   * Optional configuration for the extension.
+   * Optional, extension-specific configuration parameters.
    * 
* * .google.protobuf.Struct params = 4; @@ -181,7 +176,7 @@ public boolean hasParams() { } /** *
-   * Optional configuration for the extension.
+   * Optional, extension-specific configuration parameters.
    * 
* * .google.protobuf.Struct params = 4; @@ -193,7 +188,7 @@ public com.google.protobuf.Struct getParams() { } /** *
-   * Optional configuration for the extension.
+   * Optional, extension-specific configuration parameters.
    * 
* * .google.protobuf.Struct params = 4; @@ -400,7 +395,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AgentExtension]
-   * A declaration of an extension supported by an Agent.
+   * A declaration of a protocol extension supported by an Agent.
    * 
* * Protobuf type {@code a2a.v1.AgentExtension} @@ -598,8 +593,7 @@ public Builder mergeFrom( private java.lang.Object uri_ = ""; /** *
-     * The URI of the extension.
-     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * The unique URI identifying the extension.
      * 
* * string uri = 1; @@ -619,8 +613,7 @@ public java.lang.String getUri() { } /** *
-     * The URI of the extension.
-     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * The unique URI identifying the extension.
      * 
* * string uri = 1; @@ -641,8 +634,7 @@ public java.lang.String getUri() { } /** *
-     * The URI of the extension.
-     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * The unique URI identifying the extension.
      * 
* * string uri = 1; @@ -659,8 +651,7 @@ public Builder setUri( } /** *
-     * The URI of the extension.
-     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * The unique URI identifying the extension.
      * 
* * string uri = 1; @@ -674,8 +665,7 @@ public Builder clearUri() { } /** *
-     * The URI of the extension.
-     * Example: "https://developers.google.com/identity/protocols/oauth2"
+     * The unique URI identifying the extension.
      * 
* * string uri = 1; @@ -695,8 +685,7 @@ public Builder setUriBytes( private java.lang.Object description_ = ""; /** *
-     * A description of how this agent uses this extension.
-     * Example: "Google OAuth 2.0 authentication"
+     * A human-readable description of how this agent uses the extension.
      * 
* * string description = 2; @@ -716,8 +705,7 @@ public java.lang.String getDescription() { } /** *
-     * A description of how this agent uses this extension.
-     * Example: "Google OAuth 2.0 authentication"
+     * A human-readable description of how this agent uses the extension.
      * 
* * string description = 2; @@ -738,8 +726,7 @@ public java.lang.String getDescription() { } /** *
-     * A description of how this agent uses this extension.
-     * Example: "Google OAuth 2.0 authentication"
+     * A human-readable description of how this agent uses the extension.
      * 
* * string description = 2; @@ -756,8 +743,7 @@ public Builder setDescription( } /** *
-     * A description of how this agent uses this extension.
-     * Example: "Google OAuth 2.0 authentication"
+     * A human-readable description of how this agent uses the extension.
      * 
* * string description = 2; @@ -771,8 +757,7 @@ public Builder clearDescription() { } /** *
-     * A description of how this agent uses this extension.
-     * Example: "Google OAuth 2.0 authentication"
+     * A human-readable description of how this agent uses the extension.
      * 
* * string description = 2; @@ -792,8 +777,7 @@ public Builder setDescriptionBytes( private boolean required_ ; /** *
-     * Whether the client must follow specific requirements of the extension.
-     * Example: false
+     * If true, the client must understand and comply with the extension's requirements.
      * 
* * bool required = 3; @@ -805,8 +789,7 @@ public boolean getRequired() { } /** *
-     * Whether the client must follow specific requirements of the extension.
-     * Example: false
+     * If true, the client must understand and comply with the extension's requirements.
      * 
* * bool required = 3; @@ -822,8 +805,7 @@ public Builder setRequired(boolean value) { } /** *
-     * Whether the client must follow specific requirements of the extension.
-     * Example: false
+     * If true, the client must understand and comply with the extension's requirements.
      * 
* * bool required = 3; @@ -841,7 +823,7 @@ public Builder clearRequired() { com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> paramsBuilder_; /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -852,7 +834,7 @@ public boolean hasParams() { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -867,7 +849,7 @@ public com.google.protobuf.Struct getParams() { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -887,7 +869,7 @@ public Builder setParams(com.google.protobuf.Struct value) { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -905,7 +887,7 @@ public Builder setParams( } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -930,7 +912,7 @@ public Builder mergeParams(com.google.protobuf.Struct value) { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -947,7 +929,7 @@ public Builder clearParams() { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -959,7 +941,7 @@ public com.google.protobuf.Struct.Builder getParamsBuilder() { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; @@ -974,7 +956,7 @@ public com.google.protobuf.StructOrBuilder getParamsOrBuilder() { } /** *
-     * Optional configuration for the extension.
+     * Optional, extension-specific configuration parameters.
      * 
* * .google.protobuf.Struct params = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java index 789b314f5..ce8d86b95 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java @@ -12,8 +12,7 @@ public interface AgentExtensionOrBuilder extends /** *
-   * The URI of the extension.
-   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * The unique URI identifying the extension.
    * 
* * string uri = 1; @@ -22,8 +21,7 @@ public interface AgentExtensionOrBuilder extends java.lang.String getUri(); /** *
-   * The URI of the extension.
-   * Example: "https://developers.google.com/identity/protocols/oauth2"
+   * The unique URI identifying the extension.
    * 
* * string uri = 1; @@ -34,8 +32,7 @@ public interface AgentExtensionOrBuilder extends /** *
-   * A description of how this agent uses this extension.
-   * Example: "Google OAuth 2.0 authentication"
+   * A human-readable description of how this agent uses the extension.
    * 
* * string description = 2; @@ -44,8 +41,7 @@ public interface AgentExtensionOrBuilder extends java.lang.String getDescription(); /** *
-   * A description of how this agent uses this extension.
-   * Example: "Google OAuth 2.0 authentication"
+   * A human-readable description of how this agent uses the extension.
    * 
* * string description = 2; @@ -56,8 +52,7 @@ public interface AgentExtensionOrBuilder extends /** *
-   * Whether the client must follow specific requirements of the extension.
-   * Example: false
+   * If true, the client must understand and comply with the extension's requirements.
    * 
* * bool required = 3; @@ -67,7 +62,7 @@ public interface AgentExtensionOrBuilder extends /** *
-   * Optional configuration for the extension.
+   * Optional, extension-specific configuration parameters.
    * 
* * .google.protobuf.Struct params = 4; @@ -76,7 +71,7 @@ public interface AgentExtensionOrBuilder extends boolean hasParams(); /** *
-   * Optional configuration for the extension.
+   * Optional, extension-specific configuration parameters.
    * 
* * .google.protobuf.Struct params = 4; @@ -85,7 +80,7 @@ public interface AgentExtensionOrBuilder extends com.google.protobuf.Struct getParams(); /** *
-   * Optional configuration for the extension.
+   * Optional, extension-specific configuration parameters.
    * 
* * .google.protobuf.Struct params = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java index 8e74499bf..1af9120c1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java @@ -8,7 +8,8 @@ /** *
  * --8<-- [start:AgentInterface]
- * Defines additional transport information for the agent.
+ * Declares a combination of a target URL and a transport protocol for interacting with the agent.
+ * This allows agents to expose the same functionality over multiple protocol binding mechanisms.
  * 
* * Protobuf type {@code a2a.v1.AgentInterface} @@ -34,7 +35,7 @@ private AgentInterface(com.google.protobuf.GeneratedMessage.Builder builder) } private AgentInterface() { url_ = ""; - transport_ = ""; + protocolBinding_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -55,10 +56,11 @@ private AgentInterface() { private volatile java.lang.Object url_ = ""; /** *
-   * The url this interface is found at.
+   * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+   * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ @java.lang.Override @@ -76,10 +78,11 @@ public java.lang.String getUrl() { } /** *
-   * The url this interface is found at.
+   * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+   * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ @java.lang.Override @@ -97,51 +100,53 @@ public java.lang.String getUrl() { } } - public static final int TRANSPORT_FIELD_NUMBER = 2; + public static final int PROTOCOL_BINDING_FIELD_NUMBER = 2; @SuppressWarnings("serial") - private volatile java.lang.Object transport_ = ""; + private volatile java.lang.Object protocolBinding_ = ""; /** *
-   * The transport supported this url. This is an open form string, to be
-   * easily extended for many transport protocols. The core ones officially
+   * The protocol binding supported at this URL. This is an open form string, to be
+   * easily extended for other protocol bindings. The core ones officially
    * supported are JSONRPC, GRPC and HTTP+JSON.
+   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    * 
* - * string transport = 2; - * @return The transport. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The protocolBinding. */ @java.lang.Override - public java.lang.String getTransport() { - java.lang.Object ref = transport_; + public java.lang.String getProtocolBinding() { + java.lang.Object ref = protocolBinding_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - transport_ = s; + protocolBinding_ = s; return s; } } /** *
-   * The transport supported this url. This is an open form string, to be
-   * easily extended for many transport protocols. The core ones officially
+   * The protocol binding supported at this URL. This is an open form string, to be
+   * easily extended for other protocol bindings. The core ones officially
    * supported are JSONRPC, GRPC and HTTP+JSON.
+   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    * 
* - * string transport = 2; - * @return The bytes for transport. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protocolBinding. */ @java.lang.Override public com.google.protobuf.ByteString - getTransportBytes() { - java.lang.Object ref = transport_; + getProtocolBindingBytes() { + java.lang.Object ref = protocolBinding_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - transport_ = b; + protocolBinding_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -165,8 +170,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, url_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(transport_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 2, transport_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolBinding_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, protocolBinding_); } getUnknownFields().writeTo(output); } @@ -180,8 +185,8 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(url_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, url_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(transport_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(2, transport_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolBinding_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, protocolBinding_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -200,8 +205,8 @@ public boolean equals(final java.lang.Object obj) { if (!getUrl() .equals(other.getUrl())) return false; - if (!getTransport() - .equals(other.getTransport())) return false; + if (!getProtocolBinding() + .equals(other.getProtocolBinding())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -215,8 +220,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + URL_FIELD_NUMBER; hash = (53 * hash) + getUrl().hashCode(); - hash = (37 * hash) + TRANSPORT_FIELD_NUMBER; - hash = (53 * hash) + getTransport().hashCode(); + hash = (37 * hash) + PROTOCOL_BINDING_FIELD_NUMBER; + hash = (53 * hash) + getProtocolBinding().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -317,7 +322,8 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AgentInterface]
-   * Defines additional transport information for the agent.
+   * Declares a combination of a target URL and a transport protocol for interacting with the agent.
+   * This allows agents to expose the same functionality over multiple protocol binding mechanisms.
    * 
* * Protobuf type {@code a2a.v1.AgentInterface} @@ -354,7 +360,7 @@ public Builder clear() { super.clear(); bitField0_ = 0; url_ = ""; - transport_ = ""; + protocolBinding_ = ""; return this; } @@ -392,7 +398,7 @@ private void buildPartial0(io.a2a.grpc.AgentInterface result) { result.url_ = url_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.transport_ = transport_; + result.protocolBinding_ = protocolBinding_; } } @@ -413,8 +419,8 @@ public Builder mergeFrom(io.a2a.grpc.AgentInterface other) { bitField0_ |= 0x00000001; onChanged(); } - if (!other.getTransport().isEmpty()) { - transport_ = other.transport_; + if (!other.getProtocolBinding().isEmpty()) { + protocolBinding_ = other.protocolBinding_; bitField0_ |= 0x00000002; onChanged(); } @@ -450,7 +456,7 @@ public Builder mergeFrom( break; } // case 10 case 18: { - transport_ = input.readStringRequireUtf8(); + protocolBinding_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 18 @@ -474,10 +480,11 @@ public Builder mergeFrom( private java.lang.Object url_ = ""; /** *
-     * The url this interface is found at.
+     * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+     * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ public java.lang.String getUrl() { @@ -494,10 +501,11 @@ public java.lang.String getUrl() { } /** *
-     * The url this interface is found at.
+     * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+     * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -515,10 +523,11 @@ public java.lang.String getUrl() { } /** *
-     * The url this interface is found at.
+     * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+     * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The url to set. * @return This builder for chaining. */ @@ -532,10 +541,11 @@ public Builder setUrl( } /** *
-     * The url this interface is found at.
+     * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+     * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearUrl() { @@ -546,10 +556,11 @@ public Builder clearUrl() { } /** *
-     * The url this interface is found at.
+     * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+     * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -563,24 +574,25 @@ public Builder setUrlBytes( return this; } - private java.lang.Object transport_ = ""; + private java.lang.Object protocolBinding_ = ""; /** *
-     * The transport supported this url. This is an open form string, to be
-     * easily extended for many transport protocols. The core ones officially
+     * The protocol binding supported at this URL. This is an open form string, to be
+     * easily extended for other protocol bindings. The core ones officially
      * supported are JSONRPC, GRPC and HTTP+JSON.
+     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
      * 
* - * string transport = 2; - * @return The transport. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The protocolBinding. */ - public java.lang.String getTransport() { - java.lang.Object ref = transport_; + public java.lang.String getProtocolBinding() { + java.lang.Object ref = protocolBinding_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - transport_ = s; + protocolBinding_ = s; return s; } else { return (java.lang.String) ref; @@ -588,22 +600,23 @@ public java.lang.String getTransport() { } /** *
-     * The transport supported this url. This is an open form string, to be
-     * easily extended for many transport protocols. The core ones officially
+     * The protocol binding supported at this URL. This is an open form string, to be
+     * easily extended for other protocol bindings. The core ones officially
      * supported are JSONRPC, GRPC and HTTP+JSON.
+     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
      * 
* - * string transport = 2; - * @return The bytes for transport. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protocolBinding. */ public com.google.protobuf.ByteString - getTransportBytes() { - java.lang.Object ref = transport_; + getProtocolBindingBytes() { + java.lang.Object ref = protocolBinding_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - transport_ = b; + protocolBinding_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -611,55 +624,58 @@ public java.lang.String getTransport() { } /** *
-     * The transport supported this url. This is an open form string, to be
-     * easily extended for many transport protocols. The core ones officially
+     * The protocol binding supported at this URL. This is an open form string, to be
+     * easily extended for other protocol bindings. The core ones officially
      * supported are JSONRPC, GRPC and HTTP+JSON.
+     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
      * 
* - * string transport = 2; - * @param value The transport to set. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @param value The protocolBinding to set. * @return This builder for chaining. */ - public Builder setTransport( + public Builder setProtocolBinding( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - transport_ = value; + protocolBinding_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
-     * The transport supported this url. This is an open form string, to be
-     * easily extended for many transport protocols. The core ones officially
+     * The protocol binding supported at this URL. This is an open form string, to be
+     * easily extended for other protocol bindings. The core ones officially
      * supported are JSONRPC, GRPC and HTTP+JSON.
+     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
      * 
* - * string transport = 2; + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearTransport() { - transport_ = getDefaultInstance().getTransport(); + public Builder clearProtocolBinding() { + protocolBinding_ = getDefaultInstance().getProtocolBinding(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
-     * The transport supported this url. This is an open form string, to be
-     * easily extended for many transport protocols. The core ones officially
+     * The protocol binding supported at this URL. This is an open form string, to be
+     * easily extended for other protocol bindings. The core ones officially
      * supported are JSONRPC, GRPC and HTTP+JSON.
+     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
      * 
* - * string transport = 2; - * @param value The bytes for transport to set. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for protocolBinding to set. * @return This builder for chaining. */ - public Builder setTransportBytes( + public Builder setProtocolBindingBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - transport_ = value; + protocolBinding_ = value; bitField0_ |= 0x00000002; onChanged(); return this; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java index c0263e901..f07f30916 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java @@ -12,19 +12,21 @@ public interface AgentInterfaceOrBuilder extends /** *
-   * The url this interface is found at.
+   * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+   * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ java.lang.String getUrl(); /** *
-   * The url this interface is found at.
+   * The URL where this interface is available. Must be a valid absolute HTTPS URL in production.
+   * Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ com.google.protobuf.ByteString @@ -32,25 +34,27 @@ public interface AgentInterfaceOrBuilder extends /** *
-   * The transport supported this url. This is an open form string, to be
-   * easily extended for many transport protocols. The core ones officially
+   * The protocol binding supported at this URL. This is an open form string, to be
+   * easily extended for other protocol bindings. The core ones officially
    * supported are JSONRPC, GRPC and HTTP+JSON.
+   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    * 
* - * string transport = 2; - * @return The transport. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The protocolBinding. */ - java.lang.String getTransport(); + java.lang.String getProtocolBinding(); /** *
-   * The transport supported this url. This is an open form string, to be
-   * easily extended for many transport protocols. The core ones officially
+   * The protocol binding supported at this URL. This is an open form string, to be
+   * easily extended for other protocol bindings. The core ones officially
    * supported are JSONRPC, GRPC and HTTP+JSON.
+   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    * 
* - * string transport = 2; - * @return The bytes for transport. + * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protocolBinding. */ com.google.protobuf.ByteString - getTransportBytes(); + getProtocolBindingBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java index 32ed37765..3b4dabdfb 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java @@ -8,7 +8,7 @@ /** *
  * --8<-- [start:AgentProvider]
- * Represents information about the service provider of an agent.
+ * Represents the service provider of an agent.
  * 
* * Protobuf type {@code a2a.v1.AgentProvider} @@ -55,11 +55,11 @@ private AgentProvider() { private volatile java.lang.Object url_ = ""; /** *
-   * The providers reference url
+   * A URL for the agent provider's website or relevant documentation.
    * Example: "https://ai.google.dev"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ @java.lang.Override @@ -77,11 +77,11 @@ public java.lang.String getUrl() { } /** *
-   * The providers reference url
+   * A URL for the agent provider's website or relevant documentation.
    * Example: "https://ai.google.dev"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ @java.lang.Override @@ -104,11 +104,11 @@ public java.lang.String getUrl() { private volatile java.lang.Object organization_ = ""; /** *
-   * The providers organization name
+   * The name of the agent provider's organization.
    * Example: "Google"
    * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The organization. */ @java.lang.Override @@ -126,11 +126,11 @@ public java.lang.String getOrganization() { } /** *
-   * The providers organization name
+   * The name of the agent provider's organization.
    * Example: "Google"
    * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for organization. */ @java.lang.Override @@ -317,7 +317,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AgentProvider]
-   * Represents information about the service provider of an agent.
+   * Represents the service provider of an agent.
    * 
* * Protobuf type {@code a2a.v1.AgentProvider} @@ -474,11 +474,11 @@ public Builder mergeFrom( private java.lang.Object url_ = ""; /** *
-     * The providers reference url
+     * A URL for the agent provider's website or relevant documentation.
      * Example: "https://ai.google.dev"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ public java.lang.String getUrl() { @@ -495,11 +495,11 @@ public java.lang.String getUrl() { } /** *
-     * The providers reference url
+     * A URL for the agent provider's website or relevant documentation.
      * Example: "https://ai.google.dev"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -517,11 +517,11 @@ public java.lang.String getUrl() { } /** *
-     * The providers reference url
+     * A URL for the agent provider's website or relevant documentation.
      * Example: "https://ai.google.dev"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The url to set. * @return This builder for chaining. */ @@ -535,11 +535,11 @@ public Builder setUrl( } /** *
-     * The providers reference url
+     * A URL for the agent provider's website or relevant documentation.
      * Example: "https://ai.google.dev"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearUrl() { @@ -550,11 +550,11 @@ public Builder clearUrl() { } /** *
-     * The providers reference url
+     * A URL for the agent provider's website or relevant documentation.
      * Example: "https://ai.google.dev"
      * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -571,11 +571,11 @@ public Builder setUrlBytes( private java.lang.Object organization_ = ""; /** *
-     * The providers organization name
+     * The name of the agent provider's organization.
      * Example: "Google"
      * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The organization. */ public java.lang.String getOrganization() { @@ -592,11 +592,11 @@ public java.lang.String getOrganization() { } /** *
-     * The providers organization name
+     * The name of the agent provider's organization.
      * Example: "Google"
      * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for organization. */ public com.google.protobuf.ByteString @@ -614,11 +614,11 @@ public java.lang.String getOrganization() { } /** *
-     * The providers organization name
+     * The name of the agent provider's organization.
      * Example: "Google"
      * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The organization to set. * @return This builder for chaining. */ @@ -632,11 +632,11 @@ public Builder setOrganization( } /** *
-     * The providers organization name
+     * The name of the agent provider's organization.
      * Example: "Google"
      * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearOrganization() { @@ -647,11 +647,11 @@ public Builder clearOrganization() { } /** *
-     * The providers organization name
+     * The name of the agent provider's organization.
      * Example: "Google"
      * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for organization to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java index f5e70cc40..f7a6f2491 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java @@ -12,21 +12,21 @@ public interface AgentProviderOrBuilder extends /** *
-   * The providers reference url
+   * A URL for the agent provider's website or relevant documentation.
    * Example: "https://ai.google.dev"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ java.lang.String getUrl(); /** *
-   * The providers reference url
+   * A URL for the agent provider's website or relevant documentation.
    * Example: "https://ai.google.dev"
    * 
* - * string url = 1; + * string url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ com.google.protobuf.ByteString @@ -34,21 +34,21 @@ public interface AgentProviderOrBuilder extends /** *
-   * The providers organization name
+   * The name of the agent provider's organization.
    * Example: "Google"
    * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The organization. */ java.lang.String getOrganization(); /** *
-   * The providers organization name
+   * The name of the agent provider's organization.
    * Example: "Google"
    * 
* - * string organization = 2; + * string organization = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for organization. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java index 0895eaff1..462a8edec 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -8,11 +8,7 @@ /** *
  * --8<-- [start:AgentSkill]
- * AgentSkill represents a unit of action/solution that the agent can perform.
- * One can think of this as a type of highly reliable solution that an agent
- * can be tasked to provide. Agents have the autonomy to choose how and when
- * to use specific skills, but clients should have confidence that if the
- * skill is defined that unit of action can be reliably performed.
+ * Represents a distinct capability or function that an agent can perform.
  * 
* * Protobuf type {@code a2a.v1.AgentSkill} @@ -69,10 +65,10 @@ private AgentSkill() { private volatile java.lang.Object id_ = ""; /** *
-   * Unique identifier of the skill within this agent.
+   * A unique identifier for the agent's skill.
    * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The id. */ @java.lang.Override @@ -90,10 +86,10 @@ public java.lang.String getId() { } /** *
-   * Unique identifier of the skill within this agent.
+   * A unique identifier for the agent's skill.
    * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for id. */ @java.lang.Override @@ -116,10 +112,10 @@ public java.lang.String getId() { private volatile java.lang.Object name_ = ""; /** *
-   * A human readable name for the skill.
+   * A human-readable name for the skill.
    * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ @java.lang.Override @@ -137,10 +133,10 @@ public java.lang.String getName() { } /** *
-   * A human readable name for the skill.
+   * A human-readable name for the skill.
    * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ @java.lang.Override @@ -163,11 +159,10 @@ public java.lang.String getName() { private volatile java.lang.Object description_ = ""; /** *
-   * A human (or llm) readable description of the skill
-   * details and behaviors.
+   * A detailed description of the skill.
    * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The description. */ @java.lang.Override @@ -185,11 +180,10 @@ public java.lang.String getDescription() { } /** *
-   * A human (or llm) readable description of the skill
-   * details and behaviors.
+   * A detailed description of the skill.
    * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for description. */ @java.lang.Override @@ -213,11 +207,10 @@ public java.lang.String getDescription() { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the tags. */ public com.google.protobuf.ProtocolStringList @@ -226,11 +219,10 @@ public java.lang.String getDescription() { } /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The count of tags. */ public int getTagsCount() { @@ -238,11 +230,10 @@ public int getTagsCount() { } /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The tags at the given index. */ @@ -251,11 +242,10 @@ public java.lang.String getTags(int index) { } /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the tags at the given index. */ @@ -270,10 +260,7 @@ public java.lang.String getTags(int index) { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -285,10 +272,7 @@ public java.lang.String getTags(int index) { } /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -299,10 +283,7 @@ public int getExamplesCount() { } /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -314,10 +295,7 @@ public java.lang.String getExamples(int index) { } /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -335,7 +313,7 @@ public java.lang.String getExamples(int index) { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -347,7 +325,7 @@ public java.lang.String getExamples(int index) { } /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -358,7 +336,7 @@ public int getInputModesCount() { } /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -370,7 +348,7 @@ public java.lang.String getInputModes(int index) { } /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -388,7 +366,7 @@ public java.lang.String getInputModes(int index) { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -400,7 +378,7 @@ public java.lang.String getInputModes(int index) { } /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -411,7 +389,7 @@ public int getOutputModesCount() { } /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -423,7 +401,7 @@ public java.lang.String getOutputModes(int index) { } /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -441,10 +419,7 @@ public java.lang.String getOutputModes(int index) { /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -456,10 +431,7 @@ public java.util.List getSecurityList() { /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -472,10 +444,7 @@ public java.util.List getSecurityList() { /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -487,10 +456,7 @@ public int getSecurityCount() { /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -502,10 +468,7 @@ public io.a2a.grpc.Security getSecurity(int index) { /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -776,11 +739,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AgentSkill]
-   * AgentSkill represents a unit of action/solution that the agent can perform.
-   * One can think of this as a type of highly reliable solution that an agent
-   * can be tasked to provide. Agents have the autonomy to choose how and when
-   * to use specific skills, but clients should have confidence that if the
-   * skill is defined that unit of action can be reliably performed.
+   * Represents a distinct capability or function that an agent can perform.
    * 
* * Protobuf type {@code a2a.v1.AgentSkill} @@ -1098,10 +1057,10 @@ public Builder mergeFrom( private java.lang.Object id_ = ""; /** *
-     * Unique identifier of the skill within this agent.
+     * A unique identifier for the agent's skill.
      * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The id. */ public java.lang.String getId() { @@ -1118,10 +1077,10 @@ public java.lang.String getId() { } /** *
-     * Unique identifier of the skill within this agent.
+     * A unique identifier for the agent's skill.
      * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for id. */ public com.google.protobuf.ByteString @@ -1139,10 +1098,10 @@ public java.lang.String getId() { } /** *
-     * Unique identifier of the skill within this agent.
+     * A unique identifier for the agent's skill.
      * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The id to set. * @return This builder for chaining. */ @@ -1156,10 +1115,10 @@ public Builder setId( } /** *
-     * Unique identifier of the skill within this agent.
+     * A unique identifier for the agent's skill.
      * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearId() { @@ -1170,10 +1129,10 @@ public Builder clearId() { } /** *
-     * Unique identifier of the skill within this agent.
+     * A unique identifier for the agent's skill.
      * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for id to set. * @return This builder for chaining. */ @@ -1190,10 +1149,10 @@ public Builder setIdBytes( private java.lang.Object name_ = ""; /** *
-     * A human readable name for the skill.
+     * A human-readable name for the skill.
      * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ public java.lang.String getName() { @@ -1210,10 +1169,10 @@ public java.lang.String getName() { } /** *
-     * A human readable name for the skill.
+     * A human-readable name for the skill.
      * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -1231,10 +1190,10 @@ public java.lang.String getName() { } /** *
-     * A human readable name for the skill.
+     * A human-readable name for the skill.
      * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The name to set. * @return This builder for chaining. */ @@ -1248,10 +1207,10 @@ public Builder setName( } /** *
-     * A human readable name for the skill.
+     * A human-readable name for the skill.
      * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearName() { @@ -1262,10 +1221,10 @@ public Builder clearName() { } /** *
-     * A human readable name for the skill.
+     * A human-readable name for the skill.
      * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -1282,11 +1241,10 @@ public Builder setNameBytes( private java.lang.Object description_ = ""; /** *
-     * A human (or llm) readable description of the skill
-     * details and behaviors.
+     * A detailed description of the skill.
      * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The description. */ public java.lang.String getDescription() { @@ -1303,11 +1261,10 @@ public java.lang.String getDescription() { } /** *
-     * A human (or llm) readable description of the skill
-     * details and behaviors.
+     * A detailed description of the skill.
      * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for description. */ public com.google.protobuf.ByteString @@ -1325,11 +1282,10 @@ public java.lang.String getDescription() { } /** *
-     * A human (or llm) readable description of the skill
-     * details and behaviors.
+     * A detailed description of the skill.
      * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @param value The description to set. * @return This builder for chaining. */ @@ -1343,11 +1299,10 @@ public Builder setDescription( } /** *
-     * A human (or llm) readable description of the skill
-     * details and behaviors.
+     * A detailed description of the skill.
      * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearDescription() { @@ -1358,11 +1313,10 @@ public Builder clearDescription() { } /** *
-     * A human (or llm) readable description of the skill
-     * details and behaviors.
+     * A detailed description of the skill.
      * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for description to set. * @return This builder for chaining. */ @@ -1386,11 +1340,10 @@ private void ensureTagsIsMutable() { } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the tags. */ public com.google.protobuf.ProtocolStringList @@ -1400,11 +1353,10 @@ private void ensureTagsIsMutable() { } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The count of tags. */ public int getTagsCount() { @@ -1412,11 +1364,10 @@ public int getTagsCount() { } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The tags at the given index. */ @@ -1425,11 +1376,10 @@ public java.lang.String getTags(int index) { } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the tags at the given index. */ @@ -1439,11 +1389,10 @@ public java.lang.String getTags(int index) { } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index to set the value at. * @param value The tags to set. * @return This builder for chaining. @@ -1459,11 +1408,10 @@ public Builder setTags( } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param value The tags to add. * @return This builder for chaining. */ @@ -1478,11 +1426,10 @@ public Builder addTags( } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param values The tags to add. * @return This builder for chaining. */ @@ -1497,11 +1444,10 @@ public Builder addAllTags( } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTags() { @@ -1513,11 +1459,10 @@ public Builder clearTags() { } /** *
-     * A set of tags for the skill to enhance categorization/utilization.
-     * Example: ["cooking", "customer support", "billing"]
+     * A set of keywords describing the skill's capabilities.
      * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes of the tags to add. * @return This builder for chaining. */ @@ -1542,10 +1487,7 @@ private void ensureExamplesIsMutable() { } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1558,10 +1500,7 @@ private void ensureExamplesIsMutable() { } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1572,10 +1511,7 @@ public int getExamplesCount() { } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1587,10 +1523,7 @@ public java.lang.String getExamples(int index) { } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1603,10 +1536,7 @@ public java.lang.String getExamples(int index) { } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1625,10 +1555,7 @@ public Builder setExamples( } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1646,10 +1573,7 @@ public Builder addExamples( } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1667,10 +1591,7 @@ public Builder addAllExamples( } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1685,10 +1606,7 @@ public Builder clearExamples() { } /** *
-     * A set of example queries that this skill is designed to address.
-     * These examples should help the caller to understand how to craft requests
-     * to the agent to achieve specific goals.
-     * Example: ["I need a recipe for bread"]
+     * Example prompts or scenarios that this skill can handle.
      * 
* * repeated string examples = 5; @@ -1716,7 +1634,7 @@ private void ensureInputModesIsMutable() { } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1729,7 +1647,7 @@ private void ensureInputModesIsMutable() { } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1740,7 +1658,7 @@ public int getInputModesCount() { } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1752,7 +1670,7 @@ public java.lang.String getInputModes(int index) { } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1765,7 +1683,7 @@ public java.lang.String getInputModes(int index) { } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1784,7 +1702,7 @@ public Builder setInputModes( } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1802,7 +1720,7 @@ public Builder addInputModes( } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1820,7 +1738,7 @@ public Builder addAllInputModes( } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1835,7 +1753,7 @@ public Builder clearInputModes() { } /** *
-     * Possible input modalities supported.
+     * The set of supported input media types for this skill, overriding the agent's defaults.
      * 
* * repeated string input_modes = 6; @@ -1863,7 +1781,7 @@ private void ensureOutputModesIsMutable() { } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1876,7 +1794,7 @@ private void ensureOutputModesIsMutable() { } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1887,7 +1805,7 @@ public int getOutputModesCount() { } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1899,7 +1817,7 @@ public java.lang.String getOutputModes(int index) { } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1912,7 +1830,7 @@ public java.lang.String getOutputModes(int index) { } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1931,7 +1849,7 @@ public Builder setOutputModes( } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1949,7 +1867,7 @@ public Builder addOutputModes( } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1967,7 +1885,7 @@ public Builder addAllOutputModes( } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -1982,7 +1900,7 @@ public Builder clearOutputModes() { } /** *
-     * Possible output modalities produced
+     * The set of supported output media types for this skill, overriding the agent's defaults.
      * 
* * repeated string output_modes = 7; @@ -2015,10 +1933,7 @@ private void ensureSecurityIsMutable() { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2033,10 +1948,7 @@ public java.util.List getSecurityList() { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2051,10 +1963,7 @@ public int getSecurityCount() { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2069,10 +1978,7 @@ public io.a2a.grpc.Security getSecurity(int index) { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2094,10 +2000,7 @@ public Builder setSecurity( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2116,10 +2019,7 @@ public Builder setSecurity( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2140,10 +2040,7 @@ public Builder addSecurity(io.a2a.grpc.Security value) { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2165,10 +2062,7 @@ public Builder addSecurity( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2187,10 +2081,7 @@ public Builder addSecurity( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2209,10 +2100,7 @@ public Builder addSecurity( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2232,10 +2120,7 @@ public Builder addAllSecurity( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2253,10 +2138,7 @@ public Builder clearSecurity() { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2274,10 +2156,7 @@ public Builder removeSecurity(int index) { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2289,10 +2168,7 @@ public io.a2a.grpc.Security.Builder getSecurityBuilder( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2307,10 +2183,7 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2326,10 +2199,7 @@ public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2341,10 +2211,7 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder() { /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; @@ -2357,10 +2224,7 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder( /** *
      * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-     * Security schemes necessary for the agent to leverage this skill.
-     * As in the overall AgentCard.security, this list represents a logical OR of
-     * security requirement objects. Each object is a set of security schemes
-     * that must be used together (a logical AND).
+     * Security schemes necessary for this skill.
      * 
* * repeated .a2a.v1.Security security = 8; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java index 225316b1a..cddebfff9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -12,19 +12,19 @@ public interface AgentSkillOrBuilder extends /** *
-   * Unique identifier of the skill within this agent.
+   * A unique identifier for the agent's skill.
    * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The id. */ java.lang.String getId(); /** *
-   * Unique identifier of the skill within this agent.
+   * A unique identifier for the agent's skill.
    * 
* - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for id. */ com.google.protobuf.ByteString @@ -32,19 +32,19 @@ public interface AgentSkillOrBuilder extends /** *
-   * A human readable name for the skill.
+   * A human-readable name for the skill.
    * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ java.lang.String getName(); /** *
-   * A human readable name for the skill.
+   * A human-readable name for the skill.
    * 
* - * string name = 2; + * string name = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ com.google.protobuf.ByteString @@ -52,21 +52,19 @@ public interface AgentSkillOrBuilder extends /** *
-   * A human (or llm) readable description of the skill
-   * details and behaviors.
+   * A detailed description of the skill.
    * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The description. */ java.lang.String getDescription(); /** *
-   * A human (or llm) readable description of the skill
-   * details and behaviors.
+   * A detailed description of the skill.
    * 
* - * string description = 3; + * string description = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for description. */ com.google.protobuf.ByteString @@ -74,43 +72,39 @@ public interface AgentSkillOrBuilder extends /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the tags. */ java.util.List getTagsList(); /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The count of tags. */ int getTagsCount(); /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The tags at the given index. */ java.lang.String getTags(int index); /** *
-   * A set of tags for the skill to enhance categorization/utilization.
-   * Example: ["cooking", "customer support", "billing"]
+   * A set of keywords describing the skill's capabilities.
    * 
* - * repeated string tags = 4; + * repeated string tags = 4 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the tags at the given index. */ @@ -119,10 +113,7 @@ public interface AgentSkillOrBuilder extends /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -132,10 +123,7 @@ public interface AgentSkillOrBuilder extends getExamplesList(); /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -144,10 +132,7 @@ public interface AgentSkillOrBuilder extends int getExamplesCount(); /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -157,10 +142,7 @@ public interface AgentSkillOrBuilder extends java.lang.String getExamples(int index); /** *
-   * A set of example queries that this skill is designed to address.
-   * These examples should help the caller to understand how to craft requests
-   * to the agent to achieve specific goals.
-   * Example: ["I need a recipe for bread"]
+   * Example prompts or scenarios that this skill can handle.
    * 
* * repeated string examples = 5; @@ -172,7 +154,7 @@ public interface AgentSkillOrBuilder extends /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -182,7 +164,7 @@ public interface AgentSkillOrBuilder extends getInputModesList(); /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -191,7 +173,7 @@ public interface AgentSkillOrBuilder extends int getInputModesCount(); /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -201,7 +183,7 @@ public interface AgentSkillOrBuilder extends java.lang.String getInputModes(int index); /** *
-   * Possible input modalities supported.
+   * The set of supported input media types for this skill, overriding the agent's defaults.
    * 
* * repeated string input_modes = 6; @@ -213,7 +195,7 @@ public interface AgentSkillOrBuilder extends /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -223,7 +205,7 @@ public interface AgentSkillOrBuilder extends getOutputModesList(); /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -232,7 +214,7 @@ public interface AgentSkillOrBuilder extends int getOutputModesCount(); /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -242,7 +224,7 @@ public interface AgentSkillOrBuilder extends java.lang.String getOutputModes(int index); /** *
-   * Possible output modalities produced
+   * The set of supported output media types for this skill, overriding the agent's defaults.
    * 
* * repeated string output_modes = 7; @@ -255,10 +237,7 @@ public interface AgentSkillOrBuilder extends /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -268,10 +247,7 @@ public interface AgentSkillOrBuilder extends /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -280,10 +256,7 @@ public interface AgentSkillOrBuilder extends /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -292,10 +265,7 @@ public interface AgentSkillOrBuilder extends /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; @@ -305,10 +275,7 @@ public interface AgentSkillOrBuilder extends /** *
    * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
-   * Security schemes necessary for the agent to leverage this skill.
-   * As in the overall AgentCard.security, this list represents a logical OR of
-   * security requirement objects. Each object is a set of security schemes
-   * that must be used together (a logical AND).
+   * Security schemes necessary for this skill.
    * 
* * repeated .a2a.v1.Security security = 8; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java index 357d0c3a2..aef816208 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java @@ -8,9 +8,7 @@ /** *
  * --8<-- [start:Artifact]
- * Artifacts are the container for task completed results. These are similar
- * to Messages but are intended to be the product of a task, as opposed to
- * point-to-point communication.
+ * Artifacts represent task outputs.
  * 
* * Protobuf type {@code a2a.v1.Artifact} @@ -66,7 +64,7 @@ private Artifact() { * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The artifactId. */ @java.lang.Override @@ -88,7 +86,7 @@ public java.lang.String getArtifactId() { * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for artifactId. */ @java.lang.Override @@ -208,7 +206,7 @@ public java.lang.String getDescription() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List getPartsList() { @@ -219,7 +217,7 @@ public java.util.List getPartsList() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List @@ -231,7 +229,7 @@ public java.util.List getPartsList() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public int getPartsCount() { @@ -242,7 +240,7 @@ public int getPartsCount() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.Part getParts(int index) { @@ -253,7 +251,7 @@ public io.a2a.grpc.Part getParts(int index) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( @@ -577,9 +575,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:Artifact]
-   * Artifacts are the container for task completed results. These are similar
-   * to Messages but are intended to be the product of a task, as opposed to
-   * point-to-point communication.
+   * Artifacts represent task outputs.
    * 
* * Protobuf type {@code a2a.v1.Artifact} @@ -865,7 +861,7 @@ public Builder mergeFrom( * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The artifactId. */ public java.lang.String getArtifactId() { @@ -886,7 +882,7 @@ public java.lang.String getArtifactId() { * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for artifactId. */ public com.google.protobuf.ByteString @@ -908,7 +904,7 @@ public java.lang.String getArtifactId() { * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The artifactId to set. * @return This builder for chaining. */ @@ -926,7 +922,7 @@ public Builder setArtifactId( * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearArtifactId() { @@ -941,7 +937,7 @@ public Builder clearArtifactId() { * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for artifactId to set. * @return This builder for chaining. */ @@ -1156,7 +1152,7 @@ private void ensurePartsIsMutable() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getPartsList() { if (partsBuilder_ == null) { @@ -1170,7 +1166,7 @@ public java.util.List getPartsList() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public int getPartsCount() { if (partsBuilder_ == null) { @@ -1184,7 +1180,7 @@ public int getPartsCount() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part getParts(int index) { if (partsBuilder_ == null) { @@ -1198,7 +1194,7 @@ public io.a2a.grpc.Part getParts(int index) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setParts( int index, io.a2a.grpc.Part value) { @@ -1219,7 +1215,7 @@ public Builder setParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setParts( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1237,7 +1233,7 @@ public Builder setParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts(io.a2a.grpc.Part value) { if (partsBuilder_ == null) { @@ -1257,7 +1253,7 @@ public Builder addParts(io.a2a.grpc.Part value) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts( int index, io.a2a.grpc.Part value) { @@ -1278,7 +1274,7 @@ public Builder addParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts( io.a2a.grpc.Part.Builder builderForValue) { @@ -1296,7 +1292,7 @@ public Builder addParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1314,7 +1310,7 @@ public Builder addParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addAllParts( java.lang.Iterable values) { @@ -1333,7 +1329,7 @@ public Builder addAllParts( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearParts() { if (partsBuilder_ == null) { @@ -1350,7 +1346,7 @@ public Builder clearParts() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeParts(int index) { if (partsBuilder_ == null) { @@ -1367,7 +1363,7 @@ public Builder removeParts(int index) { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part.Builder getPartsBuilder( int index) { @@ -1378,7 +1374,7 @@ public io.a2a.grpc.Part.Builder getPartsBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index) { @@ -1392,7 +1388,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getPartsOrBuilderList() { @@ -1407,7 +1403,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part.Builder addPartsBuilder() { return internalGetPartsFieldBuilder().addBuilder( @@ -1418,7 +1414,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder() { * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part.Builder addPartsBuilder( int index) { @@ -1430,7 +1426,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder( * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getPartsBuilderList() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java index 341c9e1a1..f05c3125d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java @@ -16,7 +16,7 @@ public interface ArtifactOrBuilder extends * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The artifactId. */ java.lang.String getArtifactId(); @@ -26,7 +26,7 @@ public interface ArtifactOrBuilder extends * within a task. * * - * string artifact_id = 1; + * string artifact_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for artifactId. */ com.google.protobuf.ByteString @@ -77,7 +77,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getPartsList(); @@ -86,7 +86,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.Part getParts(int index); /** @@ -94,7 +94,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ int getPartsCount(); /** @@ -102,7 +102,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getPartsOrBuilderList(); @@ -111,7 +111,7 @@ public interface ArtifactOrBuilder extends * The content of the artifact. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java index 9456ffda4..bc6f49923 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java @@ -57,10 +57,10 @@ private AuthenticationInfo() { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the schemes. */ public com.google.protobuf.ProtocolStringList @@ -69,10 +69,10 @@ private AuthenticationInfo() { } /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The count of schemes. */ public int getSchemesCount() { @@ -80,10 +80,10 @@ public int getSchemesCount() { } /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The schemes at the given index. */ @@ -92,10 +92,10 @@ public java.lang.String getSchemes(int index) { } /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the schemes at the given index. */ @@ -499,10 +499,10 @@ private void ensureSchemesIsMutable() { } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the schemes. */ public com.google.protobuf.ProtocolStringList @@ -512,10 +512,10 @@ private void ensureSchemesIsMutable() { } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The count of schemes. */ public int getSchemesCount() { @@ -523,10 +523,10 @@ public int getSchemesCount() { } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The schemes at the given index. */ @@ -535,10 +535,10 @@ public java.lang.String getSchemes(int index) { } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the schemes at the given index. */ @@ -548,10 +548,10 @@ public java.lang.String getSchemes(int index) { } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index to set the value at. * @param value The schemes to set. * @return This builder for chaining. @@ -567,10 +567,10 @@ public Builder setSchemes( } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The schemes to add. * @return This builder for chaining. */ @@ -585,10 +585,10 @@ public Builder addSchemes( } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param values The schemes to add. * @return This builder for chaining. */ @@ -603,10 +603,10 @@ public Builder addAllSchemes( } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearSchemes() { @@ -618,10 +618,10 @@ public Builder clearSchemes() { } /** *
-     * Supported authentication schemes - e.g. Basic, Bearer, etc
+     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
      * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes of the schemes to add. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java index b18ba8b63..abb389a34 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java @@ -12,39 +12,39 @@ public interface AuthenticationInfoOrBuilder extends /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return A list containing the schemes. */ java.util.List getSchemesList(); /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The count of schemes. */ int getSchemesCount(); /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the element to return. * @return The schemes at the given index. */ java.lang.String getSchemes(int index); /** *
-   * Supported authentication schemes - e.g. Basic, Bearer, etc
+   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    * 
* - * repeated string schemes = 1; + * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; * @param index The index of the value to return. * @return The bytes of the schemes at the given index. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java index a4faf832e..c26823413 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:AuthorizationCodeOAuthFlow]
+ * Defines configuration details for the OAuth 2.0 Authorization Code flow.
  * 
* * Protobuf type {@code a2a.v1.AuthorizationCodeOAuthFlow} @@ -67,11 +68,10 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl private volatile java.lang.Object authorizationUrl_ = ""; /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The authorizationUrl. */ @java.lang.Override @@ -89,11 +89,10 @@ public java.lang.String getAuthorizationUrl() { } /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for authorizationUrl. */ @java.lang.Override @@ -116,11 +115,10 @@ public java.lang.String getAuthorizationUrl() { private volatile java.lang.Object tokenUrl_ = ""; /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ @java.lang.Override @@ -138,11 +136,10 @@ public java.lang.String getTokenUrl() { } /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -165,8 +162,7 @@ public java.lang.String getTokenUrl() { private volatile java.lang.Object refreshUrl_ = ""; /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 3; @@ -187,8 +183,7 @@ public java.lang.String getRefreshUrl() { } /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 3; @@ -237,11 +232,10 @@ public int getScopesCount() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -259,11 +253,10 @@ public java.util.Map getScopes() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -271,11 +264,10 @@ public java.util.Map getScopesMap() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -290,11 +282,10 @@ java.lang.String getScopesOrDefault( } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -509,6 +500,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:AuthorizationCodeOAuthFlow]
+   * Defines configuration details for the OAuth 2.0 Authorization Code flow.
    * 
* * Protobuf type {@code a2a.v1.AuthorizationCodeOAuthFlow} @@ -718,11 +710,10 @@ public Builder mergeFrom( private java.lang.Object authorizationUrl_ = ""; /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The authorizationUrl. */ public java.lang.String getAuthorizationUrl() { @@ -739,11 +730,10 @@ public java.lang.String getAuthorizationUrl() { } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for authorizationUrl. */ public com.google.protobuf.ByteString @@ -761,11 +751,10 @@ public java.lang.String getAuthorizationUrl() { } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The authorizationUrl to set. * @return This builder for chaining. */ @@ -779,11 +768,10 @@ public Builder setAuthorizationUrl( } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearAuthorizationUrl() { @@ -794,11 +782,10 @@ public Builder clearAuthorizationUrl() { } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for authorizationUrl to set. * @return This builder for chaining. */ @@ -815,11 +802,10 @@ public Builder setAuthorizationUrlBytes( private java.lang.Object tokenUrl_ = ""; /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -836,11 +822,10 @@ public java.lang.String getTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -858,11 +843,10 @@ public java.lang.String getTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -876,11 +860,10 @@ public Builder setTokenUrl( } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTokenUrl() { @@ -891,11 +874,10 @@ public Builder clearTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -912,8 +894,7 @@ public Builder setTokenUrlBytes( private java.lang.Object refreshUrl_ = ""; /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 3; @@ -933,8 +914,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 3; @@ -955,8 +935,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 3; @@ -973,8 +952,7 @@ public Builder setRefreshUrl( } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 3; @@ -988,8 +966,7 @@ public Builder clearRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 3; @@ -1034,11 +1011,10 @@ public int getScopesCount() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -1056,11 +1032,10 @@ public java.util.Map getScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -1068,11 +1043,10 @@ public java.util.Map getScopesMap() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -1087,11 +1061,10 @@ java.lang.String getScopesOrDefault( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -1112,11 +1085,10 @@ public Builder clearScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeScopes( java.lang.String key) { @@ -1136,11 +1108,10 @@ public Builder removeScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putScopes( java.lang.String key, @@ -1154,11 +1125,10 @@ public Builder putScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java index fd6247a7f..ed82d6dae 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java @@ -12,21 +12,19 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The authorizationUrl. */ java.lang.String getAuthorizationUrl(); /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for authorizationUrl. */ com.google.protobuf.ByteString @@ -34,21 +32,19 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ java.lang.String getTokenUrl(); /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 2; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -56,8 +52,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 3; @@ -66,8 +61,7 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends java.lang.String getRefreshUrl(); /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 3; @@ -78,20 +72,18 @@ public interface AuthorizationCodeOAuthFlowOrBuilder extends /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ int getScopesCount(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ boolean containsScopes( java.lang.String key); @@ -103,21 +95,19 @@ boolean containsScopes( getScopes(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ java.util.Map getScopesMap(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -126,11 +116,10 @@ java.lang.String getScopesOrDefault( java.lang.String defaultValue); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 4; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java index 8e4c40cb7..9127aabb1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:CancelTaskRequest]
+ * Represents a request for the `tasks/cancel` method.
  * 
* * Protobuf type {@code a2a.v1.CancelTaskRequest} @@ -256,6 +257,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:CancelTaskRequest]
+   * Represents a request for the `tasks/cancel` method.
    * 
* * Protobuf type {@code a2a.v1.CancelTaskRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java index 7e29d33d4..6fbf601c9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:ClientCredentialsOAuthFlow]
+ * Defines configuration details for the OAuth 2.0 Client Credentials flow.
  * 
* * Protobuf type {@code a2a.v1.ClientCredentialsOAuthFlow} @@ -66,11 +67,10 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl private volatile java.lang.Object tokenUrl_ = ""; /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ @java.lang.Override @@ -88,11 +88,10 @@ public java.lang.String getTokenUrl() { } /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -115,8 +114,7 @@ public java.lang.String getTokenUrl() { private volatile java.lang.Object refreshUrl_ = ""; /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -137,8 +135,7 @@ public java.lang.String getRefreshUrl() { } /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -187,11 +184,10 @@ public int getScopesCount() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -209,11 +205,10 @@ public java.util.Map getScopes() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -221,11 +216,10 @@ public java.util.Map getScopesMap() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -240,11 +234,10 @@ java.lang.String getScopesOrDefault( } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -449,6 +442,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:ClientCredentialsOAuthFlow]
+   * Defines configuration details for the OAuth 2.0 Client Credentials flow.
    * 
* * Protobuf type {@code a2a.v1.ClientCredentialsOAuthFlow} @@ -644,11 +638,10 @@ public Builder mergeFrom( private java.lang.Object tokenUrl_ = ""; /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -665,11 +658,10 @@ public java.lang.String getTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -687,11 +679,10 @@ public java.lang.String getTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -705,11 +696,10 @@ public Builder setTokenUrl( } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTokenUrl() { @@ -720,11 +710,10 @@ public Builder clearTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -741,8 +730,7 @@ public Builder setTokenUrlBytes( private java.lang.Object refreshUrl_ = ""; /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -762,8 +750,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -784,8 +771,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -802,8 +788,7 @@ public Builder setRefreshUrl( } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -817,8 +802,7 @@ public Builder clearRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -863,11 +847,10 @@ public int getScopesCount() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -885,11 +868,10 @@ public java.util.Map getScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -897,11 +879,10 @@ public java.util.Map getScopesMap() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -916,11 +897,10 @@ java.lang.String getScopesOrDefault( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -941,11 +921,10 @@ public Builder clearScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeScopes( java.lang.String key) { @@ -965,11 +944,10 @@ public Builder removeScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putScopes( java.lang.String key, @@ -983,11 +961,10 @@ public Builder putScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java index 81ab91e99..b41bc82fe 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java @@ -12,21 +12,19 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ java.lang.String getTokenUrl(); /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -34,8 +32,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -44,8 +41,7 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends java.lang.String getRefreshUrl(); /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -56,20 +52,18 @@ public interface ClientCredentialsOAuthFlowOrBuilder extends /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ int getScopesCount(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ boolean containsScopes( java.lang.String key); @@ -81,21 +75,19 @@ boolean containsScopes( getScopes(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ java.util.Map getScopesMap(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -104,11 +96,10 @@ java.lang.String getScopesOrDefault( java.lang.String defaultValue); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java index a74c5f1f7..408fe6c89 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java @@ -52,7 +52,11 @@ private DataPart() { public static final int DATA_FIELD_NUMBER = 1; private com.google.protobuf.Struct data_; /** - * .google.protobuf.Struct data = 1; + *
+   * The structured data content.
+   * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the data field is set. */ @java.lang.Override @@ -60,7 +64,11 @@ public boolean hasData() { return ((bitField0_ & 0x00000001) != 0); } /** - * .google.protobuf.Struct data = 1; + *
+   * The structured data content.
+   * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The data. */ @java.lang.Override @@ -68,7 +76,11 @@ public com.google.protobuf.Struct getData() { return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; } /** - * .google.protobuf.Struct data = 1; + *
+   * The structured data content.
+   * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getDataOrBuilder() { @@ -399,14 +411,22 @@ public Builder mergeFrom( private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> dataBuilder_; /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the data field is set. */ public boolean hasData() { return ((bitField0_ & 0x00000001) != 0); } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The data. */ public com.google.protobuf.Struct getData() { @@ -417,7 +437,11 @@ public com.google.protobuf.Struct getData() { } } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setData(com.google.protobuf.Struct value) { if (dataBuilder_ == null) { @@ -433,7 +457,11 @@ public Builder setData(com.google.protobuf.Struct value) { return this; } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setData( com.google.protobuf.Struct.Builder builderForValue) { @@ -447,7 +475,11 @@ public Builder setData( return this; } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeData(com.google.protobuf.Struct value) { if (dataBuilder_ == null) { @@ -468,7 +500,11 @@ public Builder mergeData(com.google.protobuf.Struct value) { return this; } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearData() { bitField0_ = (bitField0_ & ~0x00000001); @@ -481,7 +517,11 @@ public Builder clearData() { return this; } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ public com.google.protobuf.Struct.Builder getDataBuilder() { bitField0_ |= 0x00000001; @@ -489,7 +529,11 @@ public com.google.protobuf.Struct.Builder getDataBuilder() { return internalGetDataFieldBuilder().getBuilder(); } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ public com.google.protobuf.StructOrBuilder getDataOrBuilder() { if (dataBuilder_ != null) { @@ -500,7 +544,11 @@ public com.google.protobuf.StructOrBuilder getDataOrBuilder() { } } /** - * .google.protobuf.Struct data = 1; + *
+     * The structured data content.
+     * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java index 9877e9f4d..9dad437b0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java @@ -11,17 +11,29 @@ public interface DataPartOrBuilder extends com.google.protobuf.MessageOrBuilder { /** - * .google.protobuf.Struct data = 1; + *
+   * The structured data content.
+   * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the data field is set. */ boolean hasData(); /** - * .google.protobuf.Struct data = 1; + *
+   * The structured data content.
+   * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The data. */ com.google.protobuf.Struct getData(); /** - * .google.protobuf.Struct data = 1; + *
+   * The structured data content.
+   * 
+ * + * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; */ com.google.protobuf.StructOrBuilder getDataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java index bf667f37b..3ecd864e0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:DeleteTaskPushNotificationConfigRequest]
+ * Represents a request for the `tasks/pushNotificationConfig/delete` method.
  * 
* * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} @@ -256,6 +257,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:DeleteTaskPushNotificationConfigRequest]
+   * Represents a request for the `tasks/pushNotificationConfig/delete` method.
    * 
* * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java index 895266604..88f8aa535 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java @@ -36,7 +36,7 @@ private FilePart(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private FilePart() { - mimeType_ = ""; + mediaType_ = ""; name_ = ""; } @@ -97,6 +97,10 @@ public int getNumber() { public static final int FILE_WITH_URI_FIELD_NUMBER = 1; /** + *
+   * A URL pointing to the file's content.
+   * 
+ * * string file_with_uri = 1; * @return Whether the fileWithUri field is set. */ @@ -104,6 +108,10 @@ public boolean hasFileWithUri() { return fileCase_ == 1; } /** + *
+   * A URL pointing to the file's content.
+   * 
+ * * string file_with_uri = 1; * @return The fileWithUri. */ @@ -125,6 +133,10 @@ public java.lang.String getFileWithUri() { } } /** + *
+   * A URL pointing to the file's content.
+   * 
+ * * string file_with_uri = 1; * @return The bytes for fileWithUri. */ @@ -149,6 +161,10 @@ public java.lang.String getFileWithUri() { public static final int FILE_WITH_BYTES_FIELD_NUMBER = 2; /** + *
+   * The base64-encoded content of the file.
+   * 
+ * * bytes file_with_bytes = 2; * @return Whether the fileWithBytes field is set. */ @@ -157,6 +173,10 @@ public boolean hasFileWithBytes() { return fileCase_ == 2; } /** + *
+   * The base64-encoded content of the file.
+   * 
+ * * bytes file_with_bytes = 2; * @return The fileWithBytes. */ @@ -168,39 +188,47 @@ public com.google.protobuf.ByteString getFileWithBytes() { return com.google.protobuf.ByteString.EMPTY; } - public static final int MIME_TYPE_FIELD_NUMBER = 3; + public static final int MEDIA_TYPE_FIELD_NUMBER = 3; @SuppressWarnings("serial") - private volatile java.lang.Object mimeType_ = ""; + private volatile java.lang.Object mediaType_ = ""; /** - * string mime_type = 3; - * @return The mimeType. + *
+   * The media type of the file (e.g., "application/pdf").
+   * 
+ * + * string media_type = 3; + * @return The mediaType. */ @java.lang.Override - public java.lang.String getMimeType() { - java.lang.Object ref = mimeType_; + public java.lang.String getMediaType() { + java.lang.Object ref = mediaType_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - mimeType_ = s; + mediaType_ = s; return s; } } /** - * string mime_type = 3; - * @return The bytes for mimeType. + *
+   * The media type of the file (e.g., "application/pdf").
+   * 
+ * + * string media_type = 3; + * @return The bytes for mediaType. */ @java.lang.Override public com.google.protobuf.ByteString - getMimeTypeBytes() { - java.lang.Object ref = mimeType_; + getMediaTypeBytes() { + java.lang.Object ref = mediaType_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - mimeType_ = b; + mediaType_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -211,6 +239,10 @@ public java.lang.String getMimeType() { @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; /** + *
+   * An optional name for the file (e.g., "document.pdf").
+   * 
+ * * string name = 4; * @return The name. */ @@ -228,6 +260,10 @@ public java.lang.String getName() { } } /** + *
+   * An optional name for the file (e.g., "document.pdf").
+   * 
+ * * string name = 4; * @return The bytes for name. */ @@ -267,8 +303,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) output.writeBytes( 2, (com.google.protobuf.ByteString) file_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mimeType_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 3, mimeType_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mediaType_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, mediaType_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 4, name_); @@ -290,8 +326,8 @@ public int getSerializedSize() { .computeBytesSize( 2, (com.google.protobuf.ByteString) file_); } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mimeType_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(3, mimeType_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mediaType_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, mediaType_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(4, name_); @@ -311,8 +347,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.FilePart other = (io.a2a.grpc.FilePart) obj; - if (!getMimeType() - .equals(other.getMimeType())) return false; + if (!getMediaType() + .equals(other.getMediaType())) return false; if (!getName() .equals(other.getName())) return false; if (!getFileCase().equals(other.getFileCase())) return false; @@ -339,8 +375,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + MIME_TYPE_FIELD_NUMBER; - hash = (53 * hash) + getMimeType().hashCode(); + hash = (37 * hash) + MEDIA_TYPE_FIELD_NUMBER; + hash = (53 * hash) + getMediaType().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); switch (fileCase_) { @@ -494,7 +530,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; - mimeType_ = ""; + mediaType_ = ""; name_ = ""; fileCase_ = 0; file_ = null; @@ -533,7 +569,7 @@ public io.a2a.grpc.FilePart buildPartial() { private void buildPartial0(io.a2a.grpc.FilePart result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000004) != 0)) { - result.mimeType_ = mimeType_; + result.mediaType_ = mediaType_; } if (((from_bitField0_ & 0x00000008) != 0)) { result.name_ = name_; @@ -557,8 +593,8 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.FilePart other) { if (other == io.a2a.grpc.FilePart.getDefaultInstance()) return this; - if (!other.getMimeType().isEmpty()) { - mimeType_ = other.mimeType_; + if (!other.getMediaType().isEmpty()) { + mediaType_ = other.mediaType_; bitField0_ |= 0x00000004; onChanged(); } @@ -620,7 +656,7 @@ public Builder mergeFrom( break; } // case 18 case 26: { - mimeType_ = input.readStringRequireUtf8(); + mediaType_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000004; break; } // case 26 @@ -662,6 +698,10 @@ public Builder clearFile() { private int bitField0_; /** + *
+     * A URL pointing to the file's content.
+     * 
+ * * string file_with_uri = 1; * @return Whether the fileWithUri field is set. */ @@ -670,6 +710,10 @@ public boolean hasFileWithUri() { return fileCase_ == 1; } /** + *
+     * A URL pointing to the file's content.
+     * 
+ * * string file_with_uri = 1; * @return The fileWithUri. */ @@ -692,6 +736,10 @@ public java.lang.String getFileWithUri() { } } /** + *
+     * A URL pointing to the file's content.
+     * 
+ * * string file_with_uri = 1; * @return The bytes for fileWithUri. */ @@ -715,6 +763,10 @@ public java.lang.String getFileWithUri() { } } /** + *
+     * A URL pointing to the file's content.
+     * 
+ * * string file_with_uri = 1; * @param value The fileWithUri to set. * @return This builder for chaining. @@ -728,6 +780,10 @@ public Builder setFileWithUri( return this; } /** + *
+     * A URL pointing to the file's content.
+     * 
+ * * string file_with_uri = 1; * @return This builder for chaining. */ @@ -740,6 +796,10 @@ public Builder clearFileWithUri() { return this; } /** + *
+     * A URL pointing to the file's content.
+     * 
+ * * string file_with_uri = 1; * @param value The bytes for fileWithUri to set. * @return This builder for chaining. @@ -755,6 +815,10 @@ public Builder setFileWithUriBytes( } /** + *
+     * The base64-encoded content of the file.
+     * 
+ * * bytes file_with_bytes = 2; * @return Whether the fileWithBytes field is set. */ @@ -762,6 +826,10 @@ public boolean hasFileWithBytes() { return fileCase_ == 2; } /** + *
+     * The base64-encoded content of the file.
+     * 
+ * * bytes file_with_bytes = 2; * @return The fileWithBytes. */ @@ -772,6 +840,10 @@ public com.google.protobuf.ByteString getFileWithBytes() { return com.google.protobuf.ByteString.EMPTY; } /** + *
+     * The base64-encoded content of the file.
+     * 
+ * * bytes file_with_bytes = 2; * @param value The fileWithBytes to set. * @return This builder for chaining. @@ -784,6 +856,10 @@ public Builder setFileWithBytes(com.google.protobuf.ByteString value) { return this; } /** + *
+     * The base64-encoded content of the file.
+     * 
+ * * bytes file_with_bytes = 2; * @return This builder for chaining. */ @@ -796,73 +872,93 @@ public Builder clearFileWithBytes() { return this; } - private java.lang.Object mimeType_ = ""; + private java.lang.Object mediaType_ = ""; /** - * string mime_type = 3; - * @return The mimeType. + *
+     * The media type of the file (e.g., "application/pdf").
+     * 
+ * + * string media_type = 3; + * @return The mediaType. */ - public java.lang.String getMimeType() { - java.lang.Object ref = mimeType_; + public java.lang.String getMediaType() { + java.lang.Object ref = mediaType_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - mimeType_ = s; + mediaType_ = s; return s; } else { return (java.lang.String) ref; } } /** - * string mime_type = 3; - * @return The bytes for mimeType. + *
+     * The media type of the file (e.g., "application/pdf").
+     * 
+ * + * string media_type = 3; + * @return The bytes for mediaType. */ public com.google.protobuf.ByteString - getMimeTypeBytes() { - java.lang.Object ref = mimeType_; + getMediaTypeBytes() { + java.lang.Object ref = mediaType_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - mimeType_ = b; + mediaType_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** - * string mime_type = 3; - * @param value The mimeType to set. + *
+     * The media type of the file (e.g., "application/pdf").
+     * 
+ * + * string media_type = 3; + * @param value The mediaType to set. * @return This builder for chaining. */ - public Builder setMimeType( + public Builder setMediaType( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - mimeType_ = value; + mediaType_ = value; bitField0_ |= 0x00000004; onChanged(); return this; } /** - * string mime_type = 3; + *
+     * The media type of the file (e.g., "application/pdf").
+     * 
+ * + * string media_type = 3; * @return This builder for chaining. */ - public Builder clearMimeType() { - mimeType_ = getDefaultInstance().getMimeType(); + public Builder clearMediaType() { + mediaType_ = getDefaultInstance().getMediaType(); bitField0_ = (bitField0_ & ~0x00000004); onChanged(); return this; } /** - * string mime_type = 3; - * @param value The bytes for mimeType to set. + *
+     * The media type of the file (e.g., "application/pdf").
+     * 
+ * + * string media_type = 3; + * @param value The bytes for mediaType to set. * @return This builder for chaining. */ - public Builder setMimeTypeBytes( + public Builder setMediaTypeBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - mimeType_ = value; + mediaType_ = value; bitField0_ |= 0x00000004; onChanged(); return this; @@ -870,6 +966,10 @@ public Builder setMimeTypeBytes( private java.lang.Object name_ = ""; /** + *
+     * An optional name for the file (e.g., "document.pdf").
+     * 
+ * * string name = 4; * @return The name. */ @@ -886,6 +986,10 @@ public java.lang.String getName() { } } /** + *
+     * An optional name for the file (e.g., "document.pdf").
+     * 
+ * * string name = 4; * @return The bytes for name. */ @@ -903,6 +1007,10 @@ public java.lang.String getName() { } } /** + *
+     * An optional name for the file (e.g., "document.pdf").
+     * 
+ * * string name = 4; * @param value The name to set. * @return This builder for chaining. @@ -916,6 +1024,10 @@ public Builder setName( return this; } /** + *
+     * An optional name for the file (e.g., "document.pdf").
+     * 
+ * * string name = 4; * @return This builder for chaining. */ @@ -926,6 +1038,10 @@ public Builder clearName() { return this; } /** + *
+     * An optional name for the file (e.g., "document.pdf").
+     * 
+ * * string name = 4; * @param value The bytes for name to set. * @return This builder for chaining. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java index 2fc5d42e9..cb11ce4be 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java @@ -11,16 +11,28 @@ public interface FilePartOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
+   * A URL pointing to the file's content.
+   * 
+ * * string file_with_uri = 1; * @return Whether the fileWithUri field is set. */ boolean hasFileWithUri(); /** + *
+   * A URL pointing to the file's content.
+   * 
+ * * string file_with_uri = 1; * @return The fileWithUri. */ java.lang.String getFileWithUri(); /** + *
+   * A URL pointing to the file's content.
+   * 
+ * * string file_with_uri = 1; * @return The bytes for fileWithUri. */ @@ -28,34 +40,58 @@ public interface FilePartOrBuilder extends getFileWithUriBytes(); /** + *
+   * The base64-encoded content of the file.
+   * 
+ * * bytes file_with_bytes = 2; * @return Whether the fileWithBytes field is set. */ boolean hasFileWithBytes(); /** + *
+   * The base64-encoded content of the file.
+   * 
+ * * bytes file_with_bytes = 2; * @return The fileWithBytes. */ com.google.protobuf.ByteString getFileWithBytes(); /** - * string mime_type = 3; - * @return The mimeType. + *
+   * The media type of the file (e.g., "application/pdf").
+   * 
+ * + * string media_type = 3; + * @return The mediaType. */ - java.lang.String getMimeType(); + java.lang.String getMediaType(); /** - * string mime_type = 3; - * @return The bytes for mimeType. + *
+   * The media type of the file (e.g., "application/pdf").
+   * 
+ * + * string media_type = 3; + * @return The bytes for mediaType. */ com.google.protobuf.ByteString - getMimeTypeBytes(); + getMediaTypeBytes(); /** + *
+   * An optional name for the file (e.g., "document.pdf").
+   * 
+ * * string name = 4; * @return The name. */ java.lang.String getName(); /** + *
+   * An optional name for the file (e.g., "document.pdf").
+   * 
+ * * string name = 4; * @return The bytes for name. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java similarity index 66% rename from spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java index 6c85ec5ec..1a732e9b5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java @@ -7,16 +7,16 @@ /** *
- * --8<-- [start:GetAuthenticatedExtendedCardRequest]
+ * --8<-- [start:GetExtendedAgentCardRequest]
  * 
* - * Protobuf type {@code a2a.v1.GetAgentCardRequest} + * Protobuf type {@code a2a.v1.GetExtendedAgentCardRequest} */ @com.google.protobuf.Generated -public final class GetAgentCardRequest extends +public final class GetExtendedAgentCardRequest extends com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.GetAgentCardRequest) - GetAgentCardRequestOrBuilder { + // @@protoc_insertion_point(message_implements:a2a.v1.GetExtendedAgentCardRequest) + GetExtendedAgentCardRequestOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( @@ -25,26 +25,26 @@ public final class GetAgentCardRequest extends /* minor= */ 31, /* patch= */ 1, /* suffix= */ "", - GetAgentCardRequest.class.getName()); + GetExtendedAgentCardRequest.class.getName()); } - // Use GetAgentCardRequest.newBuilder() to construct. - private GetAgentCardRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + // Use GetExtendedAgentCardRequest.newBuilder() to construct. + private GetExtendedAgentCardRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } - private GetAgentCardRequest() { + private GetExtendedAgentCardRequest() { } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.GetAgentCardRequest.class, io.a2a.grpc.GetAgentCardRequest.Builder.class); + io.a2a.grpc.GetExtendedAgentCardRequest.class, io.a2a.grpc.GetExtendedAgentCardRequest.Builder.class); } private byte memoizedIsInitialized = -1; @@ -80,10 +80,10 @@ public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } - if (!(obj instanceof io.a2a.grpc.GetAgentCardRequest)) { + if (!(obj instanceof io.a2a.grpc.GetExtendedAgentCardRequest)) { return super.equals(obj); } - io.a2a.grpc.GetAgentCardRequest other = (io.a2a.grpc.GetAgentCardRequest) obj; + io.a2a.grpc.GetExtendedAgentCardRequest other = (io.a2a.grpc.GetExtendedAgentCardRequest) obj; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; @@ -101,44 +101,44 @@ public int hashCode() { return hash; } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom(byte[] data) + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom(java.io.InputStream input) + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -146,26 +146,26 @@ public static io.a2a.grpc.GetAgentCardRequest parseFrom( .parseWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.GetAgentCardRequest parseDelimitedFrom(java.io.InputStream input) + public static io.a2a.grpc.GetExtendedAgentCardRequest parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } - public static io.a2a.grpc.GetAgentCardRequest parseDelimitedFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.GetAgentCardRequest parseFrom( + public static io.a2a.grpc.GetExtendedAgentCardRequest parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -178,7 +178,7 @@ public static io.a2a.grpc.GetAgentCardRequest parseFrom( public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(io.a2a.grpc.GetAgentCardRequest prototype) { + public static Builder newBuilder(io.a2a.grpc.GetExtendedAgentCardRequest prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override @@ -195,29 +195,29 @@ protected Builder newBuilderForType( } /** *
-   * --8<-- [start:GetAuthenticatedExtendedCardRequest]
+   * --8<-- [start:GetExtendedAgentCardRequest]
    * 
* - * Protobuf type {@code a2a.v1.GetAgentCardRequest} + * Protobuf type {@code a2a.v1.GetExtendedAgentCardRequest} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.GetAgentCardRequest) - io.a2a.grpc.GetAgentCardRequestOrBuilder { + // @@protoc_insertion_point(builder_implements:a2a.v1.GetExtendedAgentCardRequest) + io.a2a.grpc.GetExtendedAgentCardRequestOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.GetAgentCardRequest.class, io.a2a.grpc.GetAgentCardRequest.Builder.class); + io.a2a.grpc.GetExtendedAgentCardRequest.class, io.a2a.grpc.GetExtendedAgentCardRequest.Builder.class); } - // Construct using io.a2a.grpc.GetAgentCardRequest.newBuilder() + // Construct using io.a2a.grpc.GetExtendedAgentCardRequest.newBuilder() private Builder() { } @@ -236,17 +236,17 @@ public Builder clear() { @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_GetAgentCardRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor; } @java.lang.Override - public io.a2a.grpc.GetAgentCardRequest getDefaultInstanceForType() { - return io.a2a.grpc.GetAgentCardRequest.getDefaultInstance(); + public io.a2a.grpc.GetExtendedAgentCardRequest getDefaultInstanceForType() { + return io.a2a.grpc.GetExtendedAgentCardRequest.getDefaultInstance(); } @java.lang.Override - public io.a2a.grpc.GetAgentCardRequest build() { - io.a2a.grpc.GetAgentCardRequest result = buildPartial(); + public io.a2a.grpc.GetExtendedAgentCardRequest build() { + io.a2a.grpc.GetExtendedAgentCardRequest result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } @@ -254,24 +254,24 @@ public io.a2a.grpc.GetAgentCardRequest build() { } @java.lang.Override - public io.a2a.grpc.GetAgentCardRequest buildPartial() { - io.a2a.grpc.GetAgentCardRequest result = new io.a2a.grpc.GetAgentCardRequest(this); + public io.a2a.grpc.GetExtendedAgentCardRequest buildPartial() { + io.a2a.grpc.GetExtendedAgentCardRequest result = new io.a2a.grpc.GetExtendedAgentCardRequest(this); onBuilt(); return result; } @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.GetAgentCardRequest) { - return mergeFrom((io.a2a.grpc.GetAgentCardRequest)other); + if (other instanceof io.a2a.grpc.GetExtendedAgentCardRequest) { + return mergeFrom((io.a2a.grpc.GetExtendedAgentCardRequest)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(io.a2a.grpc.GetAgentCardRequest other) { - if (other == io.a2a.grpc.GetAgentCardRequest.getDefaultInstance()) return this; + public Builder mergeFrom(io.a2a.grpc.GetExtendedAgentCardRequest other) { + if (other == io.a2a.grpc.GetExtendedAgentCardRequest.getDefaultInstance()) return this; this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -314,23 +314,23 @@ public Builder mergeFrom( return this; } - // @@protoc_insertion_point(builder_scope:a2a.v1.GetAgentCardRequest) + // @@protoc_insertion_point(builder_scope:a2a.v1.GetExtendedAgentCardRequest) } - // @@protoc_insertion_point(class_scope:a2a.v1.GetAgentCardRequest) - private static final io.a2a.grpc.GetAgentCardRequest DEFAULT_INSTANCE; + // @@protoc_insertion_point(class_scope:a2a.v1.GetExtendedAgentCardRequest) + private static final io.a2a.grpc.GetExtendedAgentCardRequest DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new io.a2a.grpc.GetAgentCardRequest(); + DEFAULT_INSTANCE = new io.a2a.grpc.GetExtendedAgentCardRequest(); } - public static io.a2a.grpc.GetAgentCardRequest getDefaultInstance() { + public static io.a2a.grpc.GetExtendedAgentCardRequest getDefaultInstance() { return DEFAULT_INSTANCE; } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { @java.lang.Override - public GetAgentCardRequest parsePartialFrom( + public GetExtendedAgentCardRequest parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { @@ -349,17 +349,17 @@ public GetAgentCardRequest parsePartialFrom( } }; - public static com.google.protobuf.Parser parser() { + public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override - public com.google.protobuf.Parser getParserForType() { + public com.google.protobuf.Parser getParserForType() { return PARSER; } @java.lang.Override - public io.a2a.grpc.GetAgentCardRequest getDefaultInstanceForType() { + public io.a2a.grpc.GetExtendedAgentCardRequest getDefaultInstanceForType() { return DEFAULT_INSTANCE; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java similarity index 62% rename from spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java index 71311b5e0..63ce0f7c0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetAgentCardRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java @@ -6,7 +6,7 @@ package io.a2a.grpc; @com.google.protobuf.Generated -public interface GetAgentCardRequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.GetAgentCardRequest) +public interface GetExtendedAgentCardRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.GetExtendedAgentCardRequest) com.google.protobuf.MessageOrBuilder { } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java index 35e5bdcc7..d4d7f274c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:GetTaskRequest]
+ * Represents a request for the `tasks/get` method.
  * 
* * Protobuf type {@code a2a.v1.GetTaskRequest} @@ -48,6 +49,7 @@ private GetTaskRequest() { io.a2a.grpc.GetTaskRequest.class, io.a2a.grpc.GetTaskRequest.Builder.class); } + private int bitField0_; public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -101,10 +103,22 @@ public java.lang.String getName() { private int historyLength_ = 0; /** *
-   * The number of most recent messages from the task's history to retrieve.
+   * The maximum number of messages to include in the history.
    * 
* - * int32 history_length = 2; + * optional int32 history_length = 2; + * @return Whether the historyLength field is set. + */ + @java.lang.Override + public boolean hasHistoryLength() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * The maximum number of messages to include in the history.
+   * 
+ * + * optional int32 history_length = 2; * @return The historyLength. */ @java.lang.Override @@ -129,7 +143,7 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); } - if (historyLength_ != 0) { + if (((bitField0_ & 0x00000001) != 0)) { output.writeInt32(2, historyLength_); } getUnknownFields().writeTo(output); @@ -144,7 +158,7 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); } - if (historyLength_ != 0) { + if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(2, historyLength_); } @@ -165,8 +179,11 @@ public boolean equals(final java.lang.Object obj) { if (!getName() .equals(other.getName())) return false; - if (getHistoryLength() - != other.getHistoryLength()) return false; + if (hasHistoryLength() != other.hasHistoryLength()) return false; + if (hasHistoryLength()) { + if (getHistoryLength() + != other.getHistoryLength()) return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -180,8 +197,10 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); - hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; - hash = (53 * hash) + getHistoryLength(); + if (hasHistoryLength()) { + hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getHistoryLength(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -282,6 +301,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:GetTaskRequest]
+   * Represents a request for the `tasks/get` method.
    * 
* * Protobuf type {@code a2a.v1.GetTaskRequest} @@ -355,9 +375,12 @@ private void buildPartial0(io.a2a.grpc.GetTaskRequest result) { if (((from_bitField0_ & 0x00000001) != 0)) { result.name_ = name_; } + int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000002) != 0)) { result.historyLength_ = historyLength_; + to_bitField0_ |= 0x00000001; } + result.bitField0_ |= to_bitField0_; } @java.lang.Override @@ -377,7 +400,7 @@ public Builder mergeFrom(io.a2a.grpc.GetTaskRequest other) { bitField0_ |= 0x00000001; onChanged(); } - if (other.getHistoryLength() != 0) { + if (other.hasHistoryLength()) { setHistoryLength(other.getHistoryLength()); } this.mergeUnknownFields(other.getUnknownFields()); @@ -533,10 +556,22 @@ public Builder setNameBytes( private int historyLength_ ; /** *
-     * The number of most recent messages from the task's history to retrieve.
+     * The maximum number of messages to include in the history.
+     * 
+ * + * optional int32 history_length = 2; + * @return Whether the historyLength field is set. + */ + @java.lang.Override + public boolean hasHistoryLength() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+     * The maximum number of messages to include in the history.
      * 
* - * int32 history_length = 2; + * optional int32 history_length = 2; * @return The historyLength. */ @java.lang.Override @@ -545,10 +580,10 @@ public int getHistoryLength() { } /** *
-     * The number of most recent messages from the task's history to retrieve.
+     * The maximum number of messages to include in the history.
      * 
* - * int32 history_length = 2; + * optional int32 history_length = 2; * @param value The historyLength to set. * @return This builder for chaining. */ @@ -561,10 +596,10 @@ public Builder setHistoryLength(int value) { } /** *
-     * The number of most recent messages from the task's history to retrieve.
+     * The maximum number of messages to include in the history.
      * 
* - * int32 history_length = 2; + * optional int32 history_length = 2; * @return This builder for chaining. */ public Builder clearHistoryLength() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java index 9de92dc09..69a84a3a6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java @@ -34,10 +34,19 @@ public interface GetTaskRequestOrBuilder extends /** *
-   * The number of most recent messages from the task's history to retrieve.
+   * The maximum number of messages to include in the history.
    * 
* - * int32 history_length = 2; + * optional int32 history_length = 2; + * @return Whether the historyLength field is set. + */ + boolean hasHistoryLength(); + /** + *
+   * The maximum number of messages to include in the history.
+   * 
+ * + * optional int32 history_length = 2; * @return The historyLength. */ int getHistoryLength(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java index 9a99d69bd..df372321d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:HTTPAuthSecurityScheme]
+ * Defines a security scheme using HTTP authentication.
  * 
* * Protobuf type {@code a2a.v1.HTTPAuthSecurityScheme} @@ -55,7 +56,7 @@ private HTTPAuthSecurityScheme() { private volatile java.lang.Object description_ = ""; /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -76,7 +77,7 @@ public java.lang.String getDescription() { } /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -102,13 +103,12 @@ public java.lang.String getDescription() { private volatile java.lang.Object scheme_ = ""; /** *
-   * The name of the HTTP Authentication scheme to be used in the
-   * Authorization header as defined in RFC7235. The values used SHOULD be
-   * registered in the IANA Authentication Scheme registry.
-   * The value is case-insensitive, as defined in RFC7235.
+   * The name of the HTTP Authentication scheme to be used in the Authorization header,
+   * as defined in RFC7235 (e.g., "Bearer").
+   * This value should be registered in the IANA Authentication Scheme registry.
    * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The scheme. */ @java.lang.Override @@ -126,13 +126,12 @@ public java.lang.String getScheme() { } /** *
-   * The name of the HTTP Authentication scheme to be used in the
-   * Authorization header as defined in RFC7235. The values used SHOULD be
-   * registered in the IANA Authentication Scheme registry.
-   * The value is case-insensitive, as defined in RFC7235.
+   * The name of the HTTP Authentication scheme to be used in the Authorization header,
+   * as defined in RFC7235 (e.g., "Bearer").
+   * This value should be registered in the IANA Authentication Scheme registry.
    * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for scheme. */ @java.lang.Override @@ -155,9 +154,8 @@ public java.lang.String getScheme() { private volatile java.lang.Object bearerFormat_ = ""; /** *
-   * A hint to the client to identify how the bearer token is formatted.
-   * Bearer tokens are usually generated by an authorization server, so
-   * this information is primarily for documentation purposes.
+   * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+   * This is primarily for documentation purposes.
    * 
* * string bearer_format = 3; @@ -178,9 +176,8 @@ public java.lang.String getBearerFormat() { } /** *
-   * A hint to the client to identify how the bearer token is formatted.
-   * Bearer tokens are usually generated by an authorization server, so
-   * this information is primarily for documentation purposes.
+   * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+   * This is primarily for documentation purposes.
    * 
* * string bearer_format = 3; @@ -380,6 +377,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:HTTPAuthSecurityScheme]
+   * Defines a security scheme using HTTP authentication.
    * 
* * Protobuf type {@code a2a.v1.HTTPAuthSecurityScheme} @@ -550,7 +548,7 @@ public Builder mergeFrom( private java.lang.Object description_ = ""; /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -570,7 +568,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -591,7 +589,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -608,7 +606,7 @@ public Builder setDescription( } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -622,7 +620,7 @@ public Builder clearDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -642,13 +640,12 @@ public Builder setDescriptionBytes( private java.lang.Object scheme_ = ""; /** *
-     * The name of the HTTP Authentication scheme to be used in the
-     * Authorization header as defined in RFC7235. The values used SHOULD be
-     * registered in the IANA Authentication Scheme registry.
-     * The value is case-insensitive, as defined in RFC7235.
+     * The name of the HTTP Authentication scheme to be used in the Authorization header,
+     * as defined in RFC7235 (e.g., "Bearer").
+     * This value should be registered in the IANA Authentication Scheme registry.
      * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The scheme. */ public java.lang.String getScheme() { @@ -665,13 +662,12 @@ public java.lang.String getScheme() { } /** *
-     * The name of the HTTP Authentication scheme to be used in the
-     * Authorization header as defined in RFC7235. The values used SHOULD be
-     * registered in the IANA Authentication Scheme registry.
-     * The value is case-insensitive, as defined in RFC7235.
+     * The name of the HTTP Authentication scheme to be used in the Authorization header,
+     * as defined in RFC7235 (e.g., "Bearer").
+     * This value should be registered in the IANA Authentication Scheme registry.
      * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for scheme. */ public com.google.protobuf.ByteString @@ -689,13 +685,12 @@ public java.lang.String getScheme() { } /** *
-     * The name of the HTTP Authentication scheme to be used in the
-     * Authorization header as defined in RFC7235. The values used SHOULD be
-     * registered in the IANA Authentication Scheme registry.
-     * The value is case-insensitive, as defined in RFC7235.
+     * The name of the HTTP Authentication scheme to be used in the Authorization header,
+     * as defined in RFC7235 (e.g., "Bearer").
+     * This value should be registered in the IANA Authentication Scheme registry.
      * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The scheme to set. * @return This builder for chaining. */ @@ -709,13 +704,12 @@ public Builder setScheme( } /** *
-     * The name of the HTTP Authentication scheme to be used in the
-     * Authorization header as defined in RFC7235. The values used SHOULD be
-     * registered in the IANA Authentication Scheme registry.
-     * The value is case-insensitive, as defined in RFC7235.
+     * The name of the HTTP Authentication scheme to be used in the Authorization header,
+     * as defined in RFC7235 (e.g., "Bearer").
+     * This value should be registered in the IANA Authentication Scheme registry.
      * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearScheme() { @@ -726,13 +720,12 @@ public Builder clearScheme() { } /** *
-     * The name of the HTTP Authentication scheme to be used in the
-     * Authorization header as defined in RFC7235. The values used SHOULD be
-     * registered in the IANA Authentication Scheme registry.
-     * The value is case-insensitive, as defined in RFC7235.
+     * The name of the HTTP Authentication scheme to be used in the Authorization header,
+     * as defined in RFC7235 (e.g., "Bearer").
+     * This value should be registered in the IANA Authentication Scheme registry.
      * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for scheme to set. * @return This builder for chaining. */ @@ -749,9 +742,8 @@ public Builder setSchemeBytes( private java.lang.Object bearerFormat_ = ""; /** *
-     * A hint to the client to identify how the bearer token is formatted.
-     * Bearer tokens are usually generated by an authorization server, so
-     * this information is primarily for documentation purposes.
+     * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+     * This is primarily for documentation purposes.
      * 
* * string bearer_format = 3; @@ -771,9 +763,8 @@ public java.lang.String getBearerFormat() { } /** *
-     * A hint to the client to identify how the bearer token is formatted.
-     * Bearer tokens are usually generated by an authorization server, so
-     * this information is primarily for documentation purposes.
+     * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+     * This is primarily for documentation purposes.
      * 
* * string bearer_format = 3; @@ -794,9 +785,8 @@ public java.lang.String getBearerFormat() { } /** *
-     * A hint to the client to identify how the bearer token is formatted.
-     * Bearer tokens are usually generated by an authorization server, so
-     * this information is primarily for documentation purposes.
+     * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+     * This is primarily for documentation purposes.
      * 
* * string bearer_format = 3; @@ -813,9 +803,8 @@ public Builder setBearerFormat( } /** *
-     * A hint to the client to identify how the bearer token is formatted.
-     * Bearer tokens are usually generated by an authorization server, so
-     * this information is primarily for documentation purposes.
+     * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+     * This is primarily for documentation purposes.
      * 
* * string bearer_format = 3; @@ -829,9 +818,8 @@ public Builder clearBearerFormat() { } /** *
-     * A hint to the client to identify how the bearer token is formatted.
-     * Bearer tokens are usually generated by an authorization server, so
-     * this information is primarily for documentation purposes.
+     * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+     * This is primarily for documentation purposes.
      * 
* * string bearer_format = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java index b4e9b4628..861964e66 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java @@ -12,7 +12,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -21,7 +21,7 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends java.lang.String getDescription(); /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -32,25 +32,23 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends /** *
-   * The name of the HTTP Authentication scheme to be used in the
-   * Authorization header as defined in RFC7235. The values used SHOULD be
-   * registered in the IANA Authentication Scheme registry.
-   * The value is case-insensitive, as defined in RFC7235.
+   * The name of the HTTP Authentication scheme to be used in the Authorization header,
+   * as defined in RFC7235 (e.g., "Bearer").
+   * This value should be registered in the IANA Authentication Scheme registry.
    * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The scheme. */ java.lang.String getScheme(); /** *
-   * The name of the HTTP Authentication scheme to be used in the
-   * Authorization header as defined in RFC7235. The values used SHOULD be
-   * registered in the IANA Authentication Scheme registry.
-   * The value is case-insensitive, as defined in RFC7235.
+   * The name of the HTTP Authentication scheme to be used in the Authorization header,
+   * as defined in RFC7235 (e.g., "Bearer").
+   * This value should be registered in the IANA Authentication Scheme registry.
    * 
* - * string scheme = 2; + * string scheme = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for scheme. */ com.google.protobuf.ByteString @@ -58,9 +56,8 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends /** *
-   * A hint to the client to identify how the bearer token is formatted.
-   * Bearer tokens are usually generated by an authorization server, so
-   * this information is primarily for documentation purposes.
+   * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+   * This is primarily for documentation purposes.
    * 
* * string bearer_format = 3; @@ -69,9 +66,8 @@ public interface HTTPAuthSecuritySchemeOrBuilder extends java.lang.String getBearerFormat(); /** *
-   * A hint to the client to identify how the bearer token is formatted.
-   * Bearer tokens are usually generated by an authorization server, so
-   * this information is primarily for documentation purposes.
+   * A hint to the client to identify how the bearer token is formatted (e.g., "JWT").
+   * This is primarily for documentation purposes.
    * 
* * string bearer_format = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java index 7466cee14..f4851f6a0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:ImplicitOAuthFlow]
+ * Defines configuration details for the OAuth 2.0 Implicit flow.
  * 
* * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} @@ -66,11 +67,10 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl private volatile java.lang.Object authorizationUrl_ = ""; /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The authorizationUrl. */ @java.lang.Override @@ -88,11 +88,10 @@ public java.lang.String getAuthorizationUrl() { } /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for authorizationUrl. */ @java.lang.Override @@ -115,8 +114,7 @@ public java.lang.String getAuthorizationUrl() { private volatile java.lang.Object refreshUrl_ = ""; /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -137,8 +135,7 @@ public java.lang.String getRefreshUrl() { } /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -187,11 +184,10 @@ public int getScopesCount() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -209,11 +205,10 @@ public java.util.Map getScopes() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -221,11 +216,10 @@ public java.util.Map getScopesMap() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -240,11 +234,10 @@ java.lang.String getScopesOrDefault( } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -449,6 +442,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:ImplicitOAuthFlow]
+   * Defines configuration details for the OAuth 2.0 Implicit flow.
    * 
* * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} @@ -644,11 +638,10 @@ public Builder mergeFrom( private java.lang.Object authorizationUrl_ = ""; /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The authorizationUrl. */ public java.lang.String getAuthorizationUrl() { @@ -665,11 +658,10 @@ public java.lang.String getAuthorizationUrl() { } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for authorizationUrl. */ public com.google.protobuf.ByteString @@ -687,11 +679,10 @@ public java.lang.String getAuthorizationUrl() { } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The authorizationUrl to set. * @return This builder for chaining. */ @@ -705,11 +696,10 @@ public Builder setAuthorizationUrl( } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearAuthorizationUrl() { @@ -720,11 +710,10 @@ public Builder clearAuthorizationUrl() { } /** *
-     * The authorization URL to be used for this flow. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS
+     * The authorization URL to be used for this flow.
      * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for authorizationUrl to set. * @return This builder for chaining. */ @@ -741,8 +730,7 @@ public Builder setAuthorizationUrlBytes( private java.lang.Object refreshUrl_ = ""; /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -762,8 +750,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -784,8 +771,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -802,8 +788,7 @@ public Builder setRefreshUrl( } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -817,8 +802,7 @@ public Builder clearRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -863,11 +847,10 @@ public int getScopesCount() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -885,11 +868,10 @@ public java.util.Map getScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -897,11 +879,10 @@ public java.util.Map getScopesMap() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -916,11 +897,10 @@ java.lang.String getScopesOrDefault( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -941,11 +921,10 @@ public Builder clearScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeScopes( java.lang.String key) { @@ -965,11 +944,10 @@ public Builder removeScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putScopes( java.lang.String key, @@ -983,11 +961,10 @@ public Builder putScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java index 9ff012f6e..791be411b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java @@ -12,21 +12,19 @@ public interface ImplicitOAuthFlowOrBuilder extends /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The authorizationUrl. */ java.lang.String getAuthorizationUrl(); /** *
-   * The authorization URL to be used for this flow. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS
+   * The authorization URL to be used for this flow.
    * 
* - * string authorization_url = 1; + * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for authorizationUrl. */ com.google.protobuf.ByteString @@ -34,8 +32,7 @@ public interface ImplicitOAuthFlowOrBuilder extends /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -44,8 +41,7 @@ public interface ImplicitOAuthFlowOrBuilder extends java.lang.String getRefreshUrl(); /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -56,20 +52,18 @@ public interface ImplicitOAuthFlowOrBuilder extends /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ int getScopesCount(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ boolean containsScopes( java.lang.String key); @@ -81,21 +75,19 @@ boolean containsScopes( getScopes(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ java.util.Map getScopesMap(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -104,11 +96,10 @@ java.lang.String getScopesOrDefault( java.lang.String defaultValue); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java index a65bfb060..f5a176f55 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java @@ -102,9 +102,7 @@ public java.lang.String getParent() { private int pageSize_ = 0; /** *
-   * For AIP-158 these fields are present. Usually not used/needed.
    * The maximum number of configurations to return.
-   * If unspecified, all configs will be returned.
    * 
* * int32 page_size = 2; @@ -120,12 +118,7 @@ public int getPageSize() { private volatile java.lang.Object pageToken_ = ""; /** *
-   * A page token received from a previous
-   * ListTaskPushNotificationConfigRequest call.
-   * Provide this to retrieve the subsequent page.
-   * When paginating, all other parameters provided to
-   * `ListTaskPushNotificationConfigRequest` must match the call that provided
-   * the page token.
+   * A page token received from a previous ListTaskPushNotificationConfigRequest call.
    * 
* * string page_token = 3; @@ -146,12 +139,7 @@ public java.lang.String getPageToken() { } /** *
-   * A page token received from a previous
-   * ListTaskPushNotificationConfigRequest call.
-   * Provide this to retrieve the subsequent page.
-   * When paginating, all other parameters provided to
-   * `ListTaskPushNotificationConfigRequest` must match the call that provided
-   * the page token.
+   * A page token received from a previous ListTaskPushNotificationConfigRequest call.
    * 
* * string page_token = 3; @@ -617,9 +605,7 @@ public Builder setParentBytes( private int pageSize_ ; /** *
-     * For AIP-158 these fields are present. Usually not used/needed.
      * The maximum number of configurations to return.
-     * If unspecified, all configs will be returned.
      * 
* * int32 page_size = 2; @@ -631,9 +617,7 @@ public int getPageSize() { } /** *
-     * For AIP-158 these fields are present. Usually not used/needed.
      * The maximum number of configurations to return.
-     * If unspecified, all configs will be returned.
      * 
* * int32 page_size = 2; @@ -649,9 +633,7 @@ public Builder setPageSize(int value) { } /** *
-     * For AIP-158 these fields are present. Usually not used/needed.
      * The maximum number of configurations to return.
-     * If unspecified, all configs will be returned.
      * 
* * int32 page_size = 2; @@ -667,12 +649,7 @@ public Builder clearPageSize() { private java.lang.Object pageToken_ = ""; /** *
-     * A page token received from a previous
-     * ListTaskPushNotificationConfigRequest call.
-     * Provide this to retrieve the subsequent page.
-     * When paginating, all other parameters provided to
-     * `ListTaskPushNotificationConfigRequest` must match the call that provided
-     * the page token.
+     * A page token received from a previous ListTaskPushNotificationConfigRequest call.
      * 
* * string page_token = 3; @@ -692,12 +669,7 @@ public java.lang.String getPageToken() { } /** *
-     * A page token received from a previous
-     * ListTaskPushNotificationConfigRequest call.
-     * Provide this to retrieve the subsequent page.
-     * When paginating, all other parameters provided to
-     * `ListTaskPushNotificationConfigRequest` must match the call that provided
-     * the page token.
+     * A page token received from a previous ListTaskPushNotificationConfigRequest call.
      * 
* * string page_token = 3; @@ -718,12 +690,7 @@ public java.lang.String getPageToken() { } /** *
-     * A page token received from a previous
-     * ListTaskPushNotificationConfigRequest call.
-     * Provide this to retrieve the subsequent page.
-     * When paginating, all other parameters provided to
-     * `ListTaskPushNotificationConfigRequest` must match the call that provided
-     * the page token.
+     * A page token received from a previous ListTaskPushNotificationConfigRequest call.
      * 
* * string page_token = 3; @@ -740,12 +707,7 @@ public Builder setPageToken( } /** *
-     * A page token received from a previous
-     * ListTaskPushNotificationConfigRequest call.
-     * Provide this to retrieve the subsequent page.
-     * When paginating, all other parameters provided to
-     * `ListTaskPushNotificationConfigRequest` must match the call that provided
-     * the page token.
+     * A page token received from a previous ListTaskPushNotificationConfigRequest call.
      * 
* * string page_token = 3; @@ -759,12 +721,7 @@ public Builder clearPageToken() { } /** *
-     * A page token received from a previous
-     * ListTaskPushNotificationConfigRequest call.
-     * Provide this to retrieve the subsequent page.
-     * When paginating, all other parameters provided to
-     * `ListTaskPushNotificationConfigRequest` must match the call that provided
-     * the page token.
+     * A page token received from a previous ListTaskPushNotificationConfigRequest call.
      * 
* * string page_token = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java index a52c00912..06b26342c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java @@ -34,9 +34,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends /** *
-   * For AIP-158 these fields are present. Usually not used/needed.
    * The maximum number of configurations to return.
-   * If unspecified, all configs will be returned.
    * 
* * int32 page_size = 2; @@ -46,12 +44,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends /** *
-   * A page token received from a previous
-   * ListTaskPushNotificationConfigRequest call.
-   * Provide this to retrieve the subsequent page.
-   * When paginating, all other parameters provided to
-   * `ListTaskPushNotificationConfigRequest` must match the call that provided
-   * the page token.
+   * A page token received from a previous ListTaskPushNotificationConfigRequest call.
    * 
* * string page_token = 3; @@ -60,12 +53,7 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends java.lang.String getPageToken(); /** *
-   * A page token received from a previous
-   * ListTaskPushNotificationConfigRequest call.
-   * Provide this to retrieve the subsequent page.
-   * When paginating, all other parameters provided to
-   * `ListTaskPushNotificationConfigRequest` must match the call that provided
-   * the page token.
+   * A page token received from a previous ListTaskPushNotificationConfigRequest call.
    * 
* * string page_token = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java index 86dcfed1a..fc66a610f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java @@ -7,7 +7,9 @@ /** *
- * --8<-- [start:ListTaskPushNotificationConfigSuccessResponse]
+ * --8<-- [start:ListTaskPushNotificationConfigResponse]
+ * Represents a successful response for the `tasks/pushNotificationConfig/list`
+ * method.
  * 
* * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigResponse} @@ -330,7 +332,9 @@ protected Builder newBuilderForType( } /** *
-   * --8<-- [start:ListTaskPushNotificationConfigSuccessResponse]
+   * --8<-- [start:ListTaskPushNotificationConfigResponse]
+   * Represents a successful response for the `tasks/pushNotificationConfig/list`
+   * method.
    * 
* * Protobuf type {@code a2a.v1.ListTaskPushNotificationConfigResponse} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java index 12e05bb63..5ececf586 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:ListTasksRequest]
+ * Parameters for listing tasks with optional filtering criteria.
  * 
* * Protobuf type {@code a2a.v1.ListTasksRequest} @@ -56,8 +57,7 @@ private ListTasksRequest() { private volatile java.lang.Object contextId_ = ""; /** *
-   * Filter tasks by context ID to get tasks from a specific conversation
-   * or session.
+   * Filter tasks by context ID to get tasks from a specific conversation or session.
    * 
* * string context_id = 1; @@ -78,8 +78,7 @@ public java.lang.String getContextId() { } /** *
-   * Filter tasks by context ID to get tasks from a specific conversation
-   * or session.
+   * Filter tasks by context ID to get tasks from a specific conversation or session.
    * 
* * string context_id = 1; @@ -134,7 +133,20 @@ public java.lang.String getContextId() { * Defaults to 50 if not specified. * * - * int32 page_size = 3; + * optional int32 page_size = 3; + * @return Whether the pageSize field is set. + */ + @java.lang.Override + public boolean hasPageSize() { + return ((bitField0_ & 0x00000001) != 0); + } + /** + *
+   * Maximum number of tasks to return. Must be between 1 and 100.
+   * Defaults to 50 if not specified.
+   * 
+ * + * optional int32 page_size = 3; * @return The pageSize. */ @java.lang.Override @@ -147,8 +159,7 @@ public int getPageSize() { private volatile java.lang.Object pageToken_ = ""; /** *
-   * Token for pagination. Use the next_page_token from a previous
-   * ListTasksResponse.
+   * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
    * 
* * string page_token = 4; @@ -169,8 +180,7 @@ public java.lang.String getPageToken() { } /** *
-   * Token for pagination. Use the next_page_token from a previous
-   * ListTasksResponse.
+   * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
    * 
* * string page_token = 4; @@ -195,68 +205,67 @@ public java.lang.String getPageToken() { private int historyLength_ = 0; /** *
-   * Number of recent messages to include in each task's history.
-   * Must be non-negative. Defaults to 0 if not specified.
+   * The maximum number of messages to include in each task's history.
    * 
* - * int32 history_length = 5; - * @return The historyLength. + * optional int32 history_length = 5; + * @return Whether the historyLength field is set. */ @java.lang.Override - public int getHistoryLength() { - return historyLength_; + public boolean hasHistoryLength() { + return ((bitField0_ & 0x00000002) != 0); } - - public static final int LAST_UPDATED_TIME_FIELD_NUMBER = 6; - private com.google.protobuf.Timestamp lastUpdatedTime_; /** *
-   * Filter tasks updated after this timestamp. Only tasks with a last
-   * updated time greater than or equal to this value will be returned.
+   * The maximum number of messages to include in each task's history.
    * 
* - * .google.protobuf.Timestamp last_updated_time = 6; - * @return Whether the lastUpdatedTime field is set. + * optional int32 history_length = 5; + * @return The historyLength. */ @java.lang.Override - public boolean hasLastUpdatedTime() { - return ((bitField0_ & 0x00000001) != 0); + public int getHistoryLength() { + return historyLength_; } + + public static final int LAST_UPDATED_AFTER_FIELD_NUMBER = 6; + private long lastUpdatedAfter_ = 0L; /** *
-   * Filter tasks updated after this timestamp. Only tasks with a last
-   * updated time greater than or equal to this value will be returned.
+   * Filter tasks updated after this timestamp (milliseconds since epoch).
+   * Only tasks with a last updated time greater than or equal to this value will be returned.
    * 
* - * .google.protobuf.Timestamp last_updated_time = 6; - * @return The lastUpdatedTime. + * int64 last_updated_after = 6; + * @return The lastUpdatedAfter. */ @java.lang.Override - public com.google.protobuf.Timestamp getLastUpdatedTime() { - return lastUpdatedTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + public long getLastUpdatedAfter() { + return lastUpdatedAfter_; } + + public static final int INCLUDE_ARTIFACTS_FIELD_NUMBER = 7; + private boolean includeArtifacts_ = false; /** *
-   * Filter tasks updated after this timestamp. Only tasks with a last
-   * updated time greater than or equal to this value will be returned.
+   * Whether to include artifacts in the returned tasks.
+   * Defaults to false to reduce payload size.
    * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * optional bool include_artifacts = 7; + * @return Whether the includeArtifacts field is set. */ @java.lang.Override - public com.google.protobuf.TimestampOrBuilder getLastUpdatedTimeOrBuilder() { - return lastUpdatedTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + public boolean hasIncludeArtifacts() { + return ((bitField0_ & 0x00000004) != 0); } - - public static final int INCLUDE_ARTIFACTS_FIELD_NUMBER = 7; - private boolean includeArtifacts_ = false; /** *
    * Whether to include artifacts in the returned tasks.
    * Defaults to false to reduce payload size.
    * 
* - * bool include_artifacts = 7; + * optional bool include_artifacts = 7; * @return The includeArtifacts. */ @java.lang.Override @@ -264,6 +273,44 @@ public boolean getIncludeArtifacts() { return includeArtifacts_; } + public static final int METADATA_FIELD_NUMBER = 8; + private com.google.protobuf.Struct metadata_; + /** + *
+   * Request-specific metadata.
+   * 
+ * + * .google.protobuf.Struct metadata = 8; + * @return Whether the metadata field is set. + */ + @java.lang.Override + public boolean hasMetadata() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + *
+   * Request-specific metadata.
+   * 
+ * + * .google.protobuf.Struct metadata = 8; + * @return The metadata. + */ + @java.lang.Override + public com.google.protobuf.Struct getMetadata() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + /** + *
+   * Request-specific metadata.
+   * 
+ * + * .google.protobuf.Struct metadata = 8; + */ + @java.lang.Override + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -284,21 +331,24 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (status_ != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED.getNumber()) { output.writeEnum(2, status_); } - if (pageSize_ != 0) { + if (((bitField0_ & 0x00000001) != 0)) { output.writeInt32(3, pageSize_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { com.google.protobuf.GeneratedMessage.writeString(output, 4, pageToken_); } - if (historyLength_ != 0) { + if (((bitField0_ & 0x00000002) != 0)) { output.writeInt32(5, historyLength_); } - if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(6, getLastUpdatedTime()); + if (lastUpdatedAfter_ != 0L) { + output.writeInt64(6, lastUpdatedAfter_); } - if (includeArtifacts_ != false) { + if (((bitField0_ & 0x00000004) != 0)) { output.writeBool(7, includeArtifacts_); } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeMessage(8, getMetadata()); + } getUnknownFields().writeTo(output); } @@ -315,25 +365,29 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeEnumSize(2, status_); } - if (pageSize_ != 0) { + if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(3, pageSize_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(4, pageToken_); } - if (historyLength_ != 0) { + if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(5, historyLength_); } - if (((bitField0_ & 0x00000001) != 0)) { + if (lastUpdatedAfter_ != 0L) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, getLastUpdatedTime()); + .computeInt64Size(6, lastUpdatedAfter_); } - if (includeArtifacts_ != false) { + if (((bitField0_ & 0x00000004) != 0)) { size += com.google.protobuf.CodedOutputStream .computeBoolSize(7, includeArtifacts_); } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(8, getMetadata()); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -352,19 +406,30 @@ public boolean equals(final java.lang.Object obj) { if (!getContextId() .equals(other.getContextId())) return false; if (status_ != other.status_) return false; - if (getPageSize() - != other.getPageSize()) return false; + if (hasPageSize() != other.hasPageSize()) return false; + if (hasPageSize()) { + if (getPageSize() + != other.getPageSize()) return false; + } if (!getPageToken() .equals(other.getPageToken())) return false; - if (getHistoryLength() - != other.getHistoryLength()) return false; - if (hasLastUpdatedTime() != other.hasLastUpdatedTime()) return false; - if (hasLastUpdatedTime()) { - if (!getLastUpdatedTime() - .equals(other.getLastUpdatedTime())) return false; - } - if (getIncludeArtifacts() - != other.getIncludeArtifacts()) return false; + if (hasHistoryLength() != other.hasHistoryLength()) return false; + if (hasHistoryLength()) { + if (getHistoryLength() + != other.getHistoryLength()) return false; + } + if (getLastUpdatedAfter() + != other.getLastUpdatedAfter()) return false; + if (hasIncludeArtifacts() != other.hasIncludeArtifacts()) return false; + if (hasIncludeArtifacts()) { + if (getIncludeArtifacts() + != other.getIncludeArtifacts()) return false; + } + if (hasMetadata() != other.hasMetadata()) return false; + if (hasMetadata()) { + if (!getMetadata() + .equals(other.getMetadata())) return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -380,19 +445,28 @@ public int hashCode() { hash = (53 * hash) + getContextId().hashCode(); hash = (37 * hash) + STATUS_FIELD_NUMBER; hash = (53 * hash) + status_; - hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; - hash = (53 * hash) + getPageSize(); + if (hasPageSize()) { + hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; + hash = (53 * hash) + getPageSize(); + } hash = (37 * hash) + PAGE_TOKEN_FIELD_NUMBER; hash = (53 * hash) + getPageToken().hashCode(); - hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; - hash = (53 * hash) + getHistoryLength(); - if (hasLastUpdatedTime()) { - hash = (37 * hash) + LAST_UPDATED_TIME_FIELD_NUMBER; - hash = (53 * hash) + getLastUpdatedTime().hashCode(); - } - hash = (37 * hash) + INCLUDE_ARTIFACTS_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getIncludeArtifacts()); + if (hasHistoryLength()) { + hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getHistoryLength(); + } + hash = (37 * hash) + LAST_UPDATED_AFTER_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong( + getLastUpdatedAfter()); + if (hasIncludeArtifacts()) { + hash = (37 * hash) + INCLUDE_ARTIFACTS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getIncludeArtifacts()); + } + if (hasMetadata()) { + hash = (37 * hash) + METADATA_FIELD_NUMBER; + hash = (53 * hash) + getMetadata().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -493,6 +567,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:ListTasksRequest]
+   * Parameters for listing tasks with optional filtering criteria.
    * 
* * Protobuf type {@code a2a.v1.ListTasksRequest} @@ -527,7 +602,7 @@ private Builder( private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage .alwaysUseFieldBuilders) { - internalGetLastUpdatedTimeFieldBuilder(); + internalGetMetadataFieldBuilder(); } } @java.lang.Override @@ -539,12 +614,13 @@ public Builder clear() { pageSize_ = 0; pageToken_ = ""; historyLength_ = 0; - lastUpdatedTime_ = null; - if (lastUpdatedTimeBuilder_ != null) { - lastUpdatedTimeBuilder_.dispose(); - lastUpdatedTimeBuilder_ = null; - } + lastUpdatedAfter_ = 0L; includeArtifacts_ = false; + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; + } return this; } @@ -584,24 +660,30 @@ private void buildPartial0(io.a2a.grpc.ListTasksRequest result) { if (((from_bitField0_ & 0x00000002) != 0)) { result.status_ = status_; } + int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000004) != 0)) { result.pageSize_ = pageSize_; + to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000008) != 0)) { result.pageToken_ = pageToken_; } if (((from_bitField0_ & 0x00000010) != 0)) { result.historyLength_ = historyLength_; + to_bitField0_ |= 0x00000002; } - int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000020) != 0)) { - result.lastUpdatedTime_ = lastUpdatedTimeBuilder_ == null - ? lastUpdatedTime_ - : lastUpdatedTimeBuilder_.build(); - to_bitField0_ |= 0x00000001; + result.lastUpdatedAfter_ = lastUpdatedAfter_; } if (((from_bitField0_ & 0x00000040) != 0)) { result.includeArtifacts_ = includeArtifacts_; + to_bitField0_ |= 0x00000004; + } + if (((from_bitField0_ & 0x00000080) != 0)) { + result.metadata_ = metadataBuilder_ == null + ? metadata_ + : metadataBuilder_.build(); + to_bitField0_ |= 0x00000008; } result.bitField0_ |= to_bitField0_; } @@ -626,7 +708,7 @@ public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) { if (other.status_ != 0) { setStatusValue(other.getStatusValue()); } - if (other.getPageSize() != 0) { + if (other.hasPageSize()) { setPageSize(other.getPageSize()); } if (!other.getPageToken().isEmpty()) { @@ -634,15 +716,18 @@ public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) { bitField0_ |= 0x00000008; onChanged(); } - if (other.getHistoryLength() != 0) { + if (other.hasHistoryLength()) { setHistoryLength(other.getHistoryLength()); } - if (other.hasLastUpdatedTime()) { - mergeLastUpdatedTime(other.getLastUpdatedTime()); + if (other.getLastUpdatedAfter() != 0L) { + setLastUpdatedAfter(other.getLastUpdatedAfter()); } - if (other.getIncludeArtifacts() != false) { + if (other.hasIncludeArtifacts()) { setIncludeArtifacts(other.getIncludeArtifacts()); } + if (other.hasMetadata()) { + mergeMetadata(other.getMetadata()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -694,18 +779,23 @@ public Builder mergeFrom( bitField0_ |= 0x00000010; break; } // case 40 - case 50: { - input.readMessage( - internalGetLastUpdatedTimeFieldBuilder().getBuilder(), - extensionRegistry); + case 48: { + lastUpdatedAfter_ = input.readInt64(); bitField0_ |= 0x00000020; break; - } // case 50 + } // case 48 case 56: { includeArtifacts_ = input.readBool(); bitField0_ |= 0x00000040; break; } // case 56 + case 66: { + input.readMessage( + internalGetMetadataFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000080; + break; + } // case 66 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -726,8 +816,7 @@ public Builder mergeFrom( private java.lang.Object contextId_ = ""; /** *
-     * Filter tasks by context ID to get tasks from a specific conversation
-     * or session.
+     * Filter tasks by context ID to get tasks from a specific conversation or session.
      * 
* * string context_id = 1; @@ -747,8 +836,7 @@ public java.lang.String getContextId() { } /** *
-     * Filter tasks by context ID to get tasks from a specific conversation
-     * or session.
+     * Filter tasks by context ID to get tasks from a specific conversation or session.
      * 
* * string context_id = 1; @@ -769,8 +857,7 @@ public java.lang.String getContextId() { } /** *
-     * Filter tasks by context ID to get tasks from a specific conversation
-     * or session.
+     * Filter tasks by context ID to get tasks from a specific conversation or session.
      * 
* * string context_id = 1; @@ -787,8 +874,7 @@ public Builder setContextId( } /** *
-     * Filter tasks by context ID to get tasks from a specific conversation
-     * or session.
+     * Filter tasks by context ID to get tasks from a specific conversation or session.
      * 
* * string context_id = 1; @@ -802,8 +888,7 @@ public Builder clearContextId() { } /** *
-     * Filter tasks by context ID to get tasks from a specific conversation
-     * or session.
+     * Filter tasks by context ID to get tasks from a specific conversation or session.
      * 
* * string context_id = 1; @@ -898,7 +983,20 @@ public Builder clearStatus() { * Defaults to 50 if not specified. * * - * int32 page_size = 3; + * optional int32 page_size = 3; + * @return Whether the pageSize field is set. + */ + @java.lang.Override + public boolean hasPageSize() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * Maximum number of tasks to return. Must be between 1 and 100.
+     * Defaults to 50 if not specified.
+     * 
+ * + * optional int32 page_size = 3; * @return The pageSize. */ @java.lang.Override @@ -911,7 +1009,7 @@ public int getPageSize() { * Defaults to 50 if not specified. * * - * int32 page_size = 3; + * optional int32 page_size = 3; * @param value The pageSize to set. * @return This builder for chaining. */ @@ -928,7 +1026,7 @@ public Builder setPageSize(int value) { * Defaults to 50 if not specified. * * - * int32 page_size = 3; + * optional int32 page_size = 3; * @return This builder for chaining. */ public Builder clearPageSize() { @@ -941,8 +1039,7 @@ public Builder clearPageSize() { private java.lang.Object pageToken_ = ""; /** *
-     * Token for pagination. Use the next_page_token from a previous
-     * ListTasksResponse.
+     * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
      * 
* * string page_token = 4; @@ -962,8 +1059,7 @@ public java.lang.String getPageToken() { } /** *
-     * Token for pagination. Use the next_page_token from a previous
-     * ListTasksResponse.
+     * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
      * 
* * string page_token = 4; @@ -984,8 +1080,7 @@ public java.lang.String getPageToken() { } /** *
-     * Token for pagination. Use the next_page_token from a previous
-     * ListTasksResponse.
+     * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
      * 
* * string page_token = 4; @@ -1002,8 +1097,7 @@ public Builder setPageToken( } /** *
-     * Token for pagination. Use the next_page_token from a previous
-     * ListTasksResponse.
+     * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
      * 
* * string page_token = 4; @@ -1017,8 +1111,7 @@ public Builder clearPageToken() { } /** *
-     * Token for pagination. Use the next_page_token from a previous
-     * ListTasksResponse.
+     * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
      * 
* * string page_token = 4; @@ -1038,11 +1131,22 @@ public Builder setPageTokenBytes( private int historyLength_ ; /** *
-     * Number of recent messages to include in each task's history.
-     * Must be non-negative. Defaults to 0 if not specified.
+     * The maximum number of messages to include in each task's history.
      * 
* - * int32 history_length = 5; + * optional int32 history_length = 5; + * @return Whether the historyLength field is set. + */ + @java.lang.Override + public boolean hasHistoryLength() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + *
+     * The maximum number of messages to include in each task's history.
+     * 
+ * + * optional int32 history_length = 5; * @return The historyLength. */ @java.lang.Override @@ -1051,11 +1155,10 @@ public int getHistoryLength() { } /** *
-     * Number of recent messages to include in each task's history.
-     * Must be non-negative. Defaults to 0 if not specified.
+     * The maximum number of messages to include in each task's history.
      * 
* - * int32 history_length = 5; + * optional int32 history_length = 5; * @param value The historyLength to set. * @return This builder for chaining. */ @@ -1068,11 +1171,10 @@ public Builder setHistoryLength(int value) { } /** *
-     * Number of recent messages to include in each task's history.
-     * Must be non-negative. Defaults to 0 if not specified.
+     * The maximum number of messages to include in each task's history.
      * 
* - * int32 history_length = 5; + * optional int32 history_length = 5; * @return This builder for chaining. */ public Builder clearHistoryLength() { @@ -1082,217 +1184,268 @@ public Builder clearHistoryLength() { return this; } - private com.google.protobuf.Timestamp lastUpdatedTime_; - private com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> lastUpdatedTimeBuilder_; + private long lastUpdatedAfter_ ; /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Filter tasks updated after this timestamp (milliseconds since epoch).
+     * Only tasks with a last updated time greater than or equal to this value will be returned.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; - * @return Whether the lastUpdatedTime field is set. + * int64 last_updated_after = 6; + * @return The lastUpdatedAfter. */ - public boolean hasLastUpdatedTime() { - return ((bitField0_ & 0x00000020) != 0); + @java.lang.Override + public long getLastUpdatedAfter() { + return lastUpdatedAfter_; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Filter tasks updated after this timestamp (milliseconds since epoch).
+     * Only tasks with a last updated time greater than or equal to this value will be returned.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; - * @return The lastUpdatedTime. + * int64 last_updated_after = 6; + * @param value The lastUpdatedAfter to set. + * @return This builder for chaining. */ - public com.google.protobuf.Timestamp getLastUpdatedTime() { - if (lastUpdatedTimeBuilder_ == null) { - return lastUpdatedTime_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; - } else { - return lastUpdatedTimeBuilder_.getMessage(); - } + public Builder setLastUpdatedAfter(long value) { + + lastUpdatedAfter_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Filter tasks updated after this timestamp (milliseconds since epoch).
+     * Only tasks with a last updated time greater than or equal to this value will be returned.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * int64 last_updated_after = 6; + * @return This builder for chaining. */ - public Builder setLastUpdatedTime(com.google.protobuf.Timestamp value) { - if (lastUpdatedTimeBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - lastUpdatedTime_ = value; - } else { - lastUpdatedTimeBuilder_.setMessage(value); - } - bitField0_ |= 0x00000020; + public Builder clearLastUpdatedAfter() { + bitField0_ = (bitField0_ & ~0x00000020); + lastUpdatedAfter_ = 0L; onChanged(); return this; } + + private boolean includeArtifacts_ ; /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * optional bool include_artifacts = 7; + * @return Whether the includeArtifacts field is set. */ - public Builder setLastUpdatedTime( - com.google.protobuf.Timestamp.Builder builderForValue) { - if (lastUpdatedTimeBuilder_ == null) { - lastUpdatedTime_ = builderForValue.build(); - } else { - lastUpdatedTimeBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000020; + @java.lang.Override + public boolean hasIncludeArtifacts() { + return ((bitField0_ & 0x00000040) != 0); + } + /** + *
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
+     * 
+ * + * optional bool include_artifacts = 7; + * @return The includeArtifacts. + */ + @java.lang.Override + public boolean getIncludeArtifacts() { + return includeArtifacts_; + } + /** + *
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
+     * 
+ * + * optional bool include_artifacts = 7; + * @param value The includeArtifacts to set. + * @return This builder for chaining. + */ + public Builder setIncludeArtifacts(boolean value) { + + includeArtifacts_ = value; + bitField0_ |= 0x00000040; onChanged(); return this; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Whether to include artifacts in the returned tasks.
+     * Defaults to false to reduce payload size.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * optional bool include_artifacts = 7; + * @return This builder for chaining. */ - public Builder mergeLastUpdatedTime(com.google.protobuf.Timestamp value) { - if (lastUpdatedTimeBuilder_ == null) { - if (((bitField0_ & 0x00000020) != 0) && - lastUpdatedTime_ != null && - lastUpdatedTime_ != com.google.protobuf.Timestamp.getDefaultInstance()) { - getLastUpdatedTimeBuilder().mergeFrom(value); - } else { - lastUpdatedTime_ = value; - } + public Builder clearIncludeArtifacts() { + bitField0_ = (bitField0_ & ~0x00000040); + includeArtifacts_ = false; + onChanged(); + return this; + } + + private com.google.protobuf.Struct metadata_; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; + /** + *
+     * Request-specific metadata.
+     * 
+ * + * .google.protobuf.Struct metadata = 8; + * @return Whether the metadata field is set. + */ + public boolean hasMetadata() { + return ((bitField0_ & 0x00000080) != 0); + } + /** + *
+     * Request-specific metadata.
+     * 
+ * + * .google.protobuf.Struct metadata = 8; + * @return The metadata. + */ + public com.google.protobuf.Struct getMetadata() { + if (metadataBuilder_ == null) { + return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; } else { - lastUpdatedTimeBuilder_.mergeFrom(value); + return metadataBuilder_.getMessage(); } - if (lastUpdatedTime_ != null) { - bitField0_ |= 0x00000020; - onChanged(); - } - return this; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Request-specific metadata.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * .google.protobuf.Struct metadata = 8; */ - public Builder clearLastUpdatedTime() { - bitField0_ = (bitField0_ & ~0x00000020); - lastUpdatedTime_ = null; - if (lastUpdatedTimeBuilder_ != null) { - lastUpdatedTimeBuilder_.dispose(); - lastUpdatedTimeBuilder_ = null; + public Builder setMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + metadata_ = value; + } else { + metadataBuilder_.setMessage(value); } + bitField0_ |= 0x00000080; onChanged(); return this; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Request-specific metadata.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * .google.protobuf.Struct metadata = 8; */ - public com.google.protobuf.Timestamp.Builder getLastUpdatedTimeBuilder() { - bitField0_ |= 0x00000020; + public Builder setMetadata( + com.google.protobuf.Struct.Builder builderForValue) { + if (metadataBuilder_ == null) { + metadata_ = builderForValue.build(); + } else { + metadataBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000080; onChanged(); - return internalGetLastUpdatedTimeFieldBuilder().getBuilder(); + return this; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Request-specific metadata.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * .google.protobuf.Struct metadata = 8; */ - public com.google.protobuf.TimestampOrBuilder getLastUpdatedTimeOrBuilder() { - if (lastUpdatedTimeBuilder_ != null) { - return lastUpdatedTimeBuilder_.getMessageOrBuilder(); + public Builder mergeMetadata(com.google.protobuf.Struct value) { + if (metadataBuilder_ == null) { + if (((bitField0_ & 0x00000080) != 0) && + metadata_ != null && + metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { + getMetadataBuilder().mergeFrom(value); + } else { + metadata_ = value; + } } else { - return lastUpdatedTime_ == null ? - com.google.protobuf.Timestamp.getDefaultInstance() : lastUpdatedTime_; + metadataBuilder_.mergeFrom(value); } + if (metadata_ != null) { + bitField0_ |= 0x00000080; + onChanged(); + } + return this; } /** *
-     * Filter tasks updated after this timestamp. Only tasks with a last
-     * updated time greater than or equal to this value will be returned.
+     * Request-specific metadata.
      * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * .google.protobuf.Struct metadata = 8; */ - private com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> - internalGetLastUpdatedTimeFieldBuilder() { - if (lastUpdatedTimeBuilder_ == null) { - lastUpdatedTimeBuilder_ = new com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>( - getLastUpdatedTime(), - getParentForChildren(), - isClean()); - lastUpdatedTime_ = null; + public Builder clearMetadata() { + bitField0_ = (bitField0_ & ~0x00000080); + metadata_ = null; + if (metadataBuilder_ != null) { + metadataBuilder_.dispose(); + metadataBuilder_ = null; } - return lastUpdatedTimeBuilder_; + onChanged(); + return this; } - - private boolean includeArtifacts_ ; /** *
-     * Whether to include artifacts in the returned tasks.
-     * Defaults to false to reduce payload size.
+     * Request-specific metadata.
      * 
* - * bool include_artifacts = 7; - * @return The includeArtifacts. + * .google.protobuf.Struct metadata = 8; */ - @java.lang.Override - public boolean getIncludeArtifacts() { - return includeArtifacts_; + public com.google.protobuf.Struct.Builder getMetadataBuilder() { + bitField0_ |= 0x00000080; + onChanged(); + return internalGetMetadataFieldBuilder().getBuilder(); } /** *
-     * Whether to include artifacts in the returned tasks.
-     * Defaults to false to reduce payload size.
+     * Request-specific metadata.
      * 
* - * bool include_artifacts = 7; - * @param value The includeArtifacts to set. - * @return This builder for chaining. + * .google.protobuf.Struct metadata = 8; */ - public Builder setIncludeArtifacts(boolean value) { - - includeArtifacts_ = value; - bitField0_ |= 0x00000040; - onChanged(); - return this; + public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { + if (metadataBuilder_ != null) { + return metadataBuilder_.getMessageOrBuilder(); + } else { + return metadata_ == null ? + com.google.protobuf.Struct.getDefaultInstance() : metadata_; + } } /** *
-     * Whether to include artifacts in the returned tasks.
-     * Defaults to false to reduce payload size.
+     * Request-specific metadata.
      * 
* - * bool include_artifacts = 7; - * @return This builder for chaining. + * .google.protobuf.Struct metadata = 8; */ - public Builder clearIncludeArtifacts() { - bitField0_ = (bitField0_ & ~0x00000040); - includeArtifacts_ = false; - onChanged(); - return this; + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> + internalGetMetadataFieldBuilder() { + if (metadataBuilder_ == null) { + metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( + getMetadata(), + getParentForChildren(), + isClean()); + metadata_ = null; + } + return metadataBuilder_; } // @@protoc_insertion_point(builder_scope:a2a.v1.ListTasksRequest) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java index 70a7061ff..28d9fa5bf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java @@ -12,8 +12,7 @@ public interface ListTasksRequestOrBuilder extends /** *
-   * Filter tasks by context ID to get tasks from a specific conversation
-   * or session.
+   * Filter tasks by context ID to get tasks from a specific conversation or session.
    * 
* * string context_id = 1; @@ -22,8 +21,7 @@ public interface ListTasksRequestOrBuilder extends java.lang.String getContextId(); /** *
-   * Filter tasks by context ID to get tasks from a specific conversation
-   * or session.
+   * Filter tasks by context ID to get tasks from a specific conversation or session.
    * 
* * string context_id = 1; @@ -57,15 +55,24 @@ public interface ListTasksRequestOrBuilder extends * Defaults to 50 if not specified. * * - * int32 page_size = 3; + * optional int32 page_size = 3; + * @return Whether the pageSize field is set. + */ + boolean hasPageSize(); + /** + *
+   * Maximum number of tasks to return. Must be between 1 and 100.
+   * Defaults to 50 if not specified.
+   * 
+ * + * optional int32 page_size = 3; * @return The pageSize. */ int getPageSize(); /** *
-   * Token for pagination. Use the next_page_token from a previous
-   * ListTasksResponse.
+   * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
    * 
* * string page_token = 4; @@ -74,8 +81,7 @@ public interface ListTasksRequestOrBuilder extends java.lang.String getPageToken(); /** *
-   * Token for pagination. Use the next_page_token from a previous
-   * ListTasksResponse.
+   * Token for pagination. Use the next_page_token from a previous ListTasksResponse.
    * 
* * string page_token = 4; @@ -86,53 +92,79 @@ public interface ListTasksRequestOrBuilder extends /** *
-   * Number of recent messages to include in each task's history.
-   * Must be non-negative. Defaults to 0 if not specified.
+   * The maximum number of messages to include in each task's history.
    * 
* - * int32 history_length = 5; - * @return The historyLength. + * optional int32 history_length = 5; + * @return Whether the historyLength field is set. */ - int getHistoryLength(); - + boolean hasHistoryLength(); /** *
-   * Filter tasks updated after this timestamp. Only tasks with a last
-   * updated time greater than or equal to this value will be returned.
+   * The maximum number of messages to include in each task's history.
    * 
* - * .google.protobuf.Timestamp last_updated_time = 6; - * @return Whether the lastUpdatedTime field is set. + * optional int32 history_length = 5; + * @return The historyLength. */ - boolean hasLastUpdatedTime(); + int getHistoryLength(); + /** *
-   * Filter tasks updated after this timestamp. Only tasks with a last
-   * updated time greater than or equal to this value will be returned.
+   * Filter tasks updated after this timestamp (milliseconds since epoch).
+   * Only tasks with a last updated time greater than or equal to this value will be returned.
    * 
* - * .google.protobuf.Timestamp last_updated_time = 6; - * @return The lastUpdatedTime. + * int64 last_updated_after = 6; + * @return The lastUpdatedAfter. */ - com.google.protobuf.Timestamp getLastUpdatedTime(); + long getLastUpdatedAfter(); + /** *
-   * Filter tasks updated after this timestamp. Only tasks with a last
-   * updated time greater than or equal to this value will be returned.
+   * Whether to include artifacts in the returned tasks.
+   * Defaults to false to reduce payload size.
    * 
* - * .google.protobuf.Timestamp last_updated_time = 6; + * optional bool include_artifacts = 7; + * @return Whether the includeArtifacts field is set. */ - com.google.protobuf.TimestampOrBuilder getLastUpdatedTimeOrBuilder(); - + boolean hasIncludeArtifacts(); /** *
    * Whether to include artifacts in the returned tasks.
    * Defaults to false to reduce payload size.
    * 
* - * bool include_artifacts = 7; + * optional bool include_artifacts = 7; * @return The includeArtifacts. */ boolean getIncludeArtifacts(); + + /** + *
+   * Request-specific metadata.
+   * 
+ * + * .google.protobuf.Struct metadata = 8; + * @return Whether the metadata field is set. + */ + boolean hasMetadata(); + /** + *
+   * Request-specific metadata.
+   * 
+ * + * .google.protobuf.Struct metadata = 8; + * @return The metadata. + */ + com.google.protobuf.Struct getMetadata(); + /** + *
+   * Request-specific metadata.
+   * 
+ * + * .google.protobuf.Struct metadata = 8; + */ + com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java index 20ed57e27..3703e3aeb 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:ListTasksResponse]
+ * Result object for tasks/list method containing an array of tasks and pagination information.
  * 
* * Protobuf type {@code a2a.v1.ListTasksResponse} @@ -57,7 +58,7 @@ private ListTasksResponse() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List getTasksList() { @@ -68,7 +69,7 @@ public java.util.List getTasksList() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List @@ -80,7 +81,7 @@ public java.util.List getTasksList() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public int getTasksCount() { @@ -91,7 +92,7 @@ public int getTasksCount() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.Task getTasks(int index) { @@ -102,7 +103,7 @@ public io.a2a.grpc.Task getTasks(int index) { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( @@ -115,11 +116,10 @@ public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( private volatile java.lang.Object nextPageToken_ = ""; /** *
-   * Token for retrieving the next page of results.
-   * Empty string if no more results.
+   * Token for retrieving the next page. Empty string if no more results.
    * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The nextPageToken. */ @java.lang.Override @@ -137,11 +137,10 @@ public java.lang.String getNextPageToken() { } /** *
-   * Token for retrieving the next page of results.
-   * Empty string if no more results.
+   * Token for retrieving the next page. Empty string if no more results.
    * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for nextPageToken. */ @java.lang.Override @@ -159,14 +158,29 @@ public java.lang.String getNextPageToken() { } } - public static final int TOTAL_SIZE_FIELD_NUMBER = 3; + public static final int PAGE_SIZE_FIELD_NUMBER = 3; + private int pageSize_ = 0; + /** + *
+   * The size of page requested.
+   * 
+ * + * int32 page_size = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The pageSize. + */ + @java.lang.Override + public int getPageSize() { + return pageSize_; + } + + public static final int TOTAL_SIZE_FIELD_NUMBER = 4; private int totalSize_ = 0; /** *
    * Total number of tasks available (before pagination).
    * 
* - * int32 total_size = 3; + * int32 total_size = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The totalSize. */ @java.lang.Override @@ -194,8 +208,11 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(nextPageToken_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, nextPageToken_); } + if (pageSize_ != 0) { + output.writeInt32(3, pageSize_); + } if (totalSize_ != 0) { - output.writeInt32(3, totalSize_); + output.writeInt32(4, totalSize_); } getUnknownFields().writeTo(output); } @@ -213,9 +230,13 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(nextPageToken_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, nextPageToken_); } + if (pageSize_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, pageSize_); + } if (totalSize_ != 0) { size += com.google.protobuf.CodedOutputStream - .computeInt32Size(3, totalSize_); + .computeInt32Size(4, totalSize_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -236,6 +257,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getTasksList())) return false; if (!getNextPageToken() .equals(other.getNextPageToken())) return false; + if (getPageSize() + != other.getPageSize()) return false; if (getTotalSize() != other.getTotalSize()) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -255,6 +278,8 @@ public int hashCode() { } hash = (37 * hash) + NEXT_PAGE_TOKEN_FIELD_NUMBER; hash = (53 * hash) + getNextPageToken().hashCode(); + hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; + hash = (53 * hash) + getPageSize(); hash = (37 * hash) + TOTAL_SIZE_FIELD_NUMBER; hash = (53 * hash) + getTotalSize(); hash = (29 * hash) + getUnknownFields().hashCode(); @@ -357,6 +382,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:ListTasksResponse]
+   * Result object for tasks/list method containing an array of tasks and pagination information.
    * 
* * Protobuf type {@code a2a.v1.ListTasksResponse} @@ -400,6 +426,7 @@ public Builder clear() { } bitField0_ = (bitField0_ & ~0x00000001); nextPageToken_ = ""; + pageSize_ = 0; totalSize_ = 0; return this; } @@ -451,6 +478,9 @@ private void buildPartial0(io.a2a.grpc.ListTasksResponse result) { result.nextPageToken_ = nextPageToken_; } if (((from_bitField0_ & 0x00000004) != 0)) { + result.pageSize_ = pageSize_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { result.totalSize_ = totalSize_; } } @@ -498,6 +528,9 @@ public Builder mergeFrom(io.a2a.grpc.ListTasksResponse other) { bitField0_ |= 0x00000002; onChanged(); } + if (other.getPageSize() != 0) { + setPageSize(other.getPageSize()); + } if (other.getTotalSize() != 0) { setTotalSize(other.getTotalSize()); } @@ -546,10 +579,15 @@ public Builder mergeFrom( break; } // case 18 case 24: { - totalSize_ = input.readInt32(); + pageSize_ = input.readInt32(); bitField0_ |= 0x00000004; break; } // case 24 + case 32: { + totalSize_ = input.readInt32(); + bitField0_ |= 0x00000008; + break; + } // case 32 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -584,7 +622,7 @@ private void ensureTasksIsMutable() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getTasksList() { if (tasksBuilder_ == null) { @@ -598,7 +636,7 @@ public java.util.List getTasksList() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public int getTasksCount() { if (tasksBuilder_ == null) { @@ -612,7 +650,7 @@ public int getTasksCount() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Task getTasks(int index) { if (tasksBuilder_ == null) { @@ -626,7 +664,7 @@ public io.a2a.grpc.Task getTasks(int index) { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setTasks( int index, io.a2a.grpc.Task value) { @@ -647,7 +685,7 @@ public Builder setTasks( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setTasks( int index, io.a2a.grpc.Task.Builder builderForValue) { @@ -665,7 +703,7 @@ public Builder setTasks( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addTasks(io.a2a.grpc.Task value) { if (tasksBuilder_ == null) { @@ -685,7 +723,7 @@ public Builder addTasks(io.a2a.grpc.Task value) { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addTasks( int index, io.a2a.grpc.Task value) { @@ -706,7 +744,7 @@ public Builder addTasks( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addTasks( io.a2a.grpc.Task.Builder builderForValue) { @@ -724,7 +762,7 @@ public Builder addTasks( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addTasks( int index, io.a2a.grpc.Task.Builder builderForValue) { @@ -742,7 +780,7 @@ public Builder addTasks( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addAllTasks( java.lang.Iterable values) { @@ -761,7 +799,7 @@ public Builder addAllTasks( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearTasks() { if (tasksBuilder_ == null) { @@ -778,7 +816,7 @@ public Builder clearTasks() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeTasks(int index) { if (tasksBuilder_ == null) { @@ -795,7 +833,7 @@ public Builder removeTasks(int index) { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Task.Builder getTasksBuilder( int index) { @@ -806,7 +844,7 @@ public io.a2a.grpc.Task.Builder getTasksBuilder( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( int index) { @@ -820,7 +858,7 @@ public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getTasksOrBuilderList() { @@ -835,7 +873,7 @@ public io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Task.Builder addTasksBuilder() { return internalGetTasksFieldBuilder().addBuilder( @@ -846,7 +884,7 @@ public io.a2a.grpc.Task.Builder addTasksBuilder() { * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Task.Builder addTasksBuilder( int index) { @@ -858,7 +896,7 @@ public io.a2a.grpc.Task.Builder addTasksBuilder( * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getTasksBuilderList() { @@ -882,11 +920,10 @@ public io.a2a.grpc.Task.Builder addTasksBuilder( private java.lang.Object nextPageToken_ = ""; /** *
-     * Token for retrieving the next page of results.
-     * Empty string if no more results.
+     * Token for retrieving the next page. Empty string if no more results.
      * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The nextPageToken. */ public java.lang.String getNextPageToken() { @@ -903,11 +940,10 @@ public java.lang.String getNextPageToken() { } /** *
-     * Token for retrieving the next page of results.
-     * Empty string if no more results.
+     * Token for retrieving the next page. Empty string if no more results.
      * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for nextPageToken. */ public com.google.protobuf.ByteString @@ -925,11 +961,10 @@ public java.lang.String getNextPageToken() { } /** *
-     * Token for retrieving the next page of results.
-     * Empty string if no more results.
+     * Token for retrieving the next page. Empty string if no more results.
      * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The nextPageToken to set. * @return This builder for chaining. */ @@ -943,11 +978,10 @@ public Builder setNextPageToken( } /** *
-     * Token for retrieving the next page of results.
-     * Empty string if no more results.
+     * Token for retrieving the next page. Empty string if no more results.
      * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearNextPageToken() { @@ -958,11 +992,10 @@ public Builder clearNextPageToken() { } /** *
-     * Token for retrieving the next page of results.
-     * Empty string if no more results.
+     * Token for retrieving the next page. Empty string if no more results.
      * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for nextPageToken to set. * @return This builder for chaining. */ @@ -976,13 +1009,57 @@ public Builder setNextPageTokenBytes( return this; } + private int pageSize_ ; + /** + *
+     * The size of page requested.
+     * 
+ * + * int32 page_size = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The pageSize. + */ + @java.lang.Override + public int getPageSize() { + return pageSize_; + } + /** + *
+     * The size of page requested.
+     * 
+ * + * int32 page_size = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The pageSize to set. + * @return This builder for chaining. + */ + public Builder setPageSize(int value) { + + pageSize_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
+     * The size of page requested.
+     * 
+ * + * int32 page_size = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearPageSize() { + bitField0_ = (bitField0_ & ~0x00000004); + pageSize_ = 0; + onChanged(); + return this; + } + private int totalSize_ ; /** *
      * Total number of tasks available (before pagination).
      * 
* - * int32 total_size = 3; + * int32 total_size = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The totalSize. */ @java.lang.Override @@ -994,14 +1071,14 @@ public int getTotalSize() { * Total number of tasks available (before pagination). * * - * int32 total_size = 3; + * int32 total_size = 4 [(.google.api.field_behavior) = REQUIRED]; * @param value The totalSize to set. * @return This builder for chaining. */ public Builder setTotalSize(int value) { totalSize_ = value; - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); return this; } @@ -1010,11 +1087,11 @@ public Builder setTotalSize(int value) { * Total number of tasks available (before pagination). * * - * int32 total_size = 3; + * int32 total_size = 4 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTotalSize() { - bitField0_ = (bitField0_ & ~0x00000004); + bitField0_ = (bitField0_ & ~0x00000008); totalSize_ = 0; onChanged(); return this; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java index 08b8eb22a..11c3173be 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java @@ -15,7 +15,7 @@ public interface ListTasksResponseOrBuilder extends * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getTasksList(); @@ -24,7 +24,7 @@ public interface ListTasksResponseOrBuilder extends * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.Task getTasks(int index); /** @@ -32,7 +32,7 @@ public interface ListTasksResponseOrBuilder extends * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ int getTasksCount(); /** @@ -40,7 +40,7 @@ public interface ListTasksResponseOrBuilder extends * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getTasksOrBuilderList(); @@ -49,39 +49,47 @@ public interface ListTasksResponseOrBuilder extends * Array of tasks matching the specified criteria. * * - * repeated .a2a.v1.Task tasks = 1; + * repeated .a2a.v1.Task tasks = 1 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.TaskOrBuilder getTasksOrBuilder( int index); /** *
-   * Token for retrieving the next page of results.
-   * Empty string if no more results.
+   * Token for retrieving the next page. Empty string if no more results.
    * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The nextPageToken. */ java.lang.String getNextPageToken(); /** *
-   * Token for retrieving the next page of results.
-   * Empty string if no more results.
+   * Token for retrieving the next page. Empty string if no more results.
    * 
* - * string next_page_token = 2; + * string next_page_token = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for nextPageToken. */ com.google.protobuf.ByteString getNextPageTokenBytes(); + /** + *
+   * The size of page requested.
+   * 
+ * + * int32 page_size = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The pageSize. + */ + int getPageSize(); + /** *
    * Total number of tasks available (before pagination).
    * 
* - * int32 total_size = 3; + * int32 total_size = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The totalSize. */ int getTotalSize(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Message.java b/spec-grpc/src/main/java/io/a2a/grpc/Message.java index 0e32af8e6..66c42d2bf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Message.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Message.java @@ -69,11 +69,11 @@ private Message() { private volatile java.lang.Object messageId_ = ""; /** *
-   * The unique identifier (e.g. UUID)of the message. This is required and
+   * The unique identifier (e.g. UUID) of the message. This is required and
    * created by the message creator.
    * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The messageId. */ @java.lang.Override @@ -91,11 +91,11 @@ public java.lang.String getMessageId() { } /** *
-   * The unique identifier (e.g. UUID)of the message. This is required and
+   * The unique identifier (e.g. UUID) of the message. This is required and
    * created by the message creator.
    * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for messageId. */ @java.lang.Override @@ -215,10 +215,10 @@ public java.lang.String getTaskId() { private int role_ = 0; /** *
-   * A role for the message.
+   * Identifies the sender of the message.
    * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The enum numeric value on the wire for role. */ @java.lang.Override public int getRoleValue() { @@ -226,10 +226,10 @@ public java.lang.String getTaskId() { } /** *
-   * A role for the message.
+   * Identifies the sender of the message.
    * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The role. */ @java.lang.Override public io.a2a.grpc.Role getRole() { @@ -246,7 +246,7 @@ public java.lang.String getTaskId() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List getPartsList() { @@ -258,7 +258,7 @@ public java.util.List getPartsList() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List @@ -271,7 +271,7 @@ public java.util.List getPartsList() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public int getPartsCount() { @@ -283,7 +283,7 @@ public int getPartsCount() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.Part getParts(int index) { @@ -295,7 +295,7 @@ public io.a2a.grpc.Part getParts(int index) { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( @@ -1024,11 +1024,11 @@ public Builder mergeFrom( private java.lang.Object messageId_ = ""; /** *
-     * The unique identifier (e.g. UUID)of the message. This is required and
+     * The unique identifier (e.g. UUID) of the message. This is required and
      * created by the message creator.
      * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The messageId. */ public java.lang.String getMessageId() { @@ -1045,11 +1045,11 @@ public java.lang.String getMessageId() { } /** *
-     * The unique identifier (e.g. UUID)of the message. This is required and
+     * The unique identifier (e.g. UUID) of the message. This is required and
      * created by the message creator.
      * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for messageId. */ public com.google.protobuf.ByteString @@ -1067,11 +1067,11 @@ public java.lang.String getMessageId() { } /** *
-     * The unique identifier (e.g. UUID)of the message. This is required and
+     * The unique identifier (e.g. UUID) of the message. This is required and
      * created by the message creator.
      * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The messageId to set. * @return This builder for chaining. */ @@ -1085,11 +1085,11 @@ public Builder setMessageId( } /** *
-     * The unique identifier (e.g. UUID)of the message. This is required and
+     * The unique identifier (e.g. UUID) of the message. This is required and
      * created by the message creator.
      * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearMessageId() { @@ -1100,11 +1100,11 @@ public Builder clearMessageId() { } /** *
-     * The unique identifier (e.g. UUID)of the message. This is required and
+     * The unique identifier (e.g. UUID) of the message. This is required and
      * created by the message creator.
      * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for messageId to set. * @return This builder for chaining. */ @@ -1315,10 +1315,10 @@ public Builder setTaskIdBytes( private int role_ = 0; /** *
-     * A role for the message.
+     * Identifies the sender of the message.
      * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The enum numeric value on the wire for role. */ @java.lang.Override public int getRoleValue() { @@ -1326,10 +1326,10 @@ public Builder setTaskIdBytes( } /** *
-     * A role for the message.
+     * Identifies the sender of the message.
      * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @param value The enum numeric value on the wire for role to set. * @return This builder for chaining. */ @@ -1341,10 +1341,10 @@ public Builder setRoleValue(int value) { } /** *
-     * A role for the message.
+     * Identifies the sender of the message.
      * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The role. */ @java.lang.Override @@ -1354,10 +1354,10 @@ public io.a2a.grpc.Role getRole() { } /** *
-     * A role for the message.
+     * Identifies the sender of the message.
      * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @param value The role to set. * @return This builder for chaining. */ @@ -1370,10 +1370,10 @@ public Builder setRole(io.a2a.grpc.Role value) { } /** *
-     * A role for the message.
+     * Identifies the sender of the message.
      * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearRole() { @@ -1401,7 +1401,7 @@ private void ensurePartsIsMutable() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getPartsList() { if (partsBuilder_ == null) { @@ -1416,7 +1416,7 @@ public java.util.List getPartsList() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public int getPartsCount() { if (partsBuilder_ == null) { @@ -1431,7 +1431,7 @@ public int getPartsCount() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part getParts(int index) { if (partsBuilder_ == null) { @@ -1446,7 +1446,7 @@ public io.a2a.grpc.Part getParts(int index) { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setParts( int index, io.a2a.grpc.Part value) { @@ -1468,7 +1468,7 @@ public Builder setParts( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setParts( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1487,7 +1487,7 @@ public Builder setParts( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts(io.a2a.grpc.Part value) { if (partsBuilder_ == null) { @@ -1508,7 +1508,7 @@ public Builder addParts(io.a2a.grpc.Part value) { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts( int index, io.a2a.grpc.Part value) { @@ -1530,7 +1530,7 @@ public Builder addParts( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts( io.a2a.grpc.Part.Builder builderForValue) { @@ -1549,7 +1549,7 @@ public Builder addParts( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addParts( int index, io.a2a.grpc.Part.Builder builderForValue) { @@ -1568,7 +1568,7 @@ public Builder addParts( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder addAllParts( java.lang.Iterable values) { @@ -1588,7 +1588,7 @@ public Builder addAllParts( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearParts() { if (partsBuilder_ == null) { @@ -1606,7 +1606,7 @@ public Builder clearParts() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeParts(int index) { if (partsBuilder_ == null) { @@ -1624,7 +1624,7 @@ public Builder removeParts(int index) { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part.Builder getPartsBuilder( int index) { @@ -1636,7 +1636,7 @@ public io.a2a.grpc.Part.Builder getPartsBuilder( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index) { @@ -1651,7 +1651,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getPartsOrBuilderList() { @@ -1667,7 +1667,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part.Builder addPartsBuilder() { return internalGetPartsFieldBuilder().addBuilder( @@ -1679,7 +1679,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder() { * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Part.Builder addPartsBuilder( int index) { @@ -1692,7 +1692,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder( * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ public java.util.List getPartsBuilderList() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java index 7e8605297..546c633ae 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java @@ -12,21 +12,21 @@ public interface MessageOrBuilder extends /** *
-   * The unique identifier (e.g. UUID)of the message. This is required and
+   * The unique identifier (e.g. UUID) of the message. This is required and
    * created by the message creator.
    * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The messageId. */ java.lang.String getMessageId(); /** *
-   * The unique identifier (e.g. UUID)of the message. This is required and
+   * The unique identifier (e.g. UUID) of the message. This is required and
    * created by the message creator.
    * 
* - * string message_id = 1; + * string message_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for messageId. */ com.google.protobuf.ByteString @@ -78,19 +78,19 @@ public interface MessageOrBuilder extends /** *
-   * A role for the message.
+   * Identifies the sender of the message.
    * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The enum numeric value on the wire for role. */ int getRoleValue(); /** *
-   * A role for the message.
+   * Identifies the sender of the message.
    * 
* - * .a2a.v1.Role role = 4; + * .a2a.v1.Role role = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The role. */ io.a2a.grpc.Role getRole(); @@ -101,7 +101,7 @@ public interface MessageOrBuilder extends * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getPartsList(); @@ -111,7 +111,7 @@ public interface MessageOrBuilder extends * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.Part getParts(int index); /** @@ -120,7 +120,7 @@ public interface MessageOrBuilder extends * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ int getPartsCount(); /** @@ -129,7 +129,7 @@ public interface MessageOrBuilder extends * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getPartsOrBuilderList(); @@ -139,7 +139,7 @@ public interface MessageOrBuilder extends * Parts is the container of the message content. * * - * repeated .a2a.v1.Part parts = 5; + * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.PartOrBuilder getPartsOrBuilder( int index); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java index 9e4cf1b9c..859fe5c48 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:MutualTLSSecurityScheme]
+ * Defines a security scheme using mTLS authentication.
  * 
* * Protobuf type {@code a2a.v1.MutualTlsSecurityScheme} @@ -53,7 +54,7 @@ private MutualTlsSecurityScheme() { private volatile java.lang.Object description_ = ""; /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -74,7 +75,7 @@ public java.lang.String getDescription() { } /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -254,6 +255,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:MutualTLSSecurityScheme]
+   * Defines a security scheme using mTLS authentication.
    * 
* * Protobuf type {@code a2a.v1.MutualTlsSecurityScheme} @@ -396,7 +398,7 @@ public Builder mergeFrom( private java.lang.Object description_ = ""; /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -416,7 +418,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -437,7 +439,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -454,7 +456,7 @@ public Builder setDescription( } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -468,7 +470,7 @@ public Builder clearDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java index a18e1311a..95f6bb130 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java @@ -12,7 +12,7 @@ public interface MutualTlsSecuritySchemeOrBuilder extends /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -21,7 +21,7 @@ public interface MutualTlsSecuritySchemeOrBuilder extends java.lang.String getDescription(); /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java index 2dde8973b..10408245b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:OAuth2SecurityScheme]
+ * Defines a security scheme using OAuth 2.0.
  * 
* * Protobuf type {@code a2a.v1.OAuth2SecurityScheme} @@ -55,7 +56,7 @@ private OAuth2SecurityScheme() { private volatile java.lang.Object description_ = ""; /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -76,7 +77,7 @@ public java.lang.String getDescription() { } /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -101,10 +102,10 @@ public java.lang.String getDescription() { private io.a2a.grpc.OAuthFlows flows_; /** *
-   * An object containing configuration information for the flow types supported
+   * An object containing configuration information for the supported OAuth 2.0 flows.
    * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the flows field is set. */ @java.lang.Override @@ -113,10 +114,10 @@ public boolean hasFlows() { } /** *
-   * An object containing configuration information for the flow types supported
+   * An object containing configuration information for the supported OAuth 2.0 flows.
    * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The flows. */ @java.lang.Override @@ -125,10 +126,10 @@ public io.a2a.grpc.OAuthFlows getFlows() { } /** *
-   * An object containing configuration information for the flow types supported
+   * An object containing configuration information for the supported OAuth 2.0 flows.
    * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { @@ -141,7 +142,7 @@ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { /** *
    * URL to the oauth2 authorization server metadata
-   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
    * 
* * string oauth2_metadata_url = 3; @@ -163,7 +164,7 @@ public java.lang.String getOauth2MetadataUrl() { /** *
    * URL to the oauth2 authorization server metadata
-   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
    * 
* * string oauth2_metadata_url = 3; @@ -369,6 +370,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:OAuth2SecurityScheme]
+   * Defines a security scheme using OAuth 2.0.
    * 
* * Protobuf type {@code a2a.v1.OAuth2SecurityScheme} @@ -554,7 +556,7 @@ public Builder mergeFrom( private java.lang.Object description_ = ""; /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -574,7 +576,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -595,7 +597,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -612,7 +614,7 @@ public Builder setDescription( } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -626,7 +628,7 @@ public Builder clearDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -648,10 +650,10 @@ public Builder setDescriptionBytes( io.a2a.grpc.OAuthFlows, io.a2a.grpc.OAuthFlows.Builder, io.a2a.grpc.OAuthFlowsOrBuilder> flowsBuilder_; /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the flows field is set. */ public boolean hasFlows() { @@ -659,10 +661,10 @@ public boolean hasFlows() { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The flows. */ public io.a2a.grpc.OAuthFlows getFlows() { @@ -674,10 +676,10 @@ public io.a2a.grpc.OAuthFlows getFlows() { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setFlows(io.a2a.grpc.OAuthFlows value) { if (flowsBuilder_ == null) { @@ -694,10 +696,10 @@ public Builder setFlows(io.a2a.grpc.OAuthFlows value) { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setFlows( io.a2a.grpc.OAuthFlows.Builder builderForValue) { @@ -712,10 +714,10 @@ public Builder setFlows( } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeFlows(io.a2a.grpc.OAuthFlows value) { if (flowsBuilder_ == null) { @@ -737,10 +739,10 @@ public Builder mergeFlows(io.a2a.grpc.OAuthFlows value) { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearFlows() { bitField0_ = (bitField0_ & ~0x00000002); @@ -754,10 +756,10 @@ public Builder clearFlows() { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.OAuthFlows.Builder getFlowsBuilder() { bitField0_ |= 0x00000002; @@ -766,10 +768,10 @@ public io.a2a.grpc.OAuthFlows.Builder getFlowsBuilder() { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { if (flowsBuilder_ != null) { @@ -781,10 +783,10 @@ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { } /** *
-     * An object containing configuration information for the flow types supported
+     * An object containing configuration information for the supported OAuth 2.0 flows.
      * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OAuthFlows, io.a2a.grpc.OAuthFlows.Builder, io.a2a.grpc.OAuthFlowsOrBuilder> @@ -804,7 +806,7 @@ public io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder() { /** *
      * URL to the oauth2 authorization server metadata
-     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
      * 
* * string oauth2_metadata_url = 3; @@ -825,7 +827,7 @@ public java.lang.String getOauth2MetadataUrl() { /** *
      * URL to the oauth2 authorization server metadata
-     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
      * 
* * string oauth2_metadata_url = 3; @@ -847,7 +849,7 @@ public java.lang.String getOauth2MetadataUrl() { /** *
      * URL to the oauth2 authorization server metadata
-     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
      * 
* * string oauth2_metadata_url = 3; @@ -865,7 +867,7 @@ public Builder setOauth2MetadataUrl( /** *
      * URL to the oauth2 authorization server metadata
-     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
      * 
* * string oauth2_metadata_url = 3; @@ -880,7 +882,7 @@ public Builder clearOauth2MetadataUrl() { /** *
      * URL to the oauth2 authorization server metadata
-     * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+     * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
      * 
* * string oauth2_metadata_url = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java index 84c35ce0c..fb2af79b7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java @@ -12,7 +12,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -21,7 +21,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends java.lang.String getDescription(); /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -32,35 +32,35 @@ public interface OAuth2SecuritySchemeOrBuilder extends /** *
-   * An object containing configuration information for the flow types supported
+   * An object containing configuration information for the supported OAuth 2.0 flows.
    * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the flows field is set. */ boolean hasFlows(); /** *
-   * An object containing configuration information for the flow types supported
+   * An object containing configuration information for the supported OAuth 2.0 flows.
    * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The flows. */ io.a2a.grpc.OAuthFlows getFlows(); /** *
-   * An object containing configuration information for the flow types supported
+   * An object containing configuration information for the supported OAuth 2.0 flows.
    * 
* - * .a2a.v1.OAuthFlows flows = 2; + * .a2a.v1.OAuthFlows flows = 2 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.OAuthFlowsOrBuilder getFlowsOrBuilder(); /** *
    * URL to the oauth2 authorization server metadata
-   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
    * 
* * string oauth2_metadata_url = 3; @@ -70,7 +70,7 @@ public interface OAuth2SecuritySchemeOrBuilder extends /** *
    * URL to the oauth2 authorization server metadata
-   * [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
+   * RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required.
    * 
* * string oauth2_metadata_url = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java index c2c2de63f..d38a64ae6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:OAuthFlows]
+ * Defines the configuration for the supported OAuth 2.0 flows.
  * 
* * Protobuf type {@code a2a.v1.OAuthFlows} @@ -95,6 +96,10 @@ public int getNumber() { public static final int AUTHORIZATION_CODE_FIELD_NUMBER = 1; /** + *
+   * Configuration for the OAuth Authorization Code flow.
+   * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return Whether the authorizationCode field is set. */ @@ -103,6 +108,10 @@ public boolean hasAuthorizationCode() { return flowCase_ == 1; } /** + *
+   * Configuration for the OAuth Authorization Code flow.
+   * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return The authorizationCode. */ @@ -114,6 +123,10 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode() { return io.a2a.grpc.AuthorizationCodeOAuthFlow.getDefaultInstance(); } /** + *
+   * Configuration for the OAuth Authorization Code flow.
+   * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ @java.lang.Override @@ -126,6 +139,10 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBui public static final int CLIENT_CREDENTIALS_FIELD_NUMBER = 2; /** + *
+   * Configuration for the OAuth Client Credentials flow.
+   * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return Whether the clientCredentials field is set. */ @@ -134,6 +151,10 @@ public boolean hasClientCredentials() { return flowCase_ == 2; } /** + *
+   * Configuration for the OAuth Client Credentials flow.
+   * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return The clientCredentials. */ @@ -145,6 +166,10 @@ public io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials() { return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); } /** + *
+   * Configuration for the OAuth Client Credentials flow.
+   * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ @java.lang.Override @@ -157,6 +182,10 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui public static final int IMPLICIT_FIELD_NUMBER = 3; /** + *
+   * Configuration for the OAuth Implicit flow.
+   * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return Whether the implicit field is set. */ @@ -165,6 +194,10 @@ public boolean hasImplicit() { return flowCase_ == 3; } /** + *
+   * Configuration for the OAuth Implicit flow.
+   * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return The implicit. */ @@ -176,6 +209,10 @@ public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); } /** + *
+   * Configuration for the OAuth Implicit flow.
+   * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ @java.lang.Override @@ -188,6 +225,10 @@ public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { public static final int PASSWORD_FIELD_NUMBER = 4; /** + *
+   * Configuration for the OAuth Resource Owner Password flow.
+   * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; * @return Whether the password field is set. */ @@ -196,6 +237,10 @@ public boolean hasPassword() { return flowCase_ == 4; } /** + *
+   * Configuration for the OAuth Resource Owner Password flow.
+   * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; * @return The password. */ @@ -207,6 +252,10 @@ public io.a2a.grpc.PasswordOAuthFlow getPassword() { return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); } /** + *
+   * Configuration for the OAuth Resource Owner Password flow.
+   * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ @java.lang.Override @@ -435,6 +484,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:OAuthFlows]
+   * Defines the configuration for the supported OAuth 2.0 flows.
    * 
* * Protobuf type {@code a2a.v1.OAuthFlows} @@ -663,6 +713,10 @@ public Builder clearFlow() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.AuthorizationCodeOAuthFlow, io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder, io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder> authorizationCodeBuilder_; /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return Whether the authorizationCode field is set. */ @@ -671,6 +725,10 @@ public boolean hasAuthorizationCode() { return flowCase_ == 1; } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return The authorizationCode. */ @@ -689,6 +747,10 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode() { } } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder setAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value) { @@ -705,6 +767,10 @@ public Builder setAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value return this; } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder setAuthorizationCode( @@ -719,6 +785,10 @@ public Builder setAuthorizationCode( return this; } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder mergeAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow value) { @@ -742,6 +812,10 @@ public Builder mergeAuthorizationCode(io.a2a.grpc.AuthorizationCodeOAuthFlow val return this; } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public Builder clearAuthorizationCode() { @@ -761,12 +835,20 @@ public Builder clearAuthorizationCode() { return this; } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ public io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder getAuthorizationCodeBuilder() { return internalGetAuthorizationCodeFieldBuilder().getBuilder(); } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ @java.lang.Override @@ -781,6 +863,10 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBui } } /** + *
+     * Configuration for the OAuth Authorization Code flow.
+     * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ private com.google.protobuf.SingleFieldBuilder< @@ -805,6 +891,10 @@ public io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBui private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.ClientCredentialsOAuthFlow, io.a2a.grpc.ClientCredentialsOAuthFlow.Builder, io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder> clientCredentialsBuilder_; /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return Whether the clientCredentials field is set. */ @@ -813,6 +903,10 @@ public boolean hasClientCredentials() { return flowCase_ == 2; } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return The clientCredentials. */ @@ -831,6 +925,10 @@ public io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials() { } } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder setClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value) { @@ -847,6 +945,10 @@ public Builder setClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value return this; } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder setClientCredentials( @@ -861,6 +963,10 @@ public Builder setClientCredentials( return this; } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder mergeClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow value) { @@ -884,6 +990,10 @@ public Builder mergeClientCredentials(io.a2a.grpc.ClientCredentialsOAuthFlow val return this; } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public Builder clearClientCredentials() { @@ -903,12 +1013,20 @@ public Builder clearClientCredentials() { return this; } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ public io.a2a.grpc.ClientCredentialsOAuthFlow.Builder getClientCredentialsBuilder() { return internalGetClientCredentialsFieldBuilder().getBuilder(); } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ @java.lang.Override @@ -923,6 +1041,10 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui } } /** + *
+     * Configuration for the OAuth Client Credentials flow.
+     * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ private com.google.protobuf.SingleFieldBuilder< @@ -947,6 +1069,10 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> implicitBuilder_; /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return Whether the implicit field is set. */ @@ -955,6 +1081,10 @@ public boolean hasImplicit() { return flowCase_ == 3; } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return The implicit. */ @@ -973,6 +1103,10 @@ public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { } } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { @@ -989,6 +1123,10 @@ public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { return this; } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder setImplicit( @@ -1003,6 +1141,10 @@ public Builder setImplicit( return this; } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { @@ -1026,6 +1168,10 @@ public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { return this; } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public Builder clearImplicit() { @@ -1045,12 +1191,20 @@ public Builder clearImplicit() { return this; } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ public io.a2a.grpc.ImplicitOAuthFlow.Builder getImplicitBuilder() { return internalGetImplicitFieldBuilder().getBuilder(); } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ @java.lang.Override @@ -1065,6 +1219,10 @@ public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { } } /** + *
+     * Configuration for the OAuth Implicit flow.
+     * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ private com.google.protobuf.SingleFieldBuilder< @@ -1089,6 +1247,10 @@ public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> passwordBuilder_; /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; * @return Whether the password field is set. */ @@ -1097,6 +1259,10 @@ public boolean hasPassword() { return flowCase_ == 4; } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; * @return The password. */ @@ -1115,6 +1281,10 @@ public io.a2a.grpc.PasswordOAuthFlow getPassword() { } } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) { @@ -1131,6 +1301,10 @@ public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) { return this; } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder setPassword( @@ -1145,6 +1319,10 @@ public Builder setPassword( return this; } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) { @@ -1168,6 +1346,10 @@ public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) { return this; } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ public Builder clearPassword() { @@ -1187,12 +1369,20 @@ public Builder clearPassword() { return this; } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ public io.a2a.grpc.PasswordOAuthFlow.Builder getPasswordBuilder() { return internalGetPasswordFieldBuilder().getBuilder(); } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ @java.lang.Override @@ -1207,6 +1397,10 @@ public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { } } /** + *
+     * Configuration for the OAuth Resource Owner Password flow.
+     * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java index 804d3a826..d7a94d7f2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java @@ -11,61 +11,109 @@ public interface OAuthFlowsOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
+   * Configuration for the OAuth Authorization Code flow.
+   * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return Whether the authorizationCode field is set. */ boolean hasAuthorizationCode(); /** + *
+   * Configuration for the OAuth Authorization Code flow.
+   * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; * @return The authorizationCode. */ io.a2a.grpc.AuthorizationCodeOAuthFlow getAuthorizationCode(); /** + *
+   * Configuration for the OAuth Authorization Code flow.
+   * 
+ * * .a2a.v1.AuthorizationCodeOAuthFlow authorization_code = 1; */ io.a2a.grpc.AuthorizationCodeOAuthFlowOrBuilder getAuthorizationCodeOrBuilder(); /** + *
+   * Configuration for the OAuth Client Credentials flow.
+   * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return Whether the clientCredentials field is set. */ boolean hasClientCredentials(); /** + *
+   * Configuration for the OAuth Client Credentials flow.
+   * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; * @return The clientCredentials. */ io.a2a.grpc.ClientCredentialsOAuthFlow getClientCredentials(); /** + *
+   * Configuration for the OAuth Client Credentials flow.
+   * 
+ * * .a2a.v1.ClientCredentialsOAuthFlow client_credentials = 2; */ io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder(); /** + *
+   * Configuration for the OAuth Implicit flow.
+   * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return Whether the implicit field is set. */ boolean hasImplicit(); /** + *
+   * Configuration for the OAuth Implicit flow.
+   * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; * @return The implicit. */ io.a2a.grpc.ImplicitOAuthFlow getImplicit(); /** + *
+   * Configuration for the OAuth Implicit flow.
+   * 
+ * * .a2a.v1.ImplicitOAuthFlow implicit = 3; */ io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder(); /** + *
+   * Configuration for the OAuth Resource Owner Password flow.
+   * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; * @return Whether the password field is set. */ boolean hasPassword(); /** + *
+   * Configuration for the OAuth Resource Owner Password flow.
+   * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; * @return The password. */ io.a2a.grpc.PasswordOAuthFlow getPassword(); /** + *
+   * Configuration for the OAuth Resource Owner Password flow.
+   * 
+ * * .a2a.v1.PasswordOAuthFlow password = 4; */ io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java index 4f2db6d61..3e10d7cb0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:OpenIdConnectSecurityScheme]
+ * Defines a security scheme using OpenID Connect.
  * 
* * Protobuf type {@code a2a.v1.OpenIdConnectSecurityScheme} @@ -54,7 +55,7 @@ private OpenIdConnectSecurityScheme() { private volatile java.lang.Object description_ = ""; /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -75,7 +76,7 @@ public java.lang.String getDescription() { } /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -101,11 +102,11 @@ public java.lang.String getDescription() { private volatile java.lang.Object openIdConnectUrl_ = ""; /** *
-   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-   * metadata.
+   * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+   * See: https://openid.net/specs/openid-connect-discovery-1_0.html
    * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The openIdConnectUrl. */ @java.lang.Override @@ -123,11 +124,11 @@ public java.lang.String getOpenIdConnectUrl() { } /** *
-   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-   * metadata.
+   * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+   * See: https://openid.net/specs/openid-connect-discovery-1_0.html
    * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for openIdConnectUrl. */ @java.lang.Override @@ -314,6 +315,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:OpenIdConnectSecurityScheme]
+   * Defines a security scheme using OpenID Connect.
    * 
* * Protobuf type {@code a2a.v1.OpenIdConnectSecurityScheme} @@ -470,7 +472,7 @@ public Builder mergeFrom( private java.lang.Object description_ = ""; /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -490,7 +492,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -511,7 +513,7 @@ public java.lang.String getDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -528,7 +530,7 @@ public Builder setDescription( } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -542,7 +544,7 @@ public Builder clearDescription() { } /** *
-     * Description of this security scheme.
+     * An optional description for the security scheme.
      * 
* * string description = 1; @@ -562,11 +564,11 @@ public Builder setDescriptionBytes( private java.lang.Object openIdConnectUrl_ = ""; /** *
-     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-     * metadata.
+     * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+     * See: https://openid.net/specs/openid-connect-discovery-1_0.html
      * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The openIdConnectUrl. */ public java.lang.String getOpenIdConnectUrl() { @@ -583,11 +585,11 @@ public java.lang.String getOpenIdConnectUrl() { } /** *
-     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-     * metadata.
+     * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+     * See: https://openid.net/specs/openid-connect-discovery-1_0.html
      * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for openIdConnectUrl. */ public com.google.protobuf.ByteString @@ -605,11 +607,11 @@ public java.lang.String getOpenIdConnectUrl() { } /** *
-     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-     * metadata.
+     * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+     * See: https://openid.net/specs/openid-connect-discovery-1_0.html
      * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The openIdConnectUrl to set. * @return This builder for chaining. */ @@ -623,11 +625,11 @@ public Builder setOpenIdConnectUrl( } /** *
-     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-     * metadata.
+     * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+     * See: https://openid.net/specs/openid-connect-discovery-1_0.html
      * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearOpenIdConnectUrl() { @@ -638,11 +640,11 @@ public Builder clearOpenIdConnectUrl() { } /** *
-     * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-     * metadata.
+     * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+     * See: https://openid.net/specs/openid-connect-discovery-1_0.html
      * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for openIdConnectUrl to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java index 20e04a27c..f9d6de482 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java @@ -12,7 +12,7 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -21,7 +21,7 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends java.lang.String getDescription(); /** *
-   * Description of this security scheme.
+   * An optional description for the security scheme.
    * 
* * string description = 1; @@ -32,21 +32,21 @@ public interface OpenIdConnectSecuritySchemeOrBuilder extends /** *
-   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-   * metadata.
+   * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+   * See: https://openid.net/specs/openid-connect-discovery-1_0.html
    * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The openIdConnectUrl. */ java.lang.String getOpenIdConnectUrl(); /** *
-   * Well-known URL to discover the [[OpenID-Connect-Discovery]] provider
-   * metadata.
+   * The OpenID Connect Discovery URL for the OIDC provider's metadata.
+   * See: https://openid.net/specs/openid-connect-discovery-1_0.html
    * 
* - * string open_id_connect_url = 2; + * string open_id_connect_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for openIdConnectUrl. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Part.java b/spec-grpc/src/main/java/io/a2a/grpc/Part.java index 3eee12040..72244a3ef 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Part.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Part.java @@ -97,6 +97,10 @@ public int getNumber() { public static final int TEXT_FIELD_NUMBER = 1; /** + *
+   * The string content of the text part.
+   * 
+ * * string text = 1; * @return Whether the text field is set. */ @@ -104,6 +108,10 @@ public boolean hasText() { return partCase_ == 1; } /** + *
+   * The string content of the text part.
+   * 
+ * * string text = 1; * @return The text. */ @@ -125,6 +133,10 @@ public java.lang.String getText() { } } /** + *
+   * The string content of the text part.
+   * 
+ * * string text = 1; * @return The bytes for text. */ @@ -149,6 +161,10 @@ public java.lang.String getText() { public static final int FILE_FIELD_NUMBER = 2; /** + *
+   * The file content, represented as either a URI or as base64-encoded bytes.
+   * 
+ * * .a2a.v1.FilePart file = 2; * @return Whether the file field is set. */ @@ -157,6 +173,10 @@ public boolean hasFile() { return partCase_ == 2; } /** + *
+   * The file content, represented as either a URI or as base64-encoded bytes.
+   * 
+ * * .a2a.v1.FilePart file = 2; * @return The file. */ @@ -168,6 +188,10 @@ public io.a2a.grpc.FilePart getFile() { return io.a2a.grpc.FilePart.getDefaultInstance(); } /** + *
+   * The file content, represented as either a URI or as base64-encoded bytes.
+   * 
+ * * .a2a.v1.FilePart file = 2; */ @java.lang.Override @@ -180,6 +204,10 @@ public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { public static final int DATA_FIELD_NUMBER = 3; /** + *
+   * The structured data content.
+   * 
+ * * .a2a.v1.DataPart data = 3; * @return Whether the data field is set. */ @@ -188,6 +216,10 @@ public boolean hasData() { return partCase_ == 3; } /** + *
+   * The structured data content.
+   * 
+ * * .a2a.v1.DataPart data = 3; * @return The data. */ @@ -199,6 +231,10 @@ public io.a2a.grpc.DataPart getData() { return io.a2a.grpc.DataPart.getDefaultInstance(); } /** + *
+   * The structured data content.
+   * 
+ * * .a2a.v1.DataPart data = 3; */ @java.lang.Override @@ -699,6 +735,10 @@ public Builder clearPart() { private int bitField0_; /** + *
+     * The string content of the text part.
+     * 
+ * * string text = 1; * @return Whether the text field is set. */ @@ -707,6 +747,10 @@ public boolean hasText() { return partCase_ == 1; } /** + *
+     * The string content of the text part.
+     * 
+ * * string text = 1; * @return The text. */ @@ -729,6 +773,10 @@ public java.lang.String getText() { } } /** + *
+     * The string content of the text part.
+     * 
+ * * string text = 1; * @return The bytes for text. */ @@ -752,6 +800,10 @@ public java.lang.String getText() { } } /** + *
+     * The string content of the text part.
+     * 
+ * * string text = 1; * @param value The text to set. * @return This builder for chaining. @@ -765,6 +817,10 @@ public Builder setText( return this; } /** + *
+     * The string content of the text part.
+     * 
+ * * string text = 1; * @return This builder for chaining. */ @@ -777,6 +833,10 @@ public Builder clearText() { return this; } /** + *
+     * The string content of the text part.
+     * 
+ * * string text = 1; * @param value The bytes for text to set. * @return This builder for chaining. @@ -794,6 +854,10 @@ public Builder setTextBytes( private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> fileBuilder_; /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; * @return Whether the file field is set. */ @@ -802,6 +866,10 @@ public boolean hasFile() { return partCase_ == 2; } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; * @return The file. */ @@ -820,6 +888,10 @@ public io.a2a.grpc.FilePart getFile() { } } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ public Builder setFile(io.a2a.grpc.FilePart value) { @@ -836,6 +908,10 @@ public Builder setFile(io.a2a.grpc.FilePart value) { return this; } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ public Builder setFile( @@ -850,6 +926,10 @@ public Builder setFile( return this; } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ public Builder mergeFile(io.a2a.grpc.FilePart value) { @@ -873,6 +953,10 @@ public Builder mergeFile(io.a2a.grpc.FilePart value) { return this; } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ public Builder clearFile() { @@ -892,12 +976,20 @@ public Builder clearFile() { return this; } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ public io.a2a.grpc.FilePart.Builder getFileBuilder() { return internalGetFileFieldBuilder().getBuilder(); } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ @java.lang.Override @@ -912,6 +1004,10 @@ public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { } } /** + *
+     * The file content, represented as either a URI or as base64-encoded bytes.
+     * 
+ * * .a2a.v1.FilePart file = 2; */ private com.google.protobuf.SingleFieldBuilder< @@ -936,6 +1032,10 @@ public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> dataBuilder_; /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; * @return Whether the data field is set. */ @@ -944,6 +1044,10 @@ public boolean hasData() { return partCase_ == 3; } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; * @return The data. */ @@ -962,6 +1066,10 @@ public io.a2a.grpc.DataPart getData() { } } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ public Builder setData(io.a2a.grpc.DataPart value) { @@ -978,6 +1086,10 @@ public Builder setData(io.a2a.grpc.DataPart value) { return this; } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ public Builder setData( @@ -992,6 +1104,10 @@ public Builder setData( return this; } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ public Builder mergeData(io.a2a.grpc.DataPart value) { @@ -1015,6 +1131,10 @@ public Builder mergeData(io.a2a.grpc.DataPart value) { return this; } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ public Builder clearData() { @@ -1034,12 +1154,20 @@ public Builder clearData() { return this; } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ public io.a2a.grpc.DataPart.Builder getDataBuilder() { return internalGetDataFieldBuilder().getBuilder(); } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ @java.lang.Override @@ -1054,6 +1182,10 @@ public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { } } /** + *
+     * The structured data content.
+     * 
+ * * .a2a.v1.DataPart data = 3; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java index efcce09b6..192df9cb0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java @@ -11,16 +11,28 @@ public interface PartOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
+   * The string content of the text part.
+   * 
+ * * string text = 1; * @return Whether the text field is set. */ boolean hasText(); /** + *
+   * The string content of the text part.
+   * 
+ * * string text = 1; * @return The text. */ java.lang.String getText(); /** + *
+   * The string content of the text part.
+   * 
+ * * string text = 1; * @return The bytes for text. */ @@ -28,31 +40,55 @@ public interface PartOrBuilder extends getTextBytes(); /** + *
+   * The file content, represented as either a URI or as base64-encoded bytes.
+   * 
+ * * .a2a.v1.FilePart file = 2; * @return Whether the file field is set. */ boolean hasFile(); /** + *
+   * The file content, represented as either a URI or as base64-encoded bytes.
+   * 
+ * * .a2a.v1.FilePart file = 2; * @return The file. */ io.a2a.grpc.FilePart getFile(); /** + *
+   * The file content, represented as either a URI or as base64-encoded bytes.
+   * 
+ * * .a2a.v1.FilePart file = 2; */ io.a2a.grpc.FilePartOrBuilder getFileOrBuilder(); /** + *
+   * The structured data content.
+   * 
+ * * .a2a.v1.DataPart data = 3; * @return Whether the data field is set. */ boolean hasData(); /** + *
+   * The structured data content.
+   * 
+ * * .a2a.v1.DataPart data = 3; * @return The data. */ io.a2a.grpc.DataPart getData(); /** + *
+   * The structured data content.
+   * 
+ * * .a2a.v1.DataPart data = 3; */ io.a2a.grpc.DataPartOrBuilder getDataOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java index b2d628b53..74cba7f15 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java @@ -8,6 +8,7 @@ /** *
  * --8<-- [start:PasswordOAuthFlow]
+ * Defines configuration details for the OAuth 2.0 Resource Owner Password flow.
  * 
* * Protobuf type {@code a2a.v1.PasswordOAuthFlow} @@ -66,11 +67,10 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl private volatile java.lang.Object tokenUrl_ = ""; /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ @java.lang.Override @@ -88,11 +88,10 @@ public java.lang.String getTokenUrl() { } /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -115,8 +114,7 @@ public java.lang.String getTokenUrl() { private volatile java.lang.Object refreshUrl_ = ""; /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -137,8 +135,7 @@ public java.lang.String getRefreshUrl() { } /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -187,11 +184,10 @@ public int getScopesCount() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -209,11 +205,10 @@ public java.util.Map getScopes() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -221,11 +216,10 @@ public java.util.Map getScopesMap() { } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -240,11 +234,10 @@ java.lang.String getScopesOrDefault( } /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -449,6 +442,7 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:PasswordOAuthFlow]
+   * Defines configuration details for the OAuth 2.0 Resource Owner Password flow.
    * 
* * Protobuf type {@code a2a.v1.PasswordOAuthFlow} @@ -644,11 +638,10 @@ public Builder mergeFrom( private java.lang.Object tokenUrl_ = ""; /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -665,11 +658,10 @@ public java.lang.String getTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -687,11 +679,10 @@ public java.lang.String getTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -705,11 +696,10 @@ public Builder setTokenUrl( } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTokenUrl() { @@ -720,11 +710,10 @@ public Builder clearTokenUrl() { } /** *
-     * The token URL to be used for this flow. This MUST be in the form of a URL.
-     * The OAuth2 standard requires the use of TLS.
+     * The token URL to be used for this flow.
      * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -741,8 +730,7 @@ public Builder setTokenUrlBytes( private java.lang.Object refreshUrl_ = ""; /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -762,8 +750,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -784,8 +771,7 @@ public java.lang.String getRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -802,8 +788,7 @@ public Builder setRefreshUrl( } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -817,8 +802,7 @@ public Builder clearRefreshUrl() { } /** *
-     * The URL to be used for obtaining refresh tokens. This MUST be in the
-     * form of a URL. The OAuth2 standard requires the use of TLS.
+     * The URL to be used for obtaining refresh tokens.
      * 
* * string refresh_url = 2; @@ -863,11 +847,10 @@ public int getScopesCount() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -885,11 +868,10 @@ public java.util.Map getScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -897,11 +879,10 @@ public java.util.Map getScopesMap() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -916,11 +897,10 @@ java.lang.String getScopesOrDefault( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -941,11 +921,10 @@ public Builder clearScopes() { } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeScopes( java.lang.String key) { @@ -965,11 +944,10 @@ public Builder removeScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putScopes( java.lang.String key, @@ -983,11 +961,10 @@ public Builder putScopes( } /** *
-     * The available scopes for the OAuth2 security scheme. A map between the
-     * scope name and a short description for it. The map MAY be empty.
+     * The available scopes for the OAuth2 security scheme.
      * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putAllScopes( java.util.Map values) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java index 1eea1da0f..280026c9a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java @@ -12,21 +12,19 @@ public interface PasswordOAuthFlowOrBuilder extends /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ java.lang.String getTokenUrl(); /** *
-   * The token URL to be used for this flow. This MUST be in the form of a URL.
-   * The OAuth2 standard requires the use of TLS.
+   * The token URL to be used for this flow.
    * 
* - * string token_url = 1; + * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -34,8 +32,7 @@ public interface PasswordOAuthFlowOrBuilder extends /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -44,8 +41,7 @@ public interface PasswordOAuthFlowOrBuilder extends java.lang.String getRefreshUrl(); /** *
-   * The URL to be used for obtaining refresh tokens. This MUST be in the
-   * form of a URL. The OAuth2 standard requires the use of TLS.
+   * The URL to be used for obtaining refresh tokens.
    * 
* * string refresh_url = 2; @@ -56,20 +52,18 @@ public interface PasswordOAuthFlowOrBuilder extends /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ int getScopesCount(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ boolean containsScopes( java.lang.String key); @@ -81,21 +75,19 @@ boolean containsScopes( getScopes(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ java.util.Map getScopesMap(); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -104,11 +96,10 @@ java.lang.String getScopesOrDefault( java.lang.String defaultValue); /** *
-   * The available scopes for the OAuth2 security scheme. A map between the
-   * scope name and a short description for it. The map MAY be empty.
+   * The available scopes for the OAuth2 security scheme.
    * 
* - * map<string, string> scopes = 3; + * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java index 628454eb5..b38d1fff2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java @@ -107,7 +107,7 @@ public java.lang.String getId() { * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ @java.lang.Override @@ -128,7 +128,7 @@ public java.lang.String getUrl() { * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ @java.lang.Override @@ -721,7 +721,7 @@ public Builder setIdBytes( * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ public java.lang.String getUrl() { @@ -741,7 +741,7 @@ public java.lang.String getUrl() { * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ public com.google.protobuf.ByteString @@ -762,7 +762,7 @@ public java.lang.String getUrl() { * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The url to set. * @return This builder for chaining. */ @@ -779,7 +779,7 @@ public Builder setUrl( * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearUrl() { @@ -793,7 +793,7 @@ public Builder clearUrl() { * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for url to set. * @return This builder for chaining. */ diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java index 287e48afc..329acafce 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java @@ -35,7 +35,7 @@ public interface PushNotificationConfigOrBuilder extends * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The url. */ java.lang.String getUrl(); @@ -44,7 +44,7 @@ public interface PushNotificationConfigOrBuilder extends * Url to send the notification too * * - * string url = 2; + * string url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for url. */ com.google.protobuf.ByteString diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Role.java b/spec-grpc/src/main/java/io/a2a/grpc/Role.java index 25f4d57ad..aa645cb5b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Role.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Role.java @@ -6,6 +6,11 @@ package io.a2a.grpc; /** + *
+ * --8<-- [start:Role]
+ * Defines the sender of a message in A2A protocol communication.
+ * 
+ * * Protobuf enum {@code a2a.v1.Role} */ @com.google.protobuf.Generated diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java index 7b2b30ec2..90fb5df7c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java @@ -8,6 +8,9 @@ /** *
  * --8<-- [start:SecurityScheme]
+ * Defines a security scheme that can be used to secure an agent's endpoints.
+ * This is a discriminated union type based on the OpenAPI 3.2 Security Scheme Object.
+ * See: https://spec.openapis.org/oas/v3.2.0.html#security-scheme-object
  * 
* * Protobuf type {@code a2a.v1.SecurityScheme} @@ -483,6 +486,9 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:SecurityScheme]
+   * Defines a security scheme that can be used to secure an agent's endpoints.
+   * This is a discriminated union type based on the OpenAPI 3.2 Security Scheme Object.
+   * See: https://spec.openapis.org/oas/v3.2.0.html#security-scheme-object
    * 
* * Protobuf type {@code a2a.v1.SecurityScheme} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index aa00a6b94..24ba5afba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -7,7 +7,7 @@ /** *
- * --8<-- [start:MessageSendConfiguration]
+ * --8<-- [start:SendMessageConfiguration]
  * Configuration of a send message request.
  * 
* @@ -146,11 +146,22 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu private int historyLength_ = 0; /** *
-   * The maximum number of messages to include in the history. if 0, the
-   * history will be unlimited.
+   * The maximum number of messages to include in the history.
    * 
* - * int32 history_length = 3; + * optional int32 history_length = 3; + * @return Whether the historyLength field is set. + */ + @java.lang.Override + public boolean hasHistoryLength() { + return ((bitField0_ & 0x00000002) != 0); + } + /** + *
+   * The maximum number of messages to include in the history.
+   * 
+ * + * optional int32 history_length = 3; * @return The historyLength. */ @java.lang.Override @@ -162,10 +173,7 @@ public int getHistoryLength() { private boolean blocking_ = false; /** *
-   * If true, the message will be blocking until the task is completed. If
-   * false, the message will be non-blocking and the task will be returned
-   * immediately. It is the caller's responsibility to check for any task
-   * updates.
+   * If true, the message will be blocking until the task is completed.
    * 
* * bool blocking = 4; @@ -196,7 +204,7 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) != 0)) { output.writeMessage(2, getPushNotificationConfig()); } - if (historyLength_ != 0) { + if (((bitField0_ & 0x00000002) != 0)) { output.writeInt32(3, historyLength_); } if (blocking_ != false) { @@ -223,7 +231,7 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(2, getPushNotificationConfig()); } - if (historyLength_ != 0) { + if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream .computeInt32Size(3, historyLength_); } @@ -253,8 +261,11 @@ public boolean equals(final java.lang.Object obj) { if (!getPushNotificationConfig() .equals(other.getPushNotificationConfig())) return false; } - if (getHistoryLength() - != other.getHistoryLength()) return false; + if (hasHistoryLength() != other.hasHistoryLength()) return false; + if (hasHistoryLength()) { + if (getHistoryLength() + != other.getHistoryLength()) return false; + } if (getBlocking() != other.getBlocking()) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -276,8 +287,10 @@ public int hashCode() { hash = (37 * hash) + PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER; hash = (53 * hash) + getPushNotificationConfig().hashCode(); } - hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; - hash = (53 * hash) + getHistoryLength(); + if (hasHistoryLength()) { + hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getHistoryLength(); + } hash = (37 * hash) + BLOCKING_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( getBlocking()); @@ -380,7 +393,7 @@ protected Builder newBuilderForType( } /** *
-   * --8<-- [start:MessageSendConfiguration]
+   * --8<-- [start:SendMessageConfiguration]
    * Configuration of a send message request.
    * 
* @@ -478,6 +491,7 @@ private void buildPartial0(io.a2a.grpc.SendMessageConfiguration result) { } if (((from_bitField0_ & 0x00000004) != 0)) { result.historyLength_ = historyLength_; + to_bitField0_ |= 0x00000002; } if (((from_bitField0_ & 0x00000008) != 0)) { result.blocking_ = blocking_; @@ -510,7 +524,7 @@ public Builder mergeFrom(io.a2a.grpc.SendMessageConfiguration other) { if (other.hasPushNotificationConfig()) { mergePushNotificationConfig(other.getPushNotificationConfig()); } - if (other.getHistoryLength() != 0) { + if (other.hasHistoryLength()) { setHistoryLength(other.getHistoryLength()); } if (other.getBlocking() != false) { @@ -889,11 +903,22 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu private int historyLength_ ; /** *
-     * The maximum number of messages to include in the history. if 0, the
-     * history will be unlimited.
+     * The maximum number of messages to include in the history.
+     * 
+ * + * optional int32 history_length = 3; + * @return Whether the historyLength field is set. + */ + @java.lang.Override + public boolean hasHistoryLength() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
+     * The maximum number of messages to include in the history.
      * 
* - * int32 history_length = 3; + * optional int32 history_length = 3; * @return The historyLength. */ @java.lang.Override @@ -902,11 +927,10 @@ public int getHistoryLength() { } /** *
-     * The maximum number of messages to include in the history. if 0, the
-     * history will be unlimited.
+     * The maximum number of messages to include in the history.
      * 
* - * int32 history_length = 3; + * optional int32 history_length = 3; * @param value The historyLength to set. * @return This builder for chaining. */ @@ -919,11 +943,10 @@ public Builder setHistoryLength(int value) { } /** *
-     * The maximum number of messages to include in the history. if 0, the
-     * history will be unlimited.
+     * The maximum number of messages to include in the history.
      * 
* - * int32 history_length = 3; + * optional int32 history_length = 3; * @return This builder for chaining. */ public Builder clearHistoryLength() { @@ -936,10 +959,7 @@ public Builder clearHistoryLength() { private boolean blocking_ ; /** *
-     * If true, the message will be blocking until the task is completed. If
-     * false, the message will be non-blocking and the task will be returned
-     * immediately. It is the caller's responsibility to check for any task
-     * updates.
+     * If true, the message will be blocking until the task is completed.
      * 
* * bool blocking = 4; @@ -951,10 +971,7 @@ public boolean getBlocking() { } /** *
-     * If true, the message will be blocking until the task is completed. If
-     * false, the message will be non-blocking and the task will be returned
-     * immediately. It is the caller's responsibility to check for any task
-     * updates.
+     * If true, the message will be blocking until the task is completed.
      * 
* * bool blocking = 4; @@ -970,10 +987,7 @@ public Builder setBlocking(boolean value) { } /** *
-     * If true, the message will be blocking until the task is completed. If
-     * false, the message will be non-blocking and the task will be returned
-     * immediately. It is the caller's responsibility to check for any task
-     * updates.
+     * If true, the message will be blocking until the task is completed.
      * 
* * bool blocking = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java index d69a9755e..e1a6f791f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -80,21 +80,26 @@ public interface SendMessageConfigurationOrBuilder extends /** *
-   * The maximum number of messages to include in the history. if 0, the
-   * history will be unlimited.
+   * The maximum number of messages to include in the history.
    * 
* - * int32 history_length = 3; + * optional int32 history_length = 3; + * @return Whether the historyLength field is set. + */ + boolean hasHistoryLength(); + /** + *
+   * The maximum number of messages to include in the history.
+   * 
+ * + * optional int32 history_length = 3; * @return The historyLength. */ int getHistoryLength(); /** *
-   * If true, the message will be blocking until the task is completed. If
-   * false, the message will be non-blocking and the task will be returned
-   * immediately. It is the caller's responsibility to check for any task
-   * updates.
+   * If true, the message will be blocking until the task is completed.
    * 
* * bool blocking = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index 4f306283e..40511b286 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -8,7 +8,8 @@ /** *
  * /////////// Request Messages ///////////
- * --8<-- [start:MessageSendParams]
+ * --8<-- [start:SendMessageRequest]
+ * Represents a request for the `message/send` method.
  * 
* * Protobuf type {@code a2a.v1.SendMessageRequest} @@ -360,7 +361,8 @@ protected Builder newBuilderForType( /** *
    * /////////// Request Messages ///////////
-   * --8<-- [start:MessageSendParams]
+   * --8<-- [start:SendMessageRequest]
+   * Represents a request for the `message/send` method.
    * 
* * Protobuf type {@code a2a.v1.SendMessageRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java index b2260e057..1d3a75c82 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java @@ -8,7 +8,7 @@ /** *
  * ////// Response Messages ///////////
- * --8<-- [start:SendMessageSuccessResponse]
+ * --8<-- [start:SendMessageResponse]
  * 
* * Protobuf type {@code a2a.v1.SendMessageResponse} @@ -340,7 +340,7 @@ protected Builder newBuilderForType( /** *
    * ////// Response Messages ///////////
-   * --8<-- [start:SendMessageSuccessResponse]
+   * --8<-- [start:SendMessageResponse]
    * 
* * Protobuf type {@code a2a.v1.SendMessageResponse} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java similarity index 84% rename from spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java index 8afc6ffc8..b12b2e1d2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java @@ -8,15 +8,16 @@ /** *
  * --8<-- [start:SetTaskPushNotificationConfigRequest]
+ * Represents a request for the `tasks/pushNotificationConfig/set` method.
  * 
* - * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} + * Protobuf type {@code a2a.v1.SetTaskPushNotificationConfigRequest} */ @com.google.protobuf.Generated -public final class CreateTaskPushNotificationConfigRequest extends +public final class SetTaskPushNotificationConfigRequest extends com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.CreateTaskPushNotificationConfigRequest) - CreateTaskPushNotificationConfigRequestOrBuilder { + // @@protoc_insertion_point(message_implements:a2a.v1.SetTaskPushNotificationConfigRequest) + SetTaskPushNotificationConfigRequestOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( @@ -25,28 +26,28 @@ public final class CreateTaskPushNotificationConfigRequest extends /* minor= */ 31, /* patch= */ 1, /* suffix= */ "", - CreateTaskPushNotificationConfigRequest.class.getName()); + SetTaskPushNotificationConfigRequest.class.getName()); } - // Use CreateTaskPushNotificationConfigRequest.newBuilder() to construct. - private CreateTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + // Use SetTaskPushNotificationConfigRequest.newBuilder() to construct. + private SetTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } - private CreateTaskPushNotificationConfigRequest() { + private SetTaskPushNotificationConfigRequest() { parent_ = ""; configId_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder.class); + io.a2a.grpc.SetTaskPushNotificationConfigRequest.class, io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder.class); } private int bitField0_; @@ -236,10 +237,10 @@ public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } - if (!(obj instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest)) { + if (!(obj instanceof io.a2a.grpc.SetTaskPushNotificationConfigRequest)) { return super.equals(obj); } - io.a2a.grpc.CreateTaskPushNotificationConfigRequest other = (io.a2a.grpc.CreateTaskPushNotificationConfigRequest) obj; + io.a2a.grpc.SetTaskPushNotificationConfigRequest other = (io.a2a.grpc.SetTaskPushNotificationConfigRequest) obj; if (!getParent() .equals(other.getParent())) return false; @@ -274,44 +275,44 @@ public int hashCode() { return hash; } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom(byte[] data) + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -319,26 +320,26 @@ public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( .parseWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseDelimitedFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -351,7 +352,7 @@ public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(io.a2a.grpc.CreateTaskPushNotificationConfigRequest prototype) { + public static Builder newBuilder(io.a2a.grpc.SetTaskPushNotificationConfigRequest prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override @@ -369,28 +370,29 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:SetTaskPushNotificationConfigRequest]
+   * Represents a request for the `tasks/pushNotificationConfig/set` method.
    * 
* - * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} + * Protobuf type {@code a2a.v1.SetTaskPushNotificationConfigRequest} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.CreateTaskPushNotificationConfigRequest) - io.a2a.grpc.CreateTaskPushNotificationConfigRequestOrBuilder { + // @@protoc_insertion_point(builder_implements:a2a.v1.SetTaskPushNotificationConfigRequest) + io.a2a.grpc.SetTaskPushNotificationConfigRequestOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder.class); + io.a2a.grpc.SetTaskPushNotificationConfigRequest.class, io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder.class); } - // Construct using io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder() + // Construct using io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder() private Builder() { maybeForceBuilderInitialization(); } @@ -423,17 +425,17 @@ public Builder clear() { @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; } @java.lang.Override - public io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstanceForType() { - return io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance(); + public io.a2a.grpc.SetTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance(); } @java.lang.Override - public io.a2a.grpc.CreateTaskPushNotificationConfigRequest build() { - io.a2a.grpc.CreateTaskPushNotificationConfigRequest result = buildPartial(); + public io.a2a.grpc.SetTaskPushNotificationConfigRequest build() { + io.a2a.grpc.SetTaskPushNotificationConfigRequest result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } @@ -441,14 +443,14 @@ public io.a2a.grpc.CreateTaskPushNotificationConfigRequest build() { } @java.lang.Override - public io.a2a.grpc.CreateTaskPushNotificationConfigRequest buildPartial() { - io.a2a.grpc.CreateTaskPushNotificationConfigRequest result = new io.a2a.grpc.CreateTaskPushNotificationConfigRequest(this); + public io.a2a.grpc.SetTaskPushNotificationConfigRequest buildPartial() { + io.a2a.grpc.SetTaskPushNotificationConfigRequest result = new io.a2a.grpc.SetTaskPushNotificationConfigRequest(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } - private void buildPartial0(io.a2a.grpc.CreateTaskPushNotificationConfigRequest result) { + private void buildPartial0(io.a2a.grpc.SetTaskPushNotificationConfigRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.parent_ = parent_; @@ -468,16 +470,16 @@ private void buildPartial0(io.a2a.grpc.CreateTaskPushNotificationConfigRequest r @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest) { - return mergeFrom((io.a2a.grpc.CreateTaskPushNotificationConfigRequest)other); + if (other instanceof io.a2a.grpc.SetTaskPushNotificationConfigRequest) { + return mergeFrom((io.a2a.grpc.SetTaskPushNotificationConfigRequest)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(io.a2a.grpc.CreateTaskPushNotificationConfigRequest other) { - if (other == io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + public Builder mergeFrom(io.a2a.grpc.SetTaskPushNotificationConfigRequest other) { + if (other == io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance()) return this; if (!other.getParent().isEmpty()) { parent_ = other.parent_; bitField0_ |= 0x00000001; @@ -897,23 +899,23 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { return configBuilder_; } - // @@protoc_insertion_point(builder_scope:a2a.v1.CreateTaskPushNotificationConfigRequest) + // @@protoc_insertion_point(builder_scope:a2a.v1.SetTaskPushNotificationConfigRequest) } - // @@protoc_insertion_point(class_scope:a2a.v1.CreateTaskPushNotificationConfigRequest) - private static final io.a2a.grpc.CreateTaskPushNotificationConfigRequest DEFAULT_INSTANCE; + // @@protoc_insertion_point(class_scope:a2a.v1.SetTaskPushNotificationConfigRequest) + private static final io.a2a.grpc.SetTaskPushNotificationConfigRequest DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new io.a2a.grpc.CreateTaskPushNotificationConfigRequest(); + DEFAULT_INSTANCE = new io.a2a.grpc.SetTaskPushNotificationConfigRequest(); } - public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstance() { + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest getDefaultInstance() { return DEFAULT_INSTANCE; } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { @java.lang.Override - public CreateTaskPushNotificationConfigRequest parsePartialFrom( + public SetTaskPushNotificationConfigRequest parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { @@ -932,17 +934,17 @@ public CreateTaskPushNotificationConfigRequest parsePartialFrom( } }; - public static com.google.protobuf.Parser parser() { + public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override - public com.google.protobuf.Parser getParserForType() { + public com.google.protobuf.Parser getParserForType() { return PARSER; } @java.lang.Override - public io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstanceForType() { + public io.a2a.grpc.SetTaskPushNotificationConfigRequest getDefaultInstanceForType() { return DEFAULT_INSTANCE; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java similarity index 92% rename from spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java index 3bba0f0ba..53a99640c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java @@ -6,8 +6,8 @@ package io.a2a.grpc; @com.google.protobuf.Generated -public interface CreateTaskPushNotificationConfigRequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.CreateTaskPushNotificationConfigRequest) +public interface SetTaskPushNotificationConfigRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SetTaskPushNotificationConfigRequest) com.google.protobuf.MessageOrBuilder { /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java index e995b6fa1..6592fcce9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java @@ -7,16 +7,8 @@ /** *
- * --8<-- [start:SendStreamingMessageSuccessResponse]
- * The stream response for a message. The stream should be one of the following
- * sequences:
- * If the response is a message, the stream should contain one, and only one,
- * message and then close
- * If the response is a task lifecycle, the first response should be a Task
- * object followed by zero or more TaskStatusUpdateEvents and
- * TaskArtifactUpdateEvents. The stream should complete when the Task
- * if in an interrupted or terminal state. A stream that ends before these
- * conditions are met are
+ * --8<-- [start:StreamResponse]
+ * Response message containing task updates or messages.
  * 
* * Protobuf type {@code a2a.v1.StreamResponse} @@ -443,16 +435,8 @@ protected Builder newBuilderForType( } /** *
-   * --8<-- [start:SendStreamingMessageSuccessResponse]
-   * The stream response for a message. The stream should be one of the following
-   * sequences:
-   * If the response is a message, the stream should contain one, and only one,
-   * message and then close
-   * If the response is a task lifecycle, the first response should be a Task
-   * object followed by zero or more TaskStatusUpdateEvents and
-   * TaskArtifactUpdateEvents. The stream should complete when the Task
-   * if in an interrupted or terminal state. A stream that ends before these
-   * conditions are met are
+   * --8<-- [start:StreamResponse]
+   * Response message containing task updates or messages.
    * 
* * Protobuf type {@code a2a.v1.StreamResponse} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java similarity index 76% rename from spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java index 93386e083..368a89fa8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java @@ -7,16 +7,16 @@ /** *
- * --8<-- [start:TaskResubscriptionRequest]
+ * --8<-- [start:SubscribeToTaskRequest]
  * 
* - * Protobuf type {@code a2a.v1.TaskSubscriptionRequest} + * Protobuf type {@code a2a.v1.SubscribeToTaskRequest} */ @com.google.protobuf.Generated -public final class TaskSubscriptionRequest extends +public final class SubscribeToTaskRequest extends com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.TaskSubscriptionRequest) - TaskSubscriptionRequestOrBuilder { + // @@protoc_insertion_point(message_implements:a2a.v1.SubscribeToTaskRequest) + SubscribeToTaskRequestOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( @@ -25,27 +25,27 @@ public final class TaskSubscriptionRequest extends /* minor= */ 31, /* patch= */ 1, /* suffix= */ "", - TaskSubscriptionRequest.class.getName()); + SubscribeToTaskRequest.class.getName()); } - // Use TaskSubscriptionRequest.newBuilder() to construct. - private TaskSubscriptionRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + // Use SubscribeToTaskRequest.newBuilder() to construct. + private SubscribeToTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } - private TaskSubscriptionRequest() { + private SubscribeToTaskRequest() { name_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SubscribeToTaskRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.TaskSubscriptionRequest.class, io.a2a.grpc.TaskSubscriptionRequest.Builder.class); + io.a2a.grpc.SubscribeToTaskRequest.class, io.a2a.grpc.SubscribeToTaskRequest.Builder.class); } public static final int NAME_FIELD_NUMBER = 1; @@ -136,10 +136,10 @@ public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } - if (!(obj instanceof io.a2a.grpc.TaskSubscriptionRequest)) { + if (!(obj instanceof io.a2a.grpc.SubscribeToTaskRequest)) { return super.equals(obj); } - io.a2a.grpc.TaskSubscriptionRequest other = (io.a2a.grpc.TaskSubscriptionRequest) obj; + io.a2a.grpc.SubscribeToTaskRequest other = (io.a2a.grpc.SubscribeToTaskRequest) obj; if (!getName() .equals(other.getName())) return false; @@ -161,44 +161,44 @@ public int hashCode() { return hash; } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom(byte[] data) + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom(java.io.InputStream input) + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -206,26 +206,26 @@ public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( .parseWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.TaskSubscriptionRequest parseDelimitedFrom(java.io.InputStream input) + public static io.a2a.grpc.SubscribeToTaskRequest parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } - public static io.a2a.grpc.TaskSubscriptionRequest parseDelimitedFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( + public static io.a2a.grpc.SubscribeToTaskRequest parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -238,7 +238,7 @@ public static io.a2a.grpc.TaskSubscriptionRequest parseFrom( public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(io.a2a.grpc.TaskSubscriptionRequest prototype) { + public static Builder newBuilder(io.a2a.grpc.SubscribeToTaskRequest prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override @@ -255,29 +255,29 @@ protected Builder newBuilderForType( } /** *
-   * --8<-- [start:TaskResubscriptionRequest]
+   * --8<-- [start:SubscribeToTaskRequest]
    * 
* - * Protobuf type {@code a2a.v1.TaskSubscriptionRequest} + * Protobuf type {@code a2a.v1.SubscribeToTaskRequest} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.TaskSubscriptionRequest) - io.a2a.grpc.TaskSubscriptionRequestOrBuilder { + // @@protoc_insertion_point(builder_implements:a2a.v1.SubscribeToTaskRequest) + io.a2a.grpc.SubscribeToTaskRequestOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SubscribeToTaskRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.TaskSubscriptionRequest.class, io.a2a.grpc.TaskSubscriptionRequest.Builder.class); + io.a2a.grpc.SubscribeToTaskRequest.class, io.a2a.grpc.SubscribeToTaskRequest.Builder.class); } - // Construct using io.a2a.grpc.TaskSubscriptionRequest.newBuilder() + // Construct using io.a2a.grpc.SubscribeToTaskRequest.newBuilder() private Builder() { } @@ -298,17 +298,17 @@ public Builder clear() { @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_TaskSubscriptionRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SubscribeToTaskRequest_descriptor; } @java.lang.Override - public io.a2a.grpc.TaskSubscriptionRequest getDefaultInstanceForType() { - return io.a2a.grpc.TaskSubscriptionRequest.getDefaultInstance(); + public io.a2a.grpc.SubscribeToTaskRequest getDefaultInstanceForType() { + return io.a2a.grpc.SubscribeToTaskRequest.getDefaultInstance(); } @java.lang.Override - public io.a2a.grpc.TaskSubscriptionRequest build() { - io.a2a.grpc.TaskSubscriptionRequest result = buildPartial(); + public io.a2a.grpc.SubscribeToTaskRequest build() { + io.a2a.grpc.SubscribeToTaskRequest result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } @@ -316,14 +316,14 @@ public io.a2a.grpc.TaskSubscriptionRequest build() { } @java.lang.Override - public io.a2a.grpc.TaskSubscriptionRequest buildPartial() { - io.a2a.grpc.TaskSubscriptionRequest result = new io.a2a.grpc.TaskSubscriptionRequest(this); + public io.a2a.grpc.SubscribeToTaskRequest buildPartial() { + io.a2a.grpc.SubscribeToTaskRequest result = new io.a2a.grpc.SubscribeToTaskRequest(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } - private void buildPartial0(io.a2a.grpc.TaskSubscriptionRequest result) { + private void buildPartial0(io.a2a.grpc.SubscribeToTaskRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.name_ = name_; @@ -332,16 +332,16 @@ private void buildPartial0(io.a2a.grpc.TaskSubscriptionRequest result) { @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.TaskSubscriptionRequest) { - return mergeFrom((io.a2a.grpc.TaskSubscriptionRequest)other); + if (other instanceof io.a2a.grpc.SubscribeToTaskRequest) { + return mergeFrom((io.a2a.grpc.SubscribeToTaskRequest)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(io.a2a.grpc.TaskSubscriptionRequest other) { - if (other == io.a2a.grpc.TaskSubscriptionRequest.getDefaultInstance()) return this; + public Builder mergeFrom(io.a2a.grpc.SubscribeToTaskRequest other) { + if (other == io.a2a.grpc.SubscribeToTaskRequest.getDefaultInstance()) return this; if (!other.getName().isEmpty()) { name_ = other.name_; bitField0_ |= 0x00000001; @@ -492,23 +492,23 @@ public Builder setNameBytes( return this; } - // @@protoc_insertion_point(builder_scope:a2a.v1.TaskSubscriptionRequest) + // @@protoc_insertion_point(builder_scope:a2a.v1.SubscribeToTaskRequest) } - // @@protoc_insertion_point(class_scope:a2a.v1.TaskSubscriptionRequest) - private static final io.a2a.grpc.TaskSubscriptionRequest DEFAULT_INSTANCE; + // @@protoc_insertion_point(class_scope:a2a.v1.SubscribeToTaskRequest) + private static final io.a2a.grpc.SubscribeToTaskRequest DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new io.a2a.grpc.TaskSubscriptionRequest(); + DEFAULT_INSTANCE = new io.a2a.grpc.SubscribeToTaskRequest(); } - public static io.a2a.grpc.TaskSubscriptionRequest getDefaultInstance() { + public static io.a2a.grpc.SubscribeToTaskRequest getDefaultInstance() { return DEFAULT_INSTANCE; } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { @java.lang.Override - public TaskSubscriptionRequest parsePartialFrom( + public SubscribeToTaskRequest parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { @@ -527,17 +527,17 @@ public TaskSubscriptionRequest parsePartialFrom( } }; - public static com.google.protobuf.Parser parser() { + public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override - public com.google.protobuf.Parser getParserForType() { + public com.google.protobuf.Parser getParserForType() { return PARSER; } @java.lang.Override - public io.a2a.grpc.TaskSubscriptionRequest getDefaultInstanceForType() { + public io.a2a.grpc.SubscribeToTaskRequest getDefaultInstanceForType() { return DEFAULT_INSTANCE; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java similarity index 83% rename from spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java index 07432f4a2..e96160913 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskSubscriptionRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java @@ -6,8 +6,8 @@ package io.a2a.grpc; @com.google.protobuf.Generated -public interface TaskSubscriptionRequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.TaskSubscriptionRequest) +public interface SubscribeToTaskRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SubscribeToTaskRequest) com.google.protobuf.MessageOrBuilder { /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Task.java b/spec-grpc/src/main/java/io/a2a/grpc/Task.java index 2e263161d..89fbe6c67 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Task.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Task.java @@ -65,7 +65,7 @@ private Task() { * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The id. */ @java.lang.Override @@ -87,7 +87,7 @@ public java.lang.String getId() { * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for id. */ @java.lang.Override @@ -114,7 +114,7 @@ public java.lang.String getId() { * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ @java.lang.Override @@ -136,7 +136,7 @@ public java.lang.String getContextId() { * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ @java.lang.Override @@ -161,7 +161,7 @@ public java.lang.String getContextId() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the status field is set. */ @java.lang.Override @@ -173,7 +173,7 @@ public boolean hasStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The status. */ @java.lang.Override @@ -185,7 +185,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { @@ -325,6 +325,8 @@ public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * A key/value object to store custom metadata about a task.
+   * Optional metadata for extensions. The key is an extension-specific
+   * identifier.
    * 
* * .google.protobuf.Struct metadata = 6; @@ -338,6 +340,8 @@ public boolean hasMetadata() { *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * A key/value object to store custom metadata about a task.
+   * Optional metadata for extensions. The key is an extension-specific
+   * identifier.
    * 
* * .google.protobuf.Struct metadata = 6; @@ -351,6 +355,8 @@ public com.google.protobuf.Struct getMetadata() { *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * A key/value object to store custom metadata about a task.
+   * Optional metadata for extensions. The key is an extension-specific
+   * identifier.
    * 
* * .google.protobuf.Struct metadata = 6; @@ -918,7 +924,7 @@ public Builder mergeFrom( * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The id. */ public java.lang.String getId() { @@ -939,7 +945,7 @@ public java.lang.String getId() { * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for id. */ public com.google.protobuf.ByteString @@ -961,7 +967,7 @@ public java.lang.String getId() { * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The id to set. * @return This builder for chaining. */ @@ -979,7 +985,7 @@ public Builder setId( * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearId() { @@ -994,7 +1000,7 @@ public Builder clearId() { * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for id to set. * @return This builder for chaining. */ @@ -1015,7 +1021,7 @@ public Builder setIdBytes( * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ public java.lang.String getContextId() { @@ -1036,7 +1042,7 @@ public java.lang.String getContextId() { * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -1058,7 +1064,7 @@ public java.lang.String getContextId() { * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The contextId to set. * @return This builder for chaining. */ @@ -1076,7 +1082,7 @@ public Builder setContextId( * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearContextId() { @@ -1091,7 +1097,7 @@ public Builder clearContextId() { * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -1113,7 +1119,7 @@ public Builder setContextIdBytes( * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the status field is set. */ public boolean hasStatus() { @@ -1124,7 +1130,7 @@ public boolean hasStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The status. */ public io.a2a.grpc.TaskStatus getStatus() { @@ -1139,7 +1145,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -1159,7 +1165,7 @@ public Builder setStatus(io.a2a.grpc.TaskStatus value) { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setStatus( io.a2a.grpc.TaskStatus.Builder builderForValue) { @@ -1177,7 +1183,7 @@ public Builder setStatus( * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -1202,7 +1208,7 @@ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearStatus() { bitField0_ = (bitField0_ & ~0x00000004); @@ -1219,7 +1225,7 @@ public Builder clearStatus() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { bitField0_ |= 0x00000004; @@ -1231,7 +1237,7 @@ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { if (statusBuilder_ != null) { @@ -1246,7 +1252,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> @@ -1911,6 +1917,8 @@ public io.a2a.grpc.Message.Builder addHistoryBuilder( *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -1923,6 +1931,8 @@ public boolean hasMetadata() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -1939,6 +1949,8 @@ public com.google.protobuf.Struct getMetadata() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -1960,6 +1972,8 @@ public Builder setMetadata(com.google.protobuf.Struct value) { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -1979,6 +1993,8 @@ public Builder setMetadata( *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -2005,6 +2021,8 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -2023,6 +2041,8 @@ public Builder clearMetadata() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -2036,6 +2056,8 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; @@ -2052,6 +2074,8 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { *
      * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
      * A key/value object to store custom metadata about a task.
+     * Optional metadata for extensions. The key is an extension-specific
+     * identifier.
      * 
* * .google.protobuf.Struct metadata = 6; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java index 83d102dba..3cbc60d1a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java @@ -57,10 +57,10 @@ private TaskArtifactUpdateEvent() { private volatile java.lang.Object taskId_ = ""; /** *
-   * The id of the task for this artifact
+   * The id of the task for this artifact.
    * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The taskId. */ @java.lang.Override @@ -78,10 +78,10 @@ public java.lang.String getTaskId() { } /** *
-   * The id of the task for this artifact
+   * The id of the task for this artifact.
    * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for taskId. */ @java.lang.Override @@ -104,10 +104,10 @@ public java.lang.String getTaskId() { private volatile java.lang.Object contextId_ = ""; /** *
-   * The id of the context that this task belongs too
+   * The id of the context that this task belongs to.
    * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ @java.lang.Override @@ -125,10 +125,10 @@ public java.lang.String getContextId() { } /** *
-   * The id of the context that this task belongs too
+   * The id of the context that this task belongs to.
    * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ @java.lang.Override @@ -150,10 +150,10 @@ public java.lang.String getContextId() { private io.a2a.grpc.Artifact artifact_; /** *
-   * The artifact itself
+   * The artifact that was generated or updated.
    * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the artifact field is set. */ @java.lang.Override @@ -162,10 +162,10 @@ public boolean hasArtifact() { } /** *
-   * The artifact itself
+   * The artifact that was generated or updated.
    * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The artifact. */ @java.lang.Override @@ -174,10 +174,10 @@ public io.a2a.grpc.Artifact getArtifact() { } /** *
-   * The artifact itself
+   * The artifact that was generated or updated.
    * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { @@ -188,7 +188,8 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { private boolean append_ = false; /** *
-   * Whether this should be appended to a prior one produced
+   * If true, the content of this artifact should be appended to a previously
+   * sent artifact with the same ID.
    * 
* * bool append = 4; @@ -203,7 +204,7 @@ public boolean getAppend() { private boolean lastChunk_ = false; /** *
-   * Whether this represents the last part of an artifact
+   * If true, this is the final chunk of the artifact.
    * 
* * bool last_chunk = 5; @@ -710,10 +711,10 @@ public Builder mergeFrom( private java.lang.Object taskId_ = ""; /** *
-     * The id of the task for this artifact
+     * The id of the task for this artifact.
      * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The taskId. */ public java.lang.String getTaskId() { @@ -730,10 +731,10 @@ public java.lang.String getTaskId() { } /** *
-     * The id of the task for this artifact
+     * The id of the task for this artifact.
      * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for taskId. */ public com.google.protobuf.ByteString @@ -751,10 +752,10 @@ public java.lang.String getTaskId() { } /** *
-     * The id of the task for this artifact
+     * The id of the task for this artifact.
      * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The taskId to set. * @return This builder for chaining. */ @@ -768,10 +769,10 @@ public Builder setTaskId( } /** *
-     * The id of the task for this artifact
+     * The id of the task for this artifact.
      * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTaskId() { @@ -782,10 +783,10 @@ public Builder clearTaskId() { } /** *
-     * The id of the task for this artifact
+     * The id of the task for this artifact.
      * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for taskId to set. * @return This builder for chaining. */ @@ -802,10 +803,10 @@ public Builder setTaskIdBytes( private java.lang.Object contextId_ = ""; /** *
-     * The id of the context that this task belongs too
+     * The id of the context that this task belongs to.
      * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ public java.lang.String getContextId() { @@ -822,10 +823,10 @@ public java.lang.String getContextId() { } /** *
-     * The id of the context that this task belongs too
+     * The id of the context that this task belongs to.
      * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -843,10 +844,10 @@ public java.lang.String getContextId() { } /** *
-     * The id of the context that this task belongs too
+     * The id of the context that this task belongs to.
      * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The contextId to set. * @return This builder for chaining. */ @@ -860,10 +861,10 @@ public Builder setContextId( } /** *
-     * The id of the context that this task belongs too
+     * The id of the context that this task belongs to.
      * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearContextId() { @@ -874,10 +875,10 @@ public Builder clearContextId() { } /** *
-     * The id of the context that this task belongs too
+     * The id of the context that this task belongs to.
      * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -896,10 +897,10 @@ public Builder setContextIdBytes( io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> artifactBuilder_; /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the artifact field is set. */ public boolean hasArtifact() { @@ -907,10 +908,10 @@ public boolean hasArtifact() { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The artifact. */ public io.a2a.grpc.Artifact getArtifact() { @@ -922,10 +923,10 @@ public io.a2a.grpc.Artifact getArtifact() { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setArtifact(io.a2a.grpc.Artifact value) { if (artifactBuilder_ == null) { @@ -942,10 +943,10 @@ public Builder setArtifact(io.a2a.grpc.Artifact value) { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setArtifact( io.a2a.grpc.Artifact.Builder builderForValue) { @@ -960,10 +961,10 @@ public Builder setArtifact( } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeArtifact(io.a2a.grpc.Artifact value) { if (artifactBuilder_ == null) { @@ -985,10 +986,10 @@ public Builder mergeArtifact(io.a2a.grpc.Artifact value) { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearArtifact() { bitField0_ = (bitField0_ & ~0x00000004); @@ -1002,10 +1003,10 @@ public Builder clearArtifact() { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.Artifact.Builder getArtifactBuilder() { bitField0_ |= 0x00000004; @@ -1014,10 +1015,10 @@ public io.a2a.grpc.Artifact.Builder getArtifactBuilder() { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { if (artifactBuilder_ != null) { @@ -1029,10 +1030,10 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { } /** *
-     * The artifact itself
+     * The artifact that was generated or updated.
      * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Artifact, io.a2a.grpc.Artifact.Builder, io.a2a.grpc.ArtifactOrBuilder> @@ -1051,7 +1052,8 @@ public io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder() { private boolean append_ ; /** *
-     * Whether this should be appended to a prior one produced
+     * If true, the content of this artifact should be appended to a previously
+     * sent artifact with the same ID.
      * 
* * bool append = 4; @@ -1063,7 +1065,8 @@ public boolean getAppend() { } /** *
-     * Whether this should be appended to a prior one produced
+     * If true, the content of this artifact should be appended to a previously
+     * sent artifact with the same ID.
      * 
* * bool append = 4; @@ -1079,7 +1082,8 @@ public Builder setAppend(boolean value) { } /** *
-     * Whether this should be appended to a prior one produced
+     * If true, the content of this artifact should be appended to a previously
+     * sent artifact with the same ID.
      * 
* * bool append = 4; @@ -1095,7 +1099,7 @@ public Builder clearAppend() { private boolean lastChunk_ ; /** *
-     * Whether this represents the last part of an artifact
+     * If true, this is the final chunk of the artifact.
      * 
* * bool last_chunk = 5; @@ -1107,7 +1111,7 @@ public boolean getLastChunk() { } /** *
-     * Whether this represents the last part of an artifact
+     * If true, this is the final chunk of the artifact.
      * 
* * bool last_chunk = 5; @@ -1123,7 +1127,7 @@ public Builder setLastChunk(boolean value) { } /** *
-     * Whether this represents the last part of an artifact
+     * If true, this is the final chunk of the artifact.
      * 
* * bool last_chunk = 5; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java index 8d208de11..558068b71 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java @@ -12,19 +12,19 @@ public interface TaskArtifactUpdateEventOrBuilder extends /** *
-   * The id of the task for this artifact
+   * The id of the task for this artifact.
    * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The taskId. */ java.lang.String getTaskId(); /** *
-   * The id of the task for this artifact
+   * The id of the task for this artifact.
    * 
* - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for taskId. */ com.google.protobuf.ByteString @@ -32,19 +32,19 @@ public interface TaskArtifactUpdateEventOrBuilder extends /** *
-   * The id of the context that this task belongs too
+   * The id of the context that this task belongs to.
    * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ java.lang.String getContextId(); /** *
-   * The id of the context that this task belongs too
+   * The id of the context that this task belongs to.
    * 
* - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -52,34 +52,35 @@ public interface TaskArtifactUpdateEventOrBuilder extends /** *
-   * The artifact itself
+   * The artifact that was generated or updated.
    * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the artifact field is set. */ boolean hasArtifact(); /** *
-   * The artifact itself
+   * The artifact that was generated or updated.
    * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The artifact. */ io.a2a.grpc.Artifact getArtifact(); /** *
-   * The artifact itself
+   * The artifact that was generated or updated.
    * 
* - * .a2a.v1.Artifact artifact = 3; + * .a2a.v1.Artifact artifact = 3 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.ArtifactOrBuilder getArtifactOrBuilder(); /** *
-   * Whether this should be appended to a prior one produced
+   * If true, the content of this artifact should be appended to a previously
+   * sent artifact with the same ID.
    * 
* * bool append = 4; @@ -89,7 +90,7 @@ public interface TaskArtifactUpdateEventOrBuilder extends /** *
-   * Whether this represents the last part of an artifact
+   * If true, this is the final chunk of the artifact.
    * 
* * bool last_chunk = 5; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java index 10bfd49ec..097c353a5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java @@ -16,7 +16,7 @@ public interface TaskOrBuilder extends * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The id. */ java.lang.String getId(); @@ -26,7 +26,7 @@ public interface TaskOrBuilder extends * new task. * * - * string id = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for id. */ com.google.protobuf.ByteString @@ -38,7 +38,7 @@ public interface TaskOrBuilder extends * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ java.lang.String getContextId(); @@ -48,7 +48,7 @@ public interface TaskOrBuilder extends * (tasks and messages). Created by the A2A server. * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -59,7 +59,7 @@ public interface TaskOrBuilder extends * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the status field is set. */ boolean hasStatus(); @@ -68,7 +68,7 @@ public interface TaskOrBuilder extends * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The status. */ io.a2a.grpc.TaskStatus getStatus(); @@ -77,7 +77,7 @@ public interface TaskOrBuilder extends * The current status of a Task, including state and a message. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder(); @@ -178,6 +178,8 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * A key/value object to store custom metadata about a task.
+   * Optional metadata for extensions. The key is an extension-specific
+   * identifier.
    * 
* * .google.protobuf.Struct metadata = 6; @@ -188,6 +190,8 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * A key/value object to store custom metadata about a task.
+   * Optional metadata for extensions. The key is an extension-specific
+   * identifier.
    * 
* * .google.protobuf.Struct metadata = 6; @@ -198,6 +202,8 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
    * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
    * A key/value object to store custom metadata about a task.
+   * Optional metadata for extensions. The key is an extension-specific
+   * identifier.
    * 
* * .google.protobuf.Struct metadata = 6; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java index 95a1b892d..b91bb6d7d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java @@ -8,6 +8,8 @@ /** *
  * --8<-- [start:TaskPushNotificationConfig]
+ * A container associating a push notification configuration with a specific
+ * task.
  * 
* * Protobuf type {@code a2a.v1.TaskPushNotificationConfig} @@ -58,7 +60,7 @@ private TaskPushNotificationConfig() { * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ @java.lang.Override @@ -80,7 +82,7 @@ public java.lang.String getName() { * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ @java.lang.Override @@ -105,7 +107,7 @@ public java.lang.String getName() { * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the pushNotificationConfig field is set. */ @java.lang.Override @@ -117,7 +119,7 @@ public boolean hasPushNotificationConfig() { * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The pushNotificationConfig. */ @java.lang.Override @@ -129,7 +131,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { @@ -311,6 +313,8 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:TaskPushNotificationConfig]
+   * A container associating a push notification configuration with a specific
+   * task.
    * 
* * Protobuf type {@code a2a.v1.TaskPushNotificationConfig} @@ -486,7 +490,7 @@ public Builder mergeFrom( * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ public java.lang.String getName() { @@ -507,7 +511,7 @@ public java.lang.String getName() { * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ public com.google.protobuf.ByteString @@ -529,7 +533,7 @@ public java.lang.String getName() { * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The name to set. * @return This builder for chaining. */ @@ -547,7 +551,7 @@ public Builder setName( * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearName() { @@ -562,7 +566,7 @@ public Builder clearName() { * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for name to set. * @return This builder for chaining. */ @@ -584,7 +588,7 @@ public Builder setNameBytes( * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the pushNotificationConfig field is set. */ public boolean hasPushNotificationConfig() { @@ -595,7 +599,7 @@ public boolean hasPushNotificationConfig() { * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The pushNotificationConfig. */ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { @@ -610,7 +614,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { if (pushNotificationConfigBuilder_ == null) { @@ -630,7 +634,7 @@ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig valu * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setPushNotificationConfig( io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { @@ -648,7 +652,7 @@ public Builder setPushNotificationConfig( * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) { if (pushNotificationConfigBuilder_ == null) { @@ -673,7 +677,7 @@ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig va * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearPushNotificationConfig() { bitField0_ = (bitField0_ & ~0x00000002); @@ -690,7 +694,7 @@ public Builder clearPushNotificationConfig() { * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuilder() { bitField0_ |= 0x00000002; @@ -702,7 +706,7 @@ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuild * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder() { if (pushNotificationConfigBuilder_ != null) { @@ -717,7 +721,7 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java index f7d80a8e4..45f7d66b0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java @@ -16,7 +16,7 @@ public interface TaskPushNotificationConfigOrBuilder extends * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The name. */ java.lang.String getName(); @@ -26,7 +26,7 @@ public interface TaskPushNotificationConfigOrBuilder extends * Format: tasks/{task_id}/pushNotificationConfigs/{config_id} * * - * string name = 1; + * string name = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for name. */ com.google.protobuf.ByteString @@ -37,7 +37,7 @@ public interface TaskPushNotificationConfigOrBuilder extends * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the pushNotificationConfig field is set. */ boolean hasPushNotificationConfig(); @@ -46,7 +46,7 @@ public interface TaskPushNotificationConfigOrBuilder extends * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The pushNotificationConfig. */ io.a2a.grpc.PushNotificationConfig getPushNotificationConfig(); @@ -55,7 +55,7 @@ public interface TaskPushNotificationConfigOrBuilder extends * The push notification configuration details. * * - * .a2a.v1.PushNotificationConfig push_notification_config = 2; + * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java index 0ed32c6ad..b27f65903 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java @@ -54,10 +54,10 @@ private TaskStatus() { private int state_ = 0; /** *
-   * The current state of this task
+   * The current state of this task.
    * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The enum numeric value on the wire for state. */ @java.lang.Override public int getStateValue() { @@ -65,10 +65,10 @@ private TaskStatus() { } /** *
-   * The current state of this task
+   * The current state of this task.
    * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The state. */ @java.lang.Override public io.a2a.grpc.TaskState getState() { @@ -76,18 +76,18 @@ private TaskStatus() { return result == null ? io.a2a.grpc.TaskState.UNRECOGNIZED : result; } - public static final int UPDATE_FIELD_NUMBER = 2; - private io.a2a.grpc.Message update_; + public static final int MESSAGE_FIELD_NUMBER = 2; + private io.a2a.grpc.Message message_; /** *
    * A message associated with the status.
    * 
* - * .a2a.v1.Message update = 2 [json_name = "message"]; - * @return Whether the update field is set. + * .a2a.v1.Message message = 2; + * @return Whether the message field is set. */ @java.lang.Override - public boolean hasUpdate() { + public boolean hasMessage() { return ((bitField0_ & 0x00000001) != 0); } /** @@ -95,30 +95,30 @@ public boolean hasUpdate() { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; - * @return The update. + * .a2a.v1.Message message = 2; + * @return The message. */ @java.lang.Override - public io.a2a.grpc.Message getUpdate() { - return update_ == null ? io.a2a.grpc.Message.getDefaultInstance() : update_; + public io.a2a.grpc.Message getMessage() { + return message_ == null ? io.a2a.grpc.Message.getDefaultInstance() : message_; } /** *
    * A message associated with the status.
    * 
* - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ @java.lang.Override - public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { - return update_ == null ? io.a2a.grpc.Message.getDefaultInstance() : update_; + public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() { + return message_ == null ? io.a2a.grpc.Message.getDefaultInstance() : message_; } public static final int TIMESTAMP_FIELD_NUMBER = 3; private com.google.protobuf.Timestamp timestamp_; /** *
-   * Timestamp when the status was recorded.
+   * ISO 8601 Timestamp when the status was recorded.
    * Example: "2023-10-27T10:00:00Z"
    * 
* @@ -131,7 +131,7 @@ public boolean hasTimestamp() { } /** *
-   * Timestamp when the status was recorded.
+   * ISO 8601 Timestamp when the status was recorded.
    * Example: "2023-10-27T10:00:00Z"
    * 
* @@ -144,7 +144,7 @@ public com.google.protobuf.Timestamp getTimestamp() { } /** *
-   * Timestamp when the status was recorded.
+   * ISO 8601 Timestamp when the status was recorded.
    * Example: "2023-10-27T10:00:00Z"
    * 
* @@ -173,7 +173,7 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) output.writeEnum(1, state_); } if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(2, getUpdate()); + output.writeMessage(2, getMessage()); } if (((bitField0_ & 0x00000002) != 0)) { output.writeMessage(3, getTimestamp()); @@ -193,7 +193,7 @@ public int getSerializedSize() { } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, getUpdate()); + .computeMessageSize(2, getMessage()); } if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream @@ -215,10 +215,10 @@ public boolean equals(final java.lang.Object obj) { io.a2a.grpc.TaskStatus other = (io.a2a.grpc.TaskStatus) obj; if (state_ != other.state_) return false; - if (hasUpdate() != other.hasUpdate()) return false; - if (hasUpdate()) { - if (!getUpdate() - .equals(other.getUpdate())) return false; + if (hasMessage() != other.hasMessage()) return false; + if (hasMessage()) { + if (!getMessage() + .equals(other.getMessage())) return false; } if (hasTimestamp() != other.hasTimestamp()) return false; if (hasTimestamp()) { @@ -238,9 +238,9 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + STATE_FIELD_NUMBER; hash = (53 * hash) + state_; - if (hasUpdate()) { - hash = (37 * hash) + UPDATE_FIELD_NUMBER; - hash = (53 * hash) + getUpdate().hashCode(); + if (hasMessage()) { + hash = (37 * hash) + MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getMessage().hashCode(); } if (hasTimestamp()) { hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER; @@ -381,7 +381,7 @@ private Builder( private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage .alwaysUseFieldBuilders) { - internalGetUpdateFieldBuilder(); + internalGetMessageFieldBuilder(); internalGetTimestampFieldBuilder(); } } @@ -390,10 +390,10 @@ public Builder clear() { super.clear(); bitField0_ = 0; state_ = 0; - update_ = null; - if (updateBuilder_ != null) { - updateBuilder_.dispose(); - updateBuilder_ = null; + message_ = null; + if (messageBuilder_ != null) { + messageBuilder_.dispose(); + messageBuilder_ = null; } timestamp_ = null; if (timestampBuilder_ != null) { @@ -438,9 +438,9 @@ private void buildPartial0(io.a2a.grpc.TaskStatus result) { } int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000002) != 0)) { - result.update_ = updateBuilder_ == null - ? update_ - : updateBuilder_.build(); + result.message_ = messageBuilder_ == null + ? message_ + : messageBuilder_.build(); to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000004) != 0)) { @@ -467,8 +467,8 @@ public Builder mergeFrom(io.a2a.grpc.TaskStatus other) { if (other.state_ != 0) { setStateValue(other.getStateValue()); } - if (other.hasUpdate()) { - mergeUpdate(other.getUpdate()); + if (other.hasMessage()) { + mergeMessage(other.getMessage()); } if (other.hasTimestamp()) { mergeTimestamp(other.getTimestamp()); @@ -506,7 +506,7 @@ public Builder mergeFrom( } // case 8 case 18: { input.readMessage( - internalGetUpdateFieldBuilder().getBuilder(), + internalGetMessageFieldBuilder().getBuilder(), extensionRegistry); bitField0_ |= 0x00000002; break; @@ -538,10 +538,10 @@ public Builder mergeFrom( private int state_ = 0; /** *
-     * The current state of this task
+     * The current state of this task.
      * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The enum numeric value on the wire for state. */ @java.lang.Override public int getStateValue() { @@ -549,10 +549,10 @@ public Builder mergeFrom( } /** *
-     * The current state of this task
+     * The current state of this task.
      * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The enum numeric value on the wire for state to set. * @return This builder for chaining. */ @@ -564,10 +564,10 @@ public Builder setStateValue(int value) { } /** *
-     * The current state of this task
+     * The current state of this task.
      * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The state. */ @java.lang.Override @@ -577,10 +577,10 @@ public io.a2a.grpc.TaskState getState() { } /** *
-     * The current state of this task
+     * The current state of this task.
      * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The state to set. * @return This builder for chaining. */ @@ -593,10 +593,10 @@ public Builder setState(io.a2a.grpc.TaskState value) { } /** *
-     * The current state of this task
+     * The current state of this task.
      * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearState() { @@ -606,18 +606,18 @@ public Builder clearState() { return this; } - private io.a2a.grpc.Message update_; + private io.a2a.grpc.Message message_; private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> updateBuilder_; + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> messageBuilder_; /** *
      * A message associated with the status.
      * 
* - * .a2a.v1.Message update = 2 [json_name = "message"]; - * @return Whether the update field is set. + * .a2a.v1.Message message = 2; + * @return Whether the message field is set. */ - public boolean hasUpdate() { + public boolean hasMessage() { return ((bitField0_ & 0x00000002) != 0); } /** @@ -625,14 +625,14 @@ public boolean hasUpdate() { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; - * @return The update. + * .a2a.v1.Message message = 2; + * @return The message. */ - public io.a2a.grpc.Message getUpdate() { - if (updateBuilder_ == null) { - return update_ == null ? io.a2a.grpc.Message.getDefaultInstance() : update_; + public io.a2a.grpc.Message getMessage() { + if (messageBuilder_ == null) { + return message_ == null ? io.a2a.grpc.Message.getDefaultInstance() : message_; } else { - return updateBuilder_.getMessage(); + return messageBuilder_.getMessage(); } } /** @@ -640,16 +640,16 @@ public io.a2a.grpc.Message getUpdate() { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder setUpdate(io.a2a.grpc.Message value) { - if (updateBuilder_ == null) { + public Builder setMessage(io.a2a.grpc.Message value) { + if (messageBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - update_ = value; + message_ = value; } else { - updateBuilder_.setMessage(value); + messageBuilder_.setMessage(value); } bitField0_ |= 0x00000002; onChanged(); @@ -660,14 +660,14 @@ public Builder setUpdate(io.a2a.grpc.Message value) { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder setUpdate( + public Builder setMessage( io.a2a.grpc.Message.Builder builderForValue) { - if (updateBuilder_ == null) { - update_ = builderForValue.build(); + if (messageBuilder_ == null) { + message_ = builderForValue.build(); } else { - updateBuilder_.setMessage(builderForValue.build()); + messageBuilder_.setMessage(builderForValue.build()); } bitField0_ |= 0x00000002; onChanged(); @@ -678,21 +678,21 @@ public Builder setUpdate( * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder mergeUpdate(io.a2a.grpc.Message value) { - if (updateBuilder_ == null) { + public Builder mergeMessage(io.a2a.grpc.Message value) { + if (messageBuilder_ == null) { if (((bitField0_ & 0x00000002) != 0) && - update_ != null && - update_ != io.a2a.grpc.Message.getDefaultInstance()) { - getUpdateBuilder().mergeFrom(value); + message_ != null && + message_ != io.a2a.grpc.Message.getDefaultInstance()) { + getMessageBuilder().mergeFrom(value); } else { - update_ = value; + message_ = value; } } else { - updateBuilder_.mergeFrom(value); + messageBuilder_.mergeFrom(value); } - if (update_ != null) { + if (message_ != null) { bitField0_ |= 0x00000002; onChanged(); } @@ -703,14 +703,14 @@ public Builder mergeUpdate(io.a2a.grpc.Message value) { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder clearUpdate() { + public Builder clearMessage() { bitField0_ = (bitField0_ & ~0x00000002); - update_ = null; - if (updateBuilder_ != null) { - updateBuilder_.dispose(); - updateBuilder_ = null; + message_ = null; + if (messageBuilder_ != null) { + messageBuilder_.dispose(); + messageBuilder_ = null; } onChanged(); return this; @@ -720,26 +720,26 @@ public Builder clearUpdate() { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public io.a2a.grpc.Message.Builder getUpdateBuilder() { + public io.a2a.grpc.Message.Builder getMessageBuilder() { bitField0_ |= 0x00000002; onChanged(); - return internalGetUpdateFieldBuilder().getBuilder(); + return internalGetMessageFieldBuilder().getBuilder(); } /** *
      * A message associated with the status.
      * 
* - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { - if (updateBuilder_ != null) { - return updateBuilder_.getMessageOrBuilder(); + public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() { + if (messageBuilder_ != null) { + return messageBuilder_.getMessageOrBuilder(); } else { - return update_ == null ? - io.a2a.grpc.Message.getDefaultInstance() : update_; + return message_ == null ? + io.a2a.grpc.Message.getDefaultInstance() : message_; } } /** @@ -747,20 +747,20 @@ public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> - internalGetUpdateFieldBuilder() { - if (updateBuilder_ == null) { - updateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + internalGetMessageFieldBuilder() { + if (messageBuilder_ == null) { + messageBuilder_ = new com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( - getUpdate(), + getMessage(), getParentForChildren(), isClean()); - update_ = null; + message_ = null; } - return updateBuilder_; + return messageBuilder_; } private com.google.protobuf.Timestamp timestamp_; @@ -768,7 +768,7 @@ public io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder() { com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> timestampBuilder_; /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -780,7 +780,7 @@ public boolean hasTimestamp() { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -796,7 +796,7 @@ public com.google.protobuf.Timestamp getTimestamp() { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -817,7 +817,7 @@ public Builder setTimestamp(com.google.protobuf.Timestamp value) { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -836,7 +836,7 @@ public Builder setTimestamp( } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -862,7 +862,7 @@ public Builder mergeTimestamp(com.google.protobuf.Timestamp value) { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -880,7 +880,7 @@ public Builder clearTimestamp() { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -893,7 +893,7 @@ public com.google.protobuf.Timestamp.Builder getTimestampBuilder() { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* @@ -909,7 +909,7 @@ public com.google.protobuf.TimestampOrBuilder getTimestampOrBuilder() { } /** *
-     * Timestamp when the status was recorded.
+     * ISO 8601 Timestamp when the status was recorded.
      * Example: "2023-10-27T10:00:00Z"
      * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java index 4f5a1670a..bf75690b7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java @@ -12,19 +12,19 @@ public interface TaskStatusOrBuilder extends /** *
-   * The current state of this task
+   * The current state of this task.
    * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The enum numeric value on the wire for state. */ int getStateValue(); /** *
-   * The current state of this task
+   * The current state of this task.
    * 
* - * .a2a.v1.TaskState state = 1; + * .a2a.v1.TaskState state = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The state. */ io.a2a.grpc.TaskState getState(); @@ -34,31 +34,31 @@ public interface TaskStatusOrBuilder extends * A message associated with the status. * * - * .a2a.v1.Message update = 2 [json_name = "message"]; - * @return Whether the update field is set. + * .a2a.v1.Message message = 2; + * @return Whether the message field is set. */ - boolean hasUpdate(); + boolean hasMessage(); /** *
    * A message associated with the status.
    * 
* - * .a2a.v1.Message update = 2 [json_name = "message"]; - * @return The update. + * .a2a.v1.Message message = 2; + * @return The message. */ - io.a2a.grpc.Message getUpdate(); + io.a2a.grpc.Message getMessage(); /** *
    * A message associated with the status.
    * 
* - * .a2a.v1.Message update = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - io.a2a.grpc.MessageOrBuilder getUpdateOrBuilder(); + io.a2a.grpc.MessageOrBuilder getMessageOrBuilder(); /** *
-   * Timestamp when the status was recorded.
+   * ISO 8601 Timestamp when the status was recorded.
    * Example: "2023-10-27T10:00:00Z"
    * 
* @@ -68,7 +68,7 @@ public interface TaskStatusOrBuilder extends boolean hasTimestamp(); /** *
-   * Timestamp when the status was recorded.
+   * ISO 8601 Timestamp when the status was recorded.
    * Example: "2023-10-27T10:00:00Z"
    * 
* @@ -78,7 +78,7 @@ public interface TaskStatusOrBuilder extends com.google.protobuf.Timestamp getTimestamp(); /** *
-   * Timestamp when the status was recorded.
+   * ISO 8601 Timestamp when the status was recorded.
    * Example: "2023-10-27T10:00:00Z"
    * 
* diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java index ddf896c3e..97448c339 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java @@ -8,8 +8,8 @@ /** *
  * --8<-- [start:TaskStatusUpdateEvent]
- * TaskStatusUpdateEvent is a delta even on a task indicating that a task
- * has changed.
+ * An event sent by the agent to notify the client of a change in a task's
+ * status.
  * 
* * Protobuf type {@code a2a.v1.TaskStatusUpdateEvent} @@ -60,7 +60,7 @@ private TaskStatusUpdateEvent() { * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The taskId. */ @java.lang.Override @@ -81,7 +81,7 @@ public java.lang.String getTaskId() { * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for taskId. */ @java.lang.Override @@ -107,7 +107,7 @@ public java.lang.String getTaskId() { * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ @java.lang.Override @@ -128,7 +128,7 @@ public java.lang.String getContextId() { * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ @java.lang.Override @@ -153,7 +153,7 @@ public java.lang.String getContextId() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the status field is set. */ @java.lang.Override @@ -165,7 +165,7 @@ public boolean hasStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The status. */ @java.lang.Override @@ -177,7 +177,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { @@ -188,10 +188,10 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { private boolean final_ = false; /** *
-   * Whether this is the last status update expected for this task.
+   * If true, this is the final event in the stream for this interaction.
    * 
* - * bool final = 4; + * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The final. */ @java.lang.Override @@ -450,8 +450,8 @@ protected Builder newBuilderForType( /** *
    * --8<-- [start:TaskStatusUpdateEvent]
-   * TaskStatusUpdateEvent is a delta even on a task indicating that a task
-   * has changed.
+   * An event sent by the agent to notify the client of a change in a task's
+   * status.
    * 
* * Protobuf type {@code a2a.v1.TaskStatusUpdateEvent} @@ -674,7 +674,7 @@ public Builder mergeFrom( * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The taskId. */ public java.lang.String getTaskId() { @@ -694,7 +694,7 @@ public java.lang.String getTaskId() { * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for taskId. */ public com.google.protobuf.ByteString @@ -715,7 +715,7 @@ public java.lang.String getTaskId() { * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The taskId to set. * @return This builder for chaining. */ @@ -732,7 +732,7 @@ public Builder setTaskId( * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTaskId() { @@ -746,7 +746,7 @@ public Builder clearTaskId() { * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for taskId to set. * @return This builder for chaining. */ @@ -766,7 +766,7 @@ public Builder setTaskIdBytes( * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ public java.lang.String getContextId() { @@ -786,7 +786,7 @@ public java.lang.String getContextId() { * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ public com.google.protobuf.ByteString @@ -807,7 +807,7 @@ public java.lang.String getContextId() { * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The contextId to set. * @return This builder for chaining. */ @@ -824,7 +824,7 @@ public Builder setContextId( * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearContextId() { @@ -838,7 +838,7 @@ public Builder clearContextId() { * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for contextId to set. * @return This builder for chaining. */ @@ -860,7 +860,7 @@ public Builder setContextIdBytes( * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the status field is set. */ public boolean hasStatus() { @@ -871,7 +871,7 @@ public boolean hasStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The status. */ public io.a2a.grpc.TaskStatus getStatus() { @@ -886,7 +886,7 @@ public io.a2a.grpc.TaskStatus getStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -906,7 +906,7 @@ public Builder setStatus(io.a2a.grpc.TaskStatus value) { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setStatus( io.a2a.grpc.TaskStatus.Builder builderForValue) { @@ -924,7 +924,7 @@ public Builder setStatus( * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { if (statusBuilder_ == null) { @@ -949,7 +949,7 @@ public Builder mergeStatus(io.a2a.grpc.TaskStatus value) { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearStatus() { bitField0_ = (bitField0_ & ~0x00000004); @@ -966,7 +966,7 @@ public Builder clearStatus() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { bitField0_ |= 0x00000004; @@ -978,7 +978,7 @@ public io.a2a.grpc.TaskStatus.Builder getStatusBuilder() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { if (statusBuilder_ != null) { @@ -993,7 +993,7 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatus, io.a2a.grpc.TaskStatus.Builder, io.a2a.grpc.TaskStatusOrBuilder> @@ -1012,10 +1012,10 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { private boolean final_ ; /** *
-     * Whether this is the last status update expected for this task.
+     * If true, this is the final event in the stream for this interaction.
      * 
* - * bool final = 4; + * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The final. */ @java.lang.Override @@ -1024,10 +1024,10 @@ public boolean getFinal() { } /** *
-     * Whether this is the last status update expected for this task.
+     * If true, this is the final event in the stream for this interaction.
      * 
* - * bool final = 4; + * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; * @param value The final to set. * @return This builder for chaining. */ @@ -1040,10 +1040,10 @@ public Builder setFinal(boolean value) { } /** *
-     * Whether this is the last status update expected for this task.
+     * If true, this is the final event in the stream for this interaction.
      * 
* - * bool final = 4; + * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearFinal() { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java index 264b19295..56d479da1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java @@ -15,7 +15,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The taskId. */ java.lang.String getTaskId(); @@ -24,7 +24,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the task that is changed * * - * string task_id = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for taskId. */ com.google.protobuf.ByteString @@ -35,7 +35,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The contextId. */ java.lang.String getContextId(); @@ -44,7 +44,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The id of the context that the task belongs to * * - * string context_id = 2; + * string context_id = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for contextId. */ com.google.protobuf.ByteString @@ -55,7 +55,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the status field is set. */ boolean hasStatus(); @@ -64,7 +64,7 @@ public interface TaskStatusUpdateEventOrBuilder extends * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; * @return The status. */ io.a2a.grpc.TaskStatus getStatus(); @@ -73,16 +73,16 @@ public interface TaskStatusUpdateEventOrBuilder extends * The new status of the task. * * - * .a2a.v1.TaskStatus status = 3; + * .a2a.v1.TaskStatus status = 3 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder(); /** *
-   * Whether this is the last status update expected for this task.
+   * If true, this is the final event in the stream for this interaction.
    * 
* - * bool final = 4; + * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; * @return The final. */ boolean getFinal(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java new file mode 100644 index 000000000..cb3a05ade --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java @@ -0,0 +1,351 @@ +package io.a2a.grpc.mapper; + +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.protobuf.Struct; +import com.google.protobuf.Timestamp; +import com.google.protobuf.Value; +import java.util.Collections; +import org.mapstruct.Mapper; +import org.mapstruct.Named; + +/** + * Common field mapping utilities shared across all mappers. + *

+ * Provides reusable conversion methods for common protobuf ↔ domain transformations: + *

    + *
  • Empty string → null conversion (protobuf optional string defaults)
  • + *
  • Timestamp conversions (OffsetDateTime ↔ Protobuf Timestamp, Instant ↔ millis)
  • + *
  • Metadata conversions (Map ↔ Protobuf Struct)
  • + *
  • Empty list → null conversion (protobuf repeated field defaults)
  • + *
  • Zero/false → null conversion (protobuf optional numeric/bool defaults)
  • + *
  • Enum → null conversion (protobuf UNSPECIFIED/UNKNOWN handling)
  • + *
+ */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {TaskStateMapper.class}) +public interface A2ACommonFieldMapper { + + A2ACommonFieldMapper INSTANCE = A2AMappers.getMapper(A2ACommonFieldMapper.class); + + /** + * Converts protobuf empty strings to null for optional fields. + *

+ * Protobuf optional strings return "" when unset, but domain models use null. + * Use this with {@code @Mapping(qualifiedByName = "emptyToNull")}. + * + * @param value the protobuf string value + * @return null if empty/null, otherwise the value + */ + @Named("emptyToNull") + default String emptyToNull(String value) { + return (value == null || value.isEmpty()) ? null : value; + } + + /** + * Converts null strings to empty strings for protobuf. + *

+ * Domain models use null for optional fields, but protobuf uses "". + * Use this with {@code @Mapping(qualifiedByName = "nullToEmpty")}. + * + * @param value the domain string value + * @return "" if null, otherwise the value + */ + @Named("nullToEmpty") + default String nullToEmpty(String value) { + return value == null ? "" : value; + } + + /** + * Converts domain OffsetDateTime to protobuf Timestamp. + *

+ * Use this with {@code @Mapping(qualifiedByName = "offsetDateTimeToProtoTimestamp")}. + * + * @param dateTime the domain OffsetDateTime + * @return protobuf Timestamp, or default instance if input is null + */ + @Named("offsetDateTimeToProtoTimestamp") + default Timestamp offsetDateTimeToProtoTimestamp(OffsetDateTime dateTime) { + if (dateTime == null) { + return Timestamp.getDefaultInstance(); + } + Instant instant = dateTime.toInstant(); + return Timestamp.newBuilder() + .setSeconds(instant.getEpochSecond()) + .setNanos(instant.getNano()) + .build(); + } + + /** + * Converts protobuf Timestamp to domain OffsetDateTime (UTC). + *

+ * Use this with {@code @Mapping(qualifiedByName = "protoTimestampToOffsetDateTime")}. + * + * @param timestamp the protobuf Timestamp + * @return OffsetDateTime in UTC, or null if input is null/default + */ + @Named("protoTimestampToOffsetDateTime") + default OffsetDateTime protoTimestampToOffsetDateTime(Timestamp timestamp) { + if (timestamp == null || timestamp.equals(Timestamp.getDefaultInstance())) { + return null; + } + return OffsetDateTime.ofInstant( + Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()), + ZoneOffset.UTC + ); + } + + /** + * Converts empty lists to null for optional list fields. + *

+ * Protobuf repeated fields return empty list when unset, but domain models may use null. + * Use this with {@code @Mapping(qualifiedByName = "emptyListToNull")}. + * + * @param list the protobuf list + * @return null if empty/null, otherwise the list + */ + @Named("emptyListToNull") + default java.util.List emptyListToNull(java.util.List list) { + return (list == null || list.isEmpty()) ? null : list; + } + + /** + * Converts domain Map to protobuf Struct (generic conversion). + *

+ * Used for any Map field that maps to protobuf Struct (header, params, etc.). + * Use this with {@code @Mapping(qualifiedByName = "mapToStruct")}. + * + * @param map the domain map + * @return protobuf Struct, or default instance if input is null + */ + @Named("mapToStruct") + default Struct mapToStruct(Map map) { + if (map == null) { + return Struct.getDefaultInstance(); + } + Struct.Builder structBuilder = Struct.newBuilder(); + map.forEach((k, v) -> structBuilder.putFields(k, objectToValue(v))); + return structBuilder.build(); + } + + /** + * Converts protobuf Struct to domain Map (generic conversion). + *

+ * Used for any protobuf Struct field that maps to Map (header, params, etc.). + * Use this with {@code @Mapping(qualifiedByName = "structToMap")}. + * + * @param struct the protobuf Struct + * @return domain Map (may be null for empty Struct) + */ + @Named("structToMap") + default Map structToMap(Struct struct) { + if (struct == null || struct.getFieldsCount() == 0) { + return null; + } + return struct.getFieldsMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> valueToObject(e.getValue()))); + } + + /** + * Converts a Java Object to protobuf Value. + *

+ * Supports String, Number, Boolean, Map, and List types. + * Used internally for struct conversion. + * + * @param value the Java object + * @return protobuf Value + */ + private Value objectToValue(Object value) { + Value.Builder valueBuilder = Value.newBuilder(); + if (value instanceof String) { + valueBuilder.setStringValue((String) value); + } else if (value instanceof Number) { + valueBuilder.setNumberValue(((Number) value).doubleValue()); + } else if (value instanceof Boolean) { + valueBuilder.setBoolValue((Boolean) value); + } else if (value instanceof Map) { + valueBuilder.setStructValue(mapToStruct((Map) value)); + } else if (value instanceof List) { + valueBuilder.setListValue(listToListValue((List) value)); + } + return valueBuilder.build(); + } + + /** + * Converts protobuf Value to Java Object. + *

+ * Returns appropriate Java type based on Value's kind. + * Used internally for struct conversion. + * + * @param value the protobuf Value + * @return Java object (String, Double, Boolean, Map, List, or null) + */ + private Object valueToObject(Value value) { + switch (value.getKindCase()) { + case STRUCT_VALUE: + return structToMap(value.getStructValue()); + case LIST_VALUE: + return value.getListValue().getValuesList().stream() + .map(this::valueToObject) + .collect(Collectors.toList()); + case BOOL_VALUE: + return value.getBoolValue(); + case NUMBER_VALUE: + return value.getNumberValue(); + case STRING_VALUE: + return value.getStringValue(); + case NULL_VALUE: + default: + return null; + } + } + + /** + * Converts Java List to protobuf ListValue. + *

+ * Used internally for struct conversion. + * + * @param list the Java list + * @return protobuf ListValue + */ + private com.google.protobuf.ListValue listToListValue(List list) { + com.google.protobuf.ListValue.Builder listValueBuilder = com.google.protobuf.ListValue.newBuilder(); + if (list != null) { + list.forEach(o -> listValueBuilder.addValues(objectToValue(o))); + } + return listValueBuilder.build(); + } + + /** + * Converts domain metadata Map to protobuf Struct. + *

+ * Used for metadata fields in Artifact, Message, Task, and Events. + * Use this with {@code @Mapping(qualifiedByName = "metadataToProto")}. + * + * @param metadata the domain metadata map + * @return protobuf Struct, or default instance if input is null + */ + @Named("metadataToProto") + default Struct metadataToProto(Map metadata) { + return mapToStruct(metadata); + } + + /** + * Converts protobuf Struct to domain metadata Map. + *

+ * Used for metadata fields in Artifact, Message, Task, and Events. + * Use this with {@code @Mapping(qualifiedByName = "metadataFromProto")}. + * + * @param struct the protobuf Struct + * @return domain metadata Map (may be null for empty Struct) + */ + @Named("metadataFromProto") + default Map metadataFromProto(Struct struct) { + if (struct == null || struct.getFieldsCount() == 0) { + return Collections.emptyMap(); + } + return structToMap(struct); + } + + // ======================================================================== + // Optional Numeric/Boolean Conversions + // ======================================================================== + /** + * Converts protobuf int to Integer, treating 0 as null (unset). + *

+ * Protobuf optional int32 fields default to 0 when unset, but domain models use null. + * Use this with {@code @Mapping(qualifiedByName = "zeroToNull")}. + * + * @param value the protobuf int value + * @return Integer or null if value is 0 + */ + @Named("zeroToNull") + default Integer zeroToNull(int value) { + return value > 0 ? value : null; + } + + /** + * Converts protobuf long to Long, treating 0 as null (unset). + *

+ * Protobuf optional int64 fields default to 0 when unset, but domain models use null. + * Use this with {@code @Mapping(qualifiedByName = "zeroLongToNull")}. + * + * @param value the protobuf long value + * @return Long or null if value is 0 + */ + @Named("zeroLongToNull") + default Long zeroLongToNull(long value) { + return value > 0L ? value : null; + } + + /** + * Converts protobuf bool to Boolean, treating false as null (unset). + *

+ * Protobuf optional bool fields default to false when unset, but domain models use null. + * Use this with {@code @Mapping(qualifiedByName = "falseToNull")}. + * + * @param value the protobuf bool value + * @return Boolean or null if value is false + */ + @Named("falseToNull") + default Boolean falseToNull(boolean value) { + return value ? true : null; + } + + // ======================================================================== + // Instant ↔ Millis Conversions (for int64 timestamp fields) + // ======================================================================== + /** + * Converts domain Instant to protobuf milliseconds-since-epoch (int64). + *

+ * Returns 0 if input is null (protobuf default for unset int64). + * Use this with {@code @Mapping(qualifiedByName = "instantToMillis")}. + * + * @param instant the domain Instant + * @return milliseconds since epoch, or 0 if null + */ + @Named("instantToMillis") + default long instantToMillis(Instant instant) { + return instant != null ? instant.toEpochMilli() : 0L; + } + + /** + * Converts protobuf milliseconds-since-epoch (int64) to domain Instant. + *

+ * Returns null if input is 0 (protobuf default for unset field). + * Use this with {@code @Mapping(qualifiedByName = "millisToInstant")}. + * + * @param millis milliseconds since epoch + * @return domain Instant, or null if millis is 0 + */ + @Named("millisToInstant") + default Instant millisToInstant(long millis) { + return millis > 0L ? Instant.ofEpochMilli(millis) : null; + } + + // ======================================================================== + // Enum Conversions (handling UNSPECIFIED/UNKNOWN) + // ======================================================================== + /** + * Converts protobuf TaskState to domain TaskState, treating UNSPECIFIED/UNKNOWN as null. + *

+ * Protobuf enums default to UNSPECIFIED (0 value) when unset. The domain may also have + * UNKNOWN for unparseable values. Both should map to null for optional fields. + * Use this with {@code @Mapping(qualifiedByName = "taskStateOrNull")}. + * + * @param state the protobuf TaskState + * @return domain TaskState or null if UNSPECIFIED/UNKNOWN + */ + @Named("taskStateOrNull") + default io.a2a.spec.TaskState taskStateOrNull(io.a2a.grpc.TaskState state) { + if (state == null || state == io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED) { + return null; + } + io.a2a.spec.TaskState result = TaskStateMapper.INSTANCE.fromProto(state); + return result == io.a2a.spec.TaskState.UNKNOWN ? null : result; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AMappers.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AMappers.java new file mode 100644 index 000000000..25535b158 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AMappers.java @@ -0,0 +1,27 @@ +package io.a2a.grpc.mapper; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class A2AMappers { + private static final String IMPLEMENTATION_SUFFIX = "Impl"; + + static T getMapper(Class iface) { + try { + @SuppressWarnings( "unchecked" ) + Class implementation = + (Class) iface.getClassLoader().loadClass( iface.getName() + IMPLEMENTATION_SUFFIX ); + Constructor constructor = implementation.getDeclaredConstructor(); + constructor.setAccessible( true ); + + return constructor.newInstance(); + + } catch (ClassNotFoundException | + NoSuchMethodException | + InstantiationException | + IllegalAccessException | + InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AProtoMapperConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AProtoMapperConfig.java new file mode 100644 index 000000000..dd7e77084 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2AProtoMapperConfig.java @@ -0,0 +1,195 @@ +package io.a2a.grpc.mapper; + +import java.nio.ByteBuffer; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; +import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; +import com.google.protobuf.ListValue; +import com.google.protobuf.StringValue; +import com.google.protobuf.Struct; +import com.google.protobuf.Timestamp; +import com.google.protobuf.Value; +import org.mapstruct.MapperConfig; +import org.mapstruct.NullValuePropertyMappingStrategy; +import org.mapstruct.ReportingPolicy; + +@MapperConfig( + // 1. FAIL THE BUILD if fields are missing in either Spec or Proto + unmappedTargetPolicy = ReportingPolicy.ERROR, + + // 2. Use the default component model (Singleton instance pattern) + componentModel = "default", + + // 3. IGNORE null values when mapping to protobuf (builders don't accept null) + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE +) +public interface A2AProtoMapperConfig { + + // ======================================================================== + // 1. Enum Conversions + // ======================================================================== + + default String map(io.a2a.spec.APIKeySecurityScheme.Location location) { + return location == null ? null : location.asString(); + } + + // ======================================================================== + // 2. Time & Duration + // ======================================================================== + + default Instant map(Timestamp value) { + if (value == null) return null; + return Instant.ofEpochSecond(value.getSeconds(), value.getNanos()); + } + + default Timestamp map(Instant value) { + if (value == null) return null; + return Timestamp.newBuilder() + .setSeconds(value.getEpochSecond()) + .setNanos(value.getNano()) + .build(); + } + + default java.time.Duration map(com.google.protobuf.Duration value) { + if (value == null) return null; + return java.time.Duration.ofSeconds(value.getSeconds(), value.getNanos()); + } + + default com.google.protobuf.Duration map(java.time.Duration value) { + if (value == null) return null; + return com.google.protobuf.Duration.newBuilder() + .setSeconds(value.getSeconds()) + .setNanos(value.getNano()) + .build(); + } + + // ======================================================================== + // 3. Binary Data (ByteString) + // ======================================================================== + + default byte[] map(ByteString value) { + return value == null ? null : value.toByteArray(); + } + + default ByteString map(byte[] value) { + return value == null ? null : ByteString.copyFrom(value); + } + + default ByteBuffer mapToBuffer(ByteString value) { + return value == null ? null : value.asReadOnlyByteBuffer(); + } + + default ByteString mapFromBuffer(ByteBuffer value) { + return value == null ? null : ByteString.copyFrom(value); + } + + // ======================================================================== + // 4. Nullable Wrappers (Google Wrappers) + // ======================================================================== + + // String + default String map(StringValue value) { + return value == null ? null : value.getValue(); + } + default StringValue mapString(String value) { + return value == null ? null : StringValue.of(value); + } + + // Integer + default Integer map(Int32Value value) { + return value == null ? null : value.getValue(); + } + default Int32Value mapInt(Integer value) { + return value == null ? null : Int32Value.of(value); + } + + // Long + default Long map(Int64Value value) { + return value == null ? null : value.getValue(); + } + default Int64Value mapLong(Long value) { + return value == null ? null : Int64Value.of(value); + } + + // Boolean + default Boolean map(BoolValue value) { + return value == null ? null : value.getValue(); + } + default BoolValue mapBool(Boolean value) { + return value == null ? null : BoolValue.of(value); + } + + // ======================================================================== + // 5. JSON-RPC Support (Struct & Value) + // Maps "Struct" -> Map + // Maps "Value" -> Object + // ======================================================================== + + default Map map(Struct struct) { + if (struct == null) return null; + Map map = new HashMap<>(); + for (Map.Entry entry : struct.getFieldsMap().entrySet()) { + map.put(entry.getKey(), map(entry.getValue())); + } + return map; + } + + default Struct mapStruct(Map map) { + if (map == null) return null; + Struct.Builder builder = Struct.newBuilder(); + for (Map.Entry entry : map.entrySet()) { + builder.putFields(entry.getKey(), mapValue(entry.getValue())); + } + return builder.build(); + } + + default Object map(Value value) { + if (value == null) return null; + switch (value.getKindCase()) { + case NULL_VALUE: return null; + case NUMBER_VALUE: return value.getNumberValue(); // Returns Double + case STRING_VALUE: return value.getStringValue(); + case BOOL_VALUE: return value.getBoolValue(); + case STRUCT_VALUE: return map(value.getStructValue()); + case LIST_VALUE: + return value.getListValue().getValuesList().stream() + .map(this::map) + .collect(Collectors.toList()); + default: return null; + } + } + + default Value mapValue(Object object) { + if (object == null) { + return Value.newBuilder().setNullValue(com.google.protobuf.NullValue.NULL_VALUE).build(); + } + + if (object instanceof String) return Value.newBuilder().setStringValue((String) object).build(); + if (object instanceof Boolean) return Value.newBuilder().setBoolValue((Boolean) object).build(); + if (object instanceof Number) return Value.newBuilder().setNumberValue(((Number) object).doubleValue()).build(); + if (object instanceof Map) { + // Unchecked cast is unavoidable here unless we force Map + @SuppressWarnings("unchecked") + Map map = (Map) object; + return Value.newBuilder().setStructValue(mapStruct(map)).build(); + } + if (object instanceof List) { + ListValue.Builder listBuilder = ListValue.newBuilder(); + for (Object item : (List) object) { + listBuilder.addValues(mapValue(item)); + } + return Value.newBuilder().setListValue(listBuilder).build(); + } + + // Fallback for unknown types (e.g. custom objects inside Map) -> convert to String? + // For now, throw to catch unexpected types early + throw new IllegalArgumentException("Unsupported type for Proto Value conversion: " + object.getClass().getName()); + } +} \ No newline at end of file diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java new file mode 100644 index 000000000..8dd44a18a --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java @@ -0,0 +1,31 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.APIKeySecurityScheme} and {@link io.a2a.grpc.APIKeySecurityScheme}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface APIKeySecuritySchemeMapper { + + APIKeySecuritySchemeMapper INSTANCE = A2AMappers.getMapper(APIKeySecuritySchemeMapper.class); + + // location enum is converted to string via ProtoMapperConfig.map(Location) + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + io.a2a.grpc.APIKeySecurityScheme toProto(io.a2a.spec.APIKeySecurityScheme domain); + + default io.a2a.spec.APIKeySecurityScheme fromProto(io.a2a.grpc.APIKeySecurityScheme proto) { + if (proto == null) { + return null; + } + + io.a2a.spec.APIKeySecurityScheme.Location location = + io.a2a.spec.APIKeySecurityScheme.Location.fromString(proto.getLocation()); + String description = proto.getDescription().isEmpty() ? null : proto.getDescription(); + + return new io.a2a.spec.APIKeySecurityScheme(location, proto.getName(), description); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java new file mode 100644 index 000000000..a8216fec8 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java @@ -0,0 +1,19 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.AgentCapabilities} and {@link io.a2a.grpc.AgentCapabilities}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {AgentExtensionMapper.class}) +public interface AgentCapabilitiesMapper { + + AgentCapabilitiesMapper INSTANCE = A2AMappers.getMapper(AgentCapabilitiesMapper.class); + + io.a2a.grpc.AgentCapabilities toProto(io.a2a.spec.AgentCapabilities domain); + + io.a2a.spec.AgentCapabilities fromProto(io.a2a.grpc.AgentCapabilities proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java new file mode 100644 index 000000000..9f9f36ad7 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java @@ -0,0 +1,37 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.AgentCard} and {@link io.a2a.grpc.AgentCard}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = { + AgentProviderMapper.class, + AgentCapabilitiesMapper.class, + AgentSkillMapper.class, + SecuritySchemeMapper.class, + SecurityMapper.class, + AgentInterfaceMapper.class, + AgentCardSignatureMapper.class + }) +public interface AgentCardMapper { + + AgentCardMapper INSTANCE = A2AMappers.getMapper(AgentCardMapper.class); + + @Mapping(target = "provider", source = "provider", conditionExpression = "java(domain.provider() != null)") + @Mapping(target = "documentationUrl", source = "documentationUrl", conditionExpression = "java(domain.documentationUrl() != null)") + @Mapping(target = "iconUrl", source = "iconUrl", conditionExpression = "java(domain.iconUrl() != null)") + @Mapping(target = "url", ignore = true) // Deprecated in proto, derived from supportedInterfaces[0] + @Mapping(target = "preferredTransport", ignore = true) // Deprecated in proto, derived from supportedInterfaces[0] + @Mapping(target = "additionalInterfaces", ignore = true) // Deprecated in proto, use supportedInterfaces instead + io.a2a.grpc.AgentCard toProto(io.a2a.spec.AgentCard domain); + + @Mapping(target = "provider", source = "provider", conditionExpression = "java(proto.hasProvider())") + @Mapping(target = "documentationUrl", source = "documentationUrl", conditionExpression = "java(!proto.getDocumentationUrl().isEmpty())") + @Mapping(target = "iconUrl", source = "iconUrl", conditionExpression = "java(!proto.getIconUrl().isEmpty())") + io.a2a.spec.AgentCard fromProto(io.a2a.grpc.AgentCard proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardSignatureMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardSignatureMapper.java new file mode 100644 index 000000000..b8b83d35b --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardSignatureMapper.java @@ -0,0 +1,36 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.AgentCardSignature} and {@link io.a2a.grpc.AgentCardSignature}. + *

+ * Uses CommonFieldMapper for struct conversion (header field). + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {A2ACommonFieldMapper.class}) +public interface AgentCardSignatureMapper { + + AgentCardSignatureMapper INSTANCE = A2AMappers.getMapper(AgentCardSignatureMapper.class); + + /** + * Converts domain AgentCardSignature to proto AgentCardSignature. + *

+ * Maps protectedHeader → protected field and header via struct conversion. + */ + @Mapping(source = "protectedHeader", target = "protected") + @Mapping(target = "header", source = "header", conditionExpression = "java(domain.header() != null)", qualifiedByName = "mapToStruct") + io.a2a.grpc.AgentCardSignature toProto(io.a2a.spec.AgentCardSignature domain); + + /** + * Converts proto AgentCardSignature to domain AgentCardSignature. + *

+ * Maps protected field → protectedHeader and header from struct to map. + */ + @Mapping(source = "protected", target = "protectedHeader") + @Mapping(target = "header", source = "header", qualifiedByName = "structToMap") + io.a2a.spec.AgentCardSignature fromProto(io.a2a.grpc.AgentCardSignature proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentExtensionMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentExtensionMapper.java new file mode 100644 index 000000000..c99c947c6 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentExtensionMapper.java @@ -0,0 +1,34 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.AgentExtension} and {@link io.a2a.grpc.AgentExtension}. + *

+ * Uses CommonFieldMapper for struct conversion (params field). + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {A2ACommonFieldMapper.class}) +public interface AgentExtensionMapper { + + AgentExtensionMapper INSTANCE = A2AMappers.getMapper(AgentExtensionMapper.class); + + /** + * Converts domain AgentExtension to proto AgentExtension. + *

+ * Maps params field via struct conversion. + */ + @Mapping(target = "params", source = "params", conditionExpression = "java(domain.params() != null)", qualifiedByName = "mapToStruct") + io.a2a.grpc.AgentExtension toProto(io.a2a.spec.AgentExtension domain); + + /** + * Converts proto AgentExtension to domain AgentExtension. + *

+ * Maps params field from struct to map. + */ + @Mapping(target = "params", source = "params", qualifiedByName = "structToMap") + io.a2a.spec.AgentExtension fromProto(io.a2a.grpc.AgentExtension proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentInterfaceMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentInterfaceMapper.java new file mode 100644 index 000000000..383afb3a1 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentInterfaceMapper.java @@ -0,0 +1,18 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.AgentInterface} and {@link io.a2a.grpc.AgentInterface}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface AgentInterfaceMapper { + + AgentInterfaceMapper INSTANCE = A2AMappers.getMapper(AgentInterfaceMapper.class); + + io.a2a.grpc.AgentInterface toProto(io.a2a.spec.AgentInterface domain); + + io.a2a.spec.AgentInterface fromProto(io.a2a.grpc.AgentInterface proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentProviderMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentProviderMapper.java new file mode 100644 index 000000000..80766eb72 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentProviderMapper.java @@ -0,0 +1,18 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.AgentProvider} and {@link io.a2a.grpc.AgentProvider}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface AgentProviderMapper { + + AgentProviderMapper INSTANCE = A2AMappers.getMapper(AgentProviderMapper.class); + + io.a2a.grpc.AgentProvider toProto(io.a2a.spec.AgentProvider domain); + + io.a2a.spec.AgentProvider fromProto(io.a2a.grpc.AgentProvider proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentSkillMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentSkillMapper.java new file mode 100644 index 000000000..daa911867 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentSkillMapper.java @@ -0,0 +1,19 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.AgentSkill} and {@link io.a2a.grpc.AgentSkill}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = SecurityMapper.class) +public interface AgentSkillMapper { + + AgentSkillMapper INSTANCE = A2AMappers.getMapper(AgentSkillMapper.class); + + io.a2a.grpc.AgentSkill toProto(io.a2a.spec.AgentSkill domain); + + io.a2a.spec.AgentSkill fromProto(io.a2a.grpc.AgentSkill proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ArtifactMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ArtifactMapper.java new file mode 100644 index 000000000..b5efe8739 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ArtifactMapper.java @@ -0,0 +1,41 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.Artifact; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.Artifact} and {@link io.a2a.grpc.Artifact}. + *

+ * Uses ADDER_PREFERRED strategy to use addAllExtensions() method instead of + * trying to instantiate ProtocolStringList. Enables full compile-time validation! + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {PartMapper.class, A2ACommonFieldMapper.class}) +public interface ArtifactMapper { + + ArtifactMapper INSTANCE = A2AMappers.getMapper(ArtifactMapper.class); + + /** + * Converts domain Artifact to proto Artifact. + * ADDER_PREFERRED strategy ensures MapStruct uses addAllExtensions() method. + */ + @Mapping(target = "artifactId", source = "artifactId", conditionExpression = "java(domain.artifactId() != null)") + @Mapping(target = "name", source = "name", conditionExpression = "java(domain.name() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.description() != null)") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + io.a2a.grpc.Artifact toProto(Artifact domain); + + /** + * Converts proto Artifact to domain Artifact. + * Handles empty string → null and Struct conversions via CommonFieldMapper. + */ + @Mapping(target = "artifactId", source = "artifactId", qualifiedByName = "emptyToNull") + @Mapping(target = "name", source = "name", qualifiedByName = "emptyToNull") + @Mapping(target = "description", source = "description", qualifiedByName = "emptyToNull") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + @Mapping(target = "extensions", source = "extensions", qualifiedByName = "emptyListToNull") + Artifact fromProto(io.a2a.grpc.Artifact proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthenticationInfoMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthenticationInfoMapper.java new file mode 100644 index 000000000..8d6db42ea --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthenticationInfoMapper.java @@ -0,0 +1,20 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.AuthenticationInfo} and {@link io.a2a.grpc.AuthenticationInfo}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface AuthenticationInfoMapper { + + AuthenticationInfoMapper INSTANCE = A2AMappers.getMapper(AuthenticationInfoMapper.class); + + @Mapping(target = "credentials", source = "credentials", conditionExpression = "java(domain.credentials() != null)") + io.a2a.grpc.AuthenticationInfo toProto(io.a2a.spec.AuthenticationInfo domain); + + io.a2a.spec.AuthenticationInfo fromProto(io.a2a.grpc.AuthenticationInfo proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java new file mode 100644 index 000000000..09fdc3184 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java @@ -0,0 +1,18 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.AuthorizationCodeOAuthFlow} and {@link io.a2a.grpc.AuthorizationCodeOAuthFlow}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface AuthorizationCodeOAuthFlowMapper { + + AuthorizationCodeOAuthFlowMapper INSTANCE = A2AMappers.getMapper(AuthorizationCodeOAuthFlowMapper.class); + + io.a2a.grpc.AuthorizationCodeOAuthFlow toProto(io.a2a.spec.AuthorizationCodeOAuthFlow domain); + + io.a2a.spec.AuthorizationCodeOAuthFlow fromProto(io.a2a.grpc.AuthorizationCodeOAuthFlow proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ClientCredentialsOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ClientCredentialsOAuthFlowMapper.java new file mode 100644 index 000000000..6dabe28b6 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ClientCredentialsOAuthFlowMapper.java @@ -0,0 +1,18 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.ClientCredentialsOAuthFlow} and {@link io.a2a.grpc.ClientCredentialsOAuthFlow}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface ClientCredentialsOAuthFlowMapper { + + ClientCredentialsOAuthFlowMapper INSTANCE = A2AMappers.getMapper(ClientCredentialsOAuthFlowMapper.class); + + io.a2a.grpc.ClientCredentialsOAuthFlow toProto(io.a2a.spec.ClientCredentialsOAuthFlow domain); + + io.a2a.spec.ClientCredentialsOAuthFlow fromProto(io.a2a.grpc.ClientCredentialsOAuthFlow proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java new file mode 100644 index 000000000..ce0f38ff6 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java @@ -0,0 +1,54 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ObjectFactory; + +/** + * Mapper between {@link io.a2a.spec.DataPart} and {@link io.a2a.grpc.DataPart}. + *

+ * Handles conversion of structured data using Protobuf Struct. + * Uses CommonFieldMapper for Map ↔ Struct conversion. + *

+ * Note: Proto DataPart only has 'data' field. Domain DataPart also has 'metadata' field + * (inherited from Part), which is not persisted in the proto and will be null after conversion. + * Uses @ObjectFactory to resolve constructor ambiguity. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {A2ACommonFieldMapper.class}) +public interface DataPartMapper { + + DataPartMapper INSTANCE = A2AMappers.getMapper(DataPartMapper.class); + + /** + * Converts domain DataPart to proto DataPart. + * Uses CommonFieldMapper for Map → Struct conversion. + * Metadata is ignored (not part of proto definition). + */ + @Mapping(target = "data", source = "data", conditionExpression = "java(domain.getData() != null)", qualifiedByName = "mapToStruct") + io.a2a.grpc.DataPart toProto(io.a2a.spec.DataPart domain); + + /** + * Converts proto DataPart to domain DataPart. + * Uses CommonFieldMapper for Struct → Map conversion. + * Uses factory method to construct DataPart with single-arg constructor. + * Metadata is ignored (not part of proto definition). + */ + @Mapping(target = "metadata", ignore = true) + @Mapping(target = "data", source = "data", qualifiedByName = "structToMap") + io.a2a.spec.DataPart fromProto(io.a2a.grpc.DataPart proto); + + /** + * Object factory for creating DataPart instances. + *

+ * Resolves constructor ambiguity by explicitly using the single-arg constructor. + * The metadata field will be null (not part of proto definition). + * + * @param proto the proto DataPart + * @return new DataPart instance using single-arg constructor + */ + @ObjectFactory + default io.a2a.spec.DataPart createDataPart(io.a2a.grpc.DataPart proto) { + java.util.Map data = A2ACommonFieldMapper.INSTANCE.structToMap(proto.getData()); + return new io.a2a.spec.DataPart(data); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java new file mode 100644 index 000000000..8d1fa9df5 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java @@ -0,0 +1,35 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.grpc.DeleteTaskPushNotificationConfigRequest} and {@link io.a2a.spec.DeleteTaskPushNotificationConfigParams}. + *

+ * Extracts task ID and config ID from resource name format "tasks/{taskId}/pushNotificationConfigs/{configId}" using {@link ResourceNameParser}. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface DeleteTaskPushNotificationConfigParamsMapper { + + DeleteTaskPushNotificationConfigParamsMapper INSTANCE = A2AMappers.getMapper(DeleteTaskPushNotificationConfigParamsMapper.class); + + /** + * Converts proto DeleteTaskPushNotificationConfigRequest to domain DeleteTaskPushNotificationConfigParams. + * Parses the name field to extract both task ID and config ID. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", expression = "java(ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName())[0])") + @Mapping(target = "pushNotificationConfigId", expression = "java(ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName())[1])") + @Mapping(target = "metadata", ignore = true) + DeleteTaskPushNotificationConfigParams fromProto(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest proto); + + /** + * Converts domain DeleteTaskPushNotificationConfigParams to proto DeleteTaskPushNotificationConfigRequest. + * Constructs the name field from task ID and config ID. + */ + @Mapping(target = "name", expression = "java(ResourceNameParser.defineTaskPushNotificationConfigName(domain.id(), domain.pushNotificationConfigId()))") + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest toProto(DeleteTaskPushNotificationConfigParams domain); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java new file mode 100644 index 000000000..347a15684 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java @@ -0,0 +1,80 @@ +package io.a2a.grpc.mapper; + + +import com.google.protobuf.ByteString; +import io.a2a.spec.FileContent; +import io.a2a.spec.FileWithBytes; +import io.a2a.spec.FileWithUri; +import io.a2a.spec.InvalidRequestError; +import java.util.Base64; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.FilePart} and {@link io.a2a.grpc.FilePart}. + *

+ * Handles polymorphic FileContent (FileWithBytes vs FileWithUri) conversion. + *

+ * Manual Implementation Required: Must use manual default methods to handle protobuf oneof pattern + * (file_with_bytes vs file_with_uri fields) and ByteString conversion, which MapStruct cannot automatically handle. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface FilePartMapper { + + FilePartMapper INSTANCE = A2AMappers.getMapper(FilePartMapper.class); + + /** + * Converts domain FilePart to proto FilePart. + * Handles FileWithBytes and FileWithUri polymorphism. + */ + default io.a2a.grpc.FilePart toProto(io.a2a.spec.FilePart domain) { + if (domain == null) { + return null; + } + + io.a2a.grpc.FilePart.Builder builder = io.a2a.grpc.FilePart.newBuilder(); + FileContent fileContent = domain.getFile(); + + if (fileContent instanceof FileWithBytes fileWithBytes) { + builder.setFileWithBytes(ByteString.copyFrom(Base64.getDecoder().decode(fileWithBytes.bytes()))); + if (fileWithBytes.mimeType() != null) { + builder.setMediaType(fileWithBytes.mimeType()); + } + if (fileWithBytes.name() != null) { + builder.setName(fileWithBytes.name()); + } + } else if (fileContent instanceof FileWithUri fileWithUri) { + builder.setFileWithUri(fileWithUri.uri()); + if (fileWithUri.mimeType() != null) { + builder.setMediaType(fileWithUri.mimeType()); + } + if (fileWithUri.name() != null) { + builder.setName(fileWithUri.name()); + } + } + + return builder.build(); + } + + /** + * Converts proto FilePart to domain FilePart. + * Reconstructs FileWithBytes or FileWithUri based on oneof field. + */ + default io.a2a.spec.FilePart fromProto(io.a2a.grpc.FilePart proto) { + if (proto == null) { + return null; + } + + String mimeType = proto.getMediaType().isEmpty() ? null : proto.getMediaType(); + String name = proto.getName().isEmpty() ? null : proto.getName(); + + if (proto.hasFileWithBytes()) { + String bytes = Base64.getEncoder().encodeToString(proto.getFileWithBytes().toByteArray()); + return new io.a2a.spec.FilePart(new FileWithBytes(mimeType, name, bytes)); + } else if (proto.hasFileWithUri()) { + String uri = proto.getFileWithUri(); + return new io.a2a.spec.FilePart(new FileWithUri(mimeType, name, uri)); + } + + throw new InvalidRequestError(); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java new file mode 100644 index 000000000..80cb4e239 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java @@ -0,0 +1,39 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.grpc.GetTaskPushNotificationConfigRequest} and {@link io.a2a.spec.GetTaskPushNotificationConfigParams}. + *

+ * Extracts task ID and config ID from resource name using {@link ResourceNameParser}. + * Handles both formats: + * - "tasks/{taskId}" (uses taskId as configId) + * - "tasks/{taskId}/pushNotificationConfigs/{configId}" + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface GetTaskPushNotificationConfigParamsMapper { + + GetTaskPushNotificationConfigParamsMapper INSTANCE = A2AMappers.getMapper(GetTaskPushNotificationConfigParamsMapper.class); + + /** + * Converts proto GetTaskPushNotificationConfigRequest to domain GetTaskPushNotificationConfigParams. + * Parses the name field to extract both task ID and config ID. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", expression = "java(ResourceNameParser.parseGetTaskPushNotificationConfigName(proto.getName())[0])") + @Mapping(target = "pushNotificationConfigId", expression = "java(ResourceNameParser.parseGetTaskPushNotificationConfigName(proto.getName())[1])") + @Mapping(target = "metadata", ignore = true) + GetTaskPushNotificationConfigParams fromProto(io.a2a.grpc.GetTaskPushNotificationConfigRequest proto); + + /** + * Converts domain Message to proto Message.Uses CommonFieldMapper for metadata conversion and ADDER_PREFERRED for lists. + * @param domain + * @return + */ + @Mapping(target = "name", expression = "java(ResourceNameParser.defineGetTaskPushNotificationConfigName(domain.id(), domain.pushNotificationConfigId()))") + io.a2a.grpc.GetTaskPushNotificationConfigRequest toProto(GetTaskPushNotificationConfigParams domain); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java new file mode 100644 index 000000000..629fcdd37 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java @@ -0,0 +1,30 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.HTTPAuthSecurityScheme} and {@link io.a2a.grpc.HTTPAuthSecurityScheme}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface HTTPAuthSecuritySchemeMapper { + + HTTPAuthSecuritySchemeMapper INSTANCE = A2AMappers.getMapper(HTTPAuthSecuritySchemeMapper.class); + + @Mapping(target = "bearerFormat", source = "bearerFormat", conditionExpression = "java(domain.getBearerFormat() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + io.a2a.grpc.HTTPAuthSecurityScheme toProto(io.a2a.spec.HTTPAuthSecurityScheme domain); + + default io.a2a.spec.HTTPAuthSecurityScheme fromProto(io.a2a.grpc.HTTPAuthSecurityScheme proto) { + if (proto == null) { + return null; + } + + String bearerFormat = proto.getBearerFormat().isEmpty() ? null : proto.getBearerFormat(); + String description = proto.getDescription().isEmpty() ? null : proto.getDescription(); + + return new io.a2a.spec.HTTPAuthSecurityScheme(bearerFormat, proto.getScheme(), description); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java new file mode 100644 index 000000000..99f0b7854 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java @@ -0,0 +1,18 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.ImplicitOAuthFlow} and {@link io.a2a.grpc.ImplicitOAuthFlow}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface ImplicitOAuthFlowMapper { + + ImplicitOAuthFlowMapper INSTANCE = A2AMappers.getMapper(ImplicitOAuthFlowMapper.class); + + io.a2a.grpc.ImplicitOAuthFlow toProto(io.a2a.spec.ImplicitOAuthFlow domain); + + io.a2a.spec.ImplicitOAuthFlow fromProto(io.a2a.grpc.ImplicitOAuthFlow proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java new file mode 100644 index 000000000..faf6c6194 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java @@ -0,0 +1,36 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.grpc.ListTaskPushNotificationConfigRequest} and {@link io.a2a.spec.ListTaskPushNotificationConfigParams}. + *

+ * Extracts task ID from parent resource name format "tasks/{id}" using {@link ResourceNameParser}. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface ListTaskPushNotificationConfigParamsMapper { + + ListTaskPushNotificationConfigParamsMapper INSTANCE = A2AMappers.getMapper(ListTaskPushNotificationConfigParamsMapper.class); + + /** + * Converts proto ListTaskPushNotificationConfigRequest to domain ListTaskPushNotificationConfigParams. + * Extracts task ID from the parent field and maps pagination parameters. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", expression = "java(ResourceNameParser.extractParentId(proto.getParent()))") + @Mapping(target = "metadata", ignore = true) + ListTaskPushNotificationConfigParams fromProto(io.a2a.grpc.ListTaskPushNotificationConfigRequest proto); + + /** + * Converts domain ListTaskPushNotificationConfigParams to proto ListTaskPushNotificationConfigRequest. + * Constructs the parent field from task ID. + */ + @Mapping(target = "parent", expression = "java(ResourceNameParser.defineTaskName(domain.id()))") + @Mapping(target = "pageSize", ignore = true) + @Mapping(target = "pageToken", ignore = true) + io.a2a.grpc.ListTaskPushNotificationConfigRequest toProto(ListTaskPushNotificationConfigParams domain); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java new file mode 100644 index 000000000..fe43a95f3 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java @@ -0,0 +1,49 @@ +package io.a2a.grpc.mapper; + +import io.a2a.grpc.ListTasksRequest; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * MapStruct mapper for ListTasksParams ↔ ListTasksRequest conversions. + *

+ * Handles the conversion between domain ListTasksParams and protobuf ListTasksRequest, + * with special handling for optional fields and timestamp conversions. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {TaskStateMapper.class, A2ACommonFieldMapper.class}) +public interface ListTasksParamsMapper { + + ListTasksParamsMapper INSTANCE = A2AMappers.getMapper(ListTasksParamsMapper.class); + + /** + * Converts domain ListTasksParams to protobuf ListTasksRequest. + * + * @param params the domain ListTasksParams + * @return protobuf ListTasksRequest + */ + @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(params.contextId() != null)") + @Mapping(target = "status", source = "status", conditionExpression = "java(params.status() != null)") + @Mapping(target = "pageSize", source = "pageSize", conditionExpression = "java(params.pageSize() != null)") + @Mapping(target = "pageToken", source = "pageToken", conditionExpression = "java(params.pageToken() != null)") + @Mapping(target = "historyLength", source = "historyLength", conditionExpression = "java(params.historyLength() != null)") + @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "instantToMillis") + @Mapping(target = "includeArtifacts", source = "includeArtifacts", conditionExpression = "java(params.includeArtifacts() != null)") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + ListTasksRequest toProto(io.a2a.spec.ListTasksParams params); + + /** + * Converts protobuf ListTasksRequest to domain ListTasksParams. + * + * @param request the protobuf ListTasksRequest + * @return domain ListTasksParams + */ + @Mapping(target = "contextId", source = "contextId", qualifiedByName = "emptyToNull") + @Mapping(target = "status", source = "status", qualifiedByName = "taskStateOrNull") + @Mapping(target = "pageSize", source = "pageSize", qualifiedByName = "zeroToNull") + @Mapping(target = "pageToken", source = "pageToken", qualifiedByName = "emptyToNull") + @Mapping(target = "historyLength", source = "historyLength", qualifiedByName = "zeroToNull") + @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "millisToInstant") + @Mapping(target = "includeArtifacts", source = "includeArtifacts", qualifiedByName = "falseToNull") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + io.a2a.spec.ListTasksParams fromProto(ListTasksRequest request); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java new file mode 100644 index 000000000..6189daa4f --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java @@ -0,0 +1,29 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.ListTasksResult; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.ListTasksResult} and {@link io.a2a.grpc.ListTasksResponse}. + *

+ * Handles conversion with null handling for nextPageToken field. + * Uses ADDER_PREFERRED strategy to avoid ProtocolMessageList instantiation issues. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = org.mapstruct.CollectionMappingStrategy.ADDER_PREFERRED, + uses = {TaskMapper.class}) +public interface ListTasksResultMapper { + + ListTasksResultMapper INSTANCE = A2AMappers.getMapper(ListTasksResultMapper.class); + + /** + * Converts domain ListTasksResult to proto ListTasksResponse. + * Protobuf builders don't accept null, so nextPageToken is conditionally mapped. + */ + @Mapping(target = "nextPageToken", source = "nextPageToken", conditionExpression = "java(domain.nextPageToken() != null)") + io.a2a.grpc.ListTasksResponse toProto(ListTasksResult domain); + + @Mapping(source = "nextPageToken", target = "nextPageToken", conditionExpression = "java(proto.getNextPageToken() != null)") + ListTasksResult fromProto(io.a2a.grpc.ListTasksResponse proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java new file mode 100644 index 000000000..53dd56222 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java @@ -0,0 +1,46 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.Message; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.Message} and {@link io.a2a.grpc.Message}. + *

+ * Uses ADDER_PREFERRED strategy for List fields (parts, extensions, referenceTaskIds) + * to avoid ProtocolStringList instantiation issues. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {RoleMapper.class, PartMapper.class, A2ACommonFieldMapper.class}) +public interface MessageMapper { + + MessageMapper INSTANCE = A2AMappers.getMapper(MessageMapper.class); + + /** + * Converts domain Message to proto Message. + * Uses CommonFieldMapper for metadata conversion and ADDER_PREFERRED for lists. + */ + @Mapping(target = "messageId", source = "messageId", conditionExpression = "java(domain.getMessageId() != null)") + @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(domain.getContextId() != null)") + @Mapping(target = "taskId", source = "taskId", conditionExpression = "java(domain.getTaskId() != null)") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + io.a2a.grpc.Message toProto(Message domain); + + /** + * Converts proto Message to domain Message. + * Handles empty string → null and Struct conversions via CommonFieldMapper. + * Uses Builder pattern explicitly configured via @BeanMapping. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "messageId", source = "messageId", qualifiedByName = "emptyToNull") + @Mapping(target = "contextId", source = "contextId", qualifiedByName = "emptyToNull") + @Mapping(target = "taskId", source = "taskId", qualifiedByName = "emptyToNull") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + @Mapping(target = "extensions", expression = "java(io.a2a.grpc.mapper.A2ACommonFieldMapper.INSTANCE.emptyListToNull(proto.getExtensionsList()))") + @Mapping(target = "referenceTaskIds", expression = "java(io.a2a.grpc.mapper.A2ACommonFieldMapper.INSTANCE.emptyListToNull(proto.getReferenceTaskIdsList()))") + Message fromProto(io.a2a.grpc.Message proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendConfigurationMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendConfigurationMapper.java new file mode 100644 index 000000000..d615527d4 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendConfigurationMapper.java @@ -0,0 +1,36 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.MessageSendConfiguration; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.MessageSendConfiguration} and {@link io.a2a.grpc.SendMessageConfiguration}. + *

+ * Handles bidirectional mapping with null/empty list conversions and push notification config delegation. + * Uses ADDER_PREFERRED strategy to avoid ProtocolStringList instantiation issues. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {PushNotificationConfigMapper.class, A2ACommonFieldMapper.class}) +public interface MessageSendConfigurationMapper { + + MessageSendConfigurationMapper INSTANCE = A2AMappers.getMapper(MessageSendConfigurationMapper.class); + + /** + * Converts domain MessageSendConfiguration to proto SendMessageConfiguration. + */ + @Mapping(target = "pushNotificationConfig", source = "pushNotificationConfig", conditionExpression = "java(domain.pushNotificationConfig() != null)") + io.a2a.grpc.SendMessageConfiguration toProto(MessageSendConfiguration domain); + + /** + * Converts proto SendMessageConfiguration to domain MessageSendConfiguration. + * Uses Builder pattern for record construction. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "acceptedOutputModes", expression = "java(io.a2a.grpc.mapper.A2ACommonFieldMapper.INSTANCE.emptyListToNull(proto.getAcceptedOutputModesList()))") + MessageSendConfiguration fromProto(io.a2a.grpc.SendMessageConfiguration proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java new file mode 100644 index 000000000..759fc0fef --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java @@ -0,0 +1,37 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.MessageSendParams; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.MessageSendParams} and {@link io.a2a.grpc.SendMessageRequest}. + *

+ * Handles bidirectional mapping with message/request field name difference and Struct conversions. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {MessageMapper.class, MessageSendConfigurationMapper.class, A2ACommonFieldMapper.class}) +public interface MessageSendParamsMapper { + + MessageSendParamsMapper INSTANCE = A2AMappers.getMapper(MessageSendParamsMapper.class); + + /** + * Converts domain MessageSendParams to proto SendMessageRequest. + * Maps domain "message" field to proto "request" field. + */ + @Mapping(target = "request", source = "message") + @Mapping(target = "configuration", source = "configuration", conditionExpression = "java(domain.configuration() != null)") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + io.a2a.grpc.SendMessageRequest toProto(MessageSendParams domain); + + /** + * Converts proto SendMessageRequest to domain MessageSendParams. + * Maps proto "request" field to domain "message" field. + * Uses Builder pattern for record construction. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "message", source = "request") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + MessageSendParams fromProto(io.a2a.grpc.SendMessageRequest proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java new file mode 100644 index 000000000..2464d0c38 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java @@ -0,0 +1,28 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.MutualTLSSecurityScheme} and {@link io.a2a.grpc.MutualTlsSecurityScheme}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface MutualTLSSecuritySchemeMapper { + + MutualTLSSecuritySchemeMapper INSTANCE = A2AMappers.getMapper(MutualTLSSecuritySchemeMapper.class); + + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + io.a2a.grpc.MutualTlsSecurityScheme toProto(io.a2a.spec.MutualTLSSecurityScheme domain); + + default io.a2a.spec.MutualTLSSecurityScheme fromProto(io.a2a.grpc.MutualTlsSecurityScheme proto) { + if (proto == null) { + return null; + } + + String description = proto.getDescription().isEmpty() ? null : proto.getDescription(); + + return new io.a2a.spec.MutualTLSSecurityScheme(description); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java new file mode 100644 index 000000000..b762f720b --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java @@ -0,0 +1,32 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.OAuth2SecurityScheme} and {@link io.a2a.grpc.OAuth2SecurityScheme}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {OAuthFlowsMapper.class}) +public interface OAuth2SecuritySchemeMapper { + + OAuth2SecuritySchemeMapper INSTANCE = A2AMappers.getMapper(OAuth2SecuritySchemeMapper.class); + + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + @Mapping(target = "oauth2MetadataUrl", source = "oauth2MetadataUrl", conditionExpression = "java(domain.getOauth2MetadataUrl() != null)") + io.a2a.grpc.OAuth2SecurityScheme toProto(io.a2a.spec.OAuth2SecurityScheme domain); + + default io.a2a.spec.OAuth2SecurityScheme fromProto(io.a2a.grpc.OAuth2SecurityScheme proto) { + if (proto == null) { + return null; + } + + io.a2a.spec.OAuthFlows flows = OAuthFlowsMapper.INSTANCE.fromProto(proto.getFlows()); + String description = proto.getDescription().isEmpty() ? null : proto.getDescription(); + String oauth2MetadataUrl = proto.getOauth2MetadataUrl().isEmpty() ? null : proto.getOauth2MetadataUrl(); + + return new io.a2a.spec.OAuth2SecurityScheme(flows, description, oauth2MetadataUrl); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java new file mode 100644 index 000000000..6af0e28ce --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java @@ -0,0 +1,24 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.OAuthFlows} and {@link io.a2a.grpc.OAuthFlows}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = { + AuthorizationCodeOAuthFlowMapper.class, + ClientCredentialsOAuthFlowMapper.class, + ImplicitOAuthFlowMapper.class, + PasswordOAuthFlowMapper.class + }) +public interface OAuthFlowsMapper { + + OAuthFlowsMapper INSTANCE = A2AMappers.getMapper(OAuthFlowsMapper.class); + + io.a2a.grpc.OAuthFlows toProto(io.a2a.spec.OAuthFlows domain); + + io.a2a.spec.OAuthFlows fromProto(io.a2a.grpc.OAuthFlows proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java new file mode 100644 index 000000000..14d848fd9 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java @@ -0,0 +1,28 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.OpenIdConnectSecurityScheme} and {@link io.a2a.grpc.OpenIdConnectSecurityScheme}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface OpenIdConnectSecuritySchemeMapper { + + OpenIdConnectSecuritySchemeMapper INSTANCE = A2AMappers.getMapper(OpenIdConnectSecuritySchemeMapper.class); + + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + io.a2a.grpc.OpenIdConnectSecurityScheme toProto(io.a2a.spec.OpenIdConnectSecurityScheme domain); + + default io.a2a.spec.OpenIdConnectSecurityScheme fromProto(io.a2a.grpc.OpenIdConnectSecurityScheme proto) { + if (proto == null) { + return null; + } + + String description = proto.getDescription().isEmpty() ? null : proto.getDescription(); + + return new io.a2a.spec.OpenIdConnectSecurityScheme(proto.getOpenIdConnectUrl(), description); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java new file mode 100644 index 000000000..a82d8527c --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java @@ -0,0 +1,69 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.DataPart; +import io.a2a.spec.FilePart; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.Part} and {@link io.a2a.grpc.Part}. + *

+ * Handles polymorphic Part conversion by delegating to type-specific mappers: + *

    + *
  • TextPart - handled directly (text field on proto Part)
  • + *
  • FilePart - delegated to FilePartMapper
  • + *
  • DataPart - delegated to DataPartMapper
  • + *
+ *

+ * Manual Implementation Required: Must use manual instanceof dispatch to handle protobuf oneof pattern + * (text vs file vs data fields), as MapStruct's @SubclassMapping maps to different target types, not different fields of the same type. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {FilePartMapper.class, DataPartMapper.class}) +public interface PartMapper { + + PartMapper INSTANCE = A2AMappers.getMapper(PartMapper.class); + + /** + * Converts domain Part to proto Part. + * Handles TextPart, FilePart, and DataPart polymorphism. + */ + default io.a2a.grpc.Part toProto(Part domain) { + if (domain == null) { + return null; + } + + io.a2a.grpc.Part.Builder builder = io.a2a.grpc.Part.newBuilder(); + + if (domain instanceof TextPart textPart) { + builder.setText(textPart.getText()); + } else if (domain instanceof FilePart filePart) { + builder.setFile(FilePartMapper.INSTANCE.toProto(filePart)); + } else if (domain instanceof DataPart dataPart) { + builder.setData(DataPartMapper.INSTANCE.toProto(dataPart)); + } + + return builder.build(); + } + + /** + * Converts proto Part to domain Part. + * Reconstructs TextPart, FilePart, or DataPart based on oneof field. + */ + default Part fromProto(io.a2a.grpc.Part proto) { + if (proto == null) { + return null; + } + + if (proto.hasText()) { + return new TextPart(proto.getText()); + } else if (proto.hasFile()) { + return FilePartMapper.INSTANCE.fromProto(proto.getFile()); + } else if (proto.hasData()) { + return DataPartMapper.INSTANCE.fromProto(proto.getData()); + } + + throw new InvalidRequestError(); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java new file mode 100644 index 000000000..84b6e1bdf --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java @@ -0,0 +1,18 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.PasswordOAuthFlow} and {@link io.a2a.grpc.PasswordOAuthFlow}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +public interface PasswordOAuthFlowMapper { + + PasswordOAuthFlowMapper INSTANCE = A2AMappers.getMapper(PasswordOAuthFlowMapper.class); + + io.a2a.grpc.PasswordOAuthFlow toProto(io.a2a.spec.PasswordOAuthFlow domain); + + io.a2a.spec.PasswordOAuthFlow fromProto(io.a2a.grpc.PasswordOAuthFlow proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PushNotificationConfigMapper.java new file mode 100644 index 000000000..18ae705d5 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PushNotificationConfigMapper.java @@ -0,0 +1,31 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.PushNotificationConfig} and {@link io.a2a.grpc.PushNotificationConfig}. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {AuthenticationInfoMapper.class, A2ACommonFieldMapper.class}) +public interface PushNotificationConfigMapper { + + PushNotificationConfigMapper INSTANCE = A2AMappers.getMapper(PushNotificationConfigMapper.class); + + @Mapping(target = "url", source = "url", conditionExpression = "java(domain.url() != null)") + @Mapping(target = "token", source = "token", conditionExpression = "java(domain.token() != null)") + @Mapping(target = "authentication", source = "authentication", conditionExpression = "java(domain.authentication() != null)") + @Mapping(target = "id", source = "id", conditionExpression = "java(domain.id() != null)") + io.a2a.grpc.PushNotificationConfig toProto(io.a2a.spec.PushNotificationConfig domain); + + /** + * Converts proto PushNotificationConfig to domain. + * Uses declarative mappings with empty string → null conversion via CommonFieldMapper. + */ + @Mapping(target = "token", source = "token", qualifiedByName = "emptyToNull") + @Mapping(target = "id", source = "id", qualifiedByName = "emptyToNull") + @Mapping(target = "authentication", source = "authentication", conditionExpression = "java(proto.hasAuthentication())") + io.a2a.spec.PushNotificationConfig fromProto(io.a2a.grpc.PushNotificationConfig proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ResourceNameParser.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ResourceNameParser.java new file mode 100644 index 000000000..ba7303a74 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ResourceNameParser.java @@ -0,0 +1,106 @@ +package io.a2a.grpc.mapper; + +/** + * Utility class for parsing gRPC resource names. + *

+ * Provides methods to extract IDs and components from resource name strings + * following the pattern: "tasks/{taskId}/pushNotificationConfigs/{configId}" + */ +public class ResourceNameParser { + + /** + * Extracts the task ID from a simple resource name like "tasks/{taskId}". + * + * @param resourceName the resource name (e.g., "tasks/abc123") + * @return the extracted task ID + */ + public static String extractTaskId(String resourceName) { + return resourceName.substring(resourceName.lastIndexOf('/') + 1); + } + + /** + * Define the task name form its taskId as in "tasks/{taskId}". + * + * @param taskId the taskId + * @return the task name + */ + public static String defineTaskName(String taskId) { + return "tasks/" + taskId; + } + + /** + * Parses a task push notification config resource name and extracts taskId and configId. + *

+ * Expected format: "tasks/{taskId}/pushNotificationConfigs/{configId}" + * + * @param resourceName the resource name to parse + * @return array with [taskId, configId] + * @throws IllegalArgumentException if the format is invalid + */ + public static String[] parseTaskPushNotificationConfigName(String resourceName) { + String[] parts = resourceName.split("/"); + if (parts.length < 4) { + throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + resourceName); + } + return new String[]{parts[1], parts[3]}; + } + + /** + * Parses a get task push notification config request name and extracts taskId and configId. + *

+ * Expected formats: + * - "tasks/{taskId}" (returns taskId for both values) + * - "tasks/{taskId}/pushNotificationConfigs/{configId}" + * + * @param resourceName the resource name to parse + * @return array with [taskId, configId] + * @throws IllegalArgumentException if the format is invalid + */ + public static String[] parseGetTaskPushNotificationConfigName(String resourceName) { + String[] parts = resourceName.split("/"); + String taskId = parts[1]; + String configId; + + if (parts.length == 2) { + // "tasks/{taskId}" - use taskId as configId + configId = taskId; + } else if (parts.length < 4) { + throw new IllegalArgumentException("Invalid name format for GetTaskPushNotificationConfigRequest: " + resourceName); + } else { + // "tasks/{taskId}/pushNotificationConfigs/{configId}" + configId = parts[3]; + } + + return new String[]{taskId, configId}; + } + public static String defineGetTaskPushNotificationConfigName(String taskId, String configId) { + String name= "tasks/" + taskId ; + if(configId != null && !configId.isBlank()) { + name = name + "/pushNotificationConfigs/" + configId; + } + return name; + } + + /** + * Constructs a task push notification config resource name from task ID and config ID. + *

+ * Format: "tasks/{taskId}/pushNotificationConfigs/{configId}" + * + * @param taskId the task identifier + * @param configId the push notification config identifier + * @return the formatted resource name + */ + public static String defineTaskPushNotificationConfigName(String taskId, String configId) { + return "tasks/" + taskId + "/pushNotificationConfigs/" + configId; + } + + /** + * Extracts the parent ID (task ID) from a parent resource name like "tasks/{taskId}". + * + * @param parentName the parent resource name + * @return the extracted parent ID + */ + public static String extractParentId(String parentName) { + return parentName.substring(parentName.lastIndexOf('/') + 1); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/RoleMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/RoleMapper.java new file mode 100644 index 000000000..13e003a6f --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/RoleMapper.java @@ -0,0 +1,52 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.Message; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.Message.Role} and {@link io.a2a.grpc.Role}. + *

+ * Handles enum conversion between domain and protobuf role representations: + *

    + *
  • USER (domain) ↔ ROLE_USER (proto)
  • + *
  • AGENT (domain) ↔ ROLE_AGENT (proto)
  • + *
+ *

+ * Manual Implementation Required: Uses manual switch statements instead of @ValueMapping + * to avoid mapstruct-spi-protobuf enum strategy initialization issues. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface RoleMapper { + + RoleMapper INSTANCE = A2AMappers.getMapper(RoleMapper.class); + + /** + * Converts domain Role to proto Role. + * Maps USER → ROLE_USER, AGENT → ROLE_AGENT. + */ + default io.a2a.grpc.Role toProto(Message.Role domain) { + if (domain == null) { + return io.a2a.grpc.Role.ROLE_UNSPECIFIED; + } + return switch (domain) { + case USER -> io.a2a.grpc.Role.ROLE_USER; + case AGENT -> io.a2a.grpc.Role.ROLE_AGENT; + }; + } + + /** + * Converts proto Role to domain Role. + * Maps ROLE_USER → USER, ROLE_AGENT → AGENT. + * ROLE_UNSPECIFIED returns null. + */ + default Message.Role fromProto(io.a2a.grpc.Role proto) { + if (proto == null || proto == io.a2a.grpc.Role.ROLE_UNSPECIFIED) { + return null; + } + return switch (proto) { + case ROLE_USER -> Message.Role.USER; + case ROLE_AGENT -> Message.Role.AGENT; + default -> null; + }; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SecurityMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SecurityMapper.java new file mode 100644 index 000000000..1e6f26dde --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SecurityMapper.java @@ -0,0 +1,110 @@ +package io.a2a.grpc.mapper; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.google.protobuf.ProtocolStringList; +import io.a2a.grpc.Security; +import io.a2a.grpc.StringList; +import org.mapstruct.Mapper; + +/** + * Mapper between domain security requirements and protobuf Security messages. + *

+ * Domain representation: {@code List>>} where each map represents + * one security option with scheme names as keys and scopes as values. + *

+ * Proto representation: {@code repeated Security} where each Security has + * {@code map schemes}. + *

+ * Example: A security requirement that allows either OAuth2 with read/write scopes OR API Key: + *

+ * Domain: [
+ *   {"oauth2": ["read", "write"]},
+ *   {"apiKey": []}
+ * ]
+ * Proto: [
+ *   Security{schemes: {"oauth2": StringList{values: ["read", "write"]}}},
+ *   Security{schemes: {"apiKey": StringList{values: []}}}
+ * ]
+ * 
+ *

+ * Manual Implementation Required: Handles complex nested structure ({@code List>>} ↔ + * {@code repeated Security} with {@code map}) requiring manual iteration and StringList wrapper handling. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface SecurityMapper { + + SecurityMapper INSTANCE = A2AMappers.getMapper(SecurityMapper.class); + + /** + * Converts a single domain security requirement map to a proto Security message. + *

+ * MapStruct will call this method for each element when mapping the list. + * + * @param schemeMap map of scheme names to scopes + * @return Security proto message, or null if input is null + */ + default Security mapSecurityItem(Map> schemeMap) { + if (schemeMap == null) { + return null; + } + + Security.Builder securityBuilder = Security.newBuilder(); + for (Map.Entry> entry : schemeMap.entrySet()) { + StringList.Builder stringListBuilder = StringList.newBuilder(); + if (entry.getValue() != null) { + stringListBuilder.addAllList(entry.getValue()); + } + securityBuilder.putSchemes(entry.getKey(), stringListBuilder.build()); + } + return securityBuilder.build(); + } + + /** + * Converts domain security requirements to proto Security messages. + *

+ * Each Map in the domain list becomes one Security message in proto, representing + * one way to satisfy the security requirements (OR relationship between list items). + * + * @param domainSecurity list of maps representing security requirement options + * @return list of Security proto messages, or null if input is null + */ + default List toProto(List>> domainSecurity) { + if (domainSecurity == null) { + return null; + } + + List protoList = new ArrayList<>(domainSecurity.size()); + for (Map> schemeMap : domainSecurity) { + protoList.add(mapSecurityItem(schemeMap)); + } + return protoList; + } + + /** + * Converts proto Security messages to domain security requirements. + * + * @param protoSecurity list of Security proto messages + * @return list of maps representing security requirement options, or null if input is null + */ + default List>> fromProto(List protoSecurity) { + if (protoSecurity == null) { + return null; + } + + List>> domainList = new ArrayList<>(protoSecurity.size()); + for (Security security : protoSecurity) { + Map> schemeMap = new LinkedHashMap<>(); + for (Map.Entry entry : security.getSchemesMap().entrySet()) { + ProtocolStringList listList = entry.getValue().getListList(); + List values = new ArrayList<>(listList); + schemeMap.put(entry.getKey(), values); + } + domainList.add(schemeMap); + } + return domainList; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SecuritySchemeMapper.java new file mode 100644 index 000000000..723762785 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SecuritySchemeMapper.java @@ -0,0 +1,104 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.APIKeySecurityScheme; +import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.MutualTLSSecurityScheme; +import io.a2a.spec.OAuth2SecurityScheme; +import io.a2a.spec.OpenIdConnectSecurityScheme; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.SecurityScheme} and {@link io.a2a.grpc.SecurityScheme}. + *

+ * This mapper handles the polymorphic sealed interface SecurityScheme by using a custom + * default method with switch expression. MapStruct doesn't natively support sealed interfaces + * with protobuf's oneof pattern, so we manually dispatch to the appropriate concrete mapper. + *

+ * Manual Implementation Required: Must use manual instanceof dispatch to handle sealed interface (5 permitted subtypes) + * to protobuf oneof pattern, as MapStruct's @SubclassMapping cannot map different source types to different fields of the same target type. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = { + APIKeySecuritySchemeMapper.class, + HTTPAuthSecuritySchemeMapper.class, + OAuth2SecuritySchemeMapper.class, + OpenIdConnectSecuritySchemeMapper.class, + MutualTLSSecuritySchemeMapper.class + }) +public interface SecuritySchemeMapper { + + SecuritySchemeMapper INSTANCE = A2AMappers.getMapper(SecuritySchemeMapper.class); + + /** + * Converts a domain SecurityScheme to protobuf SecurityScheme. + *

+ * Uses instanceof checks to handle sealed interface polymorphism, dispatching to the + * appropriate concrete mapper based on the runtime type. This is necessary because + * MapStruct cannot automatically handle sealed interfaces with protobuf oneof fields. + * + * @param domain the domain security scheme (sealed interface) + * @return the protobuf SecurityScheme with the appropriate oneof field set + */ + default io.a2a.grpc.SecurityScheme toProto(io.a2a.spec.SecurityScheme domain) { + if (domain == null) { + return null; + } + + if (domain instanceof APIKeySecurityScheme s) { + return io.a2a.grpc.SecurityScheme.newBuilder() + .setApiKeySecurityScheme(APIKeySecuritySchemeMapper.INSTANCE.toProto(s)) + .build(); + } else if (domain instanceof HTTPAuthSecurityScheme s) { + return io.a2a.grpc.SecurityScheme.newBuilder() + .setHttpAuthSecurityScheme(HTTPAuthSecuritySchemeMapper.INSTANCE.toProto(s)) + .build(); + } else if (domain instanceof OAuth2SecurityScheme s) { + return io.a2a.grpc.SecurityScheme.newBuilder() + .setOauth2SecurityScheme(OAuth2SecuritySchemeMapper.INSTANCE.toProto(s)) + .build(); + } else if (domain instanceof OpenIdConnectSecurityScheme s) { + return io.a2a.grpc.SecurityScheme.newBuilder() + .setOpenIdConnectSecurityScheme(OpenIdConnectSecuritySchemeMapper.INSTANCE.toProto(s)) + .build(); + } else if (domain instanceof MutualTLSSecurityScheme s) { + return io.a2a.grpc.SecurityScheme.newBuilder() + .setMtlsSecurityScheme(MutualTLSSecuritySchemeMapper.INSTANCE.toProto(s)) + .build(); + } + + throw new IllegalArgumentException("Unknown SecurityScheme type: " + domain.getClass()); + } + + /** + * Converts a protobuf SecurityScheme to domain SecurityScheme. + *

+ * Uses oneof checks to determine which security scheme type is set, dispatching to the + * appropriate concrete mapper. This reverses the toProto conversion by checking which + * oneof field is populated. + * + * @param proto the protobuf SecurityScheme with a oneof field set + * @return the domain security scheme (sealed interface implementation) + */ + default io.a2a.spec.SecurityScheme fromProto(io.a2a.grpc.SecurityScheme proto) { + if (proto == null) { + return null; + } + + return switch (proto.getSchemeCase()) { + case API_KEY_SECURITY_SCHEME -> + APIKeySecuritySchemeMapper.INSTANCE.fromProto(proto.getApiKeySecurityScheme()); + case HTTP_AUTH_SECURITY_SCHEME -> + HTTPAuthSecuritySchemeMapper.INSTANCE.fromProto(proto.getHttpAuthSecurityScheme()); + case OAUTH2_SECURITY_SCHEME -> + OAuth2SecuritySchemeMapper.INSTANCE.fromProto(proto.getOauth2SecurityScheme()); + case OPEN_ID_CONNECT_SECURITY_SCHEME -> + OpenIdConnectSecuritySchemeMapper.INSTANCE.fromProto(proto.getOpenIdConnectSecurityScheme()); + case MTLS_SECURITY_SCHEME -> + MutualTLSSecuritySchemeMapper.INSTANCE.fromProto(proto.getMtlsSecurityScheme()); + case SCHEME_NOT_SET -> + throw new IllegalArgumentException("SecurityScheme oneof field not set"); + }; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java new file mode 100644 index 000000000..df0c2d1af --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java @@ -0,0 +1,136 @@ +package io.a2a.grpc.mapper; + +import io.a2a.grpc.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.TaskPushNotificationConfig; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * MapStruct mapper for SetTaskPushNotificationConfigRequest → TaskPushNotificationConfig. + *

+ * Handles resource name parsing and ID override logic for creating push notification configs. + *

+ * Resource Name Handling: + *

    + *
  • Extracts taskId from parent resource name (format: "tasks/{task_id}")
  • + *
  • Fallback: Extracts from config.name if parent is blank
  • + *
  • Overrides PushNotificationConfig.id with config_id from request
  • + *
+ *

+ * Compile-Time Safety: If the proto changes fields, MapStruct will fail to compile. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {PushNotificationConfigMapper.class}) +public interface SetTaskPushNotificationConfigMapper { + + SetTaskPushNotificationConfigMapper INSTANCE = A2AMappers.getMapper(SetTaskPushNotificationConfigMapper.class); + + /** + * Converts SetTaskPushNotificationConfigRequest to domain TaskPushNotificationConfig. + *

+ * Extracts taskId from parent resource name and maps PushNotificationConfig with + * ID override from config_id field. + * + * @param request the protobuf SetTaskPushNotificationConfigRequest + * @return domain TaskPushNotificationConfig + */ + @Mapping(target = "taskId", expression = "java(extractTaskId(request))") + @Mapping(target = "pushNotificationConfig", expression = "java(mapPushNotificationConfigWithId(request))") + TaskPushNotificationConfig fromProto(SetTaskPushNotificationConfigRequest request); + + /** + * Converts SetTaskPushNotificationConfigRequest to domain TaskPushNotificationConfig. + *

+ * Extracts taskId from parent resource name and maps PushNotificationConfig with + * ID override from config_id field. + * + * @param config the domainTaskPushNotificationConfig + * @return proto SetTaskPushNotificationConfigRequest + */ + @Mapping(target = "parent", expression = "java(ResourceNameParser.defineTaskName(config.taskId()))") + @Mapping(target = "configId", expression = "java(extractConfigId(config))") + @Mapping(target = "config", expression = "java(mapPushNotificationConfig(config))") + SetTaskPushNotificationConfigRequest toProto(TaskPushNotificationConfig config); + + /** + * Extracts the task ID from the parent resource name. + *

+ * Format: "tasks/{task_id}" + * Fallback: If parent is blank, extracts from config.name instead. + * + * @param request the protobuf SetTaskPushNotificationConfigRequest + * @return the extracted task ID + */ + default String extractTaskId(SetTaskPushNotificationConfigRequest request) { + String parent = request.getParent(); + + if (parent == null || parent.isBlank()) { + // Fallback: extract from config.name + return ResourceNameParser.extractTaskId(request.getConfig().getName()); + } + + // Extract from parent resource name + return ResourceNameParser.extractParentId(parent); + } + + /** + * Extracts the config ID from the configuration. If it is not defined, the task ID is used. + * + * @param config the TaskPushNotificationConfig + * @return the extracted config ID + */ + default String extractConfigId(TaskPushNotificationConfig config) { + if (config.pushNotificationConfig() != null && config.pushNotificationConfig().id() != null && !config.pushNotificationConfig().id().isBlank()) { + return config.pushNotificationConfig().id(); + } + return config.taskId(); + } + + /** + * Maps the protobuf PushNotificationConfig to domain, injecting config_id from request. + *

+ * The config_id from the request overrides the ID in the proto's PushNotificationConfig. + * + * @param request the protobuf SetTaskPushNotificationConfigRequest + * @return domain PushNotificationConfig with config_id injected + */ + default PushNotificationConfig mapPushNotificationConfigWithId(SetTaskPushNotificationConfigRequest request) { + // Check if config and push_notification_config exist + if (!request.hasConfig() + || !request.getConfig().hasPushNotificationConfig() + || request.getConfig().getPushNotificationConfig() + .equals(io.a2a.grpc.PushNotificationConfig.getDefaultInstance())) { + return null; + } + + // Map the proto PushNotificationConfig + PushNotificationConfig result = PushNotificationConfigMapper.INSTANCE.fromProto( + request.getConfig().getPushNotificationConfig() + ); + + // Override ID with config_id from request + String configId = request.getConfigId(); + if (configId != null && !configId.isEmpty() && !configId.equals(result.id())) { + return new PushNotificationConfig( + result.url(), + result.token(), + result.authentication(), + configId + ); + } + + return result; + } + + /** + * Maps the protobuf PushNotificationConfig to domain, injecting config_id from request. + *

+ * The config_id from the request overrides the ID in the proto's PushNotificationConfig. + * + * @param request the protobuf SetTaskPushNotificationConfigRequest + * @return domain PushNotificationConfig with config_id injected + */ + default io.a2a.grpc.TaskPushNotificationConfig mapPushNotificationConfig(TaskPushNotificationConfig domain) { + return TaskPushNotificationConfigMapper.INSTANCE.toProto(domain); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java new file mode 100644 index 000000000..eb2abf624 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java @@ -0,0 +1,90 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.Message; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskStatusUpdateEvent; +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.StreamingEventKind} and {@link io.a2a.grpc.StreamResponse}. + *

+ * StreamResponse uses a protobuf oneof field to represent polymorphic streaming events. + * StreamingEventKind is a sealed interface with four permitted implementations: + *

    + *
  • {@link Task} - Complete task state
  • + *
  • {@link Message} - Full message
  • + *
  • {@link TaskStatusUpdateEvent} - Status update event
  • + *
  • {@link TaskArtifactUpdateEvent} - Artifact update event
  • + *
+ *

+ * This mapper provides bidirectional conversion using instanceof checks (toProto) + * and switch expressions on the oneof case (fromProto). + */ +@Mapper(config = A2AProtoMapperConfig.class, + uses = {TaskMapper.class, MessageMapper.class, TaskStatusUpdateEventMapper.class, TaskArtifactUpdateEventMapper.class}) +public interface StreamResponseMapper { + + StreamResponseMapper INSTANCE = A2AMappers.getMapper(StreamResponseMapper.class); + + /** + * Converts domain StreamingEventKind to proto StreamResponse. + * Uses instanceof checks to determine which oneof field to set. + * + * @param domain the streaming event kind (Task, Message, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent) + * @return the proto StreamResponse with the appropriate oneof field set + */ + default io.a2a.grpc.StreamResponse toProto(StreamingEventKind domain) { + if (domain == null) { + return null; + } + + if (domain instanceof Task task) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setTask(TaskMapper.INSTANCE.toProto(task)) + .build(); + } else if (domain instanceof Message message) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setMsg(MessageMapper.INSTANCE.toProto(message)) + .build(); + } else if (domain instanceof TaskStatusUpdateEvent statusUpdate) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setStatusUpdate(TaskStatusUpdateEventMapper.INSTANCE.toProto(statusUpdate)) + .build(); + } else if (domain instanceof TaskArtifactUpdateEvent artifactUpdate) { + return io.a2a.grpc.StreamResponse.newBuilder() + .setArtifactUpdate(TaskArtifactUpdateEventMapper.INSTANCE.toProto(artifactUpdate)) + .build(); + } + + throw new IllegalArgumentException("Unknown StreamingEventKind type: " + domain.getClass().getName()); + } + + /** + * Converts proto StreamResponse to domain StreamingEventKind. + * Uses switch expression on the oneof case to determine which type to return. + * + * @param proto the proto StreamResponse + * @return the corresponding domain streaming event kind + * @throws IllegalArgumentException if the oneof field is not set + */ + default StreamingEventKind fromProto(io.a2a.grpc.StreamResponse proto) { + if (proto == null) { + return null; + } + + return switch (proto.getPayloadCase()) { + case TASK -> + TaskMapper.INSTANCE.fromProto(proto.getTask()); + case MSG -> + MessageMapper.INSTANCE.fromProto(proto.getMsg()); + case STATUS_UPDATE -> + TaskStatusUpdateEventMapper.INSTANCE.fromProto(proto.getStatusUpdate()); + case ARTIFACT_UPDATE -> + TaskArtifactUpdateEventMapper.INSTANCE.fromProto(proto.getArtifactUpdate()); + case PAYLOAD_NOT_SET -> + throw new IllegalArgumentException("StreamResponse payload oneof field not set"); + }; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java new file mode 100644 index 000000000..6735612c5 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java @@ -0,0 +1,54 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.SubscribeToTaskRequest} and {@link io.a2a.grpc.SubscribeToTaskRequest}. + *

+ * The mapping handles the structural difference between domain and proto representations: + *

    + *
  • Domain: Full JSONRPC request with id, jsonrpc, method, and params (TaskIdParams)
  • + *
  • Proto: Simple request with name field in format "tasks/{task_id}"
  • + *
+ *

+ * Note: The domain object is a complete JSONRPC request, while the proto is just the gRPC + * request parameters. The JSONRPC envelope (id, jsonrpc, method) is handled separately + * by the transport layer. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {TaskIdParamsMapper.class}) +public interface SubscribeToTaskRequestMapper { + + SubscribeToTaskRequestMapper INSTANCE = A2AMappers.getMapper(SubscribeToTaskRequestMapper.class); + + /** + * Converts domain SubscribeToTaskRequest to proto SubscribeToTaskRequest. + * Extracts the task ID from params and formats it as "tasks/{task_id}". + * + * @param domain the domain SubscribeToTaskRequest + * @return the proto SubscribeToTaskRequest + */ + default io.a2a.grpc.SubscribeToTaskRequest toProto(io.a2a.spec.SubscribeToTaskRequest domain) { + if (domain == null || domain.getParams() == null || domain.getParams().id() == null) { + return null; + } + return io.a2a.grpc.SubscribeToTaskRequest.newBuilder() + .setName(ResourceNameParser.defineTaskName(domain.getParams().id())) + .build(); + } + + /** + * Converts proto SubscribeToTaskRequest to domain SubscribeToTaskRequest. + * Extracts the task ID from the name field and creates a TaskIdParams. + * + * @param proto the proto SubscribeToTaskRequest + * @return the domain SubscribeToTaskRequest + */ + default io.a2a.spec.SubscribeToTaskRequest fromProto(io.a2a.grpc.SubscribeToTaskRequest proto) { + if (proto == null || proto.getName() == null) { + return null; + } + return new io.a2a.spec.SubscribeToTaskRequest.Builder() + .params(new io.a2a.spec.TaskIdParams(ResourceNameParser.extractTaskId(proto.getName()))) + .build(); + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java new file mode 100644 index 000000000..6c907d22b --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java @@ -0,0 +1,35 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.TaskArtifactUpdateEvent; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.TaskArtifactUpdateEvent} and {@link io.a2a.grpc.TaskArtifactUpdateEvent}. + *

+ * Now fully declarative using Builder pattern with @BeanMapping. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {ArtifactMapper.class, A2ACommonFieldMapper.class}) +public interface TaskArtifactUpdateEventMapper { + + TaskArtifactUpdateEventMapper INSTANCE = A2AMappers.getMapper(TaskArtifactUpdateEventMapper.class); + + /** + * Converts domain TaskArtifactUpdateEvent to proto. + * Uses declarative mapping with CommonFieldMapper for metadata conversion. + */ + @Mapping(target = "append", source = "append", conditionExpression = "java(domain.isAppend() != null)") + @Mapping(target = "lastChunk", source = "lastChunk", conditionExpression = "java(domain.isLastChunk() != null)") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + io.a2a.grpc.TaskArtifactUpdateEvent toProto(TaskArtifactUpdateEvent domain); + + /** + * Converts proto TaskArtifactUpdateEvent to domain. + * Now fully declarative using Builder pattern configured via @BeanMapping. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + TaskArtifactUpdateEvent fromProto(io.a2a.grpc.TaskArtifactUpdateEvent proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java new file mode 100644 index 000000000..b2edab0bd --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java @@ -0,0 +1,53 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.TaskIdParams; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper for {@link io.a2a.spec.TaskIdParams} from various gRPC request types. + *

+ * Extracts task ID from resource name format "tasks/{id}" using {@link ResourceNameParser}. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface TaskIdParamsMapper { + + TaskIdParamsMapper INSTANCE = A2AMappers.getMapper(TaskIdParamsMapper.class); + + /** + * Converts proto CancelTaskRequest to domain TaskIdParams. + * Extracts task ID from the resource name. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") + @Mapping(target = "metadata", ignore = true) + TaskIdParams fromProtoCancelTaskRequest(io.a2a.grpc.CancelTaskRequest proto); + + /** + * Converts proto CancelTaskRequest to domain TaskIdParams. + * Extracts task ID from the resource name. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "name", expression = "java(ResourceNameParser.defineTaskName(domain.id()))") + io.a2a.grpc.CancelTaskRequest toProtoCancelTaskRequest(TaskIdParams domain); + + + /** + * Converts proto SubscribeToTaskRequest to domain TaskIdParams. + * Extracts task ID from the resource name. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") + @Mapping(target = "metadata", ignore = true) + TaskIdParams fromProtoSubscribeToTaskRequest(io.a2a.grpc.SubscribeToTaskRequest proto); + + /** + * Converts domain TaskIdParams to proto SubscribeToTaskRequest. + * Creates resource name from task ID. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "name", expression = "java(ResourceNameParser.defineTaskName(domain.id()))") + io.a2a.grpc.SubscribeToTaskRequest toProtoSubscribeToTaskRequest(TaskIdParams domain); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java new file mode 100644 index 000000000..a76cb259b --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java @@ -0,0 +1,42 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.Task; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.Task} and {@link io.a2a.grpc.Task}. + *

+ * Uses ADDER_PREFERRED strategy for List fields (artifacts, history) + * to use addAllArtifacts() and addAllHistory() methods. + */ +@Mapper(config = A2AProtoMapperConfig.class, + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, + uses = {TaskStatusMapper.class, ArtifactMapper.class, MessageMapper.class, A2ACommonFieldMapper.class}) +public interface TaskMapper { + + TaskMapper INSTANCE = A2AMappers.getMapper(TaskMapper.class); + + /** + * Converts domain Task to proto Task. + * Uses CommonFieldMapper for metadata conversion and ADDER_PREFERRED for lists. + */ + @Mapping(target = "id", source = "id", conditionExpression = "java(domain.getId() != null)") + @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(domain.getContextId() != null)") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + io.a2a.grpc.Task toProto(Task domain); + + /** + * Converts proto Task to domain Task. + * Handles empty string → null and Struct conversions via CommonFieldMapper. + * Uses Builder pattern explicitly configured via @BeanMapping. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", source = "id", qualifiedByName = "emptyToNull") + @Mapping(target = "contextId", source = "contextId", qualifiedByName = "emptyToNull") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + Task fromProto(io.a2a.grpc.Task proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java new file mode 100644 index 000000000..5978b7419 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java @@ -0,0 +1,115 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.TaskPushNotificationConfig; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * MapStruct mapper for TaskPushNotificationConfig with resource name handling. + *

+ * This mapper provides compile-time safety while handling the Google Cloud resource name + * convention where the protobuf has a single "name" field in the format + * "tasks/{task_id}/pushNotificationConfigs/{config_id}", but the domain model has separate + * taskId and configId fields. + *

+ * Resource Name Format: tasks/{task_id}/pushNotificationConfigs/{config_id} + *

+ * Compile-Time Safety: If the proto adds/removes/renames fields, MapStruct will + * fail to compile, ensuring we update the domain model accordingly. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {PushNotificationConfigMapper.class}) +public interface TaskPushNotificationConfigMapper { + + TaskPushNotificationConfigMapper INSTANCE = A2AMappers.getMapper(TaskPushNotificationConfigMapper.class); + + /** + * Converts domain TaskPushNotificationConfig to protobuf TaskPushNotificationConfig. + *

+ * Synthesizes the resource name from taskId and configId following the format: + * "tasks/{task_id}/pushNotificationConfigs/{config_id}" + * + * @param config the domain TaskPushNotificationConfig + * @return protobuf TaskPushNotificationConfig with synthesized resource name + */ + @Mapping(target = "name", expression = "java(buildResourceName(config))") + @Mapping(target = "pushNotificationConfig", source = "pushNotificationConfig") + io.a2a.grpc.TaskPushNotificationConfig toProto(TaskPushNotificationConfig config); + + /** + * Converts protobuf TaskPushNotificationConfig to domain TaskPushNotificationConfig. + *

+ * Parses the resource name to extract taskId and configId, then creates the domain object. + * The configId is injected into the PushNotificationConfig if it differs from what's in the proto. + * + * @param proto the protobuf TaskPushNotificationConfig + * @return domain TaskPushNotificationConfig with extracted taskId and configId + * @throws IllegalArgumentException if the resource name format is invalid + */ + @Mapping(target = "taskId", expression = "java(extractTaskId(proto))") + @Mapping(target = "pushNotificationConfig", expression = "java(mapPushNotificationConfigWithId(proto))") + TaskPushNotificationConfig fromProto(io.a2a.grpc.TaskPushNotificationConfig proto); + + /** + * Builds the resource name from domain model fields. + *

+ * Format: "tasks/{task_id}/pushNotificationConfigs/{config_id}" + * If configId is null, format: "tasks/{task_id}/pushNotificationConfigs" + * + * @param config the domain TaskPushNotificationConfig + * @return the synthesized resource name + */ + default String buildResourceName(TaskPushNotificationConfig config) { + String taskId = config.taskId(); + String configId = config.pushNotificationConfig().id(); + + if (configId == null || configId.isEmpty()) { + return "tasks/" + taskId + "/pushNotificationConfigs"; + } + return "tasks/" + taskId + "/pushNotificationConfigs/" + configId; + } + + /** + * Extracts the task ID from the protobuf resource name. + * + * @param proto the protobuf TaskPushNotificationConfig + * @return the extracted task ID + * @throws IllegalArgumentException if the resource name format is invalid + */ + default String extractTaskId(io.a2a.grpc.TaskPushNotificationConfig proto) { + String[] parts = ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName()); + return parts[0]; // taskId + } + + /** + * Maps the protobuf PushNotificationConfig to domain, injecting the configId from the resource name. + *

+ * The configId is parsed from the resource name and may override the ID in the proto's + * PushNotificationConfig if they differ. + * + * @param proto the protobuf TaskPushNotificationConfig + * @return domain PushNotificationConfig with correct ID + * @throws IllegalArgumentException if the resource name format is invalid + */ + default PushNotificationConfig mapPushNotificationConfigWithId(io.a2a.grpc.TaskPushNotificationConfig proto) { + // Parse configId from resource name + String[] parts = ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName()); + String configId = parts[1]; // configId + + // Check if proto has PushNotificationConfig + if (!proto.hasPushNotificationConfig() || + proto.getPushNotificationConfig().equals(io.a2a.grpc.PushNotificationConfig.getDefaultInstance())) { + return null; + } + + // Map the proto PushNotificationConfig + PushNotificationConfig result = PushNotificationConfigMapper.INSTANCE.fromProto(proto.getPushNotificationConfig()); + + // Override ID if configId from resource name differs from the one in PushNotificationConfig + if (configId != null && !configId.isEmpty() && !configId.equals(result.id())) { + return new PushNotificationConfig(result.url(), result.token(), result.authentication(), configId); + } + + return result; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java new file mode 100644 index 000000000..0d91a538e --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java @@ -0,0 +1,33 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.TaskQueryParams; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.grpc.GetTaskRequest} and {@link io.a2a.spec.TaskQueryParams}. + *

+ * Extracts task ID from resource name format "tasks/{id}" using {@link ResourceNameParser}. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface TaskQueryParamsMapper { + + TaskQueryParamsMapper INSTANCE = A2AMappers.getMapper(TaskQueryParamsMapper.class); + + /** + * Converts proto GetTaskRequest to domain TaskQueryParams. + * Extracts task ID from the resource name. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") + @Mapping(target = "historyLength", source = "historyLength") + @Mapping(target = "metadata", ignore = true) + TaskQueryParams fromProto(io.a2a.grpc.GetTaskRequest proto); + + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "name", expression = "java(ResourceNameParser.defineTaskName(domain.id()))") + @Mapping(target = "historyLength", source = "historyLength") + io.a2a.grpc.GetTaskRequest toProto(TaskQueryParams domain); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStateMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStateMapper.java new file mode 100644 index 000000000..f151be7c2 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStateMapper.java @@ -0,0 +1,68 @@ +package io.a2a.grpc.mapper; + +import org.mapstruct.Mapper; + +/** + * Mapper between {@link io.a2a.spec.TaskState} and {@link io.a2a.grpc.TaskState}. + *

+ * Handles the conversion between domain TaskState enum (with string-based wire format) + * and protobuf TaskState enum (with integer-based wire format and TASK_STATE_ prefix). + *

+ * Note: Proto uses CANCELLED spelling while domain uses CANCELED. + *

+ * Manual Implementation Required: Uses manual switch statements instead of @ValueMapping + * to avoid mapstruct-spi-protobuf enum strategy initialization issues. + */ +@Mapper(config = A2AProtoMapperConfig.class) +public interface TaskStateMapper { + + TaskStateMapper INSTANCE = A2AMappers.getMapper(TaskStateMapper.class); + + /** + * Converts domain TaskState to proto TaskState. + * + * @param domain the domain task state + * @return the proto task state, or TASK_STATE_UNSPECIFIED if input is null + */ + default io.a2a.grpc.TaskState toProto(io.a2a.spec.TaskState domain) { + if (domain == null) { + return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; + } + + return switch (domain) { + case SUBMITTED -> io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED; + case WORKING -> io.a2a.grpc.TaskState.TASK_STATE_WORKING; + case INPUT_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED; + case AUTH_REQUIRED -> io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED; + case COMPLETED -> io.a2a.grpc.TaskState.TASK_STATE_COMPLETED; + case CANCELED -> io.a2a.grpc.TaskState.TASK_STATE_CANCELLED; // Note spelling difference + case FAILED -> io.a2a.grpc.TaskState.TASK_STATE_FAILED; + case REJECTED -> io.a2a.grpc.TaskState.TASK_STATE_REJECTED; + case UNKNOWN -> io.a2a.grpc.TaskState.UNRECOGNIZED; + }; + } + + /** + * Converts proto TaskState to domain TaskState. + * + * @param proto the proto task state + * @return the domain task state, or UNKNOWN if input is null or unrecognized + */ + default io.a2a.spec.TaskState fromProto(io.a2a.grpc.TaskState proto) { + if (proto == null) { + return io.a2a.spec.TaskState.UNKNOWN; + } + + return switch (proto) { + case TASK_STATE_SUBMITTED -> io.a2a.spec.TaskState.SUBMITTED; + case TASK_STATE_WORKING -> io.a2a.spec.TaskState.WORKING; + case TASK_STATE_INPUT_REQUIRED -> io.a2a.spec.TaskState.INPUT_REQUIRED; + case TASK_STATE_AUTH_REQUIRED -> io.a2a.spec.TaskState.AUTH_REQUIRED; + case TASK_STATE_COMPLETED -> io.a2a.spec.TaskState.COMPLETED; + case TASK_STATE_CANCELLED -> io.a2a.spec.TaskState.CANCELED; // Note spelling difference + case TASK_STATE_FAILED -> io.a2a.spec.TaskState.FAILED; + case TASK_STATE_REJECTED -> io.a2a.spec.TaskState.REJECTED; + case TASK_STATE_UNSPECIFIED, UNRECOGNIZED -> io.a2a.spec.TaskState.UNKNOWN; + }; + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusMapper.java new file mode 100644 index 000000000..f6c32c81c --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusMapper.java @@ -0,0 +1,37 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.TaskStatus; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.TaskStatus} and {@link io.a2a.grpc.TaskStatus}. + *

+ * Handles conversion of task status including state, optional message, and timestamp. + * Uses TaskStateMapper for state conversion, MessageMapper for message conversion, + * and CommonFieldMapper for timestamp conversion. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {TaskStateMapper.class, MessageMapper.class, A2ACommonFieldMapper.class}) +public interface TaskStatusMapper { + + TaskStatusMapper INSTANCE = A2AMappers.getMapper(TaskStatusMapper.class); + + /** + * Converts domain TaskStatus to proto TaskStatus. + * Uses MessageMapper for message and CommonFieldMapper for timestamp conversion. + */ + @Mapping(target = "state", source = "state", conditionExpression = "java(domain.state() != null)") + @Mapping(target = "message", source = "message", conditionExpression = "java(domain.message() != null)") + @Mapping(target = "timestamp", source = "timestamp", qualifiedByName = "offsetDateTimeToProtoTimestamp") + io.a2a.grpc.TaskStatus toProto(TaskStatus domain); + + /** + * Converts proto TaskStatus to domain TaskStatus. + * Uses MessageMapper for message and CommonFieldMapper for timestamp conversion. + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "timestamp", source = "timestamp", qualifiedByName = "protoTimestampToOffsetDateTime") + TaskStatus fromProto(io.a2a.grpc.TaskStatus proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java new file mode 100644 index 000000000..fb6cce46f --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java @@ -0,0 +1,37 @@ +package io.a2a.grpc.mapper; + +import io.a2a.spec.TaskStatusUpdateEvent; +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +/** + * Mapper between {@link io.a2a.spec.TaskStatusUpdateEvent} and {@link io.a2a.grpc.TaskStatusUpdateEvent}. + *

+ * Now fully declarative using Builder pattern with @BeanMapping. + * Builder's isFinal() method handles the Java "final" keyword mapping. + */ +@Mapper(config = A2AProtoMapperConfig.class, uses = {TaskStatusMapper.class, A2ACommonFieldMapper.class}) +public interface TaskStatusUpdateEventMapper { + + TaskStatusUpdateEventMapper INSTANCE = A2AMappers.getMapper(TaskStatusUpdateEventMapper.class); + + /** + * Converts domain TaskStatusUpdateEvent to proto. + * Uses declarative mapping with CommonFieldMapper for metadata conversion. + */ + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") + @Mapping(target = "final", source = "final") + io.a2a.grpc.TaskStatusUpdateEvent toProto(TaskStatusUpdateEvent domain); + + /** + * Converts proto TaskStatusUpdateEvent to domain. + * Now fully declarative using Builder pattern configured via @BeanMapping. + * MapStruct automatically maps proto.getFinal() → builder.isFinal(). + */ + @BeanMapping(builder = @Builder(buildMethod = "build")) + @Mapping(target = "isFinal", source = "final") + @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") + TaskStatusUpdateEvent fromProto(io.a2a.grpc.TaskStatusUpdateEvent proto); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java new file mode 100644 index 000000000..01e24c8f6 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -0,0 +1,553 @@ +package io.a2a.grpc.utils; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.Strictness; +import com.google.gson.stream.JsonWriter; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.CancelTaskRequest; +import io.a2a.spec.CancelTaskResponse; +import io.a2a.spec.ContentTypeNotSupportedError; +import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; +import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; +import io.a2a.spec.GetAuthenticatedExtendedCardResponse; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskPushNotificationConfigResponse; +import io.a2a.spec.GetTaskRequest; +import io.a2a.spec.GetTaskResponse; +import io.a2a.spec.IdJsonMappingException; +import io.a2a.spec.InvalidAgentResponseError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidParamsJsonMappingException; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.JSONRPCMessage; +import io.a2a.spec.JSONRPCRequest; +import io.a2a.spec.JSONRPCResponse; +import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.ListTasksRequest; +import io.a2a.spec.ListTasksResponse; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.MethodNotFoundJsonMappingException; +import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.SendMessageRequest; +import io.a2a.spec.SendMessageResponse; +import io.a2a.spec.SendStreamingMessageRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.SetTaskPushNotificationConfigResponse; +import io.a2a.spec.SubscribeToTaskRequest; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskNotFoundError; +import io.a2a.spec.UnsupportedOperationError; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; + +import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; + +/** + * Utilities for converting between JSON-RPC 2.0 messages and Protocol Buffer objects. + *

+ * This class provides a unified strategy for handling JSON-RPC requests and responses in the A2A SDK + * by bridging the JSON-RPC transport layer with Protocol Buffer-based internal representations. + * + *

Conversion Strategy

+ * The conversion process follows a two-step approach: + *
    + *
  1. JSON → Proto: JSON-RPC messages are parsed using Gson, then converted to Protocol Buffer + * objects using Google's {@link JsonFormat} parser. This ensures consistent handling of field names, + * types, and nested structures according to the proto3 specification.
  2. + *
  3. Proto → Spec: Protocol Buffer objects are converted to A2A spec objects using + * {@link ProtoUtils.FromProto} converters, which handle type mappings and create immutable + * spec-compliant Java objects.
  4. + *
+ * + *

Request Processing Flow

+ *
+ * Incoming JSON-RPC Request
+ *   ↓ parseRequestBody(String)
+ * Validate version, id, method
+ *   ↓ parseMethodRequest()
+ * Parse params → Proto Builder
+ *   ↓ ProtoUtils.FromProto.*
+ * Create JSONRPCRequest<?> with spec objects
+ * 
+ * + *

Response Processing Flow

+ *
+ * Incoming JSON-RPC Response
+ *   ↓ parseResponseBody(String, String)
+ * Validate version, id, check for errors
+ *   ↓ Parse result/error
+ * Proto Builder → spec objects
+ *   ↓ ProtoUtils.FromProto.*
+ * Create JSONRPCResponse<?> with result or error
+ * 
+ * + *

Serialization Flow

+ *
+ * Proto MessageOrBuilder
+ *   ↓ JsonFormat.printer()
+ * Proto JSON string
+ *   ↓ Gson JsonWriter
+ * Complete JSON-RPC envelope
+ * 
+ * + *

Error Handling

+ * The class provides detailed error messages for common failure scenarios: + *
    + *
  • Missing/invalid method: Returns {@link MethodNotFoundError} with the invalid method name
  • + *
  • Invalid parameters: Returns {@link InvalidParamsError} with proto parsing details
  • + *
  • Protocol version mismatch: Returns {@link InvalidRequestError} with version info
  • + *
  • Missing/invalid id: Returns {@link InvalidRequestError} with id validation details
  • + *
+ * + *

Thread Safety

+ * This class is thread-safe. All methods are stateless and use immutable shared resources + * ({@link Gson} instance is thread-safe, proto builders are created per-invocation). + * + *

Usage Example

+ *
{@code
+ * // Parse incoming JSON-RPC request
+ * String jsonRequest = """
+ *     {"jsonrpc":"2.0","id":1,"method":"tasks.get","params":{"name":"tasks/task-123"}}
+ *     """;
+ * JSONRPCRequest request = JSONRPCUtils.parseRequestBody(jsonRequest);
+ *
+ * // Create JSON-RPC request from proto
+ * io.a2a.grpc.GetTaskRequest protoRequest = ...;
+ * String json = JSONRPCUtils.toJsonRPCRequest("req-1", "tasks.get", protoRequest);
+ *
+ * // Create JSON-RPC response from proto
+ * io.a2a.grpc.Task protoTask = ...;
+ * String response = JSONRPCUtils.toJsonRPCResultResponse("req-1", protoTask);
+ * }
+ * + * @see ProtoUtils + * @see JSONRPCRequest + * @see JSONRPCResponse + * @see JSON-RPC 2.0 Specification + */ +public class JSONRPCUtils { + + private static final Logger log = Logger.getLogger(JSONRPCUtils.class.getName()); + private static final Gson GSON = new GsonBuilder().setStrictness(Strictness.STRICT).create(); + + public static JSONRPCRequest parseRequestBody(String body) throws JsonProcessingException { + JsonElement jelement = JsonParser.parseString(body); + JsonObject jsonRpc = jelement.getAsJsonObject(); + if (!jsonRpc.has("method")) { + throw new IdJsonMappingException( + "JSON-RPC request missing required 'method' field. Request must include: jsonrpc, id, method, and params.", + getIdIfPossible(jsonRpc)); + } + String version = getAndValidateJsonrpc(jsonRpc); + Object id = getAndValidateId(jsonRpc); + String method = jsonRpc.get("method").getAsString(); + JsonElement paramsNode = jsonRpc.get("params"); + + try { + return parseMethodRequest(version, id, method, paramsNode); + } catch (InvalidParamsError e) { + throw new InvalidParamsJsonMappingException(e.getMessage(), id); + } + } + + private static JSONRPCRequest parseMethodRequest(String version, Object id, String method, JsonElement paramsNode) throws InvalidParamsError, MethodNotFoundJsonMappingException { + switch (method) { + case GetTaskRequest.METHOD -> { + io.a2a.grpc.GetTaskRequest.Builder builder = io.a2a.grpc.GetTaskRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new GetTaskRequest(version, id, method, ProtoUtils.FromProto.taskQueryParams(builder)); + } + case CancelTaskRequest.METHOD -> { + io.a2a.grpc.CancelTaskRequest.Builder builder = io.a2a.grpc.CancelTaskRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new CancelTaskRequest(version, id, method, ProtoUtils.FromProto.taskIdParams(builder)); + } + case ListTasksRequest.METHOD -> { + io.a2a.grpc.ListTasksRequest.Builder builder = io.a2a.grpc.ListTasksRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new ListTasksRequest(version, id, method, ProtoUtils.FromProto.listTasksParams(builder)); + } + case SetTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new SetTaskPushNotificationConfigRequest(version, id, method, ProtoUtils.FromProto.setTaskPushNotificationConfig(builder)); + } + case GetTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new GetTaskPushNotificationConfigRequest(version, id, method, ProtoUtils.FromProto.getTaskPushNotificationConfigParams(builder)); + } + case SendMessageRequest.METHOD -> { + io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new SendMessageRequest(version, id, method, ProtoUtils.FromProto.messageSendParams(builder)); + } + case ListTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new ListTaskPushNotificationConfigRequest(version, id, method, ProtoUtils.FromProto.listTaskPushNotificationConfigParams(builder)); + } + case DeleteTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new DeleteTaskPushNotificationConfigRequest(version, id, method, ProtoUtils.FromProto.deleteTaskPushNotificationConfigParams(builder)); + } + case GetAuthenticatedExtendedCardRequest.METHOD -> { + return new GetAuthenticatedExtendedCardRequest(version, id, method, null); + } + case SendStreamingMessageRequest.METHOD -> { + io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new SendStreamingMessageRequest(version, id, method, ProtoUtils.FromProto.messageSendParams(builder)); + } + case SubscribeToTaskRequest.METHOD -> { + io.a2a.grpc.SubscribeToTaskRequest.Builder builder = io.a2a.grpc.SubscribeToTaskRequest.newBuilder(); + parseRequestBody(paramsNode, builder); + return new SubscribeToTaskRequest(version, id, method, ProtoUtils.FromProto.taskIdParams(builder)); + } + default -> + throw new MethodNotFoundJsonMappingException("Unsupported JSON-RPC method: '" + method + "'", id); + } + } + + public static StreamResponse parseResponseEvent(String body) throws JsonProcessingException { + JsonElement jelement = JsonParser.parseString(body); + JsonObject jsonRpc = jelement.getAsJsonObject(); + String version = getAndValidateJsonrpc(jsonRpc); + Object id = getAndValidateId(jsonRpc); + JsonElement paramsNode = jsonRpc.get("result"); + if (jsonRpc.has("error")) { + throw processError(jsonRpc.getAsJsonObject("error")); + } + StreamResponse.Builder builder = StreamResponse.newBuilder(); + parseRequestBody(paramsNode, builder); + return builder.build(); + } + + public static JSONRPCResponse parseResponseBody(String body, String method) throws JsonProcessingException { + JsonElement jelement = JsonParser.parseString(body); + JsonObject jsonRpc = jelement.getAsJsonObject(); + String version = getAndValidateJsonrpc(jsonRpc); + Object id = getAndValidateId(jsonRpc); + JsonElement paramsNode = jsonRpc.get("result"); + if (jsonRpc.has("error")) { + return parseError(jsonRpc.getAsJsonObject("error"), id, method); + } + switch (method) { + case GetTaskRequest.METHOD -> { + io.a2a.grpc.Task.Builder builder = io.a2a.grpc.Task.newBuilder(); + parseRequestBody(paramsNode, builder); + return new GetTaskResponse(id, ProtoUtils.FromProto.task(builder)); + } + case CancelTaskRequest.METHOD -> { + io.a2a.grpc.Task.Builder builder = io.a2a.grpc.Task.newBuilder(); + parseRequestBody(paramsNode, builder); + return new CancelTaskResponse(id, ProtoUtils.FromProto.task(builder)); + } + case ListTasksRequest.METHOD -> { + io.a2a.grpc.ListTasksResponse.Builder builder = io.a2a.grpc.ListTasksResponse.newBuilder(); + parseRequestBody(paramsNode, builder); + return new ListTasksResponse(id, ProtoUtils.FromProto.listTasksResult(builder)); + } + case SetTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); + parseRequestBody(paramsNode, builder); + return new SetTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.taskPushNotificationConfig(builder)); + } + case GetTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); + parseRequestBody(paramsNode, builder); + return new GetTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.taskPushNotificationConfig(builder)); + } + case SendMessageRequest.METHOD -> { + io.a2a.grpc.SendMessageResponse.Builder builder = io.a2a.grpc.SendMessageResponse.newBuilder(); + parseRequestBody(paramsNode, builder); + if (builder.hasMsg()) { + return new SendMessageResponse(id, ProtoUtils.FromProto.message(builder.getMsg())); + } + return new SendMessageResponse(id, ProtoUtils.FromProto.task(builder.getTask())); + } + case ListTaskPushNotificationConfigRequest.METHOD -> { + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); + parseRequestBody(paramsNode, builder); + return new ListTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.listTaskPushNotificationConfigParams(builder)); + } + case DeleteTaskPushNotificationConfigRequest.METHOD -> { + return new DeleteTaskPushNotificationConfigResponse(id); + } + case GetAuthenticatedExtendedCardRequest.METHOD -> { + io.a2a.grpc.AgentCard.Builder builder = io.a2a.grpc.AgentCard.newBuilder(); + parseRequestBody(paramsNode, builder); + return new GetAuthenticatedExtendedCardResponse(id, ProtoUtils.FromProto.agentCard(builder)); + } + default -> + throw new MethodNotFoundJsonMappingException("Unsupported JSON-RPC method: '" + method + "' in response parsing.", getIdIfPossible(jsonRpc)); + } + } + + public static JSONRPCResponse parseError(JsonObject error, Object id, String method) throws JsonProcessingException { + JSONRPCError rpcError = processError(error); + switch (method) { + case GetTaskRequest.METHOD -> { + return new GetTaskResponse(id, rpcError); + } + case CancelTaskRequest.METHOD -> { + return new CancelTaskResponse(id, rpcError); + } + case ListTasksRequest.METHOD -> { + return new ListTasksResponse(id, rpcError); + } + case SetTaskPushNotificationConfigRequest.METHOD -> { + return new SetTaskPushNotificationConfigResponse(id, rpcError); + } + case GetTaskPushNotificationConfigRequest.METHOD -> { + return new GetTaskPushNotificationConfigResponse(id, rpcError); + } + case SendMessageRequest.METHOD -> { + return new SendMessageResponse(id, rpcError); + } + case ListTaskPushNotificationConfigRequest.METHOD -> { + return new ListTaskPushNotificationConfigResponse(id, rpcError); + } + case DeleteTaskPushNotificationConfigRequest.METHOD -> { + return new DeleteTaskPushNotificationConfigResponse(id, rpcError); + } + default -> + throw new MethodNotFoundJsonMappingException("Unsupported JSON-RPC method: '" + method + "'", id); + } + } + + private static JSONRPCError processError(JsonObject error) { + String message = error.has("message") ? error.get("message").getAsString() : null; + Integer code = error.has("code") ? error.get("code").getAsInt() : null; + String data = error.has("data") ? error.get("data").toString() : null; + if (code != null) { + switch (code) { + case JSON_PARSE_ERROR_CODE: + return new JSONParseError(code, message, data); + case INVALID_REQUEST_ERROR_CODE: + return new InvalidRequestError(code, message, data); + case METHOD_NOT_FOUND_ERROR_CODE: + return new MethodNotFoundError(code, message, data); + case INVALID_PARAMS_ERROR_CODE: + return new InvalidParamsError(code, message, data); + case INTERNAL_ERROR_CODE: + return new io.a2a.spec.InternalError(code, message, data); + case PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE: + return new PushNotificationNotSupportedError(code, message, data); + case UNSUPPORTED_OPERATION_ERROR_CODE: + return new UnsupportedOperationError(code, message, data); + case CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE: + return new ContentTypeNotSupportedError(code, message, data); + case INVALID_AGENT_RESPONSE_ERROR_CODE: + return new InvalidAgentResponseError(code, message, data); + case TASK_NOT_CANCELABLE_ERROR_CODE: + return new TaskNotCancelableError(code, message, data); + case TASK_NOT_FOUND_ERROR_CODE: + return new TaskNotFoundError(code, message, data); + default: + return new JSONRPCError(code, message, data); + } + } + return new JSONRPCError(code, message, data); + } + + protected static void parseRequestBody(JsonElement jsonRpc, com.google.protobuf.Message.Builder builder) throws JSONRPCError { + try (Writer writer = new StringWriter()) { + GSON.toJson(jsonRpc, writer); + parseJsonString(writer.toString(), builder); + } catch (IOException e) { + log.log(Level.SEVERE, "Failed to serialize JSON element to string during proto conversion. JSON: {0}", jsonRpc); + log.log(Level.SEVERE, "Serialization error details", e); + throw new InvalidParamsError( + "Failed to parse request content. " + + "This may indicate invalid JSON structure or unsupported field types. Error: " + e.getMessage()); + } + } + + public static void parseJsonString(String body, com.google.protobuf.Message.Builder builder) throws JSONRPCError { + try { + JsonFormat.parser().merge(body, builder); + } catch (InvalidProtocolBufferException e) { + log.log(Level.SEVERE, "Protocol buffer parsing failed for JSON: {0}", body); + log.log(Level.SEVERE, "Proto parsing error details", e); + throw new InvalidParamsError( + "Invalid request content: " + extractProtoErrorMessage(e) + + ". Please verify the request matches the expected schema for this method."); + } + } + + /** + * Extracts a user-friendly error message from Protocol Buffer parsing exceptions. + * + * @param e the InvalidProtocolBufferException + * @return a cleaned error message with field information + */ + private static String extractProtoErrorMessage(InvalidProtocolBufferException e) { + String message = e.getMessage(); + if (message == null) { + return "unknown parsing error"; + } + // Extract field name if present in error message + if (message.contains("Cannot find field:")) { + return message.substring(message.indexOf("Cannot find field:")); + } + if (message.contains("Invalid value for")) { + return message.substring(message.indexOf("Invalid value for")); + } + return message; + } + + protected static String getAndValidateJsonrpc(JsonObject jsonRpc) throws JsonMappingException { + if (!jsonRpc.has("jsonrpc")) { + throw new IdJsonMappingException( + "Missing required 'jsonrpc' field. All requests must include 'jsonrpc': '2.0'", + getIdIfPossible(jsonRpc)); + } + String version = jsonRpc.get("jsonrpc").getAsString(); + if (!JSONRPCMessage.JSONRPC_VERSION.equals(version)) { + throw new IdJsonMappingException( + "Unsupported JSON-RPC version: '" + version + "'. Expected version '2.0'", + getIdIfPossible(jsonRpc)); + } + return version; + } + + /** + * Try to get the request id if possible , returns "UNDETERMINED ID" otherwise. + * This should be only used for errors. + * @param jsonRpc the json rpc JSON. + * @return the request id if possible , "UNDETERMINED ID" otherwise. + */ + protected static Object getIdIfPossible(JsonObject jsonRpc) { + try { + return getAndValidateId(jsonRpc); + } catch (JsonProcessingException e) { + // id can't be determined + return "UNDETERMINED ID"; + } + } + + protected static Object getAndValidateId(JsonObject jsonRpc) throws JsonProcessingException { + Object id = null; + if (jsonRpc.has("id")) { + if (jsonRpc.get("id").isJsonPrimitive()) { + try { + id = jsonRpc.get("id").getAsInt(); + } catch (UnsupportedOperationException | NumberFormatException | IllegalStateException e) { + id = jsonRpc.get("id").getAsString(); + } + } else { + throw new JsonMappingException(null, "Invalid 'id' type: " + jsonRpc.get("id").getClass().getSimpleName() + + ". ID must be a JSON string or number, not an object or array."); + } + } + if (id == null) { + throw new JsonMappingException(null, "Request 'id' cannot be null. Use a string or number identifier."); + } + return id; + } + + public static String toJsonRPCRequest(@Nullable String requestId, String method, com.google.protobuf.@Nullable MessageOrBuilder payload) { + try (StringWriter result = new StringWriter(); JsonWriter output = GSON.newJsonWriter(result)) { + output.beginObject(); + output.name("jsonrpc").value("2.0"); + String id = requestId; + if (requestId == null) { + id = UUID.randomUUID().toString(); + } + output.name("id").value(id); + if (method != null) { + output.name("method").value(method); + } + if (payload != null) { + String resultValue = JsonFormat.printer().omittingInsignificantWhitespace().print(payload); + output.name("params").jsonValue(resultValue); + } + output.endObject(); + return result.toString(); + } catch (IOException ex) { + throw new RuntimeException( + "Failed to serialize JSON-RPC request for method '" + method + "'. " + + "This indicates an internal error in JSON generation. Request ID: " + requestId, ex); + } + } + + public static String toJsonRPCResultResponse(Object requestId, com.google.protobuf.MessageOrBuilder builder) { + try (StringWriter result = new StringWriter(); JsonWriter output = GSON.newJsonWriter(result)) { + output.beginObject(); + output.name("jsonrpc").value("2.0"); + if (requestId != null) { + if (requestId instanceof String string) { + output.name("id").value(string); + } else if (requestId instanceof Number number) { + output.name("id").value(number); + } + } + String resultValue = JsonFormat.printer().omittingInsignificantWhitespace().print(builder); + output.name("result").jsonValue(resultValue); + output.endObject(); + return result.toString(); + } catch (IOException ex) { + throw new RuntimeException( + "Failed to serialize JSON-RPC success response. " + + "Proto type: " + builder.getClass().getSimpleName() + ", Request ID: " + requestId, ex); + } + } + + public static String toJsonRPCErrorResponse(Object requestId, JSONRPCError error) { + try (StringWriter result = new StringWriter(); JsonWriter output = GSON.newJsonWriter(result)) { + output.beginObject(); + output.name("jsonrpc").value("2.0"); + if (requestId != null) { + if (requestId instanceof String string) { + output.name("id").value(string); + } else if (requestId instanceof Number number) { + output.name("id").value(number); + } + } + output.name("error"); + output.beginObject(); + output.name("code").value(error.getCode()); + output.name("message").value(error.getMessage()); + if (error.getData() != null) { + output.name("data").value(error.getData().toString()); + } + output.endObject(); + output.endObject(); + return result.toString(); + } catch (IOException ex) { + throw new RuntimeException( + "Failed to serialize JSON-RPC error response. " + + "Error code: " + error.getCode() + ", Request ID: " + requestId, ex); + } + } +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 3589f7ae2..44f6a6b82 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -1,55 +1,39 @@ package io.a2a.grpc.utils; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; +import io.a2a.grpc.GetExtendedAgentCardRequest; +import io.a2a.grpc.StreamResponse; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import com.google.protobuf.ByteString; -import com.google.protobuf.Struct; -import com.google.protobuf.Value; -import io.a2a.grpc.StreamResponse; -import io.a2a.spec.APIKeySecurityScheme; -import io.a2a.spec.AgentCapabilities; +import io.a2a.grpc.mapper.AgentCardMapper; +import io.a2a.grpc.mapper.DeleteTaskPushNotificationConfigParamsMapper; +import io.a2a.grpc.mapper.GetTaskPushNotificationConfigParamsMapper; +import io.a2a.grpc.mapper.ListTaskPushNotificationConfigParamsMapper; +import io.a2a.grpc.mapper.ListTasksParamsMapper; +import io.a2a.grpc.mapper.ListTasksResultMapper; +import io.a2a.grpc.mapper.MessageMapper; +import io.a2a.grpc.mapper.MessageSendConfigurationMapper; +import io.a2a.grpc.mapper.MessageSendParamsMapper; +import io.a2a.grpc.mapper.SetTaskPushNotificationConfigMapper; +import io.a2a.grpc.mapper.StreamResponseMapper; +import io.a2a.grpc.mapper.TaskArtifactUpdateEventMapper; +import io.a2a.grpc.mapper.TaskIdParamsMapper; +import io.a2a.grpc.mapper.TaskMapper; +import io.a2a.grpc.mapper.TaskPushNotificationConfigMapper; +import io.a2a.grpc.mapper.TaskQueryParamsMapper; +import io.a2a.grpc.mapper.TaskStateMapper; +import io.a2a.grpc.mapper.TaskStatusUpdateEventMapper; import io.a2a.spec.AgentCard; -import io.a2a.spec.AgentCardSignature; -import io.a2a.spec.AgentExtension; -import io.a2a.spec.AgentInterface; -import io.a2a.spec.AgentProvider; -import io.a2a.spec.AgentSkill; -import io.a2a.spec.Artifact; -import io.a2a.spec.AuthorizationCodeOAuthFlow; -import io.a2a.spec.ClientCredentialsOAuthFlow; -import io.a2a.spec.DataPart; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; -import io.a2a.spec.FileContent; -import io.a2a.spec.FilePart; -import io.a2a.spec.FileWithBytes; -import io.a2a.spec.FileWithUri; +import io.a2a.spec.GetAuthenticatedExtendedCardRequest; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.HTTPAuthSecurityScheme; -import io.a2a.spec.ImplicitOAuthFlow; import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.InvalidRequestError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; +import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; -import io.a2a.spec.MutualTLSSecurityScheme; -import io.a2a.spec.OAuth2SecurityScheme; -import io.a2a.spec.OAuthFlows; -import io.a2a.spec.OpenIdConnectSecurityScheme; -import io.a2a.spec.Part; -import io.a2a.spec.PasswordOAuthFlow; -import io.a2a.spec.PushNotificationAuthenticationInfo; -import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SecurityScheme; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -57,10 +41,7 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; -import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.spec.TextPart; -import org.jspecify.annotations.Nullable; /** * Utility class to convert between GRPC and Spec objects. @@ -70,622 +51,91 @@ public class ProtoUtils { public static class ToProto { public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { - io.a2a.grpc.AgentCard.Builder builder = io.a2a.grpc.AgentCard.newBuilder(); - if (agentCard.protocolVersion() != null) { - builder.setProtocolVersion(agentCard.protocolVersion()); - } - if (agentCard.name() != null) { - builder.setName(agentCard.name()); - } - if (agentCard.description() != null) { - builder.setDescription(agentCard.description()); - } - if (agentCard.url() != null) { - builder.setUrl(agentCard.url()); - } - if (agentCard.preferredTransport() != null) { - builder.setPreferredTransport(agentCard.preferredTransport()); - } - if (agentCard.additionalInterfaces() != null) { - builder.addAllAdditionalInterfaces(agentCard.additionalInterfaces().stream().map(item -> agentInterface(item)).collect(Collectors.toList())); - } - if (agentCard.provider() != null) { - builder.setProvider(agentProvider(agentCard.provider())); - } - if (agentCard.version() != null) { - builder.setVersion(agentCard.version()); - } - if (agentCard.documentationUrl() != null) { - builder.setDocumentationUrl(agentCard.documentationUrl()); - } - if (agentCard.capabilities() != null) { - builder.setCapabilities(agentCapabilities(agentCard.capabilities())); - } - if (agentCard.securitySchemes() != null) { - builder.putAllSecuritySchemes( - agentCard.securitySchemes().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> securityScheme(e.getValue()))) - ); - } - if (agentCard.security() != null) { - builder.addAllSecurity(agentCard.security().stream().map(s -> { - io.a2a.grpc.Security.Builder securityBuilder = io.a2a.grpc.Security.newBuilder(); - s.forEach((key, value) -> { - io.a2a.grpc.StringList.Builder stringListBuilder = io.a2a.grpc.StringList.newBuilder(); - stringListBuilder.addAllList(value); - securityBuilder.putSchemes(key, stringListBuilder.build()); - }); - return securityBuilder.build(); - }).collect(Collectors.toList())); - } - if (agentCard.defaultInputModes() != null) { - builder.addAllDefaultInputModes(agentCard.defaultInputModes()); - } - if (agentCard.defaultOutputModes() != null) { - builder.addAllDefaultOutputModes(agentCard.defaultOutputModes()); - } - if (agentCard.skills() != null) { - builder.addAllSkills(agentCard.skills().stream().map(ToProto::agentSkill).collect(Collectors.toList())); - } - builder.setSupportsAuthenticatedExtendedCard(agentCard.supportsAuthenticatedExtendedCard()); - if (agentCard.signatures() != null) { - builder.addAllSignatures(agentCard.signatures().stream().map(ToProto::agentCardSignature).collect(Collectors.toList())); - } - if (agentCard.iconUrl() != null) { - builder.setIconUrl(agentCard.iconUrl()); - } - return builder.build(); + return AgentCardMapper.INSTANCE.toProto(agentCard); } - public static io.a2a.grpc.Task task(Task task) { - io.a2a.grpc.Task.Builder builder = io.a2a.grpc.Task.newBuilder(); - builder.setId(task.getId()); - builder.setContextId(task.getContextId()); - builder.setStatus(taskStatus(task.getStatus())); - if (task.getArtifacts() != null) { - builder.addAllArtifacts(task.getArtifacts().stream().map(ToProto::artifact).collect(Collectors.toList())); - } - if (task.getHistory() != null) { - builder.addAllHistory(task.getHistory().stream().map(ToProto::message).collect(Collectors.toList())); - } - builder.setMetadata(struct(task.getMetadata())); - return builder.build(); - } - - public static io.a2a.grpc.ListTasksResponse listTasksResult(io.a2a.spec.ListTasksResult result) { - io.a2a.grpc.ListTasksResponse.Builder builder = io.a2a.grpc.ListTasksResponse.newBuilder(); - if (result.tasks() != null) { - builder.addAllTasks(result.tasks().stream().map(ToProto::task).collect(Collectors.toList())); - } - if (result.nextPageToken() != null) { - builder.setNextPageToken(result.nextPageToken()); - } - builder.setTotalSize(result.totalSize()); - return builder.build(); + public static io.a2a.grpc.GetExtendedAgentCardRequest extendedAgentCard(GetAuthenticatedExtendedCardRequest request) { + return GetExtendedAgentCardRequest.newBuilder().build(); } - public static io.a2a.grpc.Message message(Message message) { - io.a2a.grpc.Message.Builder builder = io.a2a.grpc.Message.newBuilder(); - builder.setMessageId(message.getMessageId()); - if (message.getContextId() != null) { - builder.setContextId(message.getContextId()); - } - if (message.getTaskId() != null) { - builder.setTaskId(message.getTaskId()); - } - builder.setRole(role(message.getRole())); - if (message.getParts() != null) { - builder.addAllParts(message.getParts().stream().map(ToProto::part).collect(Collectors.toList())); - } - builder.setMetadata(struct(message.getMetadata())); - if (message.getReferenceTaskIds() != null && !message.getReferenceTaskIds().isEmpty()) { - builder.addAllReferenceTaskIds(message.getReferenceTaskIds()); - } - return builder.build(); + public static io.a2a.grpc.GetTaskRequest getTaskRequest(TaskQueryParams params) { + return TaskQueryParamsMapper.INSTANCE.toProto(params); } - public static io.a2a.grpc.TaskPushNotificationConfig taskPushNotificationConfig(TaskPushNotificationConfig config) { - String id = config.pushNotificationConfig().id(); - io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); - builder.setName("tasks/" + config.taskId() + "/pushNotificationConfigs" + (id == null ? "" : ('/' + id))); - builder.setPushNotificationConfig(pushNotificationConfig(config.pushNotificationConfig())); - return builder.build(); + public static io.a2a.grpc.CancelTaskRequest cancelTaskRequest(TaskIdParams params) { + return TaskIdParamsMapper.INSTANCE.toProtoCancelTaskRequest(params); } - private static io.a2a.grpc.PushNotificationConfig pushNotificationConfig(PushNotificationConfig config) { - io.a2a.grpc.PushNotificationConfig.Builder builder = io.a2a.grpc.PushNotificationConfig.newBuilder(); - if (config.url() != null) { - builder.setUrl(config.url()); - } - if (config.token() != null) { - builder.setToken(config.token()); - } - if (config.authentication() != null) { - builder.setAuthentication(authenticationInfo(config.authentication())); - } - if (config.id() != null) { - builder.setId(config.id()); - } - return builder.build(); + public static io.a2a.grpc.SubscribeToTaskRequest subscribeToTaskRequest(TaskIdParams params) { + return TaskIdParamsMapper.INSTANCE.toProtoSubscribeToTaskRequest(params); } - public static io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent(TaskArtifactUpdateEvent event) { - io.a2a.grpc.TaskArtifactUpdateEvent.Builder builder = io.a2a.grpc.TaskArtifactUpdateEvent.newBuilder(); - builder.setTaskId(event.getTaskId()); - builder.setContextId(event.getContextId()); - builder.setArtifact(artifact(event.getArtifact())); - if (event.isAppend() != null) { - builder.setAppend(event.isAppend()); - } - if (event.isLastChunk() != null) { - builder.setLastChunk(event.isLastChunk()); - } - if (event.getMetadata() != null) { - builder.setMetadata(struct(event.getMetadata())); - } - return builder.build(); + public static io.a2a.grpc.SetTaskPushNotificationConfigRequest setTaskPushNotificationConfigRequest(TaskPushNotificationConfig config) { + return SetTaskPushNotificationConfigMapper.INSTANCE.toProto(config); } - public static io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent(TaskStatusUpdateEvent event) { - io.a2a.grpc.TaskStatusUpdateEvent.Builder builder = io.a2a.grpc.TaskStatusUpdateEvent.newBuilder(); - builder.setTaskId(event.getTaskId()); - builder.setContextId(event.getContextId()); - builder.setStatus(taskStatus(event.getStatus())); - builder.setFinal(event.isFinal()); - if (event.getMetadata() != null) { - builder.setMetadata(struct(event.getMetadata())); - } - return builder.build(); + public static io.a2a.grpc.GetTaskPushNotificationConfigRequest getTaskPushNotificationConfigRequest(GetTaskPushNotificationConfigParams params) { + return GetTaskPushNotificationConfigParamsMapper.INSTANCE.toProto(params); } - private static io.a2a.grpc.Artifact artifact(Artifact artifact) { - io.a2a.grpc.Artifact.Builder builder = io.a2a.grpc.Artifact.newBuilder(); - if (artifact.artifactId() != null) { - builder.setArtifactId(artifact.artifactId()); - } - if (artifact.name() != null) { - builder.setName(artifact.name()); - } - if (artifact.description() != null) { - builder.setDescription(artifact.description()); - } - if (artifact.parts() != null) { - builder.addAllParts(artifact.parts().stream().map(ToProto::part).collect(Collectors.toList())); - } - if (artifact.metadata() != null) { - builder.setMetadata(struct(artifact.metadata())); - } - return builder.build(); + public static io.a2a.grpc.DeleteTaskPushNotificationConfigRequest deleteTaskPushNotificationConfigRequest(DeleteTaskPushNotificationConfigParams params) { + return DeleteTaskPushNotificationConfigParamsMapper.INSTANCE.toProto(params); } - private static io.a2a.grpc.Part part(Part part) { - io.a2a.grpc.Part.Builder builder = io.a2a.grpc.Part.newBuilder(); - if (part instanceof TextPart) { - builder.setText(((TextPart) part).getText()); - } else if (part instanceof FilePart) { - builder.setFile(filePart((FilePart) part)); - } else if (part instanceof DataPart) { - builder.setData(dataPart((DataPart) part)); - } - return builder.build(); + public static io.a2a.grpc.ListTaskPushNotificationConfigRequest listTaskPushNotificationConfigRequest(ListTaskPushNotificationConfigParams params) { + return ListTaskPushNotificationConfigParamsMapper.INSTANCE.toProto(params); } - private static io.a2a.grpc.FilePart filePart(FilePart filePart) { - io.a2a.grpc.FilePart.Builder builder = io.a2a.grpc.FilePart.newBuilder(); - FileContent fileContent = filePart.getFile(); - if (fileContent instanceof FileWithBytes) { - builder.setFileWithBytes(ByteString.copyFrom(((FileWithBytes) fileContent).bytes(), StandardCharsets.UTF_8)); - } else if (fileContent instanceof FileWithUri) { - builder.setFileWithUri(((FileWithUri) fileContent).uri()); - } - if (fileContent.name() != null) { - builder.setName(fileContent.name()); - } - return builder.build(); + public static io.a2a.grpc.Task task(Task task) { + return TaskMapper.INSTANCE.toProto(task); } - private static io.a2a.grpc.DataPart dataPart(DataPart dataPart) { - io.a2a.grpc.DataPart.Builder builder = io.a2a.grpc.DataPart.newBuilder(); - if (dataPart.getData() != null) { - builder.setData(struct(dataPart.getData())); - } - return builder.build(); + public static io.a2a.grpc.ListTasksResponse listTasksResult(ListTasksResult result) { + return ListTasksResultMapper.INSTANCE.toProto(result); } - private static io.a2a.grpc.Role role(Message.Role role) { - if (role == null) { - return io.a2a.grpc.Role.ROLE_UNSPECIFIED; - } - return switch (role) { - case USER -> - io.a2a.grpc.Role.ROLE_USER; - case AGENT -> - io.a2a.grpc.Role.ROLE_AGENT; - }; + public static io.a2a.grpc.ListTasksRequest listTasksParams(ListTasksParams params) { + return ListTasksParamsMapper.INSTANCE.toProto(params); } - private static io.a2a.grpc.TaskStatus taskStatus(TaskStatus taskStatus) { - io.a2a.grpc.TaskStatus.Builder builder = io.a2a.grpc.TaskStatus.newBuilder(); - if (taskStatus.state() != null) { - builder.setState(taskState(taskStatus.state())); - } - if (taskStatus.message() != null) { - builder.setUpdate(message(taskStatus.message())); - } - if (taskStatus.timestamp() != null) { - Instant instant = taskStatus.timestamp().toInstant(); - builder.setTimestamp(com.google.protobuf.Timestamp.newBuilder().setSeconds(instant.getEpochSecond()).setNanos(instant.getNano()).build()); - } - return builder.build(); + public static io.a2a.grpc.Message message(Message message) { + return MessageMapper.INSTANCE.toProto(message); } - public static io.a2a.grpc.TaskState taskState(TaskState taskState) { - if (taskState == null) { - return io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; - } - return switch (taskState) { - case SUBMITTED -> - io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED; - case WORKING -> - io.a2a.grpc.TaskState.TASK_STATE_WORKING; - case INPUT_REQUIRED -> - io.a2a.grpc.TaskState.TASK_STATE_INPUT_REQUIRED; - case AUTH_REQUIRED -> - io.a2a.grpc.TaskState.TASK_STATE_AUTH_REQUIRED; - case COMPLETED -> - io.a2a.grpc.TaskState.TASK_STATE_COMPLETED; - case CANCELED -> - io.a2a.grpc.TaskState.TASK_STATE_CANCELLED; - case FAILED -> - io.a2a.grpc.TaskState.TASK_STATE_FAILED; - case REJECTED -> - io.a2a.grpc.TaskState.TASK_STATE_REJECTED; - default -> - io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED; - }; + public static io.a2a.grpc.TaskPushNotificationConfig taskPushNotificationConfig(TaskPushNotificationConfig config) { + return TaskPushNotificationConfigMapper.INSTANCE.toProto(config); } - private static io.a2a.grpc.AuthenticationInfo authenticationInfo(PushNotificationAuthenticationInfo pushNotificationAuthenticationInfo) { - io.a2a.grpc.AuthenticationInfo.Builder builder = io.a2a.grpc.AuthenticationInfo.newBuilder(); - if (pushNotificationAuthenticationInfo.schemes() != null) { - builder.addAllSchemes(pushNotificationAuthenticationInfo.schemes()); - } - if (pushNotificationAuthenticationInfo.credentials() != null) { - builder.setCredentials(pushNotificationAuthenticationInfo.credentials()); - } - return builder.build(); + public static io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent(TaskArtifactUpdateEvent event) { + return TaskArtifactUpdateEventMapper.INSTANCE.toProto(event); } - public static io.a2a.grpc.SendMessageConfiguration messageSendConfiguration(MessageSendConfiguration messageSendConfiguration) { - io.a2a.grpc.SendMessageConfiguration.Builder builder = io.a2a.grpc.SendMessageConfiguration.newBuilder(); - if (messageSendConfiguration.acceptedOutputModes() != null) { - builder.addAllAcceptedOutputModes(messageSendConfiguration.acceptedOutputModes()); - } - if (messageSendConfiguration.historyLength() != null) { - builder.setHistoryLength(messageSendConfiguration.historyLength()); - } - if (messageSendConfiguration.pushNotificationConfig() != null) { - builder.setPushNotificationConfig(pushNotificationConfig(messageSendConfiguration.pushNotificationConfig())); - } - builder.setBlocking(messageSendConfiguration.blocking()); - return builder.build(); + public static io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent(TaskStatusUpdateEvent event) { + return TaskStatusUpdateEventMapper.INSTANCE.toProto(event); } - private static io.a2a.grpc.AgentProvider agentProvider(AgentProvider agentProvider) { - io.a2a.grpc.AgentProvider.Builder builder = io.a2a.grpc.AgentProvider.newBuilder(); - builder.setOrganization(agentProvider.organization()); - builder.setUrl(agentProvider.url()); - return builder.build(); + public static io.a2a.grpc.TaskState taskState(TaskState taskState) { + return TaskStateMapper.INSTANCE.toProto(taskState); } - private static io.a2a.grpc.AgentCapabilities agentCapabilities(AgentCapabilities agentCapabilities) { - io.a2a.grpc.AgentCapabilities.Builder builder = io.a2a.grpc.AgentCapabilities.newBuilder(); - builder.setStreaming(agentCapabilities.streaming()); - builder.setPushNotifications(agentCapabilities.pushNotifications()); - builder.setStateTransitionHistory(agentCapabilities.stateTransitionHistory()); - if (agentCapabilities.extensions() != null) { - builder.addAllExtensions(agentCapabilities.extensions().stream().map(ToProto::agentExtension).collect(Collectors.toList())); - } - return builder.build(); + public static io.a2a.grpc.SendMessageConfiguration messageSendConfiguration(MessageSendConfiguration messageSendConfiguration) { + return MessageSendConfigurationMapper.INSTANCE.toProto(messageSendConfiguration); } public static io.a2a.grpc.SendMessageRequest sendMessageRequest(MessageSendParams request) { - io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); - builder.setRequest(message(request.message())); - if (request.configuration() != null) { - builder.setConfiguration(messageSendConfiguration(request.configuration())); - } - if (request.metadata() != null && ! request.metadata().isEmpty()) { - builder.setMetadata(struct(request.metadata())); - } - return builder.build(); - } - private static io.a2a.grpc.AgentExtension agentExtension(AgentExtension agentExtension) { - io.a2a.grpc.AgentExtension.Builder builder = io.a2a.grpc.AgentExtension.newBuilder(); - if (agentExtension.description() != null) { - builder.setDescription(agentExtension.description()); - } - if (agentExtension.params() != null) { - builder.setParams(struct(agentExtension.params())); - } - builder.setRequired(agentExtension.required()); - if (agentExtension.uri() != null) { - builder.setUri(agentExtension.uri()); - } - return builder.build(); - } - - private static io.a2a.grpc.AgentSkill agentSkill(AgentSkill agentSkill) { - io.a2a.grpc.AgentSkill.Builder builder = io.a2a.grpc.AgentSkill.newBuilder(); - if (agentSkill.id() != null) { - builder.setId(agentSkill.id()); - } - if (agentSkill.name() != null) { - builder.setName(agentSkill.name()); - } - if (agentSkill.description() != null) { - builder.setDescription(agentSkill.description()); - } - if (agentSkill.tags() != null) { - builder.addAllTags(agentSkill.tags()); - } - if (agentSkill.examples() != null) { - builder.addAllExamples(agentSkill.examples()); - } - if (agentSkill.inputModes() != null) { - builder.addAllInputModes(agentSkill.inputModes()); - } - if (agentSkill.outputModes() != null) { - builder.addAllOutputModes(agentSkill.outputModes()); - } - if (agentSkill.security() != null) { - builder.addAllSecurity(agentSkill.security().stream().map(s -> { - io.a2a.grpc.Security.Builder securityBuilder = io.a2a.grpc.Security.newBuilder(); - s.forEach((key, value) -> { - io.a2a.grpc.StringList.Builder stringListBuilder = io.a2a.grpc.StringList.newBuilder(); - stringListBuilder.addAllList(value); - securityBuilder.putSchemes(key, stringListBuilder.build()); - }); - return securityBuilder.build(); - }).collect(Collectors.toList())); - } - return builder.build(); - } - - private static io.a2a.grpc.AgentCardSignature agentCardSignature(AgentCardSignature agentCardSignature) { - io.a2a.grpc.AgentCardSignature.Builder builder = io.a2a.grpc.AgentCardSignature.newBuilder(); - builder.setProtected(agentCardSignature.protectedHeader()); - builder.setSignature(agentCardSignature.signature()); - if (agentCardSignature.header() != null) { - builder.setHeader(struct(agentCardSignature.header())); - } - return builder.build(); - } - - private static io.a2a.grpc.SecurityScheme securityScheme(SecurityScheme securityScheme) { - io.a2a.grpc.SecurityScheme.Builder builder = io.a2a.grpc.SecurityScheme.newBuilder(); - if (securityScheme instanceof APIKeySecurityScheme) { - builder.setApiKeySecurityScheme(apiKeySecurityScheme((APIKeySecurityScheme) securityScheme)); - } else if (securityScheme instanceof HTTPAuthSecurityScheme) { - builder.setHttpAuthSecurityScheme(httpAuthSecurityScheme((HTTPAuthSecurityScheme) securityScheme)); - } else if (securityScheme instanceof OAuth2SecurityScheme) { - builder.setOauth2SecurityScheme(oauthSecurityScheme((OAuth2SecurityScheme) securityScheme)); - } else if (securityScheme instanceof OpenIdConnectSecurityScheme) { - builder.setOpenIdConnectSecurityScheme(openIdConnectSecurityScheme((OpenIdConnectSecurityScheme) securityScheme)); - } else if (securityScheme instanceof MutualTLSSecurityScheme) { - builder.setMtlsSecurityScheme(mutualTlsSecurityScheme((MutualTLSSecurityScheme) securityScheme)); - } - return builder.build(); - } - - private static io.a2a.grpc.APIKeySecurityScheme apiKeySecurityScheme(APIKeySecurityScheme apiKeySecurityScheme) { - io.a2a.grpc.APIKeySecurityScheme.Builder builder = io.a2a.grpc.APIKeySecurityScheme.newBuilder(); - if (apiKeySecurityScheme.getDescription() != null) { - builder.setDescription(apiKeySecurityScheme.getDescription()); - } - if (apiKeySecurityScheme.getIn() != null) { - builder.setLocation(apiKeySecurityScheme.getIn()); - } - if (apiKeySecurityScheme.getName() != null) { - builder.setName(apiKeySecurityScheme.getName()); - } - return builder.build(); - } - - private static io.a2a.grpc.HTTPAuthSecurityScheme httpAuthSecurityScheme(HTTPAuthSecurityScheme httpAuthSecurityScheme) { - io.a2a.grpc.HTTPAuthSecurityScheme.Builder builder = io.a2a.grpc.HTTPAuthSecurityScheme.newBuilder(); - if (httpAuthSecurityScheme.getBearerFormat() != null) { - builder.setBearerFormat(httpAuthSecurityScheme.getBearerFormat()); - } - if (httpAuthSecurityScheme.getDescription() != null) { - builder.setDescription(httpAuthSecurityScheme.getDescription()); - } - if (httpAuthSecurityScheme.getScheme() != null) { - builder.setScheme(httpAuthSecurityScheme.getScheme()); - } - return builder.build(); - } - - private static io.a2a.grpc.OAuth2SecurityScheme oauthSecurityScheme(OAuth2SecurityScheme oauth2SecurityScheme) { - io.a2a.grpc.OAuth2SecurityScheme.Builder builder = io.a2a.grpc.OAuth2SecurityScheme.newBuilder(); - if (oauth2SecurityScheme.getDescription() != null) { - builder.setDescription(oauth2SecurityScheme.getDescription()); - } - if (oauth2SecurityScheme.getFlows() != null) { - builder.setFlows(oauthFlows(oauth2SecurityScheme.getFlows())); - } - if (oauth2SecurityScheme.getOauth2MetadataUrl() != null) { - builder.setOauth2MetadataUrl(oauth2SecurityScheme.getOauth2MetadataUrl()); - } - return builder.build(); - } - - private static io.a2a.grpc.OAuthFlows oauthFlows(OAuthFlows oAuthFlows) { - io.a2a.grpc.OAuthFlows.Builder builder = io.a2a.grpc.OAuthFlows.newBuilder(); - if (oAuthFlows.authorizationCode() != null) { - builder.setAuthorizationCode(authorizationCodeOAuthFlow(oAuthFlows.authorizationCode())); - } - if (oAuthFlows.clientCredentials() != null) { - builder.setClientCredentials(clientCredentialsOAuthFlow(oAuthFlows.clientCredentials())); - } - if (oAuthFlows.implicit() != null) { - builder.setImplicit(implicitOAuthFlow(oAuthFlows.implicit())); - } - if (oAuthFlows.password() != null) { - builder.setPassword(passwordOAuthFlow(oAuthFlows.password())); - } - return builder.build(); - } - - private static io.a2a.grpc.AuthorizationCodeOAuthFlow authorizationCodeOAuthFlow(AuthorizationCodeOAuthFlow authorizationCodeOAuthFlow) { - io.a2a.grpc.AuthorizationCodeOAuthFlow.Builder builder = io.a2a.grpc.AuthorizationCodeOAuthFlow.newBuilder(); - if (authorizationCodeOAuthFlow.authorizationUrl() != null) { - builder.setAuthorizationUrl(authorizationCodeOAuthFlow.authorizationUrl()); - } - if (authorizationCodeOAuthFlow.refreshUrl() != null) { - builder.setRefreshUrl(authorizationCodeOAuthFlow.refreshUrl()); - } - if (authorizationCodeOAuthFlow.scopes() != null) { - builder.putAllScopes(authorizationCodeOAuthFlow.scopes()); - } - if (authorizationCodeOAuthFlow.tokenUrl() != null) { - builder.setTokenUrl(authorizationCodeOAuthFlow.tokenUrl()); - } - return builder.build(); + return MessageSendParamsMapper.INSTANCE.toProto(request); } public static io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfigResponse(List configs) { List confs = new ArrayList<>(configs.size()); - for(TaskPushNotificationConfig config: configs) { + for (TaskPushNotificationConfig config : configs) { confs.add(taskPushNotificationConfig(config)); } return io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder().addAllConfigs(confs).build(); } - private static io.a2a.grpc.ClientCredentialsOAuthFlow clientCredentialsOAuthFlow(ClientCredentialsOAuthFlow clientCredentialsOAuthFlow) { - io.a2a.grpc.ClientCredentialsOAuthFlow.Builder builder = io.a2a.grpc.ClientCredentialsOAuthFlow.newBuilder(); - if (clientCredentialsOAuthFlow.refreshUrl() != null) { - builder.setRefreshUrl(clientCredentialsOAuthFlow.refreshUrl()); - } - if (clientCredentialsOAuthFlow.scopes() != null) { - builder.putAllScopes(clientCredentialsOAuthFlow.scopes()); - } - if (clientCredentialsOAuthFlow.tokenUrl() != null) { - builder.setTokenUrl(clientCredentialsOAuthFlow.tokenUrl()); - } - return builder.build(); - } - - private static io.a2a.grpc.ImplicitOAuthFlow implicitOAuthFlow(ImplicitOAuthFlow implicitOAuthFlow) { - io.a2a.grpc.ImplicitOAuthFlow.Builder builder = io.a2a.grpc.ImplicitOAuthFlow.newBuilder(); - if (implicitOAuthFlow.authorizationUrl() != null) { - builder.setAuthorizationUrl(implicitOAuthFlow.authorizationUrl()); - } - if (implicitOAuthFlow.refreshUrl() != null) { - builder.setRefreshUrl(implicitOAuthFlow.refreshUrl()); - } - if (implicitOAuthFlow.scopes() != null) { - builder.putAllScopes(implicitOAuthFlow.scopes()); - } - return builder.build(); - } - - private static io.a2a.grpc.PasswordOAuthFlow passwordOAuthFlow(PasswordOAuthFlow passwordOAuthFlow) { - io.a2a.grpc.PasswordOAuthFlow.Builder builder = io.a2a.grpc.PasswordOAuthFlow.newBuilder(); - if (passwordOAuthFlow.refreshUrl() != null) { - builder.setRefreshUrl(passwordOAuthFlow.refreshUrl()); - } - if (passwordOAuthFlow.scopes() != null) { - builder.putAllScopes(passwordOAuthFlow.scopes()); - } - if (passwordOAuthFlow.tokenUrl() != null) { - builder.setTokenUrl(passwordOAuthFlow.tokenUrl()); - } - return builder.build(); - } - - private static io.a2a.grpc.OpenIdConnectSecurityScheme openIdConnectSecurityScheme(OpenIdConnectSecurityScheme openIdConnectSecurityScheme) { - io.a2a.grpc.OpenIdConnectSecurityScheme.Builder builder = io.a2a.grpc.OpenIdConnectSecurityScheme.newBuilder(); - if (openIdConnectSecurityScheme.getDescription() != null) { - builder.setDescription(openIdConnectSecurityScheme.getDescription()); - } - if (openIdConnectSecurityScheme.getOpenIdConnectUrl() != null) { - builder.setOpenIdConnectUrl(openIdConnectSecurityScheme.getOpenIdConnectUrl()); - } - return builder.build(); - } - - private static io.a2a.grpc.MutualTlsSecurityScheme mutualTlsSecurityScheme(MutualTLSSecurityScheme mutualTlsSecurityScheme) { - io.a2a.grpc.MutualTlsSecurityScheme.Builder builder = io.a2a.grpc.MutualTlsSecurityScheme.newBuilder(); - if (mutualTlsSecurityScheme.getDescription() != null) { - builder.setDescription(mutualTlsSecurityScheme.getDescription()); - } - return builder.build(); - } - - private static io.a2a.grpc.AgentInterface agentInterface(AgentInterface agentInterface) { - io.a2a.grpc.AgentInterface.Builder builder = io.a2a.grpc.AgentInterface.newBuilder(); - if (agentInterface.transport() != null) { - builder.setTransport(agentInterface.transport()); - } - if (agentInterface.url() != null) { - builder.setUrl(agentInterface.url()); - } - return builder.build(); - } - - public static Struct struct(Map map) { - Struct.Builder structBuilder = Struct.newBuilder(); - if (map != null) { - map.forEach((k, v) -> structBuilder.putFields(k, value(v))); - } - return structBuilder.build(); - } - - private static Value value(Object value) { - Value.Builder valueBuilder = Value.newBuilder(); - if (value instanceof String) { - valueBuilder.setStringValue((String) value); - } else if (value instanceof Number) { - valueBuilder.setNumberValue(((Number) value).doubleValue()); - } else if (value instanceof Boolean) { - valueBuilder.setBoolValue((Boolean) value); - } else if (value instanceof Map) { - valueBuilder.setStructValue(struct((Map) value)); - } else if (value instanceof List) { - valueBuilder.setListValue(listValue((List) value)); - } - return valueBuilder.build(); - } - - private static com.google.protobuf.ListValue listValue(List list) { - com.google.protobuf.ListValue.Builder listValueBuilder = com.google.protobuf.ListValue.newBuilder(); - if (list != null) { - list.forEach(o -> listValueBuilder.addValues(value(o))); - } - return listValueBuilder.build(); - } - public static StreamResponse streamResponse(StreamingEventKind streamingEventKind) { - if (streamingEventKind instanceof TaskStatusUpdateEvent) { - return StreamResponse.newBuilder() - .setStatusUpdate(taskStatusUpdateEvent((TaskStatusUpdateEvent) streamingEventKind)) - .build(); - } else if (streamingEventKind instanceof TaskArtifactUpdateEvent) { - return StreamResponse.newBuilder() - .setArtifactUpdate(taskArtifactUpdateEvent((TaskArtifactUpdateEvent) streamingEventKind)) - .build(); - } else if (streamingEventKind instanceof Message) { - return StreamResponse.newBuilder() - .setMsg(message((Message) streamingEventKind)) - .build(); - } else if (streamingEventKind instanceof Task) { - return StreamResponse.newBuilder() - .setTask(task((Task) streamingEventKind)) - .build(); - } else { - throw new IllegalArgumentException("Unsupported event type: " + streamingEventKind); - } + return StreamResponseMapper.INSTANCE.toProto(streamingEventKind); } public static io.a2a.grpc.SendMessageResponse taskOrMessage(EventKind eventKind) { @@ -724,356 +174,150 @@ public static io.a2a.grpc.StreamResponse taskOrMessageStream(StreamingEventKind } } + public static io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfigResponse(TaskPushNotificationConfig config) { + return taskPushNotificationConfig(config); + } + + public static io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConfigResponse(TaskPushNotificationConfig config) { + return taskPushNotificationConfig(config); + } + + public static io.a2a.grpc.AgentCard getAuthenticatedExtendedCardResponse(AgentCard card) { + return agentCard(card); + } } public static class FromProto { + public static AgentCard agentCard(io.a2a.grpc.AgentCardOrBuilder agentCard) { + io.a2a.grpc.AgentCard agentCardProto = agentCard instanceof io.a2a.grpc.AgentCard + ? (io.a2a.grpc.AgentCard) agentCard + : ((io.a2a.grpc.AgentCard.Builder) agentCard).build(); + return AgentCardMapper.INSTANCE.fromProto(agentCardProto); + } + public static TaskQueryParams taskQueryParams(io.a2a.grpc.GetTaskRequestOrBuilder request) { - String name = request.getName(); - String id = name.substring(name.lastIndexOf('/') + 1); - return new TaskQueryParams(id, request.getHistoryLength()); + io.a2a.grpc.GetTaskRequest reqProto = request instanceof io.a2a.grpc.GetTaskRequest + ? (io.a2a.grpc.GetTaskRequest) request + : ((io.a2a.grpc.GetTaskRequest.Builder) request).build(); + return TaskQueryParamsMapper.INSTANCE.fromProto(reqProto); } - public static io.a2a.spec.ListTasksParams listTasksParams(io.a2a.grpc.ListTasksRequestOrBuilder request) { - io.a2a.spec.ListTasksParams.Builder builder = new io.a2a.spec.ListTasksParams.Builder(); - if (!request.getContextId().isEmpty()) { - builder.contextId(request.getContextId()); - } - if (request.getStatus() != io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED) { - builder.status(taskState(request.getStatus())); - } - if (request.getPageSize() > 0) { - builder.pageSize(request.getPageSize()); - } - if (!request.getPageToken().isEmpty()) { - builder.pageToken(request.getPageToken()); - } - if (request.getHistoryLength() > 0) { - builder.historyLength(request.getHistoryLength()); - } - if (request.hasLastUpdatedTime()) { - Instant instant = Instant.ofEpochSecond( - request.getLastUpdatedTime().getSeconds(), - request.getLastUpdatedTime().getNanos()); - builder.lastUpdatedAfter(instant); - } - if (request.getIncludeArtifacts()) { - builder.includeArtifacts(true); - } - return builder.build(); + public static ListTasksParams listTasksParams(io.a2a.grpc.ListTasksRequestOrBuilder request) { + io.a2a.grpc.ListTasksRequest reqProto = request instanceof io.a2a.grpc.ListTasksRequest + ? (io.a2a.grpc.ListTasksRequest) request + : ((io.a2a.grpc.ListTasksRequest.Builder) request).build(); + return ListTasksParamsMapper.INSTANCE.fromProto(reqProto); } public static TaskIdParams taskIdParams(io.a2a.grpc.CancelTaskRequestOrBuilder request) { - String name = request.getName(); - String id = name.substring(name.lastIndexOf('/') + 1); - return new TaskIdParams(id); + io.a2a.grpc.CancelTaskRequest reqProto = request instanceof io.a2a.grpc.CancelTaskRequest + ? (io.a2a.grpc.CancelTaskRequest) request + : ((io.a2a.grpc.CancelTaskRequest.Builder) request).build(); + return TaskIdParamsMapper.INSTANCE.fromProtoCancelTaskRequest(reqProto); } public static MessageSendParams messageSendParams(io.a2a.grpc.SendMessageRequestOrBuilder request) { - MessageSendParams.Builder builder = new MessageSendParams.Builder(); - builder.message(message(request.getRequest())); - if (request.hasConfiguration()) { - builder.configuration(messageSendConfiguration(request.getConfiguration())); - } - if (request.hasMetadata()) { - builder.metadata(struct(request.getMetadata())); - } - return builder.build(); + io.a2a.grpc.SendMessageRequest requestProto = request instanceof io.a2a.grpc.SendMessageRequest + ? (io.a2a.grpc.SendMessageRequest) request + : ((io.a2a.grpc.SendMessageRequest.Builder) request).build(); + return MessageSendParamsMapper.INSTANCE.fromProto(requestProto); } - public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequestOrBuilder request) { - return taskPushNotificationConfig(request.getConfig(), true); + public static TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequestOrBuilder config) { + io.a2a.grpc.SetTaskPushNotificationConfigRequest reqProto = config instanceof io.a2a.grpc.SetTaskPushNotificationConfigRequest + ? (io.a2a.grpc.SetTaskPushNotificationConfigRequest) config + : ((io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder) config).build(); + return SetTaskPushNotificationConfigMapper.INSTANCE.fromProto(reqProto); } public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfigOrBuilder config) { - return taskPushNotificationConfig(config, false); - } - - private static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfigOrBuilder config, boolean create) { - String name = config.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" - String[] parts = name.split("/"); - String taskId = parts[1]; - String configId = ""; - if (create) { - if (parts.length < 3) { - throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + name); - } - if (parts.length == 4) { - configId = parts[3]; - } else { - configId = taskId; - } - } else { - if (parts.length < 4) { - throw new IllegalArgumentException("Invalid name format for TaskPushNotificationConfig: " + name); - } - configId = parts[3]; - } - PushNotificationConfig pnc = pushNotification(config.getPushNotificationConfig(), configId); - return new TaskPushNotificationConfig(taskId, pnc); + io.a2a.grpc.TaskPushNotificationConfig proto = config instanceof io.a2a.grpc.TaskPushNotificationConfig + ? (io.a2a.grpc.TaskPushNotificationConfig) config + : ((io.a2a.grpc.TaskPushNotificationConfig.Builder) config).build(); + return TaskPushNotificationConfigMapper.INSTANCE.fromProto(proto); } public static GetTaskPushNotificationConfigParams getTaskPushNotificationConfigParams(io.a2a.grpc.GetTaskPushNotificationConfigRequestOrBuilder request) { - String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" - String[] parts = name.split("/"); - String taskId = parts[1]; - String configId; - if (parts.length == 2) { - configId = taskId; - } else if (parts.length < 4) { - throw new IllegalArgumentException("Invalid name format for GetTaskPushNotificationConfigRequest: " + name); - } else { - configId = parts[3]; - } - return new GetTaskPushNotificationConfigParams(taskId, configId); + io.a2a.grpc.GetTaskPushNotificationConfigRequest reqProto = request instanceof io.a2a.grpc.GetTaskPushNotificationConfigRequest + ? (io.a2a.grpc.GetTaskPushNotificationConfigRequest) request + : ((io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder) request).build(); + return GetTaskPushNotificationConfigParamsMapper.INSTANCE.fromProto(reqProto); } - public static TaskIdParams taskIdParams(io.a2a.grpc.TaskSubscriptionRequestOrBuilder request) { - String name = request.getName(); - String id = name.substring(name.lastIndexOf('/') + 1); - return new TaskIdParams(id); + public static TaskIdParams taskIdParams(io.a2a.grpc.SubscribeToTaskRequestOrBuilder request) { + io.a2a.grpc.SubscribeToTaskRequest reqProto = request instanceof io.a2a.grpc.SubscribeToTaskRequest + ? (io.a2a.grpc.SubscribeToTaskRequest) request + : ((io.a2a.grpc.SubscribeToTaskRequest.Builder) request).build(); + return TaskIdParamsMapper.INSTANCE.fromProtoSubscribeToTaskRequest(reqProto); } public static List listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigResponseOrBuilder response) { List configs = response.getConfigsList(); List result = new ArrayList<>(configs.size()); - for(io.a2a.grpc.TaskPushNotificationConfig config : configs) { - result.add(taskPushNotificationConfig(config, false)); + for (io.a2a.grpc.TaskPushNotificationConfig config : configs) { + result.add(taskPushNotificationConfig(config)); } return result; } public static ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigRequestOrBuilder request) { - String parent = request.getParent(); - String id = parent.substring(parent.lastIndexOf('/') + 1); - return new ListTaskPushNotificationConfigParams(id); + io.a2a.grpc.ListTaskPushNotificationConfigRequest reqProto = request instanceof io.a2a.grpc.ListTaskPushNotificationConfigRequest + ? (io.a2a.grpc.ListTaskPushNotificationConfigRequest) request + : ((io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder) request).build(); + return ListTaskPushNotificationConfigParamsMapper.INSTANCE.fromProto(reqProto); } public static DeleteTaskPushNotificationConfigParams deleteTaskPushNotificationConfigParams(io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder request) { - String name = request.getName(); // "tasks/{id}/pushNotificationConfigs/{push_id}" - String[] parts = name.split("/"); - if (parts.length < 4) { - throw new IllegalArgumentException("Invalid name format for DeleteTaskPushNotificationConfigRequest: " + name); - } - String taskId = parts[1]; - String configId = parts[3]; - return new DeleteTaskPushNotificationConfigParams(taskId, configId); - } - - private static AgentExtension agentExtension(io.a2a.grpc.AgentExtensionOrBuilder agentExtension) { - return new AgentExtension( - agentExtension.getDescription(), - struct(agentExtension.getParams()), - agentExtension.getRequired(), - agentExtension.getUri() - ); - } - - private static MessageSendConfiguration messageSendConfiguration(io.a2a.grpc.SendMessageConfigurationOrBuilder sendMessageConfiguration) { - return new MessageSendConfiguration( - sendMessageConfiguration.getAcceptedOutputModesList().isEmpty() ? null : - new ArrayList<>(sendMessageConfiguration.getAcceptedOutputModesList()), - sendMessageConfiguration.getHistoryLength(), - pushNotification(sendMessageConfiguration.getPushNotificationConfig()), - sendMessageConfiguration.getBlocking() - ); - } - - private static @Nullable PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification, String configId) { - if(pushNotification == null || pushNotification.getDefaultInstanceForType().equals(pushNotification)) { - return null; - } - return new PushNotificationConfig( - pushNotification.getUrl(), - pushNotification.getToken().isEmpty() ? null : pushNotification.getToken(), - pushNotification.hasAuthentication() ? authenticationInfo(pushNotification.getAuthentication()) : null, - pushNotification.getId().isEmpty() ? configId : pushNotification.getId() - ); - } - - private static @Nullable PushNotificationConfig pushNotification(io.a2a.grpc.PushNotificationConfigOrBuilder pushNotification) { - return pushNotification(pushNotification, pushNotification.getId()); - } - - private static PushNotificationAuthenticationInfo authenticationInfo(io.a2a.grpc.AuthenticationInfoOrBuilder authenticationInfo) { - return new PushNotificationAuthenticationInfo( - new ArrayList<>(authenticationInfo.getSchemesList()), - authenticationInfo.getCredentials() - ); + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest reqProto = request instanceof io.a2a.grpc.DeleteTaskPushNotificationConfigRequest + ? (io.a2a.grpc.DeleteTaskPushNotificationConfigRequest) request + : ((io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder) request).build(); + return DeleteTaskPushNotificationConfigParamsMapper.INSTANCE.fromProto(reqProto); } public static Task task(io.a2a.grpc.TaskOrBuilder task) { - return new Task( - task.getId(), - task.getContextId(), - taskStatus(task.getStatus()), - task.getArtifactsList().stream().map(item -> artifact(item)).collect(Collectors.toList()), - task.getHistoryList().stream().map(item -> message(item)).collect(Collectors.toList()), - struct(task.getMetadata()) - ); + io.a2a.grpc.Task taskProto = task instanceof io.a2a.grpc.Task + ? (io.a2a.grpc.Task) task + : ((io.a2a.grpc.Task.Builder) task).build(); + return TaskMapper.INSTANCE.fromProto(taskProto); } public static Message message(io.a2a.grpc.MessageOrBuilder message) { if (message.getMessageId().isEmpty()) { throw new InvalidParamsError(); } - - return new Message( - role(message.getRole()), - message.getPartsList().stream().map(item -> part(item)).collect(Collectors.toList()), - message.getMessageId().isEmpty() ? null : message.getMessageId(), - message.getContextId().isEmpty() ? null : message.getContextId(), - message.getTaskId().isEmpty() ? null : message.getTaskId(), - message.getReferenceTaskIdsList().isEmpty() ? null : message.getReferenceTaskIdsList(), - struct(message.getMetadata()), - message.getExtensionsList().isEmpty() ? null : message.getExtensionsList() - ); + io.a2a.grpc.Message messageProto = message instanceof io.a2a.grpc.Message + ? (io.a2a.grpc.Message) message + : ((io.a2a.grpc.Message.Builder) message).build(); + return MessageMapper.INSTANCE.fromProto(messageProto); } public static TaskStatusUpdateEvent taskStatusUpdateEvent(io.a2a.grpc.TaskStatusUpdateEventOrBuilder taskStatusUpdateEvent) { - return new TaskStatusUpdateEvent.Builder() - .taskId(taskStatusUpdateEvent.getTaskId()) - .status(taskStatus(taskStatusUpdateEvent.getStatus())) - .contextId(taskStatusUpdateEvent.getContextId()) - .isFinal(taskStatusUpdateEvent.getFinal()) - .metadata(struct(taskStatusUpdateEvent.getMetadata())) - .build(); + io.a2a.grpc.TaskStatusUpdateEvent eventProto = taskStatusUpdateEvent instanceof io.a2a.grpc.TaskStatusUpdateEvent + ? (io.a2a.grpc.TaskStatusUpdateEvent) taskStatusUpdateEvent + : ((io.a2a.grpc.TaskStatusUpdateEvent.Builder) taskStatusUpdateEvent).build(); + return TaskStatusUpdateEventMapper.INSTANCE.fromProto(eventProto); } public static TaskArtifactUpdateEvent taskArtifactUpdateEvent(io.a2a.grpc.TaskArtifactUpdateEventOrBuilder taskArtifactUpdateEvent) { - return new TaskArtifactUpdateEvent.Builder() - .taskId(taskArtifactUpdateEvent.getTaskId()) - .append(taskArtifactUpdateEvent.getAppend()) - .lastChunk(taskArtifactUpdateEvent.getLastChunk()) - .artifact(artifact(taskArtifactUpdateEvent.getArtifact())) - .contextId(taskArtifactUpdateEvent.getContextId()) - .metadata(struct(taskArtifactUpdateEvent.getMetadata())) - .build(); - } - - private static Artifact artifact(io.a2a.grpc.ArtifactOrBuilder artifact) { - return new Artifact( - artifact.getArtifactId(), - artifact.getName(), - artifact.getDescription(), - artifact.getPartsList().stream().map(item -> part(item)).collect(Collectors.toList()), - struct(artifact.getMetadata()), - artifact.getExtensionsList().isEmpty() ? null : artifact.getExtensionsList() - ); - } - - private static Part part(io.a2a.grpc.PartOrBuilder part) { - if (part.hasText()) { - return textPart(part.getText()); - } else if (part.hasFile()) { - return filePart(part.getFile()); - } else if (part.hasData()) { - return dataPart(part.getData()); - } - throw new InvalidRequestError(); - } - - private static TextPart textPart(String text) { - return new TextPart(text); - } - - private static FilePart filePart(io.a2a.grpc.FilePartOrBuilder filePart) { - if (filePart.hasFileWithBytes()) { - return new FilePart(new FileWithBytes(filePart.getMimeType(), - ! filePart.getName().isEmpty() ? filePart.getName() : null, - filePart.getFileWithBytes().toStringUtf8())); - } else if (filePart.hasFileWithUri()) { - return new FilePart(new FileWithUri(filePart.getMimeType(), - ! filePart.getName().isEmpty() ? filePart.getName() : null, - filePart.getFileWithUri())); - } - throw new InvalidRequestError(); + io.a2a.grpc.TaskArtifactUpdateEvent eventProto = taskArtifactUpdateEvent instanceof io.a2a.grpc.TaskArtifactUpdateEvent + ? (io.a2a.grpc.TaskArtifactUpdateEvent) taskArtifactUpdateEvent + : ((io.a2a.grpc.TaskArtifactUpdateEvent.Builder) taskArtifactUpdateEvent).build(); + return TaskArtifactUpdateEventMapper.INSTANCE.fromProto(eventProto); } - private static DataPart dataPart(io.a2a.grpc.DataPartOrBuilder dataPart) { - return new DataPart(struct(dataPart.getData())); + public static ListTasksResult listTasksResult(io.a2a.grpc.ListTasksResponseOrBuilder listTasksResponse) { + io.a2a.grpc.ListTasksResponse eventProto = listTasksResponse instanceof io.a2a.grpc.ListTasksResponse + ? (io.a2a.grpc.ListTasksResponse) listTasksResponse + : ((io.a2a.grpc.ListTasksResponse.Builder) listTasksResponse).build(); + return ListTasksResultMapper.INSTANCE.fromProto(eventProto); } - private static @Nullable TaskStatus taskStatus(io.a2a.grpc.TaskStatusOrBuilder taskStatus) { - TaskState state = taskState(taskStatus.getState()); - if (state == null) { - return null; - } - return new TaskStatus( - taskState(taskStatus.getState()), - taskStatus.hasUpdate() ? message(taskStatus.getUpdateOrBuilder()) : null, - OffsetDateTime.ofInstant(Instant.ofEpochSecond(taskStatus.getTimestamp().getSeconds(), taskStatus.getTimestamp().getNanos()), ZoneOffset.UTC) - ); - } - - private static Message.@Nullable Role role(io.a2a.grpc.Role role) { - if (role == null) { - return null; - } - return switch (role) { - case ROLE_USER -> - Message.Role.USER; - case ROLE_AGENT -> - Message.Role.AGENT; - default -> - throw new InvalidRequestError(); - }; - } - - private static @Nullable TaskState taskState(io.a2a.grpc.TaskState taskState) { - if (taskState == null) { - return null; - } - return switch (taskState) { - case TASK_STATE_SUBMITTED -> - TaskState.SUBMITTED; - case TASK_STATE_WORKING -> - TaskState.WORKING; - case TASK_STATE_INPUT_REQUIRED -> - TaskState.INPUT_REQUIRED; - case TASK_STATE_AUTH_REQUIRED -> - TaskState.AUTH_REQUIRED; - case TASK_STATE_COMPLETED -> - TaskState.COMPLETED; - case TASK_STATE_CANCELLED -> - TaskState.CANCELED; - case TASK_STATE_FAILED -> - TaskState.FAILED; - case TASK_STATE_REJECTED -> - TaskState.REJECTED; - case TASK_STATE_UNSPECIFIED -> - null; - case UNRECOGNIZED -> - null; - }; - } - - private static @Nullable Map struct(Struct struct) { - if (struct == null || struct.getFieldsCount() == 0) { - return null; - } - return struct.getFieldsMap().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> value(e.getValue()))); - } - - private static @Nullable Object value(Value value) { - switch (value.getKindCase()) { - case STRUCT_VALUE: - return struct(value.getStructValue()); - case LIST_VALUE: - return value.getListValue().getValuesList().stream() - .map(FromProto::value) - .collect(Collectors.toList()); - case BOOL_VALUE: - return value.getBoolValue(); - case NUMBER_VALUE: - return value.getNumberValue(); - case STRING_VALUE: - return value.getStringValue(); - case NULL_VALUE: - default: - throw new InvalidRequestError(); - } + public static StreamingEventKind streamingEventKind(io.a2a.grpc.StreamResponseOrBuilder streamResponse) { + io.a2a.grpc.StreamResponse streamResponseProto = streamResponse instanceof io.a2a.grpc.StreamResponse + ? (io.a2a.grpc.StreamResponse) streamResponse + : ((io.a2a.grpc.StreamResponse.Builder) streamResponse).build(); + return StreamResponseMapper.INSTANCE.fromProto(streamResponseProto); } } diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index 6904a918a..e5987813c 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package a2a.v1; +//From commit b0784199543eebf2e95dcb02e9336cb213923506 + import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; @@ -15,27 +17,16 @@ option java_multiple_files = true; option java_outer_classname = "A2A"; option java_package = "io.a2a.grpc"; -// A2AService defines the gRPC version of the A2A protocol. This has a slightly -// different shape than the JSONRPC version to better conform to AIP-127, -// where appropriate. The nouns are AgentCard, Message, Task and -// TaskPushNotificationConfig. -// - Messages are not a standard resource so there is no get/delete/update/list -// interface, only a send and stream custom methods. -// - Tasks have a get interface and custom cancel and subscribe methods. -// - TaskPushNotificationConfig are a resource whose parent is a task. -// They have get, list and create methods. -// - AgentCard is a static resource with only a get method. +// A2AService defines the operations of the A2A protocol. service A2AService { - // Send a message to the agent. This is a blocking call that will return the - // task once it is completed, or a LRO if requested. + // Send a message to the agent. rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { option (google.api.http) = { post: "/v1/message:send" body: "*" }; } - // SendStreamingMessage is a streaming call that will return a stream of - // task update events until the Task is in an interrupted or terminal state. + // SendStreamingMessage is a streaming version of SendMessage. rpc SendStreamingMessage(SendMessageRequest) returns (stream StreamResponse) { option (google.api.http) = { post: "/v1/message:stream" @@ -56,19 +47,16 @@ service A2AService { get: "/v1/tasks" }; } - // Cancel a task from the agent. If supported one should expect no - // more task updates for the task. + // Cancel a task. rpc CancelTask(CancelTaskRequest) returns (Task) { option (google.api.http) = { post: "/v1/{name=tasks/*}:cancel" body: "*" }; } - // TaskSubscription is a streaming call that will return a stream of task - // update events. This attaches the stream to an existing in process task. - // If the task is complete the stream will return the completed task (like - // GetTask) and close the stream. - rpc TaskSubscription(TaskSubscriptionRequest) + // SubscribeToTask allows subscribing to task updates for tasks not in terminal state. + // Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected). + rpc SubscribeToTask(SubscribeToTaskRequest) returns (stream StreamResponse) { option (google.api.http) = { get: "/v1/{name=tasks/*}:subscribe" @@ -76,7 +64,7 @@ service A2AService { } // Set a push notification config for a task. - rpc CreateTaskPushNotificationConfig(CreateTaskPushNotificationConfigRequest) + rpc SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig) { option (google.api.http) = { post: "/v1/{parent=tasks/*/pushNotificationConfigs}" @@ -100,10 +88,10 @@ service A2AService { }; option (google.api.method_signature) = "parent"; } - // GetAgentCard returns the agent card for the agent. - rpc GetAgentCard(GetAgentCardRequest) returns (AgentCard) { + // GetExtendedAgentCard returns the extended agent card for authenticated agents. + rpc GetExtendedAgentCard(GetExtendedAgentCardRequest) returns (AgentCard) { option (google.api.http) = { - get: "/v1/card" + get: "/v1/extendedAgentCard" }; } // Delete a push notification config for a task. @@ -118,23 +106,19 @@ service A2AService { ///////// Data Model //////////// -// --8<-- [start:MessageSendConfiguration] +// --8<-- [start:SendMessageConfiguration] // Configuration of a send message request. message SendMessageConfiguration { // The output modes that the agent is expected to respond with. repeated string accepted_output_modes = 1; // A configuration of a webhook that can be used to receive updates PushNotificationConfig push_notification_config = 2; - // The maximum number of messages to include in the history. if 0, the - // history will be unlimited. - int32 history_length = 3; - // If true, the message will be blocking until the task is completed. If - // false, the message will be non-blocking and the task will be returned - // immediately. It is the caller's responsibility to check for any task - // updates. + // The maximum number of messages to include in the history. + optional int32 history_length = 3; + // If true, the message will be blocking until the task is completed. bool blocking = 4; } -// --8<-- [end:MessageSendConfiguration] +// --8<-- [end:SendMessageConfiguration] // --8<-- [start:Task] // Task is the core unit of action for A2A. It has a current status @@ -144,12 +128,12 @@ message SendMessageConfiguration { message Task { // Unique identifier (e.g. UUID) for the task, generated by the server for a // new task. - string id = 1; + string id = 1 [(google.api.field_behavior) = REQUIRED]; // Unique identifier (e.g. UUID) for the contextual collection of interactions // (tasks and messages). Created by the A2A server. - string context_id = 2; + string context_id = 2 [(google.api.field_behavior) = REQUIRED]; // The current status of a Task, including state and a message. - TaskStatus status = 3; + TaskStatus status = 3 [(google.api.field_behavior) = REQUIRED]; // A set of output artifacts for a Task. repeated Artifact artifacts = 4; // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED @@ -157,6 +141,8 @@ message Task { repeated Message history = 5; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED // A key/value object to store custom metadata about a task. + // Optional metadata for extensions. The key is an extension-specific + // identifier. google.protobuf.Struct metadata = 6; } // --8<-- [end:Task] @@ -193,11 +179,11 @@ enum TaskState { // --8<-- [start:TaskStatus] // A container for the status of a task message TaskStatus { - // The current state of this task - TaskState state = 1; + // The current state of this task. + TaskState state = 1 [(google.api.field_behavior) = REQUIRED]; // A message associated with the status. - Message update = 2 [json_name = "message"]; - // Timestamp when the status was recorded. + Message message = 2; + // ISO 8601 Timestamp when the status was recorded. // Example: "2023-10-27T10:00:00Z" google.protobuf.Timestamp timestamp = 3; } @@ -209,8 +195,11 @@ message TaskStatus { // a structured data blob (i.e. JSON). message Part { oneof part { + // The string content of the text part. string text = 1; + // The file content, represented as either a URI or as base64-encoded bytes. FilePart file = 2; + // The structured data content. DataPart data = 3; } // Optional metadata associated with this part. @@ -225,10 +214,14 @@ message Part { // from the file_with_uri source. message FilePart { oneof file { + // A URL pointing to the file's content. string file_with_uri = 1; + // The base64-encoded content of the file. bytes file_with_bytes = 2; } - string mime_type = 3; + // The media type of the file (e.g., "application/pdf"). + string media_type = 3; + // An optional name for the file (e.g., "document.pdf"). string name = 4; } // --8<-- [end:FilePart] @@ -236,10 +229,13 @@ message FilePart { // --8<-- [start:DataPart] // DataPart represents a structured blob. This is most commonly a JSON payload. message DataPart { - google.protobuf.Struct data = 1; + // The structured data content. + google.protobuf.Struct data = 1 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:DataPart] +// --8<-- [start:Role] +// Defines the sender of a message in A2A protocol communication. enum Role { ROLE_UNSPECIFIED = 0; // USER role refers to communication from the client to the server. @@ -247,6 +243,7 @@ enum Role { // AGENT role refers to communication from the server to the client. ROLE_AGENT = 2; } +// --8<-- [end:Role] // --8<-- [start:Message] // Message is one unit of communication between client and server. It is @@ -257,20 +254,20 @@ enum Role { // except the server decides if a task is created and whether to include the // task_id. message Message { - // The unique identifier (e.g. UUID)of the message. This is required and + // The unique identifier (e.g. UUID) of the message. This is required and // created by the message creator. - string message_id = 1; + string message_id = 1 [(google.api.field_behavior) = REQUIRED]; // The context id of the message. This is optional and if set, the message // will be associated with the given context. string context_id = 2; // The task id of the message. This is optional and if set, the message // will be associated with the given task. string task_id = 3; - // A role for the message. - Role role = 4; + // Identifies the sender of the message. + Role role = 4 [(google.api.field_behavior) = REQUIRED]; // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED // Parts is the container of the message content. - repeated Part parts = 5; + repeated Part parts = 5 [(google.api.field_behavior) = REQUIRED]; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED // Any optional metadata to provide along with the message. google.protobuf.Struct metadata = 6; @@ -282,19 +279,17 @@ message Message { // --8<-- [end:Message] // --8<-- [start:Artifact] -// Artifacts are the container for task completed results. These are similar -// to Messages but are intended to be the product of a task, as opposed to -// point-to-point communication. +// Artifacts represent task outputs. message Artifact { // Unique identifier (e.g. UUID) for the artifact. It must be at least unique // within a task. - string artifact_id = 1; + string artifact_id = 1 [(google.api.field_behavior) = REQUIRED]; // A human readable name for the artifact. string name = 3; // A human readable description of the artifact, optional. string description = 4; // The content of the artifact. - repeated Part parts = 5; + repeated Part parts = 5 [(google.api.field_behavior) = REQUIRED]; // Optional metadata included with the artifact. google.protobuf.Struct metadata = 6; // The URIs of extensions that are present or contributed to this Artifact. @@ -303,17 +298,17 @@ message Artifact { // --8<-- [end:Artifact] // --8<-- [start:TaskStatusUpdateEvent] -// TaskStatusUpdateEvent is a delta even on a task indicating that a task -// has changed. +// An event sent by the agent to notify the client of a change in a task's +// status. message TaskStatusUpdateEvent { // The id of the task that is changed - string task_id = 1; + string task_id = 1 [(google.api.field_behavior) = REQUIRED]; // The id of the context that the task belongs to - string context_id = 2; + string context_id = 2 [(google.api.field_behavior) = REQUIRED]; // The new status of the task. - TaskStatus status = 3; - // Whether this is the last status update expected for this task. - bool final = 4; + TaskStatus status = 3 [(google.api.field_behavior) = REQUIRED]; + // If true, this is the final event in the stream for this interaction. + bool final = 4 [(google.api.field_behavior) = REQUIRED]; // Optional metadata to associate with the task update. google.protobuf.Struct metadata = 5; } @@ -323,15 +318,16 @@ message TaskStatusUpdateEvent { // TaskArtifactUpdateEvent represents a task delta where an artifact has // been generated. message TaskArtifactUpdateEvent { - // The id of the task for this artifact - string task_id = 1; - // The id of the context that this task belongs too - string context_id = 2; - // The artifact itself - Artifact artifact = 3; - // Whether this should be appended to a prior one produced + // The id of the task for this artifact. + string task_id = 1 [(google.api.field_behavior) = REQUIRED]; + // The id of the context that this task belongs to. + string context_id = 2 [(google.api.field_behavior) = REQUIRED]; + // The artifact that was generated or updated. + Artifact artifact = 3 [(google.api.field_behavior) = REQUIRED]; + // If true, the content of this artifact should be appended to a previously + // sent artifact with the same ID. bool append = 4; - // Whether this represents the last part of an artifact + // If true, this is the final chunk of the artifact. bool last_chunk = 5; // Optional metadata associated with the artifact update. google.protobuf.Struct metadata = 6; @@ -344,7 +340,7 @@ message PushNotificationConfig { // A unique identifier (e.g. UUID) for this push notification. string id = 1; // Url to send the notification too - string url = 2; + string url = 2 [(google.api.field_behavior) = REQUIRED]; // Token unique for this task/session string token = 3; // Information about the authentication to sent with the notification @@ -355,170 +351,149 @@ message PushNotificationConfig { // --8<-- [start:PushNotificationAuthenticationInfo] // Defines authentication details, used for push notifications. message AuthenticationInfo { - // Supported authentication schemes - e.g. Basic, Bearer, etc - repeated string schemes = 1; + // A list of supported authentication schemes (e.g., 'Basic', 'Bearer'). + repeated string schemes = 1 [(google.api.field_behavior) = REQUIRED]; // Optional credentials string credentials = 2; } // --8<-- [end:PushNotificationAuthenticationInfo] // --8<-- [start:AgentInterface] -// Defines additional transport information for the agent. +// Declares a combination of a target URL and a transport protocol for interacting with the agent. +// This allows agents to expose the same functionality over multiple protocol binding mechanisms. message AgentInterface { - // The url this interface is found at. - string url = 1; - // The transport supported this url. This is an open form string, to be - // easily extended for many transport protocols. The core ones officially + // The URL where this interface is available. Must be a valid absolute HTTPS URL in production. + // Example: "https://api.example.com/a2a/v1", "https://grpc.example.com/a2a" + string url = 1 [(google.api.field_behavior) = REQUIRED]; + // The protocol binding supported at this URL. This is an open form string, to be + // easily extended for other protocol bindings. The core ones officially // supported are JSONRPC, GRPC and HTTP+JSON. - string transport = 2; + // Example: "JSONRPC", "GRPC", "HTTP+JSON" + string protocol_binding = 2 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:AgentInterface] // --8<-- [start:AgentCard] +// AgentCard is a self-describing manifest for an agent. It provides essential +// metadata including the agent's identity, capabilities, skills, supported +// communication methods, and security requirements. // AgentCard conveys key information: // - Overall details (version, name, description, uses) // - Skills; a set of actions/solutions the agent can perform // - Default modalities/content types supported by the agent. // - Authentication requirements -// Next ID: 19 +// Next ID: 20 message AgentCard { // The version of the A2A protocol this agent supports. - string protocol_version = 16; + // Default: "1.0" + optional string protocol_version = 16 [(google.api.field_behavior) = REQUIRED]; // A human readable name for the agent. // Example: "Recipe Agent" - string name = 1; - // A description of the agent's domain of action/solution space. + string name = 1 [(google.api.field_behavior) = REQUIRED]; + // A human-readable description of the agent, assisting users and other agents + // in understanding its purpose. // Example: "Agent that helps users with recipes and cooking." - string description = 2; - // A URL to the address the agent is hosted at. This represents the - // preferred endpoint as declared by the agent. - string url = 3; - // The transport of the preferred endpoint. If empty, defaults to JSONRPC. - string preferred_transport = 14; - // Announcement of additional supported transports. Client can use any of - // the supported transports. - repeated AgentInterface additional_interfaces = 15; + string description = 2 [(google.api.field_behavior) = REQUIRED]; + // Ordered list of supported interfaces. First entry is preferred. + repeated AgentInterface supported_interfaces = 19; + // DEPRECATED: Use 'supported_interfaces' instead. + optional string url = 3 [deprecated = true]; + // DEPRECATED: Use 'supported_interfaces' instead. + optional string preferred_transport = 14 [deprecated = true]; + // DEPRECATED: Use 'supported_interfaces' instead. + repeated AgentInterface additional_interfaces = 15 [deprecated = true]; // The service provider of the agent. AgentProvider provider = 4; // The version of the agent. // Example: "1.0.0" - string version = 5; + string version = 5 [(google.api.field_behavior) = REQUIRED]; // A url to provide additional documentation about the agent. - string documentation_url = 6; + optional string documentation_url = 6; // A2A Capability set supported by the agent. - AgentCapabilities capabilities = 7; + AgentCapabilities capabilities = 7 [(google.api.field_behavior) = REQUIRED]; // The security scheme details used for authenticating with this agent. map security_schemes = 8; // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED // Security requirements for contacting the agent. - // This list can be seen as an OR of ANDs. Each object in the list describes - // one possible set of security requirements that must be present on a - // request. This allows specifying, for example, "callers must either use - // OAuth OR an API Key AND mTLS." - // Example: - // security { - // schemes { key: "oauth" value { list: ["read"] } } - // } - // security { - // schemes { key: "api-key" } - // schemes { key: "mtls" } - // } repeated Security security = 9; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED // The set of interaction modes that the agent supports across all skills. - // This can be overridden per skill. Defined as mime types. - repeated string default_input_modes = 10; - // The mime types supported as outputs from this agent. - repeated string default_output_modes = 11; + // This can be overridden per skill. Defined as media types. + repeated string default_input_modes = 10 [(google.api.field_behavior) = REQUIRED]; + // The media types supported as outputs from this agent. + repeated string default_output_modes = 11 [(google.api.field_behavior) = REQUIRED]; // Skills represent a unit of ability an agent can perform. This may // somewhat abstract but represents a more focused set of actions that the // agent is highly likely to succeed at. - repeated AgentSkill skills = 12; - // Whether the agent supports providing an extended agent card when - // the user is authenticated, i.e. is the card from .well-known - // different than the card from GetAgentCard. - bool supports_authenticated_extended_card = 13; + repeated AgentSkill skills = 12 [(google.api.field_behavior) = REQUIRED]; + // Whether the agent supports providing an extended agent card when authenticated. + optional bool supports_authenticated_extended_card = 13; // JSON Web Signatures computed for this AgentCard. repeated AgentCardSignature signatures = 17; // An optional URL to an icon for the agent. - string icon_url = 18; + optional string icon_url = 18; } // --8<-- [end:AgentCard] // --8<-- [start:AgentProvider] -// Represents information about the service provider of an agent. +// Represents the service provider of an agent. message AgentProvider { - // The providers reference url + // A URL for the agent provider's website or relevant documentation. // Example: "https://ai.google.dev" - string url = 1; - // The providers organization name + string url = 1 [(google.api.field_behavior) = REQUIRED]; + // The name of the agent provider's organization. // Example: "Google" - string organization = 2; + string organization = 2 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:AgentProvider] // --8<-- [start:AgentCapabilities] -// Defines the A2A feature set supported by the agent +// Defines optional capabilities supported by an agent. message AgentCapabilities { - // If the agent will support streaming responses - bool streaming = 1; - // If the agent can send push notifications to the clients webhook - bool push_notifications = 2; - // Extensions supported by this agent. + // Indicates if the agent supports streaming responses. + optional bool streaming = 1; + // Indicates if the agent supports sending push notifications for asynchronous task updates. + optional bool push_notifications = 2; + // A list of protocol extensions supported by the agent. repeated AgentExtension extensions = 3; - // If the agent provides a history of state transitions for a task. - bool state_transition_history = 4; + // Indicates if the agent provides a history of state transitions for a task. + optional bool state_transition_history = 4; } // --8<-- [end:AgentCapabilities] // --8<-- [start:AgentExtension] -// A declaration of an extension supported by an Agent. +// A declaration of a protocol extension supported by an Agent. message AgentExtension { - // The URI of the extension. - // Example: "https://developers.google.com/identity/protocols/oauth2" + // The unique URI identifying the extension. string uri = 1; - // A description of how this agent uses this extension. - // Example: "Google OAuth 2.0 authentication" + // A human-readable description of how this agent uses the extension. string description = 2; - // Whether the client must follow specific requirements of the extension. - // Example: false + // If true, the client must understand and comply with the extension's requirements. bool required = 3; - // Optional configuration for the extension. + // Optional, extension-specific configuration parameters. google.protobuf.Struct params = 4; } // --8<-- [end:AgentExtension] // --8<-- [start:AgentSkill] -// AgentSkill represents a unit of action/solution that the agent can perform. -// One can think of this as a type of highly reliable solution that an agent -// can be tasked to provide. Agents have the autonomy to choose how and when -// to use specific skills, but clients should have confidence that if the -// skill is defined that unit of action can be reliably performed. +// Represents a distinct capability or function that an agent can perform. message AgentSkill { - // Unique identifier of the skill within this agent. - string id = 1; - // A human readable name for the skill. - string name = 2; - // A human (or llm) readable description of the skill - // details and behaviors. - string description = 3; - // A set of tags for the skill to enhance categorization/utilization. - // Example: ["cooking", "customer support", "billing"] - repeated string tags = 4; - // A set of example queries that this skill is designed to address. - // These examples should help the caller to understand how to craft requests - // to the agent to achieve specific goals. - // Example: ["I need a recipe for bread"] + // A unique identifier for the agent's skill. + string id = 1 [(google.api.field_behavior) = REQUIRED]; + // A human-readable name for the skill. + string name = 2 [(google.api.field_behavior) = REQUIRED]; + // A detailed description of the skill. + string description = 3 [(google.api.field_behavior) = REQUIRED]; + // A set of keywords describing the skill's capabilities. + repeated string tags = 4 [(google.api.field_behavior) = REQUIRED]; + // Example prompts or scenarios that this skill can handle. repeated string examples = 5; - // Possible input modalities supported. + // The set of supported input media types for this skill, overriding the agent's defaults. repeated string input_modes = 6; - // Possible output modalities produced + // The set of supported output media types for this skill, overriding the agent's defaults. repeated string output_modes = 7; // protolint:disable REPEATED_FIELD_NAMES_PLURALIZED - // Security schemes necessary for the agent to leverage this skill. - // As in the overall AgentCard.security, this list represents a logical OR of - // security requirement objects. Each object is a set of security schemes - // that must be used together (a logical AND). + // Security schemes necessary for this skill. repeated Security security = 8; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED } @@ -539,12 +514,14 @@ message AgentCardSignature { // --8<-- [end:AgentCardSignature] // --8<-- [start:TaskPushNotificationConfig] +// A container associating a push notification configuration with a specific +// task. message TaskPushNotificationConfig { // The resource name of the config. // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} - string name = 1; + string name = 1 [(google.api.field_behavior) = REQUIRED]; // The push notification configuration details. - PushNotificationConfig push_notification_config = 2; + PushNotificationConfig push_notification_config = 2 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:TaskPushNotificationConfig] @@ -559,6 +536,9 @@ message Security { } // --8<-- [start:SecurityScheme] +// Defines a security scheme that can be used to secure an agent's endpoints. +// This is a discriminated union type based on the OpenAPI 3.2 Security Scheme Object. +// See: https://spec.openapis.org/oas/v3.2.0.html#security-scheme-object message SecurityScheme { oneof scheme { APIKeySecurityScheme api_key_security_scheme = 1; @@ -571,133 +551,133 @@ message SecurityScheme { // --8<-- [end:SecurityScheme] // --8<-- [start:APIKeySecurityScheme] +// Defines a security scheme using an API key. message APIKeySecurityScheme { - // Description of this security scheme. + // An optional description for the security scheme. string description = 1; - // Location of the API key, valid values are "query", "header", or "cookie" - string location = 2; - // Name of the header, query or cookie parameter to be used. - string name = 3; + // The location of the API key. Valid values are "query", "header", or "cookie". + string location = 2 [(google.api.field_behavior) = REQUIRED]; + // The name of the header, query, or cookie parameter to be used. + string name = 3 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:APIKeySecurityScheme] // --8<-- [start:HTTPAuthSecurityScheme] +// Defines a security scheme using HTTP authentication. message HTTPAuthSecurityScheme { - // Description of this security scheme. + // An optional description for the security scheme. string description = 1; - // The name of the HTTP Authentication scheme to be used in the - // Authorization header as defined in RFC7235. The values used SHOULD be - // registered in the IANA Authentication Scheme registry. - // The value is case-insensitive, as defined in RFC7235. - string scheme = 2; - // A hint to the client to identify how the bearer token is formatted. - // Bearer tokens are usually generated by an authorization server, so - // this information is primarily for documentation purposes. + // The name of the HTTP Authentication scheme to be used in the Authorization header, + // as defined in RFC7235 (e.g., "Bearer"). + // This value should be registered in the IANA Authentication Scheme registry. + string scheme = 2 [(google.api.field_behavior) = REQUIRED]; + // A hint to the client to identify how the bearer token is formatted (e.g., "JWT"). + // This is primarily for documentation purposes. string bearer_format = 3; } // --8<-- [end:HTTPAuthSecurityScheme] // --8<-- [start:OAuth2SecurityScheme] +// Defines a security scheme using OAuth 2.0. message OAuth2SecurityScheme { - // Description of this security scheme. + // An optional description for the security scheme. string description = 1; - // An object containing configuration information for the flow types supported - OAuthFlows flows = 2; + // An object containing configuration information for the supported OAuth 2.0 flows. + OAuthFlows flows = 2 [(google.api.field_behavior) = REQUIRED]; // URL to the oauth2 authorization server metadata - // [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required. + // RFC8414 (https://datatracker.ietf.org/doc/html/rfc8414). TLS is required. string oauth2_metadata_url = 3; } // --8<-- [end:OAuth2SecurityScheme] // --8<-- [start:OpenIdConnectSecurityScheme] +// Defines a security scheme using OpenID Connect. message OpenIdConnectSecurityScheme { - // Description of this security scheme. + // An optional description for the security scheme. string description = 1; - // Well-known URL to discover the [[OpenID-Connect-Discovery]] provider - // metadata. - string open_id_connect_url = 2; + // The OpenID Connect Discovery URL for the OIDC provider's metadata. + // See: https://openid.net/specs/openid-connect-discovery-1_0.html + string open_id_connect_url = 2 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:OpenIdConnectSecurityScheme] // --8<-- [start:MutualTLSSecurityScheme] +// Defines a security scheme using mTLS authentication. message MutualTlsSecurityScheme { - // Description of this security scheme. + // An optional description for the security scheme. string description = 1; } // --8<-- [end:MutualTLSSecurityScheme] // --8<-- [start:OAuthFlows] +// Defines the configuration for the supported OAuth 2.0 flows. message OAuthFlows { oneof flow { + // Configuration for the OAuth Authorization Code flow. AuthorizationCodeOAuthFlow authorization_code = 1; + // Configuration for the OAuth Client Credentials flow. ClientCredentialsOAuthFlow client_credentials = 2; + // Configuration for the OAuth Implicit flow. ImplicitOAuthFlow implicit = 3; + // Configuration for the OAuth Resource Owner Password flow. PasswordOAuthFlow password = 4; } } // --8<-- [end:OAuthFlows] // --8<-- [start:AuthorizationCodeOAuthFlow] +// Defines configuration details for the OAuth 2.0 Authorization Code flow. message AuthorizationCodeOAuthFlow { - // The authorization URL to be used for this flow. This MUST be in the - // form of a URL. The OAuth2 standard requires the use of TLS - string authorization_url = 1; - // The token URL to be used for this flow. This MUST be in the form of a URL. - // The OAuth2 standard requires the use of TLS. - string token_url = 2; - // The URL to be used for obtaining refresh tokens. This MUST be in the - // form of a URL. The OAuth2 standard requires the use of TLS. + // The authorization URL to be used for this flow. + string authorization_url = 1 [(google.api.field_behavior) = REQUIRED]; + // The token URL to be used for this flow. + string token_url = 2 [(google.api.field_behavior) = REQUIRED]; + // The URL to be used for obtaining refresh tokens. string refresh_url = 3; - // The available scopes for the OAuth2 security scheme. A map between the - // scope name and a short description for it. The map MAY be empty. - map scopes = 4; + // The available scopes for the OAuth2 security scheme. + map scopes = 4 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:AuthorizationCodeOAuthFlow] // --8<-- [start:ClientCredentialsOAuthFlow] +// Defines configuration details for the OAuth 2.0 Client Credentials flow. message ClientCredentialsOAuthFlow { - // The token URL to be used for this flow. This MUST be in the form of a URL. - // The OAuth2 standard requires the use of TLS. - string token_url = 1; - // The URL to be used for obtaining refresh tokens. This MUST be in the - // form of a URL. The OAuth2 standard requires the use of TLS. + // The token URL to be used for this flow. + string token_url = 1 [(google.api.field_behavior) = REQUIRED]; + // The URL to be used for obtaining refresh tokens. string refresh_url = 2; - // The available scopes for the OAuth2 security scheme. A map between the - // scope name and a short description for it. The map MAY be empty. - map scopes = 3; + // The available scopes for the OAuth2 security scheme. + map scopes = 3 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:ClientCredentialsOAuthFlow] // --8<-- [start:ImplicitOAuthFlow] +// Defines configuration details for the OAuth 2.0 Implicit flow. message ImplicitOAuthFlow { - // The authorization URL to be used for this flow. This MUST be in the - // form of a URL. The OAuth2 standard requires the use of TLS - string authorization_url = 1; - // The URL to be used for obtaining refresh tokens. This MUST be in the - // form of a URL. The OAuth2 standard requires the use of TLS. + // The authorization URL to be used for this flow. + string authorization_url = 1 [(google.api.field_behavior) = REQUIRED]; + // The URL to be used for obtaining refresh tokens. string refresh_url = 2; - // The available scopes for the OAuth2 security scheme. A map between the - // scope name and a short description for it. The map MAY be empty. - map scopes = 3; + // The available scopes for the OAuth2 security scheme. + map scopes = 3 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:ImplicitOAuthFlow] // --8<-- [start:PasswordOAuthFlow] +// Defines configuration details for the OAuth 2.0 Resource Owner Password flow. message PasswordOAuthFlow { - // The token URL to be used for this flow. This MUST be in the form of a URL. - // The OAuth2 standard requires the use of TLS. - string token_url = 1; - // The URL to be used for obtaining refresh tokens. This MUST be in the - // form of a URL. The OAuth2 standard requires the use of TLS. + // The token URL to be used for this flow. + string token_url = 1 [(google.api.field_behavior) = REQUIRED]; + // The URL to be used for obtaining refresh tokens. string refresh_url = 2; - // The available scopes for the OAuth2 security scheme. A map between the - // scope name and a short description for it. The map MAY be empty. - map scopes = 3; + // The available scopes for the OAuth2 security scheme. + map scopes = 3 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:PasswordOAuthFlow] ///////////// Request Messages /////////// -// --8<-- [start:MessageSendParams] +// --8<-- [start:SendMessageRequest] +// Represents a request for the `message/send` method. message SendMessageRequest { // The message to send to the agent. Message request = 1 @@ -707,56 +687,60 @@ message SendMessageRequest { // Optional metadata for the request. google.protobuf.Struct metadata = 3; } -// --8<-- [end:MessageSendParams] +// --8<-- [end:SendMessageRequest] // --8<-- [start:GetTaskRequest] +// Represents a request for the `tasks/get` method. message GetTaskRequest { // The resource name of the task. // Format: tasks/{task_id} string name = 1 [(google.api.field_behavior) = REQUIRED]; - // The number of most recent messages from the task's history to retrieve. - int32 history_length = 2; + // The maximum number of messages to include in the history. + optional int32 history_length = 2; } // --8<-- [end:GetTaskRequest] // --8<-- [start:ListTasksRequest] +// Parameters for listing tasks with optional filtering criteria. message ListTasksRequest { - // Filter tasks by context ID to get tasks from a specific conversation - // or session. + // Filter tasks by context ID to get tasks from a specific conversation or session. string context_id = 1; // Filter tasks by their current status state. TaskState status = 2; // Maximum number of tasks to return. Must be between 1 and 100. // Defaults to 50 if not specified. - int32 page_size = 3; - // Token for pagination. Use the next_page_token from a previous - // ListTasksResponse. + optional int32 page_size = 3; + // Token for pagination. Use the next_page_token from a previous ListTasksResponse. string page_token = 4; - // Number of recent messages to include in each task's history. - // Must be non-negative. Defaults to 0 if not specified. - int32 history_length = 5; - // Filter tasks updated after this timestamp. Only tasks with a last - // updated time greater than or equal to this value will be returned. - google.protobuf.Timestamp last_updated_time = 6; + // The maximum number of messages to include in each task's history. + optional int32 history_length = 5; + // Filter tasks updated after this timestamp (milliseconds since epoch). + // Only tasks with a last updated time greater than or equal to this value will be returned. + int64 last_updated_after = 6; // Whether to include artifacts in the returned tasks. // Defaults to false to reduce payload size. - bool include_artifacts = 7; + optional bool include_artifacts = 7; + // Request-specific metadata. + google.protobuf.Struct metadata = 8; } // --8<-- [end:ListTasksRequest] // --8<-- [start:ListTasksResponse] +// Result object for tasks/list method containing an array of tasks and pagination information. message ListTasksResponse { // Array of tasks matching the specified criteria. - repeated Task tasks = 1; - // Token for retrieving the next page of results. - // Empty string if no more results. - string next_page_token = 2; + repeated Task tasks = 1 [(google.api.field_behavior) = REQUIRED]; + // Token for retrieving the next page. Empty string if no more results. + string next_page_token = 2 [(google.api.field_behavior) = REQUIRED]; + // The size of page requested. + int32 page_size = 3 [(google.api.field_behavior) = REQUIRED]; // Total number of tasks available (before pagination). - int32 total_size = 3; + int32 total_size = 4 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:ListTasksResponse] // --8<-- [start:CancelTaskRequest] +// Represents a request for the `tasks/cancel` method. message CancelTaskRequest { // The resource name of the task to cancel. // Format: tasks/{task_id} @@ -773,6 +757,7 @@ message GetTaskPushNotificationConfigRequest { // --8<-- [end:GetTaskPushNotificationConfigRequest] // --8<-- [start:DeleteTaskPushNotificationConfigRequest] +// Represents a request for the `tasks/pushNotificationConfig/delete` method. message DeleteTaskPushNotificationConfigRequest { // The resource name of the config to delete. // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} @@ -781,7 +766,8 @@ message DeleteTaskPushNotificationConfigRequest { // --8<-- [end:DeleteTaskPushNotificationConfigRequest] // --8<-- [start:SetTaskPushNotificationConfigRequest] -message CreateTaskPushNotificationConfigRequest { +// Represents a request for the `tasks/pushNotificationConfig/set` method. +message SetTaskPushNotificationConfigRequest { // The parent task resource for this config. // Format: tasks/{task_id} string parent = 1 [(google.api.field_behavior) = REQUIRED]; @@ -793,60 +779,45 @@ message CreateTaskPushNotificationConfigRequest { } // --8<-- [end:SetTaskPushNotificationConfigRequest] -// --8<-- [start:TaskResubscriptionRequest] -message TaskSubscriptionRequest { +// --8<-- [start:SubscribeToTaskRequest] +message SubscribeToTaskRequest { // The resource name of the task to subscribe to. // Format: tasks/{task_id} string name = 1; } -// --8<-- [end:TaskResubscriptionRequest] +// --8<-- [end:SubscribeToTaskRequest] // --8<-- [start:ListTaskPushNotificationConfigRequest] message ListTaskPushNotificationConfigRequest { // The parent task resource. // Format: tasks/{task_id} string parent = 1; - // For AIP-158 these fields are present. Usually not used/needed. // The maximum number of configurations to return. - // If unspecified, all configs will be returned. int32 page_size = 2; - // A page token received from a previous - // ListTaskPushNotificationConfigRequest call. - // Provide this to retrieve the subsequent page. - // When paginating, all other parameters provided to - // `ListTaskPushNotificationConfigRequest` must match the call that provided - // the page token. + // A page token received from a previous ListTaskPushNotificationConfigRequest call. string page_token = 3; } // --8<-- [end:ListTaskPushNotificationConfigRequest] -// --8<-- [start:GetAuthenticatedExtendedCardRequest] -message GetAgentCardRequest { +// --8<-- [start:GetExtendedAgentCardRequest] +message GetExtendedAgentCardRequest { // Empty. Added to fix linter violation. } -// --8<-- [end:GetAuthenticatedExtendedCardRequest] +// --8<-- [end:GetExtendedAgentCardRequest] //////// Response Messages /////////// -// --8<-- [start:SendMessageSuccessResponse] +// --8<-- [start:SendMessageResponse] message SendMessageResponse { oneof payload { Task task = 1; Message msg = 2 [json_name = "message"]; } } -// --8<-- [end:SendMessageSuccessResponse] - -// --8<-- [start:SendStreamingMessageSuccessResponse] -// The stream response for a message. The stream should be one of the following -// sequences: -// If the response is a message, the stream should contain one, and only one, -// message and then close -// If the response is a task lifecycle, the first response should be a Task -// object followed by zero or more TaskStatusUpdateEvents and -// TaskArtifactUpdateEvents. The stream should complete when the Task -// if in an interrupted or terminal state. A stream that ends before these -// conditions are met are +// --8<-- [end:SendMessageResponse] + +// --8<-- [start:StreamResponse] +// Response message containing task updates or messages. message StreamResponse { oneof payload { Task task = 1; @@ -855,9 +826,11 @@ message StreamResponse { TaskArtifactUpdateEvent artifact_update = 4; } } -// --8<-- [end:SendStreamingMessageSuccessResponse] +// --8<-- [end:StreamResponse] -// --8<-- [start:ListTaskPushNotificationConfigSuccessResponse] +// --8<-- [start:ListTaskPushNotificationConfigResponse] +// Represents a successful response for the `tasks/pushNotificationConfig/list` +// method. message ListTaskPushNotificationConfigResponse { // The list of push notification configurations. repeated TaskPushNotificationConfig configs = 1; @@ -865,4 +838,4 @@ message ListTaskPushNotificationConfigResponse { // If this field is omitted, there are no subsequent pages. string next_page_token = 2; } -// --8<-- [end:ListTaskPushNotificationConfigSuccessResponse] +// --8<-- [end:ListTaskPushNotificationConfigResponse] diff --git a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java new file mode 100644 index 000000000..e1023ceae --- /dev/null +++ b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java @@ -0,0 +1,274 @@ +package io.a2a.grpc.mapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.a2a.spec.Artifact; +import io.a2a.spec.Message; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +public class StreamResponseMapperTest { + + @Test + void testConvertTask_ToProto() { + // Arrange + Task task = new Task.Builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + + // Act + io.a2a.grpc.StreamResponse result = StreamResponseMapper.INSTANCE.toProto(task); + + // Assert + assertNotNull(result); + assertEquals(io.a2a.grpc.StreamResponse.PayloadCase.TASK, result.getPayloadCase()); + assertEquals("task-123", result.getTask().getId()); + assertEquals("context-456", result.getTask().getContextId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_COMPLETED, result.getTask().getStatus().getState()); + } + + @Test + void testConvertTask_FromProto() { + // Arrange + io.a2a.grpc.StreamResponse proto = io.a2a.grpc.StreamResponse.newBuilder() + .setTask(io.a2a.grpc.Task.newBuilder() + .setId("task-123") + .setContextId("context-456") + .setStatus(io.a2a.grpc.TaskStatus.newBuilder() + .setState(io.a2a.grpc.TaskState.TASK_STATE_COMPLETED) + .build()) + .build()) + .build(); + + // Act + StreamingEventKind result = StreamResponseMapper.INSTANCE.fromProto(proto); + + // Assert + assertNotNull(result); + assertInstanceOf(Task.class, result); + Task task = (Task) result; + assertEquals("task-123", task.getId()); + assertEquals("context-456", task.getContextId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + } + + @Test + void testConvertMessage_ToProto() { + // Arrange + Message message = new Message.Builder() + .messageId("msg-123") + .contextId("context-456") + .role(Message.Role.USER) + .parts(Collections.singletonList(new TextPart("Hello"))) + .build(); + + // Act + io.a2a.grpc.StreamResponse result = StreamResponseMapper.INSTANCE.toProto(message); + + // Assert + assertNotNull(result); + assertEquals(io.a2a.grpc.StreamResponse.PayloadCase.MSG, result.getPayloadCase()); + assertEquals("msg-123", result.getMsg().getMessageId()); + assertEquals("context-456", result.getMsg().getContextId()); + assertEquals(io.a2a.grpc.Role.ROLE_USER, result.getMsg().getRole()); + } + + @Test + void testConvertMessage_FromProto() { + // Arrange + io.a2a.grpc.StreamResponse proto = io.a2a.grpc.StreamResponse.newBuilder() + .setMsg(io.a2a.grpc.Message.newBuilder() + .setMessageId("msg-123") + .setContextId("context-456") + .setRole(io.a2a.grpc.Role.ROLE_USER) + .addParts(io.a2a.grpc.Part.newBuilder() + .setText("Hello") + .build()) + .build()) + .build(); + + // Act + StreamingEventKind result = StreamResponseMapper.INSTANCE.fromProto(proto); + + // Assert + assertNotNull(result); + assertInstanceOf(Message.class, result); + Message message = (Message) result; + assertEquals("msg-123", message.getMessageId()); + assertEquals("context-456", message.getContextId()); + assertEquals(Message.Role.USER, message.getRole()); + } + + @Test + void testConvertTaskStatusUpdateEvent_ToProto() { + // Arrange + TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + .taskId("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .build(); + + // Act + io.a2a.grpc.StreamResponse result = StreamResponseMapper.INSTANCE.toProto(event); + + // Assert + assertNotNull(result); + assertEquals(io.a2a.grpc.StreamResponse.PayloadCase.STATUS_UPDATE, result.getPayloadCase()); + assertEquals("task-123", result.getStatusUpdate().getTaskId()); + assertEquals("context-456", result.getStatusUpdate().getContextId()); + assertEquals(io.a2a.grpc.TaskState.TASK_STATE_WORKING, result.getStatusUpdate().getStatus().getState()); + assertEquals(false, result.getStatusUpdate().getFinal()); + } + + @Test + void testConvertTaskStatusUpdateEvent_FromProto() { + // Arrange + io.a2a.grpc.StreamResponse proto = io.a2a.grpc.StreamResponse.newBuilder() + .setStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent.newBuilder() + .setTaskId("task-123") + .setContextId("context-456") + .setStatus(io.a2a.grpc.TaskStatus.newBuilder() + .setState(io.a2a.grpc.TaskState.TASK_STATE_WORKING) + .build()) + .setFinal(false) + .build()) + .build(); + + // Act + StreamingEventKind result = StreamResponseMapper.INSTANCE.fromProto(proto); + + // Assert + assertNotNull(result); + assertInstanceOf(TaskStatusUpdateEvent.class, result); + TaskStatusUpdateEvent event = (TaskStatusUpdateEvent) result; + assertEquals("task-123", event.getTaskId()); + assertEquals("context-456", event.getContextId()); + assertEquals(TaskState.WORKING, event.getStatus().state()); + assertEquals(false, event.isFinal()); + } + + @Test + void testConvertTaskArtifactUpdateEvent_ToProto() { + // Arrange + TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + .taskId("task-123") + .contextId("context-456") + .artifact(new Artifact.Builder() + .artifactId("artifact-1") + .name("result") + .parts(new TextPart("Result text")) + .build()) + .build(); + + // Act + io.a2a.grpc.StreamResponse result = StreamResponseMapper.INSTANCE.toProto(event); + + // Assert + assertNotNull(result); + assertEquals(io.a2a.grpc.StreamResponse.PayloadCase.ARTIFACT_UPDATE, result.getPayloadCase()); + assertEquals("task-123", result.getArtifactUpdate().getTaskId()); + assertEquals("context-456", result.getArtifactUpdate().getContextId()); + assertEquals("artifact-1", result.getArtifactUpdate().getArtifact().getArtifactId()); + assertEquals("result", result.getArtifactUpdate().getArtifact().getName()); + } + + @Test + void testConvertTaskArtifactUpdateEvent_FromProto() { + // Arrange + io.a2a.grpc.StreamResponse proto = io.a2a.grpc.StreamResponse.newBuilder() + .setArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent.newBuilder() + .setTaskId("task-123") + .setContextId("context-456") + .setArtifact(io.a2a.grpc.Artifact.newBuilder() + .setArtifactId("artifact-1") + .setName("result") + .addParts(io.a2a.grpc.Part.newBuilder() + .setText("Result text") + .build()) + .build()) + .build()) + .build(); + + // Act + StreamingEventKind result = StreamResponseMapper.INSTANCE.fromProto(proto); + + // Assert + assertNotNull(result); + assertInstanceOf(TaskArtifactUpdateEvent.class, result); + TaskArtifactUpdateEvent event = (TaskArtifactUpdateEvent) result; + assertEquals("task-123", event.getTaskId()); + assertEquals("context-456", event.getContextId()); + assertEquals("artifact-1", event.getArtifact().artifactId()); + assertEquals("result", event.getArtifact().name()); + } + + @Test + void testConvertStreamResponse_FromProto_PayloadNotSet_ThrowsException() { + // Arrange + io.a2a.grpc.StreamResponse proto = io.a2a.grpc.StreamResponse.newBuilder().build(); + + // Act & Assert + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { + StreamResponseMapper.INSTANCE.fromProto(proto); + }); + assertEquals("StreamResponse payload oneof field not set", exception.getMessage()); + } + + @Test + void testConvertStreamResponse_Roundtrip_Task() { + // Arrange + Task originalTask = new Task.Builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + // Act + io.a2a.grpc.StreamResponse proto = StreamResponseMapper.INSTANCE.toProto(originalTask); + StreamingEventKind result = StreamResponseMapper.INSTANCE.fromProto(proto); + + // Assert + assertNotNull(result); + assertInstanceOf(Task.class, result); + Task roundtrippedTask = (Task) result; + assertEquals(originalTask.getId(), roundtrippedTask.getId()); + assertEquals(originalTask.getContextId(), roundtrippedTask.getContextId()); + assertEquals(originalTask.getStatus().state(), roundtrippedTask.getStatus().state()); + } + + @Test + void testConvertStreamResponse_Roundtrip_Message() { + // Arrange + Message originalMessage = new Message.Builder() + .messageId("msg-123") + .contextId("context-456") + .role(Message.Role.AGENT) + .parts(Collections.singletonList(new TextPart("Response"))) + .build(); + + // Act + io.a2a.grpc.StreamResponse proto = StreamResponseMapper.INSTANCE.toProto(originalMessage); + StreamingEventKind result = StreamResponseMapper.INSTANCE.fromProto(proto); + + // Assert + assertNotNull(result); + assertInstanceOf(Message.class, result); + Message roundtrippedMessage = (Message) result; + assertEquals(originalMessage.getMessageId(), roundtrippedMessage.getMessageId()); + assertEquals(originalMessage.getContextId(), roundtrippedMessage.getContextId()); + assertEquals(originalMessage.getRole(), roundtrippedMessage.getRole()); + } +} diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java new file mode 100644 index 000000000..25eada184 --- /dev/null +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -0,0 +1,247 @@ +package io.a2a.grpc.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.gson.JsonSyntaxException; +import io.a2a.spec.GetTaskPushNotificationConfigRequest; +import io.a2a.spec.GetTaskPushNotificationConfigResponse; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidParamsJsonMappingException; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCRequest; +import io.a2a.spec.SetTaskPushNotificationConfigRequest; +import io.a2a.spec.SetTaskPushNotificationConfigResponse; +import io.a2a.spec.TaskPushNotificationConfig; +import org.junit.jupiter.api.Test; + +public class JSONRPCUtilsTest { + + @Test + public void testParseSetTaskPushNotificationConfigRequest_ValidProtoFormat() throws JsonProcessingException { + String validRequest = """ + { + "jsonrpc": "2.0", + "method": "SetTaskPushNotificationConfig", + "id": "1", + "params": { + "parent": "tasks/task-123", + "configId": "config-456", + "config": { + "name": "tasks/task-123/pushNotificationConfigs/config-456", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "authentication": { + "schemes": ["jwt"] + } + } + } + } + } + """; + + JSONRPCRequest request = JSONRPCUtils.parseRequestBody(validRequest); + + assertNotNull(request); + assertInstanceOf(SetTaskPushNotificationConfigRequest.class, request); + SetTaskPushNotificationConfigRequest setRequest = (SetTaskPushNotificationConfigRequest) request; + assertEquals("2.0", setRequest.getJsonrpc()); + assertEquals(1, setRequest.getId()); + assertEquals("SetTaskPushNotificationConfig", setRequest.getMethod()); + + TaskPushNotificationConfig config = setRequest.getParams(); + assertNotNull(config); + assertEquals("task-123", config.taskId()); + assertNotNull(config.pushNotificationConfig()); + assertEquals("https://example.com/callback", config.pushNotificationConfig().url()); + } + + @Test + public void testParseGetTaskPushNotificationConfigRequest_ValidProtoFormat() throws JsonProcessingException { + String validRequest = """ + { + "jsonrpc": "2.0", + "method": "GetTaskPushNotificationConfig", + "id": "2", + "params": { + "name": "tasks/task-123/pushNotificationConfigs/config-456" + } + } + """; + + JSONRPCRequest request = JSONRPCUtils.parseRequestBody(validRequest); + + assertNotNull(request); + assertInstanceOf(GetTaskPushNotificationConfigRequest.class, request); + GetTaskPushNotificationConfigRequest getRequest = (GetTaskPushNotificationConfigRequest) request; + assertEquals("2.0", getRequest.getJsonrpc()); + assertEquals(2, getRequest.getId()); + assertEquals("GetTaskPushNotificationConfig", getRequest.getMethod()); + assertNotNull(getRequest.getParams()); + assertEquals("task-123", getRequest.getParams().id()); + } + + @Test + public void testParseMalformedJSON_ThrowsJsonSyntaxException() { + String malformedRequest = """ + { + "jsonrpc": "2.0", + "method": "SetTaskPushNotificationConfig", + "params": { + "parent": "tasks/task-123" + """; // Missing closing braces + + assertThrows(JsonSyntaxException.class, () -> { + JSONRPCUtils.parseRequestBody(malformedRequest); + }); + } + + @Test + public void testParseInvalidParams_ThrowsInvalidParamsError() { + String invalidParamsRequest = """ + { + "jsonrpc": "2.0", + "method": "SetTaskPushNotificationConfig", + "id": "3", + "params": "not_a_dict" + } + """; + + InvalidParamsJsonMappingException exception = assertThrows( + InvalidParamsJsonMappingException.class, + () -> JSONRPCUtils.parseRequestBody(invalidParamsRequest) + ); + assertEquals(3, exception.getId()); + } + + @Test + public void testParseInvalidProtoStructure_ThrowsInvalidParamsError() { + String invalidStructure = """ + { + "jsonrpc": "2.0", + "method": "SetTaskPushNotificationConfig", + "id": "4", + "params": { + "invalid_field": "value" + } + } + """; + + InvalidParamsJsonMappingException exception = assertThrows( + InvalidParamsJsonMappingException.class, + () -> JSONRPCUtils.parseRequestBody(invalidStructure) + ); + assertEquals(4, exception.getId()); + } + + @Test + public void testGenerateSetTaskPushNotificationConfigResponse_Success() throws Exception { + TaskPushNotificationConfig config = new TaskPushNotificationConfig( + "task-123", + new io.a2a.spec.PushNotificationConfig.Builder() + .url("https://example.com/callback") + .id("config-456") + .build() + ); + + String responseJson = """ + { + "jsonrpc": "2.0", + "id": "1", + "result": { + "name": "tasks/task-123/pushNotificationConfigs/config-456", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "id": "config-456" + } + } + } + """; + + SetTaskPushNotificationConfigResponse response = + (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, SetTaskPushNotificationConfigRequest.METHOD); + + assertNotNull(response); + assertEquals(1, response.getId()); + assertNotNull(response.getResult()); + assertEquals("task-123", response.getResult().taskId()); + assertEquals("https://example.com/callback", response.getResult().pushNotificationConfig().url()); + } + + @Test + public void testGenerateGetTaskPushNotificationConfigResponse_Success() throws Exception { + String responseJson = """ + { + "jsonrpc": "2.0", + "id": "2", + "result": { + "name": "tasks/task-123/pushNotificationConfigs/config-456", + "pushNotificationConfig": { + "url": "https://example.com/callback", + "id": "config-456" + } + } + } + """; + + GetTaskPushNotificationConfigResponse response = + (GetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, GetTaskPushNotificationConfigRequest.METHOD); + + assertNotNull(response); + assertEquals(2, response.getId()); + assertNotNull(response.getResult()); + assertEquals("task-123", response.getResult().taskId()); + assertEquals("https://example.com/callback", response.getResult().pushNotificationConfig().url()); + } + + @Test + public void testParseErrorResponse_InvalidParams() throws Exception { + String errorResponse = """ + { + "jsonrpc": "2.0", + "id": "5", + "error": { + "code": -32602, + "message": "Invalid params" + } + } + """; + + SetTaskPushNotificationConfigResponse response = + (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SetTaskPushNotificationConfigRequest.METHOD); + + assertNotNull(response); + assertEquals(5, response.getId()); + assertNotNull(response.getError()); + assertInstanceOf(InvalidParamsError.class, response.getError()); + assertEquals(-32602, response.getError().getCode()); + assertEquals("Invalid params", response.getError().getMessage()); + } + + @Test + public void testParseErrorResponse_ParseError() throws Exception { + String errorResponse = """ + { + "jsonrpc": "2.0", + "id": 6, + "error": { + "code": -32700, + "message": "Parse error" + } + } + """; + + SetTaskPushNotificationConfigResponse response = + (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SetTaskPushNotificationConfigRequest.METHOD); + + assertNotNull(response); + assertEquals(6, response.getId()); + assertNotNull(response.getError()); + assertInstanceOf(JSONParseError.class, response.getError()); + assertEquals(-32700, response.getError().getCode()); + assertEquals("Parse error", response.getError().getMessage()); + } +} diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index ef3ace765..36b4ddea1 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -8,12 +8,15 @@ import io.a2a.grpc.SendMessageConfiguration; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; -import io.a2a.spec.PushNotificationAuthenticationInfo; +import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -43,7 +46,7 @@ public void convertAgentCard() { AgentCard agentCard = new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") - .url("http://localhost:9999") + .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() @@ -60,23 +63,25 @@ public void convertAgentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.2.5") + .protocolVersion("1.0.0") .build(); io.a2a.grpc.AgentCard result = ProtoUtils.ToProto.agentCard(agentCard); assertEquals("Hello World Agent", result.getName()); assertEquals("Just a hello world agent", result.getDescription()); - assertEquals("http://localhost:9999", result.getUrl()); + assertEquals(1, result.getSupportedInterfacesList().size()); + assertEquals("http://localhost:9999", result.getSupportedInterfacesList().get(0).getUrl()); + assertEquals("jsonrpc", result.getSupportedInterfacesList().get(0).getProtocolBinding()); assertEquals("1.0.0", result.getVersion()); assertEquals("http://example.com/docs", result.getDocumentationUrl()); assertEquals(1, result.getDefaultInputModesCount()); assertEquals("text", result.getDefaultInputModes(0)); assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); - assertEquals("0.2.5", result.getProtocolVersion()); + assertEquals("1.0.0", result.getProtocolVersion()); agentCard = new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") - .url("http://localhost:9999") + .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() @@ -93,24 +98,24 @@ public void convertAgentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .preferredTransport("HTTP+JSON") -// .iconUrl("http://example.com/icon.svg") + // .iconUrl("http://example.com/icon.svg") .securitySchemes(Map.of("basic", new HTTPAuthSecurityScheme.Builder().scheme("basic").description("Basic Auth").build())) .security(List.of(Map.of("oauth", List.of("read")))) - .protocolVersion("0.2.5") + .protocolVersion("1.0.0") .build(); - result = ProtoUtils.ToProto.agentCard(agentCard); + result = ProtoUtils.ToProto.agentCard(agentCard); assertEquals("Hello World Agent", result.getName()); assertEquals("Just a hello world agent", result.getDescription()); - assertEquals("http://localhost:9999", result.getUrl()); + assertEquals(1, result.getSupportedInterfacesList().size()); + assertEquals("http://localhost:9999", result.getSupportedInterfacesList().get(0).getUrl()); + assertEquals("jsonrpc", result.getSupportedInterfacesList().get(0).getProtocolBinding()); assertEquals("1.0.0", result.getVersion()); assertEquals("http://example.com/docs", result.getDocumentationUrl()); assertEquals(1, result.getDefaultInputModesCount()); assertEquals("text", result.getDefaultInputModes(0)); assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); - assertEquals("0.2.5", result.getProtocolVersion()); - assertEquals("HTTP+JSON", result.getPreferredTransport()); + assertEquals("1.0.0", result.getProtocolVersion()); assertEquals(1, result.getSecurityCount()); assertEquals(1, result.getSecurity(0).getSchemesMap().size()); assertEquals(true, result.getSecurity(0).getSchemesMap().containsKey("oauth")); @@ -160,7 +165,7 @@ public void convertTask() { assertEquals(false, result.getArtifacts(0).getParts(0).hasData()); assertEquals("text", result.getArtifacts(0).getParts(0).getText()); assertEquals(1, result.getHistoryCount()); - assertEquals("context-1234", result.getHistory(0).getContextId()); + assertEquals("context-1234", result.getHistory(0).getContextId()); assertEquals("message-1234", result.getHistory(0).getMessageId()); assertEquals(ROLE_USER, result.getHistory(0).getRole()); assertEquals(1, result.getHistory(0).getPartsCount()); @@ -211,7 +216,7 @@ public void convertTaskPushNotificationConfig() { = new TaskPushNotificationConfig("push-task-123", new PushNotificationConfig.Builder() .token("AAAAAA") - .authenticationInfo(new PushNotificationAuthenticationInfo(Collections.singletonList("jwt"), "credentials")) + .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), "credentials")) .url("http://example.com") .id("xyz") .build()); @@ -289,4 +294,38 @@ public void convertTaskTimestampStatus() { assertNotNull(status.timestamp()); assertEquals(expectedTimestamp, status.timestamp()); } + + @Test + public void convertDeleteTaskPushNotificationConfigRequest() { + DeleteTaskPushNotificationConfigParams params = new DeleteTaskPushNotificationConfigParams( + "task-123", + "config-456" + ); + + io.a2a.grpc.DeleteTaskPushNotificationConfigRequest result = + ProtoUtils.ToProto.deleteTaskPushNotificationConfigRequest(params); + + assertEquals("tasks/task-123/pushNotificationConfigs/config-456", result.getName()); + + // Test round-trip conversion + DeleteTaskPushNotificationConfigParams convertedBack = + ProtoUtils.FromProto.deleteTaskPushNotificationConfigParams(result); + assertEquals("task-123", convertedBack.id()); + assertEquals("config-456", convertedBack.pushNotificationConfigId()); + } + + @Test + public void convertListTaskPushNotificationConfigRequest() { + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams("task-789"); + + io.a2a.grpc.ListTaskPushNotificationConfigRequest result = + ProtoUtils.ToProto.listTaskPushNotificationConfigRequest(params); + + assertEquals("tasks/task-789", result.getParent()); + + // Test round-trip conversion + ListTaskPushNotificationConfigParams convertedBack = + ProtoUtils.FromProto.listTaskPushNotificationConfigParams(result); + assertEquals("task-789", convertedBack.id()); + } } diff --git a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java new file mode 100644 index 000000000..13801bbfb --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java @@ -0,0 +1,26 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ +package io.a2a.spec; + +/** + * All the error codes for A2A errors. + */ +public interface A2AErrorCodes { + + int TASK_NOT_FOUND_ERROR_CODE = -32001; + int TASK_NOT_CANCELABLE_ERROR_CODE = -32002; + int PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE = -32003; + int UNSUPPORTED_OPERATION_ERROR_CODE = -32004; + int CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE = -32005; + int INVALID_AGENT_RESPONSE_ERROR_CODE = -32006; + int AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE = -32007; + + int INVALID_REQUEST_ERROR_CODE = -32600; + int METHOD_NOT_FOUND_ERROR_CODE = -32601; + int INVALID_PARAMS_ERROR_CODE = -32602; + int INTERNAL_ERROR_CODE = -32603; + + int JSON_PARSE_ERROR_CODE = -32700; +} diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index bf814bd40..8819daf26 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -29,7 +29,7 @@ public final class APIKeySecurityScheme implements SecurityScheme { public static final String API_KEY = "apiKey"; - private final String in; + private final Location location; private final String name; private final String type; private final String description; @@ -70,20 +70,20 @@ public static Location fromString(String location) { } } - public APIKeySecurityScheme(String in, String name, String description) { - this(in, name, description, API_KEY); + public APIKeySecurityScheme(Location location, String name, String description) { + this(location, name, description, API_KEY); } @JsonCreator - public APIKeySecurityScheme(@JsonProperty("in") String in, @JsonProperty("name") String name, + public APIKeySecurityScheme(@JsonProperty("location") Location location, @JsonProperty("name") String name, @JsonProperty("description") String description, @JsonProperty("type") String type) { - Assert.checkNotNullParam("in", in); + Assert.checkNotNullParam("location", location); Assert.checkNotNullParam("name", name); Assert.checkNotNullParam("type", type); if (! type.equals(API_KEY)) { throw new IllegalArgumentException("Invalid type for APIKeySecurityScheme"); } - this.in = in; + this.location = location; this.name = name; this.description = description; this.type = type; @@ -94,9 +94,8 @@ public String getDescription() { return description; } - - public String getIn() { - return in; + public Location getLocation() { + return location; } public String getName() { @@ -108,12 +107,12 @@ public String getType() { } public static class Builder { - private String in; + private Location location; private String name; private String description; - public Builder in(String in) { - this.in = in; + public Builder location(Location location) { + this.location = location; return this; } @@ -128,7 +127,7 @@ public Builder description(String description) { } public APIKeySecurityScheme build() { - return new APIKeySecurityScheme(in, name, description); + return new APIKeySecurityScheme(location, name, description); } } } diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index 80df1df7f..cd936abd1 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -23,7 +23,6 @@ * * @param name the human-readable name of the agent (required) * @param description a brief description of the agent's purpose and functionality (required) - * @param url the base URL where the agent can be accessed (required) * @param provider information about the organization or entity providing the agent (optional) * @param version the version of the agent implementation (required) * @param documentationUrl URL to human-readable documentation for the agent (optional) @@ -35,8 +34,7 @@ * @param securitySchemes map of security scheme names to their definitions (optional) * @param security list of security requirements for accessing the agent (optional) * @param iconUrl URL to an icon representing the agent (optional) - * @param additionalInterfaces list of additional transport interfaces beyond the primary url/transport (optional) - * @param preferredTransport the preferred transport protocol, e.g., "jsonrpc", "grpc" (defaults to "jsonrpc") + * @param supportedInterfaces ordered list of supported interfaces. First entry is preferred. (required) * @param protocolVersion the version of the A2A Protocol this agent implements (defaults to {@link #DEFAULT_PROTOCOL_VERSION}) * @param signatures digital signatures verifying the authenticity of the agent card (optional) * @see A2A Protocol Specification @@ -46,7 +44,6 @@ public record AgentCard( String name, String description, - String url, AgentProvider provider, String version, String documentationUrl, @@ -58,15 +55,12 @@ public record AgentCard( Map securitySchemes, List>> security, String iconUrl, - List additionalInterfaces, - String preferredTransport, + List supportedInterfaces, String protocolVersion, List signatures) { /** The default A2A Protocol version used when not explicitly specified. */ - public static final String DEFAULT_PROTOCOL_VERSION = "0.3.0"; - - private static final TransportProtocol DEFAULT_TRANSPORT = TransportProtocol.JSONRPC; + public static final String DEFAULT_PROTOCOL_VERSION = "1.0.0"; /** * Compact constructor that validates required fields and sets defaults. @@ -80,14 +74,23 @@ public record AgentCard( Assert.checkNotNullParam("description", description); Assert.checkNotNullParam("name", name); Assert.checkNotNullParam("skills", skills); - Assert.checkNotNullParam("url", url); + Assert.checkNotNullParam("supportedInterfaces", supportedInterfaces); Assert.checkNotNullParam("version", version); + if (protocolVersion == null) { protocolVersion = DEFAULT_PROTOCOL_VERSION; } - if (preferredTransport == null) { - preferredTransport = DEFAULT_TRANSPORT.asString(); - } + } + + /** + * Returns the list of additional interfaces. + * + * @return the list of supported interfaces + * @deprecated Use {@link #supportedInterfaces()} instead. This field has been renamed to 'supportedInterfaces'. + */ + @Deprecated(since = "0.4.0", forRemoval = true) + public List additionalInterfaces() { + return supportedInterfaces; } /** @@ -103,7 +106,8 @@ public record AgentCard( * AgentCard card = new AgentCard.Builder() * .name("Weather Agent") * .description("Provides weather information") - * .url("http://localhost:9999") + * .supportedInterfaces(List.of( + * new AgentInterface("JSONRPC", "http://localhost:9999"))) * .version("1.0.0") * .capabilities(new AgentCapabilities.Builder() * .streaming(true) @@ -122,7 +126,6 @@ public record AgentCard( public static class Builder { private String name; private String description; - private String url; private AgentProvider provider; private String version; private String documentationUrl; @@ -134,8 +137,7 @@ public static class Builder { private Map securitySchemes; private List>> security; private String iconUrl; - private List additionalInterfaces; - private String preferredTransport; + private List supportedInterfaces; private String protocolVersion; private List signatures; @@ -157,7 +159,6 @@ public Builder() { public Builder(AgentCard card) { this.name = card.name; this.description = card.description; - this.url = card.url; this.provider = card.provider; this.version = card.version; this.documentationUrl = card.documentationUrl; @@ -169,8 +170,7 @@ public Builder(AgentCard card) { this.securitySchemes = card.securitySchemes != null ? Map.copyOf(card.securitySchemes) : null; this.security = card.security != null ? new ArrayList<>(card.security) : null; this.iconUrl = card.iconUrl; - this.additionalInterfaces = card.additionalInterfaces != null ? new ArrayList<>(card.additionalInterfaces) : null; - this.preferredTransport = card.preferredTransport; + this.supportedInterfaces = card.supportedInterfaces != null ? new ArrayList<>(card.supportedInterfaces) : null; this.protocolVersion = card.protocolVersion; this.signatures = card.signatures != null ? new ArrayList<>(card.signatures) : null; } @@ -197,16 +197,6 @@ public Builder description(String description) { return this; } - /** - * Sets the base URL where the agent can be accessed. - * - * @param url the agent URL (required) - * @return this builder for method chaining - */ - public Builder url(String url) { - this.url = url; - return this; - } /** * Sets information about the organization or entity providing the agent. @@ -353,34 +343,37 @@ public Builder iconUrl(String iconUrl) { } /** - * Sets the list of additional transport interfaces. + * Sets the ordered list of supported interfaces (first entry is preferred). *

- * Additional interfaces allow the agent to be accessed through multiple - * transport protocols beyond the primary url/preferredTransport combination. + * Each interface defines a combination of protocol binding and URL for accessing the agent. * - * @param additionalInterfaces the list of additional interfaces (optional) + * @param supportedInterfaces the ordered list of supported interfaces (optional) * @return this builder for method chaining * @see AgentInterface */ - public Builder additionalInterfaces(List additionalInterfaces) { - this.additionalInterfaces = additionalInterfaces; + public Builder supportedInterfaces(List supportedInterfaces) { + this.supportedInterfaces = supportedInterfaces; return this; } /** - * Sets the preferred transport protocol. + * Sets the list of additional transport interfaces. *

- * Valid values include "jsonrpc", "grpc", and "rest". If not set, defaults to "jsonrpc". + * Additional interfaces allow the agent to be accessed through multiple + * transport protocols beyond the primary url/preferredTransport combination. * - * @param preferredTransport the preferred transport protocol name + * @param additionalInterfaces the list of additional interfaces (optional) * @return this builder for method chaining - * @see TransportProtocol + * @see AgentInterface + * @deprecated Use {@link #supportedInterfaces(List)} instead. This field has been renamed to 'supportedInterfaces'. */ - public Builder preferredTransport(String preferredTransport) { - this.preferredTransport = preferredTransport; + @Deprecated(since = "0.4.0", forRemoval = true) + public Builder additionalInterfaces(List additionalInterfaces) { + this.supportedInterfaces = additionalInterfaces; return this; } + /** * Sets the version of the A2A Protocol this agent implements. *

@@ -412,26 +405,16 @@ public Builder signatures(List signatures) { /** * Builds an immutable {@link AgentCard} from the current builder state. *

- * This method applies default values for optional fields and creates an entry - * in additionalInterfaces matching the primary url and preferredTransport if - * additionalInterfaces was not explicitly set. + * This method applies default values for optional fields. * * @return a new AgentCard instance * @throws IllegalArgumentException if any required field is null */ public AgentCard build() { - if (preferredTransport == null) { - preferredTransport = DEFAULT_TRANSPORT.asString(); - } - if (additionalInterfaces == null) { - // should include an entry matching the main 'url' and 'preferredTransport' - additionalInterfaces = new ArrayList<>(); - additionalInterfaces.add(new AgentInterface(preferredTransport, url)); - } - return new AgentCard(name, description, url, provider, version, documentationUrl, + return new AgentCard(name, description, provider, version, documentationUrl, capabilities, defaultInputModes, defaultOutputModes, skills, supportsAuthenticatedExtendedCard, securitySchemes, security, iconUrl, - additionalInterfaces, preferredTransport, protocolVersion, signatures); + supportedInterfaces, protocolVersion, signatures); } } } diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index dd19ed4b5..0d8aa52a1 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -1,14 +1,17 @@ package io.a2a.spec; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** - * Declares a combination of a target URL and transport protocol for accessing an agent. + * Declares a combination of a target URL and protocol binding for accessing an agent. *

* AgentInterface defines how clients can connect to and communicate with an agent using - * a specific transport mechanism (such as JSON-RPC, gRPC, or REST) at a particular endpoint. + * a specific protocol binding at a particular endpoint. The protocol binding is an open-form + * string that can be extended for other protocol bindings. Core officially supported bindings + * are JSONRPC, GRPC, and HTTP+JSON. *

* Agents may support multiple interfaces to allow flexibility in how clients communicate. * The {@link AgentCard} includes a primary interface (url + preferredTransport) and may @@ -16,18 +19,18 @@ *

* This class is immutable. * - * @param transport the transport protocol name (e.g., "jsonrpc", "grpc", "rest") (required) - * @param url the endpoint URL for this transport interface (required) + * @param protocolBinding the protocol binding supported at this URL (e.g., "JSONRPC", "GRPC", "HTTP+JSON") (required) + * @param url the endpoint URL where this interface is available; must be a valid absolute HTTPS URL in production (required) * @see AgentCard * @see TransportProtocol * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record AgentInterface(String transport, String url) { +public record AgentInterface(String protocolBinding, String url) { public AgentInterface { - Assert.checkNotNullParam("transport", transport); + Assert.checkNotNullParam("protocolBinding", protocolBinding); Assert.checkNotNullParam("url", url); } } diff --git a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java index a318bdd3d..1c4611cfa 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -36,15 +37,13 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class AuthenticatedExtendedCardNotConfiguredError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32007; - @JsonCreator public AuthenticatedExtendedCardNotConfiguredError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE), defaultIfNull(message, "Authenticated Extended Card not configured"), data); } diff --git a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java index a1cd3c293..d4cf992c6 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java @@ -7,18 +7,20 @@ import io.a2a.util.Assert; /** - * Authentication information for accessing an agent's authenticated endpoints. + * Authentication information for agent authentication and push notification endpoints. *

- * This record encapsulates the authentication schemes supported by an agent and - * optionally provides credentials for authentication. It is used when clients need - * to authenticate to access protected agent resources. - *

- * The {@code schemes} list identifies which security schemes from the agent's - * {@link AgentCard#securitySchemes()} should be used for authentication. + * This record encapsulates authentication schemes and credentials for two primary use cases: + *

    + *
  • Agent Authentication: Clients authenticate to access protected agent resources. + * The {@code schemes} list references security schemes from {@link AgentCard#securitySchemes()}.
  • + *
  • Push Notification Authentication: Agents authenticate when POSTing task updates to + * client-provided push notification endpoints. Supports HTTP Basic, Bearer tokens, API keys, OAuth.
  • + *
* - * @param schemes list of security scheme names that should be used for authentication (required) - * @param credentials optional credentials string for authentication (format depends on scheme) + * @param schemes list of security scheme names for authentication (required) + * @param credentials optional credentials string (format depends on scheme, e.g., base64-encoded for Basic auth) * @see AgentCard#securitySchemes() for available security schemes + * @see PushNotificationConfig for push notification configuration * @see SecurityScheme for security scheme definitions * @see A2A Protocol Specification */ diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java index 2e0db609a..d3017a660 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java @@ -32,7 +32,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class CancelTaskRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/cancel"; + public static final String METHOD = "CancelTask"; @JsonCreator public CancelTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index a73b5c038..f97cc5502 100644 --- a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; + /** * A2A Protocol error indicating incompatibility between requested content types and agent capabilities. *

@@ -44,15 +46,12 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class ContentTypeNotSupportedError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32005; - @JsonCreator public ContentTypeNotSupportedError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { - super( - defaultIfNull(code, DEFAULT_CODE), + super(defaultIfNull(code, CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE), defaultIfNull(message, "Incompatible content types"), data); } diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java index b261a36ef..4f65b0fd6 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -27,7 +27,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class DeleteTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/pushNotificationConfig/delete"; + public static final String METHOD = "DeleteTaskPushNotificationConfig"; @JsonCreator public DeleteTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java index 8730d89f0..9b9ba7724 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -38,7 +38,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "agent/getAuthenticatedExtendedCard"; + public static final String METHOD = "GetExtendedAgentCard"; @JsonCreator public GetAuthenticatedExtendedCardRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index bea3b1246..e1f66f6c0 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -26,7 +26,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/pushNotificationConfig/get"; + public static final String METHOD = "GetTaskPushNotificationConfig"; @JsonCreator public GetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java index 6c85bd6ea..567b42384 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java @@ -29,7 +29,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/get"; + public static final String METHOD = "GetTask"; @JsonCreator public GetTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @@ -57,7 +57,7 @@ public GetTaskRequest(Object id, TaskQueryParams params) { public static class Builder { private String jsonrpc; private Object id; - private String method = "tasks/get"; + private String method = METHOD; private TaskQueryParams params; public GetTaskRequest.Builder jsonrpc(String jsonrpc) { diff --git a/spec/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java index 3d1b7b5ca..6b1714b29 100644 --- a/spec/src/main/java/io/a2a/spec/InternalError.java +++ b/spec/src/main/java/io/a2a/spec/InternalError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -34,15 +35,13 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class InternalError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32603; - @JsonCreator public InternalError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, INTERNAL_ERROR_CODE), defaultIfNull(message, "Internal Error"), data); } diff --git a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index d3c23e657..60eede9c5 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -44,15 +45,13 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidAgentResponseError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32006; - @JsonCreator public InvalidAgentResponseError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, INVALID_AGENT_RESPONSE_ERROR_CODE), defaultIfNull(message, "Invalid agent response"), data); } diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java index 8a3b6b81f..735816eac 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -38,15 +39,13 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidParamsError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32602; - @JsonCreator public InvalidParamsError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, INVALID_PARAMS_ERROR_CODE), defaultIfNull(message, "Invalid parameters"), data); } diff --git a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java index 77c9bd6de..9535d0857 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -38,8 +39,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class InvalidRequestError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32600; - public InvalidRequestError() { this(null, null, null); } @@ -50,7 +49,7 @@ public InvalidRequestError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, INVALID_REQUEST_ERROR_CODE), defaultIfNull(message, "Request payload validation error"), data); } diff --git a/spec/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java index d7716a753..ad750ea0a 100644 --- a/spec/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -1,5 +1,7 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -34,8 +36,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class JSONParseError extends JSONRPCError implements A2AError { - public final static Integer DEFAULT_CODE = -32700; - public JSONParseError() { this(null, null, null); } @@ -50,7 +50,7 @@ public JSONParseError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, JSON_PARSE_ERROR_CODE), defaultIfNull(message, "Invalid JSON payload"), data); } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java index 229abf55d..90c85cf81 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java @@ -1,5 +1,6 @@ package io.a2a.spec; + import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -10,22 +11,34 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; + public class JSONRPCErrorDeserializer extends StdDeserializer { private static final Map> ERROR_MAP = new HashMap<>(); static { - ERROR_MAP.put(JSONParseError.DEFAULT_CODE, JSONParseError::new); - ERROR_MAP.put(InvalidRequestError.DEFAULT_CODE, InvalidRequestError::new); - ERROR_MAP.put(MethodNotFoundError.DEFAULT_CODE, MethodNotFoundError::new); - ERROR_MAP.put(InvalidParamsError.DEFAULT_CODE, InvalidParamsError::new); - ERROR_MAP.put(InternalError.DEFAULT_CODE, InternalError::new); - ERROR_MAP.put(PushNotificationNotSupportedError.DEFAULT_CODE, PushNotificationNotSupportedError::new); - ERROR_MAP.put(UnsupportedOperationError.DEFAULT_CODE, UnsupportedOperationError::new); - ERROR_MAP.put(ContentTypeNotSupportedError.DEFAULT_CODE, ContentTypeNotSupportedError::new); - ERROR_MAP.put(InvalidAgentResponseError.DEFAULT_CODE, InvalidAgentResponseError::new); - ERROR_MAP.put(TaskNotCancelableError.DEFAULT_CODE, TaskNotCancelableError::new); - ERROR_MAP.put(TaskNotFoundError.DEFAULT_CODE, TaskNotFoundError::new); + ERROR_MAP.put(JSON_PARSE_ERROR_CODE, JSONParseError::new); + ERROR_MAP.put(INVALID_REQUEST_ERROR_CODE, InvalidRequestError::new); + ERROR_MAP.put(METHOD_NOT_FOUND_ERROR_CODE, MethodNotFoundError::new); + ERROR_MAP.put(INVALID_PARAMS_ERROR_CODE, InvalidParamsError::new); + ERROR_MAP.put(INTERNAL_ERROR_CODE, InternalError::new); + ERROR_MAP.put(PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE, PushNotificationNotSupportedError::new); + ERROR_MAP.put(UNSUPPORTED_OPERATION_ERROR_CODE, UnsupportedOperationError::new); + ERROR_MAP.put(CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE, ContentTypeNotSupportedError::new); + ERROR_MAP.put(INVALID_AGENT_RESPONSE_ERROR_CODE, InvalidAgentResponseError::new); + ERROR_MAP.put(TASK_NOT_CANCELABLE_ERROR_CODE, TaskNotCancelableError::new); + ERROR_MAP.put(TASK_NOT_FOUND_ERROR_CODE, TaskNotFoundError::new); } public JSONRPCErrorDeserializer() { diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java index c3affe4ee..d9a9ba51b 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java @@ -79,7 +79,7 @@ protected static boolean isValidMethodName(String methodName) { || methodName.equals(ListTasksRequest.METHOD) || methodName.equals(GetTaskPushNotificationConfigRequest.METHOD) || methodName.equals(SetTaskPushNotificationConfigRequest.METHOD) - || methodName.equals(TaskResubscriptionRequest.METHOD) + || methodName.equals(SubscribeToTaskRequest.METHOD) || methodName.equals(SendMessageRequest.METHOD) || methodName.equals(SendStreamingMessageRequest.METHOD) || methodName.equals(ListTaskPushNotificationConfigRequest.METHOD) diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java index 03f58874c..beb50622c 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java @@ -27,7 +27,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class ListTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/pushNotificationConfig/list"; + public static final String METHOD = "ListTaskPushNotificationConfig"; @JsonCreator public ListTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java index f3b7fdf94..84a18104c 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java @@ -18,7 +18,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class ListTasksRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/list"; + public static final String METHOD = "ListTask"; @JsonCreator public ListTasksRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index 19843d26c..728ab7c1e 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -129,6 +129,15 @@ public String getKind() { return kind; } + /** + * Creates a new Builder for constructing Message instances. + * + * @return a new Message.Builder instance + */ + public static Builder builder() { + return new Builder(); + } + /** * Defines the role of the message sender in the conversation. *

diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java index 764b8beca..2207af712 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -30,20 +31,18 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class MethodNotFoundError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32601; - @JsonCreator public MethodNotFoundError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, METHOD_NOT_FOUND_ERROR_CODE), defaultIfNull(message, "Method not found"), data); } public MethodNotFoundError() { - this(DEFAULT_CODE, null, null); + this(METHOD_NOT_FOUND_ERROR_CODE, null, null); } } diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java deleted file mode 100644 index 86342374f..000000000 --- a/spec/src/main/java/io/a2a/spec/PushNotificationAuthenticationInfo.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.a2a.spec; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.a2a.util.Assert; - -/** - * Authentication details for push notification endpoints. - *

- * This record specifies the authentication schemes and credentials required for the agent - * to authenticate when POSTing task updates to the push notification endpoint. Supports - * various authentication methods including HTTP Basic, Bearer tokens, API keys, and OAuth. - *

- * The {@code schemes} field lists supported authentication types (e.g., "Basic", "Bearer"), - * while {@code credentials} contains the actual authentication data (e.g., base64-encoded - * credentials, tokens). - * - * @param schemes list of supported authentication scheme names (required) - * @param credentials the authentication credentials or tokens (optional) - * @see PushNotificationConfig for the full push notification configuration - * @see A2A Protocol Specification - */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) -public record PushNotificationAuthenticationInfo(List schemes, String credentials) { - - public PushNotificationAuthenticationInfo { - Assert.checkNotNullParam("schemes", schemes); - } -} diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index 100c75e76..ef471c087 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -16,21 +16,21 @@ * Authentication can be provided via either: *

    *
  • Simple bearer token in the {@code token} field
  • - *
  • More complex authentication via {@link PushNotificationAuthenticationInfo}
  • + *
  • More complex authentication via {@link AuthenticationInfo}
  • *
* * @param url the HTTP/HTTPS endpoint URL to receive push notifications (required) * @param token optional bearer token for simple authentication * @param authentication optional complex authentication configuration * @param id optional client-provided identifier for this configuration - * @see PushNotificationAuthenticationInfo for authentication details + * @see AuthenticationInfo for authentication details * @see TaskPushNotificationConfig for task-specific bindings * @see MessageSendConfiguration for configuring push notifications on message send * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public record PushNotificationConfig(String url, String token, PushNotificationAuthenticationInfo authentication, String id) { +public record PushNotificationConfig(String url, String token, AuthenticationInfo authentication, String id) { public static final TypeReference TYPE_REFERENCE = new TypeReference<>() {}; public PushNotificationConfig { @@ -46,7 +46,7 @@ public record PushNotificationConfig(String url, String token, PushNotificationA public static class Builder { private String url; private String token; - private PushNotificationAuthenticationInfo authentication; + private AuthenticationInfo authentication; private String id; /** Creates an empty builder. */ @@ -93,7 +93,7 @@ public Builder token(String token) { * @param authenticationInfo the authentication configuration * @return this builder */ - public Builder authenticationInfo(PushNotificationAuthenticationInfo authenticationInfo) { + public Builder authenticationInfo(AuthenticationInfo authenticationInfo) { this.authentication = authenticationInfo; return this; } diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index a2a676063..211fe8b59 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -36,8 +37,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class PushNotificationNotSupportedError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32003; - public PushNotificationNotSupportedError() { this(null, null, null); } @@ -48,7 +47,7 @@ public PushNotificationNotSupportedError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE), defaultIfNull(message, "Push Notification is not supported"), data); } diff --git a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java index dd4faefcb..06ce2d34c 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java @@ -40,15 +40,15 @@ public final class SendMessageRequest extends NonStreamingJSONRPCRequest { /** - * The JSON-RPC method name for sending a message: "message/send". + * The JSON-RPC method name for sending a message: "SendMessage". */ - public static final String METHOD = "message/send"; + public static final String METHOD = "SendMessage"; /** * Constructs a SendMessageRequest with the specified JSON-RPC fields. *

* This constructor is used by Jackson for JSON deserialization and validates - * that the method name is exactly "message/send". + * that the method name is exactly "SendMessage". * * @param jsonrpc the JSON-RPC version (must be "2.0") * @param id the request correlation identifier (String, Integer, or null) diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java index 34489af42..fdb259fe7 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java @@ -31,7 +31,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class SendStreamingMessageRequest extends StreamingJSONRPCRequest { - public static final String METHOD = "message/stream"; + public static final String METHOD = "SendStreamingMessage"; @JsonCreator public SendStreamingMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @@ -92,7 +92,7 @@ public Builder id(Object id) { /** * Sets the JSON-RPC method name. * - * @param method the method name (defaults to "message/stream") + * @param method the method name (defaults to "SendStreamingMessage") * @return this builder */ public Builder method(String method) { diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java index edc9ae6cb..636f17945 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java @@ -29,7 +29,7 @@ @JsonIgnoreProperties(ignoreUnknown = true) public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "tasks/pushNotificationConfig/set"; + public static final String METHOD = "SetTaskPushNotificationConfig"; @JsonCreator public SetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java index f0dd5f244..d9f89cf03 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java @@ -34,7 +34,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) @JsonDeserialize(using = StreamingJSONRPCRequestDeserializer.class) -public abstract sealed class StreamingJSONRPCRequest extends JSONRPCRequest permits TaskResubscriptionRequest, +public abstract sealed class StreamingJSONRPCRequest extends JSONRPCRequest permits SubscribeToTaskRequest, SendStreamingMessageRequest { } diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java index b66533620..48e0decf8 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java @@ -27,8 +27,8 @@ public StreamingJSONRPCRequest deserialize(JsonParser jsonParser, Deserializa JsonNode paramsNode = treeNode.get("params"); switch (method) { - case TaskResubscriptionRequest.METHOD -> { - return new TaskResubscriptionRequest(jsonrpc, id, method, + case SubscribeToTaskRequest.METHOD -> { + return new SubscribeToTaskRequest(jsonrpc, id, method, getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); } case SendStreamingMessageRequest.METHOD -> { diff --git a/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java similarity index 75% rename from spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java rename to spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java index b9e5589da..7978b3956 100644 --- a/spec/src/main/java/io/a2a/spec/TaskResubscriptionRequest.java +++ b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java @@ -24,7 +24,7 @@ *

  • Retrieving final results for completed tasks
  • * *

    - * This class implements the JSON-RPC {@code tasks/resubscribe} method as specified in the A2A Protocol. + * This class implements the JSON-RPC {@code SubscribeToTask} method as specified in the A2A Protocol. * * @see TaskIdParams for the parameter structure * @see StreamingEventKind for the types of events that can be streamed @@ -32,12 +32,12 @@ */ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonIgnoreProperties(ignoreUnknown = true) -public final class TaskResubscriptionRequest extends StreamingJSONRPCRequest { +public final class SubscribeToTaskRequest extends StreamingJSONRPCRequest { - public static final String METHOD = "tasks/resubscribe"; + public static final String METHOD = "SubscribeToTask"; @JsonCreator - public TaskResubscriptionRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, + public SubscribeToTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); @@ -53,7 +53,7 @@ public TaskResubscriptionRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonP this.params = params; } - public TaskResubscriptionRequest(Object id, TaskIdParams params) { + public SubscribeToTaskRequest(Object id, TaskIdParams params) { this(null, id, METHOD, params); } @@ -63,31 +63,31 @@ public static class Builder { private String method = METHOD; private TaskIdParams params; - public TaskResubscriptionRequest.Builder jsonrpc(String jsonrpc) { + public SubscribeToTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } - public TaskResubscriptionRequest.Builder id(Object id) { + public SubscribeToTaskRequest.Builder id(Object id) { this.id = id; return this; } - public TaskResubscriptionRequest.Builder method(String method) { + public SubscribeToTaskRequest.Builder method(String method) { this.method = method; return this; } - public TaskResubscriptionRequest.Builder params(TaskIdParams params) { + public SubscribeToTaskRequest.Builder params(TaskIdParams params) { this.params = params; return this; } - public TaskResubscriptionRequest build() { + public SubscribeToTaskRequest build() { if (id == null) { id = UUID.randomUUID().toString(); } - return new TaskResubscriptionRequest(jsonrpc, id, method, params); + return new SubscribeToTaskRequest(jsonrpc, id, method, params); } } } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index c2fd23d10..c3fbd09ce 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -113,6 +113,15 @@ public String getKind() { return kind; } + /** + * Creates a new Builder for constructing Task instances. + * + * @return a new Task.Builder instance + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing immutable {@link Task} instances. *

    diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 6cd073eb6..f215fd3a3 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -109,6 +109,15 @@ public String getKind() { return kind; } + /** + * Creates a new Builder for constructing TaskArtifactUpdateEvent instances. + * + * @return a new TaskArtifactUpdateEvent.Builder instance + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link TaskArtifactUpdateEvent} instances. *

    diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index c567959b1..31462d115 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -38,8 +39,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotCancelableError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32002; - public TaskNotCancelableError() { this(null, null, null); } @@ -50,7 +49,7 @@ public TaskNotCancelableError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, TASK_NOT_CANCELABLE_ERROR_CODE), defaultIfNull(message, "Task cannot be canceled"), data); } diff --git a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index 81c95ed05..2fdb94023 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -1,5 +1,7 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -41,8 +43,6 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotFoundError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32001; - public TaskNotFoundError() { this(null, null, null); } @@ -54,7 +54,7 @@ public TaskNotFoundError( @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, TASK_NOT_FOUND_ERROR_CODE), defaultIfNull(message, "Task not found"), data); } diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index 89b42d210..c44c2b847 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -28,5 +28,6 @@ public record TaskPushNotificationConfig(String taskId, PushNotificationConfig p public TaskPushNotificationConfig { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("pushNotificationConfig", pushNotificationConfig); + Assert.checkNotNullParam("configId", pushNotificationConfig.id()); } } diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 25e2cd170..a4f0c9644 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -79,6 +79,15 @@ public String getKind() { return kind; } + /** + * Creates a new Builder for constructing TaskStatusUpdateEvent instances. + * + * @return a new TaskStatusUpdateEvent.Builder instance + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String taskId; private TaskStatus status; diff --git a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java index 9e4fded80..0086135dc 100644 --- a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -1,5 +1,6 @@ package io.a2a.spec; +import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; import com.fasterxml.jackson.annotation.JsonCreator; @@ -39,15 +40,13 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class UnsupportedOperationError extends JSONRPCError { - public final static Integer DEFAULT_CODE = -32004; - @JsonCreator public UnsupportedOperationError( @JsonProperty("code") Integer code, @JsonProperty("message") String message, @JsonProperty("data") Object data) { super( - defaultIfNull(code, DEFAULT_CODE), + defaultIfNull(code, UNSUPPORTED_OPERATION_ERROR_CODE), defaultIfNull(message, "This operation is not supported"), data); } diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 59db4fb67..87230c6af 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -8,6 +8,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.a2a.spec.A2AClientException; +import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.Part; @@ -199,4 +201,17 @@ public static String toJsonString(Object o) { } } + /** + * Get the first defined URL in the supported interaces of the agent card. + * @param agentCard the agentcard where the interfaces are defined. + * @return the first defined URL in the supported interaces of the agent card. + * @throws A2AClientException + */ + public static String getFavoriteInterface(AgentCard agentCard) throws A2AClientException { + if(agentCard.supportedInterfaces() == null || agentCard.supportedInterfaces().isEmpty()) { + throw new A2AClientException("No server interface available in the AgentCard"); + } + return agentCard.supportedInterfaces().get(0).url(); + } + } diff --git a/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java b/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java index 24251a879..5fb704bcc 100644 --- a/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java +++ b/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java @@ -1,5 +1,6 @@ package io.a2a.spec; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; @@ -8,6 +9,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; public class JSONRPCErrorSerializationTest { @Test @@ -19,18 +31,17 @@ public void shouldDeserializeToCorrectJSONRPCErrorSubclass() { record ErrorCase(int code, Class clazz) {} - List cases = List.of( - new ErrorCase(JSONParseError.DEFAULT_CODE, JSONParseError.class), - new ErrorCase(InvalidRequestError.DEFAULT_CODE, InvalidRequestError.class), - new ErrorCase(MethodNotFoundError.DEFAULT_CODE, MethodNotFoundError.class), - new ErrorCase(InvalidParamsError.DEFAULT_CODE, InvalidParamsError.class), - new ErrorCase(InternalError.DEFAULT_CODE, InternalError.class), - new ErrorCase(PushNotificationNotSupportedError.DEFAULT_CODE, PushNotificationNotSupportedError.class), - new ErrorCase(UnsupportedOperationError.DEFAULT_CODE, UnsupportedOperationError.class), - new ErrorCase(ContentTypeNotSupportedError.DEFAULT_CODE, ContentTypeNotSupportedError.class), - new ErrorCase(InvalidAgentResponseError.DEFAULT_CODE, InvalidAgentResponseError.class), - new ErrorCase(TaskNotCancelableError.DEFAULT_CODE, TaskNotCancelableError.class), - new ErrorCase(TaskNotFoundError.DEFAULT_CODE, TaskNotFoundError.class), + List cases = List.of(new ErrorCase(JSON_PARSE_ERROR_CODE, JSONParseError.class), + new ErrorCase(INVALID_REQUEST_ERROR_CODE, InvalidRequestError.class), + new ErrorCase(METHOD_NOT_FOUND_ERROR_CODE, MethodNotFoundError.class), + new ErrorCase(INVALID_PARAMS_ERROR_CODE, InvalidParamsError.class), + new ErrorCase(INTERNAL_ERROR_CODE, InternalError.class), + new ErrorCase(PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE, PushNotificationNotSupportedError.class), + new ErrorCase(UNSUPPORTED_OPERATION_ERROR_CODE, UnsupportedOperationError.class), + new ErrorCase(CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE, ContentTypeNotSupportedError.class), + new ErrorCase(INVALID_AGENT_RESPONSE_ERROR_CODE, InvalidAgentResponseError.class), + new ErrorCase(TASK_NOT_CANCELABLE_ERROR_CODE, TaskNotCancelableError.class), + new ErrorCase(TASK_NOT_FOUND_ERROR_CODE, TaskNotFoundError.class), new ErrorCase(Integer.MAX_VALUE, JSONRPCError.class) // Any unknown code will be treated as JSONRPCError ); diff --git a/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java b/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java index a4571f923..63d5e32fe 100644 --- a/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java +++ b/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java @@ -88,7 +88,7 @@ private static Stream serializationTestCases() { ), Arguments.of( new APIKeySecurityScheme.Builder() - .in("test").name("name").description("description").build(), + .location(APIKeySecurityScheme.Location.HEADER).name("name").description("description").build(), "type", APIKeySecurityScheme.API_KEY ), Arguments.of( diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 2ab3d9a4e..cd35e5986 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -28,7 +28,10 @@ public AgentCard agentCard() { return new AgentCard.Builder() .name("Hello World Agent") .description("Just a hello world agent") - .url(sutJsonRpcUrl) + .supportedInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), sutJsonRpcUrl), + new AgentInterface(TransportProtocol.GRPC.asString(), sutGrpcUrl), + new AgentInterface(TransportProtocol.HTTP_JSON.asString(), sutRestcUrl))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() @@ -46,10 +49,6 @@ public AgentCard agentCard() { .examples(List.of("hi", "hello world")) .build())) .protocolVersion("0.3.0") - .additionalInterfaces(List.of( - new AgentInterface(TransportProtocol.JSONRPC.asString(), sutJsonRpcUrl), - new AgentInterface(TransportProtocol.GRPC.asString(), sutGrpcUrl), - new AgentInterface(TransportProtocol.HTTP_JSON.asString(), sutRestcUrl))) .build(); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 3a707876b..74ca331a6 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -39,6 +39,8 @@ import io.a2a.client.TaskEvent; import io.a2a.client.TaskUpdateEvent; import io.a2a.client.config.ClientConfig; +import io.a2a.grpc.utils.JSONRPCUtils; +import io.a2a.grpc.utils.ProtoUtils; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; @@ -78,7 +80,8 @@ import org.junit.jupiter.api.Timeout; /** - * This test requires doing some work on the server to add/get/delete tasks, and enqueue events. This is exposed via REST, + * This test requires doing some work on the server to add/get/delete tasks, and enqueue events. This is exposed via + * REST, * which delegates to {@link TestUtilsBean}. */ public abstract class AbstractA2AServerTest { @@ -449,7 +452,6 @@ public void testSendMessageNewMessageSuccess() throws Exception { .contextId(MINIMAL_TASK.getContextId()) .build(); - CountDownLatch latch = new CountDownLatch(1); AtomicReference receivedMessage = new AtomicReference<>(); AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); @@ -527,16 +529,17 @@ public void testSendMessageExistingTaskSuccess() throws Exception { public void testSetPushNotificationSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); + assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", config.pushNotificationConfig().id()); } catch (A2AClientException e) { fail("Unexpected exception during set push notification test: " + e.getMessage(), e); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "c295ea44-7543-4f78-b524-7a38915ad6e4"); deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @@ -545,9 +548,9 @@ public void testSetPushNotificationSuccess() throws Exception { public void testGetPushNotificationSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertNotNull(setResult); @@ -559,7 +562,7 @@ public void testGetPushNotificationSuccess() throws Exception { } catch (A2AClientException e) { fail("Unexpected exception during get push notification test: " + e.getMessage(), e); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "c295ea44-7543-4f78-b524-7a38915ad6e4"); deleteTaskInTaskStore(MINIMAL_TASK.getId()); } } @@ -588,7 +591,9 @@ public void testGetAgentCard() throws A2AClientException { assertNotNull(agentCard); assertEquals("test-card", agentCard.name()); assertEquals("A test agent card", agentCard.description()); - assertEquals(getTransportUrl(), agentCard.url()); + assertNotNull(agentCard.supportedInterfaces()); + assertFalse(agentCard.supportedInterfaces().isEmpty()); + assertEquals(getTransportUrl(), Utils.getFavoriteInterface(agentCard)); assertEquals("1.0", agentCard.version()); assertEquals("http://example.com/docs", agentCard.documentationUrl()); assertTrue(agentCard.capabilities().pushNotifications()); @@ -871,7 +876,7 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti // Verify child queue count increased (now ensureQueue + first + second) int childCountAfterSecond = getChildQueueCount(MINIMAL_TASK.getId()); assertTrue(childCountAfterSecond > childCountBeforeSecond, - "Child queue count should increase after second resubscription"); + "Child queue count should increase after second resubscription"); // 4. Enqueue second event - both consumers should receive it TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder() @@ -928,13 +933,13 @@ private boolean waitForChildQueueCountToBe(String taskId, int expectedCount, lon @Test public void testListPushNotificationConfigWithConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); - PushNotificationConfig notificationConfig1 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig1 + = new PushNotificationConfig.Builder() .url("http://example.com") .id("config1") .build(); - PushNotificationConfig notificationConfig2 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig2 + = new PushNotificationConfig.Builder() .url("http://example.com") .id("config2") .build(); @@ -959,12 +964,12 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { @Test public void testListPushNotificationConfigWithoutConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); - PushNotificationConfig notificationConfig1 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig1 + = new PushNotificationConfig.Builder() .url("http://1.example.com") .build(); - PushNotificationConfig notificationConfig2 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig2 + = new PushNotificationConfig.Builder() .url("http://2.example.com") .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); @@ -1024,13 +1029,13 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception .status(new TaskStatus(TaskState.SUBMITTED)) .build()); - PushNotificationConfig notificationConfig1 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig1 + = new PushNotificationConfig.Builder() .url("http://example.com") .id("config1") .build(); - PushNotificationConfig notificationConfig2 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig2 + = new PushNotificationConfig.Builder() .url("http://example.com") .id("config2") .build(); @@ -1066,13 +1071,13 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception @Test public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); - PushNotificationConfig notificationConfig1 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig1 + = new PushNotificationConfig.Builder() .url("http://example.com") .id("config1") .build(); - PushNotificationConfig notificationConfig2 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig2 + = new PushNotificationConfig.Builder() .url("http://example.com") .id("config2") .build(); @@ -1111,12 +1116,12 @@ public void testDeletePushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); - PushNotificationConfig notificationConfig1 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig1 + = new PushNotificationConfig.Builder() .url("http://1.example.com") .build(); - PushNotificationConfig notificationConfig2 = - new PushNotificationConfig.Builder() + PushNotificationConfig notificationConfig2 + = new PushNotificationConfig.Builder() .url("http://2.example.com") .build(); savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); @@ -1145,10 +1150,10 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio public void testNonBlockingWithMultipleMessages() throws Exception { // 1. Send first non-blocking message to create task in WORKING state Message message1 = new Message.Builder(MESSAGE) - .taskId("multi-event-test") - .contextId("test-context") - .parts(new TextPart("First request")) - .build(); + .taskId("multi-event-test") + .contextId("test-context") + .parts(new TextPart("First request")) + .build(); AtomicReference taskIdRef = new AtomicReference<>(); CountDownLatch firstTaskLatch = new CountDownLatch(1); @@ -1196,20 +1201,20 @@ public void testNonBlockingWithMultipleMessages() throws Exception { // Wait for subscription to be active CountDownLatch subscriptionLatch = new CountDownLatch(1); awaitStreamingSubscription() - .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); + .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); getClient().resubscribe(new TaskIdParams(taskId), - List.of(resubConsumer), - resubErrorHandler); + List.of(resubConsumer), + resubErrorHandler); assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); // 3. Send second streaming message to same taskId Message message2 = new Message.Builder(MESSAGE) - .taskId("multi-event-test") // Same taskId - .contextId("test-context") - .parts(new TextPart("Second request")) - .build(); + .taskId("multi-event-test") // Same taskId + .contextId("test-context") + .parts(new TextPart("Second request")) + .build(); CountDownLatch streamEventLatch = new CountDownLatch(2); // artifact-2 + completion List streamReceivedEvents = new CopyOnWriteArrayList<>(); @@ -1241,45 +1246,45 @@ public void testNonBlockingWithMultipleMessages() throws Exception { // Verify resubscription events long resubArtifactCount = resubReceivedEvents.stream() - .filter(e -> e instanceof TaskArtifactUpdateEvent) - .count(); + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .count(); assertEquals(1, resubArtifactCount); long resubCompletionCount = resubReceivedEvents.stream() - .filter(e -> e instanceof TaskStatusUpdateEvent) - .filter(e -> ((TaskStatusUpdateEvent) e).isFinal()) - .count(); + .filter(e -> e instanceof TaskStatusUpdateEvent) + .filter(e -> ((TaskStatusUpdateEvent) e).isFinal()) + .count(); assertEquals(1, resubCompletionCount); // Verify streaming events long streamArtifactCount = streamReceivedEvents.stream() - .filter(e -> e instanceof TaskArtifactUpdateEvent) - .count(); + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .count(); assertEquals(1, streamArtifactCount); long streamCompletionCount = streamReceivedEvents.stream() - .filter(e -> e instanceof TaskStatusUpdateEvent) - .filter(e -> ((TaskStatusUpdateEvent) e).isFinal()) - .count(); + .filter(e -> e instanceof TaskStatusUpdateEvent) + .filter(e -> ((TaskStatusUpdateEvent) e).isFinal()) + .count(); assertEquals(1, streamCompletionCount); // Verify artifact-2 details from resubscription TaskArtifactUpdateEvent resubArtifact = (TaskArtifactUpdateEvent) resubReceivedEvents.stream() - .filter(e -> e instanceof TaskArtifactUpdateEvent) - .findFirst() - .orElseThrow(); + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .findFirst() + .orElseThrow(); assertEquals("artifact-2", resubArtifact.getArtifact().artifactId()); assertEquals("Second message artifact", - ((TextPart) resubArtifact.getArtifact().parts().get(0)).getText()); + ((TextPart) resubArtifact.getArtifact().parts().get(0)).getText()); // Verify artifact-2 details from streaming TaskArtifactUpdateEvent streamArtifact = (TaskArtifactUpdateEvent) streamReceivedEvents.stream() - .filter(e -> e instanceof TaskArtifactUpdateEvent) - .findFirst() - .orElseThrow(); + .filter(e -> e instanceof TaskArtifactUpdateEvent) + .findFirst() + .orElseThrow(); assertEquals("artifact-2", streamArtifact.getArtifact().artifactId()); assertEquals("Second message artifact", - ((TextPart) streamArtifact.getArtifact().parts().get(0)).getText()); + ((TextPart) streamArtifact.getArtifact().parts().get(0)).getText()); } @Test @@ -1287,7 +1292,7 @@ public void testMalformedJSONRPCRequest() { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + // missing closing bracket String malformedRequest = "{\"jsonrpc\": \"2.0\", \"method\": \"message/send\", \"params\": {\"foo\": \"bar\"}"; JSONRPCErrorResponse response = given() @@ -1308,14 +1313,14 @@ public void testInvalidParamsJSONRPCRequest() { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + String invalidParamsRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": "not_a_dict", "id": "1"} + {"jsonrpc": "2.0", "method": "SendMessage", "params": "not_a_dict", "id": "1"} """; testInvalidParams(invalidParamsRequest); invalidParamsRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": {"message": {"parts": "invalid"}}, "id": "1"} + {"jsonrpc": "2.0", "method": "SendMessage", "params": {"message": {"parts": "invalid"}}, "id": "1"} """; testInvalidParams(invalidParamsRequest); } @@ -1332,7 +1337,7 @@ private void testInvalidParams(String invalidParamsRequest) { .as(JSONRPCErrorResponse.class); assertNotNull(response.getError()); assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); - assertEquals("1", response.getId()); + assertEquals("1", response.getId().toString()); } @Test @@ -1340,10 +1345,10 @@ public void testInvalidJSONRPCRequestMissingJsonrpc() { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + String invalidRequest = """ { - "method": "message/send", + "method": "SendMessage", "params": {} } """; @@ -1365,7 +1370,7 @@ public void testInvalidJSONRPCRequestMissingMethod() { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + String invalidRequest = """ {"jsonrpc": "2.0", "params": {}} """; @@ -1387,9 +1392,9 @@ public void testInvalidJSONRPCRequestInvalidId() { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + String invalidRequest = """ - {"jsonrpc": "2.0", "method": "message/send", "params": {}, "id": {"bad": "type"}} + {"jsonrpc": "2.0", "method": "SendMessage", "params": {}, "id": {"bad": "type"}} """; JSONRPCErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) @@ -1409,9 +1414,9 @@ public void testInvalidJSONRPCRequestNonExistentMethod() { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + String invalidRequest = """ - {"jsonrpc": "2.0", "method" : "nonexistent/method", "params": {}} + {"jsonrpc": "2.0", "id":"5", "method" : "nonexistent/method", "params": {}} """; JSONRPCErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) @@ -1439,7 +1444,7 @@ public void testStreamingMethodWithAcceptHeader() throws Exception { // skip this test for non-JSONRPC transports assumeTrue(TransportProtocol.JSONRPC.asString().equals(getTransportProtocol()), "JSONRPC-specific test"); - + testSendStreamingMessageWithHttpClient(MediaType.SERVER_SENT_EVENTS); } @@ -1475,7 +1480,8 @@ private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exc SendStreamingMessageResponse jsonResponse = extractJsonResponseFromSseLine(line); if (jsonResponse != null) { assertNull(jsonResponse.getError()); - Message messageResponse = (Message) jsonResponse.getResult(); + System.out.println("#################################### Response " + jsonResponse); + Message messageResponse = (Message) jsonResponse.getResult(); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); assertEquals(MESSAGE.getRole(), messageResponse.getRole()); Part part = messageResponse.getParts().get(0); @@ -1495,7 +1501,6 @@ private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exc return null; }); - boolean dataRead = latch.await(20, TimeUnit.SECONDS); Assertions.assertTrue(dataRead); Assertions.assertNull(errorRef.get()); @@ -1541,7 +1546,6 @@ public void testSendStreamingMessage(boolean createTask) throws Exception { assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(wasUnexpectedEvent.get()); assertNull(errorRef.get()); - Message messageResponse = receivedMessage.get(); assertNotNull(messageResponse); assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); @@ -1565,18 +1569,21 @@ private CompletableFuture>> initialiseStreamingReque HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .build(); + String body = ""; + if (request instanceof SendStreamingMessageRequest streamingRequest) { + body = JSONRPCUtils.toJsonRPCRequest((String) streamingRequest.getId(), SendStreamingMessageRequest.METHOD, ProtoUtils.ToProto.sendMessageRequest(streamingRequest.getParams())); + } // Create the request HttpRequest.Builder builder = HttpRequest.newBuilder() .uri(URI.create("http://localhost:" + serverPort + "/")) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(request))) + .POST(HttpRequest.BodyPublishers.ofString(body)) .header("Content-Type", APPLICATION_JSON); if (mediaType != null) { builder.header("Accept", mediaType); } HttpRequest httpRequest = builder.build(); - // Send request async and return the CompletableFuture return client.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()); } @@ -1584,14 +1591,14 @@ private CompletableFuture>> initialiseStreamingReque private SendStreamingMessageResponse extractJsonResponseFromSseLine(String line) throws JsonProcessingException { line = extractSseData(line); if (line != null) { - return Utils.OBJECT_MAPPER.readValue(line, SendStreamingMessageResponse.class); + return new SendStreamingMessageResponse("", ProtoUtils.FromProto.streamingEventKind(JSONRPCUtils.parseResponseEvent(line))); } return null; } private static String extractSseData(String line) { if (line.startsWith("data:")) { - line = line.substring(5).trim(); + line = line.substring(5).trim(); return line; } return null; @@ -1685,8 +1692,8 @@ protected void enqueueEventOnServer(Event event) throws Exception { } else if (event instanceof TaskStatusUpdateEvent e) { path = "test/queue/enqueueTaskStatusUpdateEvent/" + e.getTaskId(); } else { - throw new RuntimeException("Unknown event type " + event.getClass() + ". If you need the ability to" + - " handle more types, please add the REST endpoints."); + throw new RuntimeException("Unknown event type " + event.getClass() + ". If you need the ability to" + + " handle more types, please add the REST endpoints."); } HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) @@ -1846,10 +1853,8 @@ private AgentCard createTestAgentCard() { return new AgentCard.Builder() .name("test-card") .description("A test agent card") - .url(getTransportUrl()) .version("1.0") .documentationUrl("http://example.com/docs") - .preferredTransport(getTransportProtocol()) .capabilities(new AgentCapabilities.Builder() .streaming(true) .pushNotifications(true) @@ -1858,7 +1863,7 @@ private AgentCard createTestAgentCard() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .additionalInterfaces(List.of(new AgentInterface(getTransportProtocol(), getTransportUrl()))) + .supportedInterfaces(List.of(new AgentInterface(getTransportProtocol(), getTransportUrl()))) .protocolVersion("0.2.5") .build(); } @@ -1878,8 +1883,8 @@ private ClientConfig createClientConfig(boolean streaming) { private Client createPollingClient() throws A2AClientException { AgentCard agentCard = createTestAgentCard(); ClientConfig clientConfig = new ClientConfig.Builder() - .setStreaming(false) // Non-streaming - .setPolling(true) // Polling mode (translates to blocking=false on server) + .setStreaming(false) // Non-streaming + .setPolling(true) // Polling mode (translates to blocking=false on server) .build(); ClientBuilder clientBuilder = Client @@ -1909,10 +1914,10 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception { // Create task in WORKING state (non-final) Task workingTask = new Task.Builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); + .id(taskId) + .contextId(contextId) + .status(new TaskStatus(TaskState.WORKING)) + .build(); saveTaskInTaskStore(workingTask); try { @@ -1921,10 +1926,10 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception { // Send a message that will leave task in WORKING state (fire-and-forget pattern) Message message = new Message.Builder(MESSAGE) - .taskId(taskId) - .contextId(contextId) - .parts(new TextPart("fire and forget")) - .build(); + .taskId(taskId) + .contextId(contextId) + .parts(new TextPart("fire and forget")) + .build(); CountDownLatch firstEventLatch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); @@ -1946,17 +1951,17 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception { // Start streaming subscription CountDownLatch subscriptionLatch = new CountDownLatch(1); awaitStreamingSubscription() - .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); + .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); getClient().sendMessage(message, List.of(consumer), errorHandler); // Wait for subscription to be established assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS), - "Subscription should be established"); + "Subscription should be established"); // Wait for agent to respond (test agent sends Message, not WORKING status) assertTrue(firstEventLatch.await(15, TimeUnit.SECONDS), - "Should receive agent response"); + "Should receive agent response"); assertNull(errorRef.get()); // Give agent time to finish (task remains in WORKING state - non-final) @@ -1965,7 +1970,6 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception { // THE BIG IDEA TEST: Resubscribe to the task // Even though the agent finished and original ChildQueue closed, // MainQueue should still be open because task is in non-final WORKING state - CountDownLatch resubLatch = new CountDownLatch(1); AtomicReference resubErrorRef = new AtomicReference<>(); @@ -1984,19 +1988,19 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception { // This should succeed - MainQueue is still open for non-final task CountDownLatch resubSubscriptionLatch = new CountDownLatch(1); awaitStreamingSubscription() - .whenComplete((unused, throwable) -> resubSubscriptionLatch.countDown()); + .whenComplete((unused, throwable) -> resubSubscriptionLatch.countDown()); getClient().resubscribe(new TaskIdParams(taskId), - List.of(resubConsumer), - resubErrorHandler); + List.of(resubConsumer), + resubErrorHandler); // Wait for resubscription to be established assertTrue(resubSubscriptionLatch.await(15, TimeUnit.SECONDS), - "Resubscription should succeed - MainQueue stayed open for non-final task"); + "Resubscription should succeed - MainQueue stayed open for non-final task"); // Verify no errors during resubscription assertNull(resubErrorRef.get(), - "Resubscription should not error - validates THE BIG IDEA works end-to-end"); + "Resubscription should not error - validates THE BIG IDEA works end-to-end"); } finally { deleteTaskInTaskStore(taskId); @@ -2022,10 +2026,10 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { // Send a message that will create and complete the task Message message = new Message.Builder(MESSAGE) - .taskId(taskId) - .contextId(contextId) - .parts(new TextPart("complete task")) - .build(); + .taskId(taskId) + .contextId(contextId) + .parts(new TextPart("complete task")) + .build(); CountDownLatch completionLatch = new CountDownLatch(1); AtomicReference errorRef = new AtomicReference<>(); @@ -2039,8 +2043,8 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { } else if (event instanceof MessageEvent me) { // Message is considered a final event completionLatch.countDown(); - } else if (event instanceof TaskUpdateEvent tue && - tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { + } else if (event instanceof TaskUpdateEvent tue + && tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { if (status.isFinal()) { completionLatch.countDown(); } @@ -2059,7 +2063,7 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { getClient().sendMessage(message, List.of(consumer), errorHandler); assertTrue(completionLatch.await(15, TimeUnit.SECONDS), - "Should receive final event"); + "Should receive final event"); assertNull(errorRef.get(), "Should not have errors during message send"); // Give cleanup time to run after final event @@ -2083,12 +2087,12 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { // Attempt resubscription try { getClient().resubscribe(new TaskIdParams(taskId), - List.of(), - resubErrorHandler); + List.of(), + resubErrorHandler); // Wait for error assertTrue(errorLatch.await(15, TimeUnit.SECONDS), - "Should receive error for finalized task"); + "Should receive error for finalized task"); Throwable error = resubErrorRef.get(); assertNotNull(error, "Resubscription should fail for finalized task"); @@ -2097,20 +2101,20 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { Throwable cause = error; boolean foundTaskNotFound = false; while (cause != null && !foundTaskNotFound) { - if (cause instanceof TaskNotFoundError || - (cause instanceof A2AClientException && - ((A2AClientException) cause).getCause() instanceof TaskNotFoundError)) { + if (cause instanceof TaskNotFoundError + || (cause instanceof A2AClientException + && ((A2AClientException) cause).getCause() instanceof TaskNotFoundError)) { foundTaskNotFound = true; } cause = cause.getCause(); } assertTrue(foundTaskNotFound, - "Should receive TaskNotFoundError - MainQueue closed for finalized task"); + "Should receive TaskNotFoundError - MainQueue closed for finalized task"); } catch (A2AClientException e) { // Exception might be thrown immediately instead of via error handler assertInstanceOf(TaskNotFoundError.class, e.getCause(), - "Should fail with TaskNotFoundError - MainQueue cleaned up for finalized task"); + "Should fail with TaskNotFoundError - MainQueue cleaned up for finalized task"); } } finally { @@ -2126,4 +2130,4 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { } } -} \ No newline at end of file +} diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index 31516c4dc..404e0bfb9 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -13,6 +13,8 @@ import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; import io.quarkus.arc.profile.IfBuildProfile; import org.junit.jupiter.api.Assertions; @@ -27,11 +29,11 @@ public class AgentCardProducer { @PublicAgentCard public AgentCard agentCard() { String port = System.getProperty("test.agent.card.port", "8081"); + String preferredTransport = loadPreferredTransportFromProperties(); AgentCard.Builder builder = new AgentCard.Builder() .name("test-card") .description("A test agent card") - .url("http://localhost:" + port) .version("1.0") .documentationUrl("http://example.com/docs") .capabilities(new AgentCapabilities.Builder() @@ -42,13 +44,8 @@ public AgentCard agentCard() { .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(new ArrayList<>()) - .protocolVersion("0.2.5"); - - String preferredTransport = loadPreferredTransportFromProperties(); - if (preferredTransport != null) { - builder.preferredTransport(preferredTransport); - } - + .protocolVersion("0.2.5") + .supportedInterfaces(Collections.singletonList(new AgentInterface(preferredTransport, "http://localhost:" + port))); return builder.build(); } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index c5174c4ad..ea14783bd 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -152,7 +152,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, } @Override - public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, + public void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { if (!getAgentCardInternal().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); @@ -161,7 +161,7 @@ public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificat try { ServerCallContext context = createCallContext(responseObserver); - TaskPushNotificationConfig config = FromProto.taskPushNotificationConfig(request); + TaskPushNotificationConfig config = FromProto.setTaskPushNotificationConfig(request); TaskPushNotificationConfig responseConfig = getRequestHandler().onSetTaskPushNotificationConfig(config, context); responseObserver.onNext(ToProto.taskPushNotificationConfig(responseConfig)); responseObserver.onCompleted(); @@ -248,7 +248,7 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, } @Override - public void taskSubscription(io.a2a.grpc.TaskSubscriptionRequest request, + public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, StreamObserver responseObserver) { if (!getAgentCardInternal().capabilities().streaming()) { handleError(responseObserver, new InvalidRequestError()); @@ -317,7 +317,7 @@ public void onComplete() { } @Override - public void getAgentCard(io.a2a.grpc.GetAgentCardRequest request, + public void getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request, StreamObserver responseObserver) { try { responseObserver.onNext(ToProto.agentCard(getAgentCardInternal())); diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index e1bff7f41..44c465508 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -12,7 +12,6 @@ import com.google.protobuf.Struct; import io.a2a.grpc.AuthenticationInfo; import io.a2a.grpc.CancelTaskRequest; -import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; import io.a2a.grpc.GetTaskPushNotificationConfigRequest; import io.a2a.grpc.GetTaskRequest; @@ -24,12 +23,13 @@ import io.a2a.grpc.Role; import io.a2a.grpc.SendMessageRequest; import io.a2a.grpc.SendMessageResponse; +import io.a2a.grpc.SetTaskPushNotificationConfigRequest; import io.a2a.grpc.StreamResponse; import io.a2a.grpc.Task; import io.a2a.grpc.TaskPushNotificationConfig; import io.a2a.grpc.TaskState; import io.a2a.grpc.TaskStatus; -import io.a2a.grpc.TaskSubscriptionRequest; +import io.a2a.grpc.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.events.EventConsumer; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; @@ -492,11 +492,11 @@ public void onCompleted() { @Test public void testOnResubscribeNoExistingTaskError() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); - handler.taskSubscription(request, streamRecorder); + handler.subscribeToTask(request, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); assertGrpcError(streamRecorder, Status.Code.NOT_FOUND); } @@ -512,10 +512,10 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { }; StreamRecorder streamRecorder = StreamRecorder.create(); - TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); - handler.taskSubscription(request, streamRecorder); + handler.subscribeToTask(request, streamRecorder); // We need to send some events in order for those to end up in the queue SendMessageRequest sendMessageRequest = SendMessageRequest.newBuilder() @@ -558,14 +558,14 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { .build()); StreamRecorder streamRecorder = StreamRecorder.create(); - TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ - handler.taskSubscription(request, streamRecorder); + handler.subscribeToTask(request, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); } List result = streamRecorder.getValues(); @@ -593,11 +593,11 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception // This test does not exist in the Python implementation AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); - TaskSubscriptionRequest request = TaskSubscriptionRequest.newBuilder() + SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); - handler.taskSubscription(request, streamRecorder); + handler.subscribeToTask(request, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); } @@ -822,17 +822,20 @@ private StreamRecorder createTaskPushNotificationCon taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); PushNotificationConfig config = PushNotificationConfig.newBuilder() .setUrl("http://example.com") + .setId("config456") .build(); TaskPushNotificationConfig taskPushNotificationConfig = TaskPushNotificationConfig.newBuilder() .setName(name) .setPushNotificationConfig(config) .build(); - CreateTaskPushNotificationConfigRequest setRequest = CreateTaskPushNotificationConfigRequest.newBuilder() + SetTaskPushNotificationConfigRequest setRequest = SetTaskPushNotificationConfigRequest.newBuilder() .setConfig(taskPushNotificationConfig) + .setConfigId("config456") + .setParent("tasks/" + MINIMAL_TASK.getId()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); - handler.createTaskPushNotificationConfig(setRequest, streamRecorder); + handler.setTaskPushNotificationConfig(setRequest, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); return streamRecorder; } diff --git a/transport/grpc/src/main/resources/a2a-requesthandler-test.properties b/transport/grpc/src/test/resources/a2a-requesthandler-test.properties similarity index 100% rename from transport/grpc/src/main/resources/a2a-requesthandler-test.properties rename to transport/grpc/src/test/resources/a2a-requesthandler-test.properties diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 2b302ec53..fb570936c 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -19,9 +19,21 @@ - ${project.groupId} + io.github.a2asdk a2a-java-sdk-server-common + + io.github.a2asdk + a2a-java-sdk-spec-grpc + + + io.github.a2asdk + a2a-java-sdk-spec + + + com.google.protobuf + protobuf-java-util + ${project.groupId} a2a-java-sdk-server-common @@ -43,7 +55,6 @@ mockito-core test - diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 058ebc5f9..4f712b67e 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -1,6 +1,7 @@ package io.a2a.transport.jsonrpc.handler; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; @@ -47,8 +48,8 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.spec.TaskResubscriptionRequest; import io.a2a.server.util.async.Internal; +import io.a2a.spec.SubscribeToTaskRequest; import mutiny.zero.ZeroPublisher; import org.jspecify.annotations.Nullable; @@ -124,8 +125,8 @@ public CancelTaskResponse onCancelTask(CancelTaskRequest request, ServerCallCont } } - public Flow.Publisher onResubscribeToTask( - TaskResubscriptionRequest request, ServerCallContext context) { + public Flow.Publisher onSubscribeToTask( + SubscribeToTaskRequest request, ServerCallContext context) { if (!agentCard.capabilities().streaming()) { return ZeroPublisher.fromItems( new SendStreamingMessageResponse( diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 450c0e76d..dc2088a2d 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -62,7 +62,7 @@ import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; -import io.a2a.spec.TaskResubscriptionRequest; +import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; @@ -672,12 +672,13 @@ public void testSetPushNotificationConfigSuccess() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com") + .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); TaskPushNotificationConfig taskPushConfigResult = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").id(MINIMAL_TASK.getId()).build()); + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").id("c295ea44-7543-4f78-b524-7a38915ad6e4").build()); Assertions.assertEquals(taskPushConfigResult, response.getResult()); } @@ -692,7 +693,8 @@ public void testGetPushNotificationConfigSuccess() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").build()); + MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); @@ -702,7 +704,7 @@ public void testGetPushNotificationConfigSuccess() { GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest, callContext); TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), - new PushNotificationConfig.Builder().id(MINIMAL_TASK.getId()).url("http://example.com").build()); + new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); assertEquals(expectedConfig, getResponse.getResult()); } @@ -738,7 +740,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex TaskPushNotificationConfig config = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), - new PushNotificationConfig.Builder().url("http://example.com").build()); + new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); assertNull(stpnResponse.getError()); @@ -823,8 +825,8 @@ public void testOnResubscribeExistingTaskSuccess() { //eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - Flow.Publisher response = handler.onResubscribeToTask(request, callContext); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + Flow.Publisher response = handler.onSubscribeToTask(request, callContext); // We need to send some events in order for those to end up in the queue Message message = new Message.Builder() @@ -900,13 +902,13 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { .status(new TaskStatus(TaskState.WORKING)) .build()); - TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); Flow.Publisher response; try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ - response = handler.onResubscribeToTask(request, callContext); + response = handler.onSubscribeToTask(request, callContext); } CompletableFuture future = new CompletableFuture<>(); @@ -955,8 +957,8 @@ public void onComplete() { public void testOnResubscribeNoExistingTaskError() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - Flow.Publisher response = handler.onResubscribeToTask(request, callContext); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + Flow.Publisher response = handler.onSubscribeToTask(request, callContext); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -1049,8 +1051,8 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() { AgentCard card = createAgentCard(false, true, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - TaskResubscriptionRequest request = new TaskResubscriptionRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); - Flow.Publisher response = handler.onResubscribeToTask(request, callContext); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + Flow.Publisher response = handler.onSubscribeToTask(request, callContext); List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); @@ -1100,6 +1102,7 @@ public void testPushNotificationsNotSupportedError() { new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("http://example.com") .build()); @@ -1143,6 +1146,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { new TaskPushNotificationConfig( MINIMAL_TASK.getId(), new PushNotificationConfig.Builder() + .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("http://example.com") .build()); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 105831262..de4f45399 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -1,6 +1,7 @@ package io.a2a.transport.rest.handler; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; import com.fasterxml.jackson.core.JacksonException; import com.google.protobuf.InvalidProtocolBufferException; @@ -62,8 +63,7 @@ public class RestHandler { private static final Logger log = Logger.getLogger(RestHandler.class.getName()); private AgentCard agentCard; - private @Nullable - Instance extendedAgentCard; + private @Nullable Instance extendedAgentCard; private RequestHandler requestHandler; private final Executor executor; @@ -143,9 +143,9 @@ public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, Stri if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } - io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder(); + io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(body, builder); - TaskPushNotificationConfig result = requestHandler.onSetTaskPushNotificationConfig(ProtoUtils.FromProto.taskPushNotificationConfig(builder), context); + TaskPushNotificationConfig result = requestHandler.onSetTaskPushNotificationConfig(ProtoUtils.FromProto.setTaskPushNotificationConfig(builder), context); return createSuccessResponse(201, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(result))); } catch (JSONRPCError e) { return createErrorResponse(e); @@ -294,7 +294,7 @@ private void validate(String json) { try { Utils.OBJECT_MAPPER.readTree(json); } catch (JacksonException e) { - throw new JSONParseError(JSONParseError.DEFAULT_CODE, "Failed to parse json", e.getMessage()); + throw new JSONParseError(JSON_PARSE_ERROR_CODE, "Failed to parse json", e.getMessage()); } } diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index eca44a8db..109bfc7a9 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -221,8 +221,9 @@ public void testPushNotificationConfigSuccess() { { "parent": "tasks/%s", "config": { - "name": "tasks/%s/pushNotificationConfigs/", + "name": "tasks/%s/pushNotificationConfigs/default-config-id", "pushNotificationConfig": { + "id":"default-config-id", "url": "https://example.com/callback", "authentication": { "schemes": ["jwt"] @@ -268,8 +269,9 @@ public void testGetPushNotificationConfig() { { "parent": "tasks/%s", "config": { - "name": "tasks/%s/pushNotificationConfigs/", + "name": "tasks/%s/pushNotificationConfigs/default-config-id", "pushNotificationConfig": { + "id":"default-config-id", "url": "https://example.com/callback", "authentication": { "schemes": ["jwt"] diff --git a/transport/rest/src/main/resources/a2a-requesthandler-test.properties b/transport/rest/src/test/resources/a2a-requesthandler-test.properties similarity index 100% rename from transport/rest/src/main/resources/a2a-requesthandler-test.properties rename to transport/rest/src/test/resources/a2a-requesthandler-test.properties From c1541045fbb430746d8476918dc34bfb7cf737dc Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Thu, 4 Dec 2025 15:14:55 +0100 Subject: [PATCH 215/493] chore: fix javadoc errors (#510) # Description - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Signed-off-by: Jeff Mesnil --- .../main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java | 4 ++-- .../a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java | 2 +- spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java | 4 ++-- spec/src/main/java/io/a2a/spec/TaskIdParams.java | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java index cb3a05ade..b47a56e37 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java @@ -116,7 +116,7 @@ default java.util.List emptyListToNull(java.util.List list) { /** * Converts domain Map to protobuf Struct (generic conversion). *

    - * Used for any Map field that maps to protobuf Struct (header, params, etc.). + * Used for any {@code Map} field that maps to protobuf Struct (header, params, etc.). * Use this with {@code @Mapping(qualifiedByName = "mapToStruct")}. * * @param map the domain map @@ -135,7 +135,7 @@ default Struct mapToStruct(Map map) { /** * Converts protobuf Struct to domain Map (generic conversion). *

    - * Used for any protobuf Struct field that maps to Map (header, params, etc.). + * Used for any protobuf Struct field that maps to {@code Map} (header, params, etc.). * Use this with {@code @Mapping(qualifiedByName = "structToMap")}. * * @param struct the protobuf Struct diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java index df0c2d1af..2d0127e21 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java @@ -127,7 +127,7 @@ default PushNotificationConfig mapPushNotificationConfigWithId(SetTaskPushNotifi *

    * The config_id from the request overrides the ID in the proto's PushNotificationConfig. * - * @param request the protobuf SetTaskPushNotificationConfigRequest + * @param domain the protobuf SetTaskPushNotificationConfigRequest * @return domain PushNotificationConfig with config_id injected */ default io.a2a.grpc.TaskPushNotificationConfig mapPushNotificationConfig(TaskPushNotificationConfig domain) { diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java index d9f89cf03..0e70127a2 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java @@ -15,7 +15,7 @@ * The A2A Protocol defines two primary streaming operations: *

      *
    • {@link SendStreamingMessageRequest} - Stream task execution events in real-time
    • - *
    • {@link TaskResubscriptionRequest} - Resubscribe to events from an existing task
    • + *
    • {@link SubscribeToTaskRequest} - Resubscribe to events from an existing task
    • *
    *

    * Streaming requests follow the JSON-RPC 2.0 specification structure but the response @@ -27,7 +27,7 @@ * * @param the type of the params object for this streaming request * @see SendStreamingMessageRequest - * @see TaskResubscriptionRequest + * @see SubscribeToTaskRequest * @see StreamingEventKind * @see A2A Protocol Specification */ diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index fd7ef9980..573aff655 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -10,13 +10,13 @@ * Parameters containing a task identifier for task-related operations. *

    * This simple parameter record is used by operations that only need a task ID, - * such as {@link CancelTaskRequest}, {@link TaskResubscriptionRequest}, and similar + * such as {@link CancelTaskRequest}, {@link SubscribeToTaskRequest}, and similar * task-specific requests. It optionally includes metadata for additional context. * * @param id the unique task identifier (required) * @param metadata optional arbitrary key-value metadata for the request * @see CancelTaskRequest for task cancellation - * @see TaskResubscriptionRequest for task resubscription + * @see SubscribeToTaskRequest for task resubscription * @see A2A Protocol Specification */ @JsonInclude(JsonInclude.Include.NON_ABSENT) From 46793cfb4d96bb58fc4d1d2c75972b4e8a193781 Mon Sep 17 00:00:00 2001 From: Anastasia Date: Fri, 5 Dec 2025 12:21:33 +0100 Subject: [PATCH 216/493] feat: update grpc-java to the newer version; handle introduced StatusException (#488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Update the [grpc-java](https://github.com/grpc/grpc-java/releases) used to the latest available release - 1.77. It introduces the StatusException on the BlockingClientCall, add adjustments for this case as well. I've run the tests and they all finished successfully: ``` [INFO] --------------------------------[ pom ]--------------------------------- [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary for Java SDK A2A Parent 0.4.0.Alpha1-SNAPSHOT: [INFO] [INFO] Java SDK A2A Parent ................................ SUCCESS [ 0.006 s] [INFO] A2A Java SDK - BOM Test Utilities .................. SUCCESS [ 1.071 s] [INFO] Java SDK A2A Common ................................ SUCCESS [ 0.211 s] [INFO] Java SDK A2A Spec .................................. SUCCESS [ 1.999 s] [INFO] Java SDK A2A HTTP Client ........................... SUCCESS [ 1.993 s] [INFO] Java SDK A2A Client Transport: SPI ................. SUCCESS [ 0.777 s] [INFO] Java SDK A2A Client Transport: JSONRPC ............. SUCCESS [ 5.879 s] [INFO] Java SDK A2A Spec: gRPC ............................ SUCCESS [ 1.466 s] [INFO] Java SDK A2A Client Transport: gRPC ................ SUCCESS [ 0.058 s] [INFO] Java SDK A2A Client Transport: JSON+HTTP/REST ...... SUCCESS [ 4.507 s] [INFO] Java SDK A2A Client ................................ SUCCESS [ 5.255 s] [INFO] Java SDK A2A Examples: Hello World ................. SUCCESS [ 0.019 s] [INFO] Java SDK A2A Examples .............................. SUCCESS [ 0.801 s] [INFO] Java SDK A2A Core .................................. SUCCESS [ 16.824 s] [INFO] Java A2A SDK Server Tests Common ................... SUCCESS [ 0.094 s] [INFO] A2A Java SDK - MicroProfile Config Integration ..... SUCCESS [ 5.229 s] [INFO] Java A2A Reference Server: Common .................. SUCCESS [ 0.451 s] [INFO] Java SDK A2A Transport: JSONRPC .................... SUCCESS [ 3.347 s] [INFO] Java A2A Reference Server: JSONRPC ................. SUCCESS [ 31.455 s] [INFO] Java SDK A2A Examples .............................. SUCCESS [ 1.216 s] [INFO] A2A Java SDK :: Extras :: Common ................... SUCCESS [ 0.023 s] [INFO] Java A2A Extras: JPA Database TaskStore ............ SUCCESS [ 15.282 s] [INFO] Java A2A Extras: JPA Database PushNotificationConfigStore SUCCESS [ 11.814 s] [INFO] Java A2A Extras: Replicated Queue Manager Parent ... SUCCESS [ 0.001 s] [INFO] Java A2A Extras: Replicated Queue Manager Core ..... SUCCESS [ 1.550 s] [INFO] Java A2A Extras: Replicated Queue Manager MicroProfile Reactive Messaging SUCCESS [ 2.447 s] [INFO] A2A Java SDK - Cloud Deployment Example Server ..... SUCCESS [ 1.379 s] [INFO] Java A2A Extras: Replicated Queue Manager Tests - Single Instance SUCCESS [ 14.694 s] [INFO] Java A2A Extras: Replicated Queue Manager Tests - Multi Instance Parent SUCCESS [ 0.001 s] [INFO] Java A2A Extras: Replicated Queue Manager Tests - Multi Instance Common SUCCESS [ 0.032 s] [INFO] Java A2A Extras: Replicated Queue Manager Tests - Multi Instance App 1 SUCCESS [ 0.058 s] [INFO] Java A2A Extras: Replicated Queue Manager Tests - Multi Instance App 2 SUCCESS [ 0.058 s] [INFO] Java A2A Extras: Replicated Queue Manager Tests - Multi Instance Tests SUCCESS [01:00 min] [INFO] Java SDK A2A Transport: gRPC ....................... SUCCESS [ 2.817 s] [INFO] Java A2A Reference Server: gRPC .................... SUCCESS [ 26.423 s] [INFO] Java SDK A2A Transport: JSON+HTTP/REST ............. SUCCESS [ 1.915 s] [INFO] Java A2A Reference Server: JSON+HTTP/REST .......... SUCCESS [ 29.314 s] [INFO] Java SDK A2A TCK Server ............................ SUCCESS [ 1.405 s] [INFO] A2A Java SDK - BOM ................................. SUCCESS [ 0.015 s] [INFO] A2A Java SDK - Extras BOM .......................... SUCCESS [ 0.001 s] [INFO] A2A Java SDK - Reference Implementations BOM ....... SUCCESS [ 0.000 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 04:16 min [INFO] Finished at: 2025-11-26T14:02:51Z [INFO] ------------------------------------------------------------------------ ``` Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes # 🦕 --- .../transport/grpc/GrpcErrorMapper.java | 12 +++---- .../client/transport/grpc/GrpcTransport.java | 15 ++++----- pom.xml | 2 +- .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 31 +++++++++---------- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java index 5f0db8f0f..cf245553b 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcErrorMapper.java @@ -13,20 +13,20 @@ import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.UnsupportedOperationError; import io.grpc.Status; -import io.grpc.StatusRuntimeException; /** - * Utility class to map gRPC StatusRuntimeException to appropriate A2A error types + * Utility class to map gRPC exceptions to appropriate A2A error types */ public class GrpcErrorMapper { - public static A2AClientException mapGrpcError(StatusRuntimeException e) { + public static A2AClientException mapGrpcError(Throwable e) { return mapGrpcError(e, "gRPC error: "); } - public static A2AClientException mapGrpcError(StatusRuntimeException e, String errorPrefix) { - Status.Code code = e.getStatus().getCode(); - String description = e.getStatus().getDescription(); + public static A2AClientException mapGrpcError(Throwable e, String errorPrefix) { + Status status = Status.fromThrowable(e); + Status.Code code = status.getCode(); + String description = status.getDescription(); // Extract the actual error type from the description if possible // (using description because the same code can map to multiple errors - diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 1f6dc19a4..4ff3c0bc8 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -44,6 +44,7 @@ import io.a2a.spec.TaskQueryParams; import io.grpc.Channel; import io.grpc.Metadata; +import io.grpc.StatusException; import io.grpc.StatusRuntimeException; import io.grpc.stub.MetadataUtils; import io.grpc.stub.StreamObserver; @@ -93,7 +94,7 @@ public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallCont } else { throw new A2AClientException("Server response did not contain a message or task"); } - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to send message: "); } } @@ -130,7 +131,7 @@ public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.task(stubWithMetadata.getTask(getTaskRequest)); - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to get task: "); } } @@ -148,7 +149,7 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.task(stubWithMetadata.cancelTask(cancelTaskRequest)); - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to cancel task: "); } } @@ -195,7 +196,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo grpcResponse.getTasksCount(), grpcResponse.getNextPageToken().isEmpty() ? null : grpcResponse.getNextPageToken() ); - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to list tasks: "); } } @@ -237,7 +238,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); return FromProto.taskPushNotificationConfig(stubWithMetadata.getTaskPushNotificationConfig(grpcRequest)); - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to get task push notification config: "); } } @@ -259,7 +260,7 @@ public List listTaskPushNotificationConfigurations( return stubWithMetadata.listTaskPushNotificationConfig(grpcRequest).getConfigsList().stream() .map(FromProto::taskPushNotificationConfig) .collect(Collectors.toList()); - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to list task push notification config: "); } } @@ -278,7 +279,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); stubWithMetadata.deleteTaskPushNotificationConfig(grpcRequest); - } catch (StatusRuntimeException e) { + } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to delete task push notification config: "); } } diff --git a/pom.xml b/pom.xml index e4bc77a17..854bf3f73 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ - 1.73.0 + 1.77.0 UTF-8 3.5.0 3.14.1 diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java index a20922afe..d5261ab39 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -7,9 +7,6 @@ * A2AService defines the operations of the A2A protocol. * */ -@javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.73.0)", - comments = "Source: a2a.proto") @io.grpc.stub.annotations.GrpcGenerated public final class A2AServiceGrpc { @@ -717,8 +714,8 @@ protected A2AServiceBlockingV2Stub build( * Send a message to the agent. * */ - public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getSendMessageMethod(), getCallOptions(), request); } @@ -739,8 +736,8 @@ public io.a2a.grpc.SendMessageResponse sendMessage(io.a2a.grpc.SendMessageReques * Get the current state of a task from the agent. * */ - public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getGetTaskMethod(), getCallOptions(), request); } @@ -749,8 +746,8 @@ public io.a2a.grpc.Task getTask(io.a2a.grpc.GetTaskRequest request) { * List tasks with optional filtering and pagination. * */ - public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getListTasksMethod(), getCallOptions(), request); } @@ -759,8 +756,8 @@ public io.a2a.grpc.ListTasksResponse listTasks(io.a2a.grpc.ListTasksRequest requ * Cancel a task. * */ - public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getCancelTaskMethod(), getCallOptions(), request); } @@ -792,8 +789,8 @@ public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a * Get a push notification config for a task. * */ - public io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationConfigRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getGetTaskPushNotificationConfigMethod(), getCallOptions(), request); } @@ -802,8 +799,8 @@ public io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConfig(io.a * Get a list of push notifications configured for a task. * */ - public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationConfigRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getListTaskPushNotificationConfigMethod(), getCallOptions(), request); } @@ -822,8 +819,8 @@ public io.a2a.grpc.AgentCard getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCa * Delete a push notification config for a task. * */ - public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public com.google.protobuf.Empty deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getDeleteTaskPushNotificationConfigMethod(), getCallOptions(), request); } } From 509917405ef88bfa482c1c60e23eb59910a33f14 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Tue, 9 Dec 2025 14:49:47 +0100 Subject: [PATCH 217/493] chore: Upgrading and fixing nullaway on JDK 25 (#520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * upgrading nullaway and error-prone on default profile * fixing nullaway detection not working Fixes #519 🦕 Signed-off-by: Emmanuel Hugonnet --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 854bf3f73..bbfa70615 100644 --- a/pom.xml +++ b/pom.xml @@ -66,8 +66,8 @@ 5.5.1 2.0.17 1.5.18 - 2.43.0 - 0.12.12 + 2.45.0 + 0.12.14 -XDaddTypeAnnotationsToSymbol=true @@ -341,7 +341,8 @@ -XDcompilePolicy=simple --should-stop=ifError=FLOW -parameters - ${error-prone.flag}-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode + ${error-prone.flag} + -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode From 5ce7929abbeec5321e15d5486181f7a5e3beef46 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Tue, 9 Dec 2025 22:57:37 +0100 Subject: [PATCH 218/493] chore(dep): Upgrading versions (#521) - Upgrading protobuf java from 4.31.1 to 4.33.1 - Upgrading quarkus from 3.28.2 to 3.30.1 Signed-off-by: Emmanuel Hugonnet --- pom.xml | 6 +++--- transport/jsonrpc/pom.xml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bbfa70615..13b390d00 100644 --- a/pom.xml +++ b/pom.xml @@ -60,15 +60,15 @@ 5.15.0 1.1.1 1.7.1 - 4.31.1 + 4.33.1 0.6.1 - 3.28.2 + 3.30.1 5.5.1 2.0.17 1.5.18 2.45.0 0.12.14 - -XDaddTypeAnnotationsToSymbol=true + -XDaddTypeAnnotationsToSymbol=true true diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index fb570936c..e5cf67dec 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -34,6 +34,10 @@ com.google.protobuf protobuf-java-util + + com.google.code.gson + gson + ${project.groupId} a2a-java-sdk-server-common From e2e32e5bdc3088a0502518fce2a1238b991b47ba Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 10 Dec 2025 11:47:05 +0100 Subject: [PATCH 219/493] refactor: Complete migration from Jackson to Gson across codebase (#514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Jackson ObjectMapper with Gson throughout the codebase to eliminate Jackson dependency and provide custom serialization control. Key Changes: - Created JsonUtil.java with Gson-based OBJECT_MAPPER singleton - Implemented 9 custom TypeAdapters for complex type handling: * OffsetDateTimeTypeAdapter - ISO-8601 datetime serialization * JSONRPCErrorTypeAdapter - Polymorphic error deserialization (12 error types) * ThrowableTypeAdapter - Avoids Java 17+ reflection restrictions * TaskStateTypeAdapter - Wire format enum serialization * RoleTypeAdapter - Message.Role enum handling * PartKindTypeAdapter - Part.Kind enum handling * PartTypeAdapter - Polymorphic Part deserialization (Text/File/Data) * StreamingEventKindTypeAdapter - Event type deserialization * FileContentTypeAdapter - FileWithBytes/FileWithUri discrimination - Added TaskSerializationTest with 21 comprehensive tests covering: * Round-trip serialization for all Task components * Direct JSON deserialization tests * All Part types and FileContent variants - Removed Jackson-specific deserializers and annotations - Updated all usages across 142 files (client, server, extras, tests) - Removed Jackson dependencies from spec/pom.xml Technical Details: - Thread-safe singleton OBJECT_MAPPER with all adapters registered - Handles polymorphic types via two-pass parsing strategy - Maintains wire format compatibility with existing JSON-RPC protocol - Preserves null-safety with NullAway/JSpecify conventions Fixes #487 🦕 Signed-off-by: Emmanuel Hugonnet --- boms/sdk/pom.xml | 10 - boms/test-utils/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 5 - .../transport/jsonrpc/JSONRPCTransport.java | 26 +- .../jsonrpc/sse/SSEEventListener.java | 2 +- .../transport/rest/RestErrorMapper.java | 17 +- .../client/transport/rest/RestTransport.java | 19 +- .../examples/helloworld/HelloWorldClient.java | 7 +- ...paDatabasePushNotificationConfigStore.java | 2 +- .../jpa/JpaPushNotificationConfig.java | 10 +- .../core/EventSerializationTest.java | 44 +- .../core/ReplicatedQueueManagerTest.java | 10 +- .../ReactiveMessagingReplicationStrategy.java | 8 +- ...ctiveMessagingReplicationStrategyTest.java | 4 +- .../KafkaReplicationIntegrationTest.java | 6 +- .../tests/TestKafkaEventConsumer.java | 4 +- .../database/jpa/JpaDatabaseTaskStore.java | 2 +- .../taskstore/database/jpa/JpaTask.java | 10 +- .../jpa/JpaDatabaseTaskStoreTest.java | 2 +- pom.xml | 24 +- .../server/grpc/quarkus/A2ATestResource.java | 12 +- .../server/apps/quarkus/A2AServerRoutes.java | 45 +- .../server/apps/quarkus/A2ATestRoutes.java | 12 +- .../server/rest/quarkus/A2ATestRoutes.java | 12 +- .../tasks/BasePushNotificationSender.java | 10 +- .../a2a/server/events/EventConsumerTest.java | 14 +- .../io/a2a/server/events/EventQueueTest.java | 22 +- .../AbstractA2ARequestHandlerTest.java | 12 +- ...MemoryPushNotificationConfigStoreTest.java | 2 +- .../server/tasks/InMemoryTaskStoreTest.java | 4 +- .../tasks/PushNotificationSenderTest.java | 11 +- .../io/a2a/server/tasks/TaskManagerTest.java | 2 +- .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 24 +- .../io/a2a/grpc/utils/JSONRPCUtilsTest.java | 2 +- spec/pom.xml | 9 +- .../io/a2a/json/JsonMappingException.java | 102 +++ .../io/a2a/json/JsonProcessingException.java | 55 ++ spec/src/main/java/io/a2a/json/JsonUtil.java | 763 ++++++++++++++++++ .../main/java/io/a2a/json/package-info.java | 8 + .../java/io/a2a/spec/A2AClientJSONError.java | 2 +- .../main/java/io/a2a/spec/A2AErrorCodes.java | 4 - .../io/a2a/spec/APIKeySecurityScheme.java | 17 +- .../java/io/a2a/spec/AgentCapabilities.java | 5 - spec/src/main/java/io/a2a/spec/AgentCard.java | 4 - .../java/io/a2a/spec/AgentCardSignature.java | 9 +- .../main/java/io/a2a/spec/AgentInterface.java | 5 - .../main/java/io/a2a/spec/AgentProvider.java | 4 - .../src/main/java/io/a2a/spec/AgentSkill.java | 4 - spec/src/main/java/io/a2a/spec/Artifact.java | 4 - ...ticatedExtendedCardNotConfiguredError.java | 18 +- .../java/io/a2a/spec/AuthenticationInfo.java | 4 - .../a2a/spec/AuthorizationCodeOAuthFlow.java | 4 - .../java/io/a2a/spec/CancelTaskRequest.java | 10 +- .../java/io/a2a/spec/CancelTaskResponse.java | 12 +- .../a2a/spec/ClientCredentialsOAuthFlow.java | 4 - .../spec/ContentTypeNotSupportedError.java | 12 +- spec/src/main/java/io/a2a/spec/DataPart.java | 13 +- ...eleteTaskPushNotificationConfigParams.java | 4 - ...leteTaskPushNotificationConfigRequest.java | 12 +- ...eteTaskPushNotificationConfigResponse.java | 14 +- spec/src/main/java/io/a2a/spec/EventKind.java | 16 - .../main/java/io/a2a/spec/FileContent.java | 3 - .../io/a2a/spec/FileContentDeserializer.java | 38 - spec/src/main/java/io/a2a/spec/FilePart.java | 11 +- .../main/java/io/a2a/spec/FileWithBytes.java | 5 - .../main/java/io/a2a/spec/FileWithUri.java | 5 - .../GetAuthenticatedExtendedCardRequest.java | 10 +- .../GetAuthenticatedExtendedCardResponse.java | 12 +- .../GetTaskPushNotificationConfigParams.java | 4 - .../GetTaskPushNotificationConfigRequest.java | 10 +- ...GetTaskPushNotificationConfigResponse.java | 12 +- .../main/java/io/a2a/spec/GetTaskRequest.java | 10 +- .../java/io/a2a/spec/GetTaskResponse.java | 11 +- .../io/a2a/spec/HTTPAuthSecurityScheme.java | 15 +- .../io/a2a/spec/IdJsonMappingException.java | 6 +- .../java/io/a2a/spec/ImplicitOAuthFlow.java | 4 - .../main/java/io/a2a/spec/InternalError.java | 12 +- .../a2a/spec/InvalidAgentResponseError.java | 20 +- .../java/io/a2a/spec/InvalidParamsError.java | 21 +- .../java/io/a2a/spec/InvalidRequestError.java | 20 +- .../java/io/a2a/spec/JSONErrorResponse.java | 5 - .../main/java/io/a2a/spec/JSONParseError.java | 17 +- .../main/java/io/a2a/spec/JSONRPCError.java | 16 +- .../io/a2a/spec/JSONRPCErrorDeserializer.java | 72 -- .../io/a2a/spec/JSONRPCErrorResponse.java | 13 +- .../io/a2a/spec/JSONRPCErrorSerializer.java | 29 - .../main/java/io/a2a/spec/JSONRPCRequest.java | 5 - .../spec/JSONRPCRequestDeserializerBase.java | 90 --- .../java/io/a2a/spec/JSONRPCResponse.java | 5 - .../spec/JSONRPCVoidResponseSerializer.java | 32 - .../ListTaskPushNotificationConfigParams.java | 5 - ...ListTaskPushNotificationConfigRequest.java | 12 +- ...istTaskPushNotificationConfigResponse.java | 12 +- .../java/io/a2a/spec/ListTasksParams.java | 4 - .../java/io/a2a/spec/ListTasksRequest.java | 11 +- .../java/io/a2a/spec/ListTasksResponse.java | 11 +- .../java/io/a2a/spec/ListTasksResult.java | 4 - spec/src/main/java/io/a2a/spec/Message.java | 24 +- .../io/a2a/spec/MessageSendConfiguration.java | 4 - .../java/io/a2a/spec/MessageSendParams.java | 4 - .../java/io/a2a/spec/MethodNotFoundError.java | 14 +- .../io/a2a/spec/MutualTLSSecurityScheme.java | 12 +- .../a2a/spec/NonStreamingJSONRPCRequest.java | 7 - ...onStreamingJSONRPCRequestDeserializer.java | 61 -- .../io/a2a/spec/OAuth2SecurityScheme.java | 13 +- .../src/main/java/io/a2a/spec/OAuthFlows.java | 5 - .../a2a/spec/OpenIdConnectSecurityScheme.java | 12 +- spec/src/main/java/io/a2a/spec/Part.java | 16 - .../java/io/a2a/spec/PasswordOAuthFlow.java | 5 - .../io/a2a/spec/PushNotificationConfig.java | 6 - .../PushNotificationNotSupportedError.java | 14 +- .../main/java/io/a2a/spec/SecurityScheme.java | 16 - .../java/io/a2a/spec/SendMessageRequest.java | 13 +- .../java/io/a2a/spec/SendMessageResponse.java | 11 +- .../a2a/spec/SendStreamingMessageRequest.java | 10 +- .../spec/SendStreamingMessageResponse.java | 12 +- .../SetTaskPushNotificationConfigRequest.java | 11 +- ...SetTaskPushNotificationConfigResponse.java | 12 +- .../java/io/a2a/spec/StreamingEventKind.java | 15 - .../io/a2a/spec/StreamingJSONRPCRequest.java | 7 - .../StreamingJSONRPCRequestDeserializer.java | 41 - .../io/a2a/spec/SubscribeToTaskRequest.java | 10 +- spec/src/main/java/io/a2a/spec/Task.java | 18 +- .../io/a2a/spec/TaskArtifactUpdateEvent.java | 16 +- .../main/java/io/a2a/spec/TaskIdParams.java | 4 - .../io/a2a/spec/TaskNotCancelableError.java | 16 +- .../java/io/a2a/spec/TaskNotFoundError.java | 16 +- .../a2a/spec/TaskPushNotificationConfig.java | 4 - .../java/io/a2a/spec/TaskQueryParams.java | 6 - spec/src/main/java/io/a2a/spec/TaskState.java | 9 +- .../src/main/java/io/a2a/spec/TaskStatus.java | 8 +- .../io/a2a/spec/TaskStatusUpdateEvent.java | 16 +- spec/src/main/java/io/a2a/spec/TextPart.java | 11 +- .../java/io/a2a/spec/TransportProtocol.java | 9 +- .../a2a/spec/UnsupportedOperationError.java | 14 +- spec/src/main/java/io/a2a/util/Utils.java | 83 +- .../spec/JSONRPCErrorSerializationTest.java | 17 +- .../io/a2a/spec/SubTypeSerializationTest.java | 129 --- .../io/a2a/spec/TaskDeserializationTest.java | 90 --- .../io/a2a/spec/TaskSerializationTest.java | 713 ++++++++++++++++ .../test/java/io/a2a/spec/TaskStatusTest.java | 94 --- .../apps/common/A2AGsonObjectMapper.java | 38 + .../apps/common/AbstractA2AServerTest.java | 22 +- .../server/apps/common/TestHttpClient.java | 9 +- .../jsonrpc/handler/JSONRPCHandler.java | 2 +- .../transport/rest/handler/RestHandler.java | 15 +- 146 files changed, 2031 insertions(+), 1771 deletions(-) create mode 100644 spec/src/main/java/io/a2a/json/JsonMappingException.java create mode 100644 spec/src/main/java/io/a2a/json/JsonProcessingException.java create mode 100644 spec/src/main/java/io/a2a/json/JsonUtil.java create mode 100644 spec/src/main/java/io/a2a/json/package-info.java delete mode 100644 spec/src/main/java/io/a2a/spec/FileContentDeserializer.java delete mode 100644 spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java delete mode 100644 spec/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java delete mode 100644 spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java delete mode 100644 spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java delete mode 100644 spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java delete mode 100644 spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java delete mode 100644 spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java delete mode 100644 spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java create mode 100644 spec/src/test/java/io/a2a/spec/TaskSerializationTest.java delete mode 100644 spec/src/test/java/io/a2a/spec/TaskStatusTest.java create mode 100644 tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml index 38246898e..c0b51b73a 100644 --- a/boms/sdk/pom.xml +++ b/boms/sdk/pom.xml @@ -142,16 +142,6 @@ - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} - com.google.protobuf protobuf-java diff --git a/boms/test-utils/pom.xml b/boms/test-utils/pom.xml index febf8dec1..5c0cdff0a 100644 --- a/boms/test-utils/pom.xml +++ b/boms/test-utils/pom.xml @@ -28,7 +28,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.14.1 diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 98cd051bf..3961bdaf2 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -37,11 +37,6 @@ ${project.groupId} a2a-java-sdk-spec-grpc - - com.google.code.gson - gson - provided - com.google.protobuf protobuf-java-util diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 005fafd62..7511d0ad6 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import com.google.protobuf.MessageOrBuilder; import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; @@ -100,7 +100,7 @@ public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallCont return response.getResult(); } catch (A2AClientException e) { throw e; - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to send message: " + e, e); } } @@ -129,6 +129,8 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations( return response.getResult(); } catch (A2AClientException e) { throw e; - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to list task push notification configs: " + e, e); } } @@ -251,7 +253,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC // Response validated (no error), but no result to return } catch (A2AClientException e) { throw e; - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to delete task push notification configs: " + e, e); } } @@ -281,6 +283,8 @@ public void resubscribe(TaskIdParams request, Consumer event throw new A2AClientException("Failed to send task resubscription request: " + e, e); } catch (InterruptedException e) { throw new A2AClientException("Task resubscription request timed out: " + e, e); + } catch (JsonProcessingException e) { + throw new A2AClientException("Failed to process JSON for task resubscription request: " + e, e); } } @@ -312,7 +316,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli agentCard = response.getResult(); needsExtendedCard = false; return agentCard; - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); } } catch(A2AClientError e){ @@ -337,7 +341,7 @@ private PayloadAndHeaders applyInterceptors(String methodName, @Nullable Object return payloadAndHeaders; } - private String sendPostRequest(PayloadAndHeaders payloadAndHeaders, String method) throws IOException, InterruptedException { + private String sendPostRequest(PayloadAndHeaders payloadAndHeaders, String method) throws IOException, InterruptedException, JsonProcessingException { A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders,method); A2AHttpResponse response = builder.post(); if (!response.success()) { diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index 8cee952f1..f940f09d7 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -1,6 +1,6 @@ package io.a2a.client.transport.jsonrpc.sse; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.spec.JSONRPCError; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.TaskStatusUpdateEvent; diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java index 965cc2962..c23eab4a0 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java @@ -1,10 +1,9 @@ package io.a2a.client.transport.rest; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.gson.JsonObject; import io.a2a.client.http.A2AHttpResponse; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.spec.A2AClientException; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; import io.a2a.spec.ContentTypeNotSupportedError; @@ -26,8 +25,6 @@ */ public class RestErrorMapper { - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().registerModule(new JavaTimeModule()); - public static A2AClientException mapRestError(A2AHttpResponse response) { return RestErrorMapper.mapRestError(response.body(), response.status()); } @@ -35,9 +32,9 @@ public static A2AClientException mapRestError(A2AHttpResponse response) { public static A2AClientException mapRestError(String body, int code) { try { if (body != null && !body.isBlank()) { - JsonNode node = OBJECT_MAPPER.readTree(body); - String className = node.findValue("error").asText(); - String errorMessage = node.findValue("message").asText(); + JsonObject node = JsonUtil.fromJson(body, JsonObject.class); + String className = node.has("error") ? node.get("error").getAsString() : ""; + String errorMessage = node.has("message") ? node.get("message").getAsString() : ""; return mapRestError(className, errorMessage, code); } return mapRestError("", "", code); @@ -50,7 +47,7 @@ public static A2AClientException mapRestError(String body, int code) { public static A2AClientException mapRestError(String className, String errorMessage, int code) { return switch (className) { case "io.a2a.spec.TaskNotFoundError" -> new A2AClientException(errorMessage, new TaskNotFoundError()); - case "io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError" -> new A2AClientException(errorMessage, new AuthenticatedExtendedCardNotConfiguredError()); + case "io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError" -> new A2AClientException(errorMessage, new AuthenticatedExtendedCardNotConfiguredError(null, errorMessage, null)); case "io.a2a.spec.ContentTypeNotSupportedError" -> new A2AClientException(errorMessage, new ContentTypeNotSupportedError(null, null, errorMessage)); case "io.a2a.spec.InternalError" -> new A2AClientException(errorMessage, new InternalError(errorMessage)); case "io.a2a.spec.InvalidAgentResponseError" -> new A2AClientException(errorMessage, new InvalidAgentResponseError(null, null, errorMessage)); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 32fd26133..cd13687c5 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -13,7 +13,7 @@ import java.util.function.Consumer; import java.util.logging.Logger; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.util.JsonFormat; @@ -27,6 +27,7 @@ import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.json.JsonUtil; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; @@ -94,7 +95,7 @@ public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable Clie throw new A2AClientException("Failed to send message, wrong response:" + httpResponseBody); } catch (A2AClientException e) { throw e; - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to send message: " + e, e); } } @@ -121,6 +122,8 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer event throw new A2AClientException("Failed to send streaming message request: " + e, e); } catch (InterruptedException e) { throw new A2AClientException("Send streaming message request timed out: " + e, e); + } catch (JsonProcessingException e) { + throw new A2AClientException("Failed to process JSON for streaming message request: " + e, e); } } @@ -424,10 +429,10 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli throw RestErrorMapper.mapRestError(response); } String httpResponseBody = response.body(); - agentCard = Utils.OBJECT_MAPPER.readValue(httpResponseBody, AgentCard.class); + agentCard = JsonUtil.fromJson(httpResponseBody, AgentCard.class); needsExtendedCard = false; return agentCard; - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); } catch (A2AClientError e) { throw new A2AClientException("Failed to get agent card: " + e, e); @@ -451,7 +456,7 @@ private PayloadAndHeaders applyInterceptors(String methodName, @Nullable Message return payloadAndHeaders; } - private String sendPostRequest(String url, PayloadAndHeaders payloadAndHeaders) throws IOException, InterruptedException { + private String sendPostRequest(String url, PayloadAndHeaders payloadAndHeaders) throws IOException, InterruptedException, JsonProcessingException { A2AHttpClient.PostBuilder builder = createPostBuilder(url, payloadAndHeaders); A2AHttpResponse response = builder.post(); if (!response.success()) { diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index 8192346cd..ecdf5c5f3 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -8,7 +8,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; -import com.fasterxml.jackson.databind.ObjectMapper; import io.a2a.A2A; import io.a2a.client.Client; @@ -17,6 +16,7 @@ import io.a2a.client.http.A2ACardResolver; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; +import io.a2a.json.JsonUtil; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.Part; @@ -30,14 +30,13 @@ public class HelloWorldClient { private static final String SERVER_URL = "http://localhost:9999"; private static final String MESSAGE_TEXT = "how much is 10 USD in INR?"; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static void main(String[] args) { try { AgentCard finalAgentCard = null; AgentCard publicAgentCard = new A2ACardResolver("http://localhost:9999").getAgentCard(); System.out.println("Successfully fetched public agent card:"); - System.out.println(OBJECT_MAPPER.writeValueAsString(publicAgentCard)); + System.out.println(JsonUtil.toJson(publicAgentCard)); System.out.println("Using public agent card for client initialization (default)."); finalAgentCard = publicAgentCard; @@ -47,7 +46,7 @@ public static void main(String[] args) { authHeaders.put("Authorization", "Bearer dummy-token-for-extended-card"); AgentCard extendedAgentCard = A2A.getAgentCard(SERVER_URL, "/agent/authenticatedExtendedCard", authHeaders); System.out.println("Successfully fetched authenticated extended agent card:"); - System.out.println(OBJECT_MAPPER.writeValueAsString(extendedAgentCard)); + System.out.println(JsonUtil.toJson(extendedAgentCard)); System.out.println("Using AUTHENTICATED EXTENDED agent card for client initialization."); finalAgentCard = extendedAgentCard; } else { diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index f002256cd..835ffea38 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -9,7 +9,7 @@ import jakarta.persistence.PersistenceContext; import jakarta.transaction.Transactional; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.spec.PushNotificationConfig; import org.slf4j.Logger; diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java index a55e252b5..225936e47 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java @@ -6,9 +6,9 @@ import jakarta.persistence.Table; import jakarta.persistence.Transient; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.spec.PushNotificationConfig; -import io.a2a.util.Utils; @Entity @Table(name = "a2a_push_notification_configs") @@ -46,7 +46,7 @@ public void setConfigJson(String configJson) { public PushNotificationConfig getConfig() throws JsonProcessingException { if (config == null) { - this.config = Utils.unmarshalFrom(configJson, PushNotificationConfig.TYPE_REFERENCE); + this.config = JsonUtil.fromJson(configJson, PushNotificationConfig.class); } return config; } @@ -56,12 +56,12 @@ public void setConfig(PushNotificationConfig config) throws JsonProcessingExcept throw new IllegalArgumentException("Mismatched config id. " + "Expected '" + id.getConfigId() + "'. Got: '" + config.id() + "'"); } - configJson = Utils.OBJECT_MAPPER.writeValueAsString(config); + configJson = JsonUtil.toJson(config); this.config = config; } static JpaPushNotificationConfig createFromConfig(String taskId, PushNotificationConfig config) throws JsonProcessingException { - String json = Utils.OBJECT_MAPPER.writeValueAsString(config); + String json = JsonUtil.toJson(config); JpaPushNotificationConfig jpaPushNotificationConfig = new JpaPushNotificationConfig(new TaskConfigId(taskId, config.id()), json); jpaPushNotificationConfig.config = config; diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index 7e18ca4a8..3f7759153 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -9,7 +9,7 @@ import java.util.List; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.server.events.QueueClosedEvent; import io.a2a.spec.Artifact; import io.a2a.spec.Event; @@ -32,7 +32,7 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import org.junit.jupiter.api.Test; /** @@ -52,12 +52,12 @@ public void testTaskSerialization() throws JsonProcessingException { .build(); // Test serialization as Event - String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalTask); + String json = JsonUtil.toJson(originalTask); assertTrue(json.contains("\"kind\":\"task\""), "JSON should contain task kind"); assertTrue(json.contains("\"id\":\"test-task-123\""), "JSON should contain task ID"); // Test deserialization back to StreamingEventKind - StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedEvent = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(Task.class, deserializedEvent, "Should deserialize to Task"); Task deserializedTask = (Task) deserializedEvent; @@ -67,7 +67,7 @@ public void testTaskSerialization() throws JsonProcessingException { assertEquals(originalTask.getStatus().state(), deserializedTask.getStatus().state()); // Test as StreamingEventKind - StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(Task.class, deserializedAsStreaming, "Should deserialize to Task as StreamingEventKind"); } @@ -83,12 +83,12 @@ public void testMessageSerialization() throws JsonProcessingException { .build(); // Test serialization as Event - String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalMessage); + String json = JsonUtil.toJson(originalMessage); assertTrue(json.contains("\"kind\":\"message\""), "JSON should contain message kind"); assertTrue(json.contains("\"taskId\":\"test-task-789\""), "JSON should contain task ID"); // Test deserialization back to StreamingEventKind - StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedEvent = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(Message.class, deserializedEvent, "Should deserialize to Message"); Message deserializedMessage = (Message) deserializedEvent; @@ -98,7 +98,7 @@ public void testMessageSerialization() throws JsonProcessingException { assertEquals(originalMessage.getParts().size(), deserializedMessage.getParts().size()); // Test as StreamingEventKind - StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(Message.class, deserializedAsStreaming, "Should deserialize to Message as StreamingEventKind"); } @@ -114,13 +114,13 @@ public void testTaskStatusUpdateEventSerialization() throws JsonProcessingExcept .build(); // Test serialization as Event - String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalEvent); + String json = JsonUtil.toJson((Event) originalEvent); assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain status-update kind"); assertTrue(json.contains("\"taskId\":\"test-task-abc\""), "JSON should contain task ID"); assertTrue(json.contains("\"final\":true"), "JSON should contain final flag"); // Test deserialization back to StreamingEventKind - StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedEvent = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(TaskStatusUpdateEvent.class, deserializedEvent, "Should deserialize to TaskStatusUpdateEvent"); TaskStatusUpdateEvent deserializedStatusEvent = (TaskStatusUpdateEvent) deserializedEvent; @@ -131,7 +131,7 @@ public void testTaskStatusUpdateEventSerialization() throws JsonProcessingExcept assertEquals(originalEvent.isFinal(), deserializedStatusEvent.isFinal()); // Test as StreamingEventKind - StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(TaskStatusUpdateEvent.class, deserializedAsStreaming, "Should deserialize to TaskStatusUpdateEvent as StreamingEventKind"); } @@ -147,13 +147,13 @@ public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingExce .build(); // Test serialization as Event - String json = Utils.OBJECT_MAPPER.writeValueAsString((Event) originalEvent); + String json = JsonUtil.toJson((Event) originalEvent); assertTrue(json.contains("\"kind\":\"artifact-update\""), "JSON should contain artifact-update kind"); assertTrue(json.contains("\"taskId\":\"test-task-xyz\""), "JSON should contain task ID"); assertTrue(json.contains("\"test-artifact-123\""), "JSON should contain artifact ID"); // Test deserialization back to StreamingEventKind - StreamingEventKind deserializedEvent = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedEvent = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(TaskArtifactUpdateEvent.class, deserializedEvent, "Should deserialize to TaskArtifactUpdateEvent"); TaskArtifactUpdateEvent deserializedArtifactEvent = (TaskArtifactUpdateEvent) deserializedEvent; @@ -164,7 +164,7 @@ public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingExce assertEquals(originalEvent.getArtifact().name(), deserializedArtifactEvent.getArtifact().name()); // Test as StreamingEventKind - StreamingEventKind deserializedAsStreaming = Utils.OBJECT_MAPPER.readValue(json, StreamingEventKind.class); + StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); assertInstanceOf(TaskArtifactUpdateEvent.class, deserializedAsStreaming, "Should deserialize to TaskArtifactUpdateEvent as StreamingEventKind"); } @@ -186,11 +186,11 @@ public void testJSONRPCErrorSubclassesSerialization() throws JsonProcessingExcep for (JSONRPCError originalError : errors) { // Test serialization - String json = Utils.OBJECT_MAPPER.writeValueAsString(originalError); + String json = JsonUtil.toJson(originalError); assertTrue(json.contains("\"message\""), "JSON should contain error message for " + originalError.getClass().getSimpleName()); // Test deserialization - it's acceptable to deserialize as base JSONRPCError - JSONRPCError deserializedError = Utils.OBJECT_MAPPER.readValue(json, JSONRPCError.class); + JSONRPCError deserializedError = JsonUtil.fromJson(json, JSONRPCError.class); assertNotNull(deserializedError, "Should deserialize successfully for " + originalError.getClass().getSimpleName()); assertEquals(originalError.getMessage(), deserializedError.getMessage(), "Error message should match for " + originalError.getClass().getSimpleName()); assertEquals(originalError.getCode(), deserializedError.getCode(), "Error code should match for " + originalError.getClass().getSimpleName()); @@ -213,14 +213,14 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc ReplicatedEventQueueItem originalReplicatedEvent = new ReplicatedEventQueueItem("replicated-test-task", statusEvent); // Serialize the ReplicatedEventQueueItem - String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); + String json = JsonUtil.toJson(originalReplicatedEvent); assertTrue(json.contains("\"taskId\":\"replicated-test-task\""), "JSON should contain task ID"); assertTrue(json.contains("\"event\""), "JSON should contain event field"); assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain the event kind"); assertFalse(json.contains("\"error\""), "JSON should not contain error field"); // Deserialize the ReplicatedEventQueueItem - ReplicatedEventQueueItem deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + ReplicatedEventQueueItem deserializedReplicatedEvent = JsonUtil.fromJson(json, ReplicatedEventQueueItem.class); assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); // Now we should get the proper type back! @@ -250,14 +250,14 @@ public void testReplicatedEventWithErrorSerialization() throws JsonProcessingExc ReplicatedEventQueueItem originalReplicatedEvent = new ReplicatedEventQueueItem("error-test-task", error); // Serialize the ReplicatedEventQueueItemQueueItem - String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); + String json = JsonUtil.toJson(originalReplicatedEvent); assertTrue(json.contains("\"taskId\":\"error-test-task\""), "JSON should contain task ID"); assertTrue(json.contains("\"error\""), "JSON should contain error field"); assertTrue(json.contains("\"message\""), "JSON should contain error message"); assertFalse(json.contains("\"event\""), "JSON should not contain event field"); // Deserialize the ReplicatedEventQueueItem - ReplicatedEventQueueItem deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + ReplicatedEventQueueItem deserializedReplicatedEvent = JsonUtil.fromJson(json, ReplicatedEventQueueItem.class); assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); // Should get the error back @@ -308,14 +308,14 @@ public void testQueueClosedEventSerialization() throws JsonProcessingException { assertFalse(originalReplicatedEvent.hasError(), "Should not have error"); // Serialize the ReplicatedEventQueueItem - String json = Utils.OBJECT_MAPPER.writeValueAsString(originalReplicatedEvent); + String json = JsonUtil.toJson(originalReplicatedEvent); assertTrue(json.contains("\"taskId\":\"" + taskId + "\""), "JSON should contain task ID"); assertTrue(json.contains("\"closedEvent\":true"), "JSON should contain closedEvent flag set to true"); assertFalse(json.contains("\"event\""), "JSON should not contain event field"); assertFalse(json.contains("\"error\""), "JSON should not contain error field"); // Deserialize the ReplicatedEventQueueItem - ReplicatedEventQueueItem deserializedReplicatedEvent = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + ReplicatedEventQueueItem deserializedReplicatedEvent = JsonUtil.fromJson(json, ReplicatedEventQueueItem.class); assertEquals(taskId, deserializedReplicatedEvent.getTaskId()); // Verify the deserialized item is marked as a closed event diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 3e2da2f51..42454ea3b 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -27,7 +27,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -199,14 +199,14 @@ void testReplicatedEventJsonSerialization() throws Exception { ReplicatedEventQueueItem original = new ReplicatedEventQueueItem("json-test-task", originalEvent); // Serialize to JSON - String json = Utils.OBJECT_MAPPER.writeValueAsString(original); + String json = JsonUtil.toJson(original); assertNotNull(json); assertTrue(json.contains("json-test-task")); assertTrue(json.contains("\"event\":{")); assertTrue(json.contains("\"kind\":\"status-update\"")); // Deserialize back - ReplicatedEventQueueItem deserialized = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + ReplicatedEventQueueItem deserialized = JsonUtil.fromJson(json, ReplicatedEventQueueItem.class); assertNotNull(deserialized); assertEquals("json-test-task", deserialized.getTaskId()); assertNotNull(deserialized.getEvent()); @@ -427,13 +427,13 @@ void testQueueClosedEventJsonSerialization() throws Exception { assertFalse(original.hasError(), "Should not have error"); // Serialize to JSON - String json = Utils.OBJECT_MAPPER.writeValueAsString(original); + String json = JsonUtil.toJson(original); assertNotNull(json); assertTrue(json.contains(taskId), "JSON should contain taskId"); assertTrue(json.contains("\"closedEvent\":true"), "JSON should contain closedEvent flag"); // Deserialize back - ReplicatedEventQueueItem deserialized = Utils.OBJECT_MAPPER.readValue(json, ReplicatedEventQueueItem.class); + ReplicatedEventQueueItem deserialized = JsonUtil.fromJson(json, ReplicatedEventQueueItem.class); assertNotNull(deserialized); assertEquals(taskId, deserialized.getTaskId()); assertTrue(deserialized.isClosedEvent(), "Deserialized should be marked as closed event"); diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java index 04238c27d..26797bc5f 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java @@ -4,10 +4,9 @@ import jakarta.enterprise.event.Event; import jakarta.inject.Inject; -import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.extras.queuemanager.replicated.core.ReplicationStrategy; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; import org.eclipse.microprofile.reactive.messaging.Incoming; @@ -18,7 +17,6 @@ public class ReactiveMessagingReplicationStrategy implements ReplicationStrategy { private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveMessagingReplicationStrategy.class); - private static final TypeReference REPLICATED_EVENT_TYPE_REF = new TypeReference() {}; @Inject @Channel("replicated-events-out") @@ -33,7 +31,7 @@ public void send(String taskId, io.a2a.spec.Event event) { try { ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); - String json = Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); + String json = JsonUtil.toJson(replicatedEvent); emitter.send(json); LOGGER.debug("Successfully sent replicated event for task: {}", taskId); } catch (Exception e) { @@ -47,7 +45,7 @@ public void onReplicatedEvent(String jsonMessage) { LOGGER.debug("Received replicated event JSON: {}", jsonMessage); try { - ReplicatedEventQueueItem replicatedEvent = Utils.unmarshalFrom(jsonMessage, REPLICATED_EVENT_TYPE_REF); + ReplicatedEventQueueItem replicatedEvent = JsonUtil.fromJson(jsonMessage, ReplicatedEventQueueItem.class); LOGGER.debug("Deserialized replicated event for task: {}, event: {}", replicatedEvent.getTaskId(), replicatedEvent.getEvent()); diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java index a10b019db..26976d1e3 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java @@ -19,7 +19,7 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; @ExtendWith(MockitoExtension.class) class ReactiveMessagingReplicationStrategyTest { @@ -54,7 +54,7 @@ private String createValidJsonMessage(String taskId, String contextId) throws Ex .isFinal(false) .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); - return Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); + return JsonUtil.toJson(replicatedEvent); } @Test diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 52f5676d3..08c198281 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -39,7 +39,7 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import io.quarkus.test.junit.QuarkusTest; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; @@ -261,7 +261,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, statusEvent); - String eventJson = Utils.OBJECT_MAPPER.writeValueAsString(replicatedEvent); + String eventJson = JsonUtil.toJson(replicatedEvent); // Send to Kafka using reactive messaging testEmitter.send(eventJson); @@ -361,7 +361,7 @@ public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { // Now manually send a QueueClosedEvent to Kafka to simulate queue closure on another node QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); ReplicatedEventQueueItem replicatedClosedEvent = new ReplicatedEventQueueItem(taskId, closedEvent); - String eventJson = Utils.OBJECT_MAPPER.writeValueAsString(replicatedClosedEvent); + String eventJson = JsonUtil.toJson(replicatedClosedEvent); // Send to Kafka using reactive messaging testEmitter.send(eventJson); diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java index 7571a6ba9..9e308ec42 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java @@ -9,7 +9,7 @@ import org.eclipse.microprofile.reactive.messaging.Incoming; import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import io.quarkus.arc.profile.IfBuildProfile; /** @@ -26,7 +26,7 @@ public class TestKafkaEventConsumer { @Incoming("test-replicated-events-in") public void onTestReplicatedEvent(String jsonMessage) { try { - ReplicatedEventQueueItem event = Utils.OBJECT_MAPPER.readValue(jsonMessage, ReplicatedEventQueueItem.class); + ReplicatedEventQueueItem event = JsonUtil.fromJson(jsonMessage, ReplicatedEventQueueItem.class); receivedEvents.offer(event); // Signal any waiting threads diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 4245cb391..dede9022b 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -16,7 +16,7 @@ import jakarta.persistence.TypedQuery; import jakarta.transaction.Transactional; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.extras.common.events.TaskFinalizedEvent; import io.a2a.server.config.A2AConfigProvider; import io.a2a.server.tasks.TaskStateProvider; diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java index 9f38dee41..7253515ad 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -8,9 +8,9 @@ import jakarta.persistence.Table; import jakarta.persistence.Transient; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.spec.Task; -import io.a2a.util.Utils; @Entity @Table(name = "a2a_tasks") @@ -109,13 +109,13 @@ public void setFinalizedAt(Instant finalizedAt, boolean isFinalState) { public Task getTask() throws JsonProcessingException { if (task == null) { - this.task = Utils.unmarshalFrom(taskJson, Task.TYPE_REFERENCE); + this.task = JsonUtil.fromJson(taskJson, Task.class); } return task; } public void setTask(Task task) throws JsonProcessingException { - taskJson = Utils.OBJECT_MAPPER.writeValueAsString(task); + taskJson = JsonUtil.toJson(task); if (id == null) { id = task.getId(); } @@ -125,7 +125,7 @@ public void setTask(Task task) throws JsonProcessingException { } static JpaTask createFromTask(Task task) throws JsonProcessingException { - String json = Utils.OBJECT_MAPPER.writeValueAsString(task); + String json = JsonUtil.toJson(task); JpaTask jpaTask = new JpaTask(task.getId(), json); jpaTask.task = task; jpaTask.updateDenormalizedFields(task); diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index a7cb8d79d..18d52ed4d 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -187,7 +187,7 @@ public void testTaskWithComplexMetadata() { assertEquals("test-task-5", retrieved.getId()); assertNotNull(retrieved.getMetadata()); assertEquals("value1", retrieved.getMetadata().get("key1")); - assertEquals(42, retrieved.getMetadata().get("key2")); + assertEquals(42, ((Number)retrieved.getMetadata().get("key2")).intValue()); assertEquals(true, retrieved.getMetadata().get("key3")); } diff --git a/pom.xml b/pom.xml index 13b390d00..806123b2e 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ 3.8.0 3.2.4 0.8.0 - 2.17.0 + 2.13.2 4.1.0 2.0.1 2.1.3 @@ -196,20 +196,21 @@ pom import - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} - com.google.protobuf protobuf-java ${protobuf-java.version} + + + com.google.code.gson + gson + + + + + com.google.code.gson + gson + ${gson.version} io.smallrye.reactive @@ -311,7 +312,6 @@ org.jspecify jspecify - diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java index 6c24c5166..ef28ada75 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -24,7 +24,7 @@ import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.transport.grpc.handler.GrpcHandler; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; @Path("/test") @ApplicationScoped @@ -44,7 +44,7 @@ public void init() { @Path("/task") @Consumes(MediaType.APPLICATION_JSON) public Response saveTask(String body) throws Exception { - Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class); + Task task = JsonUtil.fromJson(body, Task.class); testUtilsBean.saveTask(task); return Response.ok().build(); } @@ -57,7 +57,7 @@ public Response getTask(@PathParam("taskId") String taskId) throws Exception { return Response.status(404).build(); } return Response.ok() - .entity(Utils.OBJECT_MAPPER.writeValueAsString(task)) + .entity(JsonUtil.toJson(task)) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) .build(); } @@ -85,7 +85,7 @@ public Response ensureQueue(@PathParam("taskId") String taskId) { @POST @Path("/queue/enqueueTaskStatusUpdateEvent/{taskId}") public Response enqueueTaskStatusUpdateEvent(@PathParam("taskId") String taskId, String body) throws Exception { - TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class); + TaskStatusUpdateEvent event = JsonUtil.fromJson(body, TaskStatusUpdateEvent.class); testUtilsBean.enqueueEvent(taskId, event); return Response.ok().build(); } @@ -93,7 +93,7 @@ public Response enqueueTaskStatusUpdateEvent(@PathParam("taskId") String taskId, @POST @Path("/queue/enqueueTaskArtifactUpdateEvent/{taskId}") public Response enqueueTaskArtifactUpdateEvent(@PathParam("taskId") String taskId, String body) throws Exception { - TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class); + TaskArtifactUpdateEvent event = JsonUtil.fromJson(body, TaskArtifactUpdateEvent.class); testUtilsBean.enqueueEvent(taskId, event); return Response.ok().build(); } @@ -130,7 +130,7 @@ public Response deleteTaskPushNotificationConfig(@PathParam("taskId") String tas @Path("/task/{taskId}") @Consumes(MediaType.APPLICATION_JSON) public Response savePushNotificationConfigInStore(@PathParam("taskId") String taskId, String body) throws Exception { - PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class); + PushNotificationConfig notificationConfig = JsonUtil.fromJson(body, PushNotificationConfig.class); if (notificationConfig == null) { return Response.status(404).build(); } diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 5da6cf045..75ebf8a0e 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -19,12 +19,10 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.io.JsonEOFException; -import com.google.gson.JsonSyntaxException; +import io.a2a.json.JsonProcessingException; import io.a2a.common.A2AHeaders; import io.a2a.grpc.utils.JSONRPCUtils; +import io.a2a.json.JsonMappingException; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; @@ -41,11 +39,8 @@ import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.IdJsonMappingException; import io.a2a.spec.InternalError; -import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidParamsJsonMappingException; -import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.JSONRPCErrorResponse; @@ -55,7 +50,6 @@ import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.ListTasksRequest; import io.a2a.spec.ListTasksResponse; -import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.MethodNotFoundJsonMappingException; import io.a2a.spec.NonStreamingJSONRPCRequest; import io.a2a.spec.SendMessageRequest; @@ -115,9 +109,18 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { } } catch (JSONRPCError e) { error = new JSONRPCErrorResponse(e); + } catch (InvalidParamsJsonMappingException e) { + error = new JSONRPCErrorResponse(e.getId(), new io.a2a.spec.InvalidParamsError(null, e.getMessage(), null)); + } catch (MethodNotFoundJsonMappingException e) { + error = new JSONRPCErrorResponse(e.getId(), new io.a2a.spec.MethodNotFoundError(null, e.getMessage(), null)); + } catch (io.a2a.spec.IdJsonMappingException e) { + error = new JSONRPCErrorResponse(e.getId(), new io.a2a.spec.InvalidRequestError(null, e.getMessage(), null)); + } catch (JsonMappingException e) { + // General JsonMappingException - treat as InvalidRequest + error = new JSONRPCErrorResponse(new io.a2a.spec.InvalidRequestError(null, e.getMessage(), null)); + } catch (com.google.gson.JsonSyntaxException e) { + error = new JSONRPCErrorResponse(new JSONParseError(e.getMessage())); } catch (JsonProcessingException e) { - error = handleError(e); - } catch (JsonSyntaxException e) { error = new JSONRPCErrorResponse(new JSONParseError(e.getMessage())); } catch (Throwable t) { error = new JSONRPCErrorResponse(new InternalError(t.getMessage())); @@ -143,28 +146,6 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { } } - private JSONRPCErrorResponse handleError(JsonProcessingException exception) { - Object id = null; - JSONRPCError jsonRpcError; - if (exception.getCause() instanceof JsonParseException) { - jsonRpcError = new JSONParseError(); - } else if (exception instanceof JsonEOFException) { - jsonRpcError = new JSONParseError(exception.getMessage()); - } else if (exception instanceof MethodNotFoundJsonMappingException err) { - id = err.getId(); - jsonRpcError = new MethodNotFoundError(); - } else if (exception instanceof InvalidParamsJsonMappingException err) { - id = err.getId(); - jsonRpcError = new InvalidParamsError(); - } else if (exception instanceof IdJsonMappingException err) { - id = err.getId(); - jsonRpcError = new InvalidRequestError(); - } else { - jsonRpcError = new InvalidRequestError(); - } - return new JSONRPCErrorResponse(id, jsonRpcError); - } - /** * /** * Handles incoming GET requests to the agent card endpoint. diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java index 3e6be1a31..d3bfca712 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java @@ -15,7 +15,7 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; @@ -43,7 +43,7 @@ public void init() { @Route(path = "/test/task", methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void saveTask(@Body String body, RoutingContext rc) { try { - Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class); + Task task = JsonUtil.fromJson(body, Task.class); testUtilsBean.saveTask(task); rc.response() .setStatusCode(200) @@ -66,7 +66,7 @@ public void getTask(@Param String taskId, RoutingContext rc) { rc.response() .setStatusCode(200) .putHeader(CONTENT_TYPE, APPLICATION_JSON) - .end(Utils.OBJECT_MAPPER.writeValueAsString(task)); + .end(JsonUtil.toJson(task)); } catch (Throwable t) { errorResponse(t, rc); @@ -108,7 +108,7 @@ public void ensureTaskQueue(@Param String taskId, RoutingContext rc) { public void enqueueTaskStatusUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { try { - TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class); + TaskStatusUpdateEvent event = JsonUtil.fromJson(body, TaskStatusUpdateEvent.class); testUtilsBean.enqueueEvent(taskId, event); rc.response() .setStatusCode(200) @@ -122,7 +122,7 @@ public void enqueueTaskStatusUpdateEvent(@Param String taskId, @Body String body public void enqueueTaskArtifactUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { try { - TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class); + TaskArtifactUpdateEvent event = JsonUtil.fromJson(body, TaskArtifactUpdateEvent.class); testUtilsBean.enqueueEvent(taskId, event); rc.response() .setStatusCode(200) @@ -169,7 +169,7 @@ public void deleteTaskPushNotificationConfig(@Param String taskId, @Param String @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void saveTaskPushNotificationConfig(@Param String taskId, @Body String body, RoutingContext rc) { try { - PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class); + PushNotificationConfig notificationConfig = JsonUtil.fromJson(body, PushNotificationConfig.class); if (notificationConfig == null) { rc.response() .setStatusCode(404) diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java index 6cda8dc8f..ba5bd1cfd 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java @@ -17,7 +17,7 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; @@ -45,7 +45,7 @@ public void init() { @Route(path = "/test/task", methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void saveTask(@Body String body, RoutingContext rc) { try { - Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class); + Task task = JsonUtil.fromJson(body, Task.class); testUtilsBean.saveTask(task); rc.response() .setStatusCode(200) @@ -68,7 +68,7 @@ public void getTask(@Param String taskId, RoutingContext rc) { rc.response() .setStatusCode(200) .putHeader(CONTENT_TYPE, APPLICATION_JSON) - .end(Utils.OBJECT_MAPPER.writeValueAsString(task)); + .end(JsonUtil.toJson(task)); } catch (Throwable t) { errorResponse(t, rc); @@ -110,7 +110,7 @@ public void ensureTaskQueue(@Param String taskId, RoutingContext rc) { public void enqueueTaskStatusUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { try { - TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class); + TaskStatusUpdateEvent event = JsonUtil.fromJson(body, TaskStatusUpdateEvent.class); testUtilsBean.enqueueEvent(taskId, event); rc.response() .setStatusCode(200) @@ -124,7 +124,7 @@ public void enqueueTaskStatusUpdateEvent(@Param String taskId, @Body String body public void enqueueTaskArtifactUpdateEvent(@Param String taskId, @Body String body, RoutingContext rc) { try { - TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class); + TaskArtifactUpdateEvent event = JsonUtil.fromJson(body, TaskArtifactUpdateEvent.class); testUtilsBean.enqueueEvent(taskId, event); rc.response() .setStatusCode(200) @@ -171,7 +171,7 @@ public void deleteTaskPushNotificationConfig(@Param String taskId, @Param String @Route(path = "/test/task/:taskId", methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void saveTaskPushNotificationConfig(@Param String taskId, @Body String body, RoutingContext rc) { try { - PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class); + PushNotificationConfig notificationConfig = JsonUtil.fromJson(body, PushNotificationConfig.class); if (notificationConfig == null) { rc.response() .setStatusCode(404) diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index e400b7ba0..fda2ff97b 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -3,6 +3,7 @@ import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON; import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE; import static io.a2a.common.A2AHeaders.X_A2A_NOTIFICATION_TOKEN; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -11,13 +12,13 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.json.JsonUtil; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; -import io.a2a.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,10 +81,7 @@ private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo) String body; try { - body = Utils.OBJECT_MAPPER.writeValueAsString(task); - } catch (JsonProcessingException e) { - LOGGER.debug("Error writing value as string: {}", e.getMessage(), e); - return false; + body = JsonUtil.toJson(task); } catch (Throwable throwable) { LOGGER.debug("Error writing value as string: {}", throwable.getMessage(), throwable); return false; diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 0cf633fff..3e5611d9e 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.spec.A2AError; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; @@ -63,13 +63,13 @@ public void init() { @Test public void testConsumeOneTaskEvent() throws Exception { - Task event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Task event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); enqueueAndConsumeOneEvent(event); } @Test public void testConsumeOneMessageEvent() throws Exception { - Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); enqueueAndConsumeOneEvent(event); } @@ -93,7 +93,7 @@ public void testConsumeOneQueueEmpty() throws A2AServerException { @Test public void testConsumeAllMultipleEvents() throws JsonProcessingException { List events = List.of( - Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE), + Utils.unmarshalFrom(MINIMAL_TASK, Task.class), new TaskArtifactUpdateEvent.Builder() .taskId("task-123") .contextId("session-xyz") @@ -154,7 +154,7 @@ public void onComplete() { @Test public void testConsumeUntilMessage() throws Exception { List events = List.of( - Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE), + Utils.unmarshalFrom(MINIMAL_TASK, Task.class), new TaskArtifactUpdateEvent.Builder() .taskId("task-123") .contextId("session-xyz") @@ -214,7 +214,7 @@ public void onComplete() { @Test public void testConsumeMessageEvents() throws Exception { - Message message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + Message message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); Message message2 = new Message.Builder(message).build(); List events = List.of(message, message2); @@ -393,7 +393,7 @@ public void testConsumeAllHandlesQueueClosedException() throws Exception { EventConsumer consumer = new EventConsumer(queue); // Add a message event (which will complete the stream) - Event message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + Event message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); queue.enqueueEvent(message); // Close the queue before consuming diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 389c21d88..75888f151 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -100,7 +100,7 @@ public void testEnqueueEventPropagagesToChildren() throws Exception { EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue = parentQueue.tap(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); parentQueue.enqueueEvent(event); // Event should be available in both parent and child queues @@ -117,8 +117,8 @@ public void testMultipleChildQueuesReceiveEvents() throws Exception { EventQueue childQueue1 = parentQueue.tap(); EventQueue childQueue2 = parentQueue.tap(); - Event event1 = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); - Event event2 = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + Event event1 = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event2 = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); parentQueue.enqueueEvent(event1); parentQueue.enqueueEvent(event2); @@ -140,7 +140,7 @@ public void testChildQueueDequeueIndependently() throws Exception { EventQueue childQueue1 = parentQueue.tap(); EventQueue childQueue2 = parentQueue.tap(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); parentQueue.enqueueEvent(event); // Dequeue from child1 first @@ -163,7 +163,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { EventQueue childQueue = parentQueue.tap(); // Add events to both parent and child - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); parentQueue.enqueueEvent(event); assertFalse(childQueue.isClosed()); @@ -190,7 +190,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { @Test public void testEnqueueEventWhenClosed() throws Exception { EventQueue queue = EventQueue.builder().build(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); queue.close(); // Close the queue first assertTrue(queue.isClosed()); @@ -220,7 +220,7 @@ public void testDequeueEventWhenClosedAndEmpty() throws Exception { @Test public void testDequeueEventWhenClosedButHasEvents() throws Exception { EventQueue queue = EventQueue.builder().build(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); queue.enqueueEvent(event); queue.close(); // Graceful close - events should remain @@ -236,7 +236,7 @@ public void testDequeueEventWhenClosedButHasEvents() throws Exception { @Test public void testEnqueueAndDequeueEvent() throws Exception { - Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); eventQueue.enqueueEvent(event); Event dequeuedEvent = eventQueue.dequeueEventItem(200).getEvent(); assertSame(event, dequeuedEvent); @@ -244,7 +244,7 @@ public void testEnqueueAndDequeueEvent() throws Exception { @Test public void testDequeueEventNoWait() throws Exception { - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); Event dequeuedEvent = eventQueue.dequeueEventItem(-1).getEvent(); assertSame(event, dequeuedEvent); @@ -305,7 +305,7 @@ public void testEnqueueDifferentEventTypes() throws Exception { */ @Test public void testCloseGracefulSetsFlag() throws Exception { - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); eventQueue.close(false); // Graceful close @@ -318,7 +318,7 @@ public void testCloseGracefulSetsFlag() throws Exception { */ @Test public void testCloseImmediateClearsQueue() throws Exception { - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.TYPE_REFERENCE); + Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); eventQueue.close(true); // Immediate close diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 08e406243..c11330f33 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -38,7 +38,8 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.Event; import io.a2a.spec.TextPart; -import io.a2a.util.Utils; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.quarkus.arc.profile.IfBuildProfile; import java.util.Map; @@ -199,8 +200,9 @@ public PostBuilder body(String body) { @Override public A2AHttpResponse post() throws IOException, InterruptedException { - tasks.add(Utils.OBJECT_MAPPER.readValue(body, Task.TYPE_REFERENCE)); try { + Task task = JsonUtil.fromJson(body, Task.class); + tasks.add(task); return new A2AHttpResponse() { @Override public int status() { @@ -217,8 +219,12 @@ public String body() { return ""; } }; + } catch (JsonProcessingException e) { + throw new IOException("Failed to parse task JSON", e); } finally { - latch.countDown(); + if (latch != null) { + latch.countDown(); + } } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java index bc984ae68..91b30e1af 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java @@ -243,7 +243,7 @@ public void testSendNotificationSuccess() throws Exception { verify(mockPostBuilder).url(config.url()); verify(mockPostBuilder).body(bodyCaptor.capture()); verify(mockPostBuilder).post(); - + // Verify the request body contains the task data String sentBody = bodyCaptor.getValue(); assertTrue(sentBody.contains(task.getId())); diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java index 1ae4174dc..d49f6de4f 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java @@ -19,7 +19,7 @@ public class InMemoryTaskStoreTest { @Test public void testSaveAndGet() throws Exception { InMemoryTaskStore store = new InMemoryTaskStore(); - Task task = Utils.unmarshalFrom(TASK_JSON, Task.TYPE_REFERENCE); + Task task = Utils.unmarshalFrom(TASK_JSON, Task.class); store.save(task); Task retrieved = store.get(task.getId()); assertSame(task, retrieved); @@ -35,7 +35,7 @@ public void testGetNonExistent() throws Exception { @Test public void testDelete() throws Exception { InMemoryTaskStore store = new InMemoryTaskStore(); - Task task = Utils.unmarshalFrom(TASK_JSON, Task.TYPE_REFERENCE); + Task task = Utils.unmarshalFrom(TASK_JSON, Task.class); store.save(task); store.delete(task.getId()); Task retrieved = store.get(task.getId()); diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java index c52a4cf20..9834b0e85 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java @@ -22,7 +22,8 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.common.A2AHeaders; -import io.a2a.util.Utils; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskState; @@ -75,13 +76,13 @@ public A2AHttpResponse post() throws IOException, InterruptedException { if (shouldThrowException) { throw new IOException("Simulated network error"); } - + try { - Task task = Utils.OBJECT_MAPPER.readValue(body, Task.TYPE_REFERENCE); + Task task = JsonUtil.fromJson(body, Task.class); tasks.add(task); urls.add(url); headers.add(new java.util.HashMap<>(requestHeaders)); - + return new A2AHttpResponse() { @Override public int status() { @@ -98,6 +99,8 @@ public String body() { return ""; } }; + } catch (JsonProcessingException e) { + throw new IOException("Failed to parse task JSON", e); } finally { if (latch != null) { latch.countDown(); diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 2edbebaed..0f8e5f16f 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -41,7 +41,7 @@ public class TaskManagerTest { @BeforeEach public void init() throws Exception { - minimalTask = Utils.unmarshalFrom(TASK_JSON, Task.TYPE_REFERENCE); + minimalTask = Utils.unmarshalFrom(TASK_JSON, Task.class); taskStore = new InMemoryTaskStore(); taskManager = new TaskManager(minimalTask.getId(), minimalTask.getContextId(), taskStore, null); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index 01e24c8f6..4702adc19 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -1,8 +1,7 @@ package io.a2a.grpc.utils; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; +import io.a2a.json.JsonMappingException; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; @@ -70,6 +69,7 @@ import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; + /** * Utilities for converting between JSON-RPC 2.0 messages and Protocol Buffer objects. *

    @@ -156,9 +156,11 @@ public class JSONRPCUtils { private static final Logger log = Logger.getLogger(JSONRPCUtils.class.getName()); - private static final Gson GSON = new GsonBuilder().setStrictness(Strictness.STRICT).create(); + private static final Gson GSON = new GsonBuilder() + .setStrictness(Strictness.STRICT) + .create(); - public static JSONRPCRequest parseRequestBody(String body) throws JsonProcessingException { + public static JSONRPCRequest parseRequestBody(String body) throws JsonMappingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); if (!jsonRpc.has("method")) { @@ -238,7 +240,7 @@ private static JSONRPCRequest parseMethodRequest(String version, Object id, S } } - public static StreamResponse parseResponseEvent(String body) throws JsonProcessingException { + public static StreamResponse parseResponseEvent(String body) throws JsonMappingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); String version = getAndValidateJsonrpc(jsonRpc); @@ -252,7 +254,7 @@ public static StreamResponse parseResponseEvent(String body) throws JsonProcessi return builder.build(); } - public static JSONRPCResponse parseResponseBody(String body, String method) throws JsonProcessingException { + public static JSONRPCResponse parseResponseBody(String body, String method) throws JsonMappingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); String version = getAndValidateJsonrpc(jsonRpc); @@ -313,7 +315,7 @@ public static JSONRPCResponse parseResponseBody(String body, String method) t } } - public static JSONRPCResponse parseError(JsonObject error, Object id, String method) throws JsonProcessingException { + public static JSONRPCResponse parseError(JsonObject error, Object id, String method) throws JsonMappingException { JSONRPCError rpcError = processError(error); switch (method) { case GetTaskRequest.METHOD -> { @@ -450,13 +452,13 @@ protected static String getAndValidateJsonrpc(JsonObject jsonRpc) throws JsonMap protected static Object getIdIfPossible(JsonObject jsonRpc) { try { return getAndValidateId(jsonRpc); - } catch (JsonProcessingException e) { + } catch (JsonMappingException e) { // id can't be determined return "UNDETERMINED ID"; } } - protected static Object getAndValidateId(JsonObject jsonRpc) throws JsonProcessingException { + protected static Object getAndValidateId(JsonObject jsonRpc) throws JsonMappingException { Object id = null; if (jsonRpc.has("id")) { if (jsonRpc.get("id").isJsonPrimitive()) { @@ -509,7 +511,7 @@ public static String toJsonRPCResultResponse(Object requestId, com.google.protob if (requestId instanceof String string) { output.name("id").value(string); } else if (requestId instanceof Number number) { - output.name("id").value(number); + output.name("id").value(number.longValue()); } } String resultValue = JsonFormat.printer().omittingInsignificantWhitespace().print(builder); @@ -531,7 +533,7 @@ public static String toJsonRPCErrorResponse(Object requestId, JSONRPCError error if (requestId instanceof String string) { output.name("id").value(string); } else if (requestId instanceof Number number) { - output.name("id").value(number); + output.name("id").value(number.longValue()); } } output.name("error"); diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java index 25eada184..c64ce6711 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import com.google.gson.JsonSyntaxException; import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; diff --git a/spec/pom.xml b/spec/pom.xml index af6a5a023..b1fc0a6f6 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -21,14 +21,9 @@ ${project.groupId} a2a-java-sdk-common - - - com.fasterxml.jackson.core - jackson-databind - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 + com.google.code.gson + gson diff --git a/spec/src/main/java/io/a2a/json/JsonMappingException.java b/spec/src/main/java/io/a2a/json/JsonMappingException.java new file mode 100644 index 000000000..550b7827c --- /dev/null +++ b/spec/src/main/java/io/a2a/json/JsonMappingException.java @@ -0,0 +1,102 @@ +package io.a2a.json; + +import org.jspecify.annotations.Nullable; + +/** + * Exception for JSON mapping errors when converting between JSON and Java objects. + *

    + * This exception serves as a replacement for Jackson's JsonMappingException, allowing + * the A2A Java SDK to remain independent of any specific JSON library implementation. + * It represents errors that occur during the mapping phase of deserialization or + * serialization, such as type mismatches, invalid values, or constraint violations. + *

    + * This exception extends {@link JsonProcessingException} and is used for more specific + * mapping-related errors compared to general parsing errors. + *

    + * Usage example: + *

    {@code
    + * try {
    + *     Task task = JsonUtil.fromJson(json, Task.class);
    + *     if (task.getId() == null) {
    + *         throw new JsonMappingException(null, "Task ID cannot be null");
    + *     }
    + * } catch (JsonProcessingException e) {
    + *     throw new JsonMappingException(null, "Invalid task format: " + e.getMessage(), e);
    + * }
    + * }
    + * + * @see JsonProcessingException for the base exception class + */ +public class JsonMappingException extends JsonProcessingException { + + /** + * Optional reference object that caused the mapping error (e.g., JsonParser or field path). + */ + private final @Nullable Object reference; + + /** + * Constructs a new JsonMappingException with the specified reference and message. + *

    + * The reference parameter can be used to provide context about where the mapping + * error occurred (e.g., a field name, path, or parser reference). It can be null + * if no specific reference is available. + * + * @param reference optional reference object providing context for the error (may be null) + * @param message the detail message explaining the mapping error + */ + public JsonMappingException(@Nullable Object reference, String message) { + super(message); + this.reference = reference; + } + + /** + * Constructs a new JsonMappingException with the specified reference, message, and cause. + *

    + * The reference parameter can be used to provide context about where the mapping + * error occurred (e.g., a field name, path, or parser reference). It can be null + * if no specific reference is available. + * + * @param reference optional reference object providing context for the error (may be null) + * @param message the detail message explaining the mapping error + * @param cause the underlying cause of the mapping error (may be null) + */ + public JsonMappingException(@Nullable Object reference, String message, @Nullable Throwable cause) { + super(message, cause); + this.reference = reference; + } + + /** + * Constructs a new JsonMappingException with the specified message and cause. + *

    + * This constructor is provided for compatibility when no reference object is needed. + * + * @param message the detail message explaining the mapping error + * @param cause the underlying cause of the mapping error (may be null) + */ + public JsonMappingException(String message, @Nullable Throwable cause) { + this(null, message, cause); + } + + /** + * Constructs a new JsonMappingException with the specified message. + *

    + * This constructor is provided for compatibility when no reference object is needed. + * + * @param message the detail message explaining the mapping error + */ + public JsonMappingException(String message) { + this(null, message); + } + + /** + * Returns the reference object that provides context for the mapping error. + *

    + * This may be null if no specific reference was available when the exception + * was created. + * + * @return the reference object, or null if not available + */ + public @Nullable Object getReference() { + return reference; + } +} diff --git a/spec/src/main/java/io/a2a/json/JsonProcessingException.java b/spec/src/main/java/io/a2a/json/JsonProcessingException.java new file mode 100644 index 000000000..9af50b7ce --- /dev/null +++ b/spec/src/main/java/io/a2a/json/JsonProcessingException.java @@ -0,0 +1,55 @@ +package io.a2a.json; + +import org.jspecify.annotations.Nullable; + +/** + * General exception for JSON processing errors during serialization or deserialization. + *

    + * This exception serves as a replacement for Jackson's JsonProcessingException, allowing + * the A2A Java SDK to remain independent of any specific JSON library implementation. + * It can be used with any JSON processing library (Gson, Jackson, etc.). + *

    + * This is the base class for more specific JSON processing exceptions like + * {@link JsonMappingException}. + *

    + * Usage example: + *

    {@code
    + * try {
    + *     String json = gson.toJson(object);
    + * } catch (Exception e) {
    + *     throw new JsonProcessingException("Failed to serialize object", e);
    + * }
    + * }
    + * + * @see JsonMappingException for mapping-specific errors + */ +public class JsonProcessingException extends Exception { + + /** + * Constructs a new JsonProcessingException with the specified message. + * + * @param message the detail message explaining the cause of the exception + */ + public JsonProcessingException(String message) { + super(message); + } + + /** + * Constructs a new JsonProcessingException with the specified message and cause. + * + * @param message the detail message explaining the cause of the exception + * @param cause the underlying cause of the exception (may be null) + */ + public JsonProcessingException(String message, @Nullable Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new JsonProcessingException with the specified cause. + * + * @param cause the underlying cause of the exception + */ + public JsonProcessingException(Throwable cause) { + super(cause); + } +} diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/spec/src/main/java/io/a2a/json/JsonUtil.java new file mode 100644 index 000000000..80f16819e --- /dev/null +++ b/spec/src/main/java/io/a2a/json/JsonUtil.java @@ -0,0 +1,763 @@ +package io.a2a.json; + +import static com.google.gson.stream.JsonToken.BEGIN_ARRAY; +import static com.google.gson.stream.JsonToken.BEGIN_OBJECT; +import static com.google.gson.stream.JsonToken.BOOLEAN; +import static com.google.gson.stream.JsonToken.NULL; +import static com.google.gson.stream.JsonToken.NUMBER; +import static com.google.gson.stream.JsonToken.STRING; +import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; +import com.google.gson.ToNumberPolicy; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.a2a.spec.ContentTypeNotSupportedError; +import io.a2a.spec.DataPart; +import io.a2a.spec.FileContent; +import io.a2a.spec.FilePart; +import io.a2a.spec.FileWithBytes; +import io.a2a.spec.FileWithUri; +import io.a2a.spec.InvalidAgentResponseError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.JSONParseError; +import io.a2a.spec.JSONRPCError; +import io.a2a.spec.Message; +import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.Part; +import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskNotFoundError; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import io.a2a.spec.UnsupportedOperationError; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import org.jspecify.annotations.Nullable; + +import static io.a2a.json.JsonUtil.JSONRPCErrorTypeAdapter.THROWABLE_MARKER_FIELD; + +public class JsonUtil { + + private static GsonBuilder createBaseGsonBuilder() { + return new GsonBuilder() + .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) + .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter()) + .registerTypeHierarchyAdapter(JSONRPCError.class, new JSONRPCErrorTypeAdapter()) + .registerTypeAdapter(TaskState.class, new TaskStateTypeAdapter()) + .registerTypeAdapter(Message.Role.class, new RoleTypeAdapter()) + .registerTypeAdapter(Part.Kind.class, new PartKindTypeAdapter()) + .registerTypeHierarchyAdapter(FileContent.class, new FileContentTypeAdapter()); + } + + /** + * Pre-configured {@link Gson} instance for JSON operations. + *

    + * This mapper is configured with strict parsing mode and all necessary custom TypeAdapters + * for A2A Protocol types including polymorphic types, enums, and date/time types. + *

    + * Used throughout the SDK for consistent JSON serialization and deserialization. + * + * @see GsonFactory#createGson() + */ + public static final Gson OBJECT_MAPPER = createBaseGsonBuilder() + .registerTypeHierarchyAdapter(Part.class, new PartTypeAdapter()) + .registerTypeHierarchyAdapter(StreamingEventKind.class, new StreamingEventKindTypeAdapter()) + .create(); + + public static T fromJson(String json, Class classOfT) throws JsonProcessingException { + try { + return OBJECT_MAPPER.fromJson(json, classOfT); + } catch (JsonSyntaxException e) { + throw new JsonProcessingException("Failed to parse JSON", e); + } + } + + public static T fromJson(String json, Type type) throws JsonProcessingException { + try { + return OBJECT_MAPPER.fromJson(json, type); + } catch (JsonSyntaxException e) { + throw new JsonProcessingException("Failed to parse JSON", e); + } + } + + /** + * Serializes an object to a JSON string using Gson. + *

    + * This method uses the pre-configured {@link #OBJECT_MAPPER} to produce + * JSON representation of the provided object. + * + * @param data the object to serialize + * @return JSON string representation of the object + */ + public static String toJson(Object data) throws JsonProcessingException { + try { + return OBJECT_MAPPER.toJson(data); + } catch (JsonSyntaxException e) { + throw new JsonProcessingException("Failed to generate JSON", e); + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link OffsetDateTime} to/from ISO-8601 format. + *

    + * This adapter ensures that OffsetDateTime instances are serialized to ISO-8601 formatted strings + * (e.g., "2023-10-01T12:00:00.234-05:00") and deserialized from the same format. + * This is necessary because Gson cannot access private fields of java.time classes via reflection + * in Java 17+ due to module system restrictions. + *

    + * The adapter uses {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME} for both serialization and + * deserialization, which ensures proper handling of timezone offsets. + * + * @see OffsetDateTime + * @see DateTimeFormatter#ISO_OFFSET_DATE_TIME + */ + static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, OffsetDateTime value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + } + } + + @Override + public @Nullable + OffsetDateTime read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + String dateTimeString = in.nextString(); + try { + return OffsetDateTime.parse(dateTimeString, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } catch (DateTimeParseException e) { + throw new JsonSyntaxException("Failed to parse OffsetDateTime: " + dateTimeString, e); + } + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link Throwable} and its subclasses. + *

    + * This adapter avoids reflection into {@link Throwable}'s private fields, which is not allowed + * in Java 17+ due to module system restrictions. Instead, it serializes Throwables as simple + * objects containing only the type (fully qualified class name) and message. + *

    + * Serialization: Converts a Throwable to a JSON object with: + *

      + *
    • "type": The fully qualified class name (e.g., "java.lang.IllegalArgumentException")
    • + *
    • "message": The exception message
    • + *
    + *

    + * Deserialization: Reads the JSON and reconstructs the Throwable using reflection to find + * a constructor that accepts a String message parameter. If no such constructor exists or if + * instantiation fails, returns a generic {@link RuntimeException} with the message. + * + * @see Throwable + */ + static class ThrowableTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, Throwable value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + out.beginObject(); + out.name("type").value(value.getClass().getName()); + out.name("message").value(value.getMessage()); + out.name(THROWABLE_MARKER_FIELD).value(true); + out.endObject(); + } + + @Override + public @Nullable + Throwable read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + + String type = null; + String message = null; + + in.beginObject(); + while (in.hasNext()) { + String fieldName = in.nextName(); + switch (fieldName) { + case "type" -> + type = in.nextString(); + case "message" -> + message = in.nextString(); + default -> + in.skipValue(); + } + } + in.endObject(); + + // Try to reconstruct the Throwable + if (type != null) { + try { + Class throwableClass = Class.forName(type); + if (Throwable.class.isAssignableFrom(throwableClass)) { + // Try to find a constructor that takes a String message + try { + var constructor = throwableClass.getConstructor(String.class); + return (Throwable) constructor.newInstance(message); + } catch (NoSuchMethodException e) { + // No String constructor, return a generic RuntimeException + return new RuntimeException(message); + } + } + } catch (Exception e) { + // If we can't reconstruct the exact type, return a generic RuntimeException + return new RuntimeException(message); + } + } + return new RuntimeException(message); + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link JSONRPCError} and its subclasses. + *

    + * This adapter handles polymorphic deserialization based on the error code, creating the + * appropriate subclass instance. + *

    + * The adapter maps error codes to their corresponding error classes: + *

      + *
    • -32700: {@link JSONParseError}
    • + *
    • -32600: {@link InvalidRequestError}
    • + *
    • -32601: {@link MethodNotFoundError}
    • + *
    • -32602: {@link InvalidParamsError}
    • + *
    • -32603: {@link InternalError}
    • + *
    • -32001: {@link TaskNotFoundError}
    • + *
    • -32002: {@link TaskNotCancelableError}
    • + *
    • -32003: {@link PushNotificationNotSupportedError}
    • + *
    • -32004: {@link UnsupportedOperationError}
    • + *
    • -32005: {@link ContentTypeNotSupportedError}
    • + *
    • -32006: {@link InvalidAgentResponseError}
    • + *
    • Other codes: {@link JSONRPCError}
    • + *
    + * + * @see JSONRPCError + */ + static class JSONRPCErrorTypeAdapter extends TypeAdapter { + + private static final ThrowableTypeAdapter THROWABLE_ADAPTER = new ThrowableTypeAdapter(); + static final String THROWABLE_MARKER_FIELD = "__throwable"; + private static final String CODE_FIELD = "code"; + private static final String DATA_FIELD = "data"; + private static final String MESSAGE_FIELD = "message"; + private static final String TYPE_FIELD = "type"; + + @Override + public void write(JsonWriter out, JSONRPCError value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + out.beginObject(); + out.name(CODE_FIELD).value(value.getCode()); + out.name(MESSAGE_FIELD).value(value.getMessage()); + if (value.getData() != null) { + out.name(DATA_FIELD); + // If data is a Throwable, use ThrowableTypeAdapter to avoid reflection issues + if (value.getData() instanceof Throwable throwable) { + THROWABLE_ADAPTER.write(out, throwable); + } else { + // Use Gson to serialize the data field for non-Throwable types + OBJECT_MAPPER.toJson(value.getData(), Object.class, out); + } + } + out.endObject(); + } + + @Override + public @Nullable + JSONRPCError read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + + Integer code = null; + String message = null; + Object data = null; + + in.beginObject(); + while (in.hasNext()) { + String fieldName = in.nextName(); + switch (fieldName) { + case CODE_FIELD -> + code = in.nextInt(); + case MESSAGE_FIELD -> + message = in.nextString(); + case DATA_FIELD -> { + // Read data as a generic object (could be string, number, object, etc.) + data = readDataValue(in); + } + default -> + in.skipValue(); + } + } + in.endObject(); + + // Create the appropriate subclass based on the error code + return createErrorInstance(code, message, data); + } + + /** + * Reads the data field value, which can be of any JSON type. + */ + private @Nullable + Object readDataValue(JsonReader in) throws java.io.IOException { + return switch (in.peek()) { + case STRING -> + in.nextString(); + case NUMBER -> + in.nextDouble(); + case BOOLEAN -> + in.nextBoolean(); + case NULL -> { + in.nextNull(); + yield null; + } + case BEGIN_OBJECT -> { + // Parse as JsonElement to check if it's a Throwable + com.google.gson.JsonElement element = com.google.gson.JsonParser.parseReader(in); + if (element.isJsonObject()) { + com.google.gson.JsonObject obj = element.getAsJsonObject(); + // Check if it has the structure of a serialized Throwable (type + message) + if (obj.has(TYPE_FIELD) && obj.has(MESSAGE_FIELD) && obj.has(THROWABLE_MARKER_FIELD)) { + // Deserialize as Throwable using ThrowableTypeAdapter + yield THROWABLE_ADAPTER.read(new JsonReader(new StringReader(element.toString()))); + } + } + // Otherwise, deserialize as generic object + yield OBJECT_MAPPER.fromJson(element, Object.class); + } + case BEGIN_ARRAY -> + // For arrays, read as raw JSON using Gson + OBJECT_MAPPER.fromJson(in, Object.class); + default -> { + in.skipValue(); + yield null; + } + }; + } + + /** + * Creates the appropriate JSONRPCError subclass based on the error code. + */ + private JSONRPCError createErrorInstance(@Nullable Integer code, @Nullable String message, @Nullable Object data) { + if (code == null) { + throw new JsonSyntaxException("JSONRPCError must have a code field"); + } + + return switch (code) { + case JSON_PARSE_ERROR_CODE -> + new JSONParseError(code, message, data); + case INVALID_REQUEST_ERROR_CODE -> + new InvalidRequestError(code, message, data); + case METHOD_NOT_FOUND_ERROR_CODE -> + new MethodNotFoundError(code, message, data); + case INVALID_PARAMS_ERROR_CODE -> + new InvalidParamsError(code, message, data); + case INTERNAL_ERROR_CODE -> + new io.a2a.spec.InternalError(code, message, data); + case TASK_NOT_FOUND_ERROR_CODE -> + new TaskNotFoundError(code, message, data); + case TASK_NOT_CANCELABLE_ERROR_CODE -> + new TaskNotCancelableError(code, message, data); + case PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE -> + new PushNotificationNotSupportedError(code, message, data); + case UNSUPPORTED_OPERATION_ERROR_CODE -> + new UnsupportedOperationError(code, message, data); + case CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE -> + new ContentTypeNotSupportedError(code, message, data); + case INVALID_AGENT_RESPONSE_ERROR_CODE -> + new InvalidAgentResponseError(code, message, data); + default -> + new JSONRPCError(code, message, data); + }; + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link TaskState} enum. + *

    + * This adapter ensures that TaskState enum values are serialized using their + * wire format string representation (e.g., "completed", "working") rather than + * the Java enum constant name (e.g., "COMPLETED", "WORKING"). + *

    + * For serialization, it uses {@link TaskState#asString()} to get the wire format. + * For deserialization, it uses {@link TaskState#fromString(String)} to parse the + * wire format back to the enum constant. + * + * @see TaskState + * @see TaskState#asString() + * @see TaskState#fromString(String) + */ + static class TaskStateTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, TaskState value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.asString()); + } + } + + @Override + public @Nullable + TaskState read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + String stateString = in.nextString(); + try { + return TaskState.fromString(stateString); + } catch (IllegalArgumentException e) { + throw new JsonSyntaxException("Invalid TaskState: " + stateString, e); + } + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link Message.Role} enum. + *

    + * This adapter ensures that Message.Role enum values are serialized using their + * wire format string representation (e.g., "user", "agent") rather than the Java + * enum constant name (e.g., "USER", "AGENT"). + *

    + * For serialization, it uses {@link Message.Role#asString()} to get the wire format. + * For deserialization, it parses the string to the enum constant. + * + * @see Message.Role + * @see Message.Role#asString() + */ + static class RoleTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, Message.Role value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.asString()); + } + } + + @Override + public Message.@Nullable Role read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + String roleString = in.nextString(); + try { + return switch (roleString) { + case "user" -> + Message.Role.USER; + case "agent" -> + Message.Role.AGENT; + default -> + throw new IllegalArgumentException("Invalid Role: " + roleString); + }; + } catch (IllegalArgumentException e) { + throw new JsonSyntaxException("Invalid Message.Role: " + roleString, e); + } + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link Part.Kind} enum. + *

    + * This adapter ensures that Part.Kind enum values are serialized using their + * wire format string representation (e.g., "text", "file", "data") rather than + * the Java enum constant name (e.g., "TEXT", "FILE", "DATA"). + *

    + * For serialization, it uses {@link Part.Kind#asString()} to get the wire format. + * For deserialization, it parses the string to the enum constant. + * + * @see Part.Kind + * @see Part.Kind#asString() + */ + static class PartKindTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, Part.Kind value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.asString()); + } + } + + @Override + public Part.@Nullable Kind read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + String kindString = in.nextString(); + try { + return switch (kindString) { + case "text" -> + Part.Kind.TEXT; + case "file" -> + Part.Kind.FILE; + case "data" -> + Part.Kind.DATA; + default -> + throw new IllegalArgumentException("Invalid Part.Kind: " + kindString); + }; + } catch (IllegalArgumentException e) { + throw new JsonSyntaxException("Invalid Part.Kind: " + kindString, e); + } + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link Part} and its subclasses. + *

    + * This adapter handles polymorphic deserialization based on the "kind" field, creating the + * appropriate subclass instance (TextPart, FilePart, or DataPart). + *

    + * The adapter uses a two-pass approach: first reads the JSON as a tree to inspect the "kind" + * field, then deserializes to the appropriate concrete type. + * + * @see Part + * @see TextPart + * @see FilePart + * @see DataPart + */ + static class PartTypeAdapter extends TypeAdapter> { + + // Create separate Gson instance without the Part adapter to avoid recursion + private final Gson delegateGson = createBaseGsonBuilder().create(); + + @Override + public void write(JsonWriter out, Part value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + // Delegate to Gson's default serialization for the concrete type + if (value instanceof TextPart textPart) { + delegateGson.toJson(textPart, TextPart.class, out); + } else if (value instanceof FilePart filePart) { + delegateGson.toJson(filePart, FilePart.class, out); + } else if (value instanceof DataPart dataPart) { + delegateGson.toJson(dataPart, DataPart.class, out); + } else { + throw new JsonSyntaxException("Unknown Part subclass: " + value.getClass().getName()); + } + } + + @Override + public @Nullable + Part read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + + // Read the JSON as a tree so we can inspect the "kind" field + com.google.gson.JsonElement jsonElement = com.google.gson.JsonParser.parseReader(in); + if (!jsonElement.isJsonObject()) { + throw new JsonSyntaxException("Part must be a JSON object"); + } + + com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); + com.google.gson.JsonElement kindElement = jsonObject.get("kind"); + if (kindElement == null || !kindElement.isJsonPrimitive()) { + throw new JsonSyntaxException("Part must have a 'kind' field"); + } + + String kind = kindElement.getAsString(); + // Use the delegate Gson to deserialize to the concrete type + return switch (kind) { + case "text" -> + delegateGson.fromJson(jsonElement, TextPart.class); + case "file" -> + delegateGson.fromJson(jsonElement, FilePart.class); + case "data" -> + delegateGson.fromJson(jsonElement, DataPart.class); + default -> + throw new JsonSyntaxException("Unknown Part kind: " + kind); + }; + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link StreamingEventKind} and its implementations. + *

    + * This adapter handles polymorphic deserialization based on the "kind" field, creating the + * appropriate implementation instance (Task, Message, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent). + *

    + * The adapter uses a two-pass approach: first reads the JSON as a tree to inspect the "kind" + * field, then deserializes to the appropriate concrete type. + * + * @see StreamingEventKind + * @see Task + * @see Message + * @see TaskStatusUpdateEvent + * @see TaskArtifactUpdateEvent + */ + static class StreamingEventKindTypeAdapter extends TypeAdapter { + + // Create separate Gson instance without the StreamingEventKind adapter to avoid recursion + private final Gson delegateGson = createBaseGsonBuilder() + .registerTypeHierarchyAdapter(Part.class, new PartTypeAdapter()) + .create(); + + @Override + public void write(JsonWriter out, StreamingEventKind value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + // Delegate to Gson's default serialization for the concrete type + if (value instanceof Task task) { + delegateGson.toJson(task, Task.class, out); + } else if (value instanceof Message message) { + delegateGson.toJson(message, Message.class, out); + } else if (value instanceof TaskStatusUpdateEvent event) { + delegateGson.toJson(event, TaskStatusUpdateEvent.class, out); + } else if (value instanceof TaskArtifactUpdateEvent event) { + delegateGson.toJson(event, TaskArtifactUpdateEvent.class, out); + } else { + throw new JsonSyntaxException("Unknown StreamingEventKind implementation: " + value.getClass().getName()); + } + } + + @Override + public @Nullable + StreamingEventKind read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + + // Read the JSON as a tree so we can inspect the "kind" field + com.google.gson.JsonElement jsonElement = com.google.gson.JsonParser.parseReader(in); + if (!jsonElement.isJsonObject()) { + throw new JsonSyntaxException("StreamingEventKind must be a JSON object"); + } + + com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); + com.google.gson.JsonElement kindElement = jsonObject.get("kind"); + if (kindElement == null || !kindElement.isJsonPrimitive()) { + throw new JsonSyntaxException("StreamingEventKind must have a 'kind' field"); + } + + String kind = kindElement.getAsString(); + // Use the delegate Gson to deserialize to the concrete type + return switch (kind) { + case "task" -> + delegateGson.fromJson(jsonElement, Task.class); + case "message" -> + delegateGson.fromJson(jsonElement, Message.class); + case "status-update" -> + delegateGson.fromJson(jsonElement, TaskStatusUpdateEvent.class); + case "artifact-update" -> + delegateGson.fromJson(jsonElement, TaskArtifactUpdateEvent.class); + default -> + throw new JsonSyntaxException("Unknown StreamingEventKind kind: " + kind); + }; + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link FileContent} and its implementations. + *

    + * This adapter handles polymorphic deserialization for the sealed FileContent interface, + * which permits two implementations: + *

      + *
    • {@link FileWithBytes} - File content embedded as base64-encoded bytes
    • + *
    • {@link FileWithUri} - File content referenced by URI
    • + *
    + *

    + * The adapter distinguishes between the two types by checking for the presence of + * "bytes" or "uri" fields in the JSON object. + * + * @see FileContent + * @see FileWithBytes + * @see FileWithUri + */ + static class FileContentTypeAdapter extends TypeAdapter { + + // Create separate Gson instance without the FileContent adapter to avoid recursion + private final Gson delegateGson = new Gson(); + + @Override + public void write(JsonWriter out, FileContent value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + // Delegate to Gson's default serialization for the concrete type + if (value instanceof FileWithBytes fileWithBytes) { + delegateGson.toJson(fileWithBytes, FileWithBytes.class, out); + } else if (value instanceof FileWithUri fileWithUri) { + delegateGson.toJson(fileWithUri, FileWithUri.class, out); + } else { + throw new JsonSyntaxException("Unknown FileContent implementation: " + value.getClass().getName()); + } + } + + @Override + public @Nullable + FileContent read(JsonReader in) throws java.io.IOException { + if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + in.nextNull(); + return null; + } + + // Read the JSON as a tree to inspect the fields + com.google.gson.JsonElement jsonElement = com.google.gson.JsonParser.parseReader(in); + if (!jsonElement.isJsonObject()) { + throw new JsonSyntaxException("FileContent must be a JSON object"); + } + + com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); + + // Distinguish between FileWithBytes and FileWithUri by checking for "bytes" or "uri" field + if (jsonObject.has("bytes")) { + return delegateGson.fromJson(jsonElement, FileWithBytes.class); + } else if (jsonObject.has("uri")) { + return delegateGson.fromJson(jsonElement, FileWithUri.class); + } else { + throw new JsonSyntaxException("FileContent must have either 'bytes' or 'uri' field"); + } + } + } + +} diff --git a/spec/src/main/java/io/a2a/json/package-info.java b/spec/src/main/java/io/a2a/json/package-info.java new file mode 100644 index 000000000..81fe05a3a --- /dev/null +++ b/spec/src/main/java/io/a2a/json/package-info.java @@ -0,0 +1,8 @@ +/** + * JSON processing exceptions for the A2A Java SDK. + *

    + * This package provides custom exceptions that replace Jackson's JSON processing exceptions, + * allowing the SDK to be independent of any specific JSON library implementation. + */ +@org.jspecify.annotations.NullMarked +package io.a2a.json; diff --git a/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java index e558d672a..06d04605c 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java @@ -17,7 +17,7 @@ *

    {@code
      * try {
      *     AgentCard card = objectMapper.readValue(json, AgentCard.class);
    - * } catch (JsonProcessingException e) {
    + * } catch (io.a2a.json.JsonProcessingException e) {
      *     throw new A2AClientJSONError("Failed to parse agent card", e);
      * }
      * }
    diff --git a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java index 13801bbfb..09175b744 100644 --- a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java +++ b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java @@ -1,7 +1,3 @@ -/* - * Copyright The WildFly Authors - * SPDX-License-Identifier: Apache-2.0 - */ package io.a2a.spec; /** diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index 8819daf26..1eae287b3 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -1,12 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonValue; - import io.a2a.util.Assert; import static io.a2a.spec.APIKeySecurityScheme.API_KEY; @@ -23,9 +16,6 @@ * @see OpenAPI Security Scheme * @see A2A Protocol Specification */ -@JsonTypeName(API_KEY) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class APIKeySecurityScheme implements SecurityScheme { public static final String API_KEY = "apiKey"; @@ -48,12 +38,10 @@ public enum Location { this.location = location; } - @JsonValue public String asString() { return location; } - @JsonCreator public static Location fromString(String location) { switch (location) { case "cookie" -> { @@ -74,9 +62,8 @@ public APIKeySecurityScheme(Location location, String name, String description) this(location, name, description, API_KEY); } - @JsonCreator - public APIKeySecurityScheme(@JsonProperty("location") Location location, @JsonProperty("name") String name, - @JsonProperty("description") String description, @JsonProperty("type") String type) { + public APIKeySecurityScheme(Location location, String name, + String description, String type) { Assert.checkNotNullParam("location", location); Assert.checkNotNullParam("name", name); Assert.checkNotNullParam("type", type); diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java index c35cd39f5..a5c5bc4d8 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java @@ -2,9 +2,6 @@ import java.util.List; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - /** * Defines optional capabilities supported by an agent in the A2A Protocol. *

    @@ -34,8 +31,6 @@ * @see AgentExtension * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AgentCapabilities(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory, List extensions) { diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index cd936abd1..c61586422 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -4,8 +4,6 @@ import java.util.List; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -39,8 +37,6 @@ * @param signatures digital signatures verifying the authenticity of the agent card (optional) * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AgentCard( String name, String description, diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java index 5a5275a65..aecaf1861 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -1,11 +1,8 @@ package io.a2a.spec; +import com.google.gson.annotations.SerializedName; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** @@ -34,9 +31,7 @@ * @see RFC 7515 - JSON Web Signature * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) -public record AgentCardSignature(Map header, @JsonProperty("protected") String protectedHeader, +public record AgentCardSignature(Map header, @SerializedName("protected")String protectedHeader, String signature) { public AgentCardSignature { diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index 0d8aa52a1..d887d9bcc 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonAlias; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -25,8 +22,6 @@ * @see TransportProtocol * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AgentInterface(String protocolBinding, String url) { public AgentInterface { diff --git a/spec/src/main/java/io/a2a/spec/AgentProvider.java b/spec/src/main/java/io/a2a/spec/AgentProvider.java index 2ee796b7d..ec88ad2bc 100644 --- a/spec/src/main/java/io/a2a/spec/AgentProvider.java +++ b/spec/src/main/java/io/a2a/spec/AgentProvider.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -21,8 +19,6 @@ * @see AgentCard * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AgentProvider(String organization, String url) { public AgentProvider { diff --git a/spec/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java index be701dc6f..c6d2ae027 100644 --- a/spec/src/main/java/io/a2a/spec/AgentSkill.java +++ b/spec/src/main/java/io/a2a/spec/AgentSkill.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -41,8 +39,6 @@ * @see AgentCard * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AgentSkill(String id, String name, String description, List tags, List examples, List inputModes, List outputModes, List>> security) { diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java index 763ad37c7..1d1e9338a 100644 --- a/spec/src/main/java/io/a2a/spec/Artifact.java +++ b/spec/src/main/java/io/a2a/spec/Artifact.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -31,8 +29,6 @@ * @param extensions protocol extensions used in this artifact (optional) * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record Artifact(String artifactId, String name, String description, List> parts, Map metadata, List extensions) { diff --git a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java index 1c4611cfa..9f075d862 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java @@ -3,10 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; /** * A2A Protocol error indicating that the agent does not have an authenticated extended card configured. @@ -33,23 +29,15 @@ * @see AgentCard for the base agent card structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class AuthenticatedExtendedCardNotConfiguredError extends JSONRPCError { - @JsonCreator public AuthenticatedExtendedCardNotConfiguredError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE), defaultIfNull(message, "Authenticated Extended Card not configured"), data); } - - public AuthenticatedExtendedCardNotConfiguredError() { - this(null, null, null); - } - } diff --git a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java index d4cf992c6..336281707 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java @@ -2,8 +2,6 @@ import java.util.List; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -24,8 +22,6 @@ * @see SecurityScheme for security scheme definitions * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AuthenticationInfo(List schemes, String credentials) { public AuthenticationInfo { diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index f3435be96..166bd58ba 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -2,8 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; @@ -27,8 +25,6 @@ * @see RFC 6749 - Authorization Code Grant * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record AuthorizationCodeOAuthFlow(String authorizationUrl, String refreshUrl, Map scopes, String tokenUrl) { diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java index d3017a660..b292f028b 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java @@ -4,10 +4,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import io.a2a.util.Assert; @@ -28,15 +24,11 @@ * @see TaskNotCancelableError for the error when cancellation is not possible * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class CancelTaskRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "CancelTask"; - @JsonCreator - public CancelTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { + public CancelTaskRequest(String jsonrpc, Object id, String method, TaskIdParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java index 05b7215e6..e8e0ed346 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response for task cancellation requests. *

    @@ -20,13 +15,10 @@ * @see TaskNotCancelableError for the error when cancellation fails * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) + public final class CancelTaskResponse extends JSONRPCResponse { - @JsonCreator - public CancelTaskResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") Task result, @JsonProperty("error") JSONRPCError error) { + public CancelTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError error) { super(jsonrpc, id, result, error, Task.class); } diff --git a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java index dc5ad21cd..84d323bc6 100644 --- a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java @@ -3,8 +3,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; @@ -28,8 +26,6 @@ * @see RFC 6749 - Client Credentials Grant * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record ClientCredentialsOAuthFlow(String refreshUrl, Map scopes, String tokenUrl) { public ClientCredentialsOAuthFlow { diff --git a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index f97cc5502..b1047e728 100644 --- a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -2,10 +2,6 @@ import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; @@ -42,15 +38,9 @@ * @see MessageSendConfiguration for client content type preferences * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class ContentTypeNotSupportedError extends JSONRPCError { - @JsonCreator - public ContentTypeNotSupportedError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + public ContentTypeNotSupportedError(Integer code, String message, Object data) { super(defaultIfNull(code, CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE), defaultIfNull(message, "Incompatible content types"), data); diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 79fdec7e0..772e38d4b 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -2,14 +2,8 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; -import static io.a2a.spec.DataPart.DATA; /** * Represents a structured data content part within a {@link Message} or {@link Artifact}. @@ -41,9 +35,6 @@ * @see Message * @see Artifact */ -@JsonTypeName(DATA) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class DataPart extends Part> { public static final String DATA = "data"; @@ -55,9 +46,7 @@ public DataPart(Map data) { this(data, null); } - @JsonCreator - public DataPart(@JsonProperty("data") Map data, - @JsonProperty("metadata") Map metadata) { + public DataPart(Map data, Map metadata) { Assert.checkNotNullParam("data", data); this.data = data; this.metadata = metadata; diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index 510b16aad..3813d51b2 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -2,8 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; @@ -19,8 +17,6 @@ * @see DeleteTaskPushNotificationConfigRequest for the request using these parameters * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, Map metadata) { public DeleteTaskPushNotificationConfigParams { diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java index 4f65b0fd6..2fb4b1757 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -2,11 +2,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; import io.a2a.util.Utils; @@ -23,16 +18,11 @@ * @see DeleteTaskPushNotificationConfigParams for the parameter structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class DeleteTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "DeleteTaskPushNotificationConfig"; - @JsonCreator - public DeleteTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, - @JsonProperty("params") DeleteTaskPushNotificationConfigParams params) { + public DeleteTaskPushNotificationConfigRequest(String jsonrpc, Object id, String method, DeleteTaskPushNotificationConfigParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java index 35bbd05df..11c8846b2 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java @@ -1,11 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - /** * JSON-RPC response confirming deletion of a task's push notification configuration. *

    @@ -17,15 +11,9 @@ * @see DeleteTaskPushNotificationConfigRequest for the corresponding request * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonSerialize(using = JSONRPCVoidResponseSerializer.class) public final class DeleteTaskPushNotificationConfigResponse extends JSONRPCResponse { - @JsonCreator - public DeleteTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") Void result, - @JsonProperty("error") JSONRPCError error) { + public DeleteTaskPushNotificationConfigResponse(String jsonrpc, Object id, Void result,JSONRPCError error) { super(jsonrpc, id, result, error, Void.class); } diff --git a/spec/src/main/java/io/a2a/spec/EventKind.java b/spec/src/main/java/io/a2a/spec/EventKind.java index b16e9cb45..7c53cf653 100644 --- a/spec/src/main/java/io/a2a/spec/EventKind.java +++ b/spec/src/main/java/io/a2a/spec/EventKind.java @@ -1,11 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.Message.MESSAGE; -import static io.a2a.spec.Task.TASK; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - /** * Interface for events that can be returned from non-streaming A2A Protocol operations. *

    @@ -25,16 +19,6 @@ * @see StreamingEventKind * @see Event */ -@JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - property = "kind", - visible = true -) -@JsonSubTypes({ - @JsonSubTypes.Type(value = Task.class, name = TASK), - @JsonSubTypes.Type(value = Message.class, name = MESSAGE) -}) public interface EventKind { /** diff --git a/spec/src/main/java/io/a2a/spec/FileContent.java b/spec/src/main/java/io/a2a/spec/FileContent.java index 8b87bf5f9..f258e3eb5 100644 --- a/spec/src/main/java/io/a2a/spec/FileContent.java +++ b/spec/src/main/java/io/a2a/spec/FileContent.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - /** * Sealed interface representing file content in the A2A Protocol. *

    @@ -26,7 +24,6 @@ * @see FileWithBytes * @see FileWithUri */ -@JsonDeserialize(using = FileContentDeserializer.class) public sealed interface FileContent permits FileWithBytes, FileWithUri { /** diff --git a/spec/src/main/java/io/a2a/spec/FileContentDeserializer.java b/spec/src/main/java/io/a2a/spec/FileContentDeserializer.java deleted file mode 100644 index aa763db42..000000000 --- a/spec/src/main/java/io/a2a/spec/FileContentDeserializer.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.a2a.spec; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - -public class FileContentDeserializer extends StdDeserializer { - - public FileContentDeserializer() { - this(null); - } - - public FileContentDeserializer(Class vc) { - super(vc); - } - - @Override - public FileContent deserialize(JsonParser jsonParser, DeserializationContext context) - throws IOException, JsonProcessingException { - JsonNode node = jsonParser.getCodec().readTree(jsonParser); - JsonNode mimeType = node.get("mimeType"); - JsonNode name = node.get("name"); - JsonNode bytes = node.get("bytes"); - if (bytes != null) { - return new FileWithBytes(mimeType != null ? mimeType.asText() : null, - name != null ? name.asText() : null, bytes.asText()); - } else if (node.has("uri")) { - return new FileWithUri(mimeType != null ? mimeType.asText() : null, - name != null ? name.asText() : null, node.get("uri").asText()); - } else { - throw new IOException("Invalid file format: missing 'bytes' or 'uri'"); - } - } -} diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 9e44a0f49..969684d35 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -2,11 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.FilePart.FILE; @@ -42,9 +37,6 @@ * @see FileWithBytes * @see FileWithUri */ -@JsonTypeName(FILE) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class FilePart extends Part { public static final String FILE = "file"; @@ -56,8 +48,7 @@ public FilePart(FileContent file) { this(file, null); } - @JsonCreator - public FilePart(@JsonProperty("file") FileContent file, @JsonProperty("metadata") Map metadata) { + public FilePart(FileContent file, Map metadata) { Assert.checkNotNullParam("file", file); this.file = file; this.metadata = metadata; diff --git a/spec/src/main/java/io/a2a/spec/FileWithBytes.java b/spec/src/main/java/io/a2a/spec/FileWithBytes.java index eeeea9375..b5aef3813 100644 --- a/spec/src/main/java/io/a2a/spec/FileWithBytes.java +++ b/spec/src/main/java/io/a2a/spec/FileWithBytes.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - /** * Represents file content embedded directly as base64-encoded bytes. *

    @@ -27,7 +24,5 @@ * @see FilePart * @see FileWithUri */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record FileWithBytes(String mimeType, String name, String bytes) implements FileContent { } diff --git a/spec/src/main/java/io/a2a/spec/FileWithUri.java b/spec/src/main/java/io/a2a/spec/FileWithUri.java index 54f53c80b..b3fb72363 100644 --- a/spec/src/main/java/io/a2a/spec/FileWithUri.java +++ b/spec/src/main/java/io/a2a/spec/FileWithUri.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - /** * Represents file content referenced by a URI location. *

    @@ -31,8 +28,6 @@ * @see FilePart * @see FileWithBytes */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record FileWithUri(String mimeType, String name, String uri) implements FileContent { } diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java index 9b9ba7724..26369b3ac 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -2,10 +2,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import io.a2a.util.Assert; import io.a2a.util.Utils; @@ -34,15 +30,11 @@ * @see AuthenticatedExtendedCardNotConfiguredError for the error when unsupported * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "GetExtendedAgentCard"; - @JsonCreator - public GetAuthenticatedExtendedCardRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") Void params) { + public GetAuthenticatedExtendedCardRequest(String jsonrpc, Object id, String method, Void params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java index 2802d5648..d02c11393 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response containing an agent's extended card with authenticated details. *

    @@ -21,14 +16,9 @@ * @see AuthenticatedExtendedCardNotConfiguredError for the error when unsupported * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class GetAuthenticatedExtendedCardResponse extends JSONRPCResponse { - @JsonCreator - public GetAuthenticatedExtendedCardResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") AgentCard result, - @JsonProperty("error") JSONRPCError error) { + public GetAuthenticatedExtendedCardResponse(String jsonrpc, Object id, AgentCard result, JSONRPCError error) { super(jsonrpc, id, result, error, AgentCard.class); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index f95e83436..906267cc4 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -2,8 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; @@ -21,8 +19,6 @@ * @see TaskPushNotificationConfig for the returned configuration structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record GetTaskPushNotificationConfigParams(String id, @Nullable String pushNotificationConfigId, @Nullable Map metadata) { public GetTaskPushNotificationConfigParams { diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index e1f66f6c0..6a6d72b2f 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -1,9 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import io.a2a.util.Assert; import io.a2a.util.Utils; @@ -22,15 +18,11 @@ * @see TaskPushNotificationConfig for the returned configuration * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "GetTaskPushNotificationConfig"; - @JsonCreator - public GetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") GetTaskPushNotificationConfigParams params) { + public GetTaskPushNotificationConfigRequest( String jsonrpc, Object id, String method, GetTaskPushNotificationConfigParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java index 76b411c1a..a42cd98b0 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response containing a task's push notification configuration. *

    @@ -18,14 +13,9 @@ * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskPushNotificationConfigResponse extends JSONRPCResponse { - @JsonCreator - public GetTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") TaskPushNotificationConfig result, - @JsonProperty("error") JSONRPCError error) { + public GetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, JSONRPCError error) { super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java index 567b42384..79a15a32c 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java @@ -4,10 +4,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import io.a2a.util.Assert; @@ -25,15 +21,11 @@ * @see Task for the returned task structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "GetTask"; - @JsonCreator - public GetTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") TaskQueryParams params) { + public GetTaskRequest(String jsonrpc, Object id, String method, TaskQueryParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java index 81166b5af..db1f1b147 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response containing requested task information. *

    @@ -19,13 +14,9 @@ * @see TaskNotFoundError for the common error case * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class GetTaskResponse extends JSONRPCResponse { - @JsonCreator - public GetTaskResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") Task result, @JsonProperty("error") JSONRPCError error) { + public GetTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError error) { super(jsonrpc, id, result, error, Task.class); } diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 69a8f0728..4e1ebc308 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -1,11 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.HTTPAuthSecurityScheme.HTTP; @@ -40,9 +34,6 @@ * @see RFC 7235 - HTTP Authentication * @see A2A Protocol Specification */ -@JsonTypeName(HTTP) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class HTTPAuthSecurityScheme implements SecurityScheme { public static final String HTTP = "http"; @@ -55,12 +46,10 @@ public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String descrip this(bearerFormat, scheme, description, HTTP); } - @JsonCreator - public HTTPAuthSecurityScheme(@JsonProperty("bearerFormat") String bearerFormat, @JsonProperty("scheme") String scheme, - @JsonProperty("description") String description, @JsonProperty("type") String type) { + public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String description, String type) { Assert.checkNotNullParam("scheme", scheme); Assert.checkNotNullParam("type", type); - if (! type.equals(HTTP)) { + if (! HTTP.equals(type)) { throw new IllegalArgumentException("Invalid type for HTTPAuthSecurityScheme"); } this.bearerFormat = bearerFormat; diff --git a/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java b/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java index 15e0b07b1..00270f909 100644 --- a/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java +++ b/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java @@ -1,18 +1,18 @@ package io.a2a.spec; -import com.fasterxml.jackson.databind.JsonMappingException; +import io.a2a.json.JsonMappingException; public class IdJsonMappingException extends JsonMappingException { Object id; public IdJsonMappingException(String msg, Object id) { - super(null, msg); + super(msg); this.id = id; } public IdJsonMappingException(String msg, Throwable cause, Object id) { - super(null, msg, cause); + super(msg, cause); this.id = id; } diff --git a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java index a63336ef1..646d470cc 100644 --- a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java @@ -2,8 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; @@ -26,8 +24,6 @@ * @see RFC 6749 - Implicit Grant * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record ImplicitOAuthFlow(String authorizationUrl, String refreshUrl, Map scopes) { public ImplicitOAuthFlow { diff --git a/spec/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java index 6b1714b29..6da5c7634 100644 --- a/spec/src/main/java/io/a2a/spec/InternalError.java +++ b/spec/src/main/java/io/a2a/spec/InternalError.java @@ -3,10 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; /** * JSON-RPC error indicating an internal error occurred on the server. @@ -31,15 +27,9 @@ * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class InternalError extends JSONRPCError { - @JsonCreator - public InternalError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + public InternalError(Integer code, String message, Object data) { super( defaultIfNull(code, INTERNAL_ERROR_CODE), defaultIfNull(message, "Internal Error"), diff --git a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index 60eede9c5..830f6e1a2 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -3,10 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; /** * A2A Protocol error indicating that an agent returned a response not conforming to protocol specifications. @@ -17,10 +13,10 @@ *

    * Common violations: *

      - *
    • Missing required fields in response objects
    • - *
    • Invalid field types or values
    • - *
    • Malformed event stream data
    • - *
    • Response doesn't match declared agent capabilities
    • + *
    • Missing required fields in response objects
    • + *
    • Invalid field types or values
    • + *
    • Malformed event stream data
    • + *
    • Response doesn't match declared agent capabilities
    • *
    *

    * Corresponds to A2A-specific error code {@code -32006}. @@ -41,15 +37,9 @@ * @see SendMessageResponse for message send response * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class InvalidAgentResponseError extends JSONRPCError { - @JsonCreator - public InvalidAgentResponseError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + public InvalidAgentResponseError(Integer code, String message, Object data) { super( defaultIfNull(code, INVALID_AGENT_RESPONSE_ERROR_CODE), defaultIfNull(message, "Invalid agent response"), diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java index 735816eac..83e333de5 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -3,21 +3,16 @@ import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC error indicating that method parameters are invalid or missing required fields. *

    * This error is returned when a JSON-RPC method is called with parameters that fail validation. * Common causes include: *

      - *
    • Missing required parameters
    • - *
    • Parameters of incorrect type
    • - *
    • Parameter values outside acceptable ranges
    • - *
    • Malformed parameter structures
    • + *
    • Missing required parameters
    • + *
    • Parameters of incorrect type
    • + *
    • Parameter values outside acceptable ranges
    • + *
    • Malformed parameter structures
    • *
    *

    * Corresponds to JSON-RPC 2.0 error code {@code -32602}. @@ -35,15 +30,9 @@ * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class InvalidParamsError extends JSONRPCError { - @JsonCreator - public InvalidParamsError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + public InvalidParamsError(Integer code, String message, Object data) { super( defaultIfNull(code, INVALID_PARAMS_ERROR_CODE), defaultIfNull(message, "Invalid parameters"), diff --git a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java index 9535d0857..9ba2429fb 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -3,10 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; /** * JSON-RPC error indicating that the request payload is not a valid JSON-RPC Request object. @@ -14,10 +10,10 @@ * This error is returned when the JSON-RPC request fails structural validation. * Common causes include: *

      - *
    • Missing required JSON-RPC fields (jsonrpc, method, id)
    • - *
    • Invalid JSON-RPC version (must be "2.0")
    • - *
    • Malformed request structure
    • - *
    • Type mismatches in required fields
    • + *
    • Missing required JSON-RPC fields (jsonrpc, method, id)
    • + *
    • Invalid JSON-RPC version (must be "2.0")
    • + *
    • Malformed request structure
    • + *
    • Type mismatches in required fields
    • *
    *

    * Corresponds to JSON-RPC 2.0 error code {@code -32600}. @@ -35,19 +31,13 @@ * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class InvalidRequestError extends JSONRPCError { public InvalidRequestError() { this(null, null, null); } - @JsonCreator - public InvalidRequestError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + public InvalidRequestError(Integer code, String message, Object data) { super( defaultIfNull(code, INVALID_REQUEST_ERROR_CODE), defaultIfNull(message, "Request payload validation error"), diff --git a/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java index ea8aac7ec..8823f9bd3 100644 --- a/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - /** * A simplified error response wrapper for non-JSON-RPC error scenarios. *

    @@ -18,7 +15,5 @@ * @see JSONRPCErrorResponse * @see JSONRPCError */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record JSONErrorResponse(String error) { } diff --git a/spec/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java index ad750ea0a..07b1bf777 100644 --- a/spec/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -1,12 +1,6 @@ package io.a2a.spec; import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import static io.a2a.util.Utils.defaultIfNull; /** @@ -22,7 +16,7 @@ *

    {@code
      * try {
      *     objectMapper.readValue(payload, JSONRPCRequest.class);
    - * } catch (JsonProcessingException e) {
    + * } catch (io.a2a.json.JsonProcessingException e) {
      *     throw new JSONParseError("Malformed JSON: " + e.getMessage());
      * }
      * }
    @@ -32,8 +26,6 @@ * @see InvalidRequestError for structurally valid but invalid requests * @see JSON-RPC 2.0 Error Codes */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class JSONParseError extends JSONRPCError implements A2AError { public JSONParseError() { @@ -44,11 +36,10 @@ public JSONParseError(String message) { this(null, message, null); } - @JsonCreator public JSONParseError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, JSON_PARSE_ERROR_CODE), defaultIfNull(message, "Invalid JSON payload"), diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCError.java b/spec/src/main/java/io/a2a/spec/JSONRPCError.java index 53ae0b05b..998fc443c 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCError.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCError.java @@ -1,11 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.a2a.util.Assert; @@ -39,10 +33,6 @@ * @see JSONRPCErrorResponse * @see JSON-RPC 2.0 Error Object */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(using = JSONRPCErrorDeserializer.class) -@JsonSerialize(using = JSONRPCErrorSerializer.class) -@JsonIgnoreProperties(ignoreUnknown = true) public class JSONRPCError extends Error implements Event, A2AError { private final Integer code; @@ -58,11 +48,7 @@ public class JSONRPCError extends Error implements Event, A2AError { * @param data additional error information, structure defined by the error code (optional) * @throws IllegalArgumentException if code or message is null */ - @JsonCreator - public JSONRPCError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + public JSONRPCError(Integer code, String message, Object data) { super(message); Assert.checkNotNullParam("code", code); Assert.checkNotNullParam("message", message); diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java deleted file mode 100644 index 90c85cf81..000000000 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorDeserializer.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.a2a.spec; - - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - -import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; - -public class JSONRPCErrorDeserializer extends StdDeserializer { - - private static final Map> ERROR_MAP = new HashMap<>(); - - static { - ERROR_MAP.put(JSON_PARSE_ERROR_CODE, JSONParseError::new); - ERROR_MAP.put(INVALID_REQUEST_ERROR_CODE, InvalidRequestError::new); - ERROR_MAP.put(METHOD_NOT_FOUND_ERROR_CODE, MethodNotFoundError::new); - ERROR_MAP.put(INVALID_PARAMS_ERROR_CODE, InvalidParamsError::new); - ERROR_MAP.put(INTERNAL_ERROR_CODE, InternalError::new); - ERROR_MAP.put(PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE, PushNotificationNotSupportedError::new); - ERROR_MAP.put(UNSUPPORTED_OPERATION_ERROR_CODE, UnsupportedOperationError::new); - ERROR_MAP.put(CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE, ContentTypeNotSupportedError::new); - ERROR_MAP.put(INVALID_AGENT_RESPONSE_ERROR_CODE, InvalidAgentResponseError::new); - ERROR_MAP.put(TASK_NOT_CANCELABLE_ERROR_CODE, TaskNotCancelableError::new); - ERROR_MAP.put(TASK_NOT_FOUND_ERROR_CODE, TaskNotFoundError::new); - } - - public JSONRPCErrorDeserializer() { - this(null); - } - - public JSONRPCErrorDeserializer(Class vc) { - super(vc); - } - - @Override - public JSONRPCError deserialize(JsonParser jsonParser, DeserializationContext context) - throws IOException, JsonProcessingException { - JsonNode node = jsonParser.getCodec().readTree(jsonParser); - int code = node.get("code").asInt(); - String message = node.get("message").asText(); - JsonNode dataNode = node.get("data"); - Object data = dataNode != null ? jsonParser.getCodec().treeToValue(dataNode, Object.class) : null; - TriFunction constructor = ERROR_MAP.get(code); - if (constructor != null) { - return constructor.apply(code, message, data); - } else { - return new JSONRPCError(code, message, data); - } - } - - @FunctionalInterface - private interface TriFunction { - R apply(A a, B b, C c); - } -} diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java index 18fbd12ae..445b6f330 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** @@ -25,14 +20,12 @@ * @see JSONRPCResponse * @see JSON-RPC 2.0 Response Object */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class JSONRPCErrorResponse extends JSONRPCResponse { /** * Constructs a JSON-RPC error response with all fields. *

    - * This constructor is used by Jackson for JSON deserialization. + * This constructor is used for JSON deserialization. * * @param jsonrpc the JSON-RPC version (must be "2.0") * @param id the request ID, or null if the ID could not be determined from the request @@ -40,9 +33,7 @@ public final class JSONRPCErrorResponse extends JSONRPCResponse { * @param error the error object describing what went wrong (required) * @throws IllegalArgumentException if error is null */ - @JsonCreator - public JSONRPCErrorResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") Void result, @JsonProperty("error") JSONRPCError error) { + public JSONRPCErrorResponse(String jsonrpc, Object id, Void result, JSONRPCError error) { super(jsonrpc, id, result, error, Void.class); Assert.checkNotNullParam("error", error); } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java deleted file mode 100644 index 87b427548..000000000 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorSerializer.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.a2a.spec; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -public class JSONRPCErrorSerializer extends StdSerializer { - - public JSONRPCErrorSerializer() { - this(null); - } - - public JSONRPCErrorSerializer(Class vc) { - super(vc); - } - - @Override - public void serialize(JSONRPCError value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); - gen.writeNumberField("code", value.getCode()); - gen.writeStringField("message", value.getMessage()); - if (value.getData() != null) { - gen.writeObjectField("data", value.getData()); - } - gen.writeEndObject(); - } -} diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java index 90a7c3b7a..d7305d84d 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java @@ -2,9 +2,6 @@ import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - import io.a2a.util.Assert; /** @@ -32,8 +29,6 @@ * @see JSON-RPC 2.0 Specification * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public abstract sealed class JSONRPCRequest implements JSONRPCMessage permits NonStreamingJSONRPCRequest, StreamingJSONRPCRequest { protected String jsonrpc; diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java deleted file mode 100644 index d9a9ba51b..000000000 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequestDeserializerBase.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.a2a.spec; - -import static io.a2a.util.Utils.OBJECT_MAPPER; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; - -public abstract class JSONRPCRequestDeserializerBase extends StdDeserializer> { - - public JSONRPCRequestDeserializerBase() { - this(null); - } - - public JSONRPCRequestDeserializerBase(Class vc) { - super(vc); - } - - protected T getAndValidateParams(JsonNode paramsNode, JsonParser jsonParser, JsonNode node, Class paramsType) throws JsonMappingException { - if (paramsNode == null) { - return null; - } - try { - return OBJECT_MAPPER.treeToValue(paramsNode, paramsType); - } catch (JsonProcessingException e) { - throw new InvalidParamsJsonMappingException("Invalid params", e, getIdIfPossible(node, jsonParser)); - } - } - - protected String getAndValidateJsonrpc(JsonNode treeNode, JsonParser jsonParser) throws JsonMappingException { - JsonNode jsonrpcNode = treeNode.get("jsonrpc"); - if (jsonrpcNode == null || ! jsonrpcNode.asText().equals(JSONRPCMessage.JSONRPC_VERSION)) { - throw new IdJsonMappingException("Invalid JSON-RPC protocol version", getIdIfPossible(treeNode, jsonParser)); - } - return jsonrpcNode.asText(); - } - - protected String getAndValidateMethod(JsonNode treeNode, JsonParser jsonParser) throws JsonMappingException { - JsonNode methodNode = treeNode.get("method"); - if (methodNode == null) { - throw new IdJsonMappingException("Missing method", getIdIfPossible(treeNode, jsonParser)); - } - String method = methodNode.asText(); - if (! isValidMethodName(method)) { - throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); - } - return method; - } - - protected Object getAndValidateId(JsonNode treeNode, JsonParser jsonParser) throws JsonProcessingException { - JsonNode idNode = treeNode.get("id"); - Object id = null; - if (idNode != null) { - if (idNode.isTextual()) { - id = OBJECT_MAPPER.treeToValue(idNode, String.class); - } else if (idNode.isNumber()) { - id = OBJECT_MAPPER.treeToValue(idNode, Integer.class); - } else { - throw new JsonMappingException(jsonParser, "Invalid id"); - } - } - return id; - } - - protected Object getIdIfPossible(JsonNode treeNode, JsonParser jsonParser) { - try { - return getAndValidateId(treeNode, jsonParser); - } catch (JsonProcessingException e) { - // id can't be determined - return null; - } - } - - protected static boolean isValidMethodName(String methodName) { - return methodName != null && (methodName.equals(CancelTaskRequest.METHOD) - || methodName.equals(GetTaskRequest.METHOD) - || methodName.equals(ListTasksRequest.METHOD) - || methodName.equals(GetTaskPushNotificationConfigRequest.METHOD) - || methodName.equals(SetTaskPushNotificationConfigRequest.METHOD) - || methodName.equals(SubscribeToTaskRequest.METHOD) - || methodName.equals(SendMessageRequest.METHOD) - || methodName.equals(SendStreamingMessageRequest.METHOD) - || methodName.equals(ListTaskPushNotificationConfigRequest.METHOD) - || methodName.equals(DeleteTaskPushNotificationConfigRequest.METHOD) - || methodName.equals(GetAuthenticatedExtendedCardRequest.METHOD)); - - } -} diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 04f6cf4e8..5da4b8980 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -2,16 +2,11 @@ import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - import io.a2a.util.Assert; /** * Represents a JSONRPC response. */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { protected String jsonrpc; diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java b/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java deleted file mode 100644 index 200bc4cd4..000000000 --- a/spec/src/main/java/io/a2a/spec/JSONRPCVoidResponseSerializer.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.a2a.spec; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.databind.type.TypeFactory; - -public class JSONRPCVoidResponseSerializer extends StdSerializer> { - - private static final JSONRPCErrorSerializer JSON_RPC_ERROR_SERIALIZER = new JSONRPCErrorSerializer(); - - public JSONRPCVoidResponseSerializer() { - super(TypeFactory.defaultInstance().constructParametricType(JSONRPCResponse.class, - Void.class)); - } - - @Override - public void serialize(JSONRPCResponse value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); - gen.writeStringField("jsonrpc", value.getJsonrpc()); - gen.writeObjectField("id", value.getId()); - if (value.getError() != null) { - gen.writeFieldName("error"); - JSON_RPC_ERROR_SERIALIZER.serialize(value.getError(), gen, provider); - } else { - gen.writeNullField("result"); - } - gen.writeEndObject(); - } -} diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index ed65ebde6..1f12f9f84 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -2,9 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - import io.a2a.util.Assert; /** @@ -19,8 +16,6 @@ * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record ListTaskPushNotificationConfigParams(String id, Map metadata) { public ListTaskPushNotificationConfigParams { diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java index beb50622c..807231c21 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java @@ -2,11 +2,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; import io.a2a.util.Utils; @@ -23,16 +18,11 @@ * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class ListTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "ListTaskPushNotificationConfig"; - @JsonCreator - public ListTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, - @JsonProperty("params") ListTaskPushNotificationConfigParams params) { + public ListTaskPushNotificationConfigRequest(String jsonrpc, Object id, String method, ListTaskPushNotificationConfigParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java index 7d0eb8611..a8d1cc187 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java @@ -2,11 +2,6 @@ import java.util.List; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response containing all push notification configurations for a task. *

    @@ -19,14 +14,9 @@ * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class ListTaskPushNotificationConfigResponse extends JSONRPCResponse> { - @JsonCreator - public ListTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") List result, - @JsonProperty("error") JSONRPCError error) { + public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, List result, JSONRPCError error) { super(jsonrpc, id, result, error, (Class>) (Class) List.class); } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index beda51345..b481c89c1 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import org.jspecify.annotations.Nullable; import java.time.Instant; @@ -19,8 +17,6 @@ * @param includeArtifacts Whether to include artifacts in the returned tasks (defaults to false) * @param metadata Additional filter properties */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record ListTasksParams( @Nullable String contextId, @Nullable TaskState status, diff --git a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java index 84a18104c..89ef2fc66 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java @@ -4,25 +4,16 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** * A list tasks request. */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class ListTasksRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "ListTask"; - @JsonCreator - public ListTasksRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") ListTasksParams params) { + public ListTasksRequest(String jsonrpc, Object id, String method, ListTasksParams params) { if (jsonrpc == null || jsonrpc.isEmpty()) { throw new IllegalArgumentException("JSON-RPC protocol version cannot be null or empty"); } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResponse.java b/spec/src/main/java/io/a2a/spec/ListTasksResponse.java index d6b6d4e81..b2540ee5d 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksResponse.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksResponse.java @@ -1,20 +1,11 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * The response for a list tasks request. */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class ListTasksResponse extends JSONRPCResponse { - @JsonCreator - public ListTasksResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") ListTasksResult result, @JsonProperty("error") JSONRPCError error) { + public ListTasksResponse(String jsonrpc, Object id, ListTasksResult result, JSONRPCError error) { super(jsonrpc, id, result, error, ListTasksResult.class); } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResult.java b/spec/src/main/java/io/a2a/spec/ListTasksResult.java index 3a02cd2f4..2c1780bde 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksResult.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksResult.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; @@ -15,8 +13,6 @@ * @param pageSize Number of tasks returned in this response * @param nextPageToken Token for retrieving the next page of results (null if no more results) */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record ListTasksResult( List tasks, int totalSize, diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index 728ab7c1e..1dd6e8f9f 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -4,13 +4,6 @@ import java.util.Map; import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonValue; -import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.util.Assert; import static io.a2a.spec.Message.MESSAGE; @@ -34,13 +27,8 @@ * * @see A2A Protocol Specification */ -@JsonTypeName(MESSAGE) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class Message implements EventKind, StreamingEventKind { - public static final TypeReference TYPE_REFERENCE = new TypeReference<>() {}; - public static final String MESSAGE = "message"; private final Role role; private final List> parts; @@ -57,12 +45,11 @@ public Message(Role role, List> parts, String messageId, String contextI this(role, parts, messageId, contextId, taskId, referenceTaskIds, metadata, extensions, MESSAGE); } - @JsonCreator - public Message(@JsonProperty("role") Role role, @JsonProperty("parts") List> parts, - @JsonProperty("messageId") String messageId, @JsonProperty("contextId") String contextId, - @JsonProperty("taskId") String taskId, @JsonProperty("referenceTaskIds") List referenceTaskIds, - @JsonProperty("metadata") Map metadata, @JsonProperty("extensions") List extensions, - @JsonProperty("kind") String kind) { + public Message(Role role, List> parts, + String messageId, String contextId, + String taskId, List referenceTaskIds, + Map metadata, List extensions, + String kind) { Assert.checkNotNullParam("kind", kind); Assert.checkNotNullParam("parts", parts); if (parts.isEmpty()) { @@ -166,7 +153,6 @@ public enum Role { * * @return the role as a string ("user" or "agent") */ - @JsonValue public String asString() { return this.role; } diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index 0142addb6..974ab41ab 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -2,8 +2,6 @@ import java.util.List; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -23,8 +21,6 @@ * @see PushNotificationConfig for push notification options * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record MessageSendConfiguration(List acceptedOutputModes, Integer historyLength, PushNotificationConfig pushNotificationConfig, Boolean blocking) { diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index 106c65b18..252db36c7 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -2,8 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -24,8 +22,6 @@ * @see MessageSendConfiguration for available configuration options * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record MessageSendParams(Message message, MessageSendConfiguration configuration, Map metadata) { diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java index 2207af712..3f1fe36de 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -3,11 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC error indicating that the requested method does not exist or is not available. *

    @@ -27,15 +22,12 @@ * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class MethodNotFoundError extends JSONRPCError { - @JsonCreator public MethodNotFoundError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, METHOD_NOT_FOUND_ERROR_CODE), defaultIfNull(message, "Method not found"), diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index 88589bb7e..a2107b0b2 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.MutualTLSSecurityScheme.MUTUAL_TLS; @@ -32,9 +27,6 @@ * @see RFC 8446 - TLS 1.3 Client Authentication * @see A2A Protocol Specification */ -@JsonTypeName(MUTUAL_TLS) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class MutualTLSSecurityScheme implements SecurityScheme { public static final String MUTUAL_TLS = "mutualTLS"; @@ -49,9 +41,7 @@ public MutualTLSSecurityScheme() { this(null, MUTUAL_TLS); } - @JsonCreator - public MutualTLSSecurityScheme(@JsonProperty("description") String description, - @JsonProperty("type") String type) { + public MutualTLSSecurityScheme(String description, String type) { Assert.checkNotNullParam("type", type); if (!type.equals(MUTUAL_TLS)) { throw new IllegalArgumentException("Invalid type for MutualTLSSecurityScheme"); diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java index 41302136c..dd7a753ab 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java @@ -1,15 +1,8 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - /** * Represents a non-streaming JSON-RPC request. */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonDeserialize(using = NonStreamingJSONRPCRequestDeserializer.class) public abstract sealed class NonStreamingJSONRPCRequest extends JSONRPCRequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest, diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java deleted file mode 100644 index 44d7a3d58..000000000 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequestDeserializer.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.a2a.spec; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; - -public class NonStreamingJSONRPCRequestDeserializer extends JSONRPCRequestDeserializerBase> { - - public NonStreamingJSONRPCRequestDeserializer() { - this(null); - } - - public NonStreamingJSONRPCRequestDeserializer(Class vc) { - super(vc); - } - - @Override - public NonStreamingJSONRPCRequest deserialize(JsonParser jsonParser, DeserializationContext context) - throws IOException, JsonProcessingException { - JsonNode treeNode = jsonParser.getCodec().readTree(jsonParser); - String jsonrpc = getAndValidateJsonrpc(treeNode, jsonParser); - String method = getAndValidateMethod(treeNode, jsonParser); - Object id = getAndValidateId(treeNode, jsonParser); - JsonNode paramsNode = treeNode.get("params"); - - switch (method) { - case GetTaskRequest.METHOD: - return new GetTaskRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, TaskQueryParams.class)); - case CancelTaskRequest.METHOD: - return new CancelTaskRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - case ListTasksRequest.METHOD: - return new ListTasksRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, ListTasksParams.class)); - case SetTaskPushNotificationConfigRequest.METHOD: - return new SetTaskPushNotificationConfigRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, TaskPushNotificationConfig.class)); - case GetTaskPushNotificationConfigRequest.METHOD: - return new GetTaskPushNotificationConfigRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, GetTaskPushNotificationConfigParams.class)); - case SendMessageRequest.METHOD: - return new SendMessageRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); - case ListTaskPushNotificationConfigRequest.METHOD: - return new ListTaskPushNotificationConfigRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, ListTaskPushNotificationConfigParams.class)); - case DeleteTaskPushNotificationConfigRequest.METHOD: - return new DeleteTaskPushNotificationConfigRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, DeleteTaskPushNotificationConfigParams.class)); - case GetAuthenticatedExtendedCardRequest.METHOD: - return new GetAuthenticatedExtendedCardRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, Void.class)); - default: - throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); - } - } -} diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 7a1d972b0..44e8cbb6f 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -1,11 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.OAuth2SecurityScheme.OAUTH2; @@ -24,9 +18,6 @@ * @see OpenAPI Security Scheme * @see A2A Protocol Specification */ -@JsonTypeName(OAUTH2) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class OAuth2SecurityScheme implements SecurityScheme { public static final String OAUTH2 = "oauth2"; @@ -39,9 +30,7 @@ public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2M this(flows, description, oauth2MetadataUrl, OAUTH2); } - @JsonCreator - public OAuth2SecurityScheme(@JsonProperty("flows") OAuthFlows flows, @JsonProperty("description") String description, - @JsonProperty("oauth2MetadataUrl") String oauth2MetadataUrl, @JsonProperty("type") String type) { + public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2MetadataUrl, String type) { Assert.checkNotNullParam("flows", flows); Assert.checkNotNullParam("type", type); if (!type.equals(OAUTH2)) { diff --git a/spec/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java index 6aa704db7..e09b5e095 100644 --- a/spec/src/main/java/io/a2a/spec/OAuthFlows.java +++ b/spec/src/main/java/io/a2a/spec/OAuthFlows.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - /** * Configuration for supported OAuth 2.0 authorization flows. *

    @@ -19,8 +16,6 @@ * @see OpenAPI OAuth Flows Object * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record OAuthFlows(AuthorizationCodeOAuthFlow authorizationCode, ClientCredentialsOAuthFlow clientCredentials, ImplicitOAuthFlow implicit, PasswordOAuthFlow password) { diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 01906bd52..e324d2cb9 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.OpenIdConnectSecurityScheme.OPENID_CONNECT; @@ -33,9 +28,6 @@ * @see OpenID Connect Discovery * @see A2A Protocol Specification */ -@JsonTypeName(OPENID_CONNECT) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class OpenIdConnectSecurityScheme implements SecurityScheme { public static final String OPENID_CONNECT = "openIdConnect"; @@ -47,9 +39,7 @@ public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description) this(openIdConnectUrl, description, OPENID_CONNECT); } - @JsonCreator - public OpenIdConnectSecurityScheme(@JsonProperty("openIdConnectUrl") String openIdConnectUrl, - @JsonProperty("description") String description, @JsonProperty("type") String type) { + public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description, String type) { Assert.checkNotNullParam("type", type); Assert.checkNotNullParam("openIdConnectUrl", openIdConnectUrl); if (!type.equals(OPENID_CONNECT)) { diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index 93297a047..11a246676 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -2,10 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonValue; - /** * Base class for content parts within {@link Message}s and {@link Artifact}s. *

    @@ -27,17 +23,6 @@ * @see Artifact * @see A2A Protocol Specification */ -@JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - property = "kind", - visible = true -) -@JsonSubTypes({ - @JsonSubTypes.Type(value = TextPart.class, name = TextPart.TEXT), - @JsonSubTypes.Type(value = FilePart.class, name = FilePart.FILE), - @JsonSubTypes.Type(value = DataPart.class, name = DataPart.DATA) -}) public abstract class Part { /** * Enum defining the different types of content parts. @@ -69,7 +54,6 @@ public enum Kind { * * @return the kind as a string */ - @JsonValue public String asString() { return this.kind; } diff --git a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java index 55554bbb3..e8b8d0bc5 100644 --- a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java @@ -2,9 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - import io.a2a.util.Assert; /** @@ -26,8 +23,6 @@ * @see RFC 6749 - Resource Owner Password Credentials Grant * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record PasswordOAuthFlow(String refreshUrl, Map scopes, String tokenUrl) { public PasswordOAuthFlow { diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index ef471c087..5041e1cc5 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.util.Assert; /** @@ -28,10 +25,7 @@ * @see MessageSendConfiguration for configuring push notifications on message send * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record PushNotificationConfig(String url, String token, AuthenticationInfo authentication, String id) { - public static final TypeReference TYPE_REFERENCE = new TypeReference<>() {}; public PushNotificationConfig { Assert.checkNotNullParam("url", url); diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index 211fe8b59..0cb7c1850 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -3,11 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * A2A Protocol error indicating that the agent does not support push notifications. *

    @@ -33,19 +28,16 @@ * @see TaskPushNotificationConfig for push notification configuration * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class PushNotificationNotSupportedError extends JSONRPCError { public PushNotificationNotSupportedError() { this(null, null, null); } - @JsonCreator public PushNotificationNotSupportedError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE), defaultIfNull(message, "Push Notification is not supported"), diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index afc617ed2..d956dceb2 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -2,22 +2,6 @@ import static io.a2a.spec.APIKeySecurityScheme.API_KEY; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -@JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - property = "type", - visible = true -) -@JsonSubTypes({ - @JsonSubTypes.Type(value = APIKeySecurityScheme.class, name = API_KEY), - @JsonSubTypes.Type(value = HTTPAuthSecurityScheme.class, name = HTTPAuthSecurityScheme.HTTP), - @JsonSubTypes.Type(value = OAuth2SecurityScheme.class, name = OAuth2SecurityScheme.OAUTH2), - @JsonSubTypes.Type(value = OpenIdConnectSecurityScheme.class, name = OpenIdConnectSecurityScheme.OPENID_CONNECT), - @JsonSubTypes.Type(value = MutualTLSSecurityScheme.class, name = MutualTLSSecurityScheme.MUTUAL_TLS) -}) /** * Base interface for security schemes used to authenticate access to agent endpoints. *

    diff --git a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java index 06ce2d34c..3276dbd71 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java @@ -4,11 +4,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** @@ -35,8 +30,6 @@ * @see Task * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class SendMessageRequest extends NonStreamingJSONRPCRequest { /** @@ -47,7 +40,7 @@ public final class SendMessageRequest extends NonStreamingJSONRPCRequest - * This constructor is used by Jackson for JSON deserialization and validates + * This constructor is used for JSON deserialization and validates * that the method name is exactly "SendMessage". * * @param jsonrpc the JSON-RPC version (must be "2.0") @@ -56,9 +49,7 @@ public final class SendMessageRequest extends NonStreamingJSONRPCRequest { - @JsonCreator - public SendMessageResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") EventKind result, @JsonProperty("error") JSONRPCError error) { + public SendMessageResponse(String jsonrpc, Object id, EventKind result, JSONRPCError error) { super(jsonrpc, id, result, error, EventKind.class); } diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java index fdb259fe7..a47c1125a 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java @@ -2,10 +2,6 @@ import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import io.a2a.util.Assert; import java.util.UUID; @@ -27,15 +23,11 @@ * @see StreamingEventKind for event types in streaming responses * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class SendStreamingMessageRequest extends StreamingJSONRPCRequest { public static final String METHOD = "SendStreamingMessage"; - @JsonCreator - public SendStreamingMessageRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") MessageSendParams params) { + public SendStreamingMessageRequest(String jsonrpc, Object id, String method, MessageSendParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java index f9e4013ea..271511b45 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java @@ -1,11 +1,5 @@ package io.a2a.spec; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response for streaming message initiation requests. *

    @@ -23,13 +17,9 @@ * @see StreamingEventKind for the types of events that can be streamed * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class SendStreamingMessageResponse extends JSONRPCResponse { - @JsonCreator - public SendStreamingMessageResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") StreamingEventKind result, @JsonProperty("error") JSONRPCError error) { + public SendStreamingMessageResponse(String jsonrpc, Object id, StreamingEventKind result, JSONRPCError error) { super(jsonrpc, id, result, error, StreamingEventKind.class); } diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java index 636f17945..0cd14904e 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java @@ -4,11 +4,6 @@ import java.util.UUID; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import io.a2a.util.Assert; /** @@ -25,15 +20,11 @@ * @see PushNotificationConfig for notification endpoint details * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { public static final String METHOD = "SetTaskPushNotificationConfig"; - @JsonCreator - public SetTaskPushNotificationConfigRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") TaskPushNotificationConfig params) { + public SetTaskPushNotificationConfigRequest(String jsonrpc, Object id, String method, TaskPushNotificationConfig params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java index a8d302106..8cc710d54 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * JSON-RPC response confirming push notification configuration for a task. *

    @@ -20,14 +15,9 @@ * @see PushNotificationNotSupportedError for the error when unsupported * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class SetTaskPushNotificationConfigResponse extends JSONRPCResponse { - @JsonCreator - public SetTaskPushNotificationConfigResponse(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("result") TaskPushNotificationConfig result, - @JsonProperty("error") JSONRPCError error) { + public SetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, JSONRPCError error) { super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } diff --git a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java index dcd497211..561cba970 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java +++ b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java @@ -5,9 +5,6 @@ import static io.a2a.spec.TaskArtifactUpdateEvent.ARTIFACT_UPDATE; import static io.a2a.spec.TaskStatusUpdateEvent.STATUS_UPDATE; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - /** * Sealed interface for events that can be emitted during streaming A2A Protocol operations. *

    @@ -37,18 +34,6 @@ * @see EventKind * @see UpdateEvent */ -@JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - property = "kind", - visible = true -) -@JsonSubTypes({ - @JsonSubTypes.Type(value = Task.class, name = TASK), - @JsonSubTypes.Type(value = Message.class, name = MESSAGE), - @JsonSubTypes.Type(value = TaskStatusUpdateEvent.class, name = STATUS_UPDATE), - @JsonSubTypes.Type(value = TaskArtifactUpdateEvent.class, name = ARTIFACT_UPDATE) -}) public sealed interface StreamingEventKind extends Event permits Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent { /** diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java index 0e70127a2..8a18440f7 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java @@ -1,9 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - /** * Base class for JSON-RPC requests that support streaming responses in the A2A Protocol. *

    @@ -31,9 +27,6 @@ * @see StreamingEventKind * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonDeserialize(using = StreamingJSONRPCRequestDeserializer.class) public abstract sealed class StreamingJSONRPCRequest extends JSONRPCRequest permits SubscribeToTaskRequest, SendStreamingMessageRequest { diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java b/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java deleted file mode 100644 index 48e0decf8..000000000 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequestDeserializer.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.a2a.spec; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; - -public class StreamingJSONRPCRequestDeserializer extends JSONRPCRequestDeserializerBase> { - - public StreamingJSONRPCRequestDeserializer() { - this(null); - } - - public StreamingJSONRPCRequestDeserializer(Class vc) { - super(vc); - } - - @Override - public StreamingJSONRPCRequest deserialize(JsonParser jsonParser, DeserializationContext context) - throws IOException, JsonProcessingException { - JsonNode treeNode = jsonParser.getCodec().readTree(jsonParser); - String jsonrpc = getAndValidateJsonrpc(treeNode, jsonParser); - String method = getAndValidateMethod(treeNode, jsonParser); - Object id = getAndValidateId(treeNode, jsonParser); - JsonNode paramsNode = treeNode.get("params"); - - switch (method) { - case SubscribeToTaskRequest.METHOD -> { - return new SubscribeToTaskRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, TaskIdParams.class)); - } - case SendStreamingMessageRequest.METHOD -> { - return new SendStreamingMessageRequest(jsonrpc, id, method, - getAndValidateParams(paramsNode, jsonParser, treeNode, MessageSendParams.class)); - } - default -> throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(treeNode, jsonParser)); - } - } -} diff --git a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java index 7978b3956..a245431d5 100644 --- a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java @@ -2,10 +2,6 @@ import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import io.a2a.util.Assert; import java.util.UUID; @@ -30,15 +26,11 @@ * @see StreamingEventKind for the types of events that can be streamed * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class SubscribeToTaskRequest extends StreamingJSONRPCRequest { public static final String METHOD = "SubscribeToTask"; - @JsonCreator - public SubscribeToTaskRequest(@JsonProperty("jsonrpc") String jsonrpc, @JsonProperty("id") Object id, - @JsonProperty("method") String method, @JsonProperty("params") TaskIdParams params) { + public SubscribeToTaskRequest(String jsonrpc, Object id, String method, TaskIdParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index c3fbd09ce..3cd18fc28 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -3,12 +3,6 @@ import java.util.List; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.core.type.TypeReference; import io.a2a.util.Assert; import static io.a2a.spec.Task.TASK; @@ -43,13 +37,8 @@ * @see Message * @see A2A Protocol Specification */ -@JsonTypeName(TASK) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class Task implements EventKind, StreamingEventKind { - public static final TypeReference TYPE_REFERENCE = new TypeReference<>() {}; - public static final String TASK = "task"; private final String id; private final String contextId; @@ -64,10 +53,9 @@ public Task(String id, String contextId, TaskStatus status, List artif this(id, contextId, status, artifacts, history, metadata, TASK); } - @JsonCreator - public Task(@JsonProperty("id") String id, @JsonProperty("contextId") String contextId, @JsonProperty("status") TaskStatus status, - @JsonProperty("artifacts") List artifacts, @JsonProperty("history") List history, - @JsonProperty("metadata") Map metadata, @JsonProperty("kind") String kind) { + public Task(String id, String contextId, TaskStatus status, + List artifacts, List history, + Map metadata, String kind) { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("contextId", contextId); Assert.checkNotNullParam("status", status); diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index f215fd3a3..a0b3113a0 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -2,11 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.TaskArtifactUpdateEvent.ARTIFACT_UPDATE; @@ -39,9 +34,6 @@ * @see Artifact * @see Task */ -@JsonTypeName(ARTIFACT_UPDATE) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { public static final String ARTIFACT_UPDATE = "artifact-update"; @@ -57,13 +49,7 @@ public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextI this(taskId, artifact, contextId, append, lastChunk, metadata, ARTIFACT_UPDATE); } - @JsonCreator - public TaskArtifactUpdateEvent(@JsonProperty("taskId") String taskId, @JsonProperty("artifact") Artifact artifact, - @JsonProperty("contextId") String contextId, - @JsonProperty("append") Boolean append, - @JsonProperty("lastChunk") Boolean lastChunk, - @JsonProperty("metadata") Map metadata, - @JsonProperty("kind") String kind) { + public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextId, Boolean append, Boolean lastChunk, Map metadata, String kind) { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("artifact", artifact); Assert.checkNotNullParam("contextId", contextId); diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index 573aff655..f954dcaf5 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -2,8 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -19,8 +17,6 @@ * @see SubscribeToTaskRequest for task resubscription * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record TaskIdParams(String id, Map metadata) { public TaskIdParams { diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index 31462d115..fad3c8cb7 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -3,11 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * A2A Protocol error indicating that a task cannot be canceled in its current state. *

    @@ -35,26 +30,23 @@ * @see TaskStatus#state() for current task state * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotCancelableError extends JSONRPCError { public TaskNotCancelableError() { this(null, null, null); } - @JsonCreator public TaskNotCancelableError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, TASK_NOT_CANCELABLE_ERROR_CODE), defaultIfNull(message, "Task cannot be canceled"), data); } - public TaskNotCancelableError(@JsonProperty("message") String message) { + public TaskNotCancelableError(String message) { this(null, message, null); } diff --git a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index 2fdb94023..8c277e2e6 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -1,12 +1,6 @@ package io.a2a.spec; import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import static io.a2a.util.Utils.defaultIfNull; /** @@ -39,20 +33,16 @@ * @see CancelTaskRequest for task cancellation * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class TaskNotFoundError extends JSONRPCError { public TaskNotFoundError() { this(null, null, null); } - @JsonCreator - public TaskNotFoundError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, TASK_NOT_FOUND_ERROR_CODE), defaultIfNull(message, "Task not found"), diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index c44c2b847..8e0d8a406 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; /** @@ -21,8 +19,6 @@ * @see SetTaskPushNotificationConfigRequest for setting push notifications * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record TaskPushNotificationConfig(String taskId, PushNotificationConfig pushNotificationConfig) { public TaskPushNotificationConfig { diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index 92c2453c5..f119bd14b 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -1,8 +1,5 @@ package io.a2a.spec; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; import io.a2a.util.Assert; import java.util.Map; import org.jspecify.annotations.Nullable; @@ -14,9 +11,6 @@ * @param historyLength the maximum number of items of history for the task to include in the response * @param metadata additional properties */ - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record TaskQueryParams(String id, int historyLength, @Nullable Map metadata) { public TaskQueryParams { diff --git a/spec/src/main/java/io/a2a/spec/TaskState.java b/spec/src/main/java/io/a2a/spec/TaskState.java index 6d7647b4f..742e74488 100644 --- a/spec/src/main/java/io/a2a/spec/TaskState.java +++ b/spec/src/main/java/io/a2a/spec/TaskState.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - /** * Defines the lifecycle states of a {@link Task} in the A2A Protocol. *

    @@ -77,12 +74,11 @@ public enum TaskState { /** * Returns the string representation of this task state for JSON serialization. *

    - * This method is used by Jackson to serialize TaskState values to their + * This method is used to serialize TaskState values to their * wire format (e.g., "working", "completed"). * * @return the string representation of this state */ - @JsonValue public String asString() { return state; } @@ -104,14 +100,13 @@ public boolean isFinal(){ /** * Deserializes a string value into a TaskState enum constant. *

    - * This method is used by Jackson to deserialize TaskState values from their + * This method is used to deserialize TaskState values from their * wire format during JSON parsing. * * @param state the string representation of the state * @return the corresponding TaskState enum constant * @throws IllegalArgumentException if the state string is not recognized */ - @JsonCreator public static TaskState fromString(String state) { return switch (state) { case "submitted" -> SUBMITTED; diff --git a/spec/src/main/java/io/a2a/spec/TaskStatus.java b/spec/src/main/java/io/a2a/spec/TaskStatus.java index a31c9e067..98ad686e4 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatus.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatus.java @@ -3,10 +3,6 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; - import io.a2a.util.Assert; /** @@ -41,10 +37,8 @@ * @see Task * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public record TaskStatus(TaskState state, Message message, - @JsonFormat(shape = JsonFormat.Shape.STRING) OffsetDateTime timestamp) { + OffsetDateTime timestamp) { public TaskStatus { Assert.checkNotNullParam("state", state); diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index a4f0c9644..495f9574a 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -1,12 +1,8 @@ package io.a2a.spec; +import com.google.gson.annotations.SerializedName; import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.TaskStatusUpdateEvent.STATUS_UPDATE; @@ -15,15 +11,13 @@ * An event sent by the agent to notify the client of a change in a task's status. * This is typically used in streaming or subscription models. */ -@JsonTypeName(STATUS_UPDATE) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { public static final String STATUS_UPDATE = "status-update"; private final String taskId; private final TaskStatus status; private final String contextId; + @SerializedName("final") private final boolean isFinal; private final Map metadata; private final String kind; @@ -34,10 +28,7 @@ public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, this(taskId, status, contextId, isFinal, metadata, STATUS_UPDATE); } - @JsonCreator - public TaskStatusUpdateEvent(@JsonProperty("taskId") String taskId, @JsonProperty("status") TaskStatus status, - @JsonProperty("contextId") String contextId, @JsonProperty("final") boolean isFinal, - @JsonProperty("metadata") Map metadata, @JsonProperty("kind") String kind) { + public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, boolean isFinal, Map metadata, String kind) { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("status", status); Assert.checkNotNullParam("contextId", contextId); @@ -65,7 +56,6 @@ public String getContextId() { return contextId; } - @JsonProperty("final") public boolean isFinal() { return isFinal; } diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index ddf461d69..8441db90c 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -2,11 +2,6 @@ import java.util.Map; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeName; import io.a2a.util.Assert; import static io.a2a.spec.TextPart.TEXT; @@ -30,9 +25,6 @@ * @see Message * @see Artifact */ -@JsonTypeName(TEXT) -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class TextPart extends Part { public static final String TEXT = "text"; @@ -44,8 +36,7 @@ public TextPart(String text) { this(text, null); } - @JsonCreator - public TextPart(@JsonProperty("text") String text, @JsonProperty("metadata") Map metadata) { + public TextPart(String text, Map metadata) { Assert.checkNotNullParam("text", text); this.text = text; this.metadata = metadata; diff --git a/spec/src/main/java/io/a2a/spec/TransportProtocol.java b/spec/src/main/java/io/a2a/spec/TransportProtocol.java index 24fecefcf..4c852903b 100644 --- a/spec/src/main/java/io/a2a/spec/TransportProtocol.java +++ b/spec/src/main/java/io/a2a/spec/TransportProtocol.java @@ -1,8 +1,5 @@ package io.a2a.spec; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - /** * Enumeration of supported transport protocols for A2A Protocol communication. *

    @@ -65,11 +62,10 @@ public enum TransportProtocol { /** * Returns the string representation of this transport protocol. *

    - * Used for JSON serialization via Jackson's {@code @JsonValue} annotation. + * Used for JSON serialization. * * @return the transport protocol name as a string */ - @JsonValue public String asString() { return transport; } @@ -77,13 +73,12 @@ public String asString() { /** * Parses a string into a {@link TransportProtocol} enum constant. *

    - * Used for JSON deserialization via Jackson's {@code @JsonCreator} annotation. + * Used for JSON deserialization. * * @param transport the transport protocol string (e.g., "JSONRPC", "GRPC", "HTTP+JSON") * @return the corresponding TransportProtocol enum constant * @throws IllegalArgumentException if the transport string is not recognized */ - @JsonCreator public static TransportProtocol fromString(String transport) { return switch (transport) { case "JSONRPC" -> JSONRPC; diff --git a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java index 0086135dc..d95072623 100644 --- a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -3,11 +3,6 @@ import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * A2A Protocol error indicating that the requested operation is not supported by the agent. *

    @@ -36,15 +31,12 @@ * @see MethodNotFoundError for unknown method errors * @see A2A Protocol Specification */ -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonIgnoreProperties(ignoreUnknown = true) public class UnsupportedOperationError extends JSONRPCError { - @JsonCreator public UnsupportedOperationError( - @JsonProperty("code") Integer code, - @JsonProperty("message") String message, - @JsonProperty("data") Object data) { + Integer code, + String message, + Object data) { super( defaultIfNull(code, UNSUPPORTED_OPERATION_ERROR_CODE), defaultIfNull(message, "This operation is not supported"), diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 87230c6af..6128f3a1f 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -2,19 +2,20 @@ import java.util.ArrayList; import java.util.List; -import java.util.logging.Logger; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.gson.Gson; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; -import io.a2a.spec.Part; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.Part; +import java.util.logging.Logger; + + /** * Utility class providing common helper methods for A2A Protocol operations. @@ -25,50 +26,33 @@ *

    * Key capabilities: *

      - *
    • JSON processing with pre-configured {@link ObjectMapper}
    • - *
    • Null-safe value defaults via {@link #defaultIfNull(Object, Object)}
    • - *
    • Artifact streaming support via {@link #appendArtifactToTask(Task, TaskArtifactUpdateEvent, String)}
    • - *
    • Type-safe exception rethrowing via {@link #rethrow(Throwable)}
    • + *
    • JSON processing with pre-configured {@link Gson}
    • + *
    • Null-safe value defaults via {@link #defaultIfNull(Object, Object)}
    • + *
    • Artifact streaming support via {@link #appendArtifactToTask(Task, TaskArtifactUpdateEvent, String)}
    • + *
    • Type-safe exception rethrowing via {@link #rethrow(Throwable)}
    • *
    * - * @see ObjectMapper for JSON processing + * @see Gson for JSON processing * @see TaskArtifactUpdateEvent for streaming artifact updates */ public class Utils { - /** - * Pre-configured Jackson {@link ObjectMapper} for JSON operations. - *

    - * This mapper is configured with: - *

      - *
    • {@link JavaTimeModule} for Java 8 date/time type support
    • - *
    - *

    - * Used throughout the SDK for consistent JSON serialization and deserialization. - */ - public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Logger log = Logger.getLogger(Utils.class.getName()); - static { - // needed for date/time types - OBJECT_MAPPER.registerModule(new JavaTimeModule()); - } - /** - * Deserializes JSON string into a typed object using Jackson. + * Deserializes JSON string into a typed object using Gson. *

    - * This method uses the pre-configured {@link #OBJECT_MAPPER} to parse JSON - * with support for generic types via {@link TypeReference}. + * This method uses the pre-configured {@link #OBJECT_MAPPER} to parse JSON. * * @param the target type * @param data JSON string to deserialize - * @param typeRef type reference specifying the target type (for generic types) + * @param typeRef class reference specifying the target type * @return deserialized object of type T * @throws JsonProcessingException if JSON parsing fails */ - public static T unmarshalFrom(String data, TypeReference typeRef) throws JsonProcessingException { - return OBJECT_MAPPER.readValue(data, typeRef); + public static T unmarshalFrom(String data, Class typeRef) throws JsonProcessingException { + return JsonUtil.fromJson(data, typeRef); } /** @@ -109,15 +93,15 @@ public static void rethrow(Throwable t) throws T { *

    * This method handles streaming artifact updates, supporting both: *

      - *
    • Adding new artifacts to the task
    • - *
    • Replacing existing artifacts (when {@code append=false})
    • - *
    • Appending parts to existing artifacts (when {@code append=true})
    • + *
    • Adding new artifacts to the task
    • + *
    • Replacing existing artifacts (when {@code append=false})
    • + *
    • Appending parts to existing artifacts (when {@code append=true})
    • *
    *

    * The {@code append} flag in the event determines the behavior: *

      - *
    • {@code false} or {@code null}: Replace/add the entire artifact
    • - *
    • {@code true}: Append the new artifact's parts to an existing artifact with matching {@code artifactId}
    • + *
    • {@code false} or {@code null}: Replace/add the entire artifact
    • + *
    • {@code true}: Append the new artifact's parts to an existing artifact with matching {@code artifactId}
    • *
    * * @param task the current task to update @@ -174,7 +158,7 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event // We will ignore this chunk log.warning( String.format("Received append=true for nonexistent artifact index for artifact %s in task %s. Ignoring chunk.", - artifactId, taskId)); + artifactId, taskId)); } return new Task.Builder(task) @@ -183,32 +167,15 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event } - /** - * Serializes an object to a JSON string using Jackson. - *

    - * This method uses the pre-configured {@link #OBJECT_MAPPER} to produce - * JSON representation of the provided object. - * - * @param o the object to serialize - * @return JSON string representation of the object - * @throws RuntimeException if JSON serialization fails (wraps {@link JsonProcessingException}) - */ - public static String toJsonString(Object o) { - try { - return OBJECT_MAPPER.writeValueAsString(o); - } catch (JsonProcessingException ex) { - throw new RuntimeException(ex); - } - } - /** * Get the first defined URL in the supported interaces of the agent card. + * * @param agentCard the agentcard where the interfaces are defined. * @return the first defined URL in the supported interaces of the agent card. * @throws A2AClientException */ public static String getFavoriteInterface(AgentCard agentCard) throws A2AClientException { - if(agentCard.supportedInterfaces() == null || agentCard.supportedInterfaces().isEmpty()) { + if (agentCard.supportedInterfaces() == null || agentCard.supportedInterfaces().isEmpty()) { throw new A2AClientException("No server interface available in the AgentCard"); } return agentCard.supportedInterfaces().get(0).url(); diff --git a/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java b/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java index 5fb704bcc..684f8a256 100644 --- a/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java +++ b/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java @@ -1,8 +1,5 @@ package io.a2a.spec; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.util.List; @@ -21,10 +18,13 @@ import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; + + public class JSONRPCErrorSerializationTest { @Test - public void shouldDeserializeToCorrectJSONRPCErrorSubclass() { - ObjectMapper objectMapper = new ObjectMapper(); + public void shouldDeserializeToCorrectJSONRPCErrorSubclass() throws JsonProcessingException { String jsonTemplate = """ {"code": %s, "message": "error", "data": "anything"} """; @@ -47,12 +47,7 @@ record ErrorCase(int code, Class clazz) {} for (ErrorCase errorCase : cases) { String json = jsonTemplate.formatted(errorCase.code()); - JSONRPCError error; - try { - error = objectMapper.readValue(json, JSONRPCError.class); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } + JSONRPCError error = JsonUtil.fromJson(json, JSONRPCError.class); assertInstanceOf(errorCase.clazz(), error); assertEquals("error", error.getMessage()); assertEquals("anything", error.getData().toString()); diff --git a/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java b/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java deleted file mode 100644 index 63d5e32fe..000000000 --- a/spec/src/test/java/io/a2a/spec/SubTypeSerializationTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package io.a2a.spec; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SubTypeSerializationTest { - - private static final Task MINIMAL_TASK = new Task.Builder() - .id("task-123") - .contextId("session-xyz") - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - private static final TypeReference> MAP_TYPE_REFERENCE = new TypeReference<>() { - }; - - private static final ObjectMapper OBJECT_MAPPER; - - static { - OBJECT_MAPPER = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addAbstractTypeMapping(Map.class, SingleKeyHashMap.class); - OBJECT_MAPPER.registerModule(module); - OBJECT_MAPPER.registerModule(new JavaTimeModule()); - } - - @ParameterizedTest - @MethodSource("serializationTestCases") - void testSubtypeSerialization(Object objectToSerialize, String typePropertyName, String expectedTypeValue) throws JsonProcessingException { - Map map = OBJECT_MAPPER.readValue(OBJECT_MAPPER.writeValueAsString(objectToSerialize), - MAP_TYPE_REFERENCE); - assertEquals(expectedTypeValue, map.get(typePropertyName)); - } - - private static Stream serializationTestCases() { - return Stream.of( - Arguments.of( - new TaskStatusUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) - .build(), "kind", TaskStatusUpdateEvent.STATUS_UPDATE - ), - Arguments.of( - new TaskArtifactUpdateEvent.Builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) - .artifact(new Artifact.Builder() - .artifactId("11") - .parts(new TextPart("text")) - .build()) - .build(), "kind", TaskArtifactUpdateEvent.ARTIFACT_UPDATE - ), - Arguments.of( - MINIMAL_TASK, "kind", Task.TASK - ), - Arguments.of( - new Message.Builder() - .role(Message.Role.USER) - .parts(new TextPart("tell me some jokes")) - .contextId("context-1234") - .messageId("message-1234") - .build(), "kind", Message.MESSAGE - ), - Arguments.of( - new TextPart("text"), "kind", TextPart.TEXT - ), - Arguments.of( - new FilePart(new FileWithUri( - "image/jpeg", null, "file:///path/to/image.jpg")), - "kind", FilePart.FILE - ), - Arguments.of( - new DataPart(Map.of("chartType", "bar")), "kind", DataPart.DATA - ), - Arguments.of( - new APIKeySecurityScheme.Builder() - .location(APIKeySecurityScheme.Location.HEADER).name("name").description("description").build(), - "type", APIKeySecurityScheme.API_KEY - ), - Arguments.of( - new HTTPAuthSecurityScheme.Builder() - .scheme("basic").description("Basic Auth").build(), - "type", HTTPAuthSecurityScheme.HTTP - ), - Arguments.of( - new OAuth2SecurityScheme.Builder() - .flows(new OAuthFlows.Builder().build()) - .description("oAuth2SecurityScheme").build(), - "type", OAuth2SecurityScheme.OAUTH2 - ), - Arguments.of( - new OpenIdConnectSecurityScheme.Builder() - .openIdConnectUrl("https://accounts.google.com/.well-known/openid-configuration") - .description("OpenId").build(), - "type", OpenIdConnectSecurityScheme.OPENID_CONNECT - ), - Arguments.of( - new MutualTLSSecurityScheme("mutual tls test"), - "type", MutualTLSSecurityScheme.MUTUAL_TLS - ) - ); - } - - private static class SingleKeyHashMap extends HashMap { - @Override - public V put(K key, V value) { - if (containsKey(key)) { - throw new IllegalArgumentException("duplicate key " + key - + " with value " + get(key) + " and new value " + value); - } - return super.put(key, value); - } - } - -} diff --git a/spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java b/spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java deleted file mode 100644 index fa67b59f5..000000000 --- a/spec/src/test/java/io/a2a/spec/TaskDeserializationTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.a2a.spec; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class TaskDeserializationTest { - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Test - void testTaskWithMissingHistoryAndArtifacts() throws Exception { - // JSON without history and artifacts fields (common server response) - String json = """ - { - "id": "task-123", - "contextId": "context-456", - "status": { - "state": "completed" - }, - "kind": "task" - } - """; - - Task task = objectMapper.readValue(json, Task.class); - - assertNotNull(task.getHistory(), "history should not be null"); - assertNotNull(task.getArtifacts(), "artifacts should not be null"); - - assertTrue(task.getHistory().isEmpty(), "history should be empty list when not provided"); - assertTrue(task.getArtifacts().isEmpty(), "artifacts should be empty list when not provided"); - } - - @Test - void testTaskWithExplicitNullValues() throws Exception { - // JSON with explicit null values - String json = """ - { - "id": "task-123", - "contextId": "context-456", - "status": { - "state": "completed" - }, - "history": null, - "artifacts": null, - "kind": "task" - } - """; - - Task task = objectMapper.readValue(json, Task.class); - - // Should never be null even with explicit null in JSON - assertNotNull(task.getHistory(), "history should not be null even when JSON contains null"); - assertNotNull(task.getArtifacts(), "artifacts should not be null even when JSON contains null"); - - assertTrue(task.getHistory().isEmpty()); - assertTrue(task.getArtifacts().isEmpty()); - } - - @Test - void testTaskWithPopulatedArrays() throws Exception { - String json = """ - { - "id": "task-123", - "contextId": "context-456", - "status": { - "state": "completed" - }, - "history": [ - { - "role": "user", - "parts": [{"kind": "text", "text": "hello"}], - "messageId": "msg-1", - "kind": "message" - } - ], - "artifacts": [], - "kind": "task" - } - """; - - Task task = objectMapper.readValue(json, Task.class); - - assertNotNull(task.getHistory()); - assertEquals(1, task.getHistory().size()); - - assertNotNull(task.getArtifacts()); - assertTrue(task.getArtifacts().isEmpty()); - } -} diff --git a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java new file mode 100644 index 000000000..cdef4f113 --- /dev/null +++ b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java @@ -0,0 +1,713 @@ +package io.a2a.spec; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; + +/** + * Tests for Task serialization and deserialization using Gson. + */ +class TaskSerializationTest { + + @Test + void testBasicTaskSerialization() throws JsonProcessingException { + // Create a basic task + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + // Serialize to JSON + String json = JsonUtil.toJson(task); + + // Verify JSON contains expected fields + assertNotNull(json); + assertTrue(json.contains("\"id\":\"task-123\"")); + assertTrue(json.contains("\"state\":\"submitted\"")); + + // Deserialize back to Task + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify deserialized task matches original + assertEquals(task.getId(), deserialized.getId()); + assertEquals(task.getStatus().state(), deserialized.getStatus().state()); + } + + @Test + void testTaskWithTimestamp() throws JsonProcessingException { + OffsetDateTime timestamp = OffsetDateTime.now(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.WORKING, null, timestamp)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify OffsetDateTime timestamp is preserved + assertNotNull(deserialized.getStatus().timestamp()); + assertEquals(task.getStatus().timestamp(), deserialized.getStatus().timestamp()); + } + + @Test + void testTaskWithArtifacts() throws JsonProcessingException { + Artifact artifact = new Artifact.Builder() + .artifactId("artifact-1") + .name("Test Artifact") + .description("Description of artifact") + .parts(List.of( + new TextPart("Hello"), + new TextPart("World") + )) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED)) + .artifacts(List.of(artifact)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify JSON contains artifact data + assertTrue(json.contains("\"artifactId\":\"artifact-1\"")); + assertTrue(json.contains("Hello")); + assertTrue(json.contains("World")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify artifacts are preserved + assertNotNull(deserialized.getArtifacts()); + assertEquals(1, deserialized.getArtifacts().size()); + assertEquals("artifact-1", deserialized.getArtifacts().get(0).artifactId()); + assertEquals(2, deserialized.getArtifacts().get(0).parts().size()); + } + + @Test + void testTaskWithHistory() throws JsonProcessingException { + Message message = Message.builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("Test message"))) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.WORKING)) + .history(List.of(message)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify JSON contains history data + assertTrue(json.contains("\"role\":\"user\"")); + assertTrue(json.contains("Test message")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify history is preserved + assertNotNull(deserialized.getHistory()); + assertEquals(1, deserialized.getHistory().size()); + assertEquals(Message.Role.USER, deserialized.getHistory().get(0).getRole()); + assertEquals(1, deserialized.getHistory().get(0).getParts().size()); + } + + @Test + void testTaskWithAllFields() throws JsonProcessingException { + OffsetDateTime timestamp = OffsetDateTime.now(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-789") + .status(new TaskStatus(TaskState.WORKING, null, timestamp)) + .history(List.of( + Message.builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("User message"))) + .build(), + Message.builder() + .role(Message.Role.AGENT) + .parts(List.of(new TextPart("Agent response"))) + .build() + )) + .artifacts(List.of( + new Artifact.Builder() + .artifactId("artifact-1") + .parts(List.of(new TextPart("Artifact content"))) + .build() + )) + .metadata(Map.of("key1", "value1", "key2", 42)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify all fields are preserved + assertEquals(task.getId(), deserialized.getId()); + assertEquals(task.getContextId(), deserialized.getContextId()); + assertEquals(task.getStatus().state(), deserialized.getStatus().state()); + assertEquals(task.getStatus().timestamp(), deserialized.getStatus().timestamp()); + assertEquals(task.getHistory().size(), deserialized.getHistory().size()); + assertEquals(task.getArtifacts().size(), deserialized.getArtifacts().size()); + assertNotNull(deserialized.getMetadata()); + assertEquals("value1", deserialized.getMetadata().get("key1")); + } + + @Test + void testTaskWithDifferentStates() throws JsonProcessingException { + for (TaskState state : TaskState.values()) { + Task task = Task.builder() + .id("task-" + state.asString()) + .contextId("context-123") + .status(new TaskStatus(state)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify state is serialized correctly + assertTrue(json.contains("\"state\":\"" + state.asString() + "\"")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify state is preserved + assertEquals(state, deserialized.getStatus().state()); + } + } + + @Test + void testTaskWithNullOptionalFields() throws JsonProcessingException { + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.SUBMITTED)) + // artifacts, history, metadata not set + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify required fields are present + assertEquals("task-123", deserialized.getId()); + assertEquals("context-456", deserialized.getContextId()); + assertEquals(TaskState.SUBMITTED, deserialized.getStatus().state()); + + // Verify optional lists default to empty + assertNotNull(deserialized.getArtifacts()); + assertEquals(0, deserialized.getArtifacts().size()); + assertNotNull(deserialized.getHistory()); + assertEquals(0, deserialized.getHistory().size()); + } + + @Test + void testTaskWithFilePartBytes() throws JsonProcessingException { + FilePart filePart = new FilePart(new FileWithBytes("application/pdf", "document.pdf", "base64data")); + + Artifact artifact = new Artifact.Builder() + .artifactId("file-artifact") + .parts(List.of(filePart)) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED)) + .artifacts(List.of(artifact)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify JSON contains file part data + assertTrue(json.contains("\"kind\":\"file\"")); + assertTrue(json.contains("document.pdf")); + assertTrue(json.contains("application/pdf")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify file part is preserved + Part part = deserialized.getArtifacts().get(0).parts().get(0); + assertTrue(part instanceof FilePart); + FilePart deserializedFilePart = (FilePart) part; + assertTrue(deserializedFilePart.getFile() instanceof FileWithBytes); + FileWithBytes fileWithBytes = (FileWithBytes) deserializedFilePart.getFile(); + assertEquals("document.pdf", fileWithBytes.name()); + assertEquals("application/pdf", fileWithBytes.mimeType()); + } + + @Test + void testTaskWithFilePartUri() throws JsonProcessingException { + FilePart filePart = new FilePart(new FileWithUri("image/png", "photo.png", "https://example.com/photo.png")); + + Artifact artifact = new Artifact.Builder() + .artifactId("uri-artifact") + .parts(List.of(filePart)) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED)) + .artifacts(List.of(artifact)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify JSON contains URI + assertTrue(json.contains("https://example.com/photo.png")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify file part URI is preserved + Part part = deserialized.getArtifacts().get(0).parts().get(0); + assertTrue(part instanceof FilePart); + FilePart deserializedFilePart = (FilePart) part; + assertTrue(deserializedFilePart.getFile() instanceof FileWithUri); + FileWithUri fileWithUri = (FileWithUri) deserializedFilePart.getFile(); + assertEquals("https://example.com/photo.png", fileWithUri.uri()); + } + + @Test + void testTaskWithDataPart() throws JsonProcessingException { + DataPart dataPart = new DataPart(Map.of("temperature", 22.5, "humidity", 65)); + + Artifact artifact = new Artifact.Builder() + .artifactId("data-artifact") + .parts(List.of(dataPart)) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED)) + .artifacts(List.of(artifact)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify JSON contains data part + assertTrue(json.contains("\"kind\":\"data\"")); + assertTrue(json.contains("temperature")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify data part is preserved + Part part = deserialized.getArtifacts().get(0).parts().get(0); + assertTrue(part instanceof DataPart); + DataPart deserializedDataPart = (DataPart) part; + assertNotNull(deserializedDataPart.getData()); + } + + @Test + void testTaskRoundTrip() throws JsonProcessingException { + // Create a comprehensive task with all part types + OffsetDateTime timestamp = OffsetDateTime.now(); + + Task original = Task.builder() + .id("task-123") + .contextId("context-789") + .status(new TaskStatus(TaskState.WORKING, null, timestamp)) + .history(List.of( + Message.builder() + .role(Message.Role.USER) + .parts(List.of( + new TextPart("Text"), + new FilePart(new FileWithBytes("text/plain", "file.txt", "data")), + new DataPart(Map.of("key", "value")) + )) + .build() + )) + .artifacts(List.of( + new Artifact.Builder() + .artifactId("artifact-1") + .parts(List.of(new TextPart("Content"))) + .build() + )) + .metadata(Map.of("meta1", "value1")) + .build(); + + // Serialize to JSON + String json = JsonUtil.toJson(original); + + // Deserialize back to Task + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Serialize again + String json2 = JsonUtil.toJson(deserialized); + + // Deserialize again + Task deserialized2 = JsonUtil.fromJson(json2, Task.class); + + // Verify multiple round-trips produce identical results + assertEquals(deserialized.getId(), deserialized2.getId()); + assertEquals(deserialized.getContextId(), deserialized2.getContextId()); + assertEquals(deserialized.getStatus().state(), deserialized2.getStatus().state()); + assertEquals(deserialized.getHistory().size(), deserialized2.getHistory().size()); + assertEquals(deserialized.getArtifacts().size(), deserialized2.getArtifacts().size()); + } + + @Test + void testTaskStatusWithMessage() throws JsonProcessingException { + Message statusMessage = Message.builder() + .role(Message.Role.AGENT) + .parts(List.of(new TextPart("Processing complete"))) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED, statusMessage, null)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Verify JSON contains status message + assertTrue(json.contains("\"state\":\"completed\"")); + assertTrue(json.contains("Processing complete")); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify status message is preserved + assertEquals(TaskState.COMPLETED, deserialized.getStatus().state()); + assertNotNull(deserialized.getStatus().message()); + assertEquals(Message.Role.AGENT, deserialized.getStatus().message().getRole()); + assertTrue(deserialized.getStatus().message().getParts().get(0) instanceof TextPart); + } + + @Test + void testDeserializeTaskFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "submitted" + }, + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + assertEquals("context-456", task.getContextId()); + assertEquals(TaskState.SUBMITTED, task.getStatus().state()); + assertNull(task.getStatus().message()); + // TaskStatus automatically sets timestamp to current time if not provided + assertNotNull(task.getStatus().timestamp()); + } + + @Test + void testDeserializeTaskWithArtifactsFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "artifact-1", + "name": "Result", + "parts": [ + { + "kind": "text", + "text": "Hello World" + } + ] + } + ], + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + assertEquals(TaskState.COMPLETED, task.getStatus().state()); + assertEquals(1, task.getArtifacts().size()); + assertEquals("artifact-1", task.getArtifacts().get(0).artifactId()); + assertEquals("Result", task.getArtifacts().get(0).name()); + assertEquals(1, task.getArtifacts().get(0).parts().size()); + assertTrue(task.getArtifacts().get(0).parts().get(0) instanceof TextPart); + assertEquals("Hello World", ((TextPart) task.getArtifacts().get(0).parts().get(0)).getText()); + } + + @Test + void testDeserializeTaskWithFilePartBytesFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "file-artifact", + "parts": [ + { + "kind": "file", + "file": { + "mimeType": "application/pdf", + "name": "document.pdf", + "bytes": "base64encodeddata" + } + } + ] + } + ], + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + assertEquals(1, task.getArtifacts().size()); + Part part = task.getArtifacts().get(0).parts().get(0); + assertTrue(part instanceof FilePart); + FilePart filePart = (FilePart) part; + assertTrue(filePart.getFile() instanceof FileWithBytes); + FileWithBytes fileWithBytes = (FileWithBytes) filePart.getFile(); + assertEquals("application/pdf", fileWithBytes.mimeType()); + assertEquals("document.pdf", fileWithBytes.name()); + assertEquals("base64encodeddata", fileWithBytes.bytes()); + } + + @Test + void testDeserializeTaskWithFilePartUriFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "uri-artifact", + "parts": [ + { + "kind": "file", + "file": { + "mimeType": "image/png", + "name": "photo.png", + "uri": "https://example.com/photo.png" + } + } + ] + } + ], + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + Part part = task.getArtifacts().get(0).parts().get(0); + assertTrue(part instanceof FilePart); + FilePart filePart = (FilePart) part; + assertTrue(filePart.getFile() instanceof FileWithUri); + FileWithUri fileWithUri = (FileWithUri) filePart.getFile(); + assertEquals("image/png", fileWithUri.mimeType()); + assertEquals("photo.png", fileWithUri.name()); + assertEquals("https://example.com/photo.png", fileWithUri.uri()); + } + + @Test + void testDeserializeTaskWithDataPartFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "artifacts": [ + { + "artifactId": "data-artifact", + "parts": [ + { + "kind": "data", + "data": { + "temperature": 22.5, + "humidity": 65 + } + } + ] + } + ], + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + Part part = task.getArtifacts().get(0).parts().get(0); + assertTrue(part instanceof DataPart); + DataPart dataPart = (DataPart) part; + assertNotNull(dataPart.getData()); + } + + @Test + void testDeserializeTaskWithHistoryFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "working" + }, + "history": [ + { + "role": "user", + "parts": [ + { + "kind": "text", + "text": "User message" + } + ] + }, + { + "role": "agent", + "parts": [ + { + "kind": "text", + "text": "Agent response" + } + ] + } + ], + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + assertEquals(2, task.getHistory().size()); + assertEquals(Message.Role.USER, task.getHistory().get(0).getRole()); + assertEquals(Message.Role.AGENT, task.getHistory().get(1).getRole()); + assertTrue(task.getHistory().get(0).getParts().get(0) instanceof TextPart); + assertEquals("User message", ((TextPart) task.getHistory().get(0).getParts().get(0)).getText()); + } + + @Test + void testDeserializeTaskWithTimestampFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "working", + "timestamp": "2023-10-01T12:00:00.234-05:00" + }, + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + assertEquals(TaskState.WORKING, task.getStatus().state()); + assertNotNull(task.getStatus().timestamp()); + assertEquals("2023-10-01T12:00:00.234-05:00", task.getStatus().timestamp().toString()); + } + + @Test + void testDeserializeTaskWithMetadataFromJson() throws JsonProcessingException { + String json = """ + { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "completed" + }, + "metadata": { + "key1": "value1", + "key2": 42 + }, + "kind": "task" + } + """; + + Task task = JsonUtil.fromJson(json, Task.class); + + assertEquals("task-123", task.getId()); + assertNotNull(task.getMetadata()); + assertEquals("value1", task.getMetadata().get("key1")); + } + + @Test + void testTaskWithMixedPartTypes() throws JsonProcessingException { + Artifact artifact = new Artifact.Builder() + .artifactId("mixed-artifact") + .parts(List.of( + new TextPart("Text content"), + new FilePart(new FileWithBytes("application/json", "data.json", "{}")), + new DataPart(Map.of("result", 42)), + new FilePart(new FileWithUri("image/png", "image.png", "https://example.com/img.png")) + )) + .build(); + + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.COMPLETED)) + .artifacts(List.of(artifact)) + .build(); + + // Serialize + String json = JsonUtil.toJson(task); + + // Deserialize + Task deserialized = JsonUtil.fromJson(json, Task.class); + + // Verify all part types are preserved + List> parts = deserialized.getArtifacts().get(0).parts(); + assertEquals(4, parts.size()); + assertTrue(parts.get(0) instanceof TextPart); + assertTrue(parts.get(1) instanceof FilePart); + assertTrue(parts.get(2) instanceof DataPart); + assertTrue(parts.get(3) instanceof FilePart); + } +} diff --git a/spec/src/test/java/io/a2a/spec/TaskStatusTest.java b/spec/src/test/java/io/a2a/spec/TaskStatusTest.java deleted file mode 100644 index 7c4a9db8a..000000000 --- a/spec/src/test/java/io/a2a/spec/TaskStatusTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package io.a2a.spec; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import org.junit.jupiter.api.Test; - -import java.time.OffsetDateTime; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class TaskStatusTest { - - private static final ObjectMapper OBJECT_MAPPER; - - private static final String REPLACE_TIMESTAMP_PATTERN = ".*\"timestamp\":\"([^\"]+)\",?.*"; - - static { - OBJECT_MAPPER = new ObjectMapper(); - OBJECT_MAPPER.registerModule(new JavaTimeModule()); - OBJECT_MAPPER.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false); - } - - @Test - public void testTaskStatusWithSetTimestamp() { - TaskState state = TaskState.WORKING; - OffsetDateTime offsetDateTime = OffsetDateTime.parse("2023-10-01T12:00:00Z"); - TaskStatus status = new TaskStatus(state, offsetDateTime); - - assertNotNull(status.timestamp()); - assertEquals(offsetDateTime, status.timestamp()); - } - - @Test - public void testTaskStatusWithProvidedTimestamp() { - OffsetDateTime providedTimestamp = OffsetDateTime.parse("2024-01-01T00:00:00Z"); - TaskState state = TaskState.COMPLETED; - TaskStatus status = new TaskStatus(state, providedTimestamp); - - assertEquals(providedTimestamp, status.timestamp()); - } - - @Test - public void testTaskStatusSerializationUsesISO8601Format() throws Exception { - OffsetDateTime expectedTimestamp = OffsetDateTime.parse("2023-10-01T12:00:00.234-05:00"); - TaskState state = TaskState.WORKING; - TaskStatus status = new TaskStatus(state, expectedTimestamp); - - String json = OBJECT_MAPPER.writeValueAsString(status); - - String expectedJson = "{\"state\":\"working\",\"timestamp\":\"2023-10-01T12:00:00.234-05:00\"}"; - assertEquals(expectedJson, json); - } - - @Test - public void testTaskStatusDeserializationWithValidISO8601Format() throws Exception { - String validJson = "{" - + "\"state\": \"auth-required\"," - + "\"timestamp\": \"2023-10-01T12:00:00.10+03:00\"" - + "}"; - - TaskStatus result = OBJECT_MAPPER.readValue(validJson, TaskStatus.class); - assertEquals(TaskState.AUTH_REQUIRED, result.state()); - assertNotNull(result.timestamp()); - assertEquals(OffsetDateTime.parse("2023-10-01T12:00:00.100+03:00"), result.timestamp()); - } - - @Test - public void testTaskStatusDeserializationWithInvalidISO8601FormatFails() { - String invalidJson = "{" - + "\"state\": \"completed\"," - + "\"timestamp\": \"2023/10/01 12:00:00\"" - + "}"; - - assertThrows( - com.fasterxml.jackson.databind.exc.InvalidFormatException.class, - () -> OBJECT_MAPPER.readValue(invalidJson, TaskStatus.class) - ); - } - - @Test - public void testTaskStatusJsonTimestampMatchesISO8601Regex() throws Exception { - TaskState state = TaskState.WORKING; - OffsetDateTime expectedTimestamp = OffsetDateTime.parse("2023-10-01T12:00:00.234Z"); - TaskStatus status = new TaskStatus(state, expectedTimestamp); - - String json = OBJECT_MAPPER.writeValueAsString(status); - - String timestampValue = json.replaceAll(REPLACE_TIMESTAMP_PATTERN, "$1"); - assertEquals(expectedTimestamp, OffsetDateTime.parse(timestampValue)); - } -} diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java new file mode 100644 index 000000000..c456ff3b1 --- /dev/null +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ +package io.a2a.server.apps.common; + +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; +import io.restassured.mapper.ObjectMapper; +import io.restassured.mapper.ObjectMapperDeserializationContext; +import io.restassured.mapper.ObjectMapperSerializationContext; + + +public class A2AGsonObjectMapper implements ObjectMapper { + public static final A2AGsonObjectMapper INSTANCE = new A2AGsonObjectMapper(); + + private A2AGsonObjectMapper() { + } + + @Override + public Object deserialize(ObjectMapperDeserializationContext context) { + try { + return JsonUtil.fromJson(context.getDataToDeserialize().asString(), context.getType()); + } catch (JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public Object serialize(ObjectMapperSerializationContext context) { + try { + return JsonUtil.toJson(context.getObjectToSerialize()); + } catch (JsonProcessingException ex) { + + throw new RuntimeException(ex); + } + } +} diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 74ca331a6..bb8733c66 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1,6 +1,5 @@ package io.a2a.server.apps.common; -import static io.restassured.RestAssured.given; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -31,7 +30,7 @@ import jakarta.ws.rs.core.MediaType; -import com.fasterxml.jackson.core.JsonProcessingException; +import io.a2a.json.JsonProcessingException; import io.a2a.client.Client; import io.a2a.client.ClientBuilder; import io.a2a.client.ClientEvent; @@ -74,7 +73,11 @@ import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.json.JsonUtil; import io.a2a.util.Utils; +import io.restassured.RestAssured; +import io.restassured.config.ObjectMapperConfig; +import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -117,6 +120,13 @@ public abstract class AbstractA2AServerTest { .build(); public static final String APPLICATION_JSON = "application/json"; + public static RequestSpecification given() { + return RestAssured.given() + .config(RestAssured.config() + .objectMapperConfig(new ObjectMapperConfig(A2AGsonObjectMapper.INSTANCE))); +} + + protected final int serverPort; private Client client; private Client nonStreamingClient; @@ -1628,7 +1638,7 @@ protected void saveTaskInTaskStore(Task task) throws Exception { .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://localhost:" + serverPort + "/test/task")) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(task))) + .POST(HttpRequest.BodyPublishers.ofString(JsonUtil.toJson(task))) .header("Content-Type", APPLICATION_JSON) .build(); @@ -1654,7 +1664,7 @@ protected Task getTaskFromTaskStore(String taskId) throws Exception { if (response.statusCode() != 200) { throw new RuntimeException(String.format("Getting task failed! Status: %d, Body: %s", response.statusCode(), response.body())); } - return Utils.OBJECT_MAPPER.readValue(response.body(), Task.TYPE_REFERENCE); + return JsonUtil.fromJson(response.body(), Task.class); } protected void deleteTaskInTaskStore(String taskId) throws Exception { @@ -1701,7 +1711,7 @@ protected void enqueueEventOnServer(Event event) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://localhost:" + serverPort + "/" + path)) .header("Content-Type", APPLICATION_JSON) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(event))) + .POST(HttpRequest.BodyPublishers.ofString(JsonUtil.toJson(event))) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); @@ -1790,7 +1800,7 @@ protected void savePushNotificationConfigInStore(String taskId, PushNotification .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://localhost:" + serverPort + "/test/task/" + taskId)) - .POST(HttpRequest.BodyPublishers.ofString(Utils.OBJECT_MAPPER.writeValueAsString(notificationConfig))) + .POST(HttpRequest.BodyPublishers.ofString(JsonUtil.toJson(notificationConfig))) .header("Content-Type", APPLICATION_JSON) .build(); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java index f161307aa..7cc62dcfe 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java @@ -13,8 +13,9 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; +import io.a2a.json.JsonProcessingException; import io.a2a.spec.Task; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import java.util.Map; @Dependent @@ -48,7 +49,11 @@ public PostBuilder body(String body) { @Override public A2AHttpResponse post() throws IOException, InterruptedException { - tasks.add(Utils.OBJECT_MAPPER.readValue(body, Task.TYPE_REFERENCE)); + try { + tasks.add(JsonUtil.fromJson(body, Task.class)); + } catch (JsonProcessingException e) { + throw new IOException("Failed to parse task JSON", e); + } try { return new A2AHttpResponse() { @Override diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 4f712b67e..3df35d7e1 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -241,7 +241,7 @@ public GetAuthenticatedExtendedCardResponse onGetAuthenticatedExtendedCardReques GetAuthenticatedExtendedCardRequest request, ServerCallContext context) { if (!agentCard.supportsAuthenticatedExtendedCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { return new GetAuthenticatedExtendedCardResponse(request.getId(), - new AuthenticatedExtendedCardNotConfiguredError()); + new AuthenticatedExtendedCardNotConfiguredError(null, "Authenticated Extended Card not configured", null)); } try { return new GetAuthenticatedExtendedCardResponse(request.getId(), extendedAgentCard.get()); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index de4f45399..b16ce9449 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -3,7 +3,8 @@ import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; -import com.fasterxml.jackson.core.JacksonException; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; import io.a2a.grpc.utils.ProtoUtils; @@ -49,7 +50,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.UnsupportedOperationError; import io.a2a.server.util.async.Internal; -import io.a2a.util.Utils; +import io.a2a.json.JsonUtil; import jakarta.enterprise.inject.Instance; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -292,8 +293,8 @@ private void parseRequestBody(String body, com.google.protobuf.Message.Builder b private void validate(String json) { try { - Utils.OBJECT_MAPPER.readTree(json); - } catch (JacksonException e) { + JsonParser.parseString(json); + } catch (JsonSyntaxException e) { throw new JSONParseError(JSON_PARSE_ERROR_CODE, "Failed to parse json", e.getMessage()); } } @@ -400,9 +401,9 @@ private int mapErrorToHttpStatus(JSONRPCError error) { public HTTPRestResponse getAuthenticatedExtendedCard() { try { if (!agentCard.supportsAuthenticatedExtendedCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { - throw new AuthenticatedExtendedCardNotConfiguredError(); + throw new AuthenticatedExtendedCardNotConfiguredError(null, "Authenticated Extended Card not configured", null); } - return new HTTPRestResponse(200, "application/json", Utils.OBJECT_MAPPER.writeValueAsString(extendedAgentCard.get())); + return new HTTPRestResponse(200, "application/json", JsonUtil.toJson(extendedAgentCard.get())); } catch (JSONRPCError e) { return createErrorResponse(e); } catch (Throwable t) { @@ -412,7 +413,7 @@ public HTTPRestResponse getAuthenticatedExtendedCard() { public HTTPRestResponse getAgentCard() { try { - return new HTTPRestResponse(200, "application/json", Utils.OBJECT_MAPPER.writeValueAsString(agentCard)); + return new HTTPRestResponse(200, "application/json", JsonUtil.toJson(agentCard)); } catch (Throwable t) { return createErrorResponse(500, new InternalError(t.getMessage())); } From 74652139719ba1cbc122269c5c3d7bfafc2b5a41 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Wed, 10 Dec 2025 13:59:58 +0100 Subject: [PATCH 220/493] chore: Enable javadoc in the default profile (#525) Any javadoc errors (not warnings) will make Maven fail This fixes #524 --------- Signed-off-by: Jeff Mesnil Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- pom.xml | 8 ++++---- spec/src/main/java/io/a2a/json/JsonUtil.java | 2 +- spec/src/main/java/io/a2a/util/Utils.java | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 806123b2e..e4842a4ea 100644 --- a/pom.xml +++ b/pom.xml @@ -454,6 +454,10 @@ org.apache.maven.plugins maven-compiler-plugin + + org.apache.maven.plugins + maven-javadoc-plugin + org.apache.maven.plugins maven-surefire-plugin @@ -516,10 +520,6 @@ org.apache.maven.plugins maven-source-plugin - - org.apache.maven.plugins - maven-javadoc-plugin - org.apache.maven.plugins maven-gpg-plugin diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/spec/src/main/java/io/a2a/json/JsonUtil.java index 80f16819e..c5c02a89c 100644 --- a/spec/src/main/java/io/a2a/json/JsonUtil.java +++ b/spec/src/main/java/io/a2a/json/JsonUtil.java @@ -79,7 +79,7 @@ private static GsonBuilder createBaseGsonBuilder() { *

    * Used throughout the SDK for consistent JSON serialization and deserialization. * - * @see GsonFactory#createGson() + * @see GsonBuilder */ public static final Gson OBJECT_MAPPER = createBaseGsonBuilder() .registerTypeHierarchyAdapter(Part.class, new PartTypeAdapter()) diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 6128f3a1f..f567b43b8 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -1,5 +1,6 @@ package io.a2a.util; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; @@ -43,7 +44,7 @@ public class Utils { /** * Deserializes JSON string into a typed object using Gson. *

    - * This method uses the pre-configured {@link #OBJECT_MAPPER} to parse JSON. + * This method uses the pre-configured {@link JsonUtil#fromJson(String, Class)} to parse JSON. * * @param the target type * @param data JSON string to deserialize From cd322763daaab6b802dd9baeca1e1559598c7790 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 10 Dec 2025 15:46:52 +0100 Subject: [PATCH 221/493] docs: Add comprehensive Javadoc to spec module classes (#527) Added Javadoc documentation for: - Public constants (error codes, method names, type identifiers) - Generic type parameters in base request/response classes - Record compact constructors (12 record classes) - Exception constructors (5 exception classes) - APIKeySecurityScheme (enum, methods, constructors, Builder) - Builder classes and methods (CancelTaskRequest, DeleteTaskPushNotificationConfig*) - Request/response constructors This resolves all Javadoc warnings and errors in the spec module, enabling clean builds with the -Prelease profile. Signed-off-by: Emmanuel Hugonnet --- spec/src/main/java/io/a2a/json/JsonUtil.java | 2 +- .../java/io/a2a/spec/A2AClientHTTPError.java | 8 ++ .../a2a/spec/A2AClientInvalidArgsError.java | 14 +++ .../a2a/spec/A2AClientInvalidStateError.java | 14 +++ .../java/io/a2a/spec/A2AClientJSONError.java | 14 +++ .../main/java/io/a2a/spec/A2AErrorCodes.java | 21 +++++ .../java/io/a2a/spec/A2AServerException.java | 19 ++++ .../io/a2a/spec/APIKeySecurityScheme.java | 91 +++++++++++++++++++ .../java/io/a2a/spec/AgentCapabilities.java | 6 ++ .../java/io/a2a/spec/AgentCardSignature.java | 11 +++ .../main/java/io/a2a/spec/AgentExtension.java | 11 +++ .../main/java/io/a2a/spec/AgentInterface.java | 5 + .../main/java/io/a2a/spec/AgentProvider.java | 5 + .../src/main/java/io/a2a/spec/AgentSkill.java | 11 +++ spec/src/main/java/io/a2a/spec/Artifact.java | 5 + .../java/io/a2a/spec/AuthenticationInfo.java | 5 + .../a2a/spec/AuthorizationCodeOAuthFlow.java | 5 + .../java/io/a2a/spec/CancelTaskRequest.java | 57 +++++++++++- .../a2a/spec/ClientCredentialsOAuthFlow.java | 5 + spec/src/main/java/io/a2a/spec/DataPart.java | 6 ++ ...eleteTaskPushNotificationConfigParams.java | 41 +++++++++ ...leteTaskPushNotificationConfigRequest.java | 55 +++++++++++ ...eteTaskPushNotificationConfigResponse.java | 19 ++++ spec/src/main/java/io/a2a/spec/FilePart.java | 1 + .../java/io/a2a/spec/JSONRPCResponse.java | 2 + .../a2a/spec/NonStreamingJSONRPCRequest.java | 2 + .../main/java/io/a2a/spec/SecurityScheme.java | 5 + spec/src/main/java/io/a2a/util/Utils.java | 1 - 28 files changed, 438 insertions(+), 3 deletions(-) diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/spec/src/main/java/io/a2a/json/JsonUtil.java index c5c02a89c..82457cd12 100644 --- a/spec/src/main/java/io/a2a/json/JsonUtil.java +++ b/spec/src/main/java/io/a2a/json/JsonUtil.java @@ -79,7 +79,7 @@ private static GsonBuilder createBaseGsonBuilder() { *

    * Used throughout the SDK for consistent JSON serialization and deserialization. * - * @see GsonBuilder + * @see JsonUtil#createBaseGsonBuilder() */ public static final Gson OBJECT_MAPPER = createBaseGsonBuilder() .registerTypeHierarchyAdapter(Part.class, new PartTypeAdapter()) diff --git a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java index abaf9e263..278f01d5d 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java @@ -33,6 +33,14 @@ public class A2AClientHTTPError extends A2AClientError { private final int code; private final String message; + /** + * Creates a new HTTP client error with the specified status code and message. + * + * @param code the HTTP status code + * @param message the error message + * @param data additional error data (may be the response body) + * @throws IllegalArgumentException if code or message is null + */ public A2AClientHTTPError(int code, String message, Object data) { Assert.checkNotNullParam("code", code); Assert.checkNotNullParam("message", message); diff --git a/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java b/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java index 20ad84c09..ecf87e395 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientInvalidArgsError.java @@ -27,13 +27,27 @@ */ public class A2AClientInvalidArgsError extends A2AClientError { + /** + * Creates a new invalid arguments error with no message. + */ public A2AClientInvalidArgsError() { } + /** + * Creates a new invalid arguments error with the specified message. + * + * @param message the error message + */ public A2AClientInvalidArgsError(String message) { super("Invalid arguments error: " + message); } + /** + * Creates a new invalid arguments error with the specified message and cause. + * + * @param message the error message + * @param cause the underlying cause + */ public A2AClientInvalidArgsError(String message, Throwable cause) { super("Invalid arguments error: " + message, cause); } diff --git a/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java b/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java index d08e18942..f710a85b3 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientInvalidStateError.java @@ -26,13 +26,27 @@ */ public class A2AClientInvalidStateError extends A2AClientError { + /** + * Creates a new invalid state error with no message. + */ public A2AClientInvalidStateError() { } + /** + * Creates a new invalid state error with the specified message. + * + * @param message the error message + */ public A2AClientInvalidStateError(String message) { super("Invalid state error: " + message); } + /** + * Creates a new invalid state error with the specified message and cause. + * + * @param message the error message + * @param cause the underlying cause + */ public A2AClientInvalidStateError(String message, Throwable cause) { super("Invalid state error: " + message, cause); } diff --git a/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java index 06d04605c..3cf7fa7ab 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientJSONError.java @@ -27,13 +27,27 @@ */ public class A2AClientJSONError extends A2AClientError { + /** + * Creates a new JSON error with no message. + */ public A2AClientJSONError() { } + /** + * Creates a new JSON error with the specified message. + * + * @param message the error message + */ public A2AClientJSONError(String message) { super(message); } + /** + * Creates a new JSON error with the specified message and cause. + * + * @param message the error message + * @param cause the underlying cause + */ public A2AClientJSONError(String message, Throwable cause) { super(message, cause); } diff --git a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java index 09175b744..f8087bdaa 100644 --- a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java +++ b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java @@ -5,18 +5,39 @@ */ public interface A2AErrorCodes { + /** Error code indicating the requested task was not found (-32001). */ int TASK_NOT_FOUND_ERROR_CODE = -32001; + + /** Error code indicating the task cannot be canceled in its current state (-32002). */ int TASK_NOT_CANCELABLE_ERROR_CODE = -32002; + + /** Error code indicating push notifications are not supported by this agent (-32003). */ int PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE = -32003; + + /** Error code indicating the requested operation is not supported (-32004). */ int UNSUPPORTED_OPERATION_ERROR_CODE = -32004; + + /** Error code indicating the content type is not supported (-32005). */ int CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE = -32005; + + /** Error code indicating the agent returned an invalid response (-32006). */ int INVALID_AGENT_RESPONSE_ERROR_CODE = -32006; + + /** Error code indicating authenticated extended card is not configured (-32007). */ int AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE = -32007; + /** JSON-RPC error code for invalid request structure (-32600). */ int INVALID_REQUEST_ERROR_CODE = -32600; + + /** JSON-RPC error code for method not found (-32601). */ int METHOD_NOT_FOUND_ERROR_CODE = -32601; + + /** JSON-RPC error code for invalid method parameters (-32602). */ int INVALID_PARAMS_ERROR_CODE = -32602; + + /** JSON-RPC error code for internal server errors (-32603). */ int INTERNAL_ERROR_CODE = -32603; + /** JSON-RPC error code for JSON parsing errors (-32700). */ int JSON_PARSE_ERROR_CODE = -32700; } diff --git a/spec/src/main/java/io/a2a/spec/A2AServerException.java b/spec/src/main/java/io/a2a/spec/A2AServerException.java index 594502776..e2d2cf85c 100644 --- a/spec/src/main/java/io/a2a/spec/A2AServerException.java +++ b/spec/src/main/java/io/a2a/spec/A2AServerException.java @@ -19,18 +19,37 @@ */ public class A2AServerException extends A2AException { + /** + * Creates a new A2AServerException with no message. + */ public A2AServerException() { super(); } + /** + * Creates a new A2AServerException with the specified message. + * + * @param msg the exception message + */ public A2AServerException(final String msg) { super(msg); } + /** + * Creates a new A2AServerException with the specified cause. + * + * @param cause the underlying cause + */ public A2AServerException(final Throwable cause) { super(cause); } + /** + * Creates a new A2AServerException with the specified message and cause. + * + * @param msg the exception message + * @param cause the underlying cause + */ public A2AServerException(final String msg, final Throwable cause) { super(msg, cause); } diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index 1eae287b3..fd004ebb8 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -18,6 +18,7 @@ */ public final class APIKeySecurityScheme implements SecurityScheme { + /** The security scheme type identifier for API key authentication. */ public static final String API_KEY = "apiKey"; private final Location location; private final String name; @@ -28,8 +29,13 @@ public final class APIKeySecurityScheme implements SecurityScheme { * Represents the location of the API key. */ public enum Location { + /** API key sent in a cookie. */ COOKIE("cookie"), + + /** API key sent in an HTTP header. */ HEADER("header"), + + /** API key sent as a query parameter. */ QUERY("query"); private final String location; @@ -38,10 +44,22 @@ public enum Location { this.location = location; } + /** + * Converts this location to its string representation. + * + * @return the string representation of this location + */ public String asString() { return location; } + /** + * Converts a string to a Location enum value. + * + * @param location the string location ("cookie", "header", or "query") + * @return the corresponding Location enum value + * @throws IllegalArgumentException if the location string is invalid + */ public static Location fromString(String location) { switch (location) { case "cookie" -> { @@ -58,10 +76,27 @@ public static Location fromString(String location) { } } + /** + * Creates a new APIKeySecurityScheme with the specified location, name, and description. + * + * @param location the location where the API key is sent (required) + * @param name the name of the API key parameter (required) + * @param description a human-readable description (optional) + * @throws IllegalArgumentException if location or name is null + */ public APIKeySecurityScheme(Location location, String name, String description) { this(location, name, description, API_KEY); } + /** + * Creates a new APIKeySecurityScheme with explicit type specification. + * + * @param location the location where the API key is sent (required) + * @param name the name of the API key parameter (required) + * @param description a human-readable description (optional) + * @param type the security scheme type (must be "apiKey") + * @throws IllegalArgumentException if location, name, or type is null, or if type is not "apiKey" + */ public APIKeySecurityScheme(Location location, String name, String description, String type) { Assert.checkNotNullParam("location", location); @@ -76,43 +111,99 @@ public APIKeySecurityScheme(Location location, String name, this.type = type; } + /** + * Gets the human-readable description of this security scheme. + * + * @return the description, or null if not provided + */ @Override public String getDescription() { return description; } + /** + * Gets the location where the API key is sent. + * + * @return the API key location + */ public Location getLocation() { return location; } + /** + * Gets the name of the API key parameter. + * + * @return the parameter name + */ public String getName() { return name; } + /** + * Gets the security scheme type. + * + * @return always returns "apiKey" + */ public String getType() { return type; } + /** + * Builder for constructing immutable {@link APIKeySecurityScheme} instances. + *

    + * Example usage: + *

    {@code
    +     * APIKeySecurityScheme scheme = new APIKeySecurityScheme.Builder()
    +     *     .location(Location.HEADER)
    +     *     .name("X-API-Key")
    +     *     .description("API key authentication")
    +     *     .build();
    +     * }
    + */ public static class Builder { private Location location; private String name; private String description; + /** + * Sets the location where the API key should be sent. + * + * @param location the API key location (header, query, or cookie) (required) + * @return this builder for method chaining + */ public Builder location(Location location) { this.location = location; return this; } + /** + * Sets the name of the API key parameter. + * + * @param name the parameter name (required) + * @return this builder for method chaining + */ public Builder name(String name) { this.name = name; return this; } + /** + * Sets the human-readable description of the security scheme. + * + * @param description the description (optional) + * @return this builder for method chaining + */ public Builder description(String description) { this.description = description; return this; } + /** + * Builds a new immutable {@link APIKeySecurityScheme} from the current builder state. + * + * @return a new APIKeySecurityScheme instance + * @throws IllegalArgumentException if location or name is null + */ public APIKeySecurityScheme build() { return new APIKeySecurityScheme(location, name, description); } diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java index a5c5bc4d8..f86d601a5 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java @@ -56,6 +56,12 @@ public static class Builder { private boolean stateTransitionHistory; private List extensions; + /** + * Creates a new Builder with all capabilities set to false by default. + */ + public Builder() { + } + /** * Sets whether the agent supports streaming responses. *

    diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java index aecaf1861..ab4edd70e 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -34,6 +34,11 @@ public record AgentCardSignature(Map header, @SerializedName("protected")String protectedHeader, String signature) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if protectedHeader or signature is null + */ public AgentCardSignature { Assert.checkNotNullParam("protectedHeader", protectedHeader); Assert.checkNotNullParam("signature", signature); @@ -56,6 +61,12 @@ public static class Builder { String protectedHeader; String signature; + /** + * Creates a new Builder with all fields unset. + */ + public Builder() { + } + /** * Sets the optional unprotected header with additional metadata. * diff --git a/spec/src/main/java/io/a2a/spec/AgentExtension.java b/spec/src/main/java/io/a2a/spec/AgentExtension.java index 8bb5dade3..8a8cb6613 100644 --- a/spec/src/main/java/io/a2a/spec/AgentExtension.java +++ b/spec/src/main/java/io/a2a/spec/AgentExtension.java @@ -25,6 +25,11 @@ */ public record AgentExtension (String description, Map params, boolean required, String uri) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if uri is null + */ public AgentExtension { Assert.checkNotNullParam("uri", uri); } @@ -48,6 +53,12 @@ public static class Builder { boolean required; String uri; + /** + * Creates a new Builder with all fields unset. + */ + public Builder() { + } + /** * Sets a human-readable description of the extension's purpose. * diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index d887d9bcc..d035111c5 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -24,6 +24,11 @@ */ public record AgentInterface(String protocolBinding, String url) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if protocolBinding or url is null + */ public AgentInterface { Assert.checkNotNullParam("protocolBinding", protocolBinding); Assert.checkNotNullParam("url", url); diff --git a/spec/src/main/java/io/a2a/spec/AgentProvider.java b/spec/src/main/java/io/a2a/spec/AgentProvider.java index ec88ad2bc..abab7dc50 100644 --- a/spec/src/main/java/io/a2a/spec/AgentProvider.java +++ b/spec/src/main/java/io/a2a/spec/AgentProvider.java @@ -21,6 +21,11 @@ */ public record AgentProvider(String organization, String url) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if organization or url is null + */ public AgentProvider { Assert.checkNotNullParam("organization", organization); Assert.checkNotNullParam("url", url); diff --git a/spec/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java index c6d2ae027..2e0900af7 100644 --- a/spec/src/main/java/io/a2a/spec/AgentSkill.java +++ b/spec/src/main/java/io/a2a/spec/AgentSkill.java @@ -43,6 +43,11 @@ public record AgentSkill(String id, String name, String description, List examples, List inputModes, List outputModes, List>> security) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if id, name, description, or tags is null + */ public AgentSkill { Assert.checkNotNullParam("description", description); Assert.checkNotNullParam("id", id); @@ -84,6 +89,12 @@ public static class Builder { private List outputModes; private List>> security; + /** + * Creates a new Builder with all fields unset. + */ + public Builder() { + } + /** * Sets the unique identifier for the skill. *

    diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java index 1d1e9338a..5aa6870a7 100644 --- a/spec/src/main/java/io/a2a/spec/Artifact.java +++ b/spec/src/main/java/io/a2a/spec/Artifact.java @@ -32,6 +32,11 @@ public record Artifact(String artifactId, String name, String description, List> parts, Map metadata, List extensions) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if artifactId or parts is null, or if parts is empty + */ public Artifact { Assert.checkNotNullParam("artifactId", artifactId); Assert.checkNotNullParam("parts", parts); diff --git a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java index 336281707..ffcfc73df 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticationInfo.java @@ -24,6 +24,11 @@ */ public record AuthenticationInfo(List schemes, String credentials) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if schemes is null + */ public AuthenticationInfo { Assert.checkNotNullParam("schemes", schemes); } diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index 166bd58ba..351de65e0 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -28,6 +28,11 @@ public record AuthorizationCodeOAuthFlow(String authorizationUrl, String refreshUrl, Map scopes, String tokenUrl) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if authorizationUrl, scopes, or tokenUrl is null + */ public AuthorizationCodeOAuthFlow { Assert.checkNotNullParam("authorizationUrl", authorizationUrl); Assert.checkNotNullParam("scopes", scopes); diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java index b292f028b..6a52ca031 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java @@ -26,10 +26,20 @@ */ public final class CancelTaskRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name for canceling tasks. */ public static final String METHOD = "CancelTask"; + /** + * Creates a new CancelTaskRequest with the specified JSON-RPC parameters. + * + * @param jsonrpc the JSON-RPC version (defaults to "2.0" if null) + * @param id the request identifier (string, integer, or null) + * @param method the method name (must be "CancelTask") + * @param params the request parameters containing the task ID + * @throws IllegalArgumentException if jsonrpc version is invalid, method is not "CancelTask", params is null, or id is not a String/Integer/null + */ public CancelTaskRequest(String jsonrpc, Object id, String method, TaskIdParams params) { - if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { + if (jsonrpc != null && ! JSONRPC_VERSION.equals(jsonrpc)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } Assert.checkNotNullParam("method", method); @@ -44,36 +54,81 @@ public CancelTaskRequest(String jsonrpc, Object id, String method, TaskIdParams this.params = params; } + /** + * Creates a new CancelTaskRequest with default JSON-RPC version and method. + * + * @param id the request identifier (string, integer, or null) + * @param params the request parameters containing the task ID + * @throws IllegalArgumentException if params is null or id is not a string/integer/null + */ public CancelTaskRequest(Object id, TaskIdParams params) { this(null, id, METHOD, params); } + /** + * Builder for constructing {@link CancelTaskRequest} instances. + *

    + * Provides a fluent API for setting request parameters. If no id is provided, + * a random UUID will be generated when {@link #build()} is called. + */ public static class Builder { private String jsonrpc; private Object id; private String method = METHOD; private TaskIdParams params; + /** + * Sets the JSON-RPC protocol version. + * + * @param jsonrpc the JSON-RPC version (optional, defaults to "2.0") + * @return this builder for method chaining + */ public CancelTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request identifier. + * + * @param id the request identifier (string, integer, or null; if null, a UUID will be generated) + * @return this builder for method chaining + */ public CancelTaskRequest.Builder id(Object id) { this.id = id; return this; } + /** + * Sets the JSON-RPC method name. + * + * @param method the method name (should be "CancelTask") + * @return this builder for method chaining + */ public CancelTaskRequest.Builder method(String method) { this.method = method; return this; } + /** + * Sets the request parameters containing the task ID to cancel. + * + * @param params the request parameters (required) + * @return this builder for method chaining + */ public CancelTaskRequest.Builder params(TaskIdParams params) { this.params = params; return this; } + /** + * Builds a new {@link CancelTaskRequest} from the current builder state. + *

    + * If no id was provided, a random UUID will be generated. + * + * @return a new CancelTaskRequest instance + * @throws IllegalArgumentException if validation fails (invalid method, null params, invalid id type) + */ public CancelTaskRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java index 84d323bc6..e144559e4 100644 --- a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java @@ -28,6 +28,11 @@ */ public record ClientCredentialsOAuthFlow(String refreshUrl, Map scopes, String tokenUrl) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if scopes or tokenUrl is null + */ public ClientCredentialsOAuthFlow { Assert.checkNotNullParam("scopes", scopes); Assert.checkNotNullParam("tokenUrl", tokenUrl); diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 772e38d4b..3f5f1e9bf 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -37,6 +37,7 @@ */ public class DataPart extends Part> { + /** The type identifier for data parts in messages and artifacts. */ public static final String DATA = "data"; private final Map data; private final Map metadata; @@ -58,6 +59,11 @@ public Kind getKind() { return kind; } + /** + * Gets the structured data contained in this part. + * + * @return a map of key-value pairs representing the data + */ public Map getData() { return data; } diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index 3813d51b2..ae372ab43 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -19,35 +19,76 @@ */ public record DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, Map metadata) { + /** + * Compact constructor that validates required fields. + * + * @throws IllegalArgumentException if id or pushNotificationConfigId is null + */ public DeleteTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("pushNotificationConfigId", pushNotificationConfigId); } + /** + * Creates parameters without optional metadata. + * + * @param id the task identifier (required) + * @param pushNotificationConfigId the configuration ID to delete (required) + * @throws IllegalArgumentException if id or pushNotificationConfigId is null + */ public DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId) { this(id, pushNotificationConfigId, null); } + /** + * Builder for constructing {@link DeleteTaskPushNotificationConfigParams} instances. + *

    + * Provides a fluent API for setting parameters with optional metadata. + */ public static class Builder { String id; String pushNotificationConfigId; Map metadata; + /** + * Sets the task identifier. + * + * @param id the task ID (required) + * @return this builder for method chaining + */ public Builder id(String id) { this.id = id; return this; } + /** + * Sets the push notification configuration ID to delete. + * + * @param pushNotificationConfigId the configuration ID (required) + * @return this builder for method chaining + */ public Builder pushNotificationConfigId(String pushNotificationConfigId) { this.pushNotificationConfigId = pushNotificationConfigId; return this; } + /** + * Sets optional metadata for the request. + * + * @param metadata arbitrary key-value metadata (optional) + * @return this builder for method chaining + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Builds a new {@link DeleteTaskPushNotificationConfigParams} from the current builder state. + * + * @return a new DeleteTaskPushNotificationConfigParams instance + * @throws IllegalArgumentException if id or pushNotificationConfigId is null + */ public DeleteTaskPushNotificationConfigParams build() { return new DeleteTaskPushNotificationConfigParams(id, pushNotificationConfigId, metadata); } diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java index 2fb4b1757..40390299a 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -20,8 +20,18 @@ */ public final class DeleteTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name for deleting push notification configurations. */ public static final String METHOD = "DeleteTaskPushNotificationConfig"; + /** + * Creates a new DeleteTaskPushNotificationConfigRequest with the specified JSON-RPC parameters. + * + * @param jsonrpc the JSON-RPC version (defaults to "2.0" if null) + * @param id the request identifier (string, integer, or null) + * @param method the method name (must be "DeleteTaskPushNotificationConfig") + * @param params the request parameters containing task and config IDs + * @throws IllegalArgumentException if jsonrpc version is invalid, method is not "DeleteTaskPushNotificationConfig", or id is not a string/integer/null + */ public DeleteTaskPushNotificationConfigRequest(String jsonrpc, Object id, String method, DeleteTaskPushNotificationConfigParams params) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); @@ -37,36 +47,81 @@ public DeleteTaskPushNotificationConfigRequest(String jsonrpc, Object id, String this.params = params; } + /** + * Creates a new DeleteTaskPushNotificationConfigRequest with default JSON-RPC version and method. + * + * @param id the request identifier (string, integer, or null) + * @param params the request parameters containing task and config IDs + * @throws IllegalArgumentException if id is not a string/integer/null + */ public DeleteTaskPushNotificationConfigRequest(String id, DeleteTaskPushNotificationConfigParams params) { this(null, id, METHOD, params); } + /** + * Builder for constructing {@link DeleteTaskPushNotificationConfigRequest} instances. + *

    + * Provides a fluent API for setting request parameters. If no id is provided, + * a random UUID will be generated when {@link #build()} is called. + */ public static class Builder { private String jsonrpc; private Object id; private String method; private DeleteTaskPushNotificationConfigParams params; + /** + * Sets the JSON-RPC protocol version. + * + * @param jsonrpc the JSON-RPC version (optional, defaults to "2.0") + * @return this builder for method chaining + */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request identifier. + * + * @param id the request identifier (string, integer, or null; if null, a UUID will be generated) + * @return this builder for method chaining + */ public Builder id(Object id) { this.id = id; return this; } + /** + * Sets the JSON-RPC method name. + * + * @param method the method name (should be "DeleteTaskPushNotificationConfig") + * @return this builder for method chaining + */ public Builder method(String method) { this.method = method; return this; } + /** + * Sets the request parameters. + * + * @param params the request parameters containing task and config IDs (required) + * @return this builder for method chaining + */ public Builder params(DeleteTaskPushNotificationConfigParams params) { this.params = params; return this; } + /** + * Builds a new {@link DeleteTaskPushNotificationConfigRequest} from the current builder state. + *

    + * If no id was provided, a random UUID will be generated. + * + * @return a new DeleteTaskPushNotificationConfigRequest instance + * @throws IllegalArgumentException if validation fails (invalid method, invalid id type) + */ public DeleteTaskPushNotificationConfigRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java index 11c8846b2..296e1dc28 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java @@ -13,14 +13,33 @@ */ public final class DeleteTaskPushNotificationConfigResponse extends JSONRPCResponse { + /** + * Creates a new DeleteTaskPushNotificationConfigResponse with full JSON-RPC parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the response identifier matching the request + * @param result the result (always null/Void for this response type) + * @param error the error if the request failed, null on success + */ public DeleteTaskPushNotificationConfigResponse(String jsonrpc, Object id, Void result,JSONRPCError error) { super(jsonrpc, id, result, error, Void.class); } + /** + * Creates a new error DeleteTaskPushNotificationConfigResponse with default JSON-RPC version. + * + * @param id the response identifier matching the request + * @param error the error describing why the deletion failed + */ public DeleteTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Creates a new successful DeleteTaskPushNotificationConfigResponse with default JSON-RPC version. + * + * @param id the response identifier matching the request + */ public DeleteTaskPushNotificationConfigResponse(Object id) { this(null, id, null, null); } diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 969684d35..70bd0b7f1 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -39,6 +39,7 @@ */ public class FilePart extends Part { + /** The type identifier for file parts in messages and artifacts. */ public static final String FILE = "file"; private final FileContent file; private final Map metadata; diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 5da4b8980..0aaff24de 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -6,6 +6,8 @@ /** * Represents a JSONRPC response. + * + * @param the type of the result value returned in successful responses */ public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java index dd7a753ab..96cc01177 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java @@ -2,6 +2,8 @@ /** * Represents a non-streaming JSON-RPC request. + * + * @param the type of the request parameters */ public abstract sealed class NonStreamingJSONRPCRequest extends JSONRPCRequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index d956dceb2..57b409a4c 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -25,5 +25,10 @@ public sealed interface SecurityScheme permits APIKeySecurityScheme, HTTPAuthSecurityScheme, OAuth2SecurityScheme, OpenIdConnectSecurityScheme, MutualTLSSecurityScheme { + /** + * Gets the human-readable description of this security scheme. + * + * @return the description, or null if not provided + */ String getDescription(); } diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index f567b43b8..8a382fc31 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -1,6 +1,5 @@ package io.a2a.util; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; From 8ffef3577c659612f0070926998003e60888bfa9 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Wed, 10 Dec 2025 16:45:44 +0100 Subject: [PATCH 222/493] chore: add a dedicated javadoc Maven profile (#529) Javadoc is not generated by default. The "Build and Test" GitHub action is run with that profile to validate Javadoc. This fixes #525 Signed-off-by: Jeff Mesnil --- .github/workflows/build-and-test.yml | 2 +- pom.xml | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e2fb7253d..4b5e89f8c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -26,7 +26,7 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven and run tests - run: mvn -B package --file pom.xml -fae + run: mvn -Pjavadoc -B package --file pom.xml -fae - name: Upload Test Reports if: failure() uses: actions/upload-artifact@v4 diff --git a/pom.xml b/pom.xml index e4842a4ea..b1c4b6f68 100644 --- a/pom.xml +++ b/pom.xml @@ -454,10 +454,6 @@ org.apache.maven.plugins maven-compiler-plugin - - org.apache.maven.plugins - maven-javadoc-plugin - org.apache.maven.plugins maven-surefire-plugin @@ -508,6 +504,20 @@ + + + javadoc + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + true @@ -342,7 +343,7 @@ --should-stop=ifError=FLOW -parameters ${error-prone.flag} - -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode + ${nullaway.args} @@ -554,6 +555,7 @@ 2.42.0 0.12.10 + -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 805888447..a00f8a9bb 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -369,12 +369,11 @@ public boolean isAuthenticated() { } @Override - public @Nullable - String getUsername() { - if (rc.user() != null) { + public String getUsername() { + if (rc.user() != null && rc.user().subject() != null) { return rc.user().subject(); } - return null; + return ""; } }; } diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index 0078738dd..308d1d44c 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -6,6 +6,8 @@ import java.util.UUID; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; + import io.a2a.server.ServerCallContext; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.Message; @@ -17,20 +19,20 @@ public class RequestContext { - private MessageSendParams params; - private String taskId; - private String contextId; - private Task task; + private @Nullable MessageSendParams params; + private @Nullable String taskId; + private @Nullable String contextId; + private @Nullable Task task; private List relatedTasks; - private final ServerCallContext callContext; + private final @Nullable ServerCallContext callContext; public RequestContext( - MessageSendParams params, - String taskId, - String contextId, - Task task, - List relatedTasks, - ServerCallContext callContext) throws InvalidParamsError { + @Nullable MessageSendParams params, + @Nullable String taskId, + @Nullable String contextId, + @Nullable Task task, + @Nullable List relatedTasks, + @Nullable ServerCallContext callContext) throws InvalidParamsError { this.params = params; this.taskId = taskId; this.contextId = contextId; @@ -53,19 +55,19 @@ public RequestContext( } } - public MessageSendParams getParams() { + public @Nullable MessageSendParams getParams() { return params; } - public String getTaskId() { + public @Nullable String getTaskId() { return taskId; } - public String getContextId() { + public @Nullable String getContextId() { return contextId; } - public Task getTask() { + public @Nullable Task getTask() { return task; } @@ -73,15 +75,15 @@ public List getRelatedTasks() { return Collections.unmodifiableList(relatedTasks); } - public Message getMessage() { + public @Nullable Message getMessage() { return params != null ? params.message() : null; } - public MessageSendConfiguration getConfiguration() { + public @Nullable MessageSendConfiguration getConfiguration() { return params != null ? params.configuration() : null; } - public ServerCallContext getCallContext() { + public @Nullable ServerCallContext getCallContext() { return callContext; } @@ -137,64 +139,64 @@ private List getTextParts(List> parts) { } public static class Builder { - private MessageSendParams params; - private String taskId; - private String contextId; - private Task task; - private List relatedTasks; - private ServerCallContext serverCallContext; - - public Builder setParams(MessageSendParams params) { + private @Nullable MessageSendParams params; + private @Nullable String taskId; + private @Nullable String contextId; + private @Nullable Task task; + private @Nullable List relatedTasks; + private @Nullable ServerCallContext serverCallContext; + + public Builder setParams(@Nullable MessageSendParams params) { this.params = params; return this; } - public Builder setTaskId(String taskId) { + public Builder setTaskId(@Nullable String taskId) { this.taskId = taskId; return this; } - public Builder setContextId(String contextId) { + public Builder setContextId(@Nullable String contextId) { this.contextId = contextId; return this; } - public Builder setTask(Task task) { + public Builder setTask(@Nullable Task task) { this.task = task; return this; } - public Builder setRelatedTasks(List relatedTasks) { + public Builder setRelatedTasks(@Nullable List relatedTasks) { this.relatedTasks = relatedTasks; return this; } - public Builder setServerCallContext(ServerCallContext serverCallContext) { + public Builder setServerCallContext(@Nullable ServerCallContext serverCallContext) { this.serverCallContext = serverCallContext; return this; } - public MessageSendParams getParams() { + public @Nullable MessageSendParams getParams() { return params; } - public String getTaskId() { + public @Nullable String getTaskId() { return taskId; } - public String getContextId() { + public @Nullable String getContextId() { return contextId; } - public Task getTask() { + public @Nullable Task getTask() { return task; } - public List getRelatedTasks() { + public @Nullable List getRelatedTasks() { return relatedTasks; } - public ServerCallContext getServerCallContext() { + public @Nullable ServerCallContext getServerCallContext() { return serverCallContext; } diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/package-info.java b/server-common/src/main/java/io/a2a/server/agentexecution/package-info.java new file mode 100644 index 000000000..3fc934a52 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/agentexecution/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.agentexecution; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/auth/package-info.java b/server-common/src/main/java/io/a2a/server/auth/package-info.java new file mode 100644 index 000000000..524d98fac --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/auth/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.auth; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/config/package-info.java b/server-common/src/main/java/io/a2a/server/config/package-info.java new file mode 100644 index 000000000..335bf450b --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/config/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.config; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java b/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java index 380cb04f4..779ad02da 100644 --- a/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java +++ b/server-common/src/main/java/io/a2a/server/events/EnhancedRunnable.java @@ -3,11 +3,13 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import org.jspecify.annotations.Nullable; + public abstract class EnhancedRunnable implements Runnable { - private volatile Throwable error; + private volatile @Nullable Throwable error; private final List doneCallbacks = new CopyOnWriteArrayList<>(); - public Throwable getError() { + public @Nullable Throwable getError() { return error; } diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index ccf9f0ce9..b6ff7abce 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -2,6 +2,8 @@ import java.util.concurrent.Flow; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.Message; @@ -16,7 +18,7 @@ public class EventConsumer { private static final Logger LOGGER = LoggerFactory.getLogger(EventConsumer.class); private final EventQueue queue; - private Throwable error; + private volatile @Nullable Throwable error; private static final String ERROR_MSG = "Agent did not return any response"; private static final int NO_WAIT = -1; diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 6a8a154ac..92cc015d9 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -9,6 +9,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.jspecify.annotations.Nullable; + import io.a2a.server.tasks.TaskStateProvider; import io.a2a.spec.Event; import org.slf4j.Logger; @@ -49,10 +51,10 @@ static EventQueueBuilder builder() { public static class EventQueueBuilder { private int queueSize = DEFAULT_QUEUE_SIZE; - private EventEnqueueHook hook; - private String taskId; + private @Nullable EventEnqueueHook hook; + private @Nullable String taskId; private List onCloseCallbacks = new java.util.ArrayList<>(); - private TaskStateProvider taskStateProvider; + private @Nullable TaskStateProvider taskStateProvider; public EventQueueBuilder queueSize(int queueSize) { this.queueSize = queueSize; @@ -132,7 +134,7 @@ public void enqueueItem(EventQueueItem item) { * @return the EventQueueItem, or null if timeout occurs * @throws EventQueueClosedException if the queue is closed and empty */ - public EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { + public @Nullable EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { if (closed && queue.isEmpty()) { LOGGER.debug("Queue is closed, and empty. Sending termination message. {}", this); throw new EventQueueClosedException(); @@ -218,10 +220,10 @@ static class MainQueue extends EventQueue { private final List children = new CopyOnWriteArrayList<>(); private final CountDownLatch pollingStartedLatch = new CountDownLatch(1); private final AtomicBoolean pollingStarted = new AtomicBoolean(false); - private final EventEnqueueHook enqueueHook; - private final String taskId; + private final @Nullable EventEnqueueHook enqueueHook; + private final @Nullable String taskId; private final List onCloseCallbacks; - private final TaskStateProvider taskStateProvider; + private final @Nullable TaskStateProvider taskStateProvider; MainQueue() { super(); @@ -255,7 +257,7 @@ static class MainQueue extends EventQueue { this.taskStateProvider = null; } - MainQueue(int queueSize, EventEnqueueHook hook, String taskId, List onCloseCallbacks, TaskStateProvider taskStateProvider) { + MainQueue(int queueSize, @Nullable EventEnqueueHook hook, @Nullable String taskId, List onCloseCallbacks, @Nullable TaskStateProvider taskStateProvider) { super(queueSize); this.enqueueHook = hook; this.taskId = taskId; diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index 1383d058e..2ab9070d3 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -6,6 +6,8 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import org.jspecify.annotations.Nullable; + import io.a2a.server.tasks.TaskStateProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,12 +41,12 @@ public void add(String taskId, EventQueue queue) { } @Override - public EventQueue get(String taskId) { + public @Nullable EventQueue get(String taskId) { return queues.get(taskId); } @Override - public EventQueue tap(String taskId) { + public @Nullable EventQueue tap(String taskId) { EventQueue queue = queues.get(taskId); return queue == null ? null : queue.tap(); } @@ -92,6 +94,9 @@ public EventQueue createOrTap(String taskId) { } EventQueue main = existing == null ? newQueue : existing; + if (main == null) { + throw new IllegalStateException("Failed to create or retrieve queue for task " + taskId); + } EventQueue result = main.tap(); // Always return ChildQueue if (existing == null) { diff --git a/server-common/src/main/java/io/a2a/server/events/QueueManager.java b/server-common/src/main/java/io/a2a/server/events/QueueManager.java index b0eb517ae..54ef30e81 100644 --- a/server-common/src/main/java/io/a2a/server/events/QueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/QueueManager.java @@ -1,12 +1,14 @@ package io.a2a.server.events; +import org.jspecify.annotations.Nullable; + public interface QueueManager { void add(String taskId, EventQueue queue); - EventQueue get(String taskId); + @Nullable EventQueue get(String taskId); - EventQueue tap(String taskId); + @Nullable EventQueue tap(String taskId); void close(String taskId); diff --git a/server-common/src/main/java/io/a2a/server/events/package-info.java b/server-common/src/main/java/io/a2a/server/events/package-info.java new file mode 100644 index 000000000..912717787 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.events; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java index 0020d2cbc..39f65a994 100644 --- a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java +++ b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java @@ -4,6 +4,8 @@ import java.util.List; import java.util.Set; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.AgentCard; import io.a2a.spec.AgentExtension; @@ -31,7 +33,7 @@ public static Set getRequestedExtensions(List values) { return extensions; } - public static AgentExtension findExtensionByUri(AgentCard card, String uri) { + public static @Nullable AgentExtension findExtensionByUri(AgentCard card, String uri) { if (card.capabilities() == null || card.capabilities().extensions() == null) { return null; } diff --git a/server-common/src/main/java/io/a2a/server/extensions/package-info.java b/server-common/src/main/java/io/a2a/server/extensions/package-info.java new file mode 100644 index 000000000..aa966d4a2 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/extensions/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.extensions; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/package-info.java b/server-common/src/main/java/io/a2a/server/package-info.java new file mode 100644 index 000000000..a2474e4c1 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index eef11f6ac..3a760b136 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -25,6 +25,9 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import org.jspecify.annotations.Nullable; +import org.jspecify.annotations.NonNull; + import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; @@ -266,6 +269,9 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte String taskId = mss.requestContext.getTaskId(); LOGGER.debug("Request context taskId: {}", taskId); + if (taskId == null) { + throw new io.a2a.spec.InternalError("Task ID is null in onMessageSend"); + } EventQueue queue = queueManager.createOrTap(taskId); ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor); @@ -358,12 +364,14 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte } // Step 4: Fetch the final task state from TaskStore (all events have been processed) - Task updatedTask = taskStore.get(taskId); + // taskId is guaranteed non-null here (checked earlier) + String nonNullTaskId = taskId; + Task updatedTask = taskStore.get(nonNullTaskId); if (updatedTask != null) { kind = updatedTask; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Fetched final task for {} with state {} and {} artifacts", - taskId, updatedTask.getStatus().state(), + nonNullTaskId, updatedTask.getStatus().state(), updatedTask.getArtifacts().size()); } } @@ -395,12 +403,18 @@ public Flow.Publisher onMessageSendStream( params.message().getTaskId(), params.message().getContextId(), runningAgents.size(), backgroundTasks.size()); MessageSendSetup mss = initMessageSend(params, context); - AtomicReference taskId = new AtomicReference<>(mss.requestContext.getTaskId()); + @Nullable String initialTaskId = mss.requestContext.getTaskId(); + // For streaming, taskId can be null initially (will be set when Task event arrives) + // Use a temporary ID for queue creation if needed + String queueTaskId = initialTaskId != null ? initialTaskId : "temp-" + java.util.UUID.randomUUID(); + + AtomicReference<@NonNull String> taskId = new AtomicReference<>(queueTaskId); + @SuppressWarnings("NullAway") EventQueue queue = queueManager.createOrTap(taskId.get()); LOGGER.debug("Created/tapped queue for task {}: {}", taskId.get(), queue); ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor); - EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId.get(), mss.requestContext, queue); + EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(queueTaskId, mss.requestContext, queue); // Move consumer creation and callback registration outside try block // so consumer is available for background consumption on client disconnect @@ -439,7 +453,8 @@ public Flow.Publisher onMessageSendStream( } } - if (pushSender != null && taskId.get() != null) { + String currentTaskId = taskId.get(); + if (pushSender != null && currentTaskId != null) { EventKind latest = resultAggregator.getCurrentResult(); if (latest instanceof Task latestTask) { pushSender.sendNotification(latestTask); @@ -456,9 +471,10 @@ public Flow.Publisher onMessageSendStream( // Wrap publisher to detect client disconnect and continue background consumption return subscriber -> { - LOGGER.debug("Creating subscription wrapper for task {}", taskId.get()); + String currentTaskId = taskId.get(); + LOGGER.debug("Creating subscription wrapper for task {}", currentTaskId); finalPublisher.subscribe(new Flow.Subscriber() { - private Flow.Subscription subscription; + private Flow.@Nullable Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -538,7 +554,7 @@ private void startBackgroundConsumption() { CompletableFuture agentFuture = runningAgents.remove(taskId.get()); LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId.get(), runningAgents.size()); - trackBackgroundTask(cleanupProducer(agentFuture, null, taskId.get(), queue, true)); + trackBackgroundTask(cleanupProducer(agentFuture, null, Objects.requireNonNull(taskId.get()), queue, true)); } } @@ -573,11 +589,12 @@ public TaskPushNotificationConfig onGetTaskPushNotificationConfig( throw new InternalError("No push notification config found"); } - return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, params.pushNotificationConfigId())); + @Nullable String configId = params.pushNotificationConfigId(); + return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, configId)); } private PushNotificationConfig getPushNotificationConfig(List notificationConfigList, - String configId) { + @Nullable String configId) { if (configId != null) { for (PushNotificationConfig notificationConfig : notificationConfigList) { if (configId.equals(notificationConfig.id())) { @@ -746,7 +763,7 @@ public CompletableFuture waitForBackgroundTasks() { return CompletableFuture.allOf(tasks); } - private CompletableFuture cleanupProducer(CompletableFuture agentFuture, CompletableFuture consumptionFuture, String taskId, EventQueue queue, boolean isStreaming) { + private CompletableFuture cleanupProducer(@Nullable CompletableFuture agentFuture, @Nullable CompletableFuture consumptionFuture, String taskId, EventQueue queue, boolean isStreaming) { LOGGER.debug("Starting cleanup for task {} (streaming={})", taskId, isStreaming); logThreadStats("CLEANUP START"); @@ -818,7 +835,7 @@ private MessageSendSetup initMessageSend(MessageSendParams params, ServerCallCon } private void sendPushNotification(String taskId, ResultAggregator resultAggregator) { - if (pushSender != null && taskId != null) { + if (pushSender != null) { EventKind latest = resultAggregator.getCurrentResult(); if (latest instanceof Task latestTask) { pushSender.sendNotification(latestTask); @@ -868,5 +885,5 @@ private void logThreadStats(String label) { LOGGER.debug("=== END THREAD STATS ==="); } - private record MessageSendSetup(TaskManager taskManager, Task task, RequestContext requestContext) {} + private record MessageSendSetup(TaskManager taskManager, @Nullable Task task, RequestContext requestContext) {} } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/package-info.java b/server-common/src/main/java/io/a2a/server/requesthandlers/package-info.java new file mode 100644 index 000000000..d4f3270f4 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.requesthandlers; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index 4ae6520c6..9ea8a244d 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -10,6 +10,8 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.PushNotificationConfig; /** @@ -49,7 +51,7 @@ public PushNotificationConfig setInfo(String taskId, PushNotificationConfig noti } @Override - public List getInfo(String taskId) { + public @Nullable List getInfo(String taskId) { return pushNotificationInfos.get(taskId); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index c9f2f8271..92cde5587 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -7,6 +7,8 @@ import jakarta.enterprise.context.ApplicationScoped; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; @@ -24,7 +26,7 @@ public void save(Task task) { } @Override - public Task get(String taskId) { + public @Nullable Task get(String taskId) { return tasks.get(taskId); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java index de7a27deb..6e721a6ec 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java @@ -2,6 +2,8 @@ import java.util.List; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.PushNotificationConfig; /** @@ -20,9 +22,9 @@ public interface PushNotificationConfigStore { /** * Retrieves the push notification configuration for a task. * @param taskId the task ID - * @return the push notification configurations for a task + * @return the push notification configurations for a task, or null if not found */ - List getInfo(String taskId); + @Nullable List getInfo(String taskId); /** * Deletes the push notification configuration for a task. diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index 27de1defb..ffb9a3099 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -11,6 +11,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import org.jspecify.annotations.Nullable; + import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueueItem; import io.a2a.spec.A2AServerException; @@ -30,15 +32,15 @@ public class ResultAggregator { private final TaskManager taskManager; private final Executor executor; - private volatile Message message; + private volatile @Nullable Message message; - public ResultAggregator(TaskManager taskManager, Message message, Executor executor) { + public ResultAggregator(TaskManager taskManager, @Nullable Message message, Executor executor) { this.taskManager = taskManager; this.message = message; this.executor = executor; } - public EventKind getCurrentResult() { + public @Nullable EventKind getCurrentResult() { if (message != null) { return message; } @@ -98,10 +100,15 @@ public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { Utils.rethrow(err); } - if (returnedEvent.get() != null) { - return returnedEvent.get(); + EventKind result = returnedEvent.get(); + if (result != null) { + return result; } - return taskManager.getTask(); + Task task = taskManager.getTask(); + if (task == null) { + throw new io.a2a.spec.InternalError("No task or message available after consuming all events"); + } + return task; } public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking) throws JSONRPCError { @@ -255,8 +262,20 @@ else if (blocking) { Utils.rethrow(error); } + EventKind eventType; + Message msg = message.get(); + if (msg != null) { + eventType = msg; + } else { + Task task = taskManager.getTask(); + if (task == null) { + throw new io.a2a.spec.InternalError("No task or message available after consuming events"); + } + eventType = task; + } + return new EventTypeAndInterrupt( - message.get() != null ? message.get() : taskManager.getTask(), + eventType, interrupted.get(), consumptionCompletionFuture); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index c01c8b680..0c83ca0cb 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -9,6 +9,8 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.InvalidParamsError; @@ -17,6 +19,7 @@ import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; +import java.util.Collections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,13 +27,13 @@ public class TaskManager { private static final Logger LOGGER = LoggerFactory.getLogger(TaskManager.class); - private volatile String taskId; - private volatile String contextId; + private volatile @Nullable String taskId; + private volatile @Nullable String contextId; private final TaskStore taskStore; - private final Message initialMessage; - private volatile Task currentTask; + private final @Nullable Message initialMessage; + private volatile @Nullable Task currentTask; - public TaskManager(String taskId, String contextId, TaskStore taskStore, Message initialMessage) { + public TaskManager(@Nullable String taskId, @Nullable String contextId, TaskStore taskStore, @Nullable Message initialMessage) { checkNotNullParam("taskStore", taskStore); this.taskId = taskId; this.contextId = contextId; @@ -38,15 +41,15 @@ public TaskManager(String taskId, String contextId, TaskStore taskStore, Message this.initialMessage = initialMessage; } - String getTaskId() { + @Nullable String getTaskId() { return taskId; } - String getContextId() { + @Nullable String getContextId() { return contextId; } - public Task getTask() { + public @Nullable Task getTask() { if (taskId == null) { return null; } @@ -90,7 +93,12 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { checkIdsAndUpdateIfNecessary(event.getTaskId(), event.getContextId()); Task task = ensureTask(event.getTaskId(), event.getContextId()); - task = appendArtifactToTask(task, event, taskId); + // taskId is guaranteed to be non-null after checkIdsAndUpdateIfNecessary + String nonNullTaskId = taskId; + if (nonNullTaskId == null) { + throw new IllegalStateException("taskId should not be null after checkIdsAndUpdateIfNecessary"); + } + task = appendArtifactToTask(task, event, nonNullTaskId); return saveTask(task); } @@ -141,7 +149,11 @@ private Task ensureTask(String eventTaskId, String eventContextId) { if (task != null) { return task; } - task = taskStore.get(taskId); + // taskId may be null here, but get() accepts @Nullable + String currentTaskId = taskId; + if (currentTaskId != null) { + task = taskStore.get(currentTaskId); + } if (task == null) { task = createTask(eventTaskId, eventContextId); saveTask(task); @@ -150,7 +162,7 @@ private Task ensureTask(String eventTaskId, String eventContextId) { } private Task createTask(String taskId, String contextId) { - List history = initialMessage != null ? List.of(initialMessage) : null; + List history = initialMessage != null ? List.of(initialMessage) : Collections.emptyList(); return new Task.Builder() .id(taskId) .contextId(contextId) diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java index dafd90360..6d4df5dbd 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java @@ -1,5 +1,6 @@ package io.a2a.server.tasks; +import org.jspecify.annotations.Nullable; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; @@ -8,7 +9,7 @@ public interface TaskStore { void save(Task task); - Task get(String taskId); + @Nullable Task get(String taskId); void delete(String taskId); diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index 0b094a5ea..a7271c1d2 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -5,6 +5,8 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import org.jspecify.annotations.Nullable; + import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.spec.Artifact; @@ -17,8 +19,8 @@ public class TaskUpdater { private final EventQueue eventQueue; - private final String taskId; - private final String contextId; + private final @Nullable String taskId; + private final @Nullable String contextId; private final AtomicBoolean terminalStateReached = new AtomicBoolean(false); private final Object stateLock = new Object(); @@ -32,11 +34,11 @@ private void updateStatus(TaskState taskState) { updateStatus(taskState, null, taskState.isFinal()); } - public void updateStatus(TaskState taskState, Message message) { + public void updateStatus(TaskState taskState, @Nullable Message message) { updateStatus(taskState, message, taskState.isFinal()); } - public void updateStatus(TaskState state, Message message, boolean isFinal) { + public void updateStatus(TaskState state, @Nullable Message message, boolean isFinal) { synchronized (stateLock) { // Check if we're already in a terminal state if (terminalStateReached.get()) { @@ -58,11 +60,11 @@ public void updateStatus(TaskState state, Message message, boolean isFinal) { } } - public String getContextId() { + public @Nullable String getContextId() { return this.contextId; } - public String getTaskId() { + public @Nullable String getTaskId() { return this.taskId; } @@ -70,12 +72,12 @@ public void addArtifact(List> parts) { addArtifact(parts, null, null, null); } - public void addArtifact(List> parts, String artifactId, String name, Map metadata) { + public void addArtifact(List> parts, @Nullable String artifactId, @Nullable String name, @Nullable Map metadata) { addArtifact(parts, artifactId, name, metadata, null, null); } - public void addArtifact(List> parts, String artifactId, String name, Map metadata, - Boolean append, Boolean lastChunk) { + public void addArtifact(List> parts, @Nullable String artifactId, @Nullable String name, @Nullable Map metadata, + @Nullable Boolean append, @Nullable Boolean lastChunk) { if (artifactId == null) { artifactId = UUID.randomUUID().toString(); } @@ -100,7 +102,7 @@ public void complete() { complete(null); } - public void complete(Message message) { + public void complete(@Nullable Message message) { updateStatus(TaskState.COMPLETED, message); } @@ -108,7 +110,7 @@ public void fail() { fail(null); } - public void fail(Message message) { + public void fail(@Nullable Message message) { updateStatus(TaskState.FAILED, message); } @@ -116,7 +118,7 @@ public void submit() { submit(null); } - public void submit(Message message) { + public void submit(@Nullable Message message) { updateStatus(TaskState.SUBMITTED, message); } @@ -124,7 +126,7 @@ public void startWork() { startWork(null); } - public void startWork(Message message) { + public void startWork(@Nullable Message message) { updateStatus(TaskState.WORKING, message); } @@ -132,7 +134,7 @@ public void cancel() { cancel(null); } - public void cancel(Message message) { + public void cancel(@Nullable Message message) { updateStatus(TaskState.CANCELED, message); } @@ -140,7 +142,7 @@ public void reject() { reject(null); } - public void reject(Message message) { + public void reject(@Nullable Message message) { updateStatus(TaskState.REJECTED, message); } @@ -148,7 +150,7 @@ public void requiresInput() { requiresInput(null, false); } - public void requiresInput(Message message) { + public void requiresInput(@Nullable Message message) { requiresInput(message, false); } @@ -156,7 +158,7 @@ public void requiresInput(boolean isFinal) { requiresInput(null, isFinal); } - public void requiresInput(Message message, boolean isFinal) { + public void requiresInput(@Nullable Message message, boolean isFinal) { updateStatus(TaskState.INPUT_REQUIRED, message, isFinal); } @@ -164,7 +166,7 @@ public void requiresAuth() { requiresAuth(null, false); } - public void requiresAuth(Message message) { + public void requiresAuth(@Nullable Message message) { requiresAuth(message, false); } @@ -172,11 +174,11 @@ public void requiresAuth(boolean isFinal) { requiresAuth(null, isFinal); } - public void requiresAuth(Message message, boolean isFinal) { + public void requiresAuth(@Nullable Message message, boolean isFinal) { updateStatus(TaskState.AUTH_REQUIRED, message, isFinal); } - public Message newAgentMessage(List> parts, Map metadata) { + public Message newAgentMessage(List> parts, @Nullable Map metadata) { return new Message.Builder() .role(Message.Role.AGENT) .taskId(taskId) diff --git a/server-common/src/main/java/io/a2a/server/tasks/package-info.java b/server-common/src/main/java/io/a2a/server/tasks/package-info.java new file mode 100644 index 000000000..1075aded6 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/tasks/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.a2a.server.tasks; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java index 406113fd4..ada7a53fa 100644 --- a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java +++ b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java @@ -4,6 +4,8 @@ import java.util.Map; import java.util.UUID; +import org.jspecify.annotations.Nullable; + import io.a2a.spec.Artifact; import io.a2a.spec.DataPart; import io.a2a.spec.Part; @@ -26,7 +28,7 @@ private ArtifactUtils() { * @param description An optional description of the artifact. * @return A new {@code Artifact} object with a generated artifact_id. */ - public static Artifact newArtifact(String name, List> parts, String description) { + public static Artifact newArtifact(String name, List> parts, @Nullable String description) { return new Artifact( UUID.randomUUID().toString(), name, @@ -56,7 +58,7 @@ public static Artifact newArtifact(String name, List> parts) { * @param description An optional description of the artifact. * @return A new {@code Artifact} object with a generated artifact_id. */ - public static Artifact newTextArtifact(String name, String text, String description) { + public static Artifact newTextArtifact(String name, String text, @Nullable String description) { return newArtifact( name, List.of(new TextPart(text)), @@ -83,7 +85,7 @@ public static Artifact newTextArtifact(String name, String text) { * @param description An optional description of the artifact. * @return A new {@code Artifact} object with a generated artifact_id. */ - public static Artifact newDataArtifact(String name, Map data, String description) { + public static Artifact newDataArtifact(String name, Map data, @Nullable String description) { return newArtifact( name, List.of(new DataPart(data)), diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index 3d7ed0915..e4b135999 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -14,6 +14,8 @@ import jakarta.enterprise.inject.Produces; import jakarta.inject.Inject; +import org.jspecify.annotations.Nullable; + import io.a2a.server.config.A2AConfigProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,7 +58,7 @@ public class AsyncExecutorProducer { */ long keepAliveSeconds; - private ExecutorService executor; + private @Nullable ExecutorService executor; @PostConstruct public void init() { @@ -79,6 +81,9 @@ public void init() { @PreDestroy public void close() { + if (executor == null) { + return; + } LOGGER.info("Shutting down async executor"); executor.shutdown(); try { @@ -96,6 +101,9 @@ public void close() { @Produces @Internal public Executor produce() { + if (executor == null) { + throw new IllegalStateException("Executor not initialized - @PostConstruct not called"); + } return executor; } diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java index 25444cde9..8d39d2e8a 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java @@ -7,6 +7,8 @@ import java.util.function.Consumer; import java.util.function.Function; +import org.jspecify.annotations.Nullable; + import io.a2a.util.Assert; import mutiny.zero.BackpressureStrategy; import mutiny.zero.Tube; @@ -45,7 +47,7 @@ public Boolean apply(Consumer throwableConsumer, T t) { source.subscribe(new ConsumingSubscriber<>(nextBiFunction, errorConsumer)); }) .subscribe(new Flow.Subscriber() { - private Flow.Subscription subscription; + private Flow.@Nullable Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { @@ -55,17 +57,23 @@ public void onSubscribe(Flow.Subscription subscription) { @Override public void onNext(Object item) { - subscription.request(1); + if (subscription != null) { + subscription.request(1); + } } @Override public void onError(Throwable throwable) { - subscription.cancel(); + if (subscription != null) { + subscription.cancel(); + } } @Override public void onComplete() { - subscription.cancel(); + if (subscription != null) { + subscription.cancel(); + } } }); } @@ -86,25 +94,25 @@ public static Flow.Publisher convertingProcessor(Flow.Publisher sou private static abstract class AbstractSubscriber implements Flow.Subscriber { - private Flow.Subscription subscription; + private Flow.@Nullable Subscription subscription; private final BiFunction, T, Boolean> nextFunction; - private final Consumer publishNextConsumer; - private final Consumer failureOrCompleteConsumer; + private final @Nullable Consumer publishNextConsumer; + private final @Nullable Consumer failureOrCompleteConsumer; protected AbstractSubscriber( BiFunction, T, Boolean> nextFunction, - Consumer publishNextConsumer, - Consumer failureOrCompleteConsumer) { + @Nullable Consumer publishNextConsumer, + @Nullable Consumer failureOrCompleteConsumer) { Assert.checkNotNullParam("nextFunction", nextFunction); this.nextFunction = nextFunction; - this.publishNextConsumer = publishNextConsumer != null ? publishNextConsumer : t -> {}; - this.failureOrCompleteConsumer = failureOrCompleteConsumer != null ? failureOrCompleteConsumer : t -> {}; + this.publishNextConsumer = publishNextConsumer; + this.failureOrCompleteConsumer = failureOrCompleteConsumer; } @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; - this.subscription.request(1); + subscription.request(1); } @Override @@ -124,19 +132,25 @@ public void onNext(T item) { } } if (!continueProcessing || errorRaised.get() != null) { - subscription.cancel(); + if (subscription != null) { + subscription.cancel(); + } } else { if (publishNextConsumer != null) { publishNextConsumer.accept(item); } - subscription.request(1); + if (subscription != null) { + subscription.request(1); + } } } @Override public void onError(Throwable throwable) { - subscription.cancel(); + if (subscription != null) { + subscription.cancel(); + } if (failureOrCompleteConsumer != null) { failureOrCompleteConsumer.accept(throwable); } @@ -144,7 +158,9 @@ public void onError(Throwable throwable) { @Override public void onComplete() { - subscription.cancel(); + if (subscription != null) { + subscription.cancel(); + } if (failureOrCompleteConsumer != null) { failureOrCompleteConsumer.accept(null); } @@ -153,15 +169,12 @@ public void onComplete() { private static class ConsumingSubscriber extends AbstractSubscriber { public ConsumingSubscriber(BiFunction, T, Boolean> nextFunction, - Consumer failureOrCompleteConsumer) { + @Nullable Consumer failureOrCompleteConsumer) { super(nextFunction, null, failureOrCompleteConsumer); } } private static class ProcessingSubscriber extends AbstractSubscriber { - private Flow.Subscription subscription; - private final Tube tube; - public ProcessingSubscriber(Tube tube, BiFunction, T, Boolean> nextFunction) { super( nextFunction, @@ -175,13 +188,12 @@ public ProcessingSubscriber(Tube tube, BiFunction, T, Boo } ); Assert.checkNotNullParam("tube", tube); - this.tube = tube; } } private static class ConvertingProcessingSubscriber implements Flow.Subscriber { - private Flow.Subscription subscription; - private Tube tube; + private Flow.@Nullable Subscription subscription; + private final Tube tube; private final BiFunction, T, N> converterBiFunction; public ConvertingProcessingSubscriber(Tube tube, Function converterFunction) { @@ -201,7 +213,7 @@ public ConvertingProcessingSubscriber(Tube tube, BiFunction @@ -37,6 +40,7 @@ * @see Message * @see A2A Protocol Specification */ +@NullMarked public final class Task implements EventKind, StreamingEventKind { public static final String TASK = "task"; @@ -54,13 +58,13 @@ public Task(String id, String contextId, TaskStatus status, List artif } public Task(String id, String contextId, TaskStatus status, - List artifacts, List history, + @Nullable List artifacts, @Nullable List history, Map metadata, String kind) { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("contextId", contextId); Assert.checkNotNullParam("status", status); Assert.checkNotNullParam("kind", kind); - if (! kind.equals(TASK)) { + if (! TASK.equals(kind)) { throw new IllegalArgumentException("Invalid Task"); } this.id = id; @@ -130,6 +134,7 @@ public static Builder builder() { * .build(); * } */ + @SuppressWarnings("NullAway") public static class Builder { private String id; private String contextId; diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index 5c085f981..a117478c1 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -14,6 +14,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; +import java.util.List; @ApplicationScoped public class AgentExecutorProducer { @@ -22,24 +23,34 @@ public class AgentExecutorProducer { public AgentExecutor agentExecutor() { return new FireAndForgetAgentExecutor(); } - + private static class FireAndForgetAgentExecutor implements AgentExecutor { + @Override public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { Task task = context.getTask(); if (task == null) { + if (context == null) { + throw new IllegalArgumentException("RequestContext may not be null"); + } + if (context.getTaskId() == null) { + throw new IllegalArgumentException("Parameter 'id' may not be null"); + } + if (context.getContextId() == null) { + throw new IllegalArgumentException("Parameter 'contextId' may not be null"); + } task = new Task.Builder() .id(context.getTaskId()) .contextId(context.getContextId()) .status(new TaskStatus(TaskState.SUBMITTED)) - .history(context.getMessage()) + .history(List.of(context.getMessage())) .build(); eventQueue.enqueueEvent(task); } // Sleep to allow task state persistence before TCK resubscribe test - if (context.getMessage().getMessageId().startsWith("test-resubscribe-message-id")) { + if (context.getMessage() != null && context.getMessage().getMessageId().startsWith("test-resubscribe-message-id")) { int timeoutMs = Integer.parseInt(System.getenv().getOrDefault("RESUBSCRIBE_TIMEOUT_MS", "3000")); System.out.println("====> task id starts with test-resubscribe-message-id, sleeping for " + timeoutMs + " ms"); try { @@ -53,7 +64,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP // Immediately set to WORKING state updater.startWork(); System.out.println("====> task set to WORKING, starting background execution"); - + // Method returns immediately - task continues in background System.out.println("====> execute() method returning immediately, task running in background"); } @@ -62,12 +73,15 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { System.out.println("====> task cancel request received"); Task task = context.getTask(); - + if (task == null) { + System.out.println("====> No task found"); + throw new TaskNotCancelableError(); + } if (task.getStatus().state() == TaskState.CANCELED) { System.out.println("====> task already canceled"); throw new TaskNotCancelableError(); } - + if (task.getStatus().state() == TaskState.COMPLETED) { System.out.println("====> task already completed"); throw new TaskNotCancelableError(); @@ -81,7 +95,7 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC .status(new TaskStatus(TaskState.CANCELED)) .isFinal(true) .build()); - + System.out.println("====> task canceled"); } @@ -91,6 +105,6 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC @PreDestroy public void cleanup() { System.out.println("====> shutting down task executor"); - } + } } -} \ No newline at end of file +} From ec0ef925a6c519b384c69bcd95f58d6ad1472145 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 12 Dec 2025 15:37:05 +0000 Subject: [PATCH 229/493] chore: Update repository path for a2a-tck checkout (#538) We now use https://github.com/a2aproject/a2a-tck/tree/spec_1.0 as the upstream for the TCK --- .github/workflows/run-tck-1.0-wip.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tck-1.0-wip.yml b/.github/workflows/run-tck-1.0-wip.yml index ec97b9fa6..d7469d1e6 100644 --- a/.github/workflows/run-tck-1.0-wip.yml +++ b/.github/workflows/run-tck-1.0-wip.yml @@ -42,7 +42,7 @@ jobs: - name: Checkout a2a-tck uses: actions/checkout@v4 with: - repository: jmesnil/a2a-tck + repository: a2aproject/a2a-tck path: tck/a2a-tck ref: ${{ env.TCK_VERSION }} - name: Set up JDK ${{ matrix.java-version }} From a2499fb9fcf0dd47876abf90a438b726aa2baba9 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 16 Dec 2025 09:11:51 +0000 Subject: [PATCH 230/493] feat!: Replace public Builder constructors with static factory methods (#540) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #498 🦕 --- README.md | 10 +- client/base/src/main/java/io/a2a/A2A.java | 4 +- .../src/main/java/io/a2a/client/Client.java | 6 +- .../java/io/a2a/client/ClientTaskManager.java | 8 +- .../AuthenticationAuthorizationTest.java | 8 +- .../java/io/a2a/client/ClientBuilderTest.java | 11 +- .../transport/jsonrpc/JSONRPCTransport.java | 2 +- .../JSONRPCTransportStreamingTest.java | 12 +- .../jsonrpc/JSONRPCTransportTest.java | 40 +++--- .../jsonrpc/sse/SSEEventListenerTest.java | 2 +- .../transport/rest/RestTransportTest.java | 18 +-- .../auth/AuthInterceptorTest.java | 14 +- .../cloud/CloudAgentCardProducer.java | 6 +- .../examples/cloud/A2ACloudExampleClient.java | 6 +- .../helloworld/AgentCardProducer.java | 6 +- ...paDatabasePushNotificationConfigStore.java | 2 +- ...otificationConfigStoreIntegrationTest.java | 8 +- ...ationConfigStoreTestAgentCardProducer.java | 4 +- .../JpaPushNotificationConfigStoreTest.java | 10 +- .../core/EventSerializationTest.java | 12 +- .../core/ReplicatedQueueManagerTest.java | 10 +- ...ctiveMessagingReplicationStrategyTest.java | 4 +- .../MultiInstanceReplicationAgentCards.java | 6 +- .../MultiInstanceReplicationTest.java | 12 +- .../KafkaReplicationIntegrationTest.java | 10 +- .../ReplicationTestAgentCardProducer.java | 4 +- .../database/jpa/JpaDatabaseTaskStore.java | 2 +- .../JpaDatabaseTaskStoreIntegrationTest.java | 4 +- .../jpa/JpaDatabaseTaskStoreTest.java | 104 +++++++------- ...atabaseTaskStoreTestAgentCardProducer.java | 4 +- .../apps/quarkus/A2AServerRoutesTest.java | 18 +-- .../DefaultRequestHandler.java | 2 +- .../InMemoryPushNotificationConfigStore.java | 2 +- .../a2a/server/tasks/InMemoryTaskStore.java | 5 +- .../java/io/a2a/server/tasks/TaskManager.java | 6 +- .../java/io/a2a/server/tasks/TaskUpdater.java | 8 +- .../io/a2a/server/AgentCardValidatorTest.java | 4 +- .../agentexecution/RequestContextTest.java | 74 +++++----- .../a2a/server/events/EventConsumerTest.java | 14 +- .../io/a2a/server/events/EventQueueTest.java | 6 +- .../server/extensions/A2AExtensionsTest.java | 16 +-- .../AbstractA2ARequestHandlerTest.java | 8 +- .../DefaultRequestHandlerTest.java | 68 ++++----- ...MemoryPushNotificationConfigStoreTest.java | 22 ++- .../tasks/PushNotificationSenderTest.java | 4 +- .../server/tasks/ResultAggregatorTest.java | 4 +- .../io/a2a/server/tasks/TaskManagerTest.java | 96 ++++++------- .../io/a2a/server/tasks/TaskUpdaterTest.java | 2 +- .../mapper/SubscribeToTaskRequestMapper.java | 7 +- .../grpc/mapper/StreamResponseMapperTest.java | 14 +- .../io/a2a/grpc/utils/JSONRPCUtilsTest.java | 3 +- .../java/io/a2a/grpc/utils/ToProtoTest.java | 40 +++--- .../io/a2a/spec/APIKeySecurityScheme.java | 17 ++- .../java/io/a2a/spec/AgentCapabilities.java | 12 +- spec/src/main/java/io/a2a/spec/AgentCard.java | 47 ++++--- .../java/io/a2a/spec/AgentCardSignature.java | 13 +- .../main/java/io/a2a/spec/AgentExtension.java | 13 +- .../src/main/java/io/a2a/spec/AgentSkill.java | 13 +- spec/src/main/java/io/a2a/spec/Artifact.java | 28 +++- .../java/io/a2a/spec/CancelTaskRequest.java | 15 ++ ...eleteTaskPushNotificationConfigParams.java | 15 ++ ...leteTaskPushNotificationConfigRequest.java | 15 ++ .../GetAuthenticatedExtendedCardRequest.java | 15 ++ .../GetTaskPushNotificationConfigParams.java | 15 ++ .../GetTaskPushNotificationConfigRequest.java | 15 ++ .../main/java/io/a2a/spec/GetTaskRequest.java | 15 ++ .../io/a2a/spec/HTTPAuthSecurityScheme.java | 15 ++ ...ListTaskPushNotificationConfigRequest.java | 15 ++ .../java/io/a2a/spec/ListTasksParams.java | 15 ++ .../java/io/a2a/spec/ListTasksRequest.java | 15 ++ .../java/io/a2a/spec/ListTasksResult.java | 15 ++ spec/src/main/java/io/a2a/spec/Message.java | 16 ++- .../io/a2a/spec/MessageSendConfiguration.java | 15 ++ .../java/io/a2a/spec/MessageSendParams.java | 15 ++ .../io/a2a/spec/OAuth2SecurityScheme.java | 15 ++ .../src/main/java/io/a2a/spec/OAuthFlows.java | 15 ++ .../a2a/spec/OpenIdConnectSecurityScheme.java | 15 ++ .../io/a2a/spec/PushNotificationConfig.java | 25 +++- .../java/io/a2a/spec/SendMessageRequest.java | 15 ++ .../a2a/spec/SendStreamingMessageRequest.java | 15 ++ .../SetTaskPushNotificationConfigRequest.java | 15 ++ .../io/a2a/spec/SubscribeToTaskRequest.java | 15 ++ spec/src/main/java/io/a2a/spec/Task.java | 22 ++- .../io/a2a/spec/TaskArtifactUpdateEvent.java | 22 ++- .../io/a2a/spec/TaskStatusUpdateEvent.java | 18 ++- spec/src/main/java/io/a2a/util/Utils.java | 4 +- .../io/a2a/spec/TaskSerializationTest.java | 17 ++- .../io/a2a/tck/server/AgentCardProducer.java | 6 +- .../a2a/tck/server/AgentExecutorProducer.java | 4 +- .../apps/common/AbstractA2AServerTest.java | 131 +++++++++--------- .../server/apps/common/AgentCardProducer.java | 4 +- .../grpc/handler/GrpcHandlerTest.java | 22 +-- .../jsonrpc/handler/JSONRPCHandlerTest.java | 100 ++++++------- .../transport/rest/handler/RestHandler.java | 2 +- 94 files changed, 1038 insertions(+), 585 deletions(-) diff --git a/README.md b/README.md index 6245c8001..b404d7af0 100644 --- a/README.md +++ b/README.md @@ -113,19 +113,19 @@ public class WeatherAgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { - return new AgentCard.Builder() + return AgentCard.builder() .name("Weather Agent") .description("Helps with weather") .url("http://localhost:10001") .version("1.0.0") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(false) .stateTransitionHistory(false) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("weather_search") .name("Search weather") .description("Helps with weather in cities or states") @@ -565,12 +565,12 @@ TaskPushNotificationConfig config = client.getTaskPushNotificationConfiguration( ```java // Set task push notification configuration -PushNotificationConfig pushNotificationConfig = new PushNotificationConfig.Builder() +PushNotificationConfig pushNotificationConfig = PushNotificationConfig.builder() .url("https://example.com/callback") .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build(); -TaskPushNotificationConfig taskConfig = new TaskPushNotificationConfig.Builder() +TaskPushNotificationConfig taskConfig = TaskPushNotificationConfig.builder() .taskId("task-1234") .pushNotificationConfig(pushNotificationConfig) .build(); diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index 9056b661c..a0ccd5353 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -106,7 +106,7 @@ private static Message toMessage(String text, Message.Role role, String messageI } private static Message toMessage(String text, Message.Role role, String messageId, String contextId, String taskId) { - Message.Builder messageBuilder = new Message.Builder() + Message.Builder messageBuilder = Message.builder() .role(role) .parts(Collections.singletonList(new TextPart(text))) .contextId(contextId) @@ -118,7 +118,7 @@ private static Message toMessage(String text, Message.Role role, String messageI } private static Message toMessage(List> parts, Message.Role role, String messageId, String contextId, String taskId) { - Message.Builder messageBuilder = new Message.Builder() + Message.Builder messageBuilder = Message.builder() .role(role) .parts(parts) .contextId(contextId) diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index a539b3f31..0ca73446d 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -71,7 +71,7 @@ public void sendMessage(@NonNull Message request, @Nullable ClientCallContext context) throws A2AClientException { MessageSendConfiguration messageSendConfiguration = createMessageSendConfiguration(pushNotificationConfiguration); - MessageSendParams messageSendParams = new MessageSendParams.Builder() + MessageSendParams messageSendParams = MessageSendParams.builder() .message(request) .configuration(messageSendConfiguration) .metadata(metadata) @@ -169,7 +169,7 @@ private ClientEvent getClientEvent(StreamingEventKind event, ClientTaskManager t } private MessageSendConfiguration createMessageSendConfiguration(@Nullable PushNotificationConfig pushNotificationConfig) { - return new MessageSendConfiguration.Builder() + return MessageSendConfiguration.builder() .acceptedOutputModes(clientConfig.getAcceptedOutputModes()) .blocking(!clientConfig.isPolling()) .historyLength(clientConfig.getHistoryLength()) @@ -225,7 +225,7 @@ private void consume(ClientEvent clientEvent, AgentCard agentCard, @NonNull List private MessageSendParams getMessageSendParams(Message request, ClientConfig clientConfig) { MessageSendConfiguration messageSendConfiguration = createMessageSendConfiguration(clientConfig.getPushNotificationConfig()); - return new MessageSendParams.Builder() + return MessageSendParams.builder() .message(request) .configuration(messageSendConfiguration) .metadata(clientConfig.getMetadata()) diff --git a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java index f8737db9d..7272e1e0b 100644 --- a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java +++ b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java @@ -59,14 +59,14 @@ public Task saveTaskEvent(TaskStatusUpdateEvent taskStatusUpdateEvent) throws A2 } Task task = currentTask; if (task == null) { - task = new Task.Builder() + task = Task.builder() .status(new TaskStatus(TaskState.UNKNOWN)) .id(taskId) .contextId(contextId == null ? "" : contextId) .build(); } - Task.Builder taskBuilder = new Task.Builder(task); + Task.Builder taskBuilder = Task.builder(task); if (taskStatusUpdateEvent.getStatus().message() != null) { if (task.getHistory() == null) { taskBuilder.history(taskStatusUpdateEvent.getStatus().message()); @@ -95,7 +95,7 @@ public Task saveTaskEvent(TaskArtifactUpdateEvent taskArtifactUpdateEvent) { } Task task = currentTask; if (task == null) { - task = new Task.Builder() + task = Task.builder() .status(new TaskStatus(TaskState.UNKNOWN)) .id(taskId) .contextId(contextId == null ? "" : contextId) @@ -114,7 +114,7 @@ public Task saveTaskEvent(TaskArtifactUpdateEvent taskArtifactUpdateEvent) { * @return the updated task */ public Task updateWithMessage(Message message, Task task) { - Task.Builder taskBuilder = new Task.Builder(task); + Task.Builder taskBuilder = Task.builder(task); List history = task.getHistory(); if (history == null) { history = new ArrayList<>(); diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java index b06d0f49f..31c1ace88 100644 --- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -65,7 +65,7 @@ public class AuthenticationAuthorizationTest { @BeforeEach public void setUp() { server = new ClientAndServer(4001); - MESSAGE = new Message.Builder() + MESSAGE = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("test message"))) .contextId("context-1234") @@ -74,16 +74,16 @@ public void setUp() { grpcServerName = InProcessServerBuilder.generateName(); - agentCard = new AgentCard.Builder() + agentCard = AgentCard.builder() .name("Test Agent") .description("Test agent for auth tests") .version("1.0.0") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) // Support streaming for all tests .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("test_skill") .name("Test skill") .description("Test skill") diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index 4c92307e1..807eb5a43 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -21,20 +21,19 @@ public class ClientBuilderTest { - private AgentCard card = new AgentCard.Builder() + private AgentCard card = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") - .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("hello_world") .name("Returns hello world") .description("just returns hello world") @@ -42,8 +41,8 @@ public class ClientBuilderTest { .examples(List.of("hi", "hello world")) .build())) .protocolVersion("0.3.0") - .additionalInterfaces(List.of( - new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) + .supportedInterfaces(List.of( + new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) .build(); @Test diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 1febf62d1..8de407ebe 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -301,7 +301,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli return agentCard; } - GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = new GetAuthenticatedExtendedCardRequest.Builder() + GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = GetAuthenticatedExtendedCardRequest.builder() .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) .build(); // id will be randomly generated diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index 66091532e..b2a6e23ec 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -54,19 +54,19 @@ public void tearDown() { public void testSendStreamingMessageParams() { // The goal here is just to verify the correct parameters are being used // This is a unit test of the parameter construction, not the streaming itself - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("test message"))) .contextId("context-test") .messageId("message-test") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(false) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -95,17 +95,17 @@ public void testA2AClientSendStreamingMessage() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me some jokes"))) .contextId("context-1234") .messageId("message-1234") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(false) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index dcfa47c2e..4db981fe0 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -104,17 +104,17 @@ public void testA2AClientSendMessage() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) .contextId("context-1234") .messageId("message-1234") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(true) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -152,17 +152,17 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) .contextId("context-1234") .messageId("message-1234") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(true) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -194,17 +194,17 @@ public void testA2AClientSendMessageWithError() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) .contextId("context-1234") .messageId("message-1234") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(true) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -339,10 +339,10 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration( new TaskPushNotificationConfig("de38c76d-d54c-436c-8b9f-4c2703648d64", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("https://example.com/callback") - .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), + .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build()), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); @@ -532,7 +532,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(List.of( new TextPart("analyze this image"), @@ -541,11 +541,11 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { .contextId("context-1234") .messageId("message-1234-with-file") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(true) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -592,7 +592,7 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { data.put("location", "San Francisco"); data.put("timestamp", "2024-01-15T10:30:00Z"); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(List.of( new TextPart("process this data"), @@ -601,11 +601,11 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { .contextId("context-1234") .messageId("message-1234-with-data") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(true) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -649,7 +649,7 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { data.put("dataPoints", List.of(10, 20, 30, 40)); data.put("labels", List.of("Q1", "Q2", "Q3", "Q4")); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(List.of( new TextPart("analyze this data and image"), @@ -659,11 +659,11 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { .contextId("context-1234") .messageId("message-1234-with-mixed") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(true) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index da87cb985..9c25ac95a 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -185,7 +185,7 @@ public void testOnFailure() { @Test public void testFinalTaskStatusUpdateEventCancels() { - TaskStatusUpdateEvent tsue = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent tsue = TaskStatusUpdateEvent.builder() .taskId("1234") .contextId("xyz") .status(new TaskStatus(TaskState.COMPLETED)) diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index a1c79d9af..a40b3e9b2 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -67,20 +67,20 @@ public class RestTransportTest { private static final Logger log = Logger.getLogger(RestTransportTest.class.getName()); private ClientAndServer server; - private static final AgentCard CARD = new AgentCard.Builder() + private static final AgentCard CARD = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") .supportedInterfaces(Collections.singletonList(new io.a2a.spec.AgentInterface("HTTP+JSON", "http://localhost:4001"))) .version("1.0.0") .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("hello_world") .name("Returns hello world") .description("just returns hello world") @@ -108,7 +108,7 @@ public RestTransportTest() { */ @Test public void testSendMessage() throws Exception { - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) .contextId("context-1234") @@ -251,17 +251,17 @@ public void testSendMessageStreaming() throws Exception { ); RestTransport client = new RestTransport(CARD); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me some jokes"))) .contextId("context-1234") .messageId("message-1234") .build(); - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .blocking(false) .build(); - MessageSendParams params = new MessageSendParams.Builder() + MessageSendParams params = MessageSendParams.builder() .message(message) .configuration(configuration) .build(); @@ -303,10 +303,10 @@ public void testSetTaskPushNotificationConfiguration() throws Exception { RestTransport client = new RestTransport(CARD); TaskPushNotificationConfig pushedConfig = new TaskPushNotificationConfig( "de38c76d-d54c-436c-8b9f-4c2703648d64", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .id("default-config-id") .url("https://example.com/callback") - .authenticationInfo( + .authentication( new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build()); TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration(pushedConfig, null); diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java index e8807c594..ad85e0fa0 100644 --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java @@ -94,7 +94,7 @@ public void testOAuth2SecurityScheme() { "session-id", OAuth2SecurityScheme.OAUTH2, "secret-oauth-access-token", - new OAuth2SecurityScheme(new OAuthFlows.Builder().build(), "OAuth2 authentication", null), + new OAuth2SecurityScheme(OAuthFlows.builder().build(), "OAuth2 authentication", null), "Authorization", "Bearer secret-oauth-access-token" ); @@ -227,12 +227,12 @@ void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() { credentialStore.setCredential(sessionId, schemeName, credential); // Create agent card with security requirement but no scheme definition - AgentCard agentCard = new AgentCard.Builder() + AgentCard agentCard = AgentCard.builder() .name("missing") .description("Uses missing scheme definition") .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc"))) .version("1.0") - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) @@ -281,12 +281,12 @@ void testNoCredentialAvailable() { @Test void testNoAgentCardSecuritySpecified() { // Arrange - AgentCard agentCard = new AgentCard.Builder() + AgentCard agentCard = AgentCard.builder() .name("nosecuritybot") .description("A bot with no security requirements") .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc"))) .version("1.0") - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) @@ -313,12 +313,12 @@ void testNoAgentCardSecuritySpecified() { * Helper method to create an AgentCard with specified security scheme. */ private AgentCard createAgentCard(String schemeName, SecurityScheme securityScheme) { - return new AgentCard.Builder() + return AgentCard.builder() .name(schemeName + "bot") .description("A bot that uses " + schemeName) .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc"))) .version("1.0") - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java index acca34fd8..69b7a9970 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -27,14 +27,14 @@ public class CloudAgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { - return new AgentCard.Builder() + return AgentCard.builder() .name("Cloud Deployment Demo Agent") .description("Demonstrates A2A multi-pod deployment with Kafka event replication, " + "PostgreSQL persistence, and round-robin load balancing across Kubernetes pods") .supportedInterfaces(Collections.singletonList( new AgentInterface("JSONRPC", agentUrl))) .version("1.0.0") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(false) .stateTransitionHistory(false) @@ -42,7 +42,7 @@ public AgentCard agentCard() { .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(Collections.singletonList( - new AgentSkill.Builder() + AgentSkill.builder() .id("multi_pod_demo") .name("Multi-Pod Replication Demo") .description("Demonstrates cross-pod event replication. " + diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java index c02549a67..c4ce1ae36 100644 --- a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -107,7 +107,7 @@ private AgentCard fetchAndConfigureAgentCard() { System.out.println("✓ Description: " + fetchedCard.description()); // Override agent card URL to use the port-forwarded URL instead of internal K8s service URL - AgentCard agentCard = new AgentCard.Builder(fetchedCard) + AgentCard agentCard = AgentCard.builder(fetchedCard) .supportedInterfaces( Collections.singletonList( // Use localhost URL for port-forwarded connection @@ -278,7 +278,7 @@ private void sendProcessMessages() throws InterruptedException { .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) .build(); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(new TextPart("process")) .taskId(serverTaskId) @@ -323,7 +323,7 @@ private void sendCompleteMessage() { .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) .build(); - Message completeMessage = new Message.Builder() + Message completeMessage = Message.builder() .role(Message.Role.USER) .parts(new TextPart("complete")) .taskId(serverTaskId) diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index ffe610311..83a08c8d1 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -20,21 +20,21 @@ public class AgentCardProducer { public AgentCard agentCard() { // NOTE: Transport validation will automatically check that transports specified // in this AgentCard match those available on the classpath when handlers are initialized - return new AgentCard.Builder() + return AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") .supportedInterfaces(Collections.singletonList( new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("hello_world") .name("Returns hello world") .description("just returns hello world") diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index 835ffea38..b2eb7ba18 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -29,7 +29,7 @@ public class JpaDatabasePushNotificationConfigStore implements PushNotificationC @Override public PushNotificationConfig setInfo(String taskId, PushNotificationConfig notificationConfig) { // Ensure config has an ID - default to taskId if not provided (mirroring InMemoryPushNotificationConfigStore behavior) - PushNotificationConfig.Builder builder = new PushNotificationConfig.Builder(notificationConfig); + PushNotificationConfig.Builder builder = PushNotificationConfig.builder(notificationConfig); if (notificationConfig.id() == null || notificationConfig.id().isEmpty()) { builder.id(taskId); } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index 58681cf44..fd310acc1 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -78,7 +78,7 @@ public void testDirectNotificationTrigger() { // Simple test to verify the mock notification mechanism works mockPushNotificationSender.clear(); - Task testTask = new io.a2a.spec.Task.Builder() + Task testTask = Task.builder() .id("direct-test-task") .contextId("test-context") .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.SUBMITTED)) @@ -99,7 +99,7 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep final String contextId = "test-context"; // Step 1: Create the task - Message createMessage = new Message.Builder() + Message createMessage = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("create"))) // Send the "create" command .taskId(taskId) @@ -113,7 +113,7 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep assertTrue(createLatch.await(10, TimeUnit.SECONDS), "Timeout waiting for task creation"); // Step 2: Set the push notification configuration - PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() + PushNotificationConfig pushConfig = PushNotificationConfig.builder() .url("http://localhost:9999/mock-endpoint") .token("test-token-123") .id("test-config-1") @@ -134,7 +134,7 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep assertEquals("test-token-123", storedConfig.pushNotificationConfig().token()); // Step 4: Update the task to trigger the notification - Message updateMessage = new Message.Builder() + Message updateMessage = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("update"))) // Send the "update" command .taskId(taskId) diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java index 2060ead00..ac5e6ffdf 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentCardProducer.java @@ -24,7 +24,7 @@ public class JpaDatabasePushNotificationConfigStoreTestAgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { - return new AgentCard.Builder() + return AgentCard.builder() .name("JPA PushNotificationConfigStore Integration Test Agent") .description("Test agent for verifying JPA PushNotificationConfigStore integration") .version("1.0.0") @@ -33,7 +33,7 @@ public AgentCard agentCard() { new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) // Port is managed by QuarkusTest .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .pushNotifications(true) // Enable push notifications .streaming(true) // Enable streaming for automatic push notifications .build()) diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java index f8be8eea0..9566453a2 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java @@ -63,7 +63,7 @@ public void testIsJpaDatabasePushNotificationConfigStore() { } private Task createSampleTask(String taskId, TaskState state) { - return new Task.Builder() + return Task.builder() .id(taskId) .contextId("ctx456") .status(new TaskStatus(state)) @@ -71,7 +71,7 @@ private Task createSampleTask(String taskId, TaskState state) { } private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { - return new PushNotificationConfig.Builder() + return PushNotificationConfig.builder() .url(url) .id(configId) .token(token) @@ -133,7 +133,7 @@ public void testSetInfoAppendsToExistingConfig() { @Transactional public void testSetInfoWithoutConfigId() { String taskId = "task1"; - PushNotificationConfig initialConfig = new PushNotificationConfig.Builder() + PushNotificationConfig initialConfig = PushNotificationConfig.builder() .url("http://initial.url/callback") .build(); // No ID set @@ -144,7 +144,7 @@ public void testSetInfoWithoutConfigId() { assertEquals(1, configs.size()); assertEquals(taskId, configs.get(0).id()); - PushNotificationConfig updatedConfig = new PushNotificationConfig.Builder() + PushNotificationConfig updatedConfig = PushNotificationConfig.builder() .url("http://initial.url/callback_new") .build(); // No ID set @@ -213,7 +213,7 @@ public void testDeleteInfoNonExistentConfig() { @Transactional public void testDeleteInfoWithNullConfigId() { String taskId = "task_delete_null_config"; - PushNotificationConfig config = new PushNotificationConfig.Builder() + PushNotificationConfig config = PushNotificationConfig.builder() .url("http://delete.this/callback") .build(); // No ID set, will use taskId configStore.setInfo(taskId, config); diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index 3f7759153..c53cb610d 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -45,7 +45,7 @@ public class EventSerializationTest { public void testTaskSerialization() throws JsonProcessingException { // Create a Task TaskStatus status = new TaskStatus(TaskState.SUBMITTED); - Task originalTask = new Task.Builder() + Task originalTask = Task.builder() .id("test-task-123") .contextId("test-context-456") .status(status) @@ -74,7 +74,7 @@ public void testTaskSerialization() throws JsonProcessingException { @Test public void testMessageSerialization() throws JsonProcessingException { // Create a Message - Message originalMessage = new Message.Builder() + Message originalMessage = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("Hello, world!"))) .taskId("test-task-789") @@ -106,7 +106,7 @@ public void testMessageSerialization() throws JsonProcessingException { public void testTaskStatusUpdateEventSerialization() throws JsonProcessingException { // Create a TaskStatusUpdateEvent TaskStatus status = new TaskStatus(TaskState.COMPLETED); - TaskStatusUpdateEvent originalEvent = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent originalEvent = TaskStatusUpdateEvent.builder() .taskId("test-task-abc") .contextId("test-context-def") .status(status) @@ -140,7 +140,7 @@ public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingExce // Create a TaskArtifactUpdateEvent List> parts = List.of(new TextPart("Test artifact content")); Artifact artifact = new Artifact("test-artifact-123", "Test Artifact", "Test description", parts, null, null); - TaskArtifactUpdateEvent originalEvent = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent originalEvent = TaskArtifactUpdateEvent.builder() .taskId("test-task-xyz") .contextId("test-context-uvw") .artifact(artifact) @@ -202,7 +202,7 @@ public void testJSONRPCErrorSubclassesSerialization() throws JsonProcessingExcep @Test public void testReplicatedEventWithStreamingEventSerialization() throws JsonProcessingException { // Test that ReplicatedEventQueueItem can properly handle StreamingEventKind - TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder() .taskId("replicated-test-task") .contextId("replicated-test-context") .status(new TaskStatus(TaskState.WORKING)) @@ -276,7 +276,7 @@ public void testReplicatedEventWithErrorSerialization() throws JsonProcessingExc @Test public void testReplicatedEventBackwardCompatibility() throws JsonProcessingException { // Test backward compatibility with generic Event constructor - TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder() .taskId("backward-compat-task") .contextId("backward-compat-context") .status(new TaskStatus(TaskState.COMPLETED)) diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 42454ea3b..0915092ac 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -39,7 +39,7 @@ class ReplicatedQueueManagerTest { @BeforeEach void setUp() { queueManager = new ReplicatedQueueManager(new NoOpReplicationStrategy(), new MockTaskStateProvider(true)); - testEvent = new TaskStatusUpdateEvent.Builder() + testEvent = TaskStatusUpdateEvent.builder() .taskId("test-task") .contextId("test-context") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -190,7 +190,7 @@ void testQueueToTaskIdMappingMaintained() throws InterruptedException { @Test void testReplicatedEventJsonSerialization() throws Exception { // Test that ReplicatedEventQueueItem can be properly serialized and deserialized with StreamingEventKind - TaskStatusUpdateEvent originalEvent = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent originalEvent = TaskStatusUpdateEvent.builder() .taskId("json-test-task") .contextId("json-test-context") .status(new TaskStatus(TaskState.COMPLETED)) @@ -235,7 +235,7 @@ void testParallelReplicationBehavior() throws InterruptedException { try { startLatch.await(); for (int j = 0; j < eventsPerThread; j++) { - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("normal-" + threadId + "-" + j) .contextId("test-context") .status(new TaskStatus(TaskState.WORKING)) @@ -259,7 +259,7 @@ void testParallelReplicationBehavior() throws InterruptedException { try { startLatch.await(); for (int j = 0; j < eventsPerThread; j++) { - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("replicated-" + threadId + "-" + j) .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) @@ -366,7 +366,7 @@ void testReplicatedEventToExistingQueueWhenTaskBecomesInactive() throws Interrup stateProvider.setActive(false); // Process a replicated event - should be skipped - TaskStatusUpdateEvent newEvent = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent newEvent = TaskStatusUpdateEvent.builder() .taskId(taskId) .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java index 26976d1e3..593371f28 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java @@ -37,7 +37,7 @@ class ReactiveMessagingReplicationStrategyTest { @BeforeEach public void setUp() { - testEvent = new TaskStatusUpdateEvent.Builder() + testEvent = TaskStatusUpdateEvent.builder() .taskId("test-task") .contextId("test-context") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -47,7 +47,7 @@ public void setUp() { private String createValidJsonMessage(String taskId, String contextId) throws Exception { // Create a proper ReplicatedEventQueueItem JSON with StreamingEventKind - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java index 54287c9df..eff9c5b39 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java @@ -27,20 +27,20 @@ private MultiInstanceReplicationAgentCards() { public static AgentCard createAgentCard(int instanceNumber, int port) { String url = "http://localhost:" + port; - return new AgentCard.Builder() + return AgentCard.builder() .name("Multi-Instance Test Agent " + instanceNumber) .description("Test agent for multi-instance replication testing - Instance " + instanceNumber) .supportedInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), url))) .version("1.0.0") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(false) .stateTransitionHistory(false) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("replication_test") .name("Replication Test") .description("Fire-and-forget agent for testing replication") diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java index 282425fa7..2f3a3dbaa 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java @@ -147,13 +147,13 @@ public static void setup() { AgentCard originalApp2Card = A2A.getAgentCard(app2Url); // Rebuild AgentCards with correct URLs (mapped ports, not container ports) - app1Card = new AgentCard.Builder(originalApp1Card) + app1Card = AgentCard.builder(originalApp1Card) .supportedInterfaces(Collections.singletonList( new AgentInterface(TransportProtocol.JSONRPC.asString(), app1Url) )) .build(); - app2Card = new AgentCard.Builder(originalApp2Card) + app2Card = AgentCard.builder(originalApp2Card) .supportedInterfaces(Collections.singletonList( new AgentInterface(TransportProtocol.JSONRPC.asString(), app2Url) )) @@ -255,7 +255,7 @@ public void testMultiInstanceEventReplication() throws Exception { final String contextId = "replication-test-context"; // Step 1: Send initial message NON-streaming to create task - Message initialMessage = new Message.Builder(A2A.toUserMessage("Initial test message")) + Message initialMessage = Message.builder(A2A.toUserMessage("Initial test message")) .taskId(taskId) .contextId(contextId) .build(); @@ -347,7 +347,7 @@ public void testMultiInstanceEventReplication() throws Exception { int app1BeforeMsg1 = app1EventCount.get(); int app2BeforeMsg1 = app2EventCount.get(); - Message messageFromApp1 = new Message.Builder(A2A.toUserMessage("Message from app1")) + Message messageFromApp1 = Message.builder(A2A.toUserMessage("Message from app1")) .taskId(taskId) .contextId(contextId) .build(); @@ -364,7 +364,7 @@ public void testMultiInstanceEventReplication() throws Exception { int app1BeforeMsg2 = app1EventCount.get(); int app2BeforeMsg2 = app2EventCount.get(); - Message messageFromApp2 = new Message.Builder(A2A.toUserMessage("Message from app2")) + Message messageFromApp2 = Message.builder(A2A.toUserMessage("Message from app2")) .taskId(taskId) .contextId(contextId) .build(); @@ -381,7 +381,7 @@ public void testMultiInstanceEventReplication() throws Exception { int app1BeforeClose = app1EventCount.get(); int app2BeforeClose = app2EventCount.get(); - Message closeMessage = new Message.Builder(A2A.toUserMessage("close")) + Message closeMessage = Message.builder(A2A.toUserMessage("close")) .taskId(taskId) .contextId(contextId) .build(); diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 08c198281..58fa7f886 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -126,7 +126,7 @@ public void testA2AMessageReplicatedToKafka() throws Exception { testConsumer.clear(); // Send A2A message that should trigger events and replication - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("create"))) .taskId(taskId) @@ -189,7 +189,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { testConsumer.clear(); // First create a task in the A2A system using non-streaming client - Message createMessage = new Message.Builder() + Message createMessage = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("create"))) .taskId(taskId) @@ -253,7 +253,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { streamingClient.resubscribe(new TaskIdParams(taskId), List.of(consumer), errorHandler); // Now manually send a TaskStatusUpdateEvent to Kafka using reactive messaging - TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder() .taskId(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.COMPLETED)) @@ -298,7 +298,7 @@ public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { // Use polling (non-blocking) client with "working" command // This creates task in WORKING state (non-final) and keeps queue alive - Message workingMessage = new Message.Builder() + Message workingMessage = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("working"))) .taskId(taskId) @@ -386,7 +386,7 @@ public void testPoisonPillGenerationOnTaskFinalization() throws Exception { testConsumer.clear(); // Create a task that will be completed (finalized) - Message completeMessage = new Message.Builder() + Message completeMessage = Message.builder() .role(Message.Role.USER) .parts(List.of(new TextPart("complete"))) .taskId(taskId) diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java index 734e64700..b28317735 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java @@ -22,12 +22,12 @@ public class ReplicationTestAgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { - return new AgentCard.Builder() + return AgentCard.builder() .name("replication-test-agent") .description("Test agent for replicated queue manager integration testing") .version("1.0.0") .documentationUrl("http://localhost:8081/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index dede9022b..bbad23953 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -375,7 +375,7 @@ private Task transformTask(Task task, int historyLength, boolean includeArtifact } // Build new task with transformed data - return new Task.Builder(task) + return Task.builder(task) .artifacts(artifacts) .history(history) .build(); diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java index 3d1066553..a5d1aea91 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java @@ -74,7 +74,7 @@ public void testJpaDatabaseTaskStore() throws Exception { // Send a message creating the Task assertNull(taskStore.get(taskId)); - Message userMessage = new Message.Builder() + Message userMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("create"))) .taskId(taskId) @@ -103,7 +103,7 @@ public void testJpaDatabaseTaskStore() throws Exception { assertEquals(TaskState.SUBMITTED, createdTask.getStatus().state()); // Send a message updating the Task - userMessage = new Message.Builder() + userMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("add-artifact"))) .taskId(taskId) diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index 18d52ed4d..ae6962b76 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -46,7 +46,7 @@ public void testIsJpaDatabaseTaskStore() { @Transactional public void testSaveAndRetrieveTask() { // Create a test task - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-1") .contextId("test-context-1") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -69,14 +69,14 @@ public void testSaveAndRetrieveTask() { @Transactional public void testSaveAndRetrieveTaskWithHistory() { // Create a message for the task history - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("Hello, agent!"))) .messageId("msg-1") .build(); // Create a task with history - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-2") .contextId("test-context-2") .status(new TaskStatus(TaskState.WORKING)) @@ -102,7 +102,7 @@ public void testSaveAndRetrieveTaskWithHistory() { @Transactional public void testUpdateExistingTask() { // Create and save initial task - Task initialTask = new Task.Builder() + Task initialTask = Task.builder() .id("test-task-3") .contextId("test-context-3") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -111,7 +111,7 @@ public void testUpdateExistingTask() { taskStore.save(initialTask); // Update the task - Task updatedTask = new Task.Builder() + Task updatedTask = Task.builder() .id("test-task-3") .contextId("test-context-3") .status(new TaskStatus(TaskState.COMPLETED)) @@ -138,7 +138,7 @@ public void testGetNonExistentTask() { @Transactional public void testDeleteTask() { // Create and save a task - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-4") .contextId("test-context-4") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -172,7 +172,7 @@ public void testTaskWithComplexMetadata() { metadata.put("key2", 42); metadata.put("key3", true); - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-5") .contextId("test-context-5") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -195,7 +195,7 @@ public void testTaskWithComplexMetadata() { @Transactional public void testIsTaskActiveForNonFinalTask() { // Create a task in non-final state - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-active-1") .contextId("test-context") .status(new TaskStatus(TaskState.WORKING)) @@ -214,7 +214,7 @@ public void testIsTaskActiveForNonFinalTask() { @Transactional public void testIsTaskActiveForFinalTaskWithinGracePeriod() { // Create a task and update it to final state - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-active-2") .contextId("test-context") .status(new TaskStatus(TaskState.WORKING)) @@ -223,7 +223,7 @@ public void testIsTaskActiveForFinalTaskWithinGracePeriod() { taskStore.save(task); // Update to final state - Task finalTask = new Task.Builder() + Task finalTask = Task.builder() .id("test-task-active-2") .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) @@ -242,7 +242,7 @@ public void testIsTaskActiveForFinalTaskWithinGracePeriod() { @Transactional public void testIsTaskActiveForFinalTaskBeyondGracePeriod() { // Create and save a task in final state - Task task = new Task.Builder() + Task task = Task.builder() .id("test-task-active-3") .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) @@ -287,7 +287,7 @@ public void testIsTaskActiveForNonExistentTask() { @Transactional public void testListTasksEmpty() { // List with specific context that has no tasks - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("non-existent-context-12345") .build(); ListTasksResult result = taskStore.list(params); @@ -303,19 +303,19 @@ public void testListTasksEmpty() { @Transactional public void testListTasksFilterByContextId() { // Create tasks with different context IDs - Task task1 = new Task.Builder() + Task task1 = Task.builder() .id("task-context-1") .contextId("context-A") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - Task task2 = new Task.Builder() + Task task2 = Task.builder() .id("task-context-2") .contextId("context-A") .status(new TaskStatus(TaskState.WORKING)) .build(); - Task task3 = new Task.Builder() + Task task3 = Task.builder() .id("task-context-3") .contextId("context-B") .status(new TaskStatus(TaskState.COMPLETED)) @@ -326,7 +326,7 @@ public void testListTasksFilterByContextId() { taskStore.save(task3); // List tasks for context-A - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("context-A") .build(); ListTasksResult result = taskStore.list(params); @@ -341,19 +341,19 @@ public void testListTasksFilterByContextId() { @Transactional public void testListTasksFilterByStatus() { // Create tasks with different statuses - use unique context - Task task1 = new Task.Builder() + Task task1 = Task.builder() .id("task-status-filter-1") .contextId("context-status-filter-test") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - Task task2 = new Task.Builder() + Task task2 = Task.builder() .id("task-status-filter-2") .contextId("context-status-filter-test") .status(new TaskStatus(TaskState.WORKING)) .build(); - Task task3 = new Task.Builder() + Task task3 = Task.builder() .id("task-status-filter-3") .contextId("context-status-filter-test") .status(new TaskStatus(TaskState.COMPLETED)) @@ -364,7 +364,7 @@ public void testListTasksFilterByStatus() { taskStore.save(task3); // List only WORKING tasks in this context - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("context-status-filter-test") .status(TaskState.WORKING) .build(); @@ -380,19 +380,19 @@ public void testListTasksFilterByStatus() { @Transactional public void testListTasksCombinedFilters() { // Create tasks with various context IDs and statuses - Task task1 = new Task.Builder() + Task task1 = Task.builder() .id("task-combined-1") .contextId("context-X") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - Task task2 = new Task.Builder() + Task task2 = Task.builder() .id("task-combined-2") .contextId("context-X") .status(new TaskStatus(TaskState.WORKING)) .build(); - Task task3 = new Task.Builder() + Task task3 = Task.builder() .id("task-combined-3") .contextId("context-Y") .status(new TaskStatus(TaskState.WORKING)) @@ -403,7 +403,7 @@ public void testListTasksCombinedFilters() { taskStore.save(task3); // List WORKING tasks in context-X - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("context-X") .status(TaskState.WORKING) .build(); @@ -423,7 +423,7 @@ public void testListTasksPagination() { // (With timestamp DESC sorting, same timestamps allow ID ASC tie-breaking) OffsetDateTime sameTimestamp = OffsetDateTime.now(java.time.ZoneOffset.UTC); for (int i = 1; i <= 5; i++) { - Task task = new Task.Builder() + Task task = Task.builder() .id("task-page-" + i) .contextId("context-pagination") .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) @@ -432,7 +432,7 @@ public void testListTasksPagination() { } // First page: pageSize=2 - ListTasksParams params1 = new ListTasksParams.Builder() + ListTasksParams params1 = ListTasksParams.builder() .contextId("context-pagination") .pageSize(2) .build(); @@ -444,7 +444,7 @@ public void testListTasksPagination() { assertNotNull(result1.nextPageToken(), "Should have next page token"); // Second page: use pageToken from first page - ListTasksParams params2 = new ListTasksParams.Builder() + ListTasksParams params2 = ListTasksParams.builder() .contextId("context-pagination") .pageSize(2) .pageToken(result1.nextPageToken()) @@ -456,7 +456,7 @@ public void testListTasksPagination() { assertNotNull(result2.nextPageToken(), "Should have next page token"); // Third page: last page - ListTasksParams params3 = new ListTasksParams.Builder() + ListTasksParams params3 = ListTasksParams.builder() .contextId("context-pagination") .pageSize(2) .pageToken(result2.nextPageToken()) @@ -476,7 +476,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { OffsetDateTime now = OffsetDateTime.now(java.time.ZoneOffset.UTC); // Task 1: 10 minutes ago, ID="task-diff-a" - Task task1 = new Task.Builder() + Task task1 = Task.builder() .id("task-diff-a") .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(10))) @@ -484,7 +484,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { taskStore.save(task1); // Task 2: 5 minutes ago, ID="task-diff-b" - Task task2 = new Task.Builder() + Task task2 = Task.builder() .id("task-diff-b") .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(5))) @@ -492,7 +492,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { taskStore.save(task2); // Task 3: 5 minutes ago, ID="task-diff-c" (same timestamp as task2, tests ID tie-breaker) - Task task3 = new Task.Builder() + Task task3 = Task.builder() .id("task-diff-c") .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(5))) @@ -500,7 +500,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { taskStore.save(task3); // Task 4: Now, ID="task-diff-d" - Task task4 = new Task.Builder() + Task task4 = Task.builder() .id("task-diff-d") .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now)) @@ -508,7 +508,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { taskStore.save(task4); // Task 5: 1 minute ago, ID="task-diff-e" - Task task5 = new Task.Builder() + Task task5 = Task.builder() .id("task-diff-e") .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(1))) @@ -523,7 +523,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { // 5. task-diff-a (10 min ago) // Page 1: Get first 2 tasks - ListTasksParams params1 = new ListTasksParams.Builder() + ListTasksParams params1 = ListTasksParams.builder() .contextId("context-diff-timestamps") .pageSize(2) .build(); @@ -544,7 +544,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { assertEquals("task-diff-e", tokenParts[1], "PageToken should contain last task ID"); // Page 2: Get next 2 tasks - ListTasksParams params2 = new ListTasksParams.Builder() + ListTasksParams params2 = ListTasksParams.builder() .contextId("context-diff-timestamps") .pageSize(2) .pageToken(result1.nextPageToken()) @@ -560,7 +560,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { assertEquals("task-diff-c", result2.tasks().get(1).getId(), "Fourth task should be 5 min ago, ID 'c'"); // Page 3: Get last task - ListTasksParams params3 = new ListTasksParams.Builder() + ListTasksParams params3 = ListTasksParams.builder() .contextId("context-diff-timestamps") .pageSize(2) .pageToken(result2.nextPageToken()) @@ -590,7 +590,7 @@ public void testListTasksHistoryLimiting() { // Create messages for history List longHistory = new ArrayList<>(); for (int i = 1; i <= 10; i++) { - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("Message " + i))) .messageId("msg-history-limit-" + i) @@ -599,7 +599,7 @@ public void testListTasksHistoryLimiting() { } // Create task with long history - use unique context - Task task = new Task.Builder() + Task task = Task.builder() .id("task-history-limit-unique-1") .contextId("context-history-limit-unique") .status(new TaskStatus(TaskState.WORKING)) @@ -609,7 +609,7 @@ public void testListTasksHistoryLimiting() { taskStore.save(task); // List with historyLength=3 (should keep only last 3 messages) - filter by unique context - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("context-history-limit-unique") .historyLength(3) .build(); @@ -629,14 +629,14 @@ public void testListTasksHistoryLimiting() { public void testListTasksArtifactInclusion() { // Create task with artifacts - use unique context List artifacts = new ArrayList<>(); - Artifact artifact = new Artifact.Builder() + Artifact artifact = Artifact.builder() .artifactId("artifact-unique-1") .name("test-artifact") .parts(Collections.singletonList(new TextPart("Artifact content"))) .build(); artifacts.add(artifact); - Task task = new Task.Builder() + Task task = Task.builder() .id("task-artifact-unique-1") .contextId("context-artifact-unique") .status(new TaskStatus(TaskState.COMPLETED)) @@ -646,7 +646,7 @@ public void testListTasksArtifactInclusion() { taskStore.save(task); // List without artifacts (default) - filter by unique context - ListTasksParams paramsWithoutArtifacts = new ListTasksParams.Builder() + ListTasksParams paramsWithoutArtifacts = ListTasksParams.builder() .contextId("context-artifact-unique") .build(); ListTasksResult resultWithout = taskStore.list(paramsWithoutArtifacts); @@ -656,7 +656,7 @@ public void testListTasksArtifactInclusion() { "By default, artifacts should be excluded"); // List with artifacts - filter by unique context - ListTasksParams paramsWithArtifacts = new ListTasksParams.Builder() + ListTasksParams paramsWithArtifacts = ListTasksParams.builder() .contextId("context-artifact-unique") .includeArtifacts(true) .build(); @@ -673,7 +673,7 @@ public void testListTasksArtifactInclusion() { public void testListTasksDefaultPageSize() { // Create 100 tasks (more than default page size of 50) for (int i = 1; i <= 100; i++) { - Task task = new Task.Builder() + Task task = Task.builder() .id("task-default-pagesize-" + String.format("%03d", i)) .contextId("context-default-pagesize") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -682,7 +682,7 @@ public void testListTasksDefaultPageSize() { } // List without specifying pageSize (should use default of 50) - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("context-default-pagesize") .build(); ListTasksResult result = taskStore.list(params); @@ -696,7 +696,7 @@ public void testListTasksDefaultPageSize() { @Transactional public void testListTasksInvalidPageTokenFormat() { // Create a task - Task task = new Task.Builder() + Task task = Task.builder() .id("task-invalid-token") .contextId("context-invalid-token") .status(new TaskStatus(TaskState.WORKING)) @@ -704,7 +704,7 @@ public void testListTasksInvalidPageTokenFormat() { taskStore.save(task); // Test 1: Legacy ID-only pageToken should throw InvalidParamsError - ListTasksParams params1 = new ListTasksParams.Builder() + ListTasksParams params1 = ListTasksParams.builder() .contextId("context-invalid-token") .pageToken("task-invalid-token") // ID-only format (legacy) .build(); @@ -719,7 +719,7 @@ public void testListTasksInvalidPageTokenFormat() { } // Test 2: Malformed timestamp in pageToken should throw InvalidParamsError - ListTasksParams params2 = new ListTasksParams.Builder() + ListTasksParams params2 = ListTasksParams.builder() .contextId("context-invalid-token") .pageToken("not-a-number:task-id") // Invalid timestamp .build(); @@ -742,19 +742,19 @@ public void testListTasksOrderingById() { // (spec requires sorting by timestamp DESC, then ID ASC) OffsetDateTime sameTimestamp = OffsetDateTime.now(java.time.ZoneOffset.UTC); - Task task1 = new Task.Builder() + Task task1 = Task.builder() .id("task-order-a") .contextId("context-order") .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); - Task task2 = new Task.Builder() + Task task2 = Task.builder() .id("task-order-b") .contextId("context-order") .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); - Task task3 = new Task.Builder() + Task task3 = Task.builder() .id("task-order-c") .contextId("context-order") .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) @@ -766,7 +766,7 @@ public void testListTasksOrderingById() { taskStore.save(task2); // List should return sorted by timestamp DESC (all same), then by ID ASC - ListTasksParams params = new ListTasksParams.Builder() + ListTasksParams params = ListTasksParams.builder() .contextId("context-order") .build(); ListTasksResult result = taskStore.list(params); diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java index 65d31020f..00c055801 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentCardProducer.java @@ -23,13 +23,13 @@ public class JpaDatabaseTaskStoreTestAgentCardProducer { @Produces @PublicAgentCard public AgentCard agentCard() { - return new AgentCard.Builder() + return AgentCard.builder() .name("JPA TaskStore Integration Test Agent") .description("Test agent for verifying JPA TaskStore integration") .version("1.0.0") .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .skills(List.of()) .supportedInterfaces(Collections.singletonList( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081") diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java index abc7f2cef..178aa8302 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -133,7 +133,7 @@ public void testSendMessage_MethodNameSetInContext() { when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); // Create a real response with a Task - Task responseTask = new Task.Builder() + Task responseTask = Task.builder() .id("task-123") .contextId("context-1234") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -217,7 +217,7 @@ public void testGetTask_MethodNameSetInContext() { when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); // Create a real response with a Task - Task responseTask = new Task.Builder() + Task responseTask = Task.builder() .id("de38c76d-d54c-436c-8b9f-4c2703648d64") .contextId("context-1234") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -253,7 +253,7 @@ public void testCancelTask_MethodNameSetInContext() { when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); // Create a real response with a Task - Task responseTask = new Task.Builder() + Task responseTask = Task.builder() .id("de38c76d-d54c-436c-8b9f-4c2703648d64") .contextId("context-1234") .status(new TaskStatus(TaskState.CANCELED)) @@ -333,10 +333,10 @@ public void testSetTaskPushNotificationConfig_MethodNameSetInContext() { // Create a real response with a TaskPushNotificationConfig TaskPushNotificationConfig responseConfig = new TaskPushNotificationConfig( "de38c76d-d54c-436c-8b9f-4c2703648d64", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .id("config-123") .url("https://example.com/callback") - .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), null)) + .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build() ); SetTaskPushNotificationConfigResponse realResponse = new SetTaskPushNotificationConfigResponse("1", responseConfig); @@ -373,7 +373,7 @@ public void testGetTaskPushNotificationConfig_MethodNameSetInContext() { // Create a real response with a TaskPushNotificationConfig TaskPushNotificationConfig responseConfig = new TaskPushNotificationConfig( "de38c76d-d54c-436c-8b9f-4c2703648d64", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .id("config-456") .url("https://example.com/callback") .build() @@ -412,7 +412,7 @@ public void testListTaskPushNotificationConfig_MethodNameSetInContext() { // Create a real response with a list of TaskPushNotificationConfig TaskPushNotificationConfig config = new TaskPushNotificationConfig( "de38c76d-d54c-436c-8b9f-4c2703648d64", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .id("config-123") .url("https://example.com/callback") .build() @@ -474,12 +474,12 @@ public void testGetAuthenticatedExtendedCard_MethodNameSetInContext() { when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); // Create a real response with an AgentCard - AgentCard agentCard = new AgentCard.Builder() + AgentCard agentCard = AgentCard.builder() .name("Test Agent") .description("Test agent description") .version("1.0.0") .protocolVersion("1.0.0") - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(Collections.emptyList()) diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 3a760b136..3d3fe4b80 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -182,7 +182,7 @@ private static Task limitTaskHistory(Task task, int historyLength) { List limitedHistory = task.getHistory().subList( task.getHistory().size() - historyLength, task.getHistory().size()); - return new Task.Builder(task) + return Task.builder(task) .history(limitedHistory) .build(); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index 9ea8a244d..69ec5e15d 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -31,7 +31,7 @@ public InMemoryPushNotificationConfigStore() { @Override public PushNotificationConfig setInfo(String taskId, PushNotificationConfig notificationConfig) { List notificationConfigList = pushNotificationInfos.getOrDefault(taskId, new ArrayList<>()); - PushNotificationConfig.Builder builder = new PushNotificationConfig.Builder(notificationConfig); + PushNotificationConfig.Builder builder = PushNotificationConfig.builder(notificationConfig); if (notificationConfig.id() == null || notificationConfig.id().isEmpty()) { builder.id(taskId); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 92cde5587..fbc424511 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -7,13 +7,12 @@ import jakarta.enterprise.context.ApplicationScoped; -import org.jspecify.annotations.Nullable; - import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.Task; +import org.jspecify.annotations.Nullable; @ApplicationScoped public class InMemoryTaskStore implements TaskStore, TaskStateProvider { @@ -147,7 +146,7 @@ private Task transformTask(Task task, int historyLength, boolean includeArtifact } // Build new task with transformed data - return new Task.Builder(task) + return Task.builder(task) .artifacts(artifacts) .history(history) .build(); diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 0c83ca0cb..d0644b5d0 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -70,7 +70,7 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { Task task = ensureTask(event.getTaskId(), event.getContextId()); - Task.Builder builder = new Task.Builder(task) + Task.Builder builder = Task.builder(task) .status(event.getStatus()); if (task.getStatus().message() != null) { @@ -122,7 +122,7 @@ public Task updateWithMessage(Message message, Task task) { status = new TaskStatus(status.state(), null, status.timestamp()); } history.add(message); - task = new Task.Builder(task) + task = Task.builder(task) .status(status) .history(history) .build(); @@ -163,7 +163,7 @@ private Task ensureTask(String eventTaskId, String eventContextId) { private Task createTask(String taskId, String contextId) { List history = initialMessage != null ? List.of(initialMessage) : Collections.emptyList(); - return new Task.Builder() + return Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(SUBMITTED)) diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index a7271c1d2..c4044603d 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -50,7 +50,7 @@ public void updateStatus(TaskState state, @Nullable Message message, boolean isF terminalStateReached.set(true); } - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(taskId) .contextId(contextId) .isFinal(isFinal) @@ -81,11 +81,11 @@ public void addArtifact(List> parts, @Nullable String artifactId, @Nulla if (artifactId == null) { artifactId = UUID.randomUUID().toString(); } - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(taskId) .contextId(contextId) .artifact( - new Artifact.Builder() + Artifact.builder() .artifactId(artifactId) .name(name) .parts(parts) @@ -179,7 +179,7 @@ public void requiresAuth(@Nullable Message message, boolean isFinal) { } public Message newAgentMessage(List> parts, @Nullable Map metadata) { - return new Message.Builder() + return Message.builder() .role(Message.Role.AGENT) .taskId(taskId) .contextId(contextId) diff --git a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java index f9d87b03c..3ba6c4522 100644 --- a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java +++ b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java @@ -21,13 +21,13 @@ public class AgentCardValidatorTest { private AgentCard.Builder createTestAgentCardBuilder() { - return new AgentCard.Builder() + return AgentCard.builder() .name("Test Agent") .description("Test Description") .supportedInterfaces(Collections.singletonList( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) .version("1.0.0") - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(Collections.emptyList()); diff --git a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index f005c5456..7dfc0f6ac 100644 --- a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -37,8 +37,8 @@ public void testInitWithoutParams() { @Test public void testInitWithParamsNoIds() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); UUID taskId = UUID.fromString("00000000-0000-0000-0000-000000000001"); UUID contextId = UUID.fromString("00000000-0000-0000-0000-000000000002"); @@ -61,8 +61,8 @@ public void testInitWithParamsNoIds() { @Test public void testInitWithTaskId() { String taskId = "task-123"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, taskId, null, null, null, null); @@ -73,8 +73,8 @@ public void testInitWithTaskId() { @Test public void testInitWithContextId() { String contextId = "context-456"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(contextId).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, contextId, null, null, null); assertEquals(contextId, context.getContextId()); @@ -85,8 +85,8 @@ public void testInitWithContextId() { public void testInitWithBothIds() { String taskId = "task-123"; String contextId = "context-456"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(taskId).contextId(contextId).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null, null); assertEquals(taskId, context.getTaskId()); @@ -97,9 +97,9 @@ public void testInitWithBothIds() { @Test public void testInitWithTask() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, mockTask, null, null); @@ -114,7 +114,7 @@ public void testGetUserInputNoParams() { @Test public void testAttachRelatedTask() { - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); RequestContext context = new RequestContext(null, null, null, null, null, null); assertEquals(0, context.getRelatedTasks().size()); @@ -132,8 +132,8 @@ public void testAttachRelatedTask() { @Test public void testCheckOrGenerateTaskIdWithExistingTaskId() { String existingId = "existing-task-id"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId(existingId).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -145,8 +145,8 @@ public void testCheckOrGenerateTaskIdWithExistingTaskId() { public void testCheckOrGenerateContextIdWithExistingContextId() { String existingId = "existing-context-id"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).contextId(existingId).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -156,9 +156,9 @@ public void testCheckOrGenerateContextIdWithExistingContextId() { @Test public void testInitRaisesErrorOnTaskIdMismatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> new RequestContext(mockParams, "wrong-task-id", null, mockTask, null, null)); @@ -168,9 +168,9 @@ public void testInitRaisesErrorOnTaskIdMismatch() { @Test public void testInitRaisesErrorOnContextIdMismatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> new RequestContext(mockParams, mockTask.getId(), "wrong-context-id", mockTask, null, null)); @@ -180,7 +180,7 @@ public void testInitRaisesErrorOnContextIdMismatch() { @Test public void testWithRelatedTasksProvided() { - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); List relatedTasks = new ArrayList<>(); relatedTasks.add(mockTask); @@ -200,8 +200,8 @@ public void testMessagePropertyWithoutParams() { @Test public void testMessagePropertyWithParams() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(mockParams.message(), context.getMessage()); @@ -212,9 +212,9 @@ public void testInitWithExistingIdsInMessage() { String existingTaskId = "existing-task-id"; String existingContextId = "existing-context-id"; - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))) + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))) .taskId(existingTaskId).contextId(existingContextId).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); @@ -224,9 +224,9 @@ public void testInitWithExistingIdsInMessage() { @Test public void testInitWithTaskIdAndExistingTaskIdMatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); RequestContext context = new RequestContext(mockParams, mockTask.getId(), null, mockTask, null, null); @@ -237,9 +237,9 @@ public void testInitWithTaskIdAndExistingTaskIdMatch() { @Test public void testInitWithContextIdAndExistingContextIdMatch() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); - var mockTask = new Task.Builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).taskId("task-123").contextId("context-456").build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); + var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); RequestContext context = new RequestContext(mockParams, mockTask.getId(), mockTask.getContextId(), mockTask, null, null); @@ -250,8 +250,8 @@ public void testInitWithContextIdAndExistingContextIdMatch() { @Test void testMessageBuilderGeneratesId() { - var mockMessage = new Message.Builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertNotNull(mockMessage.getMessageId()); @@ -260,8 +260,8 @@ void testMessageBuilderGeneratesId() { @Test void testMessageBuilderUsesProvidedId() { - var mockMessage = new Message.Builder().messageId("123").role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); - var mockParams = new MessageSendParams.Builder().message(mockMessage).build(); + var mockMessage = Message.builder().messageId("123").role(Message.Role.USER).parts(List.of(new TextPart(""))).build(); + var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals("123", mockMessage.getMessageId()); diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 3e5611d9e..b19534e0e 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -94,15 +94,15 @@ public void testConsumeOneQueueEmpty() throws A2AServerException { public void testConsumeAllMultipleEvents() throws JsonProcessingException { List events = List.of( Utils.unmarshalFrom(MINIMAL_TASK, Task.class), - new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") - .artifact(new Artifact.Builder() + .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), - new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) @@ -155,15 +155,15 @@ public void onComplete() { public void testConsumeUntilMessage() throws Exception { List events = List.of( Utils.unmarshalFrom(MINIMAL_TASK, Task.class), - new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") - .artifact(new Artifact.Builder() + .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), - new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) @@ -215,7 +215,7 @@ public void onComplete() { @Test public void testConsumeMessageEvents() throws Exception { Message message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); - Message message2 = new Message.Builder(message).build(); + Message message2 = Message.builder(message).build(); List events = List.of(message, message2); diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 75888f151..7754fc87b 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -258,7 +258,7 @@ public void testDequeueEventEmptyQueueNoWait() throws Exception { @Test public void testDequeueEventWait() throws Exception { - Event event = new TaskStatusUpdateEvent.Builder() + Event event = TaskStatusUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) @@ -272,10 +272,10 @@ public void testDequeueEventWait() throws Exception { @Test public void testTaskDone() throws Exception { - Event event = new TaskArtifactUpdateEvent.Builder() + Event event = TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") - .artifact(new Artifact.Builder() + .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) diff --git a/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java b/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java index 56c715c49..27d5ca08a 100644 --- a/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java +++ b/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java @@ -64,16 +64,16 @@ void testGetRequestedExtensions() { @Test void testFindExtensionByUri() { - AgentExtension ext1 = new AgentExtension.Builder() + AgentExtension ext1 = AgentExtension.builder() .uri("foo") .description("The Foo extension") .build(); - AgentExtension ext2 = new AgentExtension.Builder() + AgentExtension ext2 = AgentExtension.builder() .uri("bar") .description("The Bar extension") .build(); - AgentCard card = new AgentCard.Builder() + AgentCard card = AgentCard.builder() .name("Test Agent") .description("Test Agent Description") .version("1.0") @@ -81,7 +81,7 @@ void testFindExtensionByUri() { .skills(Collections.emptyList()) .defaultInputModes(Arrays.asList("text/plain")) .defaultOutputModes(Arrays.asList("text/plain")) - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .extensions(Arrays.asList(ext1, ext2)) .build()) .build(); @@ -93,7 +93,7 @@ void testFindExtensionByUri() { @Test void testFindExtensionByUriNoExtensions() { - AgentCard card = new AgentCard.Builder() + AgentCard card = AgentCard.builder() .name("Test Agent") .description("Test Agent Description") .version("1.0") @@ -101,7 +101,7 @@ void testFindExtensionByUriNoExtensions() { .skills(Collections.emptyList()) .defaultInputModes(Arrays.asList("text/plain")) .defaultOutputModes(Arrays.asList("text/plain")) - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .extensions(null) .build()) .build(); @@ -112,7 +112,7 @@ void testFindExtensionByUriNoExtensions() { @Test void testFindExtensionByUriNoCapabilities() { // Test with empty capabilities (no extensions list) - AgentCard card = new AgentCard.Builder() + AgentCard card = AgentCard.builder() .name("Test Agent") .description("Test Agent Description") .version("1.0") @@ -120,7 +120,7 @@ void testFindExtensionByUriNoCapabilities() { .skills(Collections.emptyList()) .defaultInputModes(Arrays.asList("text/plain")) .defaultOutputModes(Arrays.asList("text/plain")) - .capabilities(new AgentCapabilities.Builder().build()) + .capabilities(AgentCapabilities.builder().build()) .build(); assertNull(A2AExtensions.findExtensionByUri(card, "foo")); diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index c11330f33..b8f1f89ba 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -51,13 +51,13 @@ public class AbstractA2ARequestHandlerTest { protected static final AgentCard CARD = createAgentCard(true, true, true); - protected static final Task MINIMAL_TASK = new Task.Builder() + protected static final Task MINIMAL_TASK = Task.builder() .id("task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - protected static final Message MESSAGE = new Message.Builder() + protected static final Message MESSAGE = Message.builder() .messageId("111") .role(Message.Role.AGENT) .parts(new TextPart("test message")) @@ -112,13 +112,13 @@ public void cleanup() { protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory) { String preferredTransport = loadPreferredTransportFromProperties(); - AgentCard.Builder builder = new AgentCard.Builder() + AgentCard.Builder builder = AgentCard.builder() .name("test-card") .description("A test agent card") .supportedInterfaces(Collections.singletonList(new AgentInterface(preferredTransport, "http://example.com"))) .version("1.0") .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(streaming) .pushNotifications(pushNotifications) .stateTransitionHistory(stateTransitionHistory) diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 099eeb851..208b989fd 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -86,14 +86,14 @@ void testBlockingMessageContinueTask() throws Exception { Task task = context.getTask(); if (task == null) { // First message: create SUBMITTED task - task = new Task.Builder() + task = Task.builder() .id(context.getTaskId()) .contextId(context.getContextId()) .status(new TaskStatus(TaskState.SUBMITTED)) .build(); } else { // Subsequent messages: emit WORKING task (non-final) - task = new Task.Builder() + task = Task.builder() .id(context.getTaskId()) .contextId(context.getContextId()) .status(new TaskStatus(TaskState.WORKING)) @@ -104,7 +104,7 @@ void testBlockingMessageContinueTask() throws Exception { }); // First blocking message - should return SUBMITTED task - Message message1 = new Message.Builder() + Message message1 = Message.builder() .messageId("msg-1") .role(Message.Role.USER) .parts(new TextPart("first message")) @@ -121,7 +121,7 @@ void testBlockingMessageContinueTask() throws Exception { assertTrue(task1.getStatus().state() == TaskState.SUBMITTED); // Second blocking message to SAME taskId - should not hang - Message message2 = new Message.Builder() + Message message2 = Message.builder() .messageId("msg-2") .role(Message.Role.USER) .parts(new TextPart("second message")) @@ -147,7 +147,7 @@ void testBackgroundCleanupTaskIsTrackedAndCleared() throws Exception { String contextId = "track-ctx-1"; // Create a task that will trigger background cleanup - Task task = new Task.Builder() + Task task = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.SUBMITTED)) @@ -155,7 +155,7 @@ void testBackgroundCleanupTaskIsTrackedAndCleared() throws Exception { taskStore.save(task); - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-track") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -206,7 +206,7 @@ void testStreamingClientDisconnectTriggersBackgroundCleanup() throws Exception { String taskId = "disc-task-1"; String contextId = "disc-ctx-1"; - Message message = new Message.Builder() + Message message = Message.builder() .messageId("mid") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -264,7 +264,7 @@ void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception String contextId = "reconn-ctx-1"; // Create initial task - Task initialTask = new Task.Builder() + Task initialTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) @@ -272,7 +272,7 @@ void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception taskStore.save(initialTask); - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-reconn") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -291,7 +291,7 @@ void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception agentStarted.countDown(); // Emit first event - Task firstEvent = new Task.Builder() + Task firstEvent = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) @@ -307,7 +307,7 @@ void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception } // Emit second event - Task secondEvent = new Task.Builder() + Task secondEvent = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.COMPLETED)) @@ -355,7 +355,7 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception { String taskId = "persist-task-1"; String contextId = "persist-ctx-1"; - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-persist") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -373,7 +373,7 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception { agentStarted.countDown(); // Emit working status - Task workingTask = new Task.Builder() + Task workingTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) @@ -388,7 +388,7 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception { } // Emit final completed status - Task completedTask = new Task.Builder() + Task completedTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.COMPLETED)) @@ -444,7 +444,7 @@ void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception { String taskId = "blocking-fire-forget-task"; String contextId = "blocking-fire-forget-ctx"; - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-blocking-fire-forget") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -452,7 +452,7 @@ void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception { .contextId(contextId) .build(); - MessageSendConfiguration config = new MessageSendConfiguration.Builder() + MessageSendConfiguration config = MessageSendConfiguration.builder() .blocking(true) .build(); @@ -508,7 +508,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { String taskId = "blocking-persist-task"; String contextId = "blocking-persist-ctx"; - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-nonblocking-persist") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -517,7 +517,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { .build(); // Use default non-blocking behavior - MessageSendConfiguration config = new MessageSendConfiguration.Builder() + MessageSendConfiguration config = MessageSendConfiguration.builder() .build(); MessageSendParams params = new MessageSendParams(message, config, null); @@ -531,7 +531,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { agentStarted.countDown(); // Emit first event (WORKING state) - Task workingTask = new Task.Builder() + Task workingTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) @@ -557,7 +557,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { // Emit final event (COMPLETED state) // This event should be persisted to TaskStore in background - Task completedTask = new Task.Builder() + Task completedTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.COMPLETED)) @@ -615,14 +615,14 @@ void testMainQueueStaysOpenForNonFinalTasks() throws Exception { String contextId = "fire-ctx"; // Create initial task in WORKING state (non-final) - Task initialTask = new Task.Builder() + Task initialTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) .build(); taskStore.save(initialTask); - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-fire") .role(Message.Role.USER) .parts(new TextPart("fire and forget")) @@ -640,7 +640,7 @@ void testMainQueueStaysOpenForNonFinalTasks() throws Exception { agentStarted.countDown(); // Emit WORKING status (non-final) - Task workingTask = new Task.Builder() + Task workingTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) @@ -699,7 +699,7 @@ void testMainQueueClosesForFinalizedTasks() throws Exception { String contextId = "completed-ctx"; // Create initial task in COMPLETED state (already finalized) - Task completedTask = new Task.Builder() + Task completedTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.COMPLETED)) @@ -743,7 +743,7 @@ void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception { String taskId = "blocking-artifacts-task"; String contextId = "blocking-artifacts-ctx"; - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-blocking-artifacts") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -751,7 +751,7 @@ void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception { .contextId(contextId) .build(); - MessageSendConfiguration config = new MessageSendConfiguration.Builder() + MessageSendConfiguration config = MessageSendConfiguration.builder() .blocking(true) .build(); @@ -827,14 +827,14 @@ void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exceptio ); // Create push notification config - PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() + PushNotificationConfig pushConfig = PushNotificationConfig.builder() .id("config-1") .url("https://example.com/webhook") .token("test-token-123") .build(); // Create message with pushNotificationConfig - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-push-config") .role(Message.Role.USER) .parts(new TextPart("test message")) @@ -842,7 +842,7 @@ void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exceptio .contextId(contextId) .build(); - MessageSendConfiguration config = new MessageSendConfiguration.Builder() + MessageSendConfiguration config = MessageSendConfiguration.builder() .blocking(true) .pushNotificationConfig(pushConfig) .build(); @@ -897,7 +897,7 @@ void testBlockingMessageStoresPushNotificationConfigForExistingTask() throws Exc ); // Create EXISTING task in store - Task existingTask = new Task.Builder() + Task existingTask = Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) @@ -905,13 +905,13 @@ void testBlockingMessageStoresPushNotificationConfigForExistingTask() throws Exc taskStore.save(existingTask); // Create push notification config - PushNotificationConfig pushConfig = new PushNotificationConfig.Builder() + PushNotificationConfig pushConfig = PushNotificationConfig.builder() .id("config-existing-1") .url("https://example.com/existing-webhook") .token("existing-token-789") .build(); - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-push-existing") .role(Message.Role.USER) .parts(new TextPart("update existing task")) @@ -919,7 +919,7 @@ void testBlockingMessageStoresPushNotificationConfigForExistingTask() throws Exc .contextId(contextId) .build(); - MessageSendConfiguration config = new MessageSendConfiguration.Builder() + MessageSendConfiguration config = MessageSendConfiguration.builder() .blocking(true) .pushNotificationConfig(pushConfig) .build(); @@ -979,7 +979,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP executeCallback.call(context, eventQueue); } else { // No custom callback - emit default completion event - Task completedTask = new Task.Builder() + Task completedTask = Task.builder() .id(context.getTaskId()) .contextId(context.getContextId()) .status(new TaskStatus(TaskState.COMPLETED)) diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java index 91b30e1af..aabeeedde 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java @@ -11,15 +11,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.mockito.ArgumentCaptor; - import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.common.A2AHeaders; @@ -27,6 +20,11 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; class InMemoryPushNotificationConfigStoreTest { @@ -74,7 +72,7 @@ private void verifyHttpCallWithoutToken(PushNotificationConfig config, Task task } private Task createSampleTask(String taskId, TaskState state) { - return new Task.Builder() + return Task.builder() .id(taskId) .contextId("ctx456") .status(new TaskStatus(state)) @@ -82,7 +80,7 @@ private Task createSampleTask(String taskId, TaskState state) { } private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { - return new PushNotificationConfig.Builder() + return PushNotificationConfig.builder() .url(url) .id(configId) .token(token) @@ -141,7 +139,7 @@ public void testSetInfoAppendsToExistingConfig() { @Test public void testSetInfoWithoutConfigId() { String taskId = "task1"; - PushNotificationConfig initialConfig = new PushNotificationConfig.Builder() + PushNotificationConfig initialConfig = PushNotificationConfig.builder() .url("http://initial.url/callback") .build(); // No ID set @@ -152,7 +150,7 @@ public void testSetInfoWithoutConfigId() { assertEquals(1, configs.size()); assertEquals(taskId, configs.get(0).id()); - PushNotificationConfig updatedConfig = new PushNotificationConfig.Builder() + PushNotificationConfig updatedConfig = PushNotificationConfig.builder() .url("http://initial.url/callback_new") .build(); // No ID set @@ -213,7 +211,7 @@ public void testDeleteInfoNonExistentConfig() { @Test public void testDeleteInfoWithNullConfigId() { String taskId = "task_delete_null_config"; - PushNotificationConfig config = new PushNotificationConfig.Builder() + PushNotificationConfig config = PushNotificationConfig.builder() .url("http://delete.this/callback") .build(); // No ID set, will use taskId configStore.setInfo(taskId, config); diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java index 9834b0e85..9a2186a68 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java @@ -173,7 +173,7 @@ private void testSendNotificationWithInvalidToken(String token, String testName) } private Task createSampleTask(String taskId, TaskState state) { - return new Task.Builder() + return Task.builder() .id(taskId) .contextId("ctx456") .status(new TaskStatus(state)) @@ -181,7 +181,7 @@ private Task createSampleTask(String taskId, TaskState state) { } private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { - return new PushNotificationConfig.Builder() + return PushNotificationConfig.builder() .url(url) .id(configId) .token(token) diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java index 0db54c373..d64729077 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -54,7 +54,7 @@ void setUp() { // Helper methods for creating sample data private Message createSampleMessage(String content, String msgId, Message.Role role) { - return new Message.Builder() + return Message.builder() .messageId(msgId) .role(role) .parts(Collections.singletonList(new TextPart(content))) @@ -62,7 +62,7 @@ private Message createSampleMessage(String content, String msgId, Message.Role r } private Task createSampleTask(String taskId, TaskState statusState, String contextId) { - return new Task.Builder() + return Task.builder() .id(taskId) .contextId(contextId) .status(new TaskStatus(statusState)) diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 0f8e5f16f..b2f440e95 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -75,7 +75,7 @@ public void testSaveTaskEventStatusUpdate() throws A2AServerException { TaskStatus newStatus = new TaskStatus( TaskState.WORKING, - new Message.Builder() + Message.builder() .role(Message.Role.AGENT) .parts(Collections.singletonList(new TextPart("content"))) .messageId("messageId") @@ -105,12 +105,12 @@ public void testSaveTaskEventStatusUpdate() throws A2AServerException { @Test public void testSaveTaskEventArtifactUpdate() throws A2AServerException { Task initialTask = minimalTask; - Artifact newArtifact = new Artifact.Builder() + Artifact newArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("content"))) .build(); - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(newArtifact) @@ -138,7 +138,7 @@ public void testEnsureTaskExisting() { public void testEnsureTaskNonExistentForStatusUpdate() throws A2AServerException { // Tests that an update event instantiates a new task and that TaskManager taskManagerWithoutId = new TaskManager(null, null, taskStore, null); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("new-task") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -159,7 +159,7 @@ public void testEnsureTaskNonExistentForStatusUpdate() throws A2AServerException @Test public void testSaveTaskEventNewTaskNoTaskId() throws A2AServerException { TaskManager taskManagerWithoutId = new TaskManager(null, null, taskStore, null); - Task task = new Task.Builder() + Task task = Task.builder() .id("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.WORKING)) @@ -187,23 +187,23 @@ public void testGetTaskNoTaskId() { public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A2AServerException { // Setup: Create a task with an existing artifact Task initialTask = minimalTask; - Artifact existingArtifact = new Artifact.Builder() + Artifact existingArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("existing content"))) .build(); - Task taskWithArtifact = new Task.Builder(initialTask) + Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) .build(); taskStore.save(taskWithArtifact); // Test: Append new parts to existing artifact - Artifact newArtifact = new Artifact.Builder() + Artifact newArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("new content"))) .build(); - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(newArtifact) @@ -227,12 +227,12 @@ public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throw taskStore.save(initialTask); // Test: Try to append to non-existent artifact (should be ignored) - Artifact newArtifact = new Artifact.Builder() + Artifact newArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("new content"))) .build(); - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(newArtifact) @@ -250,23 +250,23 @@ public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throw public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws A2AServerException { // Setup: Create a task with an existing artifact Task initialTask = minimalTask; - Artifact existingArtifact = new Artifact.Builder() + Artifact existingArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("existing content"))) .build(); - Task taskWithArtifact = new Task.Builder(initialTask) + Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) .build(); taskStore.save(taskWithArtifact); // Test: Replace existing artifact (append=false) - Artifact newArtifact = new Artifact.Builder() + Artifact newArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("replacement content"))) .build(); - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(newArtifact) @@ -287,23 +287,23 @@ public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws public void testTaskArtifactUpdateEventAppendNullWithExistingArtifact() throws A2AServerException { // Setup: Create a task with an existing artifact Task initialTask = minimalTask; - Artifact existingArtifact = new Artifact.Builder() + Artifact existingArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("existing content"))) .build(); - Task taskWithArtifact = new Task.Builder(initialTask) + Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) .build(); taskStore.save(taskWithArtifact); // Test: Replace existing artifact (append=null, defaults to false) - Artifact newArtifact = new Artifact.Builder() + Artifact newArtifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("replacement content"))) .build(); - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(newArtifact) @@ -324,7 +324,7 @@ public void testAddingTaskWithDifferentIdFails() { // Test that adding a task with a different id from the taskmanager's taskId fails TaskManager taskManagerWithId = new TaskManager("task-abc", "session-xyz", taskStore, null); - Task differentTask = new Task.Builder() + Task differentTask = Task.builder() .id("different-task-id") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -340,7 +340,7 @@ public void testAddingTaskWithDifferentIdViaStatusUpdateFails() { // Test that adding a status update with different taskId fails TaskManager taskManagerWithId = new TaskManager("task-abc", "session-xyz", taskStore, null); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("different-task-id") .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) @@ -357,12 +357,12 @@ public void testAddingTaskWithDifferentIdViaArtifactUpdateFails() { // Test that adding an artifact update with different taskId fails TaskManager taskManagerWithId = new TaskManager("task-abc", "session-xyz", taskStore, null); - Artifact artifact = new Artifact.Builder() + Artifact artifact = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("content"))) .build(); - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId("different-task-id") .contextId("session-xyz") .artifact(artifact) @@ -377,7 +377,7 @@ public void testAddingTaskWithDifferentIdViaArtifactUpdateFails() { public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException { // Test that adding a task with no message, and there is a TaskManager.initialMessage, // the initialMessage gets used - Message initialMessage = new Message.Builder() + Message initialMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("initial message"))) .messageId("initial-msg-id") @@ -386,7 +386,7 @@ public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException TaskManager taskManagerWithInitialMessage = new TaskManager(null, null, taskStore, initialMessage); // Use a status update event instead of a Task to trigger createTask - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -408,7 +408,7 @@ public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException @Test public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerException { // Test that adding a task with a message does not use the initial message - Message initialMessage = new Message.Builder() + Message initialMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("initial message"))) .messageId("initial-msg-id") @@ -416,14 +416,14 @@ public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerExcept TaskManager taskManagerWithInitialMessage = new TaskManager(null, null, taskStore, initialMessage); - Message taskMessage = new Message.Builder() + Message taskMessage = Message.builder() .role(Message.Role.AGENT) .parts(Collections.singletonList(new TextPart("task message"))) .messageId("task-msg-id") .build(); // Use TaskStatusUpdateEvent to trigger the creation of a task, which will check if the initialMessage is used. - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED, taskMessage, null)) @@ -451,12 +451,12 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException taskStore.save(initialTask); // Add first artifact - Artifact artifact1 = new Artifact.Builder() + Artifact artifact1 = Artifact.builder() .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("content 1"))) .build(); - TaskArtifactUpdateEvent event1 = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event1 = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(artifact1) @@ -464,12 +464,12 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException taskManager.saveTaskEvent(event1); // Add second artifact with same artifactId (should replace the first) - Artifact artifact2 = new Artifact.Builder() + Artifact artifact2 = Artifact.builder() .artifactId("artifact-id") .name("artifact-2") .parts(Collections.singletonList(new TextPart("content 2"))) .build(); - TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event2 = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(artifact2) @@ -491,12 +491,12 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce taskStore.save(initialTask); // Add first artifact - Artifact artifact1 = new Artifact.Builder() + Artifact artifact1 = Artifact.builder() .artifactId("artifact-id-1") .name("artifact-1") .parts(Collections.singletonList(new TextPart("content 1"))) .build(); - TaskArtifactUpdateEvent event1 = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event1 = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(artifact1) @@ -504,12 +504,12 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce taskManager.saveTaskEvent(event1); // Add second artifact with different artifactId (should be added) - Artifact artifact2 = new Artifact.Builder() + Artifact artifact2 = Artifact.builder() .artifactId("artifact-id-2") .name("artifact-2") .parts(Collections.singletonList(new TextPart("content 2"))) .build(); - TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event2 = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .artifact(artifact2) @@ -551,7 +551,7 @@ public void testSaveTaskEventMetadataUpdate() throws A2AServerException { Map newMetadata = new HashMap<>(); newMetadata.put("meta_key_test", "meta_value_test"); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .status(new TaskStatus(TaskState.WORKING)) @@ -571,7 +571,7 @@ public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { Task initialTask = minimalTask; taskStore.save(initialTask); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .status(new TaskStatus(TaskState.WORKING)) @@ -592,7 +592,7 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { Map originalMetadata = new HashMap<>(); originalMetadata.put("original_key", "original_value"); - Task taskWithMetadata = new Task.Builder(minimalTask) + Task taskWithMetadata = Task.builder(minimalTask) .metadata(originalMetadata) .build(); taskStore.save(taskWithMetadata); @@ -600,7 +600,7 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { Map newMetadata = new HashMap<>(); newMetadata.put("new_key", "new_value"); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(minimalTask.getId()) .contextId(minimalTask.getContextId()) .status(new TaskStatus(TaskState.WORKING)) @@ -620,7 +620,7 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { @Test public void testCreateTaskWithInitialMessage() throws A2AServerException { // Test equivalent of _init_task_obj functionality - Message initialMessage = new Message.Builder() + Message initialMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("initial message"))) .messageId("initial-msg-id") @@ -628,7 +628,7 @@ public void testCreateTaskWithInitialMessage() throws A2AServerException { TaskManager taskManagerWithMessage = new TaskManager(null, null, taskStore, initialMessage); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -656,7 +656,7 @@ public void testCreateTaskWithoutInitialMessage() throws A2AServerException { // Test task creation without initial message TaskManager taskManagerWithoutMessage = new TaskManager(null, null, taskStore, null); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -680,7 +680,7 @@ public void testSaveTaskInternal() throws A2AServerException { // Test equivalent of _save_task functionality through saveTaskEvent TaskManager taskManagerWithoutId = new TaskManager(null, null, taskStore, null); - Task newTask = new Task.Builder() + Task newTask = Task.builder() .id("test-task-id") .contextId("test-context") .status(new TaskStatus(TaskState.WORKING)) @@ -696,7 +696,7 @@ public void testSaveTaskInternal() throws A2AServerException { @Test public void testUpdateWithMessage() throws A2AServerException { - Message initialMessage = new Message.Builder() + Message initialMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("initial message"))) .messageId("initial-msg-id") @@ -704,13 +704,13 @@ public void testUpdateWithMessage() throws A2AServerException { TaskManager taskManagerWithInitialMessage = new TaskManager(null, null, taskStore, initialMessage); - Message taskMessage = new Message.Builder() + Message taskMessage = Message.builder() .role(Message.Role.AGENT) .parts(Collections.singletonList(new TextPart("task message"))) .messageId("task-msg-id") .build(); - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED, taskMessage, null)) @@ -719,7 +719,7 @@ public void testUpdateWithMessage() throws A2AServerException { Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); - Message updateMessage = new Message.Builder() + Message updateMessage = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("update message"))) .messageId("update-msg-id") diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 0e99f57b5..842ce72d4 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -29,7 +29,7 @@ public class TaskUpdaterTest { public static final String TEST_TASK_ID = "test-task-id"; public static final String TEST_TASK_CONTEXT_ID = "test-task-context-id"; - private static final Message SAMPLE_MESSAGE = new Message.Builder() + private static final Message SAMPLE_MESSAGE = Message.builder() .taskId(TEST_TASK_ID) .contextId(TEST_TASK_CONTEXT_ID) .parts(new TextPart("Test message")) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java index 6735612c5..10faa303f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java @@ -1,5 +1,6 @@ package io.a2a.grpc.mapper; +import io.a2a.spec.SubscribeToTaskRequest; import org.mapstruct.Mapper; /** @@ -27,7 +28,7 @@ public interface SubscribeToTaskRequestMapper { * @param domain the domain SubscribeToTaskRequest * @return the proto SubscribeToTaskRequest */ - default io.a2a.grpc.SubscribeToTaskRequest toProto(io.a2a.spec.SubscribeToTaskRequest domain) { + default io.a2a.grpc.SubscribeToTaskRequest toProto(SubscribeToTaskRequest domain) { if (domain == null || domain.getParams() == null || domain.getParams().id() == null) { return null; } @@ -43,11 +44,11 @@ default io.a2a.grpc.SubscribeToTaskRequest toProto(io.a2a.spec.SubscribeToTaskRe * @param proto the proto SubscribeToTaskRequest * @return the domain SubscribeToTaskRequest */ - default io.a2a.spec.SubscribeToTaskRequest fromProto(io.a2a.grpc.SubscribeToTaskRequest proto) { + default SubscribeToTaskRequest fromProto(io.a2a.grpc.SubscribeToTaskRequest proto) { if (proto == null || proto.getName() == null) { return null; } - return new io.a2a.spec.SubscribeToTaskRequest.Builder() + return SubscribeToTaskRequest.builder() .params(new io.a2a.spec.TaskIdParams(ResourceNameParser.extractTaskId(proto.getName()))) .build(); } diff --git a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java index e1023ceae..34c856208 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java @@ -22,7 +22,7 @@ public class StreamResponseMapperTest { @Test void testConvertTask_ToProto() { // Arrange - Task task = new Task.Builder() + Task task = Task.builder() .id("task-123") .contextId("context-456") .status(new TaskStatus(TaskState.COMPLETED)) @@ -67,7 +67,7 @@ void testConvertTask_FromProto() { @Test void testConvertMessage_ToProto() { // Arrange - Message message = new Message.Builder() + Message message = Message.builder() .messageId("msg-123") .contextId("context-456") .role(Message.Role.USER) @@ -114,7 +114,7 @@ void testConvertMessage_FromProto() { @Test void testConvertTaskStatusUpdateEvent_ToProto() { // Arrange - TaskStatusUpdateEvent event = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId("task-123") .contextId("context-456") .status(new TaskStatus(TaskState.WORKING)) @@ -163,10 +163,10 @@ void testConvertTaskStatusUpdateEvent_FromProto() { @Test void testConvertTaskArtifactUpdateEvent_ToProto() { // Arrange - TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("context-456") - .artifact(new Artifact.Builder() + .artifact(Artifact.builder() .artifactId("artifact-1") .name("result") .parts(new TextPart("Result text")) @@ -230,7 +230,7 @@ void testConvertStreamResponse_FromProto_PayloadNotSet_ThrowsException() { @Test void testConvertStreamResponse_Roundtrip_Task() { // Arrange - Task originalTask = new Task.Builder() + Task originalTask = Task.builder() .id("task-123") .contextId("context-456") .status(new TaskStatus(TaskState.SUBMITTED)) @@ -252,7 +252,7 @@ void testConvertStreamResponse_Roundtrip_Task() { @Test void testConvertStreamResponse_Roundtrip_Message() { // Arrange - Message originalMessage = new Message.Builder() + Message originalMessage = Message.builder() .messageId("msg-123") .contextId("context-456") .role(Message.Role.AGENT) diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java index ab4013202..b314615ab 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -15,6 +15,7 @@ import io.a2a.spec.InvalidParamsJsonMappingException; import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCRequest; +import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.TaskPushNotificationConfig; @@ -261,7 +262,7 @@ public void testParseInvalidTypeWithValidId_PreservesId() throws Exception { public void testGenerateSetTaskPushNotificationConfigResponse_Success() throws Exception { TaskPushNotificationConfig config = new TaskPushNotificationConfig( "task-123", - new io.a2a.spec.PushNotificationConfig.Builder() + PushNotificationConfig.builder() .url("https://example.com/callback") .id("config-456") .build() diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index 4039827fd..12588be66 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -35,7 +35,7 @@ public class ToProtoTest { - private static final Message SIMPLE_MESSAGE = new Message.Builder() + private static final Message SIMPLE_MESSAGE = Message.builder() .role(Message.Role.USER) .parts(Collections.singletonList(new TextPart("tell me a joke"))) .contextId("context-1234") @@ -44,20 +44,20 @@ public class ToProtoTest { @Test public void convertAgentCard() { - AgentCard agentCard = new AgentCard.Builder() + AgentCard agentCard = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("hello_world") .name("Returns hello world") .description("just returns hello world") @@ -79,20 +79,20 @@ public void convertAgentCard() { assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); assertEquals("1.0.0", result.getProtocolVersion()); - agentCard = new AgentCard.Builder() + agentCard = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) .version("1.0.0") .documentationUrl("http://example.com/docs") - .capabilities(new AgentCapabilities.Builder() + .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) - .skills(Collections.singletonList(new AgentSkill.Builder() + .skills(Collections.singletonList(AgentSkill.builder() .id("hello_world") .name("Returns hello world") .description("just returns hello world") @@ -100,7 +100,7 @@ public void convertAgentCard() { .examples(List.of("hi", "hello world")) .build())) // .iconUrl("http://example.com/icon.svg") - .securitySchemes(Map.of("basic", new HTTPAuthSecurityScheme.Builder().scheme("basic").description("Basic Auth").build())) + .securitySchemes(Map.of("basic", HTTPAuthSecurityScheme.builder().scheme("basic").description("Basic Auth").build())) .security(List.of(Map.of("oauth", List.of("read")))) .protocolVersion("1.0.0") .build(); @@ -132,7 +132,7 @@ public void convertAgentCard() { @Test public void convertTask() { - Task task = new Task.Builder().id("cancel-task-123") + Task task = Task.builder().id("cancel-task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); @@ -142,10 +142,10 @@ public void convertTask() { assertEquals(io.a2a.grpc.TaskState.TASK_STATE_SUBMITTED, result.getStatus().getState()); assertEquals(0, result.getArtifactsCount()); assertEquals(0, result.getHistoryCount()); - task = new Task.Builder().id("cancel-task-123") + task = Task.builder().id("cancel-task-123") .contextId("session-xyz") .status(new TaskStatus(TaskState.SUBMITTED)) - .artifacts(List.of(new Artifact.Builder() + .artifacts(List.of(Artifact.builder() .artifactId("11") .name("artefact") .parts(new TextPart("text")) @@ -185,7 +185,7 @@ public void convertMessage() { assertEquals("tell me a joke", result.getParts(0).getText()); assertEquals(io.a2a.grpc.FilePart.getDefaultInstance(), result.getParts(0).getFile()); assertEquals(io.a2a.grpc.DataPart.getDefaultInstance(), result.getParts(0).getData()); - Message message = new Message.Builder() + Message message = Message.builder() .role(Message.Role.AGENT) .parts(Collections.singletonList(new TextPart("tell me a joke"))) .messageId("message-1234") @@ -203,7 +203,7 @@ public void convertMessage() { @Test public void convertTaskPushNotificationConfig() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig("push-task-123", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .url("http://example.com") .id("xyz") .build()); @@ -215,9 +215,9 @@ public void convertTaskPushNotificationConfig() { assertEquals(false, result.getPushNotificationConfig().hasAuthentication()); taskPushConfig = new TaskPushNotificationConfig("push-task-123", - new PushNotificationConfig.Builder() + PushNotificationConfig.builder() .token("AAAAAA") - .authenticationInfo(new AuthenticationInfo(Collections.singletonList("jwt"), "credentials")) + .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), "credentials")) .url("http://example.com") .id("xyz") .build()); @@ -235,10 +235,10 @@ public void convertTaskPushNotificationConfig() { @Test public void convertTaskArtifactUpdateEvent() { - TaskArtifactUpdateEvent task = new TaskArtifactUpdateEvent.Builder() + TaskArtifactUpdateEvent task = TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("session-123") - .artifact(new Artifact.Builder() + .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()).build(); @@ -253,7 +253,7 @@ public void convertTaskArtifactUpdateEvent() { @Test public void convertTaskStatusUpdateEvent() { - TaskStatusUpdateEvent tsue = new TaskStatusUpdateEvent.Builder() + TaskStatusUpdateEvent tsue = TaskStatusUpdateEvent.builder() .taskId("1234") .contextId("xyz") .status(new TaskStatus(TaskState.COMPLETED)) @@ -268,7 +268,7 @@ public void convertTaskStatusUpdateEvent() { @Test public void convertSendMessageConfiguration() { - MessageSendConfiguration configuration = new MessageSendConfiguration.Builder() + MessageSendConfiguration configuration = MessageSendConfiguration.builder() .acceptedOutputModes(List.of("text")) .build(); SendMessageConfiguration result = ProtoUtils.ToProto.messageSendConfiguration(configuration); @@ -281,7 +281,7 @@ public void convertSendMessageConfiguration() { public void convertTaskTimestampStatus() { OffsetDateTime expectedTimestamp = OffsetDateTime.parse("2024-10-05T12:34:56Z"); TaskStatus testStatus = new TaskStatus(TaskState.COMPLETED, null, expectedTimestamp); - Task task = new Task.Builder() + Task task = Task.builder() .id("task-123") .contextId("context-456") .status(testStatus) diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index fd004ebb8..43141ce2e 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -148,12 +148,21 @@ public String getType() { return type; } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing immutable {@link APIKeySecurityScheme} instances. *

    * Example usage: *

    {@code
    -     * APIKeySecurityScheme scheme = new APIKeySecurityScheme.Builder()
    +     * APIKeySecurityScheme scheme = APIKeySecurityScheme.builder()
          *     .location(Location.HEADER)
          *     .name("X-API-Key")
          *     .description("API key authentication")
    @@ -165,6 +174,12 @@ public static class Builder {
             private String name;
             private String description;
     
    +        /**
    +         * Creates a new Builder with all fields unset.
    +         */
    +        private Builder() {
    +        }
    +
             /**
              * Sets the location where the API key should be sent.
              *
    diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java
    index f86d601a5..2913a2b97 100644
    --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java
    +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java
    @@ -34,6 +34,14 @@
     public record AgentCapabilities(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory,
                                     List extensions) {
     
    +    /**
    +     * Create a new Builder
    +     *
    +     * @return the builder
    +     */
    +    public static Builder builder() {
    +        return new Builder();
    +    }
         /**
          * Builder for constructing immutable {@link AgentCapabilities} instances.
          * 

    @@ -42,7 +50,7 @@ public record AgentCapabilities(boolean streaming, boolean pushNotifications, bo *

    * Example usage: *

    {@code
    -     * AgentCapabilities capabilities = new AgentCapabilities.Builder()
    +     * AgentCapabilities capabilities = AgentCapabilities.builder()
          *     .streaming(true)
          *     .pushNotifications(false)
          *     .stateTransitionHistory(false)
    @@ -59,7 +67,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all capabilities set to false by default.
              */
    -        public Builder() {
    +        private Builder() {
             }
     
             /**
    diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java
    index c61586422..573aa8c7a 100644
    --- a/spec/src/main/java/io/a2a/spec/AgentCard.java
    +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java
    @@ -89,6 +89,29 @@ public List additionalInterfaces() {
             return supportedInterfaces;
         }
     
    +    /**
    +     * Create a new Builder
    +     *
    +     * @return the builder
    +     */
    +    public static Builder builder() {
    +        return new Builder();
    +    }
    +
    +    /**
    +     * Create a new Builder initialized with values from an existing AgentCard.
    +     * 

    + * This builder creates defensive copies of mutable collections to ensure + * that modifications to the builder do not affect the original AgentCard. + * + * @param card the AgentCard to copy values from + * @return the builder + */ + public static Builder builder(AgentCard card) { + return new Builder(card); + } + + /** * Builder for constructing immutable {@link AgentCard} instances. *

    @@ -99,7 +122,7 @@ public List additionalInterfaces() { *

    * Example usage: *

    {@code
    -     * AgentCard card = new AgentCard.Builder()
    +     * AgentCard card = AgentCard.builder()
          *     .name("Weather Agent")
          *     .description("Provides weather information")
          *     .supportedInterfaces(List.of(
    @@ -140,7 +163,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all fields unset.
              */
    -        public Builder() {
    +        private Builder() {
     
             }
     
    @@ -152,7 +175,7 @@ public Builder() {
              *
              * @param card the AgentCard to copy values from
              */
    -        public Builder(AgentCard card) {
    +        private Builder(AgentCard card) {
                 this.name = card.name;
                 this.description = card.description;
                 this.provider = card.provider;
    @@ -352,24 +375,6 @@ public Builder supportedInterfaces(List supportedInterfaces) {
                 return this;
             }
     
    -        /**
    -         * Sets the list of additional transport interfaces.
    -         * 

    - * Additional interfaces allow the agent to be accessed through multiple - * transport protocols beyond the primary url/preferredTransport combination. - * - * @param additionalInterfaces the list of additional interfaces (optional) - * @return this builder for method chaining - * @see AgentInterface - * @deprecated Use {@link #supportedInterfaces(List)} instead. This field has been renamed to 'supportedInterfaces'. - */ - @Deprecated(since = "0.4.0", forRemoval = true) - public Builder additionalInterfaces(List additionalInterfaces) { - this.supportedInterfaces = additionalInterfaces; - return this; - } - - /** * Sets the version of the A2A Protocol this agent implements. *

    diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java index ab4edd70e..c9984dee9 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -44,12 +44,21 @@ public record AgentCardSignature(Map header, @SerializedName("pr Assert.checkNotNullParam("signature", signature); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing immutable {@link AgentCardSignature} instances. *

    * Example usage: *

    {@code
    -     * AgentCardSignature sig = new AgentCardSignature.Builder()
    +     * AgentCardSignature sig = AgentCardSignature.builder()
          *     .protectedHeader("eyJhbGciOiJFUzI1NiJ9")
          *     .signature("DtEhU3ljbEg8L38VWAfUAqOyKAM6...")
          *     .header(Map.of("kid", "2024-01"))
    @@ -64,7 +73,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all fields unset.
              */
    -        public Builder() {
    +        private Builder() {
             }
     
             /**
    diff --git a/spec/src/main/java/io/a2a/spec/AgentExtension.java b/spec/src/main/java/io/a2a/spec/AgentExtension.java
    index 8a8cb6613..60e541d65 100644
    --- a/spec/src/main/java/io/a2a/spec/AgentExtension.java
    +++ b/spec/src/main/java/io/a2a/spec/AgentExtension.java
    @@ -34,12 +34,21 @@ public record AgentExtension (String description, Map params, bo
             Assert.checkNotNullParam("uri", uri);
         }
     
    +    /**
    +     * Create a new Builder
    +     *
    +     * @return the builder
    +     */
    +    public static Builder builder() {
    +        return new Builder();
    +    }
    +
         /**
          * Builder for constructing immutable {@link AgentExtension} instances.
          * 

    * Example usage: *

    {@code
    -     * AgentExtension ext = new AgentExtension.Builder()
    +     * AgentExtension ext = AgentExtension.builder()
          *     .uri("https://example.com/extensions/custom-auth")
          *     .description("Custom authentication extension")
          *     .required(true)
    @@ -56,7 +65,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all fields unset.
              */
    -        public Builder() {
    +        private Builder() {
             }
     
             /**
    diff --git a/spec/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java
    index 2e0900af7..0140a2a7f 100644
    --- a/spec/src/main/java/io/a2a/spec/AgentSkill.java
    +++ b/spec/src/main/java/io/a2a/spec/AgentSkill.java
    @@ -55,6 +55,15 @@ public record AgentSkill(String id, String name, String description, List
    @@ -64,7 +73,7 @@ public record AgentSkill(String id, String name, String description, List
          * Example usage:
          * 
    {@code
    -     * AgentSkill skill = new AgentSkill.Builder()
    +     * AgentSkill skill = AgentSkill.builder()
          *     .id("weather_query")
          *     .name("Weather Queries")
          *     .description("Get current weather conditions for any location")
    @@ -92,7 +101,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all fields unset.
              */
    -        public Builder() {
    +        private Builder() {
             }
     
             /**
    diff --git a/spec/src/main/java/io/a2a/spec/Artifact.java b/spec/src/main/java/io/a2a/spec/Artifact.java
    index 5aa6870a7..b0c9abf84 100644
    --- a/spec/src/main/java/io/a2a/spec/Artifact.java
    +++ b/spec/src/main/java/io/a2a/spec/Artifact.java
    @@ -45,6 +45,28 @@ public record Artifact(String artifactId, String name, String description, List<
             }
         }
     
    +    /**
    +     * Create a new Builder
    +     *
    +     * @return the builder
    +     */
    +    public static Builder builder() {
    +        return new Builder();
    +    }
    +
    +    /**
    +     * Create a new Builder initialized with values from an existing Artifact.
    +     * 

    + * This builder creates defensive copies of mutable collections to ensure + * that modifications to the builder do not affect the original Artifact. + * + * @param artifact the Artifact to copy values from + * @return the builder + */ + public static Builder builder(Artifact artifact) { + return new Builder(artifact); + } + /** * Builder for constructing immutable {@link Artifact} instances. *

    @@ -53,7 +75,7 @@ public record Artifact(String artifactId, String name, String description, List< *

    * Example usage: *

    {@code
    -     * Artifact result = new Artifact.Builder()
    +     * Artifact result = Artifact.builder()
          *     .artifactId("artifact-123")
          *     .name("Analysis Report")
          *     .description("Detailed analysis of user data")
    @@ -72,7 +94,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all fields unset.
              */
    -        public Builder(){
    +        private Builder(){
             }
     
             /**
    @@ -80,7 +102,7 @@ public Builder(){
              *
              * @param existingArtifact the Artifact to copy values from
              */
    -        public Builder(Artifact existingArtifact) {
    +        private Builder(Artifact existingArtifact) {
                 artifactId = existingArtifact.artifactId;
                 name = existingArtifact.name;
                 description = existingArtifact.description;
    diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java
    index b2d97f0d9..0005c41c4 100644
    --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java
    +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java
    @@ -47,6 +47,15 @@ public CancelTaskRequest(Object id, TaskIdParams params) {
             this(null, id, params);
         }
     
    +    /**
    +     * Create a new Builder
    +     *
    +     * @return the builder
    +     */
    +    public static Builder builder() {
    +        return new Builder();
    +    }
    +
         /**
          * Builder for constructing {@link CancelTaskRequest} instances.
          * 

    @@ -58,6 +67,12 @@ public static class Builder { private Object id; private TaskIdParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the JSON-RPC protocol version. * diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index ae372ab43..bd4482666 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -40,6 +40,15 @@ public DeleteTaskPushNotificationConfigParams(String id, String pushNotification this(id, pushNotificationConfigId, null); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link DeleteTaskPushNotificationConfigParams} instances. *

    @@ -50,6 +59,12 @@ public static class Builder { String pushNotificationConfigId; Map metadata; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the task identifier. * diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java index b015ee8b3..509d4d85d 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -43,6 +43,15 @@ public DeleteTaskPushNotificationConfigRequest(String id, DeleteTaskPushNotifica this(null, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link DeleteTaskPushNotificationConfigRequest} instances. *

    @@ -54,6 +63,12 @@ public static class Builder { private Object id; private DeleteTaskPushNotificationConfigParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the JSON-RPC protocol version. * diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java index 79203a243..7a942a6a0 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -38,10 +38,25 @@ public GetAuthenticatedExtendedCardRequest(String id) { this(null, id); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public GetAuthenticatedExtendedCardRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index 906267cc4..aa40e5343 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -33,11 +33,26 @@ public GetTaskPushNotificationConfigParams(String id, String pushNotificationCon this(id, pushNotificationConfigId, null); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { String id; String pushNotificationConfigId; Map metadata; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder id(String id) { this.id = id; return this; diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index b655ba6a5..99b5cd1f9 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -27,11 +27,26 @@ public GetTaskPushNotificationConfigRequest(String id, GetTaskPushNotificationCo this(null, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; private GetTaskPushNotificationConfigParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public GetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java index cfdbc53e2..540030853 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java @@ -28,11 +28,26 @@ public GetTaskRequest(Object id, TaskQueryParams params) { this(null, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; private TaskQueryParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public GetTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 4e1ebc308..9f7efdada 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -75,6 +75,15 @@ public String getType() { return type; } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link HTTPAuthSecurityScheme} instances. *

    @@ -86,6 +95,12 @@ public static class Builder { private String scheme; private String description; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the bearer token format hint. * diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java index 5e98f2e96..5798fa7cb 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java @@ -27,11 +27,26 @@ public ListTaskPushNotificationConfigRequest(String id, ListTaskPushNotification this(null, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; private ListTaskPushNotificationConfigParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index b481c89c1..62ca74e96 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -83,6 +83,15 @@ public boolean shouldIncludeArtifacts() { return includeArtifacts != null && includeArtifacts; } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String contextId; private TaskState status; @@ -93,6 +102,12 @@ public static class Builder { private Boolean includeArtifacts; private Map metadata; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder contextId(String contextId) { this.contextId = contextId; return this; diff --git a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java index f21383546..2d855820c 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java @@ -17,11 +17,26 @@ public ListTasksRequest(Object id, ListTasksParams params) { this(JSONRPC_VERSION, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; private ListTasksParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResult.java b/spec/src/main/java/io/a2a/spec/ListTasksResult.java index 2c1780bde..6a81c386e 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksResult.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksResult.java @@ -52,12 +52,27 @@ public boolean hasMoreResults() { return nextPageToken != null && !nextPageToken.isEmpty(); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private List tasks; private int totalSize; private int pageSize; private String nextPageToken; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder tasks(List tasks) { this.tasks = tasks; return this; diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index 1dd6e8f9f..23456344a 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -125,6 +125,16 @@ public static Builder builder() { return new Builder(); } + /** + * Creates a new Builder initialized with values from an existing Message. + * + * @param message the Message to copy values from + * @return the builder + */ + public static Builder builder(Message message) { + return new Builder(message); + } + /** * Defines the role of the message sender in the conversation. *

    @@ -166,7 +176,7 @@ public String asString() { *

    * Example usage: *

    {@code
    -     * Message userMessage = new Message.Builder()
    +     * Message userMessage = Message.builder()
          *     .role(Message.Role.USER)
          *     .parts(List.of(new TextPart("Hello, agent!", null)))
          *     .contextId("conv-123")
    @@ -187,7 +197,7 @@ public static class Builder {
             /**
              * Creates a new Builder with all fields unset.
              */
    -        public Builder() {
    +        private Builder() {
             }
     
             /**
    @@ -195,7 +205,7 @@ public Builder() {
              *
              * @param message the Message to copy values from
              */
    -        public Builder(Message message) {
    +        private Builder(Message message) {
                 role = message.role;
                 parts = message.parts;
                 messageId = message.messageId;
    diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java
    index e2f27967b..ee55964ca 100644
    --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java
    +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java
    @@ -30,6 +30,15 @@ public record MessageSendConfiguration(List acceptedOutputModes, Integer
             }
         }
     
    +    /**
    +     * Create a new Builder
    +     *
    +     * @return the builder
    +     */
    +    public static Builder builder() {
    +        return new Builder();
    +    }
    +
         /**
          * Builder for constructing {@link MessageSendConfiguration} instances.
          * 

    @@ -42,6 +51,12 @@ public static class Builder { PushNotificationConfig pushNotificationConfig; Boolean blocking = false; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the accepted output modes. * diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index 252db36c7..409962d34 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -29,6 +29,15 @@ public record MessageSendParams(Message message, MessageSendConfiguration config Assert.checkNotNullParam("message", message); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link MessageSendParams} instances. *

    @@ -40,6 +49,12 @@ public static class Builder { MessageSendConfiguration configuration; Map metadata; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the message to send to the agent. * diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 44e8cbb6f..7ea145a40 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -59,11 +59,26 @@ public String getOauth2MetadataUrl() { return oauth2MetadataUrl; } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private OAuthFlows flows; private String description; private String oauth2MetadataUrl; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder flows(OAuthFlows flows) { this.flows = flows; return this; diff --git a/spec/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java index e09b5e095..eda14adc5 100644 --- a/spec/src/main/java/io/a2a/spec/OAuthFlows.java +++ b/spec/src/main/java/io/a2a/spec/OAuthFlows.java @@ -19,12 +19,27 @@ public record OAuthFlows(AuthorizationCodeOAuthFlow authorizationCode, ClientCredentialsOAuthFlow clientCredentials, ImplicitOAuthFlow implicit, PasswordOAuthFlow password) { + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private AuthorizationCodeOAuthFlow authorizationCode; private ClientCredentialsOAuthFlow clientCredentials; private ImplicitOAuthFlow implicit; private PasswordOAuthFlow password; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public Builder authorizationCode(AuthorizationCodeOAuthFlow authorizationCode) { this.authorizationCode = authorizationCode; return this; diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index e324d2cb9..050e660ac 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -63,6 +63,15 @@ public String getType() { return type; } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link OpenIdConnectSecurityScheme} instances. *

    @@ -73,6 +82,12 @@ public static class Builder { private String openIdConnectUrl; private String description; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the OpenID Connect Discovery URL. * diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index 5041e1cc5..ed932ab9c 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -31,6 +31,25 @@ public record PushNotificationConfig(String url, String token, AuthenticationInf Assert.checkNotNullParam("url", url); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Create a new Builder initialized with values from an existing PushNotificationConfig. + * + * @param config the PushNotificationConfig to copy values from + * @return the builder + */ + public static Builder builder(PushNotificationConfig config) { + return new Builder(config); + } + /** * Builder for constructing {@link PushNotificationConfig} instances. *

    @@ -44,7 +63,7 @@ public static class Builder { private String id; /** Creates an empty builder. */ - public Builder() { + private Builder() { } /** @@ -52,7 +71,7 @@ public Builder() { * * @param notificationConfig the configuration to copy */ - public Builder(PushNotificationConfig notificationConfig) { + private Builder(PushNotificationConfig notificationConfig) { this.url = notificationConfig.url; this.token = notificationConfig.token; this.authentication = notificationConfig.authentication; @@ -87,7 +106,7 @@ public Builder token(String token) { * @param authenticationInfo the authentication configuration * @return this builder */ - public Builder authenticationInfo(AuthenticationInfo authenticationInfo) { + public Builder authentication(AuthenticationInfo authenticationInfo) { this.authentication = authenticationInfo; return this; } diff --git a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java index 0ba40838a..aece5dc5b 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java @@ -58,6 +58,15 @@ public SendMessageRequest(Object id, MessageSendParams params) { this(JSONRPC_VERSION, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link SendMessageRequest} instances with a fluent API. *

    @@ -68,6 +77,12 @@ public static class Builder { private Object id; private MessageSendParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** Sets the JSON-RPC version. */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java index 1c6b3647c..9390d6fc9 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java @@ -31,6 +31,15 @@ public SendStreamingMessageRequest(Object id, MessageSendParams params) { this(null, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + /** * Builder for constructing {@link SendStreamingMessageRequest} instances. *

    @@ -42,6 +51,12 @@ public static class Builder { private Object id; private MessageSendParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + /** * Sets the JSON-RPC protocol version. * diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java index ac451fee2..e303ba2bd 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java @@ -28,11 +28,26 @@ public SetTaskPushNotificationConfigRequest(String id, TaskPushNotificationConfi this(null, id, taskPushConfig); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; private TaskPushNotificationConfig params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public SetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java index 248adfcff..36de4e1ad 100644 --- a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java @@ -34,11 +34,26 @@ public SubscribeToTaskRequest(Object id, TaskIdParams params) { this(null, id, params); } + /** + * Create a new Builder + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + public static class Builder { private String jsonrpc; private Object id; private TaskIdParams params; + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + public SubscribeToTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index ea91667be..f62ba95ed 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -106,14 +106,28 @@ public String getKind() { } /** - * Creates a new Builder for constructing Task instances. + * Create a new Builder * - * @return a new Task.Builder instance + * @return the builder */ public static Builder builder() { return new Builder(); } + /** + * Create a new Builder initialized with values from an existing Task. + *

    + * This Builder allows for creating a modified copy of an existing Task + * by copying all fields and then selectively updating specific values. + * + * @param task the Task to copy values from + * @return a new builder + */ + public static Builder builder(Task task) { + return new Builder(task); + } + + /** * Builder for constructing immutable {@link Task} instances. *

    @@ -146,7 +160,7 @@ public static class Builder { /** * Creates a new Builder with all fields unset. */ - public Builder() { + private Builder() { } @@ -158,7 +172,7 @@ public Builder() { * * @param task the Task to copy values from */ - public Builder(Task task) { + private Builder(Task task) { id = task.id; contextId = task.contextId; status = task.status; diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index a0b3113a0..9bda83d29 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -96,20 +96,30 @@ public String getKind() { } /** - * Creates a new Builder for constructing TaskArtifactUpdateEvent instances. + * Creates a new Builder * - * @return a new TaskArtifactUpdateEvent.Builder instance + * @return the builder */ public static Builder builder() { return new Builder(); } + /** + * Creates a new Builder initialized with values from an existing TaskArtifactUpdateEvent. + * + * @param event the TaskArtifactUpdateEvent to copy values from + * @return the builder + */ + public static Builder builder(TaskArtifactUpdateEvent event) { + return new Builder(event); + } + /** * Builder for constructing {@link TaskArtifactUpdateEvent} instances. *

    * Example for complete artifact: *

    {@code
    -     * TaskArtifactUpdateEvent event = new TaskArtifactUpdateEvent.Builder()
    +     * TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder()
          *     .taskId("task-123")
          *     .contextId("ctx-456")
          *     .artifact(new Artifact.Builder()
    @@ -121,7 +131,7 @@ public static Builder builder() {
          * 

    * Example for incremental chunk: *

    {@code
    -     * TaskArtifactUpdateEvent chunk = new TaskArtifactUpdateEvent.Builder()
    +     * TaskArtifactUpdateEvent chunk = TaskArtifactUpdateEvent.builder()
          *     .taskId("task-123")
          *     .contextId("ctx-456")
          *     .artifact(new Artifact.Builder()
    @@ -142,10 +152,10 @@ public static class Builder {
             private Boolean lastChunk;
             private Map metadata;
     
    -        public Builder() {
    +        private Builder() {
             }
     
    -        public Builder(TaskArtifactUpdateEvent existingTaskArtifactUpdateEvent) {
    +        private Builder(TaskArtifactUpdateEvent existingTaskArtifactUpdateEvent) {
                 this.taskId = existingTaskArtifactUpdateEvent.taskId;
                 this.artifact = existingTaskArtifactUpdateEvent.artifact;
                 this.contextId = existingTaskArtifactUpdateEvent.contextId;
    diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java
    index 495f9574a..d59b206a0 100644
    --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java
    +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java
    @@ -70,14 +70,24 @@ public String getKind() {
         }
     
         /**
    -     * Creates a new Builder for constructing TaskStatusUpdateEvent instances.
    +     * Create a new Builder
          *
    -     * @return a new TaskStatusUpdateEvent.Builder instance
    +     * @return the builder
          */
         public static Builder builder() {
             return new Builder();
         }
     
    +    /**
    +     * Create a new Builder initialized with values from an existing TaskStatusUpdateEvent.
    +     *
    +     * @param event the TaskStatusUpdateEvent to copy values from
    +     * @return the builder
    +     */
    +    public static Builder builder(TaskStatusUpdateEvent event) {
    +        return new Builder(event);
    +    }
    +
         public static class Builder {
             private String taskId;
             private TaskStatus status;
    @@ -85,10 +95,10 @@ public static class Builder {
             private boolean isFinal;
             private Map metadata;
     
    -        public Builder() {
    +        private Builder() {
             }
     
    -        public Builder(TaskStatusUpdateEvent existingTaskStatusUpdateEvent) {
    +        private Builder(TaskStatusUpdateEvent existingTaskStatusUpdateEvent) {
                 this.taskId = existingTaskStatusUpdateEvent.taskId;
                 this.status = existingTaskStatusUpdateEvent.status;
                 this.contextId = existingTaskStatusUpdateEvent.contextId;
    diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java
    index 8a382fc31..6d45ebe41 100644
    --- a/spec/src/main/java/io/a2a/util/Utils.java
    +++ b/spec/src/main/java/io/a2a/util/Utils.java
    @@ -149,7 +149,7 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event
                 log.fine(String.format("Appending parts to artifact id %s for task %s", artifactId, taskId));
                 List> parts = new ArrayList<>(existingArtifact.parts());
                 parts.addAll(newArtifact.parts());
    -            Artifact updated = new Artifact.Builder(existingArtifact)
    +            Artifact updated = Artifact.builder(existingArtifact)
                         .parts(parts)
                         .build();
                 artifacts.set(existingArtifactIndex, updated);
    @@ -161,7 +161,7 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event
                                 artifactId, taskId));
             }
     
    -        return new Task.Builder(task)
    +        return Task.builder(task)
                     .artifacts(artifacts)
                     .build();
     
    diff --git a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java
    index cdef4f113..ff33b11d2 100644
    --- a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java
    +++ b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java
    @@ -9,10 +9,9 @@
     import java.util.List;
     import java.util.Map;
     
    -import org.junit.jupiter.api.Test;
    -
     import io.a2a.json.JsonProcessingException;
     import io.a2a.json.JsonUtil;
    +import org.junit.jupiter.api.Test;
     
     /**
      * Tests for Task serialization and deserialization using Gson.
    @@ -67,7 +66,7 @@ void testTaskWithTimestamp() throws JsonProcessingException {
     
         @Test
         void testTaskWithArtifacts() throws JsonProcessingException {
    -        Artifact artifact = new Artifact.Builder()
    +        Artifact artifact = Artifact.builder()
                     .artifactId("artifact-1")
                     .name("Test Artifact")
                     .description("Description of artifact")
    @@ -152,7 +151,7 @@ void testTaskWithAllFields() throws JsonProcessingException {
                                     .build()
                     ))
                     .artifacts(List.of(
    -                        new Artifact.Builder()
    +                        Artifact.builder()
                                     .artifactId("artifact-1")
                                     .parts(List.of(new TextPart("Artifact content")))
                                     .build()
    @@ -231,7 +230,7 @@ void testTaskWithNullOptionalFields() throws JsonProcessingException {
         void testTaskWithFilePartBytes() throws JsonProcessingException {
             FilePart filePart = new FilePart(new FileWithBytes("application/pdf", "document.pdf", "base64data"));
     
    -        Artifact artifact = new Artifact.Builder()
    +        Artifact artifact = Artifact.builder()
                     .artifactId("file-artifact")
                     .parts(List.of(filePart))
                     .build();
    @@ -268,7 +267,7 @@ void testTaskWithFilePartBytes() throws JsonProcessingException {
         void testTaskWithFilePartUri() throws JsonProcessingException {
             FilePart filePart = new FilePart(new FileWithUri("image/png", "photo.png", "https://example.com/photo.png"));
     
    -        Artifact artifact = new Artifact.Builder()
    +        Artifact artifact = Artifact.builder()
                     .artifactId("uri-artifact")
                     .parts(List.of(filePart))
                     .build();
    @@ -302,7 +301,7 @@ void testTaskWithFilePartUri() throws JsonProcessingException {
         void testTaskWithDataPart() throws JsonProcessingException {
             DataPart dataPart = new DataPart(Map.of("temperature", 22.5, "humidity", 65));
     
    -        Artifact artifact = new Artifact.Builder()
    +        Artifact artifact = Artifact.builder()
                     .artifactId("data-artifact")
                     .parts(List.of(dataPart))
                     .build();
    @@ -351,7 +350,7 @@ void testTaskRoundTrip() throws JsonProcessingException {
                                     .build()
                     ))
                     .artifacts(List.of(
    -                        new Artifact.Builder()
    +                        Artifact.builder()
                                     .artifactId("artifact-1")
                                     .parts(List.of(new TextPart("Content")))
                                     .build()
    @@ -679,7 +678,7 @@ void testDeserializeTaskWithMetadataFromJson() throws JsonProcessingException {
     
         @Test
         void testTaskWithMixedPartTypes() throws JsonProcessingException {
    -        Artifact artifact = new Artifact.Builder()
    +        Artifact artifact = Artifact.builder()
                     .artifactId("mixed-artifact")
                     .parts(List.of(
                             new TextPart("Text content"),
    diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java
    index cd35e5986..cdb00343b 100644
    --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java
    +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java
    @@ -25,7 +25,7 @@ public AgentCard agentCard() {
             String sutJsonRpcUrl = getEnvOrDefault("SUT_JSONRPC_URL", DEFAULT_SUT_URL);
             String sutGrpcUrl = getEnvOrDefault("SUT_GRPC_URL", DEFAULT_SUT_URL);
             String sutRestcUrl = getEnvOrDefault("SUT_REST_URL", DEFAULT_SUT_URL);
    -        return new AgentCard.Builder()
    +        return AgentCard.builder()
                     .name("Hello World Agent")
                     .description("Just a hello world agent")
                     .supportedInterfaces(List.of(
    @@ -34,14 +34,14 @@ public AgentCard agentCard() {
                             new AgentInterface(TransportProtocol.HTTP_JSON.asString(), sutRestcUrl)))
                     .version("1.0.0")
                     .documentationUrl("http://example.com/docs")
    -                .capabilities(new AgentCapabilities.Builder()
    +                .capabilities(AgentCapabilities.builder()
                             .streaming(true)
                             .pushNotifications(true)
                             .stateTransitionHistory(true)
                             .build())
                     .defaultInputModes(Collections.singletonList("text"))
                     .defaultOutputModes(Collections.singletonList("text"))
    -                .skills(Collections.singletonList(new AgentSkill.Builder()
    +                .skills(Collections.singletonList(AgentSkill.builder()
                                     .id("hello_world")
                                     .name("Returns hello world")
                                     .description("just returns hello world")
    diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java
    index a117478c1..adf0b5394 100644
    --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java
    +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java
    @@ -40,7 +40,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP
                     if (context.getContextId() == null) {
                         throw new IllegalArgumentException("Parameter 'contextId' may not be null");
                     }
    -                task = new Task.Builder()
    +                task = Task.builder()
                             .id(context.getTaskId())
                             .contextId(context.getContextId())
                             .status(new TaskStatus(TaskState.SUBMITTED))
    @@ -89,7 +89,7 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC
     
                 TaskUpdater updater = new TaskUpdater(context, eventQueue);
                 updater.cancel();
    -            eventQueue.enqueueEvent(new TaskStatusUpdateEvent.Builder()
    +            eventQueue.enqueueEvent(TaskStatusUpdateEvent.builder()
                         .taskId(task.getId())
                         .contextId(task.getContextId())
                         .status(new TaskStatus(TaskState.CANCELED))
    diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java
    index a64f69f88..2142c15bf 100644
    --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java
    +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java
    @@ -53,6 +53,7 @@
     import io.a2a.spec.JSONParseError;
     import io.a2a.spec.JSONRPCErrorResponse;
     import io.a2a.spec.ListTaskPushNotificationConfigParams;
    +import io.a2a.spec.ListTasksParams;
     import io.a2a.spec.Message;
     import io.a2a.spec.MessageSendParams;
     import io.a2a.spec.MethodNotFoundError;
    @@ -89,31 +90,31 @@
      */
     public abstract class AbstractA2AServerTest {
     
    -    protected static final Task MINIMAL_TASK = new Task.Builder()
    +    protected static final Task MINIMAL_TASK = Task.builder()
                 .id("task-123")
                 .contextId("session-xyz")
                 .status(new TaskStatus(TaskState.SUBMITTED))
                 .build();
     
    -    private static final Task CANCEL_TASK = new Task.Builder()
    +    private static final Task CANCEL_TASK = Task.builder()
                 .id("cancel-task-123")
                 .contextId("session-xyz")
                 .status(new TaskStatus(TaskState.SUBMITTED))
                 .build();
     
    -    private static final Task CANCEL_TASK_NOT_SUPPORTED = new Task.Builder()
    +    private static final Task CANCEL_TASK_NOT_SUPPORTED = Task.builder()
                 .id("cancel-task-not-supported-123")
                 .contextId("session-xyz")
                 .status(new TaskStatus(TaskState.SUBMITTED))
                 .build();
     
    -    private static final Task SEND_MESSAGE_NOT_SUPPORTED = new Task.Builder()
    +    private static final Task SEND_MESSAGE_NOT_SUPPORTED = Task.builder()
                 .id("task-not-supported-123")
                 .contextId("session-xyz")
                 .status(new TaskStatus(TaskState.SUBMITTED))
                 .build();
     
    -    protected static final Message MESSAGE = new Message.Builder()
    +    protected static final Message MESSAGE = Message.builder()
                 .messageId("111")
                 .role(Message.Role.AGENT)
                 .parts(new TextPart("test message"))
    @@ -153,7 +154,7 @@ protected AbstractA2AServerTest(int serverPort) {
     
         @Test
         public void testTaskStoreMethodsSanityTest() throws Exception {
    -        Task task = new Task.Builder(MINIMAL_TASK).id("abcde").build();
    +        Task task = Task.builder(MINIMAL_TASK).id("abcde").build();
             saveTaskInTaskStore(task);
             Task saved = getTaskFromTaskStore(task.getId());
             assertEquals(task.getId(), saved.getId());
    @@ -243,17 +244,17 @@ public void testCancelTaskNotFound() {
         @Test
         public void testListTasksSuccess() throws Exception {
             // Create multiple tasks with different contexts and states
    -        Task task1 = new Task.Builder()
    +        Task task1 = Task.builder()
                     .id("list-task-1")
                     .contextId("context-1")
                     .status(new TaskStatus(TaskState.SUBMITTED))
                     .build();
    -        Task task2 = new Task.Builder()
    +        Task task2 = Task.builder()
                     .id("list-task-2")
                     .contextId("context-1")
                     .status(new TaskStatus(TaskState.WORKING))
                     .build();
    -        Task task3 = new Task.Builder()
    +        Task task3 = Task.builder()
                     .id("list-task-3")
                     .contextId("context-2")
                     .status(new TaskStatus(TaskState.COMPLETED))
    @@ -265,7 +266,7 @@ public void testListTasksSuccess() throws Exception {
     
             try {
                 // Test listing all tasks (no filters)
    -            io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder().build();
    +            io.a2a.spec.ListTasksParams params = ListTasksParams.builder().build();
                 io.a2a.spec.ListTasksResult result = getClient().listTasks(params);
     
                 assertNotNull(result);
    @@ -282,17 +283,17 @@ public void testListTasksSuccess() throws Exception {
     
         @Test
         public void testListTasksFilterByContextId() throws Exception {
    -        Task task1 = new Task.Builder()
    +        Task task1 = Task.builder()
                     .id("list-task-ctx-1")
                     .contextId("context-filter-1")
                     .status(new TaskStatus(TaskState.SUBMITTED))
                     .build();
    -        Task task2 = new Task.Builder()
    +        Task task2 = Task.builder()
                     .id("list-task-ctx-2")
                     .contextId("context-filter-1")
                     .status(new TaskStatus(TaskState.WORKING))
                     .build();
    -        Task task3 = new Task.Builder()
    +        Task task3 = Task.builder()
                     .id("list-task-ctx-3")
                     .contextId("context-filter-2")
                     .status(new TaskStatus(TaskState.COMPLETED))
    @@ -304,7 +305,7 @@ public void testListTasksFilterByContextId() throws Exception {
     
             try {
                 // Filter by contextId
    -            io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder()
    +            io.a2a.spec.ListTasksParams params = ListTasksParams.builder()
                         .contextId("context-filter-1")
                         .build();
                 io.a2a.spec.ListTasksResult result = getClient().listTasks(params);
    @@ -322,17 +323,17 @@ public void testListTasksFilterByContextId() throws Exception {
     
         @Test
         public void testListTasksFilterByStatus() throws Exception {
    -        Task task1 = new Task.Builder()
    +        Task task1 = Task.builder()
                     .id("list-task-status-1")
                     .contextId("context-status")
                     .status(new TaskStatus(TaskState.WORKING))
                     .build();
    -        Task task2 = new Task.Builder()
    +        Task task2 = Task.builder()
                     .id("list-task-status-2")
                     .contextId("context-status")
                     .status(new TaskStatus(TaskState.WORKING))
                     .build();
    -        Task task3 = new Task.Builder()
    +        Task task3 = Task.builder()
                     .id("list-task-status-3")
                     .contextId("context-status")
                     .status(new TaskStatus(TaskState.COMPLETED))
    @@ -344,7 +345,7 @@ public void testListTasksFilterByStatus() throws Exception {
     
             try {
                 // Filter by status WORKING
    -            io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder()
    +            io.a2a.spec.ListTasksParams params = ListTasksParams.builder()
                         .status(TaskState.WORKING)
                         .build();
                 io.a2a.spec.ListTasksResult result = getClient().listTasks(params);
    @@ -365,17 +366,17 @@ public void testListTasksFilterByStatus() throws Exception {
         @Test
         public void testListTasksWithPagination() throws Exception {
             // Create several tasks
    -        Task task1 = new Task.Builder()
    +        Task task1 = Task.builder()
                     .id("page-task-1")
                     .contextId("page-context")
                     .status(new TaskStatus(TaskState.SUBMITTED))
                     .build();
    -        Task task2 = new Task.Builder()
    +        Task task2 = Task.builder()
                     .id("page-task-2")
                     .contextId("page-context")
                     .status(new TaskStatus(TaskState.SUBMITTED))
                     .build();
    -        Task task3 = new Task.Builder()
    +        Task task3 = Task.builder()
                     .id("page-task-3")
                     .contextId("page-context")
                     .status(new TaskStatus(TaskState.SUBMITTED))
    @@ -387,7 +388,7 @@ public void testListTasksWithPagination() throws Exception {
     
             try {
                 // Get first page with pageSize=2
    -            io.a2a.spec.ListTasksParams params1 = new io.a2a.spec.ListTasksParams.Builder()
    +            io.a2a.spec.ListTasksParams params1 = ListTasksParams.builder()
                         .contextId("page-context")
                         .pageSize(2)
                         .build();
    @@ -399,7 +400,7 @@ public void testListTasksWithPagination() throws Exception {
                 assertTrue(result1.hasMoreResults());
     
                 // Get second page using pageToken
    -            io.a2a.spec.ListTasksParams params2 = new io.a2a.spec.ListTasksParams.Builder()
    +            io.a2a.spec.ListTasksParams params2 = ListTasksParams.builder()
                         .contextId("page-context")
                         .pageSize(2)
                         .pageToken(result1.nextPageToken())
    @@ -419,12 +420,12 @@ public void testListTasksWithPagination() throws Exception {
         public void testListTasksWithHistoryLimit() throws Exception {
             // Create task with multiple history messages
             List history = List.of(
    -                new Message.Builder(MESSAGE).messageId("msg-1").build(),
    -                new Message.Builder(MESSAGE).messageId("msg-2").build(),
    -                new Message.Builder(MESSAGE).messageId("msg-3").build(),
    -                new Message.Builder(MESSAGE).messageId("msg-4").build()
    +                Message.builder(MESSAGE).messageId("msg-1").build(),
    +                Message.builder(MESSAGE).messageId("msg-2").build(),
    +                Message.builder(MESSAGE).messageId("msg-3").build(),
    +                Message.builder(MESSAGE).messageId("msg-4").build()
             );
    -        Task taskWithHistory = new Task.Builder()
    +        Task taskWithHistory = Task.builder()
                     .id("list-task-history")
                     .contextId("context-history")
                     .status(new TaskStatus(TaskState.WORKING))
    @@ -435,7 +436,7 @@ public void testListTasksWithHistoryLimit() throws Exception {
     
             try {
                 // List with history limited to 2 messages
    -            io.a2a.spec.ListTasksParams params = new io.a2a.spec.ListTasksParams.Builder()
    +            io.a2a.spec.ListTasksParams params = ListTasksParams.builder()
                         .contextId("context-history")
                         .historyLength(2)
                         .build();
    @@ -457,7 +458,7 @@ public void testListTasksWithHistoryLimit() throws Exception {
         @Test
         public void testSendMessageNewMessageSuccess() throws Exception {
             assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null);
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -496,7 +497,7 @@ public void testSendMessageNewMessageSuccess() throws Exception {
         public void testSendMessageExistingTaskSuccess() throws Exception {
             saveTaskInTaskStore(MINIMAL_TASK);
             try {
    -            Message message = new Message.Builder(MESSAGE)
    +            Message message = Message.builder(MESSAGE)
                         .taskId(MINIMAL_TASK.getId())
                         .contextId(MINIMAL_TASK.getContextId())
                         .build();
    @@ -541,7 +542,7 @@ public void testSetPushNotificationSuccess() throws Exception {
             try {
                 TaskPushNotificationConfig taskPushConfig
                         = new TaskPushNotificationConfig(
    -                            MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
    +                            MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
                 TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig);
                 assertEquals(MINIMAL_TASK.getId(), config.taskId());
                 assertEquals("http://example.com", config.pushNotificationConfig().url());
    @@ -560,7 +561,7 @@ public void testGetPushNotificationSuccess() throws Exception {
             try {
                 TaskPushNotificationConfig taskPushConfig
                         = new TaskPushNotificationConfig(
    -                            MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
    +                            MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
     
                 TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig);
                 assertNotNull(setResult);
    @@ -579,7 +580,7 @@ public void testGetPushNotificationSuccess() throws Exception {
     
         @Test
         public void testError() throws A2AClientException {
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId())
                     .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId())
                     .build();
    @@ -677,15 +678,15 @@ public void testResubscribeExistingTaskSuccess() throws Exception {
     
                 // Enqueue events on the server
                 List events = List.of(
    -                    new TaskArtifactUpdateEvent.Builder()
    +                    TaskArtifactUpdateEvent.builder()
                                 .taskId(MINIMAL_TASK.getId())
                                 .contextId(MINIMAL_TASK.getContextId())
    -                            .artifact(new Artifact.Builder()
    +                            .artifact(Artifact.builder()
                                         .artifactId("11")
                                         .parts(new TextPart("text"))
                                         .build())
                                 .build(),
    -                    new TaskStatusUpdateEvent.Builder()
    +                    TaskStatusUpdateEvent.builder()
                                 .taskId(MINIMAL_TASK.getId())
                                 .contextId(MINIMAL_TASK.getContextId())
                                 .status(new TaskStatus(TaskState.COMPLETED))
    @@ -783,15 +784,15 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep
     
                 // Enqueue events on the server
                 List events = List.of(
    -                    new TaskArtifactUpdateEvent.Builder()
    +                    TaskArtifactUpdateEvent.builder()
                                 .taskId(MINIMAL_TASK.getId())
                                 .contextId(MINIMAL_TASK.getContextId())
    -                            .artifact(new Artifact.Builder()
    +                            .artifact(Artifact.builder()
                                         .artifactId("11")
                                         .parts(new TextPart("text"))
                                         .build())
                                 .build(),
    -                    new TaskStatusUpdateEvent.Builder()
    +                    TaskStatusUpdateEvent.builder()
                                 .taskId(MINIMAL_TASK.getId())
                                 .contextId(MINIMAL_TASK.getContextId())
                                 .status(new TaskStatus(TaskState.COMPLETED))
    @@ -931,10 +932,10 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti
                 assertTrue(firstSubscriptionLatch.await(15, TimeUnit.SECONDS), "First subscription should be established");
     
                 // Enqueue first event
    -            TaskArtifactUpdateEvent event1 = new TaskArtifactUpdateEvent.Builder()
    +            TaskArtifactUpdateEvent event1 = TaskArtifactUpdateEvent.builder()
                         .taskId(MINIMAL_TASK.getId())
                         .contextId(MINIMAL_TASK.getContextId())
    -                    .artifact(new Artifact.Builder()
    +                    .artifact(Artifact.builder()
                                 .artifactId("artifact-1")
                                 .parts(new TextPart("First artifact"))
                                 .build())
    @@ -995,10 +996,10 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti
                         "Child queue count should increase after second resubscription");
     
                 // 4. Enqueue second event - both consumers should receive it
    -            TaskArtifactUpdateEvent event2 = new TaskArtifactUpdateEvent.Builder()
    +            TaskArtifactUpdateEvent event2 = TaskArtifactUpdateEvent.builder()
                         .taskId(MINIMAL_TASK.getId())
                         .contextId(MINIMAL_TASK.getContextId())
    -                    .artifact(new Artifact.Builder()
    +                    .artifact(Artifact.builder()
                                 .artifactId("artifact-2")
                                 .parts(new TextPart("Second artifact"))
                                 .build())
    @@ -1050,12 +1051,12 @@ private boolean waitForChildQueueCountToBe(String taskId, int expectedCount, lon
         public void testListPushNotificationConfigWithConfigId() throws Exception {
             saveTaskInTaskStore(MINIMAL_TASK);
             PushNotificationConfig notificationConfig1
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id("config1")
                             .build();
             PushNotificationConfig notificationConfig2
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id("config2")
                             .build();
    @@ -1081,11 +1082,11 @@ public void testListPushNotificationConfigWithConfigId() throws Exception {
         public void testListPushNotificationConfigWithoutConfigId() throws Exception {
             saveTaskInTaskStore(MINIMAL_TASK);
             PushNotificationConfig notificationConfig1
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://1.example.com")
                             .build();
             PushNotificationConfig notificationConfig2
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://2.example.com")
                             .build();
             savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1);
    @@ -1097,7 +1098,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception {
                         new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()));
                 assertEquals(1, result.size());
     
    -            PushNotificationConfig expectedNotificationConfig = new PushNotificationConfig.Builder()
    +            PushNotificationConfig expectedNotificationConfig = PushNotificationConfig.builder()
                         .url("http://2.example.com")
                         .id(MINIMAL_TASK.getId())
                         .build();
    @@ -1139,19 +1140,19 @@ public void testListPushNotificationConfigEmptyList() throws Exception {
         @Test
         public void testDeletePushNotificationConfigWithValidConfigId() throws Exception {
             saveTaskInTaskStore(MINIMAL_TASK);
    -        saveTaskInTaskStore(new Task.Builder()
    +        saveTaskInTaskStore(Task.builder()
                     .id("task-456")
                     .contextId("session-xyz")
                     .status(new TaskStatus(TaskState.SUBMITTED))
                     .build());
     
             PushNotificationConfig notificationConfig1
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id("config1")
                             .build();
             PushNotificationConfig notificationConfig2
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id("config2")
                             .build();
    @@ -1188,12 +1189,12 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception
         public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exception {
             saveTaskInTaskStore(MINIMAL_TASK);
             PushNotificationConfig notificationConfig1
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id("config1")
                             .build();
             PushNotificationConfig notificationConfig2
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id("config2")
                             .build();
    @@ -1233,11 +1234,11 @@ public void testDeletePushNotificationConfigTaskNotFound() {
         public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exception {
             saveTaskInTaskStore(MINIMAL_TASK);
             PushNotificationConfig notificationConfig1
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://1.example.com")
                             .build();
             PushNotificationConfig notificationConfig2
    -                = new PushNotificationConfig.Builder()
    +                = PushNotificationConfig.builder()
                             .url("http://2.example.com")
                             .build();
             savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1);
    @@ -1265,7 +1266,7 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio
         @Timeout(value = 1, unit = TimeUnit.MINUTES)
         public void testNonBlockingWithMultipleMessages() throws Exception {
             // 1. Send first non-blocking message to create task in WORKING state
    -        Message message1 = new Message.Builder(MESSAGE)
    +        Message message1 = Message.builder(MESSAGE)
                     .taskId("multi-event-test")
                     .contextId("test-context")
                     .parts(new TextPart("First request"))
    @@ -1326,7 +1327,7 @@ public void testNonBlockingWithMultipleMessages() throws Exception {
             assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS));
     
             // 3. Send second streaming message to same taskId
    -        Message message2 = new Message.Builder(MESSAGE)
    +        Message message2 = Message.builder(MESSAGE)
                     .taskId("multi-event-test") // Same taskId
                     .contextId("test-context")
                     .parts(new TextPart("Second request"))
    @@ -1574,7 +1575,7 @@ public void testStreamingMethodWithoutAcceptHeader() throws Exception {
         }
     
         private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exception {
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -1627,7 +1628,7 @@ public void testSendStreamingMessage(boolean createTask) throws Exception {
                 saveTaskInTaskStore(MINIMAL_TASK);
             }
             try {
    -            Message message = new Message.Builder(MESSAGE)
    +            Message message = Message.builder(MESSAGE)
                         .taskId(MINIMAL_TASK.getId())
                         .contextId(MINIMAL_TASK.getContextId())
                         .build();
    @@ -1965,12 +1966,12 @@ private Client createClient(boolean streaming) throws A2AClientException {
          * Create a test agent card with the appropriate transport configuration.
          */
         private AgentCard createTestAgentCard() {
    -        return new AgentCard.Builder()
    +        return AgentCard.builder()
                     .name("test-card")
                     .description("A test agent card")
                     .version("1.0")
                     .documentationUrl("http://example.com/docs")
    -                .capabilities(new AgentCapabilities.Builder()
    +                .capabilities(AgentCapabilities.builder()
                             .streaming(true)
                             .pushNotifications(true)
                             .stateTransitionHistory(true)
    @@ -2028,7 +2029,7 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception {
             String contextId = "fire-ctx";
     
             // Create task in WORKING state (non-final)
    -        Task workingTask = new Task.Builder()
    +        Task workingTask = Task.builder()
                     .id(taskId)
                     .contextId(contextId)
                     .status(new TaskStatus(TaskState.WORKING))
    @@ -2040,7 +2041,7 @@ public void testMainQueueStaysOpenForNonFinalTasks() throws Exception {
                 ensureQueueForTask(taskId);
     
                 // Send a message that will leave task in WORKING state (fire-and-forget pattern)
    -            Message message = new Message.Builder(MESSAGE)
    +            Message message = Message.builder(MESSAGE)
                         .taskId(taskId)
                         .contextId(contextId)
                         .parts(new TextPart("fire and forget"))
    @@ -2140,7 +2141,7 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception {
             String contextId = "completed-ctx";
     
             // Send a message that will create and complete the task
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(taskId)
                     .contextId(contextId)
                     .parts(new TextPart("complete task"))
    diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java
    index 404e0bfb9..4ed3261a4 100644
    --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java
    +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java
    @@ -31,12 +31,12 @@ public AgentCard agentCard() {
             String port = System.getProperty("test.agent.card.port", "8081");
             String preferredTransport = loadPreferredTransportFromProperties();
     
    -        AgentCard.Builder builder = new AgentCard.Builder()
    +        AgentCard.Builder builder = AgentCard.builder()
                     .name("test-card")
                     .description("A test agent card")
                     .version("1.0")
                     .documentationUrl("http://example.com/docs")
    -                .capabilities(new AgentCapabilities.Builder()
    +                .capabilities(AgentCapabilities.builder()
                             .streaming(true)
                             .pushNotifications(true)
                             .stateTransitionHistory(true)
    diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java
    index 44c465508..0dc4c3a61 100644
    --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java
    +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java
    @@ -314,7 +314,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception
                 eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage());
             };
     
    -        io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK)
    +        io.a2a.spec.Task task = io.a2a.spec.Task.builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK)
                     .history(new ArrayList<>())
                     .build();
             taskStore.save(task);
    @@ -363,22 +363,22 @@ public void onCompleted() {
         public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Exception {
             GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor);
     
    -        io.a2a.spec.Task task = new io.a2a.spec.Task.Builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK)
    +        io.a2a.spec.Task task = io.a2a.spec.Task.builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK)
                     .history(new ArrayList<>())
                     .build();
             taskStore.save(task);
     
             // This is used to send events from a mock
             List events = List.of(
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(task.getId())
                             .contextId(task.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("11")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(task.getId())
                             .contextId(task.getContextId())
                             .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING))
    @@ -414,15 +414,15 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex
             GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor);
             List events = List.of(
                     AbstractA2ARequestHandlerTest.MINIMAL_TASK,
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId())
                             .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("11")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId())
                             .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId())
                             .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.COMPLETED))
    @@ -543,15 +543,15 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception {
             queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId());
     
             List events = List.of(
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId())
                             .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("11")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId())
                             .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId())
                             .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING))
    diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java
    index dc2088a2d..863e10d53 100644
    --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java
    +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java
    @@ -157,7 +157,7 @@ public void testOnMessageNewMessageSuccess() {
             agentExecutorExecute = (context, eventQueue) -> {
                 eventQueue.enqueueEvent(context.getMessage());
             };
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -177,7 +177,7 @@ public void testOnMessageNewMessageSuccess() {
         public void testOnMessageNewMessageSuccessMocks() {
             JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor);
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -201,7 +201,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() {
             agentExecutorExecute = (context, eventQueue) -> {
                 eventQueue.enqueueEvent(context.getMessage());
             };
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -222,7 +222,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() {
             JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor);
             taskStore.save(MINIMAL_TASK);
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -247,7 +247,7 @@ public void testOnMessageError() {
             agentExecutorExecute = (context, eventQueue) -> {
                 eventQueue.enqueueEvent(new UnsupportedOperationError());
             };
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -261,7 +261,7 @@ public void testOnMessageError() {
         @Test
         public void testOnMessageErrorMocks() {
             JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor);
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -286,7 +286,7 @@ public void testOnMessageStreamNewMessageSuccess() throws InterruptedException {
                 eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage());
             };
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                 .taskId(MINIMAL_TASK.getId())
                 .contextId(MINIMAL_TASK.getContextId())
                 .build();
    @@ -341,20 +341,20 @@ public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws Interrup
             JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor);
     
             // Create multiple events to be sent during streaming
    -        Task taskEvent = new Task.Builder(MINIMAL_TASK)
    +        Task taskEvent = Task.builder(MINIMAL_TASK)
                     .status(new TaskStatus(TaskState.WORKING))
                     .build();
     
    -        TaskArtifactUpdateEvent artifactEvent = new TaskArtifactUpdateEvent.Builder()
    +        TaskArtifactUpdateEvent artifactEvent = TaskArtifactUpdateEvent.builder()
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
    -                .artifact(new Artifact.Builder()
    +                .artifact(Artifact.builder()
                             .artifactId("artifact-1")
                             .parts(new TextPart("Generated artifact content"))
                             .build())
                     .build();
     
    -        TaskStatusUpdateEvent statusEvent = new TaskStatusUpdateEvent.Builder()
    +        TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder()
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .status(new TaskStatus(TaskState.COMPLETED))
    @@ -370,7 +370,7 @@ public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws Interrup
                 eventQueue.enqueueEvent(statusEvent);
             };
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -451,21 +451,21 @@ public void testOnMessageStreamNewMessageSuccessMocks() {
             // This is used to send events from a mock
             List events = List.of(
                     MINIMAL_TASK,
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(MINIMAL_TASK.getId())
                             .contextId(MINIMAL_TASK.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("art1")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(MINIMAL_TASK.getId())
                             .contextId(MINIMAL_TASK.getContextId())
                             .status(new TaskStatus(TaskState.COMPLETED))
                             .build());
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                 .taskId(MINIMAL_TASK.getId())
                 .contextId(MINIMAL_TASK.getContextId())
                 .build();
    @@ -520,12 +520,12 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception
                 eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage());
             };
     
    -        Task task = new Task.Builder(MINIMAL_TASK)
    +        Task task = Task.builder(MINIMAL_TASK)
                     .history(new ArrayList<>())
                     .build();
             taskStore.save(task);
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                 .taskId(task.getId())
                 .contextId(task.getContextId())
                 .build();
    @@ -574,7 +574,7 @@ public void onComplete() {
             // The Python implementation has several events emitted since it uses mocks.
             //
             // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation
    -        Task expected = new Task.Builder(task)
    +        Task expected = Task.builder(task)
                     .history(message)
                     .build();
             assertEquals(1, results.size());
    @@ -591,28 +591,28 @@ public void onComplete() {
         public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() {
             JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor);
     
    -        Task task = new Task.Builder(MINIMAL_TASK)
    +        Task task = Task.builder(MINIMAL_TASK)
                     .history(new ArrayList<>())
                     .build();
             taskStore.save(task);
     
             // This is used to send events from a mock
             List events = List.of(
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(task.getId())
                             .contextId(task.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("11")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(task.getId())
                             .contextId(task.getContextId())
                             .status(new TaskStatus(TaskState.WORKING))
                             .build());
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                 .taskId(task.getId())
                 .contextId(task.getContextId())
                 .build();
    @@ -672,13 +672,13 @@ public void testSetPushNotificationConfigSuccess() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com")
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com")
                                     .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build());
             SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig);
             SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext);
             TaskPushNotificationConfig taskPushConfigResult =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder().url("http://example.com").id("c295ea44-7543-4f78-b524-7a38915ad6e4").build());
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com").id("c295ea44-7543-4f78-b524-7a38915ad6e4").build());
             Assertions.assertEquals(taskPushConfigResult, response.getResult());
         }
     
    @@ -693,7 +693,7 @@ public void testGetPushNotificationConfigSuccess() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder()
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder()
                                     .id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
     
             SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig);
    @@ -704,7 +704,7 @@ public void testGetPushNotificationConfigSuccess() {
             GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest, callContext);
     
             TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(),
    -                new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
    +                PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
             assertEquals(expectedConfig, getResponse.getResult());
         }
     
    @@ -715,15 +715,15 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex
     
             List events = List.of(
                     MINIMAL_TASK,
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(MINIMAL_TASK.getId())
                             .contextId(MINIMAL_TASK.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("11")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(MINIMAL_TASK.getId())
                             .contextId(MINIMAL_TASK.getContextId())
                             .status(new TaskStatus(TaskState.COMPLETED))
    @@ -740,12 +740,12 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex
     
             TaskPushNotificationConfig config = new TaskPushNotificationConfig(
                     MINIMAL_TASK.getId(),
    -                new PushNotificationConfig.Builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
    +                PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build());
             SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config);
             SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext);
             assertNull(stpnResponse.getError());
     
    -        Message msg = new Message.Builder(MESSAGE)
    +        Message msg = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .build();
             SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(msg, null, null));
    @@ -829,7 +829,7 @@ public void testOnResubscribeExistingTaskSuccess() {
             Flow.Publisher response = handler.onSubscribeToTask(request, callContext);
     
             // We need to send some events in order for those to end up in the queue
    -        Message message = new Message.Builder()
    +        Message message = Message.builder()
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .role(Message.Role.AGENT)
    @@ -888,15 +888,15 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception {
             queueManager.createOrTap(MINIMAL_TASK.getId());
     
             List events = List.of(
    -                new TaskArtifactUpdateEvent.Builder()
    +                TaskArtifactUpdateEvent.builder()
                             .taskId(MINIMAL_TASK.getId())
                             .contextId(MINIMAL_TASK.getContextId())
    -                        .artifact(new Artifact.Builder()
    +                        .artifact(Artifact.builder()
                                     .artifactId("11")
                                     .parts(new TextPart("text"))
                                     .build())
                             .build(),
    -                new TaskStatusUpdateEvent.Builder()
    +                TaskStatusUpdateEvent.builder()
                             .taskId(MINIMAL_TASK.getId())
                             .contextId(MINIMAL_TASK.getContextId())
                             .status(new TaskStatus(TaskState.WORKING))
    @@ -1000,9 +1000,9 @@ public void testStreamingNotSupportedError() {
             AgentCard card = createAgentCard(false, true, true);
             JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor);
     
    -        SendStreamingMessageRequest request = new SendStreamingMessageRequest.Builder()
    +        SendStreamingMessageRequest request = SendStreamingMessageRequest.builder()
                     .id("1")
    -                .params(new MessageSendParams.Builder()
    +                .params(MessageSendParams.builder()
                             .message(MESSAGE)
                             .build())
                     .build();
    @@ -1101,12 +1101,12 @@ public void testPushNotificationsNotSupportedError() {
             TaskPushNotificationConfig config =
                     new TaskPushNotificationConfig(
                             MINIMAL_TASK.getId(),
    -                        new PushNotificationConfig.Builder()
    +                        PushNotificationConfig.builder()
                                     .id("c295ea44-7543-4f78-b524-7a38915ad6e4")
                                     .url("http://example.com")
                                     .build());
     
    -        SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest.Builder()
    +        SetTaskPushNotificationConfigRequest request = SetTaskPushNotificationConfigRequest.builder()
                     .params(config)
                     .build();
             SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext);
    @@ -1145,12 +1145,12 @@ public void testOnSetPushNotificationNoPushNotifierConfig() {
                     TaskPushNotificationConfig config =
                     new TaskPushNotificationConfig(
                             MINIMAL_TASK.getId(),
    -                        new PushNotificationConfig.Builder()
    +                        PushNotificationConfig.builder()
                                     .id("c295ea44-7543-4f78-b524-7a38915ad6e4")
                                     .url("http://example.com")
                                     .build());
     
    -        SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest.Builder()
    +        SetTaskPushNotificationConfigRequest request = SetTaskPushNotificationConfigRequest.builder()
                     .params(config)
                     .build();
             SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext);
    @@ -1234,7 +1234,7 @@ public void testOnMessageSendErrorHandling() {
     
             taskStore.save(MINIMAL_TASK);
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    @@ -1334,7 +1334,7 @@ public void testListPushNotificationConfig() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder()
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id(MINIMAL_TASK.getId())
                             .build());
    @@ -1361,7 +1361,7 @@ public void testListPushNotificationConfigNotSupported() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder()
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id(MINIMAL_TASK.getId())
                             .build());
    @@ -1425,7 +1425,7 @@ public void testDeletePushNotificationConfig() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder()
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id(MINIMAL_TASK.getId())
                             .build());
    @@ -1453,7 +1453,7 @@ public void testDeletePushNotificationConfigNotSupported() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder()
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id(MINIMAL_TASK.getId())
                             .build());
    @@ -1482,7 +1482,7 @@ public void testDeletePushNotificationConfigNoPushConfigStore() {
     
             TaskPushNotificationConfig taskPushConfig =
                     new TaskPushNotificationConfig(
    -                        MINIMAL_TASK.getId(), new PushNotificationConfig.Builder()
    +                        MINIMAL_TASK.getId(), PushNotificationConfig.builder()
                             .url("http://example.com")
                             .id(MINIMAL_TASK.getId())
                             .build());
    @@ -1529,7 +1529,7 @@ public void testStreamingDoesNotBlockMainThread() throws Exception {
                 eventQueue.enqueueEvent(context.getMessage());
             };
     
    -        Message message = new Message.Builder(MESSAGE)
    +        Message message = Message.builder(MESSAGE)
                     .taskId(MINIMAL_TASK.getId())
                     .contextId(MINIMAL_TASK.getContextId())
                     .build();
    diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java
    index b16ce9449..14c3e6b6d 100644
    --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java
    +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java
    @@ -192,7 +192,7 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s
                                            ServerCallContext context) {
             try {
                 // Build params
    -            ListTasksParams.Builder paramsBuilder = new ListTasksParams.Builder();
    +            ListTasksParams.Builder paramsBuilder = ListTasksParams.builder();
                 if (contextId != null) {
                     paramsBuilder.contextId(contextId);
                 }
    
    From 7b1fa9b173f24a6438a1dc1c5134c381fdee2322 Mon Sep 17 00:00:00 2001
    From: Emmanuel Hugonnet 
    Date: Tue, 16 Dec 2025 13:28:45 +0100
    Subject: [PATCH 231/493] feat!: Upgrading to latest a2a.proto (#539)
    
    * Generating new grpc specifications
     * Updating code impacted by those changes
     * Removing some unused metadata
    
    Signed-off-by: Emmanuel Hugonnet 
    ---
     .../client/transport/grpc/GrpcTransport.java  |   8 +-
     .../transport/jsonrpc/JSONRPCTransport.java   |   4 +-
     .../jsonrpc/JSONRPCTransportTest.java         |  12 +-
     .../transport/jsonrpc/JsonMessages.java       |  94 +---
     .../client/transport/rest/RestTransport.java  |  22 +-
     .../transport/rest/JsonRestMessages.java      |   6 +-
     .../transport/rest/RestTransportTest.java     |  13 +-
     .../auth/AuthInterceptorTest.java             |   2 +-
     .../cloud/CloudAgentExecutorProducer.java     |   6 +-
     .../examples/helloworld/HelloWorldClient.java |   2 +-
     ...otificationConfigStoreIntegrationTest.java |   2 +-
     .../jpa/JpaDatabaseTaskStoreTest.java         |  19 +-
     .../java/io/a2a/client/http/JsonMessages.java |  90 +---
     .../apps/quarkus/A2AServerRoutesTest.java     |  11 +-
     .../server/rest/quarkus/A2AServerRoutes.java  |   7 +-
     .../DefaultRequestHandler.java                |  10 +-
     .../DefaultRequestHandlerTest.java            |  34 +-
     spec-grpc/src/main/java/io/a2a/grpc/A2A.java  | 377 +++++++-------
     .../main/java/io/a2a/grpc/A2AServiceGrpc.java |   8 +-
     .../io/a2a/grpc/APIKeySecurityScheme.java     |   6 +-
     .../grpc/APIKeySecuritySchemeOrBuilder.java   |   2 +-
     .../java/io/a2a/grpc/AgentCapabilities.java   |   6 +-
     .../a2a/grpc/AgentCapabilitiesOrBuilder.java  |   2 +-
     .../src/main/java/io/a2a/grpc/AgentCard.java  | 266 +++++-----
     .../java/io/a2a/grpc/AgentCardOrBuilder.java  |  56 +--
     .../java/io/a2a/grpc/AgentCardSignature.java  |   6 +-
     .../a2a/grpc/AgentCardSignatureOrBuilder.java |   2 +-
     .../main/java/io/a2a/grpc/AgentExtension.java |   6 +-
     .../io/a2a/grpc/AgentExtensionOrBuilder.java  |   2 +-
     .../main/java/io/a2a/grpc/AgentInterface.java | 191 ++++++-
     .../io/a2a/grpc/AgentInterfaceOrBuilder.java  |  28 +-
     .../main/java/io/a2a/grpc/AgentProvider.java  |   6 +-
     .../io/a2a/grpc/AgentProviderOrBuilder.java   |   2 +-
     .../src/main/java/io/a2a/grpc/AgentSkill.java |   6 +-
     .../java/io/a2a/grpc/AgentSkillOrBuilder.java |   2 +-
     .../src/main/java/io/a2a/grpc/Artifact.java   |  52 +-
     .../java/io/a2a/grpc/ArtifactOrBuilder.java   |  12 +-
     .../java/io/a2a/grpc/AuthenticationInfo.java  |   6 +-
     .../a2a/grpc/AuthenticationInfoOrBuilder.java |   2 +-
     .../a2a/grpc/AuthorizationCodeOAuthFlow.java  |   6 +-
     .../AuthorizationCodeOAuthFlowOrBuilder.java  |   2 +-
     .../java/io/a2a/grpc/CancelTaskRequest.java   | 180 ++++++-
     .../a2a/grpc/CancelTaskRequestOrBuilder.java  |  22 +-
     .../a2a/grpc/ClientCredentialsOAuthFlow.java  |   6 +-
     .../ClientCredentialsOAuthFlowOrBuilder.java  |   2 +-
     .../src/main/java/io/a2a/grpc/DataPart.java   |  34 +-
     .../java/io/a2a/grpc/DataPartOrBuilder.java   |   8 +-
     ...leteTaskPushNotificationConfigRequest.java | 180 ++++++-
     ...ushNotificationConfigRequestOrBuilder.java |  22 +-
     .../src/main/java/io/a2a/grpc/FilePart.java   |   6 +-
     .../java/io/a2a/grpc/FilePartOrBuilder.java   |   2 +-
     .../a2a/grpc/GetExtendedAgentCardRequest.java | 177 ++++++-
     .../GetExtendedAgentCardRequestOrBuilder.java |  22 +-
     .../GetTaskPushNotificationConfigRequest.java | 180 ++++++-
     ...ushNotificationConfigRequestOrBuilder.java |  22 +-
     .../main/java/io/a2a/grpc/GetTaskRequest.java | 190 ++++++-
     .../io/a2a/grpc/GetTaskRequestOrBuilder.java  |  22 +-
     .../io/a2a/grpc/HTTPAuthSecurityScheme.java   |   6 +-
     .../grpc/HTTPAuthSecuritySchemeOrBuilder.java |   2 +-
     .../java/io/a2a/grpc/ImplicitOAuthFlow.java   |   6 +-
     .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java  |   2 +-
     ...ListTaskPushNotificationConfigRequest.java | 200 +++++++-
     ...ushNotificationConfigRequestOrBuilder.java |  22 +-
     ...istTaskPushNotificationConfigResponse.java |   6 +-
     ...shNotificationConfigResponseOrBuilder.java |   2 +-
     .../java/io/a2a/grpc/ListTasksRequest.java    | 476 ++++++++----------
     .../a2a/grpc/ListTasksRequestOrBuilder.java   |  49 +-
     .../java/io/a2a/grpc/ListTasksResponse.java   |   6 +-
     .../a2a/grpc/ListTasksResponseOrBuilder.java  |   2 +-
     .../src/main/java/io/a2a/grpc/Message.java    |   6 +-
     .../java/io/a2a/grpc/MessageOrBuilder.java    |   2 +-
     .../io/a2a/grpc/MutualTlsSecurityScheme.java  |   6 +-
     .../MutualTlsSecuritySchemeOrBuilder.java     |   2 +-
     .../io/a2a/grpc/OAuth2SecurityScheme.java     |   6 +-
     .../grpc/OAuth2SecuritySchemeOrBuilder.java   |   2 +-
     .../src/main/java/io/a2a/grpc/OAuthFlows.java |   6 +-
     .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java |   2 +-
     .../a2a/grpc/OpenIdConnectSecurityScheme.java |   6 +-
     .../OpenIdConnectSecuritySchemeOrBuilder.java |   2 +-
     spec-grpc/src/main/java/io/a2a/grpc/Part.java |   6 +-
     .../main/java/io/a2a/grpc/PartOrBuilder.java  |   2 +-
     .../java/io/a2a/grpc/PasswordOAuthFlow.java   |   6 +-
     .../a2a/grpc/PasswordOAuthFlowOrBuilder.java  |   2 +-
     .../io/a2a/grpc/PushNotificationConfig.java   |   6 +-
     .../grpc/PushNotificationConfigOrBuilder.java |   2 +-
     spec-grpc/src/main/java/io/a2a/grpc/Role.java |   6 +-
     .../src/main/java/io/a2a/grpc/Security.java   |   6 +-
     .../java/io/a2a/grpc/SecurityOrBuilder.java   |   2 +-
     .../main/java/io/a2a/grpc/SecurityScheme.java | 246 ++++++++-
     .../io/a2a/grpc/SecuritySchemeOrBuilder.java  |  62 ++-
     .../io/a2a/grpc/SendMessageConfiguration.java |  64 +--
     .../SendMessageConfigurationOrBuilder.java    |  18 +-
     .../java/io/a2a/grpc/SendMessageRequest.java  | 248 +++++++--
     .../a2a/grpc/SendMessageRequestOrBuilder.java |  28 +-
     .../java/io/a2a/grpc/SendMessageResponse.java |   6 +-
     .../grpc/SendMessageResponseOrBuilder.java    |   2 +-
     .../SetTaskPushNotificationConfigRequest.java | 210 +++++++-
     ...ushNotificationConfigRequestOrBuilder.java |  22 +-
     .../main/java/io/a2a/grpc/StreamResponse.java | 202 +++++++-
     .../io/a2a/grpc/StreamResponseOrBuilder.java  |  50 +-
     .../src/main/java/io/a2a/grpc/StringList.java |   6 +-
     .../java/io/a2a/grpc/StringListOrBuilder.java |   2 +-
     .../io/a2a/grpc/SubscribeToTaskRequest.java   | 180 ++++++-
     .../grpc/SubscribeToTaskRequestOrBuilder.java |  22 +-
     spec-grpc/src/main/java/io/a2a/grpc/Task.java |  30 +-
     .../io/a2a/grpc/TaskArtifactUpdateEvent.java  |   6 +-
     .../TaskArtifactUpdateEventOrBuilder.java     |   2 +-
     .../main/java/io/a2a/grpc/TaskOrBuilder.java  |   8 +-
     .../a2a/grpc/TaskPushNotificationConfig.java  |   6 +-
     .../TaskPushNotificationConfigOrBuilder.java  |   2 +-
     .../src/main/java/io/a2a/grpc/TaskState.java  |  32 +-
     .../src/main/java/io/a2a/grpc/TaskStatus.java |   6 +-
     .../java/io/a2a/grpc/TaskStatusOrBuilder.java |   2 +-
     .../io/a2a/grpc/TaskStatusUpdateEvent.java    |   6 +-
     .../grpc/TaskStatusUpdateEventOrBuilder.java  |   2 +-
     .../io/a2a/grpc/mapper/DataPartMapper.java    |   1 -
     ...askPushNotificationConfigParamsMapper.java |   1 -
     ...askPushNotificationConfigParamsMapper.java |   1 -
     ...askPushNotificationConfigParamsMapper.java |   1 -
     .../grpc/mapper/ListTasksParamsMapper.java    |   2 -
     .../SetTaskPushNotificationConfigMapper.java  |   1 +
     .../a2a/grpc/mapper/TaskIdParamsMapper.java   |   2 -
     .../TaskPushNotificationConfigMapper.java     |   2 +
     .../grpc/mapper/TaskQueryParamsMapper.java    |   1 -
     spec-grpc/src/main/proto/a2a.proto            | 164 ++++--
     .../io/a2a/grpc/utils/JSONRPCUtilsTest.java   |   4 +-
     .../java/io/a2a/grpc/utils/ToProtoTest.java   |   8 +-
     spec/src/main/java/io/a2a/spec/AgentCard.java |  18 +-
     .../main/java/io/a2a/spec/AgentInterface.java |  13 +-
     spec/src/main/java/io/a2a/spec/DataPart.java  |  12 -
     ...eleteTaskPushNotificationConfigParams.java |  20 +-
     spec/src/main/java/io/a2a/spec/FilePart.java  |  14 +-
     .../GetAuthenticatedExtendedCardRequest.java  |   2 +-
     .../GetTaskPushNotificationConfigParams.java  |  18 +-
     .../ListTaskPushNotificationConfigParams.java |   8 +-
     .../java/io/a2a/spec/ListTasksParams.java     |  21 +-
     .../java/io/a2a/spec/MessageSendParams.java   |  22 +-
     spec/src/main/java/io/a2a/spec/Part.java      |  10 -
     .../main/java/io/a2a/spec/TaskIdParams.java   |  11 +-
     .../a2a/spec/TaskPushNotificationConfig.java  |   5 +-
     .../java/io/a2a/spec/TaskQueryParams.java     |  17 +-
     spec/src/main/java/io/a2a/spec/TextPart.java  |  13 -
     .../apps/common/AbstractA2AServerTest.java    |  21 +-
     .../apps/common/AgentExecutorProducer.java    |   2 +-
     .../jsonrpc/handler/JSONRPCHandler.java       |   2 +-
     .../jsonrpc/handler/JSONRPCHandlerTest.java   | 200 ++++----
     .../transport/rest/handler/RestHandler.java   |   7 +-
     147 files changed, 3805 insertions(+), 1617 deletions(-)
    
    diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java
    index 4ff3c0bc8..7db87df44 100644
    --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java
    +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java
    @@ -123,7 +123,11 @@ public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context
     
             io.a2a.grpc.GetTaskRequest.Builder requestBuilder = io.a2a.grpc.GetTaskRequest.newBuilder();
             requestBuilder.setName("tasks/" + request.id());
    -        requestBuilder.setHistoryLength(request.historyLength());
    +        if(request.historyLength() != null) {
    +            requestBuilder.setHistoryLength(request.historyLength());
    +        } else {
    +            requestBuilder.clearHistoryLength();
    +        }
             io.a2a.grpc.GetTaskRequest getTaskRequest = requestBuilder.build();
             PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, getTaskRequest,
                     agentCard, context);
    @@ -218,7 +222,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN
             try {
                 A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders);
                 return FromProto.taskPushNotificationConfig(stubWithMetadata.setTaskPushNotificationConfig(grpcRequest));
    -        } catch (StatusRuntimeException e) {
    +        } catch (StatusRuntimeException | StatusException e) {
                 throw GrpcErrorMapper.mapGrpcError(e, "Failed to create task push notification config: ");
             }
         }
    diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java
    index 8de407ebe..06172be88 100644
    --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java
    +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java
    @@ -85,7 +85,7 @@ public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard
             this.agentCard = agentCard;
             this.agentUrl = agentUrl;
             this.interceptors = interceptors;
    -        this.needsExtendedCard = agentCard == null || agentCard.supportsAuthenticatedExtendedCard();
    +        this.needsExtendedCard = agentCard == null || agentCard.supportsExtendedAgentCard();
         }
     
         @Override
    @@ -295,7 +295,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli
                 if (agentCard == null) {
                     resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context));
                     agentCard = resolver.getAgentCard();
    -                needsExtendedCard = agentCard.supportsAuthenticatedExtendedCard();
    +                needsExtendedCard = agentCard.supportsExtendedAgentCard();
                 }
                 if (!needsExtendedCard) {
                     return agentCard;
    diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java
    index 4db981fe0..f7947eb4f 100644
    --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java
    +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java
    @@ -286,8 +286,7 @@ public void testA2AClientCancelTask() throws Exception {
                     );
     
             JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
    -        Task task = client.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64",
    -                new HashMap<>()), null);
    +        Task task = client.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64"), null);
             assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId());
             assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.getContextId());
             assertEquals(TaskState.CANCELED, task.getStatus().state());
    @@ -311,8 +310,7 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception {
     
             JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
             TaskPushNotificationConfig taskPushNotificationConfig = client.getTaskPushNotificationConfiguration(
    -                new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "c295ea44-7543-4f78-b524-7a38915ad6e4",
    -                        new HashMap<>()), null);
    +                new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "c295ea44-7543-4f78-b524-7a38915ad6e4"), null);
             PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig();
             assertNotNull(pushNotificationConfig);
             assertEquals("https://example.com/callback", pushNotificationConfig.url());
    @@ -344,7 +342,7 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception {
                                     .url("https://example.com/callback")
                                     .authentication(new AuthenticationInfo(Collections.singletonList("jwt"),
                                             null))
    -                                .build()), null);
    +                                .build(), "tenant"), null);
             PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig();
             assertNotNull(pushNotificationConfig);
             assertEquals("https://example.com/callback", pushNotificationConfig.url());
    @@ -418,7 +416,7 @@ public void testA2AClientGetAgentCard() throws Exception {
             assertEquals(inputModes, skills.get(1).inputModes());
             outputModes = List.of("image/png", "image/jpeg", "application/json", "text/html");
             assertEquals(outputModes, skills.get(1).outputModes());
    -        assertFalse(agentCard.supportsAuthenticatedExtendedCard());
    +        assertFalse(agentCard.supportsExtendedAgentCard());
             assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl());
             assertEquals("0.2.9", agentCard.protocolVersion());
             assertEquals("JSONRPC", agentCard.supportedInterfaces().get(0).protocolBinding());
    @@ -511,7 +509,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception {
             assertEquals("Extended Skill", skills.get(2).name());
             assertEquals("This is an extended skill.", skills.get(2).description());
             assertEquals(List.of("extended"), skills.get(2).tags());
    -        assertTrue(agentCard.supportsAuthenticatedExtendedCard());
    +        assertTrue(agentCard.supportsExtendedAgentCard());
             assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl());
             assertEquals("0.2.5", agentCard.protocolVersion());
         }
    diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java
    index 912d2a598..d14d63b5c 100644
    --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java
    +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java
    @@ -12,9 +12,9 @@ public class JsonMessages {
                      "name": "GeoSpatial Route Planner Agent",
                      "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.",
                      "supportedInterfaces" : [
    -                   {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"},
    -                   {"url": "https://georoute-agent.example.com/a2a/grpc", "protocolBinding": "GRPC"},
    -                   {"url": "https://georoute-agent.example.com/a2a/json", "protocolBinding": "HTTP+JSON"}
    +                   {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC", "tenant": ""},
    +                   {"url": "https://georoute-agent.example.com/a2a/grpc", "protocolBinding": "GRPC", "tenant": ""},
    +                   {"url": "https://georoute-agent.example.com/a2a/json", "protocolBinding": "HTTP+JSON", "tenant": ""}
                      ],
                      "provider": {
                        "organization": "Example Geo Services Inc.",
    @@ -73,7 +73,7 @@ public class JsonMessages {
                          ]
                        }
                      ],
    -                 "supportsAuthenticatedExtendedCard": false,
    +                 "supportsExtendedAgentCard": false,
                      "signatures": [
                        {
                          "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0",
    @@ -82,86 +82,6 @@ public class JsonMessages {
                      ]
                    }""";
     
    -    static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """
    -            {
    -                "name": "GeoSpatial Route Planner Agent Extended",
    -                "description": "Extended description",
    -                "supportedInterfaces": [
    -                  {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"}
    -                ],
    -                "provider": {
    -                  "organization": "Example Geo Services Inc.",
    -                  "url": "https://www.examplegeoservices.com"
    -                },
    -                "iconUrl": "https://georoute-agent.example.com/icon.png",
    -                "version": "1.2.0",
    -                "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api",
    -                "capabilities": {
    -                  "streaming": true,
    -                  "pushNotifications": true,
    -                  "stateTransitionHistory": false
    -                },
    -                "securitySchemes": {
    -                  "google": {
    -                    "openIdConnectSecurityScheme": {
    -                      "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration"
    -                    }
    -                  }
    -                },
    -               "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }],
    -                "defaultInputModes": ["application/json", "text/plain"],
    -                "defaultOutputModes": ["application/json", "image/png"],
    -                "skills": [
    -                  {
    -                    "id": "route-optimizer-traffic",
    -                    "name": "Traffic-Aware Route Optimizer",
    -                    "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).",
    -                    "tags": ["maps", "routing", "navigation", "directions", "traffic"],
    -                    "examples": [
    -                      "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.",
    -                      "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}"
    -                    ],
    -                    "inputModes": ["application/json", "text/plain"],
    -                    "outputModes": [
    -                      "application/json",
    -                      "application/vnd.geo+json",
    -                      "text/html"
    -                    ]
    -                  },
    -                  {
    -                    "id": "custom-map-generator",
    -                    "name": "Personalized Map Generator",
    -                    "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.",
    -                    "tags": ["maps", "customization", "visualization", "cartography"],
    -                    "examples": [
    -                      "Generate a map of my upcoming road trip with all planned stops highlighted.",
    -                      "Show me a map visualizing all coffee shops within a 1-mile radius of my current location."
    -                    ],
    -                    "inputModes": ["application/json"],
    -                    "outputModes": [
    -                      "image/png",
    -                      "image/jpeg",
    -                      "application/json",
    -                      "text/html"
    -                    ]
    -                  },
    -                  {
    -                    "id": "skill-extended",
    -                    "name": "Extended Skill",
    -                    "description": "This is an extended skill.",
    -                    "tags": ["extended"]
    -                  }
    -                ],
    -                "supportsAuthenticatedExtendedCard": true,
    -                "protocolVersion": "0.2.9",
    -                "signatures": [
    -                   {
    -                     "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0",
    -                     "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ"
    -                   }
    -                 ]
    -              }""";
    -
         static final String SEND_MESSAGE_TEST_REQUEST = """
                {
                   "jsonrpc":"2.0",
    @@ -676,7 +596,7 @@ public class JsonMessages {
                         "name": "GeoSpatial Route Planner Agent Extended",
                         "description": "Extended description",
                         "supportedInterfaces": [
    -                      {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"}
    +                      {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC", "tenant": ""}
                         ],
                         "provider": {
                           "organization": "Example Geo Services Inc.",
    @@ -741,7 +661,7 @@ public class JsonMessages {
                             "tags": ["extended"]
                           }
                         ],
    -                    "supportsAuthenticatedExtendedCard": true,
    +                    "supportsExtendedAgentCard": true,
                         "protocolVersion": "0.2.5",
                         "signatures": [
                            {
    @@ -816,7 +736,7 @@ public class JsonMessages {
                         ]
                       }
                     ],
    -                "supportsAuthenticatedExtendedCard": true,
    +                "supportsExtendedAgentCard": true,
                     "protocolVersion": "1.0.0"
                   }""";
     }
    diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java
    index cd13687c5..4af26a67f 100644
    --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java
    +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java
    @@ -136,7 +136,7 @@ public Task getTask(TaskQueryParams taskQueryParams, @Nullable ClientCallContext
                     agentCard, context);
             try {
                 String url;
    -            if (taskQueryParams.historyLength() > 0) {
    +            if (taskQueryParams.historyLength() != null && taskQueryParams.historyLength() > 0) {
                     url = agentUrl + String.format("/v1/tasks/%1s?historyLength=%2d", taskQueryParams.id(), taskQueryParams.historyLength());
                 } else {
                     url = agentUrl + String.format("/v1/tasks/%1s", taskQueryParams.id());
    @@ -200,6 +200,9 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo
             if (request.historyLength() != null) {
                 builder.setHistoryLength(request.historyLength());
             }
    +        if (request.tenant() != null) {
    +            builder.setTenant(request.tenant());
    +        }
             if (request.includeArtifacts() != null && request.includeArtifacts()) {
                 builder.setIncludeArtifacts(true);
             }
    @@ -209,7 +212,8 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo
     
             try {
                 // Build query string
    -            StringBuilder urlBuilder = new StringBuilder(agentUrl).append("/v1/tasks");
    +            StringBuilder urlBuilder = new StringBuilder(agentUrl);
    +            urlBuilder.append("/v1/tasks");
                 String queryParams = buildListTasksQueryString(request);
                 if (!queryParams.isEmpty()) {
                     urlBuilder.append("?").append(queryParams);
    @@ -268,8 +272,8 @@ private String buildListTasksQueryString(ListTasksParams request) {
         @Override
         public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException {
             checkNotNullParam("request", request);
    -        io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder =
    -                io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder();
    +        io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder
    +                = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder();
             builder.setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(request))
                     .setParent("tasks/" + request.taskId());
             if (request.pushNotificationConfig().id() != null) {
    @@ -291,8 +295,8 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN
         @Override
         public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException {
             checkNotNullParam("request", request);
    -        io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder =
    -                io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder();
    +        io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder
    +                = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder();
             builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()));
             PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskPushNotificationConfigRequest.METHOD, builder,
                     agentCard, context);
    @@ -322,8 +326,8 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu
         @Override
         public List listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException {
             checkNotNullParam("request", request);
    -        io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder =
    -                io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder();
    +        io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder
    +                = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder();
             builder.setParent(String.format("/tasks/%1s/pushNotificationConfigs", request.id()));
             PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, builder,
                     agentCard, context);
    @@ -410,7 +414,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli
                 if (agentCard == null) {
                     resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context));
                     agentCard = resolver.getAgentCard();
    -                needsExtendedCard = agentCard.supportsAuthenticatedExtendedCard();
    +                needsExtendedCard = agentCard.supportsExtendedAgentCard();
                 }
                 if (!needsExtendedCard) {
                     return agentCard;
    diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java
    index 28e59e6e8..c0c2360b0 100644
    --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java
    +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java
    @@ -168,7 +168,7 @@ public class JsonRestMessages {
                         ]
                       }
                     ],
    -                "supportsAuthenticatedExtendedCard": false,
    +                "supportsExtendedAgentCard": false,
                     "protocolVersion": "0.2.5"
                   }""";
     
    @@ -235,7 +235,7 @@ public class JsonRestMessages {
                         ]
                       }
                     ],
    -                "supportsAuthenticatedExtendedCard": true,
    +                "supportsExtendedAgentCard": true,
                     "protocolVersion": "0.2.5"
                   }""";
     
    @@ -308,7 +308,7 @@ public class JsonRestMessages {
                         "tags": ["extended"]
                       }
                     ],
    -                "supportsAuthenticatedExtendedCard": true,
    +                "supportsExtendedAgentCard": true,
                     "protocolVersion": "0.2.5"
                   }""";
     
    diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java
    index a40b3e9b2..4e5284470 100644
    --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java
    +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java
    @@ -49,7 +49,6 @@
     import io.a2a.spec.TextPart;
     import java.io.IOException;
     import java.util.Collections;
    -import java.util.HashMap;
     import java.util.List;
     import java.util.concurrent.CountDownLatch;
     import java.util.concurrent.TimeUnit;
    @@ -126,7 +125,7 @@ public void testSendMessage() throws Exception {
                                     .withStatusCode(200)
                                     .withBody(SEND_MESSAGE_TEST_RESPONSE)
                     );
    -        MessageSendParams messageSendParams = new MessageSendParams(message, null, null);
    +        MessageSendParams messageSendParams = new MessageSendParams(message, null, null, "");
             ClientCallContext context = null;
             
             RestTransport instance = new RestTransport(CARD);
    @@ -171,8 +170,7 @@ public void testCancelTask() throws Exception {
                     );
             ClientCallContext context = null;
             RestTransport instance = new RestTransport(CARD);
    -        Task task = instance.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64",
    -                new HashMap<>()), context);
    +        Task task = instance.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64"), context);
             assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId());
             assertEquals(TaskState.CANCELED, task.getStatus().state());
             assertNull(task.getStatus().message());
    @@ -308,7 +306,7 @@ public void testSetTaskPushNotificationConfiguration() throws Exception {
                             .url("https://example.com/callback")
                             .authentication(
                                     new AuthenticationInfo(Collections.singletonList("jwt"), null))
    -                        .build());
    +                        .build(), "tenant");
             TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration(pushedConfig, null);
             PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig();
             assertNotNull(pushNotificationConfig);
    @@ -336,8 +334,7 @@ public void testGetTaskPushNotificationConfiguration() throws Exception {
     
             RestTransport client = new RestTransport(CARD);
             TaskPushNotificationConfig taskPushNotificationConfig = client.getTaskPushNotificationConfiguration(
    -                new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "10",
    -                        new HashMap<>()), null);
    +                new GetTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", "10"), null);
             PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig();
             assertNotNull(pushNotificationConfig);
             assertEquals("https://example.com/callback", pushNotificationConfig.url());
    @@ -364,7 +361,7 @@ public void testListTaskPushNotificationConfigurations() throws Exception {
     
             RestTransport client = new RestTransport(CARD);
             List taskPushNotificationConfigs = client.listTaskPushNotificationConfigurations(
    -                new ListTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64", new HashMap<>()), null);
    +                new ListTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64"), null);
             assertEquals(2, taskPushNotificationConfigs.size());
             PushNotificationConfig pushNotificationConfig = taskPushNotificationConfigs.get(0).pushNotificationConfig();
             assertNotNull(pushNotificationConfig);
    diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java
    index ad85e0fa0..642c1e0ee 100644
    --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java
    +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java
    @@ -230,7 +230,7 @@ void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() {
             AgentCard agentCard = AgentCard.builder()
                 .name("missing")
                 .description("Uses missing scheme definition")
    -            .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc")))
    +            .supportedInterfaces(List.of(new AgentInterface("jsonrpc", "http://agent.com/rpc", "")))
                 .version("1.0")
                 .capabilities(AgentCapabilities.builder().build())
                 .defaultInputModes(List.of("text"))
    diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java
    index 17750d4a7..16632dedb 100644
    --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java
    +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java
    @@ -73,7 +73,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP
                         // Completion trigger - add final artifact and complete
                         LOGGER.info("Completion requested on pod: {}", podName);
                         String artifactText = "Completed by " + podName;
    -                    List> parts = List.of(new TextPart(artifactText, null));
    +                    List> parts = List.of(new TextPart(artifactText));
                         updater.addArtifact(parts);
                         updater.complete();
                         LOGGER.info("Task completed on pod: {}", podName);
    @@ -84,7 +84,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP
                         updater.submit();
                         updater.startWork();
                         String artifactText = "Started by " + podName;
    -                    List> parts = List.of(new TextPart(artifactText, null));
    +                    List> parts = List.of(new TextPart(artifactText));
                         updater.addArtifact(parts);
                         LOGGER.info("Task created and started on pod: {}", podName);
     
    @@ -92,7 +92,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP
                         // Subsequent messages - add artifacts (fire-and-forget, stays in WORKING)
                         LOGGER.info("Adding artifact on pod: {}", podName);
                         String artifactText = "Processed by " + podName;
    -                    List> parts = List.of(new TextPart(artifactText, null));
    +                    List> parts = List.of(new TextPart(artifactText));
                         updater.addArtifact(parts);
                         // No state change - task remains in WORKING
                         LOGGER.info("Artifact added on pod: {}", podName);
    diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java
    index ecdf5c5f3..d08386cbd 100644
    --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java
    +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java
    @@ -40,7 +40,7 @@ public static void main(String[] args) {
                 System.out.println("Using public agent card for client initialization (default).");
                 finalAgentCard = publicAgentCard;
     
    -            if (publicAgentCard.supportsAuthenticatedExtendedCard()) {
    +            if (publicAgentCard.supportsExtendedAgentCard()) {
                     System.out.println("Public card supports authenticated extended card. Attempting to fetch from: " + SERVER_URL + "/agent/authenticatedExtendedCard");
                     Map authHeaders = new HashMap<>();
                     authHeaders.put("Authorization", "Bearer dummy-token-for-extended-card");
    diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java
    index fd310acc1..44642ed88 100644
    --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java
    +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java
    @@ -119,7 +119,7 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep
                 .id("test-config-1")
                 .build();
     
    -        TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig(taskId, pushConfig);
    +        TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig(taskId, pushConfig, "tenant");
             TaskPushNotificationConfig setResult = client.setTaskPushNotificationConfiguration(taskPushConfig);
             assertNotNull(setResult);
     
    diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java
    index ae6962b76..42c9304f0 100644
    --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java
    +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java
    @@ -88,7 +88,7 @@ public void testSaveAndRetrieveTaskWithHistory() {
     
             // Retrieve the task
             Task retrieved = taskStore.get("test-task-2");
    -        
    +
             assertNotNull(retrieved);
             assertEquals("test-task-2", retrieved.getId());
             assertEquals("test-context-2", retrieved.getContextId());
    @@ -289,6 +289,7 @@ public void testListTasksEmpty() {
             // List with specific context that has no tasks
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("non-existent-context-12345")
    +                .tenant("tenant")
                     .build();
             ListTasksResult result = taskStore.list(params);
     
    @@ -328,6 +329,7 @@ public void testListTasksFilterByContextId() {
             // List tasks for context-A
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("context-A")
    +                .tenant("tenant")
                     .build();
             ListTasksResult result = taskStore.list(params);
     
    @@ -366,6 +368,7 @@ public void testListTasksFilterByStatus() {
             // List only WORKING tasks in this context
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("context-status-filter-test")
    +                .tenant("tenant")
                     .status(TaskState.WORKING)
                     .build();
             ListTasksResult result = taskStore.list(params);
    @@ -405,6 +408,7 @@ public void testListTasksCombinedFilters() {
             // List WORKING tasks in context-X
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("context-X")
    +                .tenant("tenant")
                     .status(TaskState.WORKING)
                     .build();
             ListTasksResult result = taskStore.list(params);
    @@ -434,6 +438,7 @@ public void testListTasksPagination() {
             // First page: pageSize=2
             ListTasksParams params1 = ListTasksParams.builder()
                     .contextId("context-pagination")
    +                .tenant("tenant")
                     .pageSize(2)
                     .build();
             ListTasksResult result1 = taskStore.list(params1);
    @@ -446,6 +451,7 @@ public void testListTasksPagination() {
             // Second page: use pageToken from first page
             ListTasksParams params2 = ListTasksParams.builder()
                     .contextId("context-pagination")
    +                .tenant("tenant")
                     .pageSize(2)
                     .pageToken(result1.nextPageToken())
                     .build();
    @@ -458,6 +464,7 @@ public void testListTasksPagination() {
             // Third page: last page
             ListTasksParams params3 = ListTasksParams.builder()
                     .contextId("context-pagination")
    +                .tenant("tenant")
                     .pageSize(2)
                     .pageToken(result2.nextPageToken())
                     .build();
    @@ -525,6 +532,7 @@ public void testListTasksPaginationWithDifferentTimestamps() {
             // Page 1: Get first 2 tasks
             ListTasksParams params1 = ListTasksParams.builder()
                     .contextId("context-diff-timestamps")
    +                .tenant("tenant")
                     .pageSize(2)
                     .build();
             ListTasksResult result1 = taskStore.list(params1);
    @@ -546,6 +554,7 @@ public void testListTasksPaginationWithDifferentTimestamps() {
             // Page 2: Get next 2 tasks
             ListTasksParams params2 = ListTasksParams.builder()
                     .contextId("context-diff-timestamps")
    +                .tenant("tenant")
                     .pageSize(2)
                     .pageToken(result1.nextPageToken())
                     .build();
    @@ -562,6 +571,7 @@ public void testListTasksPaginationWithDifferentTimestamps() {
             // Page 3: Get last task
             ListTasksParams params3 = ListTasksParams.builder()
                     .contextId("context-diff-timestamps")
    +                .tenant("tenant")
                     .pageSize(2)
                     .pageToken(result2.nextPageToken())
                     .build();
    @@ -611,6 +621,7 @@ public void testListTasksHistoryLimiting() {
             // List with historyLength=3 (should keep only last 3 messages) - filter by unique context
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("context-history-limit-unique")
    +                .tenant("tenant")
                     .historyLength(3)
                     .build();
             ListTasksResult result = taskStore.list(params);
    @@ -648,6 +659,7 @@ public void testListTasksArtifactInclusion() {
             // List without artifacts (default) - filter by unique context
             ListTasksParams paramsWithoutArtifacts = ListTasksParams.builder()
                     .contextId("context-artifact-unique")
    +                .tenant("tenant")
                     .build();
             ListTasksResult resultWithout = taskStore.list(paramsWithoutArtifacts);
     
    @@ -658,6 +670,7 @@ public void testListTasksArtifactInclusion() {
             // List with artifacts - filter by unique context
             ListTasksParams paramsWithArtifacts = ListTasksParams.builder()
                     .contextId("context-artifact-unique")
    +                .tenant("tenant")
                     .includeArtifacts(true)
                     .build();
             ListTasksResult resultWith = taskStore.list(paramsWithArtifacts);
    @@ -684,6 +697,7 @@ public void testListTasksDefaultPageSize() {
             // List without specifying pageSize (should use default of 50)
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("context-default-pagesize")
    +                .tenant("tenant")
                     .build();
             ListTasksResult result = taskStore.list(params);
     
    @@ -706,6 +720,7 @@ public void testListTasksInvalidPageTokenFormat() {
             // Test 1: Legacy ID-only pageToken should throw InvalidParamsError
             ListTasksParams params1 = ListTasksParams.builder()
                     .contextId("context-invalid-token")
    +                .tenant("tenant")
                     .pageToken("task-invalid-token")  // ID-only format (legacy)
                     .build();
     
    @@ -721,6 +736,7 @@ public void testListTasksInvalidPageTokenFormat() {
             // Test 2: Malformed timestamp in pageToken should throw InvalidParamsError
             ListTasksParams params2 = ListTasksParams.builder()
                     .contextId("context-invalid-token")
    +                .tenant("tenant")
                     .pageToken("not-a-number:task-id")  // Invalid timestamp
                     .build();
     
    @@ -768,6 +784,7 @@ public void testListTasksOrderingById() {
             // List should return sorted by timestamp DESC (all same), then by ID ASC
             ListTasksParams params = ListTasksParams.builder()
                     .contextId("context-order")
    +                .tenant("tenant")
                     .build();
             ListTasksResult result = taskStore.list(params);
     
    diff --git a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java
    index 242f66311..d889b8e2c 100644
    --- a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java
    +++ b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java
    @@ -12,9 +12,9 @@ public class JsonMessages {
                      "name": "GeoSpatial Route Planner Agent",
                      "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.",
                      "supportedInterfaces" : [
    -                   {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"},
    -                   {"url": "https://georoute-agent.example.com/a2a/grpc", "protocolBinding": "GRPC"},
    -                   {"url": "https://georoute-agent.example.com/a2a/json", "protocolBinding": "HTTP+JSON"}
    +                   {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC", "tenant": ""},
    +                   {"url": "https://georoute-agent.example.com/a2a/grpc", "protocolBinding": "GRPC", "tenant": ""},
    +                   {"url": "https://georoute-agent.example.com/a2a/json", "protocolBinding": "HTTP+JSON", "tenant": ""}
                      ],
                      "provider": {
                        "organization": "Example Geo Services Inc.",
    @@ -73,7 +73,7 @@ public class JsonMessages {
                          ]
                        }
                      ],
    -                 "supportsAuthenticatedExtendedCard": true,
    +                 "supportsExtendedAgentCard": true,
                      "signatures": [
                        {
                          "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0",
    @@ -81,86 +81,4 @@ public class JsonMessages {
                        }
                      ]
                    }""";
    -
    -    static final String AUTHENTICATION_EXTENDED_AGENT_CARD = """
    -            {
    -                "name": "GeoSpatial Route Planner Agent Extended",
    -                "description": "Extended description",
    -                "supportedInterfaces": [
    -                  {"url": "https://georoute-agent.example.com/a2a/v1", "protocolBinding": "JSONRPC"}
    -                ],
    -                "provider": {
    -                  "organization": "Example Geo Services Inc.",
    -                  "url": "https://www.examplegeoservices.com"
    -                },
    -                "iconUrl": "https://georoute-agent.example.com/icon.png",
    -                "version": "1.2.0",
    -                "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api",
    -                "capabilities": {
    -                  "streaming": true,
    -                  "pushNotifications": true,
    -                  "stateTransitionHistory": false
    -                },
    -                "securitySchemes": {
    -                  "google": {
    -                    "openIdConnectSecurityScheme": {
    -                      "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration"
    -                    }
    -                  }
    -                },
    -                "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }],
    -                "defaultInputModes": ["application/json", "text/plain"],
    -                "defaultOutputModes": ["application/json", "image/png"],
    -                "skills": [
    -                  {
    -                    "id": "route-optimizer-traffic",
    -                    "name": "Traffic-Aware Route Optimizer",
    -                    "description": "Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).",
    -                    "tags": ["maps", "routing", "navigation", "directions", "traffic"],
    -                    "examples": [
    -                      "Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.",
    -                      "{\\"origin\\": {\\"lat\\": 37.422, \\"lng\\": -122.084}, \\"destination\\": {\\"lat\\": 37.7749, \\"lng\\": -122.4194}, \\"preferences\\": [\\"avoid_ferries\\"]}"
    -                    ],
    -                    "inputModes": ["application/json", "text/plain"],
    -                    "outputModes": [
    -                      "application/json",
    -                      "application/vnd.geo+json",
    -                      "text/html"
    -                    ]
    -                  },
    -                  {
    -                    "id": "custom-map-generator",
    -                    "name": "Personalized Map Generator",
    -                    "description": "Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.",
    -                    "tags": ["maps", "customization", "visualization", "cartography"],
    -                    "examples": [
    -                      "Generate a map of my upcoming road trip with all planned stops highlighted.",
    -                      "Show me a map visualizing all coffee shops within a 1-mile radius of my current location."
    -                    ],
    -                    "inputModes": ["application/json"],
    -                    "outputModes": [
    -                      "image/png",
    -                      "image/jpeg",
    -                      "application/json",
    -                      "text/html"
    -                    ]
    -                  },
    -                  {
    -                    "id": "skill-extended",
    -                    "name": "Extended Skill",
    -                    "description": "This is an extended skill.",
    -                    "tags": ["extended"]
    -                  }
    -                ],
    -                "supportsAuthenticatedExtendedCard": true,
    -                "protocolVersion": "0.2.9",
    -                "signatures": [
    -                   {
    -                     "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0",
    -                     "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ"
    -                   }
    -                 ]
    -              }""";
    -
    -
     }
    \ No newline at end of file
    diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java
    index 178aa8302..4609118a0 100644
    --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java
    +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java
    @@ -337,8 +337,9 @@ public void testSetTaskPushNotificationConfig_MethodNameSetInContext() {
                             .id("config-123")
                             .url("https://example.com/callback")
                             .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), null))
    -                        .build()
    -        );
    +                        .build(),
    +                "tenant");
    +
             SetTaskPushNotificationConfigResponse realResponse = new SetTaskPushNotificationConfigResponse("1", responseConfig);
             when(mockJsonRpcHandler.setPushNotificationConfig(any(SetTaskPushNotificationConfigRequest.class),
                     any(ServerCallContext.class))).thenReturn(realResponse);
    @@ -376,7 +377,8 @@ public void testGetTaskPushNotificationConfig_MethodNameSetInContext() {
                     PushNotificationConfig.builder()
                             .id("config-456")
                             .url("https://example.com/callback")
    -                        .build()
    +                        .build(),
    +                null
             );
             GetTaskPushNotificationConfigResponse realResponse = new GetTaskPushNotificationConfigResponse("1", responseConfig);
             when(mockJsonRpcHandler.getPushNotificationConfig(any(GetTaskPushNotificationConfigRequest.class),
    @@ -415,7 +417,8 @@ public void testListTaskPushNotificationConfig_MethodNameSetInContext() {
                     PushNotificationConfig.builder()
                             .id("config-123")
                             .url("https://example.com/callback")
    -                        .build()
    +                        .build(),
    +                null
             );
             ListTaskPushNotificationConfigResponse realResponse = new ListTaskPushNotificationConfigResponse("1", Collections.singletonList(config));
             when(mockJsonRpcHandler.listPushNotificationConfig(any(ListTaskPushNotificationConfigRequest.class),
    diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java
    index a00f8a9bb..5d61a5f01 100644
    --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java
    +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java
    @@ -130,6 +130,11 @@ public void listTasks(RoutingContext rc) {
                 String historyLengthStr = rc.request().params().get("historyLength");
                 String lastUpdatedAfter = rc.request().params().get("lastUpdatedAfter");
                 String includeArtifactsStr = rc.request().params().get("includeArtifacts");
    +            //TODO replace by path extraction
    +            String tenant = rc.request().params().get("tenant");
    +            if (tenant == null) {
    +                tenant = "";
    +            }
     
                 // Parse optional parameters
                 Integer pageSize = null;
    @@ -148,7 +153,7 @@ public void listTasks(RoutingContext rc) {
                 }
     
                 response = jsonRestHandler.listTasks(contextId, statusStr, pageSize, pageToken,
    -                    historyLength, lastUpdatedAfter, includeArtifacts, context);
    +                    historyLength, lastUpdatedAfter, includeArtifacts, tenant, context);
             } catch (NumberFormatException e) {
                 response = jsonRestHandler.createErrorResponse(new InvalidParamsError("Invalid number format in parameters"));
             } catch (IllegalArgumentException e) {
    diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
    index 3d3fe4b80..7a6911566 100644
    --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
    +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java
    @@ -174,8 +174,8 @@ public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws
          * @param historyLength the maximum number of recent messages to keep (0 or negative = unlimited)
          * @return the task with limited history, or the original task if no limiting needed
          */
    -    private static Task limitTaskHistory(Task task, int historyLength) {
    -        if (task.getHistory() == null || historyLength <= 0 || historyLength >= task.getHistory().size()) {
    +    private static Task limitTaskHistory(Task task, @Nullable Integer historyLength) {
    +        if (task.getHistory() == null || historyLength == null || historyLength >= task.getHistory().size()) {
                 return task;
             }
             // Keep only the most recent historyLength messages
    @@ -570,7 +570,7 @@ public TaskPushNotificationConfig onSetTaskPushNotificationConfig(
             }
     
             PushNotificationConfig pushNotificationConfig = pushConfigStore.setInfo(params.taskId(), params.pushNotificationConfig());
    -        return new TaskPushNotificationConfig(params.taskId(), pushNotificationConfig);
    +        return new TaskPushNotificationConfig(params.taskId(), pushNotificationConfig, null);
         }
     
         @Override
    @@ -590,7 +590,7 @@ public TaskPushNotificationConfig onGetTaskPushNotificationConfig(
             }
     
             @Nullable String configId = params.pushNotificationConfigId();
    -        return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, configId));
    +        return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, configId), null);
         }
     
         private PushNotificationConfig getPushNotificationConfig(List notificationConfigList,
    @@ -652,7 +652,7 @@ public List onListTaskPushNotificationConfig(
             List taskPushNotificationConfigList = new ArrayList<>();
             if (pushNotificationConfigList != null) {
                 for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) {
    -                TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig);
    +                TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, null);
                     taskPushNotificationConfigList.add(taskPushNotificationConfig);
                 }
             }
    diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java
    index 208b989fd..ca6a210e2 100644
    --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java
    +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java
    @@ -4,11 +4,9 @@
     import static org.junit.jupiter.api.Assertions.assertNotNull;
     import static org.junit.jupiter.api.Assertions.assertTrue;
     
    -import java.util.ArrayList;
     import java.util.List;
     import java.util.Map;
     import java.util.Set;
    -import java.util.concurrent.ConcurrentHashMap;
     import java.util.concurrent.CountDownLatch;
     import java.util.concurrent.Executors;
     import java.util.concurrent.TimeUnit;
    @@ -112,7 +110,7 @@ void testBlockingMessageContinueTask() throws Exception {
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params1 = new MessageSendParams(message1, null, null);
    +        MessageSendParams params1 = new MessageSendParams(message1, null, null, "");
             Object result1 = requestHandler.onMessageSend(params1, serverCallContext);
     
             assertTrue(result1 instanceof Task);
    @@ -129,7 +127,7 @@ void testBlockingMessageContinueTask() throws Exception {
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params2 = new MessageSendParams(message2, null, null);
    +        MessageSendParams params2 = new MessageSendParams(message2, null, null, "");
             Object result2 = requestHandler.onMessageSend(params2, serverCallContext);
     
             // Should complete successfully (not timeout)
    @@ -163,7 +161,7 @@ void testBackgroundCleanupTaskIsTrackedAndCleared() throws Exception {
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, null, null);
    +        MessageSendParams params = new MessageSendParams(message, null, null, "");
     
             // Set up agent to finish quickly so cleanup runs
             CountDownLatch agentStarted = new CountDownLatch(1);
    @@ -214,7 +212,7 @@ void testStreamingClientDisconnectTriggersBackgroundCleanup() throws Exception {
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, null, null);
    +        MessageSendParams params = new MessageSendParams(message, null, null, "");
     
             // Agent should start and then wait
             CountDownLatch agentStarted = new CountDownLatch(1);
    @@ -280,7 +278,7 @@ void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, null, null);
    +        MessageSendParams params = new MessageSendParams(message, null, null, "");
     
             // Set up agent to emit events with controlled timing
             CountDownLatch agentStarted = new CountDownLatch(1);
    @@ -363,7 +361,7 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception {
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, null, null);
    +        MessageSendParams params = new MessageSendParams(message, null, null, "");
     
             // Agent that completes after some delay
             CountDownLatch agentStarted = new CountDownLatch(1);
    @@ -456,7 +454,7 @@ void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception {
                     .blocking(true)
                     .build();
     
    -        MessageSendParams params = new MessageSendParams(message, config, null);
    +        MessageSendParams params = new MessageSendParams(message, config, null, "");
     
             // Agent that does fire-and-forget: emits WORKING with artifact but never completes
             agentExecutor.setExecuteCallback((context, queue) -> {
    @@ -467,7 +465,7 @@ void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception {
     
                 // Add artifact
                 updater.addArtifact(
    -                List.of(new TextPart("Fire and forget artifact", null)),
    +                List.of(new TextPart("Fire and forget artifact")),
                     "artifact-1", "FireForget", null);
     
                 // Agent returns WITHOUT calling updater.complete()
    @@ -520,7 +518,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception {
             MessageSendConfiguration config = MessageSendConfiguration.builder()
                     .build();
     
    -        MessageSendParams params = new MessageSendParams(message, config, null);
    +        MessageSendParams params = new MessageSendParams(message, config, null, "");
     
             // Agent that produces multiple events with delays
             CountDownLatch agentStarted = new CountDownLatch(1);
    @@ -630,7 +628,7 @@ void testMainQueueStaysOpenForNonFinalTasks() throws Exception {
                 .contextId(contextId)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, null, null);
    +        MessageSendParams params = new MessageSendParams(message, null, null, "");
     
             // Agent that emits WORKING status but never completes (fire-and-forget pattern)
             CountDownLatch agentStarted = new CountDownLatch(1);
    @@ -755,7 +753,7 @@ void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception {
                     .blocking(true)
                     .build();
     
    -        MessageSendParams params = new MessageSendParams(message, config, null);
    +        MessageSendParams params = new MessageSendParams(message, config, null, "");
     
             // Agent that uses TaskUpdater to emit multiple artifacts (like real agents do)
             agentExecutor.setExecuteCallback((context, queue) -> {
    @@ -766,12 +764,12 @@ void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception {
     
                 // Add first artifact
                 updater.addArtifact(
    -                List.of(new TextPart("First artifact", null)),
    +                List.of(new TextPart("First artifact")),
                     "artifact-1", "First", null);
     
                 // Add second artifact
                 updater.addArtifact(
    -                List.of(new TextPart("Second artifact", null)),
    +                List.of(new TextPart("Second artifact")),
                     "artifact-2", "Second", null);
     
                 // Complete the task
    @@ -847,7 +845,7 @@ void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exceptio
                 .pushNotificationConfig(pushConfig)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, config, null);
    +        MessageSendParams params = new MessageSendParams(message, config, null, "");
     
             // Agent creates a new task
             agentExecutor.setExecuteCallback((context, queue) -> {
    @@ -924,13 +922,13 @@ void testBlockingMessageStoresPushNotificationConfigForExistingTask() throws Exc
                 .pushNotificationConfig(pushConfig)
                 .build();
     
    -        MessageSendParams params = new MessageSendParams(message, config, null);
    +        MessageSendParams params = new MessageSendParams(message, config, null, "");
     
             // Agent updates the existing task
             agentExecutor.setExecuteCallback((context, queue) -> {
                 TaskUpdater updater = new TaskUpdater(context, queue);
                 updater.addArtifact(
    -                List.of(new TextPart("update artifact", null)),
    +                List.of(new TextPart("update artifact")),
                     "artifact-1", "Update", null);
                 updater.complete();
             });
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java
    index 40ecc5080..bc454f7e1 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java
    @@ -1,21 +1,21 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
     @com.google.protobuf.Generated
    -public final class A2A {
    +public final class A2A extends com.google.protobuf.GeneratedFile {
       private A2A() {}
       static {
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      A2A.class.getName());
    +      "A2A");
       }
       public static void registerAllExtensions(
           com.google.protobuf.ExtensionRegistryLite registry) {
    @@ -350,32 +350,32 @@ public static void registerAllExtensions(
           " \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a.v1.Au" +
           "thenticationInfo\"?\n\022AuthenticationInfo\022\024" +
           "\n\007schemes\030\001 \003(\tB\003\340A\002\022\023\n\013credentials\030\002 \001(" +
    -      "\t\"A\n\016AgentInterface\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\035\n" +
    -      "\020protocol_binding\030\002 \001(\tB\003\340A\002\"\275\007\n\tAgentCa" +
    -      "rd\022\"\n\020protocol_version\030\020 \001(\tB\003\340A\002H\000\210\001\001\022\021" +
    -      "\n\004name\030\001 \001(\tB\003\340A\002\022\030\n\013description\030\002 \001(\tB\003" +
    -      "\340A\002\0224\n\024supported_interfaces\030\023 \003(\0132\026.a2a." +
    -      "v1.AgentInterface\022\024\n\003url\030\003 \001(\tB\002\030\001H\001\210\001\001\022" +
    -      "$\n\023preferred_transport\030\016 \001(\tB\002\030\001H\002\210\001\001\0229\n" +
    -      "\025additional_interfaces\030\017 \003(\0132\026.a2a.v1.Ag" +
    -      "entInterfaceB\002\030\001\022\'\n\010provider\030\004 \001(\0132\025.a2a" +
    -      ".v1.AgentProvider\022\024\n\007version\030\005 \001(\tB\003\340A\002\022" +
    -      "\036\n\021documentation_url\030\006 \001(\tH\003\210\001\001\0224\n\014capab" +
    -      "ilities\030\007 \001(\0132\031.a2a.v1.AgentCapabilities" +
    -      "B\003\340A\002\022@\n\020security_schemes\030\010 \003(\0132&.a2a.v1" +
    -      ".AgentCard.SecuritySchemesEntry\022\"\n\010secur" +
    -      "ity\030\t \003(\0132\020.a2a.v1.Security\022 \n\023default_i" +
    -      "nput_modes\030\n \003(\tB\003\340A\002\022!\n\024default_output_" +
    -      "modes\030\013 \003(\tB\003\340A\002\022\'\n\006skills\030\014 \003(\0132\022.a2a.v" +
    -      "1.AgentSkillB\003\340A\002\0221\n$supports_authentica" +
    -      "ted_extended_card\030\r \001(\010H\004\210\001\001\022.\n\nsignatur" +
    -      "es\030\021 \003(\0132\032.a2a.v1.AgentCardSignature\022\025\n\010" +
    -      "icon_url\030\022 \001(\tH\005\210\001\001\032N\n\024SecuritySchemesEn" +
    -      "try\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132\026.a2a.v1" +
    -      ".SecurityScheme:\0028\001B\023\n\021_protocol_version" +
    -      "B\006\n\004_urlB\026\n\024_preferred_transportB\024\n\022_doc" +
    -      "umentation_urlB\'\n%_supports_authenticate" +
    -      "d_extended_cardB\013\n\t_icon_url\"<\n\rAgentPro" +
    +      "\t\"Q\n\016AgentInterface\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\035\n" +
    +      "\020protocol_binding\030\002 \001(\tB\003\340A\002\022\016\n\006tenant\030\003" +
    +      " \001(\t\"\255\007\n\tAgentCard\022\"\n\020protocol_version\030\020" +
    +      " \001(\tB\003\340A\002H\000\210\001\001\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\030\n\013des" +
    +      "cription\030\002 \001(\tB\003\340A\002\0224\n\024supported_interfa" +
    +      "ces\030\023 \003(\0132\026.a2a.v1.AgentInterface\022\024\n\003url" +
    +      "\030\003 \001(\tB\002\030\001H\001\210\001\001\022$\n\023preferred_transport\030\016" +
    +      " \001(\tB\002\030\001H\002\210\001\001\0229\n\025additional_interfaces\030\017" +
    +      " \003(\0132\026.a2a.v1.AgentInterfaceB\002\030\001\022\'\n\010prov" +
    +      "ider\030\004 \001(\0132\025.a2a.v1.AgentProvider\022\024\n\007ver" +
    +      "sion\030\005 \001(\tB\003\340A\002\022\036\n\021documentation_url\030\006 \001" +
    +      "(\tH\003\210\001\001\0224\n\014capabilities\030\007 \001(\0132\031.a2a.v1.A" +
    +      "gentCapabilitiesB\003\340A\002\022@\n\020security_scheme" +
    +      "s\030\010 \003(\0132&.a2a.v1.AgentCard.SecuritySchem" +
    +      "esEntry\022\"\n\010security\030\t \003(\0132\020.a2a.v1.Secur" +
    +      "ity\022 \n\023default_input_modes\030\n \003(\tB\003\340A\002\022!\n" +
    +      "\024default_output_modes\030\013 \003(\tB\003\340A\002\022\'\n\006skil" +
    +      "ls\030\014 \003(\0132\022.a2a.v1.AgentSkillB\003\340A\002\022)\n\034sup" +
    +      "ports_extended_agent_card\030\r \001(\010H\004\210\001\001\022.\n\n" +
    +      "signatures\030\021 \003(\0132\032.a2a.v1.AgentCardSigna" +
    +      "ture\022\025\n\010icon_url\030\022 \001(\tH\005\210\001\001\032N\n\024SecurityS" +
    +      "chemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132" +
    +      "\026.a2a.v1.SecurityScheme:\0028\001B\023\n\021_protocol" +
    +      "_versionB\006\n\004_urlB\026\n\024_preferred_transport" +
    +      "B\024\n\022_documentation_urlB\037\n\035_supports_exte" +
    +      "nded_agent_cardB\013\n\t_icon_url\"<\n\rAgentPro" +
           "vider\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\031\n\014organization\030" +
           "\002 \001(\tB\003\340A\002\"\341\001\n\021AgentCapabilities\022\026\n\tstre" +
           "aming\030\001 \001(\010H\000\210\001\001\022\037\n\022push_notifications\030\002" +
    @@ -445,93 +445,106 @@ public static void registerAllExtensions(
           "token_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(" +
           "\t\022:\n\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuth" +
           "Flow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003" +
    -      "key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\250\001\n\022SendMes" +
    -      "sageRequest\022.\n\007request\030\001 \001(\0132\017.a2a.v1.Me" +
    -      "ssageB\003\340A\002R\007message\0227\n\rconfiguration\030\002 \001" +
    -      "(\0132 .a2a.v1.SendMessageConfiguration\022)\n\010" +
    -      "metadata\030\003 \001(\0132\027.google.protobuf.Struct\"" +
    -      "S\n\016GetTaskRequest\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\033\n\016" +
    -      "history_length\030\002 \001(\005H\000\210\001\001B\021\n\017_history_le" +
    -      "ngth\"\260\002\n\020ListTasksRequest\022\022\n\ncontext_id\030" +
    -      "\001 \001(\t\022!\n\006status\030\002 \001(\0162\021.a2a.v1.TaskState" +
    -      "\022\026\n\tpage_size\030\003 \001(\005H\000\210\001\001\022\022\n\npage_token\030\004" +
    -      " \001(\t\022\033\n\016history_length\030\005 \001(\005H\001\210\001\001\022\032\n\022las" +
    -      "t_updated_after\030\006 \001(\003\022\036\n\021include_artifac" +
    -      "ts\030\007 \001(\010H\002\210\001\001\022)\n\010metadata\030\010 \001(\0132\027.google" +
    -      ".protobuf.StructB\014\n\n_page_sizeB\021\n\017_histo" +
    -      "ry_lengthB\024\n\022_include_artifacts\"\204\001\n\021List" +
    -      "TasksResponse\022 \n\005tasks\030\001 \003(\0132\014.a2a.v1.Ta" +
    -      "skB\003\340A\002\022\034\n\017next_page_token\030\002 \001(\tB\003\340A\002\022\026\n" +
    -      "\tpage_size\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_size\030\004 \001(" +
    -      "\005B\003\340A\002\"!\n\021CancelTaskRequest\022\014\n\004name\030\001 \001(" +
    -      "\t\"4\n$GetTaskPushNotificationConfigReques" +
    -      "t\022\014\n\004name\030\001 \001(\t\"7\n\'DeleteTaskPushNotific" +
    -      "ationConfigRequest\022\014\n\004name\030\001 \001(\t\"\214\001\n$Set" +
    -      "TaskPushNotificationConfigRequest\022\023\n\006par" +
    -      "ent\030\001 \001(\tB\003\340A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227" +
    -      "\n\006config\030\003 \001(\0132\".a2a.v1.TaskPushNotifica" +
    -      "tionConfigB\003\340A\002\"&\n\026SubscribeToTaskReques" +
    -      "t\022\014\n\004name\030\001 \001(\t\"^\n%ListTaskPushNotificat" +
    -      "ionConfigRequest\022\016\n\006parent\030\001 \001(\t\022\021\n\tpage" +
    -      "_size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t\"\035\n\033GetEx" +
    -      "tendedAgentCardRequest\"g\n\023SendMessageRes" +
    -      "ponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003m" +
    -      "sg\030\002 \001(\0132\017.a2a.v1.MessageH\000R\007messageB\t\n\007" +
    -      "payload\"\326\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\013" +
    -      "2\014.a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.M" +
    -      "essageH\000R\007message\0226\n\rstatus_update\030\003 \001(\013" +
    -      "2\035.a2a.v1.TaskStatusUpdateEventH\000\022:\n\017art" +
    -      "ifact_update\030\004 \001(\0132\037.a2a.v1.TaskArtifact" +
    -      "UpdateEventH\000B\t\n\007payload\"v\n&ListTaskPush" +
    -      "NotificationConfigResponse\0223\n\007configs\030\001 " +
    -      "\003(\0132\".a2a.v1.TaskPushNotificationConfig\022" +
    -      "\027\n\017next_page_token\030\002 \001(\t*\372\001\n\tTaskState\022\032" +
    -      "\n\026TASK_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STATE" +
    -      "_SUBMITTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024" +
    -      "TASK_STATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FAI" +
    -      "LED\020\004\022\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK_" +
    -      "STATE_INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_REJ" +
    -      "ECTED\020\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;\n" +
    -      "\004Role\022\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USER" +
    -      "\020\001\022\016\n\nROLE_AGENT\020\0022\245\013\n\nA2AService\022c\n\013Sen" +
    -      "dMessage\022\032.a2a.v1.SendMessageRequest\032\033.a" +
    -      "2a.v1.SendMessageResponse\"\033\202\323\344\223\002\025\"\020/v1/m" +
    -      "essage:send:\001*\022k\n\024SendStreamingMessage\022\032" +
    -      ".a2a.v1.SendMessageRequest\032\026.a2a.v1.Stre" +
    -      "amResponse\"\035\202\323\344\223\002\027\"\022/v1/message:stream:\001" +
    -      "*0\001\022R\n\007GetTask\022\026.a2a.v1.GetTaskRequest\032\014" +
    -      ".a2a.v1.Task\"!\332A\004name\202\323\344\223\002\024\022\022/v1/{name=t" +
    -      "asks/*}\022S\n\tListTasks\022\030.a2a.v1.ListTasksR" +
    -      "equest\032\031.a2a.v1.ListTasksResponse\"\021\202\323\344\223\002" +
    -      "\013\022\t/v1/tasks\022[\n\nCancelTask\022\031.a2a.v1.Canc" +
    -      "elTaskRequest\032\014.a2a.v1.Task\"$\202\323\344\223\002\036\"\031/v1" +
    -      "/{name=tasks/*}:cancel:\001*\022q\n\017SubscribeTo" +
    -      "Task\022\036.a2a.v1.SubscribeToTaskRequest\032\026.a" +
    -      "2a.v1.StreamResponse\"$\202\323\344\223\002\036\022\034/v1/{name=" +
    -      "tasks/*}:subscribe0\001\022\277\001\n\035SetTaskPushNoti" +
    -      "ficationConfig\022,.a2a.v1.SetTaskPushNotif" +
    -      "icationConfigRequest\032\".a2a.v1.TaskPushNo" +
    -      "tificationConfig\"L\332A\rparent,config\202\323\344\223\0026" +
    -      "\",/v1/{parent=tasks/*/pushNotificationCo" +
    -      "nfigs}:\006config\022\256\001\n\035GetTaskPushNotificati" +
    -      "onConfig\022,.a2a.v1.GetTaskPushNotificatio" +
    -      "nConfigRequest\032\".a2a.v1.TaskPushNotifica" +
    -      "tionConfig\";\332A\004name\202\323\344\223\002.\022,/v1/{name=tas" +
    -      "ks/*/pushNotificationConfigs/*}\022\276\001\n\036List" +
    -      "TaskPushNotificationConfig\022-.a2a.v1.List" +
    -      "TaskPushNotificationConfigRequest\032..a2a." +
    -      "v1.ListTaskPushNotificationConfigRespons" +
    -      "e\"=\332A\006parent\202\323\344\223\002.\022,/v1/{parent=tasks/*}" +
    -      "/pushNotificationConfigs\022m\n\024GetExtendedA" +
    -      "gentCard\022#.a2a.v1.GetExtendedAgentCardRe" +
    -      "quest\032\021.a2a.v1.AgentCard\"\035\202\323\344\223\002\027\022\025/v1/ex" +
    -      "tendedAgentCard\022\250\001\n DeleteTaskPushNotifi" +
    -      "cationConfig\022/.a2a.v1.DeleteTaskPushNoti" +
    -      "ficationConfigRequest\032\026.google.protobuf." +
    -      "Empty\";\332A\004name\202\323\344\223\002.*,/v1/{name=tasks/*/" +
    -      "pushNotificationConfigs/*}B7\n\013io.a2a.grp" +
    -      "cB\003A2AP\001Z\030google.golang.org/a2a/v1\252\002\006A2a" +
    -      ".V1b\006proto3"
    +      "key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\270\001\n\022SendMes" +
    +      "sageRequest\022\016\n\006tenant\030\004 \001(\t\022.\n\007request\030\001" +
    +      " \001(\0132\017.a2a.v1.MessageB\003\340A\002R\007message\0227\n\rc" +
    +      "onfiguration\030\002 \001(\0132 .a2a.v1.SendMessageC" +
    +      "onfiguration\022)\n\010metadata\030\003 \001(\0132\027.google." +
    +      "protobuf.Struct\"c\n\016GetTaskRequest\022\016\n\006ten" +
    +      "ant\030\003 \001(\t\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\033\n\016history_" +
    +      "length\030\002 \001(\005H\000\210\001\001B\021\n\017_history_length\"\225\002\n" +
    +      "\020ListTasksRequest\022\016\n\006tenant\030\t \001(\t\022\022\n\ncon" +
    +      "text_id\030\001 \001(\t\022!\n\006status\030\002 \001(\0162\021.a2a.v1.T" +
    +      "askState\022\026\n\tpage_size\030\003 \001(\005H\000\210\001\001\022\022\n\npage" +
    +      "_token\030\004 \001(\t\022\033\n\016history_length\030\005 \001(\005H\001\210\001" +
    +      "\001\022\032\n\022last_updated_after\030\006 \001(\003\022\036\n\021include" +
    +      "_artifacts\030\007 \001(\010H\002\210\001\001B\014\n\n_page_sizeB\021\n\017_" +
    +      "history_lengthB\024\n\022_include_artifacts\"\204\001\n" +
    +      "\021ListTasksResponse\022 \n\005tasks\030\001 \003(\0132\014.a2a." +
    +      "v1.TaskB\003\340A\002\022\034\n\017next_page_token\030\002 \001(\tB\003\340" +
    +      "A\002\022\026\n\tpage_size\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_size" +
    +      "\030\004 \001(\005B\003\340A\002\"1\n\021CancelTaskRequest\022\016\n\006tena" +
    +      "nt\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"D\n$GetTaskPushNot" +
    +      "ificationConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\014" +
    +      "\n\004name\030\001 \001(\t\"G\n\'DeleteTaskPushNotificati" +
    +      "onConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name\030" +
    +      "\001 \001(\t\"\234\001\n$SetTaskPushNotificationConfigR" +
    +      "equest\022\016\n\006tenant\030\004 \001(\t\022\023\n\006parent\030\001 \001(\tB\003" +
    +      "\340A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 " +
    +      "\001(\0132\".a2a.v1.TaskPushNotificationConfigB" +
    +      "\003\340A\002\"6\n\026SubscribeToTaskRequest\022\016\n\006tenant" +
    +      "\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"n\n%ListTaskPushNoti" +
    +      "ficationConfigRequest\022\016\n\006tenant\030\004 \001(\t\022\016\n" +
    +      "\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npage" +
    +      "_token\030\003 \001(\t\"-\n\033GetExtendedAgentCardRequ" +
    +      "est\022\016\n\006tenant\030\001 \001(\t\"g\n\023SendMessageRespon" +
    +      "se\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030" +
    +      "\002 \001(\0132\017.a2a.v1.MessageH\000R\007messageB\t\n\007pay" +
    +      "load\"\326\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\0132\014." +
    +      "a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.Mess" +
    +      "ageH\000R\007message\0226\n\rstatus_update\030\003 \001(\0132\035." +
    +      "a2a.v1.TaskStatusUpdateEventH\000\022:\n\017artifa" +
    +      "ct_update\030\004 \001(\0132\037.a2a.v1.TaskArtifactUpd" +
    +      "ateEventH\000B\t\n\007payload\"v\n&ListTaskPushNot" +
    +      "ificationConfigResponse\0223\n\007configs\030\001 \003(\013" +
    +      "2\".a2a.v1.TaskPushNotificationConfig\022\027\n\017" +
    +      "next_page_token\030\002 \001(\t*\372\001\n\tTaskState\022\032\n\026T" +
    +      "ASK_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STATE_SU" +
    +      "BMITTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024TAS" +
    +      "K_STATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FAILED" +
    +      "\020\004\022\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK_STA" +
    +      "TE_INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_REJECT" +
    +      "ED\020\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;\n\004Ro" +
    +      "le\022\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USER\020\001\022" +
    +      "\016\n\nROLE_AGENT\020\0022\276\016\n\nA2AService\022}\n\013SendMe" +
    +      "ssage\022\032.a2a.v1.SendMessageRequest\032\033.a2a." +
    +      "v1.SendMessageResponse\"5\202\323\344\223\002/\"\r/message" +
    +      ":send:\001*Z\033\"\026/{tenant}/message:send:\001*\022\207\001" +
    +      "\n\024SendStreamingMessage\022\032.a2a.v1.SendMess" +
    +      "ageRequest\032\026.a2a.v1.StreamResponse\"9\202\323\344\223" +
    +      "\0023\"\017/message:stream:\001*Z\035\"\030/{tenant}/mess" +
    +      "age:stream:\001*0\001\022k\n\007GetTask\022\026.a2a.v1.GetT" +
    +      "askRequest\032\014.a2a.v1.Task\":\332A\004name\202\323\344\223\002-\022" +
    +      "\017/{name=tasks/*}Z\032\022\030/{tenant}/{name=task" +
    +      "s/*}\022c\n\tListTasks\022\030.a2a.v1.ListTasksRequ" +
    +      "est\032\031.a2a.v1.ListTasksResponse\"!\202\323\344\223\002\033\022\006" +
    +      "/tasksZ\021\022\017/{tenant}/tasks\022~\n\nCancelTask\022" +
    +      "\031.a2a.v1.CancelTaskRequest\032\014.a2a.v1.Task" +
    +      "\"G\202\323\344\223\002A\"\026/{name=tasks/*}:cancel:\001*Z$\"\037/" +
    +      "{tenant}/{name=tasks/*}:cancel:\001*\022\224\001\n\017Su" +
    +      "bscribeToTask\022\036.a2a.v1.SubscribeToTaskRe" +
    +      "quest\032\026.a2a.v1.StreamResponse\"G\202\323\344\223\002A\022\031/" +
    +      "{name=tasks/*}:subscribeZ$\022\"/{tenant}/{n" +
    +      "ame=tasks/*}:subscribe0\001\022\373\001\n\035SetTaskPush" +
    +      "NotificationConfig\022,.a2a.v1.SetTaskPushN" +
    +      "otificationConfigRequest\032\".a2a.v1.TaskPu" +
    +      "shNotificationConfig\"\207\001\332A\rparent,config\202" +
    +      "\323\344\223\002q\")/{parent=tasks/*/pushNotification" +
    +      "Configs}:\006configZ<\"2/{tenant}/{parent=ta" +
    +      "sks/*/pushNotificationConfigs}:\006config\022\341" +
    +      "\001\n\035GetTaskPushNotificationConfig\022,.a2a.v" +
    +      "1.GetTaskPushNotificationConfigRequest\032\"" +
    +      ".a2a.v1.TaskPushNotificationConfig\"n\332A\004n" +
    +      "ame\202\323\344\223\002a\022)/{name=tasks/*/pushNotificati" +
    +      "onConfigs/*}Z4\0222/{tenant}/{name=tasks/*/" +
    +      "pushNotificationConfigs/*}\022\361\001\n\036ListTaskP" +
    +      "ushNotificationConfig\022-.a2a.v1.ListTaskP" +
    +      "ushNotificationConfigRequest\032..a2a.v1.Li" +
    +      "stTaskPushNotificationConfigResponse\"p\332A" +
    +      "\006parent\202\323\344\223\002a\022)/{parent=tasks/*}/pushNot" +
    +      "ificationConfigsZ4\0222/{tenant}/{parent=ta" +
    +      "sks/*}/pushNotificationConfigs\022\211\001\n\024GetEx" +
    +      "tendedAgentCard\022#.a2a.v1.GetExtendedAgen" +
    +      "tCardRequest\032\021.a2a.v1.AgentCard\"9\202\323\344\223\0023\022" +
    +      "\022/extendedAgentCardZ\035\022\033/{tenant}/extende" +
    +      "dAgentCard\022\333\001\n DeleteTaskPushNotificatio" +
    +      "nConfig\022/.a2a.v1.DeleteTaskPushNotificat" +
    +      "ionConfigRequest\032\026.google.protobuf.Empty" +
    +      "\"n\332A\004name\202\323\344\223\002a*)/{name=tasks/*/pushNoti" +
    +      "ficationConfigs/*}Z4*2/{tenant}/{name=ta" +
    +      "sks/*/pushNotificationConfigs/*}B7\n\013io.a" +
    +      "2a.grpcB\003A2AP\001Z\030google.golang.org/a2a/v1" +
    +      "\252\002\006A2a.V1b\006proto3"
         };
         descriptor = com.google.protobuf.Descriptors.FileDescriptor
           .internalBuildGeneratedFileFrom(descriptorData,
    @@ -544,319 +557,319 @@ public static void registerAllExtensions(
               com.google.protobuf.TimestampProto.getDescriptor(),
             });
         internal_static_a2a_v1_SendMessageConfiguration_descriptor =
    -      getDescriptor().getMessageTypes().get(0);
    +      getDescriptor().getMessageType(0);
         internal_static_a2a_v1_SendMessageConfiguration_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_SendMessageConfiguration_descriptor,
             new java.lang.String[] { "AcceptedOutputModes", "PushNotificationConfig", "HistoryLength", "Blocking", });
         internal_static_a2a_v1_Task_descriptor =
    -      getDescriptor().getMessageTypes().get(1);
    +      getDescriptor().getMessageType(1);
         internal_static_a2a_v1_Task_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_Task_descriptor,
             new java.lang.String[] { "Id", "ContextId", "Status", "Artifacts", "History", "Metadata", });
         internal_static_a2a_v1_TaskStatus_descriptor =
    -      getDescriptor().getMessageTypes().get(2);
    +      getDescriptor().getMessageType(2);
         internal_static_a2a_v1_TaskStatus_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_TaskStatus_descriptor,
             new java.lang.String[] { "State", "Message", "Timestamp", });
         internal_static_a2a_v1_Part_descriptor =
    -      getDescriptor().getMessageTypes().get(3);
    +      getDescriptor().getMessageType(3);
         internal_static_a2a_v1_Part_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_Part_descriptor,
             new java.lang.String[] { "Text", "File", "Data", "Metadata", "Part", });
         internal_static_a2a_v1_FilePart_descriptor =
    -      getDescriptor().getMessageTypes().get(4);
    +      getDescriptor().getMessageType(4);
         internal_static_a2a_v1_FilePart_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_FilePart_descriptor,
             new java.lang.String[] { "FileWithUri", "FileWithBytes", "MediaType", "Name", "File", });
         internal_static_a2a_v1_DataPart_descriptor =
    -      getDescriptor().getMessageTypes().get(5);
    +      getDescriptor().getMessageType(5);
         internal_static_a2a_v1_DataPart_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_DataPart_descriptor,
             new java.lang.String[] { "Data", });
         internal_static_a2a_v1_Message_descriptor =
    -      getDescriptor().getMessageTypes().get(6);
    +      getDescriptor().getMessageType(6);
         internal_static_a2a_v1_Message_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_Message_descriptor,
             new java.lang.String[] { "MessageId", "ContextId", "TaskId", "Role", "Parts", "Metadata", "Extensions", "ReferenceTaskIds", });
         internal_static_a2a_v1_Artifact_descriptor =
    -      getDescriptor().getMessageTypes().get(7);
    +      getDescriptor().getMessageType(7);
         internal_static_a2a_v1_Artifact_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_Artifact_descriptor,
             new java.lang.String[] { "ArtifactId", "Name", "Description", "Parts", "Metadata", "Extensions", });
         internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor =
    -      getDescriptor().getMessageTypes().get(8);
    +      getDescriptor().getMessageType(8);
         internal_static_a2a_v1_TaskStatusUpdateEvent_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor,
             new java.lang.String[] { "TaskId", "ContextId", "Status", "Final", "Metadata", });
         internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor =
    -      getDescriptor().getMessageTypes().get(9);
    +      getDescriptor().getMessageType(9);
         internal_static_a2a_v1_TaskArtifactUpdateEvent_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor,
             new java.lang.String[] { "TaskId", "ContextId", "Artifact", "Append", "LastChunk", "Metadata", });
         internal_static_a2a_v1_PushNotificationConfig_descriptor =
    -      getDescriptor().getMessageTypes().get(10);
    +      getDescriptor().getMessageType(10);
         internal_static_a2a_v1_PushNotificationConfig_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_PushNotificationConfig_descriptor,
             new java.lang.String[] { "Id", "Url", "Token", "Authentication", });
         internal_static_a2a_v1_AuthenticationInfo_descriptor =
    -      getDescriptor().getMessageTypes().get(11);
    +      getDescriptor().getMessageType(11);
         internal_static_a2a_v1_AuthenticationInfo_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AuthenticationInfo_descriptor,
             new java.lang.String[] { "Schemes", "Credentials", });
         internal_static_a2a_v1_AgentInterface_descriptor =
    -      getDescriptor().getMessageTypes().get(12);
    +      getDescriptor().getMessageType(12);
         internal_static_a2a_v1_AgentInterface_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentInterface_descriptor,
    -        new java.lang.String[] { "Url", "ProtocolBinding", });
    +        new java.lang.String[] { "Url", "ProtocolBinding", "Tenant", });
         internal_static_a2a_v1_AgentCard_descriptor =
    -      getDescriptor().getMessageTypes().get(13);
    +      getDescriptor().getMessageType(13);
         internal_static_a2a_v1_AgentCard_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentCard_descriptor,
    -        new java.lang.String[] { "ProtocolVersion", "Name", "Description", "SupportedInterfaces", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsAuthenticatedExtendedCard", "Signatures", "IconUrl", });
    +        new java.lang.String[] { "ProtocolVersion", "Name", "Description", "SupportedInterfaces", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsExtendedAgentCard", "Signatures", "IconUrl", });
         internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor =
    -      internal_static_a2a_v1_AgentCard_descriptor.getNestedTypes().get(0);
    +      internal_static_a2a_v1_AgentCard_descriptor.getNestedType(0);
         internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor,
             new java.lang.String[] { "Key", "Value", });
         internal_static_a2a_v1_AgentProvider_descriptor =
    -      getDescriptor().getMessageTypes().get(14);
    +      getDescriptor().getMessageType(14);
         internal_static_a2a_v1_AgentProvider_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentProvider_descriptor,
             new java.lang.String[] { "Url", "Organization", });
         internal_static_a2a_v1_AgentCapabilities_descriptor =
    -      getDescriptor().getMessageTypes().get(15);
    +      getDescriptor().getMessageType(15);
         internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentCapabilities_descriptor,
             new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "StateTransitionHistory", });
         internal_static_a2a_v1_AgentExtension_descriptor =
    -      getDescriptor().getMessageTypes().get(16);
    +      getDescriptor().getMessageType(16);
         internal_static_a2a_v1_AgentExtension_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentExtension_descriptor,
             new java.lang.String[] { "Uri", "Description", "Required", "Params", });
         internal_static_a2a_v1_AgentSkill_descriptor =
    -      getDescriptor().getMessageTypes().get(17);
    +      getDescriptor().getMessageType(17);
         internal_static_a2a_v1_AgentSkill_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentSkill_descriptor,
             new java.lang.String[] { "Id", "Name", "Description", "Tags", "Examples", "InputModes", "OutputModes", "Security", });
         internal_static_a2a_v1_AgentCardSignature_descriptor =
    -      getDescriptor().getMessageTypes().get(18);
    +      getDescriptor().getMessageType(18);
         internal_static_a2a_v1_AgentCardSignature_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AgentCardSignature_descriptor,
             new java.lang.String[] { "Protected", "Signature", "Header", });
         internal_static_a2a_v1_TaskPushNotificationConfig_descriptor =
    -      getDescriptor().getMessageTypes().get(19);
    +      getDescriptor().getMessageType(19);
         internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_TaskPushNotificationConfig_descriptor,
             new java.lang.String[] { "Name", "PushNotificationConfig", });
         internal_static_a2a_v1_StringList_descriptor =
    -      getDescriptor().getMessageTypes().get(20);
    +      getDescriptor().getMessageType(20);
         internal_static_a2a_v1_StringList_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_StringList_descriptor,
             new java.lang.String[] { "List", });
         internal_static_a2a_v1_Security_descriptor =
    -      getDescriptor().getMessageTypes().get(21);
    +      getDescriptor().getMessageType(21);
         internal_static_a2a_v1_Security_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_Security_descriptor,
             new java.lang.String[] { "Schemes", });
         internal_static_a2a_v1_Security_SchemesEntry_descriptor =
    -      internal_static_a2a_v1_Security_descriptor.getNestedTypes().get(0);
    +      internal_static_a2a_v1_Security_descriptor.getNestedType(0);
         internal_static_a2a_v1_Security_SchemesEntry_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_Security_SchemesEntry_descriptor,
             new java.lang.String[] { "Key", "Value", });
         internal_static_a2a_v1_SecurityScheme_descriptor =
    -      getDescriptor().getMessageTypes().get(22);
    +      getDescriptor().getMessageType(22);
         internal_static_a2a_v1_SecurityScheme_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_SecurityScheme_descriptor,
             new java.lang.String[] { "ApiKeySecurityScheme", "HttpAuthSecurityScheme", "Oauth2SecurityScheme", "OpenIdConnectSecurityScheme", "MtlsSecurityScheme", "Scheme", });
         internal_static_a2a_v1_APIKeySecurityScheme_descriptor =
    -      getDescriptor().getMessageTypes().get(23);
    +      getDescriptor().getMessageType(23);
         internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_APIKeySecurityScheme_descriptor,
             new java.lang.String[] { "Description", "Location", "Name", });
         internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor =
    -      getDescriptor().getMessageTypes().get(24);
    +      getDescriptor().getMessageType(24);
         internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor,
             new java.lang.String[] { "Description", "Scheme", "BearerFormat", });
         internal_static_a2a_v1_OAuth2SecurityScheme_descriptor =
    -      getDescriptor().getMessageTypes().get(25);
    +      getDescriptor().getMessageType(25);
         internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_OAuth2SecurityScheme_descriptor,
             new java.lang.String[] { "Description", "Flows", "Oauth2MetadataUrl", });
         internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor =
    -      getDescriptor().getMessageTypes().get(26);
    +      getDescriptor().getMessageType(26);
         internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor,
             new java.lang.String[] { "Description", "OpenIdConnectUrl", });
         internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor =
    -      getDescriptor().getMessageTypes().get(27);
    +      getDescriptor().getMessageType(27);
         internal_static_a2a_v1_MutualTlsSecurityScheme_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor,
             new java.lang.String[] { "Description", });
         internal_static_a2a_v1_OAuthFlows_descriptor =
    -      getDescriptor().getMessageTypes().get(28);
    +      getDescriptor().getMessageType(28);
         internal_static_a2a_v1_OAuthFlows_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_OAuthFlows_descriptor,
             new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "Implicit", "Password", "Flow", });
         internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor =
    -      getDescriptor().getMessageTypes().get(29);
    +      getDescriptor().getMessageType(29);
         internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor,
             new java.lang.String[] { "AuthorizationUrl", "TokenUrl", "RefreshUrl", "Scopes", });
         internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor =
    -      internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor.getNestedTypes().get(0);
    +      internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor.getNestedType(0);
         internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor,
             new java.lang.String[] { "Key", "Value", });
         internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor =
    -      getDescriptor().getMessageTypes().get(30);
    +      getDescriptor().getMessageType(30);
         internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor,
             new java.lang.String[] { "TokenUrl", "RefreshUrl", "Scopes", });
         internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor =
    -      internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor.getNestedTypes().get(0);
    +      internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor.getNestedType(0);
         internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor,
             new java.lang.String[] { "Key", "Value", });
         internal_static_a2a_v1_ImplicitOAuthFlow_descriptor =
    -      getDescriptor().getMessageTypes().get(31);
    +      getDescriptor().getMessageType(31);
         internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ImplicitOAuthFlow_descriptor,
             new java.lang.String[] { "AuthorizationUrl", "RefreshUrl", "Scopes", });
         internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor =
    -      internal_static_a2a_v1_ImplicitOAuthFlow_descriptor.getNestedTypes().get(0);
    +      internal_static_a2a_v1_ImplicitOAuthFlow_descriptor.getNestedType(0);
         internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor,
             new java.lang.String[] { "Key", "Value", });
         internal_static_a2a_v1_PasswordOAuthFlow_descriptor =
    -      getDescriptor().getMessageTypes().get(32);
    +      getDescriptor().getMessageType(32);
         internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_PasswordOAuthFlow_descriptor,
             new java.lang.String[] { "TokenUrl", "RefreshUrl", "Scopes", });
         internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor =
    -      internal_static_a2a_v1_PasswordOAuthFlow_descriptor.getNestedTypes().get(0);
    +      internal_static_a2a_v1_PasswordOAuthFlow_descriptor.getNestedType(0);
         internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor,
             new java.lang.String[] { "Key", "Value", });
         internal_static_a2a_v1_SendMessageRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(33);
    +      getDescriptor().getMessageType(33);
         internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_SendMessageRequest_descriptor,
    -        new java.lang.String[] { "Request", "Configuration", "Metadata", });
    +        new java.lang.String[] { "Tenant", "Request", "Configuration", "Metadata", });
         internal_static_a2a_v1_GetTaskRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(34);
    +      getDescriptor().getMessageType(34);
         internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_GetTaskRequest_descriptor,
    -        new java.lang.String[] { "Name", "HistoryLength", });
    +        new java.lang.String[] { "Tenant", "Name", "HistoryLength", });
         internal_static_a2a_v1_ListTasksRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(35);
    +      getDescriptor().getMessageType(35);
         internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ListTasksRequest_descriptor,
    -        new java.lang.String[] { "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "LastUpdatedAfter", "IncludeArtifacts", "Metadata", });
    +        new java.lang.String[] { "Tenant", "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "LastUpdatedAfter", "IncludeArtifacts", });
         internal_static_a2a_v1_ListTasksResponse_descriptor =
    -      getDescriptor().getMessageTypes().get(36);
    +      getDescriptor().getMessageType(36);
         internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ListTasksResponse_descriptor,
             new java.lang.String[] { "Tasks", "NextPageToken", "PageSize", "TotalSize", });
         internal_static_a2a_v1_CancelTaskRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(37);
    +      getDescriptor().getMessageType(37);
         internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_CancelTaskRequest_descriptor,
    -        new java.lang.String[] { "Name", });
    +        new java.lang.String[] { "Tenant", "Name", });
         internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(38);
    +      getDescriptor().getMessageType(38);
         internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor,
    -        new java.lang.String[] { "Name", });
    +        new java.lang.String[] { "Tenant", "Name", });
         internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(39);
    +      getDescriptor().getMessageType(39);
         internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor,
    -        new java.lang.String[] { "Name", });
    +        new java.lang.String[] { "Tenant", "Name", });
         internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(40);
    +      getDescriptor().getMessageType(40);
         internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor,
    -        new java.lang.String[] { "Parent", "ConfigId", "Config", });
    +        new java.lang.String[] { "Tenant", "Parent", "ConfigId", "Config", });
         internal_static_a2a_v1_SubscribeToTaskRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(41);
    +      getDescriptor().getMessageType(41);
         internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_SubscribeToTaskRequest_descriptor,
    -        new java.lang.String[] { "Name", });
    +        new java.lang.String[] { "Tenant", "Name", });
         internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(42);
    +      getDescriptor().getMessageType(42);
         internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor,
    -        new java.lang.String[] { "Parent", "PageSize", "PageToken", });
    +        new java.lang.String[] { "Tenant", "Parent", "PageSize", "PageToken", });
         internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor =
    -      getDescriptor().getMessageTypes().get(43);
    +      getDescriptor().getMessageType(43);
         internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor,
    -        new java.lang.String[] { });
    +        new java.lang.String[] { "Tenant", });
         internal_static_a2a_v1_SendMessageResponse_descriptor =
    -      getDescriptor().getMessageTypes().get(44);
    +      getDescriptor().getMessageType(44);
         internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_SendMessageResponse_descriptor,
             new java.lang.String[] { "Task", "Msg", "Payload", });
         internal_static_a2a_v1_StreamResponse_descriptor =
    -      getDescriptor().getMessageTypes().get(45);
    +      getDescriptor().getMessageType(45);
         internal_static_a2a_v1_StreamResponse_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_StreamResponse_descriptor,
             new java.lang.String[] { "Task", "Msg", "StatusUpdate", "ArtifactUpdate", "Payload", });
         internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor =
    -      getDescriptor().getMessageTypes().get(46);
    +      getDescriptor().getMessageType(46);
         internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable = new
           com.google.protobuf.GeneratedMessage.FieldAccessorTable(
             internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor,
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java
    index d5261ab39..0faa8f73d 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java
    @@ -779,8 +779,8 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) throws
          * Set a push notification config for a task.
          * 
    */ - public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getSetTaskPushNotificationConfigMethod(), getCallOptions(), request); } @@ -809,8 +809,8 @@ public io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificati * GetExtendedAgentCard returns the extended agent card for authenticated agents. *
    */ - public io.a2a.grpc.AgentCard getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request) { - return io.grpc.stub.ClientCalls.blockingUnaryCall( + public io.a2a.grpc.AgentCard getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request) throws io.grpc.StatusException { + return io.grpc.stub.ClientCalls.blockingV2UnaryCall( getChannel(), getGetExtendedAgentCardMethod(), getCallOptions(), request); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java index eaefa966d..a189729df 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecurityScheme.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class APIKeySecurityScheme extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - APIKeySecurityScheme.class.getName()); + "APIKeySecurityScheme"); } // Use APIKeySecurityScheme.newBuilder() to construct. private APIKeySecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java index 3444d5fc3..757464225 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/APIKeySecuritySchemeOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java index de58e060e..84ef70448 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class AgentCapabilities extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - AgentCapabilities.class.getName()); + "AgentCapabilities"); } // Use AgentCapabilities.newBuilder() to construct. private AgentCapabilities(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java index 32214c333..df5d9277c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index cf214725b..b352e84f5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -11,11 +11,6 @@ * AgentCard is a self-describing manifest for an agent. It provides essential * metadata including the agent's identity, capabilities, skills, supported * communication methods, and security requirements. - * AgentCard conveys key information: - * - Overall details (version, name, description, uses) - * - Skills; a set of actions/solutions the agent can perform - * - Default modalities/content types supported by the agent. - * - Authentication requirements * Next ID: 20 *
    * @@ -31,10 +26,10 @@ public final class AgentCard extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - AgentCard.class.getName()); + "AgentCard"); } // Use AgentCard.newBuilder() to construct. private AgentCard(com.google.protobuf.GeneratedMessage.Builder builder) { @@ -319,7 +314,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return Whether the url field is set. */ @java.lang.Override @@ -333,7 +328,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return The url. */ @java.lang.Override @@ -356,7 +351,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return The bytes for url. */ @java.lang.Override @@ -384,7 +379,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return Whether the preferredTransport field is set. */ @java.lang.Override @@ -398,7 +393,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return The preferredTransport. */ @java.lang.Override @@ -421,7 +416,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return The bytes for preferredTransport. */ @java.lang.Override @@ -964,9 +959,9 @@ public java.lang.String getDefaultOutputModes(int index) { private java.util.List skills_; /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -977,9 +972,9 @@ public java.util.List getSkillsList() { } /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -991,9 +986,9 @@ public java.util.List getSkillsList() { } /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -1004,9 +999,9 @@ public int getSkillsCount() { } /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -1017,9 +1012,9 @@ public io.a2a.grpc.AgentSkill getSkills(int index) { } /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -1030,18 +1025,18 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( return skills_.get(index); } - public static final int SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER = 13; - private boolean supportsAuthenticatedExtendedCard_ = false; + public static final int SUPPORTS_EXTENDED_AGENT_CARD_FIELD_NUMBER = 13; + private boolean supportsExtendedAgentCard_ = false; /** *
        * Whether the agent supports providing an extended agent card when authenticated.
        * 
    * - * optional bool supports_authenticated_extended_card = 13; - * @return Whether the supportsAuthenticatedExtendedCard field is set. + * optional bool supports_extended_agent_card = 13; + * @return Whether the supportsExtendedAgentCard field is set. */ @java.lang.Override - public boolean hasSupportsAuthenticatedExtendedCard() { + public boolean hasSupportsExtendedAgentCard() { return ((bitField0_ & 0x00000040) != 0); } /** @@ -1049,12 +1044,12 @@ public boolean hasSupportsAuthenticatedExtendedCard() { * Whether the agent supports providing an extended agent card when authenticated. *
    * - * optional bool supports_authenticated_extended_card = 13; - * @return The supportsAuthenticatedExtendedCard. + * optional bool supports_extended_agent_card = 13; + * @return The supportsExtendedAgentCard. */ @java.lang.Override - public boolean getSupportsAuthenticatedExtendedCard() { - return supportsAuthenticatedExtendedCard_; + public boolean getSupportsExtendedAgentCard() { + return supportsExtendedAgentCard_; } public static final int SIGNATURES_FIELD_NUMBER = 17; @@ -1231,7 +1226,7 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) output.writeMessage(12, skills_.get(i)); } if (((bitField0_ & 0x00000040) != 0)) { - output.writeBool(13, supportsAuthenticatedExtendedCard_); + output.writeBool(13, supportsExtendedAgentCard_); } if (((bitField0_ & 0x00000004) != 0)) { com.google.protobuf.GeneratedMessage.writeString(output, 14, preferredTransport_); @@ -1319,7 +1314,7 @@ public int getSerializedSize() { } if (((bitField0_ & 0x00000040) != 0)) { size += com.google.protobuf.CodedOutputStream - .computeBoolSize(13, supportsAuthenticatedExtendedCard_); + .computeBoolSize(13, supportsExtendedAgentCard_); } if (((bitField0_ & 0x00000004) != 0)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(14, preferredTransport_); @@ -1407,10 +1402,10 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getDefaultOutputModesList())) return false; if (!getSkillsList() .equals(other.getSkillsList())) return false; - if (hasSupportsAuthenticatedExtendedCard() != other.hasSupportsAuthenticatedExtendedCard()) return false; - if (hasSupportsAuthenticatedExtendedCard()) { - if (getSupportsAuthenticatedExtendedCard() - != other.getSupportsAuthenticatedExtendedCard()) return false; + if (hasSupportsExtendedAgentCard() != other.hasSupportsExtendedAgentCard()) return false; + if (hasSupportsExtendedAgentCard()) { + if (getSupportsExtendedAgentCard() + != other.getSupportsExtendedAgentCard()) return false; } if (!getSignaturesList() .equals(other.getSignaturesList())) return false; @@ -1488,10 +1483,10 @@ public int hashCode() { hash = (37 * hash) + SKILLS_FIELD_NUMBER; hash = (53 * hash) + getSkillsList().hashCode(); } - if (hasSupportsAuthenticatedExtendedCard()) { - hash = (37 * hash) + SUPPORTS_AUTHENTICATED_EXTENDED_CARD_FIELD_NUMBER; + if (hasSupportsExtendedAgentCard()) { + hash = (37 * hash) + SUPPORTS_EXTENDED_AGENT_CARD_FIELD_NUMBER; hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getSupportsAuthenticatedExtendedCard()); + getSupportsExtendedAgentCard()); } if (getSignaturesCount() > 0) { hash = (37 * hash) + SIGNATURES_FIELD_NUMBER; @@ -1604,11 +1599,6 @@ protected Builder newBuilderForType( * AgentCard is a self-describing manifest for an agent. It provides essential * metadata including the agent's identity, capabilities, skills, supported * communication methods, and security requirements. - * AgentCard conveys key information: - * - Overall details (version, name, description, uses) - * - Skills; a set of actions/solutions the agent can perform - * - Default modalities/content types supported by the agent. - * - Authentication requirements * Next ID: 20 *
    * @@ -1729,7 +1719,7 @@ public Builder clear() { skillsBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00008000); - supportsAuthenticatedExtendedCard_ = false; + supportsExtendedAgentCard_ = false; if (signaturesBuilder_ == null) { signatures_ = java.util.Collections.emptyList(); } else { @@ -1870,7 +1860,7 @@ private void buildPartial0(io.a2a.grpc.AgentCard result) { result.defaultOutputModes_ = defaultOutputModes_; } if (((from_bitField0_ & 0x00010000) != 0)) { - result.supportsAuthenticatedExtendedCard_ = supportsAuthenticatedExtendedCard_; + result.supportsExtendedAgentCard_ = supportsExtendedAgentCard_; to_bitField0_ |= 0x00000040; } if (((from_bitField0_ & 0x00040000) != 0)) { @@ -2060,8 +2050,8 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } } } - if (other.hasSupportsAuthenticatedExtendedCard()) { - setSupportsAuthenticatedExtendedCard(other.getSupportsAuthenticatedExtendedCard()); + if (other.hasSupportsExtendedAgentCard()) { + setSupportsExtendedAgentCard(other.getSupportsExtendedAgentCard()); } if (signaturesBuilder_ == null) { if (!other.signatures_.isEmpty()) { @@ -2207,7 +2197,7 @@ public Builder mergeFrom( break; } // case 98 case 104: { - supportsAuthenticatedExtendedCard_ = input.readBool(); + supportsExtendedAgentCard_ = input.readBool(); bitField0_ |= 0x00010000; break; } // case 104 @@ -2910,7 +2900,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return Whether the url field is set. */ @java.lang.Deprecated public boolean hasUrl() { @@ -2923,7 +2913,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return The url. */ @java.lang.Deprecated public java.lang.String getUrl() { @@ -2945,7 +2935,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return The bytes for url. */ @java.lang.Deprecated public com.google.protobuf.ByteString @@ -2968,7 +2958,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @param value The url to set. * @return This builder for chaining. */ @@ -2987,7 +2977,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return This builder for chaining. */ @java.lang.Deprecated public Builder clearUrl() { @@ -3003,7 +2993,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @param value The bytes for url to set. * @return This builder for chaining. */ @@ -3025,7 +3015,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return Whether the preferredTransport field is set. */ @java.lang.Deprecated public boolean hasPreferredTransport() { @@ -3038,7 +3028,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return The preferredTransport. */ @java.lang.Deprecated public java.lang.String getPreferredTransport() { @@ -3060,7 +3050,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return The bytes for preferredTransport. */ @java.lang.Deprecated public com.google.protobuf.ByteString @@ -3083,7 +3073,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @param value The preferredTransport to set. * @return This builder for chaining. */ @@ -3102,7 +3092,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return This builder for chaining. */ @java.lang.Deprecated public Builder clearPreferredTransport() { @@ -3118,7 +3108,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @param value The bytes for preferredTransport to set. * @return This builder for chaining. */ @@ -4801,9 +4791,9 @@ private void ensureSkillsIsMutable() { /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4817,9 +4807,9 @@ public java.util.List getSkillsList() { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4833,9 +4823,9 @@ public int getSkillsCount() { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4849,9 +4839,9 @@ public io.a2a.grpc.AgentSkill getSkills(int index) { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4872,9 +4862,9 @@ public Builder setSkills( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4892,9 +4882,9 @@ public Builder setSkills( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4914,9 +4904,9 @@ public Builder addSkills(io.a2a.grpc.AgentSkill value) { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4937,9 +4927,9 @@ public Builder addSkills( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4957,9 +4947,9 @@ public Builder addSkills( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4977,9 +4967,9 @@ public Builder addSkills( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -4998,9 +4988,9 @@ public Builder addAllSkills( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5017,9 +5007,9 @@ public Builder clearSkills() { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5036,9 +5026,9 @@ public Builder removeSkills(int index) { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5049,9 +5039,9 @@ public io.a2a.grpc.AgentSkill.Builder getSkillsBuilder( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5065,9 +5055,9 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5082,9 +5072,9 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5095,9 +5085,9 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder() { } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5109,9 +5099,9 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( } /** *
    -     * Skills represent a unit of ability an agent can perform. This may
    -     * somewhat abstract but represents a more focused set of actions that the
    -     * agent is highly likely to succeed at.
    +     * Skills represent an ability of an agent. It is largely
    +     * a descriptive concept but represents a more focused set of behaviors that the
    +     * agent is likely to succeed at.
          * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -5135,17 +5125,17 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder( return skillsBuilder_; } - private boolean supportsAuthenticatedExtendedCard_ ; + private boolean supportsExtendedAgentCard_ ; /** *
          * Whether the agent supports providing an extended agent card when authenticated.
          * 
    * - * optional bool supports_authenticated_extended_card = 13; - * @return Whether the supportsAuthenticatedExtendedCard field is set. + * optional bool supports_extended_agent_card = 13; + * @return Whether the supportsExtendedAgentCard field is set. */ @java.lang.Override - public boolean hasSupportsAuthenticatedExtendedCard() { + public boolean hasSupportsExtendedAgentCard() { return ((bitField0_ & 0x00010000) != 0); } /** @@ -5153,25 +5143,25 @@ public boolean hasSupportsAuthenticatedExtendedCard() { * Whether the agent supports providing an extended agent card when authenticated. *
    * - * optional bool supports_authenticated_extended_card = 13; - * @return The supportsAuthenticatedExtendedCard. + * optional bool supports_extended_agent_card = 13; + * @return The supportsExtendedAgentCard. */ @java.lang.Override - public boolean getSupportsAuthenticatedExtendedCard() { - return supportsAuthenticatedExtendedCard_; + public boolean getSupportsExtendedAgentCard() { + return supportsExtendedAgentCard_; } /** *
          * Whether the agent supports providing an extended agent card when authenticated.
          * 
    * - * optional bool supports_authenticated_extended_card = 13; - * @param value The supportsAuthenticatedExtendedCard to set. + * optional bool supports_extended_agent_card = 13; + * @param value The supportsExtendedAgentCard to set. * @return This builder for chaining. */ - public Builder setSupportsAuthenticatedExtendedCard(boolean value) { + public Builder setSupportsExtendedAgentCard(boolean value) { - supportsAuthenticatedExtendedCard_ = value; + supportsExtendedAgentCard_ = value; bitField0_ |= 0x00010000; onChanged(); return this; @@ -5181,12 +5171,12 @@ public Builder setSupportsAuthenticatedExtendedCard(boolean value) { * Whether the agent supports providing an extended agent card when authenticated. *
    * - * optional bool supports_authenticated_extended_card = 13; + * optional bool supports_extended_agent_card = 13; * @return This builder for chaining. */ - public Builder clearSupportsAuthenticatedExtendedCard() { + public Builder clearSupportsExtendedAgentCard() { bitField0_ = (bitField0_ & ~0x00010000); - supportsAuthenticatedExtendedCard_ = false; + supportsExtendedAgentCard_ = false; onChanged(); return this; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java index d07f03e2a..91ad7f3b4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -139,7 +139,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return Whether the url field is set. */ @java.lang.Deprecated boolean hasUrl(); @@ -150,7 +150,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return The url. */ @java.lang.Deprecated java.lang.String getUrl(); @@ -161,7 +161,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string url = 3 [deprecated = true]; * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=397 + * See a2a.proto;l=426 * @return The bytes for url. */ @java.lang.Deprecated com.google.protobuf.ByteString @@ -174,7 +174,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return Whether the preferredTransport field is set. */ @java.lang.Deprecated boolean hasPreferredTransport(); @@ -185,7 +185,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return The preferredTransport. */ @java.lang.Deprecated java.lang.String getPreferredTransport(); @@ -196,7 +196,7 @@ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( * * optional string preferred_transport = 14 [deprecated = true]; * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=399 + * See a2a.proto;l=428 * @return The bytes for preferredTransport. */ @java.lang.Deprecated com.google.protobuf.ByteString @@ -546,9 +546,9 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -557,9 +557,9 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( getSkillsList(); /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -567,9 +567,9 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( io.a2a.grpc.AgentSkill getSkills(int index); /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -577,9 +577,9 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( int getSkillsCount(); /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -588,9 +588,9 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( getSkillsOrBuilderList(); /** *
    -   * Skills represent a unit of ability an agent can perform. This may
    -   * somewhat abstract but represents a more focused set of actions that the
    -   * agent is highly likely to succeed at.
    +   * Skills represent an ability of an agent. It is largely
    +   * a descriptive concept but represents a more focused set of behaviors that the
    +   * agent is likely to succeed at.
        * 
    * * repeated .a2a.v1.AgentSkill skills = 12 [(.google.api.field_behavior) = REQUIRED]; @@ -603,19 +603,19 @@ io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder( * Whether the agent supports providing an extended agent card when authenticated. *
    * - * optional bool supports_authenticated_extended_card = 13; - * @return Whether the supportsAuthenticatedExtendedCard field is set. + * optional bool supports_extended_agent_card = 13; + * @return Whether the supportsExtendedAgentCard field is set. */ - boolean hasSupportsAuthenticatedExtendedCard(); + boolean hasSupportsExtendedAgentCard(); /** *
        * Whether the agent supports providing an extended agent card when authenticated.
        * 
    * - * optional bool supports_authenticated_extended_card = 13; - * @return The supportsAuthenticatedExtendedCard. + * optional bool supports_extended_agent_card = 13; + * @return The supportsExtendedAgentCard. */ - boolean getSupportsAuthenticatedExtendedCard(); + boolean getSupportsExtendedAgentCard(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java
    index 132005624..eef107309 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignature.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -24,10 +24,10 @@ public final class AgentCardSignature extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      AgentCardSignature.class.getName());
    +      "AgentCardSignature");
       }
       // Use AgentCardSignature.newBuilder() to construct.
       private AgentCardSignature(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java
    index d863a9917..1d05b9c6d 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardSignatureOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java
    index a6920fdb1..6f942268e 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtension.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -23,10 +23,10 @@ public final class AgentExtension extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      AgentExtension.class.getName());
    +      "AgentExtension");
       }
       // Use AgentExtension.newBuilder() to construct.
       private AgentExtension(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java
    index ce8d86b95..e5df98bb1 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentExtensionOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java
    index 1af9120c1..17db01b94 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -24,10 +24,10 @@ public final class AgentInterface extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      AgentInterface.class.getName());
    +      "AgentInterface");
       }
       // Use AgentInterface.newBuilder() to construct.
       private AgentInterface(com.google.protobuf.GeneratedMessage.Builder builder) {
    @@ -36,6 +36,7 @@ private AgentInterface(com.google.protobuf.GeneratedMessage.Builder builder)
       private AgentInterface() {
         url_ = "";
         protocolBinding_ = "";
    +    tenant_ = "";
       }
     
       public static final com.google.protobuf.Descriptors.Descriptor
    @@ -107,8 +108,7 @@ public java.lang.String getUrl() {
        * 
        * The protocol binding supported at this URL. This is an open form string, to be
        * easily extended for other protocol bindings. The core ones officially
    -   * supported are JSONRPC, GRPC and HTTP+JSON.
    -   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +   * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
        * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -131,8 +131,7 @@ public java.lang.String getProtocolBinding() { *
        * The protocol binding supported at this URL. This is an open form string, to be
        * easily extended for other protocol bindings. The core ones officially
    -   * supported are JSONRPC, GRPC and HTTP+JSON.
    -   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +   * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
        * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -153,6 +152,53 @@ public java.lang.String getProtocolBinding() { } } + public static final int TENANT_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Tenant to be set in the request when calling the agent.
    +   * 
    + * + * string tenant = 3; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Tenant to be set in the request when calling the agent.
    +   * 
    + * + * string tenant = 3; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -173,6 +219,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolBinding_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, protocolBinding_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, tenant_); + } getUnknownFields().writeTo(output); } @@ -188,6 +237,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolBinding_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, protocolBinding_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -207,6 +259,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getUrl())) return false; if (!getProtocolBinding() .equals(other.getProtocolBinding())) return false; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -222,6 +276,8 @@ public int hashCode() { hash = (53 * hash) + getUrl().hashCode(); hash = (37 * hash) + PROTOCOL_BINDING_FIELD_NUMBER; hash = (53 * hash) + getProtocolBinding().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -361,6 +417,7 @@ public Builder clear() { bitField0_ = 0; url_ = ""; protocolBinding_ = ""; + tenant_ = ""; return this; } @@ -400,6 +457,9 @@ private void buildPartial0(io.a2a.grpc.AgentInterface result) { if (((from_bitField0_ & 0x00000002) != 0)) { result.protocolBinding_ = protocolBinding_; } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.tenant_ = tenant_; + } } @java.lang.Override @@ -424,6 +484,11 @@ public Builder mergeFrom(io.a2a.grpc.AgentInterface other) { bitField0_ |= 0x00000002; onChanged(); } + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000004; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -460,6 +525,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000002; break; } // case 18 + case 26: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -579,8 +649,7 @@ public Builder setUrlBytes( *
          * The protocol binding supported at this URL. This is an open form string, to be
          * easily extended for other protocol bindings. The core ones officially
    -     * supported are JSONRPC, GRPC and HTTP+JSON.
    -     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +     * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
          * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -602,8 +671,7 @@ public java.lang.String getProtocolBinding() { *
          * The protocol binding supported at this URL. This is an open form string, to be
          * easily extended for other protocol bindings. The core ones officially
    -     * supported are JSONRPC, GRPC and HTTP+JSON.
    -     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +     * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
          * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -626,8 +694,7 @@ public java.lang.String getProtocolBinding() { *
          * The protocol binding supported at this URL. This is an open form string, to be
          * easily extended for other protocol bindings. The core ones officially
    -     * supported are JSONRPC, GRPC and HTTP+JSON.
    -     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +     * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
          * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -646,8 +713,7 @@ public Builder setProtocolBinding( *
          * The protocol binding supported at this URL. This is an open form string, to be
          * easily extended for other protocol bindings. The core ones officially
    -     * supported are JSONRPC, GRPC and HTTP+JSON.
    -     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +     * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
          * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -663,8 +729,7 @@ public Builder clearProtocolBinding() { *
          * The protocol binding supported at this URL. This is an open form string, to be
          * easily extended for other protocol bindings. The core ones officially
    -     * supported are JSONRPC, GRPC and HTTP+JSON.
    -     * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +     * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
          * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -681,6 +746,98 @@ public Builder setProtocolBindingBytes( return this; } + private java.lang.Object tenant_ = ""; + /** + *
    +     * Tenant to be set in the request when calling the agent.
    +     * 
    + * + * string tenant = 3; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Tenant to be set in the request when calling the agent.
    +     * 
    + * + * string tenant = 3; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Tenant to be set in the request when calling the agent.
    +     * 
    + * + * string tenant = 3; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
    +     * Tenant to be set in the request when calling the agent.
    +     * 
    + * + * string tenant = 3; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
    +     * Tenant to be set in the request when calling the agent.
    +     * 
    + * + * string tenant = 3; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentInterface) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java index f07f30916..cfc8b7ad9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -36,8 +36,7 @@ public interface AgentInterfaceOrBuilder extends *
        * The protocol binding supported at this URL. This is an open form string, to be
        * easily extended for other protocol bindings. The core ones officially
    -   * supported are JSONRPC, GRPC and HTTP+JSON.
    -   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +   * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
        * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -48,8 +47,7 @@ public interface AgentInterfaceOrBuilder extends *
        * The protocol binding supported at this URL. This is an open form string, to be
        * easily extended for other protocol bindings. The core ones officially
    -   * supported are JSONRPC, GRPC and HTTP+JSON.
    -   * Example: "JSONRPC", "GRPC", "HTTP+JSON"
    +   * supported are `JSONRPC`, `GRPC` and `HTTP+JSON`.
        * 
    * * string protocol_binding = 2 [(.google.api.field_behavior) = REQUIRED]; @@ -57,4 +55,24 @@ public interface AgentInterfaceOrBuilder extends */ com.google.protobuf.ByteString getProtocolBindingBytes(); + + /** + *
    +   * Tenant to be set in the request when calling the agent.
    +   * 
    + * + * string tenant = 3; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Tenant to be set in the request when calling the agent.
    +   * 
    + * + * string tenant = 3; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java index 3b4dabdfb..fdd9402a3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProvider.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class AgentProvider extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - AgentProvider.class.getName()); + "AgentProvider"); } // Use AgentProvider.newBuilder() to construct. private AgentProvider(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java index f7a6f2491..a0e2efefa 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentProviderOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java index 462a8edec..19596fe14 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class AgentSkill extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - AgentSkill.class.getName()); + "AgentSkill"); } // Use AgentSkill.newBuilder() to construct. private AgentSkill(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java index cddebfff9..c9615febc 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java index aef816208..6716e86e2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Artifact.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class Artifact extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - Artifact.class.getName()); + "Artifact"); } // Use Artifact.newBuilder() to construct. private Artifact(com.google.protobuf.GeneratedMessage.Builder builder) { @@ -203,7 +203,7 @@ public java.lang.String getDescription() { private java.util.List parts_; /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -214,7 +214,7 @@ public java.util.List getPartsList() { } /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -226,7 +226,7 @@ public java.util.List getPartsList() { } /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -237,7 +237,7 @@ public int getPartsCount() { } /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -248,7 +248,7 @@ public io.a2a.grpc.Part getParts(int index) { } /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1149,7 +1149,7 @@ private void ensurePartsIsMutable() { /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1163,7 +1163,7 @@ public java.util.List getPartsList() { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1177,7 +1177,7 @@ public int getPartsCount() { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1191,7 +1191,7 @@ public io.a2a.grpc.Part getParts(int index) { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1212,7 +1212,7 @@ public Builder setParts( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1230,7 +1230,7 @@ public Builder setParts( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1250,7 +1250,7 @@ public Builder addParts(io.a2a.grpc.Part value) { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1271,7 +1271,7 @@ public Builder addParts( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1289,7 +1289,7 @@ public Builder addParts( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1307,7 +1307,7 @@ public Builder addParts( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1326,7 +1326,7 @@ public Builder addAllParts( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1343,7 +1343,7 @@ public Builder clearParts() { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1360,7 +1360,7 @@ public Builder removeParts(int index) { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1371,7 +1371,7 @@ public io.a2a.grpc.Part.Builder getPartsBuilder( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1385,7 +1385,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1400,7 +1400,7 @@ public io.a2a.grpc.PartOrBuilder getPartsOrBuilder( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1411,7 +1411,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder() { } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -1423,7 +1423,7 @@ public io.a2a.grpc.Part.Builder addPartsBuilder( } /** *
    -     * The content of the artifact.
    +     * The content of the artifact. Must contain at least one part.
          * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java index f05c3125d..3c1de141f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ArtifactOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -74,7 +74,7 @@ public interface ArtifactOrBuilder extends /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -83,7 +83,7 @@ public interface ArtifactOrBuilder extends getPartsList(); /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -91,7 +91,7 @@ public interface ArtifactOrBuilder extends io.a2a.grpc.Part getParts(int index); /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -99,7 +99,7 @@ public interface ArtifactOrBuilder extends int getPartsCount(); /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; @@ -108,7 +108,7 @@ public interface ArtifactOrBuilder extends getPartsOrBuilderList(); /** *
    -   * The content of the artifact.
    +   * The content of the artifact. Must contain at least one part.
        * 
    * * repeated .a2a.v1.Part parts = 5 [(.google.api.field_behavior) = REQUIRED]; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java index bc6f49923..38b9e065c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class AuthenticationInfo extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - AuthenticationInfo.class.getName()); + "AuthenticationInfo"); } // Use AuthenticationInfo.newBuilder() to construct. private AuthenticationInfo(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java index abb389a34..37c96ed29 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java index c26823413..9bbc4a253 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class AuthorizationCodeOAuthFlow extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - AuthorizationCodeOAuthFlow.class.getName()); + "AuthorizationCodeOAuthFlow"); } // Use AuthorizationCodeOAuthFlow.newBuilder() to construct. private AuthorizationCodeOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java index ed82d6dae..b50acbd01 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java index 9127aabb1..9ba0ba831 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,16 +23,17 @@ public final class CancelTaskRequest extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - CancelTaskRequest.class.getName()); + "CancelTaskRequest"); } // Use CancelTaskRequest.newBuilder() to construct. private CancelTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private CancelTaskRequest() { + tenant_ = ""; name_ = ""; } @@ -49,6 +50,53 @@ private CancelTaskRequest() { io.a2a.grpc.CancelTaskRequest.class, io.a2a.grpc.CancelTaskRequest.Builder.class); } + public static final int TENANT_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -115,6 +163,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); + } getUnknownFields().writeTo(output); } @@ -127,6 +178,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -142,6 +196,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.CancelTaskRequest other = (io.a2a.grpc.CancelTaskRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getName() .equals(other.getName())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -155,6 +211,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); @@ -293,6 +351,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; name_ = ""; return this; } @@ -328,6 +387,9 @@ public io.a2a.grpc.CancelTaskRequest buildPartial() { private void buildPartial0(io.a2a.grpc.CancelTaskRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; } } @@ -344,9 +406,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.CancelTaskRequest other) { if (other == io.a2a.grpc.CancelTaskRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getName().isEmpty()) { name_ = other.name_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -377,9 +444,14 @@ public Builder mergeFrom( break; case 10: { name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 + case 18: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 18 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -397,6 +469,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object name_ = ""; /** *
    @@ -455,7 +619,7 @@ public Builder setName(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -470,7 +634,7 @@ public Builder setName(
          */
         public Builder clearName() {
           name_ = getDefaultInstance().getName();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -489,7 +653,7 @@ public Builder setNameBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java
    index 5c571df44..b8a5e4f11 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface CancelTaskRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.CancelTaskRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The resource name of the task to cancel.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java
    index 6fbf601c9..1af7c907c 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlow.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -23,10 +23,10 @@ public final class ClientCredentialsOAuthFlow extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      ClientCredentialsOAuthFlow.class.getName());
    +      "ClientCredentialsOAuthFlow");
       }
       // Use ClientCredentialsOAuthFlow.newBuilder() to construct.
       private ClientCredentialsOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java
    index b41bc82fe..4178ed50d 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ClientCredentialsOAuthFlowOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java
    index 408fe6c89..92d0a6e6e 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java
    @@ -1,14 +1,14 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
     /**
      * 
      * --8<-- [start:DataPart]
    - * DataPart represents a structured blob. This is most commonly a JSON payload.
    + * DataPart represents a structured blob.
      * 
    * * Protobuf type {@code a2a.v1.DataPart} @@ -23,10 +23,10 @@ public final class DataPart extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - DataPart.class.getName()); + "DataPart"); } // Use DataPart.newBuilder() to construct. private DataPart(com.google.protobuf.GeneratedMessage.Builder builder) { @@ -53,7 +53,7 @@ private DataPart() { private com.google.protobuf.Struct data_; /** *
    -   * The structured data content.
    +   * A JSON object containing arbitrary data.
        * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -65,7 +65,7 @@ public boolean hasData() { } /** *
    -   * The structured data content.
    +   * A JSON object containing arbitrary data.
        * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -77,7 +77,7 @@ public com.google.protobuf.Struct getData() { } /** *
    -   * The structured data content.
    +   * A JSON object containing arbitrary data.
        * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -252,7 +252,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:DataPart]
    -   * DataPart represents a structured blob. This is most commonly a JSON payload.
    +   * DataPart represents a structured blob.
        * 
    * * Protobuf type {@code a2a.v1.DataPart} @@ -412,7 +412,7 @@ public Builder mergeFrom( com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> dataBuilder_; /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -423,7 +423,7 @@ public boolean hasData() { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -438,7 +438,7 @@ public com.google.protobuf.Struct getData() { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -458,7 +458,7 @@ public Builder setData(com.google.protobuf.Struct value) { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -476,7 +476,7 @@ public Builder setData( } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -501,7 +501,7 @@ public Builder mergeData(com.google.protobuf.Struct value) { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -518,7 +518,7 @@ public Builder clearData() { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -530,7 +530,7 @@ public com.google.protobuf.Struct.Builder getDataBuilder() { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -545,7 +545,7 @@ public com.google.protobuf.StructOrBuilder getDataOrBuilder() { } /** *
    -     * The structured data content.
    +     * A JSON object containing arbitrary data.
          * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java index 9dad437b0..4b61fc851 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -12,7 +12,7 @@ public interface DataPartOrBuilder extends /** *
    -   * The structured data content.
    +   * A JSON object containing arbitrary data.
        * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -21,7 +21,7 @@ public interface DataPartOrBuilder extends boolean hasData(); /** *
    -   * The structured data content.
    +   * A JSON object containing arbitrary data.
        * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; @@ -30,7 +30,7 @@ public interface DataPartOrBuilder extends com.google.protobuf.Struct getData(); /** *
    -   * The structured data content.
    +   * A JSON object containing arbitrary data.
        * 
    * * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java index 3ecd864e0..8bfc24676 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,16 +23,17 @@ public final class DeleteTaskPushNotificationConfigRequest extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - DeleteTaskPushNotificationConfigRequest.class.getName()); + "DeleteTaskPushNotificationConfigRequest"); } // Use DeleteTaskPushNotificationConfigRequest.newBuilder() to construct. private DeleteTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private DeleteTaskPushNotificationConfigRequest() { + tenant_ = ""; name_ = ""; } @@ -49,6 +50,53 @@ private DeleteTaskPushNotificationConfigRequest() { io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.class, io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder.class); } + public static final int TENANT_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -115,6 +163,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); + } getUnknownFields().writeTo(output); } @@ -127,6 +178,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -142,6 +196,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.DeleteTaskPushNotificationConfigRequest other = (io.a2a.grpc.DeleteTaskPushNotificationConfigRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getName() .equals(other.getName())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -155,6 +211,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); @@ -293,6 +351,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; name_ = ""; return this; } @@ -328,6 +387,9 @@ public io.a2a.grpc.DeleteTaskPushNotificationConfigRequest buildPartial() { private void buildPartial0(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; } } @@ -344,9 +406,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest other) { if (other == io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getName().isEmpty()) { name_ = other.name_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -377,9 +444,14 @@ public Builder mergeFrom( break; case 10: { name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 + case 18: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 18 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -397,6 +469,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object name_ = ""; /** *
    @@ -455,7 +619,7 @@ public Builder setName(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -470,7 +634,7 @@ public Builder setName(
          */
         public Builder clearName() {
           name_ = getDefaultInstance().getName();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -489,7 +653,7 @@ public Builder setNameBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java
    index 33cd1d13d..336d7eba7 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.DeleteTaskPushNotificationConfigRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The resource name of the config to delete.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java
    index 88f8aa535..8d2526d62 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -26,10 +26,10 @@ public final class FilePart extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      FilePart.class.getName());
    +      "FilePart");
       }
       // Use FilePart.newBuilder() to construct.
       private FilePart(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java
    index cb11ce4be..2fbad2bf2 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java
    index 1a732e9b5..5c1b07907 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequest.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -22,16 +22,17 @@ public final class GetExtendedAgentCardRequest extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      GetExtendedAgentCardRequest.class.getName());
    +      "GetExtendedAgentCardRequest");
       }
       // Use GetExtendedAgentCardRequest.newBuilder() to construct.
       private GetExtendedAgentCardRequest(com.google.protobuf.GeneratedMessage.Builder builder) {
         super(builder);
       }
       private GetExtendedAgentCardRequest() {
    +    tenant_ = "";
       }
     
       public static final com.google.protobuf.Descriptors.Descriptor
    @@ -47,6 +48,53 @@ private GetExtendedAgentCardRequest() {
                 io.a2a.grpc.GetExtendedAgentCardRequest.class, io.a2a.grpc.GetExtendedAgentCardRequest.Builder.class);
       }
     
    +  public static final int TENANT_FIELD_NUMBER = 1;
    +  @SuppressWarnings("serial")
    +  private volatile java.lang.Object tenant_ = "";
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 1; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 1; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -61,6 +109,9 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, tenant_); + } getUnknownFields().writeTo(output); } @@ -70,6 +121,9 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -85,6 +139,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.GetExtendedAgentCardRequest other = (io.a2a.grpc.GetExtendedAgentCardRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -96,6 +152,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -230,6 +288,8 @@ private Builder( @java.lang.Override public Builder clear() { super.clear(); + bitField0_ = 0; + tenant_ = ""; return this; } @@ -256,10 +316,18 @@ public io.a2a.grpc.GetExtendedAgentCardRequest build() { @java.lang.Override public io.a2a.grpc.GetExtendedAgentCardRequest buildPartial() { io.a2a.grpc.GetExtendedAgentCardRequest result = new io.a2a.grpc.GetExtendedAgentCardRequest(this); + if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } + private void buildPartial0(io.a2a.grpc.GetExtendedAgentCardRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + } + @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { if (other instanceof io.a2a.grpc.GetExtendedAgentCardRequest) { @@ -272,6 +340,11 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.GetExtendedAgentCardRequest other) { if (other == io.a2a.grpc.GetExtendedAgentCardRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -298,6 +371,11 @@ public Builder mergeFrom( case 0: done = true; break; + case 10: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -313,6 +391,99 @@ public Builder mergeFrom( } // finally return this; } + private int bitField0_; + + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 1; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 1; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 1; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 1; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 1; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } // @@protoc_insertion_point(builder_scope:a2a.v1.GetExtendedAgentCardRequest) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java index 63ce0f7c0..f6efaa410 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetExtendedAgentCardRequestOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -9,4 +9,24 @@ public interface GetExtendedAgentCardRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:a2a.v1.GetExtendedAgentCardRequest) com.google.protobuf.MessageOrBuilder { + + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 1; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 1; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java index 342eaade1..5d5819669 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -22,16 +22,17 @@ public final class GetTaskPushNotificationConfigRequest extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - GetTaskPushNotificationConfigRequest.class.getName()); + "GetTaskPushNotificationConfigRequest"); } // Use GetTaskPushNotificationConfigRequest.newBuilder() to construct. private GetTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private GetTaskPushNotificationConfigRequest() { + tenant_ = ""; name_ = ""; } @@ -48,6 +49,53 @@ private GetTaskPushNotificationConfigRequest() { io.a2a.grpc.GetTaskPushNotificationConfigRequest.class, io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder.class); } + public static final int TENANT_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -114,6 +162,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); + } getUnknownFields().writeTo(output); } @@ -126,6 +177,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -141,6 +195,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.GetTaskPushNotificationConfigRequest other = (io.a2a.grpc.GetTaskPushNotificationConfigRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getName() .equals(other.getName())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -154,6 +210,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); @@ -291,6 +349,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; name_ = ""; return this; } @@ -326,6 +385,9 @@ public io.a2a.grpc.GetTaskPushNotificationConfigRequest buildPartial() { private void buildPartial0(io.a2a.grpc.GetTaskPushNotificationConfigRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; } } @@ -342,9 +404,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.GetTaskPushNotificationConfigRequest other) { if (other == io.a2a.grpc.GetTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getName().isEmpty()) { name_ = other.name_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -375,9 +442,14 @@ public Builder mergeFrom( break; case 10: { name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 + case 18: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 18 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -395,6 +467,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object name_ = ""; /** *
    @@ -453,7 +617,7 @@ public Builder setName(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -468,7 +632,7 @@ public Builder setName(
          */
         public Builder clearName() {
           name_ = getDefaultInstance().getName();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -487,7 +651,7 @@ public Builder setNameBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java
    index b11dbc7d6..8224a30a2 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface GetTaskPushNotificationConfigRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.GetTaskPushNotificationConfigRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The resource name of the config to retrieve.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java
    index d4d7f274c..2322a9f8b 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -23,16 +23,17 @@ public final class GetTaskRequest extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      GetTaskRequest.class.getName());
    +      "GetTaskRequest");
       }
       // Use GetTaskRequest.newBuilder() to construct.
       private GetTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) {
         super(builder);
       }
       private GetTaskRequest() {
    +    tenant_ = "";
         name_ = "";
       }
     
    @@ -50,6 +51,53 @@ private GetTaskRequest() {
       }
     
       private int bitField0_;
    +  public static final int TENANT_FIELD_NUMBER = 3;
    +  @SuppressWarnings("serial")
    +  private volatile java.lang.Object tenant_ = "";
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 3; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 3; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -146,6 +194,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) != 0)) { output.writeInt32(2, historyLength_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, tenant_); + } getUnknownFields().writeTo(output); } @@ -162,6 +213,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeInt32Size(2, historyLength_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -177,6 +231,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.GetTaskRequest other = (io.a2a.grpc.GetTaskRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getName() .equals(other.getName())) return false; if (hasHistoryLength() != other.hasHistoryLength()) return false; @@ -195,6 +251,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); if (hasHistoryLength()) { @@ -337,6 +395,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; name_ = ""; historyLength_ = 0; return this; @@ -373,10 +432,13 @@ public io.a2a.grpc.GetTaskRequest buildPartial() { private void buildPartial0(io.a2a.grpc.GetTaskRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; } int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000002) != 0)) { + if (((from_bitField0_ & 0x00000004) != 0)) { result.historyLength_ = historyLength_; to_bitField0_ |= 0x00000001; } @@ -395,9 +457,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.GetTaskRequest other) { if (other == io.a2a.grpc.GetTaskRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getName().isEmpty()) { name_ = other.name_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } if (other.hasHistoryLength()) { @@ -431,14 +498,19 @@ public Builder mergeFrom( break; case 10: { name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 case 16: { historyLength_ = input.readInt32(); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; break; } // case 16 + case 26: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -456,6 +528,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 3; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 3; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 3; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 3; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 3; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object name_ = ""; /** *
    @@ -514,7 +678,7 @@ public Builder setName(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -529,7 +693,7 @@ public Builder setName(
          */
         public Builder clearName() {
           name_ = getDefaultInstance().getName();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -548,7 +712,7 @@ public Builder setNameBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -564,7 +728,7 @@ public Builder setNameBytes(
          */
         @java.lang.Override
         public boolean hasHistoryLength() {
    -      return ((bitField0_ & 0x00000002) != 0);
    +      return ((bitField0_ & 0x00000004) != 0);
         }
         /**
          * 
    @@ -590,7 +754,7 @@ public int getHistoryLength() {
         public Builder setHistoryLength(int value) {
     
           historyLength_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -603,7 +767,7 @@ public Builder setHistoryLength(int value) {
          * @return This builder for chaining.
          */
         public Builder clearHistoryLength() {
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000004);
           historyLength_ = 0;
           onChanged();
           return this;
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java
    index 69a84a3a6..a17389884 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface GetTaskRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.GetTaskRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 3; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 3; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The resource name of the task.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java
    index df372321d..cad53d0b9 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecurityScheme.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -23,10 +23,10 @@ public final class HTTPAuthSecurityScheme extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      HTTPAuthSecurityScheme.class.getName());
    +      "HTTPAuthSecurityScheme");
       }
       // Use HTTPAuthSecurityScheme.newBuilder() to construct.
       private HTTPAuthSecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java
    index 861964e66..9a8e8847b 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/HTTPAuthSecuritySchemeOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java
    index f4851f6a0..a7f186016 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -23,10 +23,10 @@ public final class ImplicitOAuthFlow extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      ImplicitOAuthFlow.class.getName());
    +      "ImplicitOAuthFlow");
       }
       // Use ImplicitOAuthFlow.newBuilder() to construct.
       private ImplicitOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java
    index 791be411b..e59040641 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java
    index f5a176f55..cbcdcfd18 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -22,16 +22,17 @@ public final class ListTaskPushNotificationConfigRequest extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      ListTaskPushNotificationConfigRequest.class.getName());
    +      "ListTaskPushNotificationConfigRequest");
       }
       // Use ListTaskPushNotificationConfigRequest.newBuilder() to construct.
       private ListTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) {
         super(builder);
       }
       private ListTaskPushNotificationConfigRequest() {
    +    tenant_ = "";
         parent_ = "";
         pageToken_ = "";
       }
    @@ -49,6 +50,53 @@ private ListTaskPushNotificationConfigRequest() {
                 io.a2a.grpc.ListTaskPushNotificationConfigRequest.class, io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder.class);
       }
     
    +  public static final int TENANT_FIELD_NUMBER = 4;
    +  @SuppressWarnings("serial")
    +  private volatile java.lang.Object tenant_ = "";
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int PARENT_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object parent_ = ""; @@ -183,6 +231,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { com.google.protobuf.GeneratedMessage.writeString(output, 3, pageToken_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, tenant_); + } getUnknownFields().writeTo(output); } @@ -202,6 +253,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(pageToken_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(3, pageToken_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -217,6 +271,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.ListTaskPushNotificationConfigRequest other = (io.a2a.grpc.ListTaskPushNotificationConfigRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getParent() .equals(other.getParent())) return false; if (getPageSize() @@ -234,6 +290,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + PARENT_FIELD_NUMBER; hash = (53 * hash) + getParent().hashCode(); hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; @@ -375,6 +433,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; parent_ = ""; pageSize_ = 0; pageToken_ = ""; @@ -412,12 +471,15 @@ public io.a2a.grpc.ListTaskPushNotificationConfigRequest buildPartial() { private void buildPartial0(io.a2a.grpc.ListTaskPushNotificationConfigRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - result.parent_ = parent_; + result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.pageSize_ = pageSize_; + result.parent_ = parent_; } if (((from_bitField0_ & 0x00000004) != 0)) { + result.pageSize_ = pageSize_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { result.pageToken_ = pageToken_; } } @@ -434,9 +496,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.ListTaskPushNotificationConfigRequest other) { if (other == io.a2a.grpc.ListTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getParent().isEmpty()) { parent_ = other.parent_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } if (other.getPageSize() != 0) { @@ -444,7 +511,7 @@ public Builder mergeFrom(io.a2a.grpc.ListTaskPushNotificationConfigRequest other } if (!other.getPageToken().isEmpty()) { pageToken_ = other.pageToken_; - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -475,19 +542,24 @@ public Builder mergeFrom( break; case 10: { parent_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 case 16: { pageSize_ = input.readInt32(); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; break; } // case 16 case 26: { pageToken_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; break; } // case 26 + case 34: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 34 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -505,6 +577,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object parent_ = ""; /** *
    @@ -563,7 +727,7 @@ public Builder setParent(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           parent_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -578,7 +742,7 @@ public Builder setParent(
          */
         public Builder clearParent() {
           parent_ = getDefaultInstance().getParent();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -597,7 +761,7 @@ public Builder setParentBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           parent_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -627,7 +791,7 @@ public int getPageSize() {
         public Builder setPageSize(int value) {
     
           pageSize_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -640,7 +804,7 @@ public Builder setPageSize(int value) {
          * @return This builder for chaining.
          */
         public Builder clearPageSize() {
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000004);
           pageSize_ = 0;
           onChanged();
           return this;
    @@ -701,7 +865,7 @@ public Builder setPageToken(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           pageToken_ = value;
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -715,7 +879,7 @@ public Builder setPageToken(
          */
         public Builder clearPageToken() {
           pageToken_ = getDefaultInstance().getPageToken();
    -      bitField0_ = (bitField0_ & ~0x00000004);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           onChanged();
           return this;
         }
    @@ -733,7 +897,7 @@ public Builder setPageTokenBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           pageToken_ = value;
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java
    index 06b26342c..690539ac2 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.ListTaskPushNotificationConfigRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The parent task resource.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java
    index fc66a610f..452ea2312 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -24,10 +24,10 @@ public final class ListTaskPushNotificationConfigResponse extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      ListTaskPushNotificationConfigResponse.class.getName());
    +      "ListTaskPushNotificationConfigResponse");
       }
       // Use ListTaskPushNotificationConfigResponse.newBuilder() to construct.
       private ListTaskPushNotificationConfigResponse(com.google.protobuf.GeneratedMessage.Builder builder) {
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java
    index 2e03d4a6a..a68796c49 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponseOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java
    index 5ececf586..93d5a8bc9 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -23,16 +23,17 @@ public final class ListTasksRequest extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      ListTasksRequest.class.getName());
    +      "ListTasksRequest");
       }
       // Use ListTasksRequest.newBuilder() to construct.
       private ListTasksRequest(com.google.protobuf.GeneratedMessage.Builder builder) {
         super(builder);
       }
       private ListTasksRequest() {
    +    tenant_ = "";
         contextId_ = "";
         status_ = 0;
         pageToken_ = "";
    @@ -52,6 +53,53 @@ private ListTasksRequest() {
       }
     
       private int bitField0_;
    +  public static final int TENANT_FIELD_NUMBER = 9;
    +  @SuppressWarnings("serial")
    +  private volatile java.lang.Object tenant_ = "";
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 9; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 9; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int CONTEXT_ID_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object contextId_ = ""; @@ -273,44 +321,6 @@ public boolean getIncludeArtifacts() { return includeArtifacts_; } - public static final int METADATA_FIELD_NUMBER = 8; - private com.google.protobuf.Struct metadata_; - /** - *
    -   * Request-specific metadata.
    -   * 
    - * - * .google.protobuf.Struct metadata = 8; - * @return Whether the metadata field is set. - */ - @java.lang.Override - public boolean hasMetadata() { - return ((bitField0_ & 0x00000008) != 0); - } - /** - *
    -   * Request-specific metadata.
    -   * 
    - * - * .google.protobuf.Struct metadata = 8; - * @return The metadata. - */ - @java.lang.Override - public com.google.protobuf.Struct getMetadata() { - return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; - } - /** - *
    -   * Request-specific metadata.
    -   * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - @java.lang.Override - public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { - return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; - } - private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -346,8 +356,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000004) != 0)) { output.writeBool(7, includeArtifacts_); } - if (((bitField0_ & 0x00000008) != 0)) { - output.writeMessage(8, getMetadata()); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 9, tenant_); } getUnknownFields().writeTo(output); } @@ -384,9 +394,8 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeBoolSize(7, includeArtifacts_); } - if (((bitField0_ & 0x00000008) != 0)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, getMetadata()); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(9, tenant_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -403,6 +412,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.ListTasksRequest other = (io.a2a.grpc.ListTasksRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getContextId() .equals(other.getContextId())) return false; if (status_ != other.status_) return false; @@ -425,11 +436,6 @@ public boolean equals(final java.lang.Object obj) { if (getIncludeArtifacts() != other.getIncludeArtifacts()) return false; } - if (hasMetadata() != other.hasMetadata()) return false; - if (hasMetadata()) { - if (!getMetadata() - .equals(other.getMetadata())) return false; - } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -441,6 +447,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + CONTEXT_ID_FIELD_NUMBER; hash = (53 * hash) + getContextId().hashCode(); hash = (37 * hash) + STATUS_FIELD_NUMBER; @@ -463,10 +471,6 @@ public int hashCode() { hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( getIncludeArtifacts()); } - if (hasMetadata()) { - hash = (37 * hash) + METADATA_FIELD_NUMBER; - hash = (53 * hash) + getMetadata().hashCode(); - } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -591,24 +595,19 @@ public static final class Builder extends // Construct using io.a2a.grpc.ListTasksRequest.newBuilder() private Builder() { - maybeForceBuilderInitialization(); + } private Builder( com.google.protobuf.GeneratedMessage.BuilderParent parent) { super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage - .alwaysUseFieldBuilders) { - internalGetMetadataFieldBuilder(); - } + } @java.lang.Override public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; contextId_ = ""; status_ = 0; pageSize_ = 0; @@ -616,11 +615,6 @@ public Builder clear() { historyLength_ = 0; lastUpdatedAfter_ = 0L; includeArtifacts_ = false; - metadata_ = null; - if (metadataBuilder_ != null) { - metadataBuilder_.dispose(); - metadataBuilder_ = null; - } return this; } @@ -655,36 +649,33 @@ public io.a2a.grpc.ListTasksRequest buildPartial() { private void buildPartial0(io.a2a.grpc.ListTasksRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - result.contextId_ = contextId_; + result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { + result.contextId_ = contextId_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { result.status_ = status_; } int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000004) != 0)) { + if (((from_bitField0_ & 0x00000008) != 0)) { result.pageSize_ = pageSize_; to_bitField0_ |= 0x00000001; } - if (((from_bitField0_ & 0x00000008) != 0)) { + if (((from_bitField0_ & 0x00000010) != 0)) { result.pageToken_ = pageToken_; } - if (((from_bitField0_ & 0x00000010) != 0)) { + if (((from_bitField0_ & 0x00000020) != 0)) { result.historyLength_ = historyLength_; to_bitField0_ |= 0x00000002; } - if (((from_bitField0_ & 0x00000020) != 0)) { + if (((from_bitField0_ & 0x00000040) != 0)) { result.lastUpdatedAfter_ = lastUpdatedAfter_; } - if (((from_bitField0_ & 0x00000040) != 0)) { + if (((from_bitField0_ & 0x00000080) != 0)) { result.includeArtifacts_ = includeArtifacts_; to_bitField0_ |= 0x00000004; } - if (((from_bitField0_ & 0x00000080) != 0)) { - result.metadata_ = metadataBuilder_ == null - ? metadata_ - : metadataBuilder_.build(); - to_bitField0_ |= 0x00000008; - } result.bitField0_ |= to_bitField0_; } @@ -700,9 +691,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) { if (other == io.a2a.grpc.ListTasksRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getContextId().isEmpty()) { contextId_ = other.contextId_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } if (other.status_ != 0) { @@ -713,7 +709,7 @@ public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) { } if (!other.getPageToken().isEmpty()) { pageToken_ = other.pageToken_; - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); } if (other.hasHistoryLength()) { @@ -725,9 +721,6 @@ public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) { if (other.hasIncludeArtifacts()) { setIncludeArtifacts(other.getIncludeArtifacts()); } - if (other.hasMetadata()) { - mergeMetadata(other.getMetadata()); - } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -756,46 +749,44 @@ public Builder mergeFrom( break; case 10: { contextId_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 case 16: { status_ = input.readEnum(); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; break; } // case 16 case 24: { pageSize_ = input.readInt32(); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; break; } // case 24 case 34: { pageToken_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; break; } // case 34 case 40: { historyLength_ = input.readInt32(); - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000020; break; } // case 40 case 48: { lastUpdatedAfter_ = input.readInt64(); - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000040; break; } // case 48 case 56: { includeArtifacts_ = input.readBool(); - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000080; break; } // case 56 - case 66: { - input.readMessage( - internalGetMetadataFieldBuilder().getBuilder(), - extensionRegistry); - bitField0_ |= 0x00000080; + case 74: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; break; - } // case 66 + } // case 74 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -813,6 +804,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 9; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 9; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 9; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 9; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 9; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object contextId_ = ""; /** *
    @@ -868,7 +951,7 @@ public Builder setContextId(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           contextId_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -882,7 +965,7 @@ public Builder setContextId(
          */
         public Builder clearContextId() {
           contextId_ = getDefaultInstance().getContextId();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -900,7 +983,7 @@ public Builder setContextIdBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           contextId_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -928,7 +1011,7 @@ public Builder setContextIdBytes(
          */
         public Builder setStatusValue(int value) {
           status_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -956,7 +1039,7 @@ public io.a2a.grpc.TaskState getStatus() {
          */
         public Builder setStatus(io.a2a.grpc.TaskState value) {
           if (value == null) { throw new NullPointerException(); }
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           status_ = value.getNumber();
           onChanged();
           return this;
    @@ -970,7 +1053,7 @@ public Builder setStatus(io.a2a.grpc.TaskState value) {
          * @return This builder for chaining.
          */
         public Builder clearStatus() {
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000004);
           status_ = 0;
           onChanged();
           return this;
    @@ -988,7 +1071,7 @@ public Builder clearStatus() {
          */
         @java.lang.Override
         public boolean hasPageSize() {
    -      return ((bitField0_ & 0x00000004) != 0);
    +      return ((bitField0_ & 0x00000008) != 0);
         }
         /**
          * 
    @@ -1016,7 +1099,7 @@ public int getPageSize() {
         public Builder setPageSize(int value) {
     
           pageSize_ = value;
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -1030,7 +1113,7 @@ public Builder setPageSize(int value) {
          * @return This builder for chaining.
          */
         public Builder clearPageSize() {
    -      bitField0_ = (bitField0_ & ~0x00000004);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           pageSize_ = 0;
           onChanged();
           return this;
    @@ -1091,7 +1174,7 @@ public Builder setPageToken(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           pageToken_ = value;
    -      bitField0_ |= 0x00000008;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return this;
         }
    @@ -1105,7 +1188,7 @@ public Builder setPageToken(
          */
         public Builder clearPageToken() {
           pageToken_ = getDefaultInstance().getPageToken();
    -      bitField0_ = (bitField0_ & ~0x00000008);
    +      bitField0_ = (bitField0_ & ~0x00000010);
           onChanged();
           return this;
         }
    @@ -1123,7 +1206,7 @@ public Builder setPageTokenBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           pageToken_ = value;
    -      bitField0_ |= 0x00000008;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return this;
         }
    @@ -1139,7 +1222,7 @@ public Builder setPageTokenBytes(
          */
         @java.lang.Override
         public boolean hasHistoryLength() {
    -      return ((bitField0_ & 0x00000010) != 0);
    +      return ((bitField0_ & 0x00000020) != 0);
         }
         /**
          * 
    @@ -1165,7 +1248,7 @@ public int getHistoryLength() {
         public Builder setHistoryLength(int value) {
     
           historyLength_ = value;
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000020;
           onChanged();
           return this;
         }
    @@ -1178,7 +1261,7 @@ public Builder setHistoryLength(int value) {
          * @return This builder for chaining.
          */
         public Builder clearHistoryLength() {
    -      bitField0_ = (bitField0_ & ~0x00000010);
    +      bitField0_ = (bitField0_ & ~0x00000020);
           historyLength_ = 0;
           onChanged();
           return this;
    @@ -1211,7 +1294,7 @@ public long getLastUpdatedAfter() {
         public Builder setLastUpdatedAfter(long value) {
     
           lastUpdatedAfter_ = value;
    -      bitField0_ |= 0x00000020;
    +      bitField0_ |= 0x00000040;
           onChanged();
           return this;
         }
    @@ -1225,7 +1308,7 @@ public Builder setLastUpdatedAfter(long value) {
          * @return This builder for chaining.
          */
         public Builder clearLastUpdatedAfter() {
    -      bitField0_ = (bitField0_ & ~0x00000020);
    +      bitField0_ = (bitField0_ & ~0x00000040);
           lastUpdatedAfter_ = 0L;
           onChanged();
           return this;
    @@ -1243,7 +1326,7 @@ public Builder clearLastUpdatedAfter() {
          */
         @java.lang.Override
         public boolean hasIncludeArtifacts() {
    -      return ((bitField0_ & 0x00000040) != 0);
    +      return ((bitField0_ & 0x00000080) != 0);
         }
         /**
          * 
    @@ -1271,7 +1354,7 @@ public boolean getIncludeArtifacts() {
         public Builder setIncludeArtifacts(boolean value) {
     
           includeArtifacts_ = value;
    -      bitField0_ |= 0x00000040;
    +      bitField0_ |= 0x00000080;
           onChanged();
           return this;
         }
    @@ -1285,168 +1368,11 @@ public Builder setIncludeArtifacts(boolean value) {
          * @return This builder for chaining.
          */
         public Builder clearIncludeArtifacts() {
    -      bitField0_ = (bitField0_ & ~0x00000040);
    -      includeArtifacts_ = false;
    -      onChanged();
    -      return this;
    -    }
    -
    -    private com.google.protobuf.Struct metadata_;
    -    private com.google.protobuf.SingleFieldBuilder<
    -        com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_;
    -    /**
    -     * 
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - * @return Whether the metadata field is set. - */ - public boolean hasMetadata() { - return ((bitField0_ & 0x00000080) != 0); - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - * @return The metadata. - */ - public com.google.protobuf.Struct getMetadata() { - if (metadataBuilder_ == null) { - return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; - } else { - return metadataBuilder_.getMessage(); - } - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - public Builder setMetadata(com.google.protobuf.Struct value) { - if (metadataBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - metadata_ = value; - } else { - metadataBuilder_.setMessage(value); - } - bitField0_ |= 0x00000080; - onChanged(); - return this; - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - public Builder setMetadata( - com.google.protobuf.Struct.Builder builderForValue) { - if (metadataBuilder_ == null) { - metadata_ = builderForValue.build(); - } else { - metadataBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000080; - onChanged(); - return this; - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - public Builder mergeMetadata(com.google.protobuf.Struct value) { - if (metadataBuilder_ == null) { - if (((bitField0_ & 0x00000080) != 0) && - metadata_ != null && - metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { - getMetadataBuilder().mergeFrom(value); - } else { - metadata_ = value; - } - } else { - metadataBuilder_.mergeFrom(value); - } - if (metadata_ != null) { - bitField0_ |= 0x00000080; - onChanged(); - } - return this; - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - public Builder clearMetadata() { bitField0_ = (bitField0_ & ~0x00000080); - metadata_ = null; - if (metadataBuilder_ != null) { - metadataBuilder_.dispose(); - metadataBuilder_ = null; - } + includeArtifacts_ = false; onChanged(); return this; } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - public com.google.protobuf.Struct.Builder getMetadataBuilder() { - bitField0_ |= 0x00000080; - onChanged(); - return internalGetMetadataFieldBuilder().getBuilder(); - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { - if (metadataBuilder_ != null) { - return metadataBuilder_.getMessageOrBuilder(); - } else { - return metadata_ == null ? - com.google.protobuf.Struct.getDefaultInstance() : metadata_; - } - } - /** - *
    -     * Request-specific metadata.
    -     * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - private com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> - internalGetMetadataFieldBuilder() { - if (metadataBuilder_ == null) { - metadataBuilder_ = new com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( - getMetadata(), - getParentForChildren(), - isClean()); - metadata_ = null; - } - return metadataBuilder_; - } // @@protoc_insertion_point(builder_scope:a2a.v1.ListTasksRequest) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java index 28d9fa5bf..6b832c0d1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -10,6 +10,26 @@ public interface ListTasksRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:a2a.v1.ListTasksRequest) com.google.protobuf.MessageOrBuilder { + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 9; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 9; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * Filter tasks by context ID to get tasks from a specific conversation or session.
    @@ -140,31 +160,4 @@ public interface ListTasksRequestOrBuilder extends
        * @return The includeArtifacts.
        */
       boolean getIncludeArtifacts();
    -
    -  /**
    -   * 
    -   * Request-specific metadata.
    -   * 
    - * - * .google.protobuf.Struct metadata = 8; - * @return Whether the metadata field is set. - */ - boolean hasMetadata(); - /** - *
    -   * Request-specific metadata.
    -   * 
    - * - * .google.protobuf.Struct metadata = 8; - * @return The metadata. - */ - com.google.protobuf.Struct getMetadata(); - /** - *
    -   * Request-specific metadata.
    -   * 
    - * - * .google.protobuf.Struct metadata = 8; - */ - com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java index 3703e3aeb..1c0078076 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponse.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class ListTasksResponse extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - ListTasksResponse.class.getName()); + "ListTasksResponse"); } // Use ListTasksResponse.newBuilder() to construct. private ListTasksResponse(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java index 11c3173be..430700f61 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksResponseOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Message.java b/spec-grpc/src/main/java/io/a2a/grpc/Message.java index 66c42d2bf..8ff6a60f6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Message.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Message.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -29,10 +29,10 @@ public final class Message extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - Message.class.getName()); + "Message"); } // Use Message.newBuilder() to construct. private Message(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java index 546c633ae..cfd7ea3ef 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MessageOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java index 859fe5c48..b85fce3e4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecurityScheme.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class MutualTlsSecurityScheme extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - MutualTlsSecurityScheme.class.getName()); + "MutualTlsSecurityScheme"); } // Use MutualTlsSecurityScheme.newBuilder() to construct. private MutualTlsSecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java index 95f6bb130..c5c606198 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/MutualTlsSecuritySchemeOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java index 10408245b..6b3d725e8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecurityScheme.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class OAuth2SecurityScheme extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - OAuth2SecurityScheme.class.getName()); + "OAuth2SecurityScheme"); } // Use OAuth2SecurityScheme.newBuilder() to construct. private OAuth2SecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java index fb2af79b7..52b49d1c3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuth2SecuritySchemeOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java index d38a64ae6..f2da009e6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class OAuthFlows extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - OAuthFlows.class.getName()); + "OAuthFlows"); } // Use OAuthFlows.newBuilder() to construct. private OAuthFlows(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java index d7a94d7f2..1bdc0f5e0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java index 3e10d7cb0..001ea51e8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecurityScheme.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class OpenIdConnectSecurityScheme extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - OpenIdConnectSecurityScheme.class.getName()); + "OpenIdConnectSecurityScheme"); } // Use OpenIdConnectSecurityScheme.newBuilder() to construct. private OpenIdConnectSecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java index f9d6de482..d51225f03 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OpenIdConnectSecuritySchemeOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Part.java b/spec-grpc/src/main/java/io/a2a/grpc/Part.java index 72244a3ef..511d10dae 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Part.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Part.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -25,10 +25,10 @@ public final class Part extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - Part.class.getName()); + "Part"); } // Use Part.newBuilder() to construct. private Part(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java index 192df9cb0..8c8657625 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java index 74cba7f15..2f21f9789 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class PasswordOAuthFlow extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - PasswordOAuthFlow.class.getName()); + "PasswordOAuthFlow"); } // Use PasswordOAuthFlow.newBuilder() to construct. private PasswordOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java index 280026c9a..1b0aeeac0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java index b38d1fff2..cec0fdf08 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class PushNotificationConfig extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - PushNotificationConfig.class.getName()); + "PushNotificationConfig"); } // Use PushNotificationConfig.newBuilder() to construct. private PushNotificationConfig(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java index 329acafce..bcfaf4c2e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Role.java b/spec-grpc/src/main/java/io/a2a/grpc/Role.java index aa645cb5b..1d8b02c6e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Role.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Role.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -43,10 +43,10 @@ public enum Role com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - Role.class.getName()); + "Role"); } /** * ROLE_UNSPECIFIED = 0; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Security.java b/spec-grpc/src/main/java/io/a2a/grpc/Security.java index 3602a93ad..f04498f98 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Security.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/Security.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -18,10 +18,10 @@ public final class Security extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - Security.class.getName()); + "Security"); } // Use Security.newBuilder() to construct. private Security(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java index 1b3897669..cb1b2372f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java index 90fb5df7c..c1202fccf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityScheme.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -25,10 +25,10 @@ public final class SecurityScheme extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - SecurityScheme.class.getName()); + "SecurityScheme"); } // Use SecurityScheme.newBuilder() to construct. private SecurityScheme(com.google.protobuf.GeneratedMessage.Builder builder) { @@ -100,6 +100,10 @@ public int getNumber() { public static final int API_KEY_SECURITY_SCHEME_FIELD_NUMBER = 1; /** + *
    +   * API key-based authentication.
    +   * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return Whether the apiKeySecurityScheme field is set. */ @@ -108,6 +112,10 @@ public boolean hasApiKeySecurityScheme() { return schemeCase_ == 1; } /** + *
    +   * API key-based authentication.
    +   * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return The apiKeySecurityScheme. */ @@ -119,6 +127,10 @@ public io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme() { return io.a2a.grpc.APIKeySecurityScheme.getDefaultInstance(); } /** + *
    +   * API key-based authentication.
    +   * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ @java.lang.Override @@ -131,6 +143,10 @@ public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilde public static final int HTTP_AUTH_SECURITY_SCHEME_FIELD_NUMBER = 2; /** + *
    +   * HTTP authentication (Basic, Bearer, etc.).
    +   * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return Whether the httpAuthSecurityScheme field is set. */ @@ -139,6 +155,10 @@ public boolean hasHttpAuthSecurityScheme() { return schemeCase_ == 2; } /** + *
    +   * HTTP authentication (Basic, Bearer, etc.).
    +   * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return The httpAuthSecurityScheme. */ @@ -150,6 +170,10 @@ public io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme() { return io.a2a.grpc.HTTPAuthSecurityScheme.getDefaultInstance(); } /** + *
    +   * HTTP authentication (Basic, Bearer, etc.).
    +   * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ @java.lang.Override @@ -162,6 +186,10 @@ public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBu public static final int OAUTH2_SECURITY_SCHEME_FIELD_NUMBER = 3; /** + *
    +   * OAuth 2.0 authentication.
    +   * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return Whether the oauth2SecurityScheme field is set. */ @@ -170,6 +198,10 @@ public boolean hasOauth2SecurityScheme() { return schemeCase_ == 3; } /** + *
    +   * OAuth 2.0 authentication.
    +   * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return The oauth2SecurityScheme. */ @@ -181,6 +213,10 @@ public io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme() { return io.a2a.grpc.OAuth2SecurityScheme.getDefaultInstance(); } /** + *
    +   * OAuth 2.0 authentication.
    +   * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ @java.lang.Override @@ -193,6 +229,10 @@ public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilde public static final int OPEN_ID_CONNECT_SECURITY_SCHEME_FIELD_NUMBER = 4; /** + *
    +   * OpenID Connect authentication.
    +   * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return Whether the openIdConnectSecurityScheme field is set. */ @@ -201,6 +241,10 @@ public boolean hasOpenIdConnectSecurityScheme() { return schemeCase_ == 4; } /** + *
    +   * OpenID Connect authentication.
    +   * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return The openIdConnectSecurityScheme. */ @@ -212,6 +256,10 @@ public io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme() return io.a2a.grpc.OpenIdConnectSecurityScheme.getDefaultInstance(); } /** + *
    +   * OpenID Connect authentication.
    +   * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ @java.lang.Override @@ -224,6 +272,10 @@ public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecurity public static final int MTLS_SECURITY_SCHEME_FIELD_NUMBER = 5; /** + *
    +   * Mutual TLS authentication.
    +   * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; * @return Whether the mtlsSecurityScheme field is set. */ @@ -232,6 +284,10 @@ public boolean hasMtlsSecurityScheme() { return schemeCase_ == 5; } /** + *
    +   * Mutual TLS authentication.
    +   * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; * @return The mtlsSecurityScheme. */ @@ -243,6 +299,10 @@ public io.a2a.grpc.MutualTlsSecurityScheme getMtlsSecurityScheme() { return io.a2a.grpc.MutualTlsSecurityScheme.getDefaultInstance(); } /** + *
    +   * Mutual TLS authentication.
    +   * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ @java.lang.Override @@ -735,6 +795,10 @@ public Builder clearScheme() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.APIKeySecurityScheme, io.a2a.grpc.APIKeySecurityScheme.Builder, io.a2a.grpc.APIKeySecuritySchemeOrBuilder> apiKeySecuritySchemeBuilder_; /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return Whether the apiKeySecurityScheme field is set. */ @@ -743,6 +807,10 @@ public boolean hasApiKeySecurityScheme() { return schemeCase_ == 1; } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return The apiKeySecurityScheme. */ @@ -761,6 +829,10 @@ public io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme() { } } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder setApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { @@ -777,6 +849,10 @@ public Builder setApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { return this; } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder setApiKeySecurityScheme( @@ -791,6 +867,10 @@ public Builder setApiKeySecurityScheme( return this; } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder mergeApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) { @@ -814,6 +894,10 @@ public Builder mergeApiKeySecurityScheme(io.a2a.grpc.APIKeySecurityScheme value) return this; } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public Builder clearApiKeySecurityScheme() { @@ -833,12 +917,20 @@ public Builder clearApiKeySecurityScheme() { return this; } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ public io.a2a.grpc.APIKeySecurityScheme.Builder getApiKeySecuritySchemeBuilder() { return internalGetApiKeySecuritySchemeFieldBuilder().getBuilder(); } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ @java.lang.Override @@ -853,6 +945,10 @@ public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilde } } /** + *
    +     * API key-based authentication.
    +     * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ private com.google.protobuf.SingleFieldBuilder< @@ -877,6 +973,10 @@ public io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilde private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.HTTPAuthSecurityScheme, io.a2a.grpc.HTTPAuthSecurityScheme.Builder, io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder> httpAuthSecuritySchemeBuilder_; /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return Whether the httpAuthSecurityScheme field is set. */ @@ -885,6 +985,10 @@ public boolean hasHttpAuthSecurityScheme() { return schemeCase_ == 2; } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return The httpAuthSecurityScheme. */ @@ -903,6 +1007,10 @@ public io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme() { } } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder setHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme value) { @@ -919,6 +1027,10 @@ public Builder setHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme valu return this; } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder setHttpAuthSecurityScheme( @@ -933,6 +1045,10 @@ public Builder setHttpAuthSecurityScheme( return this; } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder mergeHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme value) { @@ -956,6 +1072,10 @@ public Builder mergeHttpAuthSecurityScheme(io.a2a.grpc.HTTPAuthSecurityScheme va return this; } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public Builder clearHttpAuthSecurityScheme() { @@ -975,12 +1095,20 @@ public Builder clearHttpAuthSecurityScheme() { return this; } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ public io.a2a.grpc.HTTPAuthSecurityScheme.Builder getHttpAuthSecuritySchemeBuilder() { return internalGetHttpAuthSecuritySchemeFieldBuilder().getBuilder(); } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ @java.lang.Override @@ -995,6 +1123,10 @@ public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBu } } /** + *
    +     * HTTP authentication (Basic, Bearer, etc.).
    +     * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ private com.google.protobuf.SingleFieldBuilder< @@ -1019,6 +1151,10 @@ public io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBu private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OAuth2SecurityScheme, io.a2a.grpc.OAuth2SecurityScheme.Builder, io.a2a.grpc.OAuth2SecuritySchemeOrBuilder> oauth2SecuritySchemeBuilder_; /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return Whether the oauth2SecurityScheme field is set. */ @@ -1027,6 +1163,10 @@ public boolean hasOauth2SecurityScheme() { return schemeCase_ == 3; } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return The oauth2SecurityScheme. */ @@ -1045,6 +1185,10 @@ public io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme() { } } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder setOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { @@ -1061,6 +1205,10 @@ public Builder setOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { return this; } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder setOauth2SecurityScheme( @@ -1075,6 +1223,10 @@ public Builder setOauth2SecurityScheme( return this; } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder mergeOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) { @@ -1098,6 +1250,10 @@ public Builder mergeOauth2SecurityScheme(io.a2a.grpc.OAuth2SecurityScheme value) return this; } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public Builder clearOauth2SecurityScheme() { @@ -1117,12 +1273,20 @@ public Builder clearOauth2SecurityScheme() { return this; } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ public io.a2a.grpc.OAuth2SecurityScheme.Builder getOauth2SecuritySchemeBuilder() { return internalGetOauth2SecuritySchemeFieldBuilder().getBuilder(); } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ @java.lang.Override @@ -1137,6 +1301,10 @@ public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilde } } /** + *
    +     * OAuth 2.0 authentication.
    +     * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ private com.google.protobuf.SingleFieldBuilder< @@ -1161,6 +1329,10 @@ public io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilde private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.OpenIdConnectSecurityScheme, io.a2a.grpc.OpenIdConnectSecurityScheme.Builder, io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder> openIdConnectSecuritySchemeBuilder_; /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return Whether the openIdConnectSecurityScheme field is set. */ @@ -1169,6 +1341,10 @@ public boolean hasOpenIdConnectSecurityScheme() { return schemeCase_ == 4; } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return The openIdConnectSecurityScheme. */ @@ -1187,6 +1363,10 @@ public io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme() } } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder setOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityScheme value) { @@ -1203,6 +1383,10 @@ public Builder setOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityS return this; } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder setOpenIdConnectSecurityScheme( @@ -1217,6 +1401,10 @@ public Builder setOpenIdConnectSecurityScheme( return this; } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder mergeOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurityScheme value) { @@ -1240,6 +1428,10 @@ public Builder mergeOpenIdConnectSecurityScheme(io.a2a.grpc.OpenIdConnectSecurit return this; } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public Builder clearOpenIdConnectSecurityScheme() { @@ -1259,12 +1451,20 @@ public Builder clearOpenIdConnectSecurityScheme() { return this; } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ public io.a2a.grpc.OpenIdConnectSecurityScheme.Builder getOpenIdConnectSecuritySchemeBuilder() { return internalGetOpenIdConnectSecuritySchemeFieldBuilder().getBuilder(); } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ @java.lang.Override @@ -1279,6 +1479,10 @@ public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecurity } } /** + *
    +     * OpenID Connect authentication.
    +     * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ private com.google.protobuf.SingleFieldBuilder< @@ -1303,6 +1507,10 @@ public io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecurity private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.MutualTlsSecurityScheme, io.a2a.grpc.MutualTlsSecurityScheme.Builder, io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder> mtlsSecuritySchemeBuilder_; /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; * @return Whether the mtlsSecurityScheme field is set. */ @@ -1311,6 +1519,10 @@ public boolean hasMtlsSecurityScheme() { return schemeCase_ == 5; } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; * @return The mtlsSecurityScheme. */ @@ -1329,6 +1541,10 @@ public io.a2a.grpc.MutualTlsSecurityScheme getMtlsSecurityScheme() { } } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ public Builder setMtlsSecurityScheme(io.a2a.grpc.MutualTlsSecurityScheme value) { @@ -1345,6 +1561,10 @@ public Builder setMtlsSecurityScheme(io.a2a.grpc.MutualTlsSecurityScheme value) return this; } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ public Builder setMtlsSecurityScheme( @@ -1359,6 +1579,10 @@ public Builder setMtlsSecurityScheme( return this; } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ public Builder mergeMtlsSecurityScheme(io.a2a.grpc.MutualTlsSecurityScheme value) { @@ -1382,6 +1606,10 @@ public Builder mergeMtlsSecurityScheme(io.a2a.grpc.MutualTlsSecurityScheme value return this; } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ public Builder clearMtlsSecurityScheme() { @@ -1401,12 +1629,20 @@ public Builder clearMtlsSecurityScheme() { return this; } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ public io.a2a.grpc.MutualTlsSecurityScheme.Builder getMtlsSecuritySchemeBuilder() { return internalGetMtlsSecuritySchemeFieldBuilder().getBuilder(); } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ @java.lang.Override @@ -1421,6 +1657,10 @@ public io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder getMtlsSecuritySchemeOrBuild } } /** + *
    +     * Mutual TLS authentication.
    +     * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java index 83a6c0f00..6a5d92b7c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecuritySchemeOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -11,76 +11,136 @@ public interface SecuritySchemeOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
    +   * API key-based authentication.
    +   * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return Whether the apiKeySecurityScheme field is set. */ boolean hasApiKeySecurityScheme(); /** + *
    +   * API key-based authentication.
    +   * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; * @return The apiKeySecurityScheme. */ io.a2a.grpc.APIKeySecurityScheme getApiKeySecurityScheme(); /** + *
    +   * API key-based authentication.
    +   * 
    + * * .a2a.v1.APIKeySecurityScheme api_key_security_scheme = 1; */ io.a2a.grpc.APIKeySecuritySchemeOrBuilder getApiKeySecuritySchemeOrBuilder(); /** + *
    +   * HTTP authentication (Basic, Bearer, etc.).
    +   * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return Whether the httpAuthSecurityScheme field is set. */ boolean hasHttpAuthSecurityScheme(); /** + *
    +   * HTTP authentication (Basic, Bearer, etc.).
    +   * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; * @return The httpAuthSecurityScheme. */ io.a2a.grpc.HTTPAuthSecurityScheme getHttpAuthSecurityScheme(); /** + *
    +   * HTTP authentication (Basic, Bearer, etc.).
    +   * 
    + * * .a2a.v1.HTTPAuthSecurityScheme http_auth_security_scheme = 2; */ io.a2a.grpc.HTTPAuthSecuritySchemeOrBuilder getHttpAuthSecuritySchemeOrBuilder(); /** + *
    +   * OAuth 2.0 authentication.
    +   * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return Whether the oauth2SecurityScheme field is set. */ boolean hasOauth2SecurityScheme(); /** + *
    +   * OAuth 2.0 authentication.
    +   * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; * @return The oauth2SecurityScheme. */ io.a2a.grpc.OAuth2SecurityScheme getOauth2SecurityScheme(); /** + *
    +   * OAuth 2.0 authentication.
    +   * 
    + * * .a2a.v1.OAuth2SecurityScheme oauth2_security_scheme = 3; */ io.a2a.grpc.OAuth2SecuritySchemeOrBuilder getOauth2SecuritySchemeOrBuilder(); /** + *
    +   * OpenID Connect authentication.
    +   * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return Whether the openIdConnectSecurityScheme field is set. */ boolean hasOpenIdConnectSecurityScheme(); /** + *
    +   * OpenID Connect authentication.
    +   * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; * @return The openIdConnectSecurityScheme. */ io.a2a.grpc.OpenIdConnectSecurityScheme getOpenIdConnectSecurityScheme(); /** + *
    +   * OpenID Connect authentication.
    +   * 
    + * * .a2a.v1.OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; */ io.a2a.grpc.OpenIdConnectSecuritySchemeOrBuilder getOpenIdConnectSecuritySchemeOrBuilder(); /** + *
    +   * Mutual TLS authentication.
    +   * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; * @return Whether the mtlsSecurityScheme field is set. */ boolean hasMtlsSecurityScheme(); /** + *
    +   * Mutual TLS authentication.
    +   * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; * @return The mtlsSecurityScheme. */ io.a2a.grpc.MutualTlsSecurityScheme getMtlsSecurityScheme(); /** + *
    +   * Mutual TLS authentication.
    +   * 
    + * * .a2a.v1.MutualTlsSecurityScheme mtls_security_scheme = 5; */ io.a2a.grpc.MutualTlsSecuritySchemeOrBuilder getMtlsSecuritySchemeOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index 24ba5afba..457c55b1f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class SendMessageConfiguration extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - SendMessageConfiguration.class.getName()); + "SendMessageConfiguration"); } // Use SendMessageConfiguration.newBuilder() to construct. private SendMessageConfiguration(com.google.protobuf.GeneratedMessage.Builder builder) { @@ -57,7 +57,7 @@ private SendMessageConfiguration() { com.google.protobuf.LazyStringArrayList.emptyList(); /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -69,7 +69,7 @@ private SendMessageConfiguration() { } /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -80,7 +80,7 @@ public int getAcceptedOutputModesCount() { } /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -92,7 +92,7 @@ public java.lang.String getAcceptedOutputModes(int index) { } /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -108,7 +108,7 @@ public java.lang.String getAcceptedOutputModes(int index) { private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; /** *
    -   * A configuration of a webhook that can be used to receive updates
    +   * Configuration for the agent to send push notifications for task updates.
        * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -120,7 +120,7 @@ public boolean hasPushNotificationConfig() { } /** *
    -   * A configuration of a webhook that can be used to receive updates
    +   * Configuration for the agent to send push notifications for task updates.
        * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -132,7 +132,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { } /** *
    -   * A configuration of a webhook that can be used to receive updates
    +   * Configuration for the agent to send push notifications for task updates.
        * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -173,7 +173,7 @@ public int getHistoryLength() { private boolean blocking_ = false; /** *
    -   * If true, the message will be blocking until the task is completed.
    +   * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
        * 
    * * bool blocking = 4; @@ -606,7 +606,7 @@ private void ensureAcceptedOutputModesIsMutable() { } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -619,7 +619,7 @@ private void ensureAcceptedOutputModesIsMutable() { } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -630,7 +630,7 @@ public int getAcceptedOutputModesCount() { } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -642,7 +642,7 @@ public java.lang.String getAcceptedOutputModes(int index) { } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -655,7 +655,7 @@ public java.lang.String getAcceptedOutputModes(int index) { } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -674,7 +674,7 @@ public Builder setAcceptedOutputModes( } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -692,7 +692,7 @@ public Builder addAcceptedOutputModes( } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -710,7 +710,7 @@ public Builder addAllAcceptedOutputModes( } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -725,7 +725,7 @@ public Builder clearAcceptedOutputModes() { } /** *
    -     * The output modes that the agent is expected to respond with.
    +     * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
          * 
    * * repeated string accepted_output_modes = 1; @@ -748,7 +748,7 @@ public Builder addAcceptedOutputModesBytes( io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationConfigBuilder_; /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -759,7 +759,7 @@ public boolean hasPushNotificationConfig() { } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -774,7 +774,7 @@ public io.a2a.grpc.PushNotificationConfig getPushNotificationConfig() { } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -794,7 +794,7 @@ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig valu } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -812,7 +812,7 @@ public Builder setPushNotificationConfig( } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -837,7 +837,7 @@ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig va } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -854,7 +854,7 @@ public Builder clearPushNotificationConfig() { } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -866,7 +866,7 @@ public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuild } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -881,7 +881,7 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu } /** *
    -     * A configuration of a webhook that can be used to receive updates
    +     * Configuration for the agent to send push notifications for task updates.
          * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -959,7 +959,7 @@ public Builder clearHistoryLength() { private boolean blocking_ ; /** *
    -     * If true, the message will be blocking until the task is completed.
    +     * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
          * 
    * * bool blocking = 4; @@ -971,7 +971,7 @@ public boolean getBlocking() { } /** *
    -     * If true, the message will be blocking until the task is completed.
    +     * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
          * 
    * * bool blocking = 4; @@ -987,7 +987,7 @@ public Builder setBlocking(boolean value) { } /** *
    -     * If true, the message will be blocking until the task is completed.
    +     * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
          * 
    * * bool blocking = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java index e1a6f791f..cfbab940c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -12,7 +12,7 @@ public interface SendMessageConfigurationOrBuilder extends /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -22,7 +22,7 @@ public interface SendMessageConfigurationOrBuilder extends getAcceptedOutputModesList(); /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -31,7 +31,7 @@ public interface SendMessageConfigurationOrBuilder extends int getAcceptedOutputModesCount(); /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -41,7 +41,7 @@ public interface SendMessageConfigurationOrBuilder extends java.lang.String getAcceptedOutputModes(int index); /** *
    -   * The output modes that the agent is expected to respond with.
    +   * A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output.
        * 
    * * repeated string accepted_output_modes = 1; @@ -53,7 +53,7 @@ public interface SendMessageConfigurationOrBuilder extends /** *
    -   * A configuration of a webhook that can be used to receive updates
    +   * Configuration for the agent to send push notifications for task updates.
        * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -62,7 +62,7 @@ public interface SendMessageConfigurationOrBuilder extends boolean hasPushNotificationConfig(); /** *
    -   * A configuration of a webhook that can be used to receive updates
    +   * Configuration for the agent to send push notifications for task updates.
        * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -71,7 +71,7 @@ public interface SendMessageConfigurationOrBuilder extends io.a2a.grpc.PushNotificationConfig getPushNotificationConfig(); /** *
    -   * A configuration of a webhook that can be used to receive updates
    +   * Configuration for the agent to send push notifications for task updates.
        * 
    * * .a2a.v1.PushNotificationConfig push_notification_config = 2; @@ -99,7 +99,7 @@ public interface SendMessageConfigurationOrBuilder extends /** *
    -   * If true, the message will be blocking until the task is completed.
    +   * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
        * 
    * * bool blocking = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index 40511b286..41ba38002 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -24,16 +24,17 @@ public final class SendMessageRequest extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - SendMessageRequest.class.getName()); + "SendMessageRequest"); } // Use SendMessageRequest.newBuilder() to construct. private SendMessageRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private SendMessageRequest() { + tenant_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -50,6 +51,53 @@ private SendMessageRequest() { } private int bitField0_; + public static final int TENANT_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int REQUEST_FIELD_NUMBER = 1; private io.a2a.grpc.Message request_; /** @@ -130,7 +178,7 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder() private com.google.protobuf.Struct metadata_; /** *
    -   * Optional metadata for the request.
    +   * A flexible key-value map for passing additional context or parameters.
        * 
    * * .google.protobuf.Struct metadata = 3; @@ -142,7 +190,7 @@ public boolean hasMetadata() { } /** *
    -   * Optional metadata for the request.
    +   * A flexible key-value map for passing additional context or parameters.
        * 
    * * .google.protobuf.Struct metadata = 3; @@ -154,7 +202,7 @@ public com.google.protobuf.Struct getMetadata() { } /** *
    -   * Optional metadata for the request.
    +   * A flexible key-value map for passing additional context or parameters.
        * 
    * * .google.protobuf.Struct metadata = 3; @@ -187,6 +235,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000004) != 0)) { output.writeMessage(3, getMetadata()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, tenant_); + } getUnknownFields().writeTo(output); } @@ -208,6 +259,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, getMetadata()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -223,6 +277,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.SendMessageRequest other = (io.a2a.grpc.SendMessageRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (hasRequest() != other.hasRequest()) return false; if (hasRequest()) { if (!getRequest() @@ -249,6 +305,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); if (hasRequest()) { hash = (37 * hash) + REQUEST_FIELD_NUMBER; hash = (53 * hash) + getRequest().hashCode(); @@ -406,6 +464,7 @@ private void maybeForceBuilderInitialization() { public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; request_ = null; if (requestBuilder_ != null) { requestBuilder_.dispose(); @@ -454,20 +513,23 @@ public io.a2a.grpc.SendMessageRequest buildPartial() { private void buildPartial0(io.a2a.grpc.SendMessageRequest result) { int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000002) != 0)) { result.request_ = requestBuilder_ == null ? request_ : requestBuilder_.build(); to_bitField0_ |= 0x00000001; } - if (((from_bitField0_ & 0x00000002) != 0)) { + if (((from_bitField0_ & 0x00000004) != 0)) { result.configuration_ = configurationBuilder_ == null ? configuration_ : configurationBuilder_.build(); to_bitField0_ |= 0x00000002; } - if (((from_bitField0_ & 0x00000004) != 0)) { + if (((from_bitField0_ & 0x00000008) != 0)) { result.metadata_ = metadataBuilder_ == null ? metadata_ : metadataBuilder_.build(); @@ -488,6 +550,11 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.SendMessageRequest other) { if (other == io.a2a.grpc.SendMessageRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (other.hasRequest()) { mergeRequest(other.getRequest()); } @@ -527,23 +594,28 @@ public Builder mergeFrom( input.readMessage( internalGetRequestFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 case 18: { input.readMessage( internalGetConfigurationFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; break; } // case 18 case 26: { input.readMessage( internalGetMetadataFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; break; } // case 26 + case 34: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 34 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -561,6 +633,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private io.a2a.grpc.Message request_; private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> requestBuilder_; @@ -573,7 +737,7 @@ public Builder mergeFrom( * @return Whether the request field is set. */ public boolean hasRequest() { - return ((bitField0_ & 0x00000001) != 0); + return ((bitField0_ & 0x00000002) != 0); } /** *
    @@ -606,7 +770,7 @@ public Builder setRequest(io.a2a.grpc.Message value) {
           } else {
             requestBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -624,7 +788,7 @@ public Builder setRequest(
           } else {
             requestBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -637,7 +801,7 @@ public Builder setRequest(
          */
         public Builder mergeRequest(io.a2a.grpc.Message value) {
           if (requestBuilder_ == null) {
    -        if (((bitField0_ & 0x00000001) != 0) &&
    +        if (((bitField0_ & 0x00000002) != 0) &&
               request_ != null &&
               request_ != io.a2a.grpc.Message.getDefaultInstance()) {
               getRequestBuilder().mergeFrom(value);
    @@ -648,7 +812,7 @@ public Builder mergeRequest(io.a2a.grpc.Message value) {
             requestBuilder_.mergeFrom(value);
           }
           if (request_ != null) {
    -        bitField0_ |= 0x00000001;
    +        bitField0_ |= 0x00000002;
             onChanged();
           }
           return this;
    @@ -661,7 +825,7 @@ public Builder mergeRequest(io.a2a.grpc.Message value) {
          * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED];
          */
         public Builder clearRequest() {
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           request_ = null;
           if (requestBuilder_ != null) {
             requestBuilder_.dispose();
    @@ -678,7 +842,7 @@ public Builder clearRequest() {
          * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED];
          */
         public io.a2a.grpc.Message.Builder getRequestBuilder() {
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return internalGetRequestFieldBuilder().getBuilder();
         }
    @@ -730,7 +894,7 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() {
          * @return Whether the configuration field is set.
          */
         public boolean hasConfiguration() {
    -      return ((bitField0_ & 0x00000002) != 0);
    +      return ((bitField0_ & 0x00000004) != 0);
         }
         /**
          * 
    @@ -763,7 +927,7 @@ public Builder setConfiguration(io.a2a.grpc.SendMessageConfiguration value) {
           } else {
             configurationBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -781,7 +945,7 @@ public Builder setConfiguration(
           } else {
             configurationBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -794,7 +958,7 @@ public Builder setConfiguration(
          */
         public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) {
           if (configurationBuilder_ == null) {
    -        if (((bitField0_ & 0x00000002) != 0) &&
    +        if (((bitField0_ & 0x00000004) != 0) &&
               configuration_ != null &&
               configuration_ != io.a2a.grpc.SendMessageConfiguration.getDefaultInstance()) {
               getConfigurationBuilder().mergeFrom(value);
    @@ -805,7 +969,7 @@ public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) {
             configurationBuilder_.mergeFrom(value);
           }
           if (configuration_ != null) {
    -        bitField0_ |= 0x00000002;
    +        bitField0_ |= 0x00000004;
             onChanged();
           }
           return this;
    @@ -818,7 +982,7 @@ public Builder mergeConfiguration(io.a2a.grpc.SendMessageConfiguration value) {
          * .a2a.v1.SendMessageConfiguration configuration = 2;
          */
         public Builder clearConfiguration() {
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000004);
           configuration_ = null;
           if (configurationBuilder_ != null) {
             configurationBuilder_.dispose();
    @@ -835,7 +999,7 @@ public Builder clearConfiguration() {
          * .a2a.v1.SendMessageConfiguration configuration = 2;
          */
         public io.a2a.grpc.SendMessageConfiguration.Builder getConfigurationBuilder() {
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return internalGetConfigurationFieldBuilder().getBuilder();
         }
    @@ -880,18 +1044,18 @@ public io.a2a.grpc.SendMessageConfigurationOrBuilder getConfigurationOrBuilder()
             com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_;
         /**
          * 
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; * @return Whether the metadata field is set. */ public boolean hasMetadata() { - return ((bitField0_ & 0x00000004) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; @@ -906,7 +1070,7 @@ public com.google.protobuf.Struct getMetadata() { } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; @@ -920,13 +1084,13 @@ public Builder setMetadata(com.google.protobuf.Struct value) { } else { metadataBuilder_.setMessage(value); } - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); return this; } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; @@ -938,20 +1102,20 @@ public Builder setMetadata( } else { metadataBuilder_.setMessage(builderForValue.build()); } - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); return this; } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { - if (((bitField0_ & 0x00000004) != 0) && + if (((bitField0_ & 0x00000008) != 0) && metadata_ != null && metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { getMetadataBuilder().mergeFrom(value); @@ -962,20 +1126,20 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { metadataBuilder_.mergeFrom(value); } if (metadata_ != null) { - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); } return this; } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; */ public Builder clearMetadata() { - bitField0_ = (bitField0_ & ~0x00000004); + bitField0_ = (bitField0_ & ~0x00000008); metadata_ = null; if (metadataBuilder_ != null) { metadataBuilder_.dispose(); @@ -986,19 +1150,19 @@ public Builder clearMetadata() { } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); return internalGetMetadataFieldBuilder().getBuilder(); } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; @@ -1013,7 +1177,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { } /** *
    -     * Optional metadata for the request.
    +     * A flexible key-value map for passing additional context or parameters.
          * 
    * * .google.protobuf.Struct metadata = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java index 8edf78d41..5040d6562 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -10,6 +10,26 @@ public interface SendMessageRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:a2a.v1.SendMessageRequest) com.google.protobuf.MessageOrBuilder { + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The message to send to the agent.
    @@ -66,7 +86,7 @@ public interface SendMessageRequestOrBuilder extends
     
       /**
        * 
    -   * Optional metadata for the request.
    +   * A flexible key-value map for passing additional context or parameters.
        * 
    * * .google.protobuf.Struct metadata = 3; @@ -75,7 +95,7 @@ public interface SendMessageRequestOrBuilder extends boolean hasMetadata(); /** *
    -   * Optional metadata for the request.
    +   * A flexible key-value map for passing additional context or parameters.
        * 
    * * .google.protobuf.Struct metadata = 3; @@ -84,7 +104,7 @@ public interface SendMessageRequestOrBuilder extends com.google.protobuf.Struct getMetadata(); /** *
    -   * Optional metadata for the request.
    +   * A flexible key-value map for passing additional context or parameters.
        * 
    * * .google.protobuf.Struct metadata = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java index 1d3a75c82..0fe457f59 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class SendMessageResponse extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - SendMessageResponse.class.getName()); + "SendMessageResponse"); } // Use SendMessageResponse.newBuilder() to construct. private SendMessageResponse(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java index deb31c6cf..40a838920 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java index b12b2e1d2..161a1b257 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,16 +23,17 @@ public final class SetTaskPushNotificationConfigRequest extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - SetTaskPushNotificationConfigRequest.class.getName()); + "SetTaskPushNotificationConfigRequest"); } // Use SetTaskPushNotificationConfigRequest.newBuilder() to construct. private SetTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private SetTaskPushNotificationConfigRequest() { + tenant_ = ""; parent_ = ""; configId_ = ""; } @@ -51,6 +52,53 @@ private SetTaskPushNotificationConfigRequest() { } private int bitField0_; + public static final int TENANT_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int PARENT_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object parent_ = ""; @@ -208,6 +256,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) != 0)) { output.writeMessage(3, getConfig()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, tenant_); + } getUnknownFields().writeTo(output); } @@ -227,6 +278,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, getConfig()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -242,6 +296,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.SetTaskPushNotificationConfigRequest other = (io.a2a.grpc.SetTaskPushNotificationConfigRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getParent() .equals(other.getParent())) return false; if (!getConfigId() @@ -262,6 +318,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + PARENT_FIELD_NUMBER; hash = (53 * hash) + getParent().hashCode(); hash = (37 * hash) + CONFIG_ID_FIELD_NUMBER; @@ -412,6 +470,7 @@ private void maybeForceBuilderInitialization() { public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; parent_ = ""; configId_ = ""; config_ = null; @@ -453,13 +512,16 @@ public io.a2a.grpc.SetTaskPushNotificationConfigRequest buildPartial() { private void buildPartial0(io.a2a.grpc.SetTaskPushNotificationConfigRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - result.parent_ = parent_; + result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { + result.parent_ = parent_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { result.configId_ = configId_; } int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000004) != 0)) { + if (((from_bitField0_ & 0x00000008) != 0)) { result.config_ = configBuilder_ == null ? config_ : configBuilder_.build(); @@ -480,14 +542,19 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.SetTaskPushNotificationConfigRequest other) { if (other == io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getParent().isEmpty()) { parent_ = other.parent_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } if (!other.getConfigId().isEmpty()) { configId_ = other.configId_; - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; onChanged(); } if (other.hasConfig()) { @@ -521,21 +588,26 @@ public Builder mergeFrom( break; case 10: { parent_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 case 18: { configId_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; break; } // case 18 case 26: { input.readMessage( internalGetConfigFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; break; } // case 26 + case 34: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 34 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -553,6 +625,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 4; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object parent_ = ""; /** *
    @@ -611,7 +775,7 @@ public Builder setParent(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           parent_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -626,7 +790,7 @@ public Builder setParent(
          */
         public Builder clearParent() {
           parent_ = getDefaultInstance().getParent();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -645,7 +809,7 @@ public Builder setParentBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           parent_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -705,7 +869,7 @@ public Builder setConfigId(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           configId_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -719,7 +883,7 @@ public Builder setConfigId(
          */
         public Builder clearConfigId() {
           configId_ = getDefaultInstance().getConfigId();
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000004);
           onChanged();
           return this;
         }
    @@ -737,7 +901,7 @@ public Builder setConfigIdBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           configId_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000004;
           onChanged();
           return this;
         }
    @@ -754,7 +918,7 @@ public Builder setConfigIdBytes(
          * @return Whether the config field is set.
          */
         public boolean hasConfig() {
    -      return ((bitField0_ & 0x00000004) != 0);
    +      return ((bitField0_ & 0x00000008) != 0);
         }
         /**
          * 
    @@ -787,7 +951,7 @@ public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) {
           } else {
             configBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -805,7 +969,7 @@ public Builder setConfig(
           } else {
             configBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -818,7 +982,7 @@ public Builder setConfig(
          */
         public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) {
           if (configBuilder_ == null) {
    -        if (((bitField0_ & 0x00000004) != 0) &&
    +        if (((bitField0_ & 0x00000008) != 0) &&
               config_ != null &&
               config_ != io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()) {
               getConfigBuilder().mergeFrom(value);
    @@ -829,7 +993,7 @@ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) {
             configBuilder_.mergeFrom(value);
           }
           if (config_ != null) {
    -        bitField0_ |= 0x00000004;
    +        bitField0_ |= 0x00000008;
             onChanged();
           }
           return this;
    @@ -842,7 +1006,7 @@ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) {
          * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED];
          */
         public Builder clearConfig() {
    -      bitField0_ = (bitField0_ & ~0x00000004);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           config_ = null;
           if (configBuilder_ != null) {
             configBuilder_.dispose();
    @@ -859,7 +1023,7 @@ public Builder clearConfig() {
          * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED];
          */
         public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() {
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return internalGetConfigFieldBuilder().getBuilder();
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java
    index 53a99640c..c5a01418a 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface SetTaskPushNotificationConfigRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.SetTaskPushNotificationConfigRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 4; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The parent task resource for this config.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java
    index 6592fcce9..139650483 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java
    @@ -1,14 +1,14 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
     /**
      * 
      * --8<-- [start:StreamResponse]
    - * Response message containing task updates or messages.
    + * A wrapper object used in streaming operations to encapsulate different types of response data.
      * 
    * * Protobuf type {@code a2a.v1.StreamResponse} @@ -23,10 +23,10 @@ public final class StreamResponse extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - StreamResponse.class.getName()); + "StreamResponse"); } // Use StreamResponse.newBuilder() to construct. private StreamResponse(com.google.protobuf.GeneratedMessage.Builder builder) { @@ -96,6 +96,10 @@ public int getNumber() { public static final int TASK_FIELD_NUMBER = 1; /** + *
    +   * A Task object containing the current state of the task.
    +   * 
    + * * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ @@ -104,6 +108,10 @@ public boolean hasTask() { return payloadCase_ == 1; } /** + *
    +   * A Task object containing the current state of the task.
    +   * 
    + * * .a2a.v1.Task task = 1; * @return The task. */ @@ -115,6 +123,10 @@ public io.a2a.grpc.Task getTask() { return io.a2a.grpc.Task.getDefaultInstance(); } /** + *
    +   * A Task object containing the current state of the task.
    +   * 
    + * * .a2a.v1.Task task = 1; */ @java.lang.Override @@ -127,6 +139,10 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { public static final int MSG_FIELD_NUMBER = 2; /** + *
    +   * A Message object containing a message from the agent.
    +   * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; * @return Whether the msg field is set. */ @@ -135,6 +151,10 @@ public boolean hasMsg() { return payloadCase_ == 2; } /** + *
    +   * A Message object containing a message from the agent.
    +   * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; * @return The msg. */ @@ -146,6 +166,10 @@ public io.a2a.grpc.Message getMsg() { return io.a2a.grpc.Message.getDefaultInstance(); } /** + *
    +   * A Message object containing a message from the agent.
    +   * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ @java.lang.Override @@ -158,6 +182,10 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { public static final int STATUS_UPDATE_FIELD_NUMBER = 3; /** + *
    +   * An event indicating a task status update.
    +   * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return Whether the statusUpdate field is set. */ @@ -166,6 +194,10 @@ public boolean hasStatusUpdate() { return payloadCase_ == 3; } /** + *
    +   * An event indicating a task status update.
    +   * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return The statusUpdate. */ @@ -177,6 +209,10 @@ public io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate() { return io.a2a.grpc.TaskStatusUpdateEvent.getDefaultInstance(); } /** + *
    +   * An event indicating a task status update.
    +   * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ @java.lang.Override @@ -189,6 +225,10 @@ public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { public static final int ARTIFACT_UPDATE_FIELD_NUMBER = 4; /** + *
    +   * An event indicating a task artifact update.
    +   * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return Whether the artifactUpdate field is set. */ @@ -197,6 +237,10 @@ public boolean hasArtifactUpdate() { return payloadCase_ == 4; } /** + *
    +   * An event indicating a task artifact update.
    +   * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return The artifactUpdate. */ @@ -208,6 +252,10 @@ public io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate() { return io.a2a.grpc.TaskArtifactUpdateEvent.getDefaultInstance(); } /** + *
    +   * An event indicating a task artifact update.
    +   * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ @java.lang.Override @@ -436,7 +484,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:StreamResponse]
    -   * Response message containing task updates or messages.
    +   * A wrapper object used in streaming operations to encapsulate different types of response data.
        * 
    * * Protobuf type {@code a2a.v1.StreamResponse} @@ -665,6 +713,10 @@ public Builder clearPayload() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Task, io.a2a.grpc.Task.Builder, io.a2a.grpc.TaskOrBuilder> taskBuilder_; /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ @@ -673,6 +725,10 @@ public boolean hasTask() { return payloadCase_ == 1; } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; * @return The task. */ @@ -691,6 +747,10 @@ public io.a2a.grpc.Task getTask() { } } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ public Builder setTask(io.a2a.grpc.Task value) { @@ -707,6 +767,10 @@ public Builder setTask(io.a2a.grpc.Task value) { return this; } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ public Builder setTask( @@ -721,6 +785,10 @@ public Builder setTask( return this; } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ public Builder mergeTask(io.a2a.grpc.Task value) { @@ -744,6 +812,10 @@ public Builder mergeTask(io.a2a.grpc.Task value) { return this; } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ public Builder clearTask() { @@ -763,12 +835,20 @@ public Builder clearTask() { return this; } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ public io.a2a.grpc.Task.Builder getTaskBuilder() { return internalGetTaskFieldBuilder().getBuilder(); } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ @java.lang.Override @@ -783,6 +863,10 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { } } /** + *
    +     * A Task object containing the current state of the task.
    +     * 
    + * * .a2a.v1.Task task = 1; */ private com.google.protobuf.SingleFieldBuilder< @@ -807,6 +891,10 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> msgBuilder_; /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; * @return Whether the msg field is set. */ @@ -815,6 +903,10 @@ public boolean hasMsg() { return payloadCase_ == 2; } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; * @return The msg. */ @@ -833,6 +925,10 @@ public io.a2a.grpc.Message getMsg() { } } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ public Builder setMsg(io.a2a.grpc.Message value) { @@ -849,6 +945,10 @@ public Builder setMsg(io.a2a.grpc.Message value) { return this; } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ public Builder setMsg( @@ -863,6 +963,10 @@ public Builder setMsg( return this; } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ public Builder mergeMsg(io.a2a.grpc.Message value) { @@ -886,6 +990,10 @@ public Builder mergeMsg(io.a2a.grpc.Message value) { return this; } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ public Builder clearMsg() { @@ -905,12 +1013,20 @@ public Builder clearMsg() { return this; } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ public io.a2a.grpc.Message.Builder getMsgBuilder() { return internalGetMsgFieldBuilder().getBuilder(); } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ @java.lang.Override @@ -925,6 +1041,10 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { } } /** + *
    +     * A Message object containing a message from the agent.
    +     * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ private com.google.protobuf.SingleFieldBuilder< @@ -949,6 +1069,10 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskStatusUpdateEvent, io.a2a.grpc.TaskStatusUpdateEvent.Builder, io.a2a.grpc.TaskStatusUpdateEventOrBuilder> statusUpdateBuilder_; /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return Whether the statusUpdate field is set. */ @@ -957,6 +1081,10 @@ public boolean hasStatusUpdate() { return payloadCase_ == 3; } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return The statusUpdate. */ @@ -975,6 +1103,10 @@ public io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate() { } } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder setStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { @@ -991,6 +1123,10 @@ public Builder setStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { return this; } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder setStatusUpdate( @@ -1005,6 +1141,10 @@ public Builder setStatusUpdate( return this; } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder mergeStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { @@ -1028,6 +1168,10 @@ public Builder mergeStatusUpdate(io.a2a.grpc.TaskStatusUpdateEvent value) { return this; } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public Builder clearStatusUpdate() { @@ -1047,12 +1191,20 @@ public Builder clearStatusUpdate() { return this; } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ public io.a2a.grpc.TaskStatusUpdateEvent.Builder getStatusUpdateBuilder() { return internalGetStatusUpdateFieldBuilder().getBuilder(); } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ @java.lang.Override @@ -1067,6 +1219,10 @@ public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { } } /** + *
    +     * An event indicating a task status update.
    +     * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ private com.google.protobuf.SingleFieldBuilder< @@ -1091,6 +1247,10 @@ public io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder() { private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.TaskArtifactUpdateEvent, io.a2a.grpc.TaskArtifactUpdateEvent.Builder, io.a2a.grpc.TaskArtifactUpdateEventOrBuilder> artifactUpdateBuilder_; /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return Whether the artifactUpdate field is set. */ @@ -1099,6 +1259,10 @@ public boolean hasArtifactUpdate() { return payloadCase_ == 4; } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return The artifactUpdate. */ @@ -1117,6 +1281,10 @@ public io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate() { } } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder setArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { @@ -1133,6 +1301,10 @@ public Builder setArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { return this; } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder setArtifactUpdate( @@ -1147,6 +1319,10 @@ public Builder setArtifactUpdate( return this; } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder mergeArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { @@ -1170,6 +1346,10 @@ public Builder mergeArtifactUpdate(io.a2a.grpc.TaskArtifactUpdateEvent value) { return this; } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public Builder clearArtifactUpdate() { @@ -1189,12 +1369,20 @@ public Builder clearArtifactUpdate() { return this; } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ public io.a2a.grpc.TaskArtifactUpdateEvent.Builder getArtifactUpdateBuilder() { return internalGetArtifactUpdateFieldBuilder().getBuilder(); } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ @java.lang.Override @@ -1209,6 +1397,10 @@ public io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder() } } /** + *
    +     * An event indicating a task artifact update.
    +     * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java index c606a8751..d6596f26d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -11,61 +11,109 @@ public interface StreamResponseOrBuilder extends com.google.protobuf.MessageOrBuilder { /** + *
    +   * A Task object containing the current state of the task.
    +   * 
    + * * .a2a.v1.Task task = 1; * @return Whether the task field is set. */ boolean hasTask(); /** + *
    +   * A Task object containing the current state of the task.
    +   * 
    + * * .a2a.v1.Task task = 1; * @return The task. */ io.a2a.grpc.Task getTask(); /** + *
    +   * A Task object containing the current state of the task.
    +   * 
    + * * .a2a.v1.Task task = 1; */ io.a2a.grpc.TaskOrBuilder getTaskOrBuilder(); /** + *
    +   * A Message object containing a message from the agent.
    +   * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; * @return Whether the msg field is set. */ boolean hasMsg(); /** + *
    +   * A Message object containing a message from the agent.
    +   * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; * @return The msg. */ io.a2a.grpc.Message getMsg(); /** + *
    +   * A Message object containing a message from the agent.
    +   * 
    + * * .a2a.v1.Message msg = 2 [json_name = "message"]; */ io.a2a.grpc.MessageOrBuilder getMsgOrBuilder(); /** + *
    +   * An event indicating a task status update.
    +   * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return Whether the statusUpdate field is set. */ boolean hasStatusUpdate(); /** + *
    +   * An event indicating a task status update.
    +   * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; * @return The statusUpdate. */ io.a2a.grpc.TaskStatusUpdateEvent getStatusUpdate(); /** + *
    +   * An event indicating a task status update.
    +   * 
    + * * .a2a.v1.TaskStatusUpdateEvent status_update = 3; */ io.a2a.grpc.TaskStatusUpdateEventOrBuilder getStatusUpdateOrBuilder(); /** + *
    +   * An event indicating a task artifact update.
    +   * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return Whether the artifactUpdate field is set. */ boolean hasArtifactUpdate(); /** + *
    +   * An event indicating a task artifact update.
    +   * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; * @return The artifactUpdate. */ io.a2a.grpc.TaskArtifactUpdateEvent getArtifactUpdate(); /** + *
    +   * An event indicating a task artifact update.
    +   * 
    + * * .a2a.v1.TaskArtifactUpdateEvent artifact_update = 4; */ io.a2a.grpc.TaskArtifactUpdateEventOrBuilder getArtifactUpdateOrBuilder(); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StringList.java b/spec-grpc/src/main/java/io/a2a/grpc/StringList.java index c283c87fa..6fed6e257 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StringList.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StringList.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -22,10 +22,10 @@ public final class StringList extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - StringList.class.getName()); + "StringList"); } // Use StringList.newBuilder() to construct. private StringList(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java index 36007c638..48cdceac1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StringListOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java index 368a89fa8..cdef789c1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -22,16 +22,17 @@ public final class SubscribeToTaskRequest extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - SubscribeToTaskRequest.class.getName()); + "SubscribeToTaskRequest"); } // Use SubscribeToTaskRequest.newBuilder() to construct. private SubscribeToTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private SubscribeToTaskRequest() { + tenant_ = ""; name_ = ""; } @@ -48,6 +49,53 @@ private SubscribeToTaskRequest() { io.a2a.grpc.SubscribeToTaskRequest.class, io.a2a.grpc.SubscribeToTaskRequest.Builder.class); } + public static final int TENANT_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object tenant_ = ""; + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + @java.lang.Override + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } + } + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -114,6 +162,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); + } getUnknownFields().writeTo(output); } @@ -126,6 +177,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -141,6 +195,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.SubscribeToTaskRequest other = (io.a2a.grpc.SubscribeToTaskRequest) obj; + if (!getTenant() + .equals(other.getTenant())) return false; if (!getName() .equals(other.getName())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -154,6 +210,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); @@ -291,6 +349,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + tenant_ = ""; name_ = ""; return this; } @@ -326,6 +385,9 @@ public io.a2a.grpc.SubscribeToTaskRequest buildPartial() { private void buildPartial0(io.a2a.grpc.SubscribeToTaskRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { + result.tenant_ = tenant_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; } } @@ -342,9 +404,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.SubscribeToTaskRequest other) { if (other == io.a2a.grpc.SubscribeToTaskRequest.getDefaultInstance()) return this; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getName().isEmpty()) { name_ = other.name_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -375,9 +442,14 @@ public Builder mergeFrom( break; case 10: { name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; break; } // case 10 + case 18: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 18 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -395,6 +467,98 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object tenant_ = ""; + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The tenant. + */ + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tenant_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + public com.google.protobuf.ByteString + getTenantBytes() { + java.lang.Object ref = tenant_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The tenant to set. + * @return This builder for chaining. + */ + public Builder setTenant( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @return This builder for chaining. + */ + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * Optional tenant, provided as a path parameter.
    +     * 
    + * + * string tenant = 2; + * @param value The bytes for tenant to set. + * @return This builder for chaining. + */ + public Builder setTenantBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tenant_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object name_ = ""; /** *
    @@ -453,7 +617,7 @@ public Builder setName(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -468,7 +632,7 @@ public Builder setName(
          */
         public Builder clearName() {
           name_ = getDefaultInstance().getName();
    -      bitField0_ = (bitField0_ & ~0x00000001);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -487,7 +651,7 @@ public Builder setNameBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           name_ = value;
    -      bitField0_ |= 0x00000001;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java
    index e96160913..0d4a4fda9 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -10,6 +10,26 @@ public interface SubscribeToTaskRequestOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.SubscribeToTaskRequest)
         com.google.protobuf.MessageOrBuilder {
     
    +  /**
    +   * 
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The tenant. + */ + java.lang.String getTenant(); + /** + *
    +   * Optional tenant, provided as a path parameter.
    +   * 
    + * + * string tenant = 2; + * @return The bytes for tenant. + */ + com.google.protobuf.ByteString + getTenantBytes(); + /** *
        * The resource name of the task to subscribe to.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Task.java b/spec-grpc/src/main/java/io/a2a/grpc/Task.java
    index 89fbe6c67..cc1e66690 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/Task.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/Task.java
    @@ -1,7 +1,7 @@
     // Generated by the protocol buffer compiler.  DO NOT EDIT!
     // NO CHECKED-IN PROTOBUF GENCODE
     // source: a2a.proto
    -// Protobuf Java Version: 4.31.1
    +// Protobuf Java Version: 4.33.1
     
     package io.a2a.grpc;
     
    @@ -26,10 +26,10 @@ public final class Task extends
         com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
           com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
           /* major= */ 4,
    -      /* minor= */ 31,
    +      /* minor= */ 33,
           /* patch= */ 1,
           /* suffix= */ "",
    -      Task.class.getName());
    +      "Task");
       }
       // Use Task.newBuilder() to construct.
       private Task(com.google.protobuf.GeneratedMessage.Builder builder) {
    @@ -325,8 +325,6 @@ public io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder(
        * 
        * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
        * A key/value object to store custom metadata about a task.
    -   * Optional metadata for extensions. The key is an extension-specific
    -   * identifier.
        * 
    * * .google.protobuf.Struct metadata = 6; @@ -340,8 +338,6 @@ public boolean hasMetadata() { *
        * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
        * A key/value object to store custom metadata about a task.
    -   * Optional metadata for extensions. The key is an extension-specific
    -   * identifier.
        * 
    * * .google.protobuf.Struct metadata = 6; @@ -355,8 +351,6 @@ public com.google.protobuf.Struct getMetadata() { *
        * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
        * A key/value object to store custom metadata about a task.
    -   * Optional metadata for extensions. The key is an extension-specific
    -   * identifier.
        * 
    * * .google.protobuf.Struct metadata = 6; @@ -1917,8 +1911,6 @@ public io.a2a.grpc.Message.Builder addHistoryBuilder( *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -1931,8 +1923,6 @@ public boolean hasMetadata() { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -1949,8 +1939,6 @@ public com.google.protobuf.Struct getMetadata() { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -1972,8 +1960,6 @@ public Builder setMetadata(com.google.protobuf.Struct value) { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -1993,8 +1979,6 @@ public Builder setMetadata( *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -2021,8 +2005,6 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -2041,8 +2023,6 @@ public Builder clearMetadata() { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -2056,8 +2036,6 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; @@ -2074,8 +2052,6 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { *
          * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
          * A key/value object to store custom metadata about a task.
    -     * Optional metadata for extensions. The key is an extension-specific
    -     * identifier.
          * 
    * * .google.protobuf.Struct metadata = 6; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java index 3cbc60d1a..08be9ee0c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEvent.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -24,10 +24,10 @@ public final class TaskArtifactUpdateEvent extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - TaskArtifactUpdateEvent.class.getName()); + "TaskArtifactUpdateEvent"); } // Use TaskArtifactUpdateEvent.newBuilder() to construct. private TaskArtifactUpdateEvent(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java index 558068b71..97b99f5d1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskArtifactUpdateEventOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java index 097c353a5..2c8648576 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -178,8 +178,6 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
        * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
        * A key/value object to store custom metadata about a task.
    -   * Optional metadata for extensions. The key is an extension-specific
    -   * identifier.
        * 
    * * .google.protobuf.Struct metadata = 6; @@ -190,8 +188,6 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
        * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
        * A key/value object to store custom metadata about a task.
    -   * Optional metadata for extensions. The key is an extension-specific
    -   * identifier.
        * 
    * * .google.protobuf.Struct metadata = 6; @@ -202,8 +198,6 @@ io.a2a.grpc.MessageOrBuilder getHistoryOrBuilder( *
        * protolint:enable REPEATED_FIELD_NAMES_PLURALIZED
        * A key/value object to store custom metadata about a task.
    -   * Optional metadata for extensions. The key is an extension-specific
    -   * identifier.
        * 
    * * .google.protobuf.Struct metadata = 6; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java index b91bb6d7d..e33fb5cf4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -24,10 +24,10 @@ public final class TaskPushNotificationConfig extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - TaskPushNotificationConfig.class.getName()); + "TaskPushNotificationConfig"); } // Use TaskPushNotificationConfig.newBuilder() to construct. private TaskPushNotificationConfig(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java index 45f7d66b0..741c9b4cf 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java index 4b0f9cb27..c5f4bccd6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java @@ -1,14 +1,14 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; /** *
      * --8<-- [start:TaskState]
    - * The set of states a Task can be in.
    + * Defines the possible lifecycle states of a Task.
      * 
    * * Protobuf enum {@code a2a.v1.TaskState} @@ -17,12 +17,16 @@ public enum TaskState implements com.google.protobuf.ProtocolMessageEnum { /** + *
    +   * The task is in an unknown or indeterminate state.
    +   * 
    + * * TASK_STATE_UNSPECIFIED = 0; */ TASK_STATE_UNSPECIFIED(0), /** *
    -   * Represents the status that acknowledges a task is created
    +   * Represents the status that acknowledges a task is created.
        * 
    * * TASK_STATE_SUBMITTED = 1; @@ -30,7 +34,7 @@ public enum TaskState TASK_STATE_SUBMITTED(1), /** *
    -   * Represents the status that a task is actively being processed
    +   * Represents the status that a task is actively being processed.
        * 
    * * TASK_STATE_WORKING = 2; @@ -38,7 +42,7 @@ public enum TaskState TASK_STATE_WORKING(2), /** *
    -   * Represents the status a task is finished. This is a terminal state
    +   * Represents the status a task is finished. This is a terminal state.
        * 
    * * TASK_STATE_COMPLETED = 3; @@ -46,7 +50,7 @@ public enum TaskState TASK_STATE_COMPLETED(3), /** *
    -   * Represents the status a task is done but failed. This is a terminal state
    +   * Represents the status a task is done but failed. This is a terminal state.
        * 
    * * TASK_STATE_FAILED = 4; @@ -97,18 +101,22 @@ public enum TaskState com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - TaskState.class.getName()); + "TaskState"); } /** + *
    +   * The task is in an unknown or indeterminate state.
    +   * 
    + * * TASK_STATE_UNSPECIFIED = 0; */ public static final int TASK_STATE_UNSPECIFIED_VALUE = 0; /** *
    -   * Represents the status that acknowledges a task is created
    +   * Represents the status that acknowledges a task is created.
        * 
    * * TASK_STATE_SUBMITTED = 1; @@ -116,7 +124,7 @@ public enum TaskState public static final int TASK_STATE_SUBMITTED_VALUE = 1; /** *
    -   * Represents the status that a task is actively being processed
    +   * Represents the status that a task is actively being processed.
        * 
    * * TASK_STATE_WORKING = 2; @@ -124,7 +132,7 @@ public enum TaskState public static final int TASK_STATE_WORKING_VALUE = 2; /** *
    -   * Represents the status a task is finished. This is a terminal state
    +   * Represents the status a task is finished. This is a terminal state.
        * 
    * * TASK_STATE_COMPLETED = 3; @@ -132,7 +140,7 @@ public enum TaskState public static final int TASK_STATE_COMPLETED_VALUE = 3; /** *
    -   * Represents the status a task is done but failed. This is a terminal state
    +   * Represents the status a task is done but failed. This is a terminal state.
        * 
    * * TASK_STATE_FAILED = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java index b27f65903..d435fbb50 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatus.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -23,10 +23,10 @@ public final class TaskStatus extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - TaskStatus.class.getName()); + "TaskStatus"); } // Use TaskStatus.newBuilder() to construct. private TaskStatus(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java index bf75690b7..fc9d5434b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java index 97448c339..38b6b4768 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; @@ -24,10 +24,10 @@ public final class TaskStatusUpdateEvent extends com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, /* major= */ 4, - /* minor= */ 31, + /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - TaskStatusUpdateEvent.class.getName()); + "TaskStatusUpdateEvent"); } // Use TaskStatusUpdateEvent.newBuilder() to construct. private TaskStatusUpdateEvent(com.google.protobuf.GeneratedMessage.Builder builder) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java index 56d479da1..6e086c3f3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java @@ -1,7 +1,7 @@ // Generated by the protocol buffer compiler. DO NOT EDIT! // NO CHECKED-IN PROTOBUF GENCODE // source: a2a.proto -// Protobuf Java Version: 4.31.1 +// Protobuf Java Version: 4.33.1 package io.a2a.grpc; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java index ce0f38ff6..9ce364c0a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java @@ -33,7 +33,6 @@ public interface DataPartMapper { * Uses factory method to construct DataPart with single-arg constructor. * Metadata is ignored (not part of proto definition). */ - @Mapping(target = "metadata", ignore = true) @Mapping(target = "data", source = "data", qualifiedByName = "structToMap") io.a2a.spec.DataPart fromProto(io.a2a.grpc.DataPart proto); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java index 8d1fa9df5..cd5c5980e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java @@ -23,7 +23,6 @@ public interface DeleteTaskPushNotificationConfigParamsMapper { @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName())[0])") @Mapping(target = "pushNotificationConfigId", expression = "java(ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName())[1])") - @Mapping(target = "metadata", ignore = true) DeleteTaskPushNotificationConfigParams fromProto(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java index 80cb4e239..b9bf656b1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java @@ -26,7 +26,6 @@ public interface GetTaskPushNotificationConfigParamsMapper { @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.parseGetTaskPushNotificationConfigName(proto.getName())[0])") @Mapping(target = "pushNotificationConfigId", expression = "java(ResourceNameParser.parseGetTaskPushNotificationConfigName(proto.getName())[1])") - @Mapping(target = "metadata", ignore = true) GetTaskPushNotificationConfigParams fromProto(io.a2a.grpc.GetTaskPushNotificationConfigRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java index faf6c6194..d6aa3a9af 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java @@ -22,7 +22,6 @@ public interface ListTaskPushNotificationConfigParamsMapper { */ @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractParentId(proto.getParent()))") - @Mapping(target = "metadata", ignore = true) ListTaskPushNotificationConfigParams fromProto(io.a2a.grpc.ListTaskPushNotificationConfigRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java index fe43a95f3..05d45d68a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java @@ -28,7 +28,6 @@ public interface ListTasksParamsMapper { @Mapping(target = "historyLength", source = "historyLength", conditionExpression = "java(params.historyLength() != null)") @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "instantToMillis") @Mapping(target = "includeArtifacts", source = "includeArtifacts", conditionExpression = "java(params.includeArtifacts() != null)") - @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") ListTasksRequest toProto(io.a2a.spec.ListTasksParams params); /** @@ -44,6 +43,5 @@ public interface ListTasksParamsMapper { @Mapping(target = "historyLength", source = "historyLength", qualifiedByName = "zeroToNull") @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "millisToInstant") @Mapping(target = "includeArtifacts", source = "includeArtifacts", qualifiedByName = "falseToNull") - @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") io.a2a.spec.ListTasksParams fromProto(ListTasksRequest request); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java index 2d0127e21..2e0215bc1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java @@ -30,6 +30,7 @@ public interface SetTaskPushNotificationConfigMapper { *

    * Extracts taskId from parent resource name and maps PushNotificationConfig with * ID override from config_id field. + * The tenant field is set to null as it's not present in the protobuf definition. * * @param request the protobuf SetTaskPushNotificationConfigRequest * @return domain TaskPushNotificationConfig diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java index b2edab0bd..eb43bdc22 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java @@ -22,7 +22,6 @@ public interface TaskIdParamsMapper { */ @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") - @Mapping(target = "metadata", ignore = true) TaskIdParams fromProtoCancelTaskRequest(io.a2a.grpc.CancelTaskRequest proto); /** @@ -40,7 +39,6 @@ public interface TaskIdParamsMapper { */ @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") - @Mapping(target = "metadata", ignore = true) TaskIdParams fromProtoSubscribeToTaskRequest(io.a2a.grpc.SubscribeToTaskRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java index 5978b7419..1289938e7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskPushNotificationConfigMapper.java @@ -41,6 +41,7 @@ public interface TaskPushNotificationConfigMapper { *

    * Parses the resource name to extract taskId and configId, then creates the domain object. * The configId is injected into the PushNotificationConfig if it differs from what's in the proto. + * The tenant field is set to null as it's not present in the protobuf definition. * * @param proto the protobuf TaskPushNotificationConfig * @return domain TaskPushNotificationConfig with extracted taskId and configId @@ -48,6 +49,7 @@ public interface TaskPushNotificationConfigMapper { */ @Mapping(target = "taskId", expression = "java(extractTaskId(proto))") @Mapping(target = "pushNotificationConfig", expression = "java(mapPushNotificationConfigWithId(proto))") + @Mapping(target = "tenant", expression = "java(null)") TaskPushNotificationConfig fromProto(io.a2a.grpc.TaskPushNotificationConfig proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java index 0d91a538e..46c8994c5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java @@ -23,7 +23,6 @@ public interface TaskQueryParamsMapper { @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") @Mapping(target = "historyLength", source = "historyLength") - @Mapping(target = "metadata", ignore = true) TaskQueryParams fromProto(io.a2a.grpc.GetTaskRequest proto); @BeanMapping(builder = @Builder(buildMethod = "build")) diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index e5987813c..7243ff40a 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package a2a.v1; -//From commit b0784199543eebf2e95dcb02e9336cb213923506 +//From commit c196824396bb4af4c595f30e2c503a5ab1dbac4b import "google/api/annotations.proto"; import "google/api/client.proto"; @@ -22,83 +22,115 @@ service A2AService { // Send a message to the agent. rpc SendMessage(SendMessageRequest) returns (SendMessageResponse) { option (google.api.http) = { - post: "/v1/message:send" + post: "/message:send" body: "*" + additional_bindings: { + post: "/{tenant}/message:send" + body: "*" + } }; } // SendStreamingMessage is a streaming version of SendMessage. rpc SendStreamingMessage(SendMessageRequest) returns (stream StreamResponse) { option (google.api.http) = { - post: "/v1/message:stream" + post: "/message:stream" body: "*" + additional_bindings: { + post: "/{tenant}/message:stream" + body: "*" + } }; } // Get the current state of a task from the agent. rpc GetTask(GetTaskRequest) returns (Task) { option (google.api.http) = { - get: "/v1/{name=tasks/*}" + get: "/{name=tasks/*}" + additional_bindings: { + get: "/{tenant}/{name=tasks/*}" + } }; option (google.api.method_signature) = "name"; } // List tasks with optional filtering and pagination. rpc ListTasks(ListTasksRequest) returns (ListTasksResponse) { option (google.api.http) = { - get: "/v1/tasks" + get: "/tasks" + additional_bindings: { + get: "/{tenant}/tasks" + } }; } // Cancel a task. rpc CancelTask(CancelTaskRequest) returns (Task) { option (google.api.http) = { - post: "/v1/{name=tasks/*}:cancel" + post: "/{name=tasks/*}:cancel" body: "*" + additional_bindings: { + post: "/{tenant}/{name=tasks/*}:cancel" + body: "*" + } }; } // SubscribeToTask allows subscribing to task updates for tasks not in terminal state. // Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected). - rpc SubscribeToTask(SubscribeToTaskRequest) - returns (stream StreamResponse) { + rpc SubscribeToTask(SubscribeToTaskRequest) returns (stream StreamResponse) { option (google.api.http) = { - get: "/v1/{name=tasks/*}:subscribe" + get: "/{name=tasks/*}:subscribe" + additional_bindings: { + get: "/{tenant}/{name=tasks/*}:subscribe" + } }; } // Set a push notification config for a task. - rpc SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest) - returns (TaskPushNotificationConfig) { + rpc SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig) { option (google.api.http) = { - post: "/v1/{parent=tasks/*/pushNotificationConfigs}" + post: "/{parent=tasks/*/pushNotificationConfigs}" body: "config" + additional_bindings: { + post: "/{tenant}/{parent=tasks/*/pushNotificationConfigs}" + body: "config" + } }; option (google.api.method_signature) = "parent,config"; } // Get a push notification config for a task. - rpc GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest) - returns (TaskPushNotificationConfig) { + rpc GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig) { option (google.api.http) = { - get: "/v1/{name=tasks/*/pushNotificationConfigs/*}" + get: "/{name=tasks/*/pushNotificationConfigs/*}" + additional_bindings: { + get: "/{tenant}/{name=tasks/*/pushNotificationConfigs/*}" + } }; option (google.api.method_signature) = "name"; } // Get a list of push notifications configured for a task. - rpc ListTaskPushNotificationConfig(ListTaskPushNotificationConfigRequest) - returns (ListTaskPushNotificationConfigResponse) { + rpc ListTaskPushNotificationConfig(ListTaskPushNotificationConfigRequest) returns (ListTaskPushNotificationConfigResponse) { option (google.api.http) = { - get: "/v1/{parent=tasks/*}/pushNotificationConfigs" + get: "/{parent=tasks/*}/pushNotificationConfigs" + additional_bindings: { + get: "/{tenant}/{parent=tasks/*}/pushNotificationConfigs" + } }; option (google.api.method_signature) = "parent"; } // GetExtendedAgentCard returns the extended agent card for authenticated agents. rpc GetExtendedAgentCard(GetExtendedAgentCardRequest) returns (AgentCard) { option (google.api.http) = { - get: "/v1/extendedAgentCard" + get: "/extendedAgentCard" + additional_bindings: { + get: "/{tenant}/extendedAgentCard" + } }; } // Delete a push notification config for a task. - rpc DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest) - returns (google.protobuf.Empty) { + rpc DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest) returns (google.protobuf.Empty) { option (google.api.http) = { - delete: "/v1/{name=tasks/*/pushNotificationConfigs/*}" + delete: "/{name=tasks/*/pushNotificationConfigs/*}" + additional_bindings: { + delete: "/{tenant}/{name=tasks/*/pushNotificationConfigs/*}" + } }; option (google.api.method_signature) = "name"; } @@ -109,13 +141,13 @@ service A2AService { // --8<-- [start:SendMessageConfiguration] // Configuration of a send message request. message SendMessageConfiguration { - // The output modes that the agent is expected to respond with. + // A list of media types the client is prepared to accept for response parts. Agents SHOULD use this to tailor their output. repeated string accepted_output_modes = 1; - // A configuration of a webhook that can be used to receive updates + // Configuration for the agent to send push notifications for task updates. PushNotificationConfig push_notification_config = 2; // The maximum number of messages to include in the history. optional int32 history_length = 3; - // If true, the message will be blocking until the task is completed. + // If true, the operation waits until the task reaches a terminal state before returning. Default is false. bool blocking = 4; } // --8<-- [end:SendMessageConfiguration] @@ -141,23 +173,22 @@ message Task { repeated Message history = 5; // protolint:enable REPEATED_FIELD_NAMES_PLURALIZED // A key/value object to store custom metadata about a task. - // Optional metadata for extensions. The key is an extension-specific - // identifier. google.protobuf.Struct metadata = 6; } // --8<-- [end:Task] // --8<-- [start:TaskState] -// The set of states a Task can be in. +// Defines the possible lifecycle states of a Task. enum TaskState { + // The task is in an unknown or indeterminate state. TASK_STATE_UNSPECIFIED = 0; - // Represents the status that acknowledges a task is created + // Represents the status that acknowledges a task is created. TASK_STATE_SUBMITTED = 1; - // Represents the status that a task is actively being processed + // Represents the status that a task is actively being processed. TASK_STATE_WORKING = 2; - // Represents the status a task is finished. This is a terminal state + // Represents the status a task is finished. This is a terminal state. TASK_STATE_COMPLETED = 3; - // Represents the status a task is done but failed. This is a terminal state + // Represents the status a task is done but failed. This is a terminal state. TASK_STATE_FAILED = 4; // Represents the status a task was cancelled before it finished. // This is a terminal state. @@ -227,9 +258,9 @@ message FilePart { // --8<-- [end:FilePart] // --8<-- [start:DataPart] -// DataPart represents a structured blob. This is most commonly a JSON payload. +// DataPart represents a structured blob. message DataPart { - // The structured data content. + // A JSON object containing arbitrary data. google.protobuf.Struct data = 1 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:DataPart] @@ -288,7 +319,7 @@ message Artifact { string name = 3; // A human readable description of the artifact, optional. string description = 4; - // The content of the artifact. + // The content of the artifact. Must contain at least one part. repeated Part parts = 5 [(google.api.field_behavior) = REQUIRED]; // Optional metadata included with the artifact. google.protobuf.Struct metadata = 6; @@ -367,9 +398,10 @@ message AgentInterface { string url = 1 [(google.api.field_behavior) = REQUIRED]; // The protocol binding supported at this URL. This is an open form string, to be // easily extended for other protocol bindings. The core ones officially - // supported are JSONRPC, GRPC and HTTP+JSON. - // Example: "JSONRPC", "GRPC", "HTTP+JSON" + // supported are `JSONRPC`, `GRPC` and `HTTP+JSON`. string protocol_binding = 2 [(google.api.field_behavior) = REQUIRED]; + // Tenant to be set in the request when calling the agent. + string tenant = 3; } // --8<-- [end:AgentInterface] @@ -377,11 +409,6 @@ message AgentInterface { // AgentCard is a self-describing manifest for an agent. It provides essential // metadata including the agent's identity, capabilities, skills, supported // communication methods, and security requirements. -// AgentCard conveys key information: -// - Overall details (version, name, description, uses) -// - Skills; a set of actions/solutions the agent can perform -// - Default modalities/content types supported by the agent. -// - Authentication requirements // Next ID: 20 message AgentCard { // The version of the A2A protocol this agent supports. @@ -422,12 +449,12 @@ message AgentCard { repeated string default_input_modes = 10 [(google.api.field_behavior) = REQUIRED]; // The media types supported as outputs from this agent. repeated string default_output_modes = 11 [(google.api.field_behavior) = REQUIRED]; - // Skills represent a unit of ability an agent can perform. This may - // somewhat abstract but represents a more focused set of actions that the - // agent is highly likely to succeed at. + // Skills represent an ability of an agent. It is largely + // a descriptive concept but represents a more focused set of behaviors that the + // agent is likely to succeed at. repeated AgentSkill skills = 12 [(google.api.field_behavior) = REQUIRED]; // Whether the agent supports providing an extended agent card when authenticated. - optional bool supports_authenticated_extended_card = 13; + optional bool supports_extended_agent_card = 13; // JSON Web Signatures computed for this AgentCard. repeated AgentCardSignature signatures = 17; // An optional URL to an icon for the agent. @@ -541,10 +568,15 @@ message Security { // See: https://spec.openapis.org/oas/v3.2.0.html#security-scheme-object message SecurityScheme { oneof scheme { + // API key-based authentication. APIKeySecurityScheme api_key_security_scheme = 1; + // HTTP authentication (Basic, Bearer, etc.). HTTPAuthSecurityScheme http_auth_security_scheme = 2; + // OAuth 2.0 authentication. OAuth2SecurityScheme oauth2_security_scheme = 3; + // OpenID Connect authentication. OpenIdConnectSecurityScheme open_id_connect_security_scheme = 4; + // Mutual TLS authentication. MutualTlsSecurityScheme mtls_security_scheme = 5; } } @@ -679,12 +711,16 @@ message PasswordOAuthFlow { // --8<-- [start:SendMessageRequest] // Represents a request for the `message/send` method. message SendMessageRequest { + // Optional tenant, provided as a path parameter. + string tenant = 4; // The message to send to the agent. - Message request = 1 - [(google.api.field_behavior) = REQUIRED, json_name = "message"]; + Message request = 1 [ + (google.api.field_behavior) = REQUIRED, + json_name = "message" + ]; // Configuration for the send request. SendMessageConfiguration configuration = 2; - // Optional metadata for the request. + // A flexible key-value map for passing additional context or parameters. google.protobuf.Struct metadata = 3; } // --8<-- [end:SendMessageRequest] @@ -692,6 +728,8 @@ message SendMessageRequest { // --8<-- [start:GetTaskRequest] // Represents a request for the `tasks/get` method. message GetTaskRequest { + // Optional tenant, provided as a path parameter. + string tenant = 3; // The resource name of the task. // Format: tasks/{task_id} string name = 1 [(google.api.field_behavior) = REQUIRED]; @@ -703,6 +741,8 @@ message GetTaskRequest { // --8<-- [start:ListTasksRequest] // Parameters for listing tasks with optional filtering criteria. message ListTasksRequest { + // Optional tenant, provided as a path parameter. + string tenant = 9; // Filter tasks by context ID to get tasks from a specific conversation or session. string context_id = 1; // Filter tasks by their current status state. @@ -720,8 +760,6 @@ message ListTasksRequest { // Whether to include artifacts in the returned tasks. // Defaults to false to reduce payload size. optional bool include_artifacts = 7; - // Request-specific metadata. - google.protobuf.Struct metadata = 8; } // --8<-- [end:ListTasksRequest] @@ -742,6 +780,8 @@ message ListTasksResponse { // --8<-- [start:CancelTaskRequest] // Represents a request for the `tasks/cancel` method. message CancelTaskRequest { + // Optional tenant, provided as a path parameter. + string tenant = 2; // The resource name of the task to cancel. // Format: tasks/{task_id} string name = 1; @@ -750,6 +790,8 @@ message CancelTaskRequest { // --8<-- [start:GetTaskPushNotificationConfigRequest] message GetTaskPushNotificationConfigRequest { + // Optional tenant, provided as a path parameter. + string tenant = 2; // The resource name of the config to retrieve. // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} string name = 1; @@ -759,6 +801,8 @@ message GetTaskPushNotificationConfigRequest { // --8<-- [start:DeleteTaskPushNotificationConfigRequest] // Represents a request for the `tasks/pushNotificationConfig/delete` method. message DeleteTaskPushNotificationConfigRequest { + // Optional tenant, provided as a path parameter. + string tenant = 2; // The resource name of the config to delete. // Format: tasks/{task_id}/pushNotificationConfigs/{config_id} string name = 1; @@ -768,19 +812,22 @@ message DeleteTaskPushNotificationConfigRequest { // --8<-- [start:SetTaskPushNotificationConfigRequest] // Represents a request for the `tasks/pushNotificationConfig/set` method. message SetTaskPushNotificationConfigRequest { + // Optional tenant, provided as a path parameter. + string tenant = 4; // The parent task resource for this config. // Format: tasks/{task_id} string parent = 1 [(google.api.field_behavior) = REQUIRED]; // The ID for the new config. string config_id = 2 [(google.api.field_behavior) = REQUIRED]; // The configuration to create. - TaskPushNotificationConfig config = 3 - [(google.api.field_behavior) = REQUIRED]; + TaskPushNotificationConfig config = 3 [(google.api.field_behavior) = REQUIRED]; } // --8<-- [end:SetTaskPushNotificationConfigRequest] // --8<-- [start:SubscribeToTaskRequest] message SubscribeToTaskRequest { + // Optional tenant, provided as a path parameter. + string tenant = 2; // The resource name of the task to subscribe to. // Format: tasks/{task_id} string name = 1; @@ -789,6 +836,8 @@ message SubscribeToTaskRequest { // --8<-- [start:ListTaskPushNotificationConfigRequest] message ListTaskPushNotificationConfigRequest { + // Optional tenant, provided as a path parameter. + string tenant = 4; // The parent task resource. // Format: tasks/{task_id} string parent = 1; @@ -802,7 +851,8 @@ message ListTaskPushNotificationConfigRequest { // --8<-- [start:GetExtendedAgentCardRequest] message GetExtendedAgentCardRequest { - // Empty. Added to fix linter violation. + // Optional tenant, provided as a path parameter. + string tenant = 1; } // --8<-- [end:GetExtendedAgentCardRequest] @@ -817,12 +867,16 @@ message SendMessageResponse { // --8<-- [end:SendMessageResponse] // --8<-- [start:StreamResponse] -// Response message containing task updates or messages. +// A wrapper object used in streaming operations to encapsulate different types of response data. message StreamResponse { oneof payload { + // A Task object containing the current state of the task. Task task = 1; + // A Message object containing a message from the agent. Message msg = 2 [json_name = "message"]; + // An event indicating a task status update. TaskStatusUpdateEvent status_update = 3; + // An event indicating a task artifact update. TaskArtifactUpdateEvent artifact_update = 4; } } diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java index b314615ab..ffb048b28 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -33,6 +33,7 @@ public void testParseSetTaskPushNotificationConfigRequest_ValidProtoFormat() thr "params": { "parent": "tasks/task-123", "configId": "config-456", + "tenant": "", "config": { "name": "tasks/task-123/pushNotificationConfigs/config-456", "pushNotificationConfig": { @@ -265,7 +266,8 @@ public void testGenerateSetTaskPushNotificationConfigResponse_Success() throws E PushNotificationConfig.builder() .url("https://example.com/callback") .id("config-456") - .build() + .build(), + "tenant" ); String responseJson = """ diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index 12588be66..be5d358b2 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -47,7 +47,7 @@ public void convertAgentCard() { AgentCard agentCard = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") - .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) + .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999", ""))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(AgentCapabilities.builder() @@ -82,7 +82,7 @@ public void convertAgentCard() { agentCard = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") - .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999"))) + .supportedInterfaces(Collections.singletonList(new AgentInterface("jsonrpc", "http://localhost:9999", ""))) .version("1.0.0") .documentationUrl("http://example.com/docs") .capabilities(AgentCapabilities.builder() @@ -206,7 +206,7 @@ public void convertTaskPushNotificationConfig() { PushNotificationConfig.builder() .url("http://example.com") .id("xyz") - .build()); + .build(), null); io.a2a.grpc.TaskPushNotificationConfig result = ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig); assertEquals("tasks/push-task-123/pushNotificationConfigs/xyz", result.getName()); assertNotNull(result.getPushNotificationConfig()); @@ -220,7 +220,7 @@ public void convertTaskPushNotificationConfig() { .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), "credentials")) .url("http://example.com") .id("xyz") - .build()); + .build(), null); result = ProtoUtils.ToProto.taskPushNotificationConfig(taskPushConfig); assertEquals("tasks/push-task-123/pushNotificationConfigs/xyz", result.getName()); assertNotNull(result.getPushNotificationConfig()); diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index 573aa8c7a..31af2466c 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -28,7 +28,7 @@ * @param defaultInputModes list of supported input modes, e.g., "text", "audio" (required) * @param defaultOutputModes list of supported output modes, e.g., "text", "audio" (required) * @param skills list of skills that this agent can perform (required) - * @param supportsAuthenticatedExtendedCard whether the agent supports authenticated extended card retrieval (optional, defaults to false) + * @param supportsExtendedAgentCard whether the agent supports authenticated extended card retrieval (optional, defaults to false) * @param securitySchemes map of security scheme names to their definitions (optional) * @param security list of security requirements for accessing the agent (optional) * @param iconUrl URL to an icon representing the agent (optional) @@ -47,7 +47,7 @@ public record AgentCard( List defaultInputModes, List defaultOutputModes, List skills, - boolean supportsAuthenticatedExtendedCard, + boolean supportsExtendedAgentCard, Map securitySchemes, List>> security, String iconUrl, @@ -152,7 +152,7 @@ public static class Builder { private List defaultInputModes; private List defaultOutputModes; private List skills; - private boolean supportsAuthenticatedExtendedCard = false; + private boolean supportsExtendedAgentCard = false; private Map securitySchemes; private List>> security; private String iconUrl; @@ -185,7 +185,7 @@ private Builder(AgentCard card) { this.defaultInputModes = card.defaultInputModes != null ? new ArrayList<>(card.defaultInputModes) : null; this.defaultOutputModes = card.defaultOutputModes != null ? new ArrayList<>(card.defaultOutputModes) : null; this.skills = card.skills != null ? new ArrayList<>(card.skills) : null; - this.supportsAuthenticatedExtendedCard = card.supportsAuthenticatedExtendedCard; + this.supportsExtendedAgentCard = card.supportsExtendedAgentCard; this.securitySchemes = card.securitySchemes != null ? Map.copyOf(card.securitySchemes) : null; this.security = card.security != null ? new ArrayList<>(card.security) : null; this.iconUrl = card.iconUrl; @@ -307,17 +307,17 @@ public Builder skills(List skills) { } /** - * Sets whether the agent supports authenticated extended card retrieval. + * Sets whether the agent supports extended card retrieval. *

    * When true, the agent can provide additional information through the * {@code GetAuthenticatedExtendedCard} method, which may include private * or user-specific details not in the public card. * - * @param supportsAuthenticatedExtendedCard true if supported, false otherwise + * @param supportsExtendedAgentCard true if supported, false otherwise * @return this builder for method chaining */ - public Builder supportsAuthenticatedExtendedCard(boolean supportsAuthenticatedExtendedCard) { - this.supportsAuthenticatedExtendedCard = supportsAuthenticatedExtendedCard; + public Builder supportsExtendedAgentCard(boolean supportsExtendedAgentCard) { + this.supportsExtendedAgentCard = supportsExtendedAgentCard; return this; } @@ -414,7 +414,7 @@ public Builder signatures(List signatures) { public AgentCard build() { return new AgentCard(name, description, provider, version, documentationUrl, capabilities, defaultInputModes, defaultOutputModes, skills, - supportsAuthenticatedExtendedCard, securitySchemes, security, iconUrl, + supportsExtendedAgentCard, securitySchemes, security, iconUrl, supportedInterfaces, protocolVersion, signatures); } } diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index d035111c5..58d65a8da 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -17,20 +17,27 @@ * This class is immutable. * * @param protocolBinding the protocol binding supported at this URL (e.g., "JSONRPC", "GRPC", "HTTP+JSON") (required) - * @param url the endpoint URL where this interface is available; must be a valid absolute HTTPS URL in production (required) + * @param url the endpoint URL where this interface is available; must be a valid absolute HTTPS URL in production + * (required) + * @param tenant the tenant to be set in the request when calling the agent. * @see AgentCard * @see TransportProtocol * @see A2A Protocol Specification */ -public record AgentInterface(String protocolBinding, String url) { +public record AgentInterface(String protocolBinding, String url, String tenant) { /** * Compact constructor that validates required fields. * * @throws IllegalArgumentException if protocolBinding or url is null */ - public AgentInterface { + public AgentInterface { Assert.checkNotNullParam("protocolBinding", protocolBinding); Assert.checkNotNullParam("url", url); + Assert.checkNotNullParam("tenant", tenant); + } + + public AgentInterface(String protocolBinding, String url) { + this(protocolBinding, url, ""); } } diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 3f5f1e9bf..007c7c2fd 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -40,17 +40,11 @@ public class DataPart extends Part> { /** The type identifier for data parts in messages and artifacts. */ public static final String DATA = "data"; private final Map data; - private final Map metadata; private final Kind kind; public DataPart(Map data) { - this(data, null); - } - - public DataPart(Map data, Map metadata) { Assert.checkNotNullParam("data", data); this.data = data; - this.metadata = metadata; this.kind = Kind.DATA; } @@ -67,10 +61,4 @@ public Kind getKind() { public Map getData() { return data; } - - @Override - public Map getMetadata() { - return metadata; - } - } diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index bd4482666..9205275d5 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -1,6 +1,5 @@ package io.a2a.spec; -import java.util.Map; import io.a2a.util.Assert; @@ -13,11 +12,11 @@ * * @param id the task identifier (required) * @param pushNotificationConfigId the specific configuration ID to delete (required) - * @param metadata optional arbitrary key-value metadata for the request + * @param tenant optional tenant, provided as a path parameter. * @see DeleteTaskPushNotificationConfigRequest for the request using these parameters * @see A2A Protocol Specification */ -public record DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, Map metadata) { +public record DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, String tenant) { /** * Compact constructor that validates required fields. @@ -27,6 +26,7 @@ public record DeleteTaskPushNotificationConfigParams(String id, String pushNotif public DeleteTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("pushNotificationConfigId", pushNotificationConfigId); + Assert.checkNotNullParam("tenant", tenant); } /** @@ -37,7 +37,7 @@ public record DeleteTaskPushNotificationConfigParams(String id, String pushNotif * @throws IllegalArgumentException if id or pushNotificationConfigId is null */ public DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId) { - this(id, pushNotificationConfigId, null); + this(id, pushNotificationConfigId, ""); } /** @@ -57,7 +57,7 @@ public static Builder builder() { public static class Builder { String id; String pushNotificationConfigId; - Map metadata; + String tenant; /** * Creates a new Builder with all fields unset. @@ -88,13 +88,13 @@ public Builder pushNotificationConfigId(String pushNotificationConfigId) { } /** - * Sets optional metadata for the request. + * Sets optional tenant for the request. * - * @param metadata arbitrary key-value metadata (optional) + * @param tenant arbitrary tenant (optional) * @return this builder for method chaining */ - public Builder metadata(Map metadata) { - this.metadata = metadata; + public Builder tenant(String tenant) { + this.tenant = tenant; return this; } @@ -105,7 +105,7 @@ public Builder metadata(Map metadata) { * @throws IllegalArgumentException if id or pushNotificationConfigId is null */ public DeleteTaskPushNotificationConfigParams build() { - return new DeleteTaskPushNotificationConfigParams(id, pushNotificationConfigId, metadata); + return new DeleteTaskPushNotificationConfigParams(id, pushNotificationConfigId, tenant); } } } diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 70bd0b7f1..0661706d4 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -1,10 +1,8 @@ package io.a2a.spec; -import java.util.Map; import io.a2a.util.Assert; -import static io.a2a.spec.FilePart.FILE; /** * Represents a file content part within a {@link Message} or {@link Artifact}. @@ -42,17 +40,12 @@ public class FilePart extends Part { /** The type identifier for file parts in messages and artifacts. */ public static final String FILE = "file"; private final FileContent file; - private final Map metadata; private final Kind kind; - public FilePart(FileContent file) { - this(file, null); - } - public FilePart(FileContent file, Map metadata) { + public FilePart(FileContent file) { Assert.checkNotNullParam("file", file); this.file = file; - this.metadata = metadata; this.kind = Kind.FILE; } @@ -65,9 +58,4 @@ public FileContent getFile() { return file; } - @Override - public Map getMetadata() { - return metadata; - } - } \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java index 7a942a6a0..4e1ffc1a0 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -15,7 +15,7 @@ * *

    * The agent must support authenticated extended cards (indicated by - * {@link AgentCard#supportsAuthenticatedExtendedCard()}) and the client must provide + * {@link AgentCard#supportsExtendedAgentCard()}) and the client must provide * valid authentication credentials for this request to succeed. *

    * This class implements the JSON-RPC {@code agent/getAuthenticatedExtendedCard} method diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index aa40e5343..373e89b17 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -1,6 +1,5 @@ package io.a2a.spec; -import java.util.Map; import io.a2a.util.Assert; @@ -14,23 +13,24 @@ * * @param id the task identifier (required) * @param pushNotificationConfigId optional specific configuration ID to retrieve - * @param metadata optional arbitrary key-value metadata for the request + * @param tenant optional tenant, provided as a path parameter. * @see GetTaskPushNotificationConfigRequest for the request using these parameters * @see TaskPushNotificationConfig for the returned configuration structure * @see A2A Protocol Specification */ -public record GetTaskPushNotificationConfigParams(String id, @Nullable String pushNotificationConfigId, @Nullable Map metadata) { +public record GetTaskPushNotificationConfigParams(String id, @Nullable String pushNotificationConfigId, String tenant) { public GetTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); + Assert.checkNotNullParam("tenant", tenant); } public GetTaskPushNotificationConfigParams(String id) { - this(id, null, null); + this(id, null, ""); } public GetTaskPushNotificationConfigParams(String id, String pushNotificationConfigId) { - this(id, pushNotificationConfigId, null); + this(id, pushNotificationConfigId, ""); } /** @@ -45,7 +45,7 @@ public static Builder builder() { public static class Builder { String id; String pushNotificationConfigId; - Map metadata; + String tenant; /** * Creates a new Builder with all fields unset. @@ -63,13 +63,13 @@ public Builder pushNotificationConfigId(String pushNotificationConfigId) { return this; } - public Builder metadata(Map metadata) { - this.metadata = metadata; + public Builder tenant(String tenant) { + this.tenant = tenant; return this; } public GetTaskPushNotificationConfigParams build() { - return new GetTaskPushNotificationConfigParams(id, pushNotificationConfigId, metadata); + return new GetTaskPushNotificationConfigParams(id, pushNotificationConfigId, tenant == null ? "" : tenant); } } } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index 1f12f9f84..564b2a13d 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -1,6 +1,5 @@ package io.a2a.spec; -import java.util.Map; import io.a2a.util.Assert; @@ -11,18 +10,19 @@ * all configured notification endpoints for that task. * * @param id the task identifier (required) - * @param metadata optional arbitrary key-value metadata for the request + * @param tenant optional tenant, provided as a path parameter. * @see ListTaskPushNotificationConfigRequest for the request using these parameters * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -public record ListTaskPushNotificationConfigParams(String id, Map metadata) { +public record ListTaskPushNotificationConfigParams(String id, String tenant) { public ListTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); + Assert.checkNotNullParam("tenant", tenant); } public ListTaskPushNotificationConfigParams(String id) { - this(id, null); + this(id, ""); } } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index 62ca74e96..1a40e87fc 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -1,9 +1,9 @@ package io.a2a.spec; +import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; import java.time.Instant; -import java.util.Map; /** * Parameters for listing tasks with optional filtering and pagination. @@ -15,7 +15,7 @@ * @param historyLength Number of recent messages to include in each task's history (defaults to 0) * @param lastUpdatedAfter Filter tasks updated after this timestamp * @param includeArtifacts Whether to include artifacts in the returned tasks (defaults to false) - * @param metadata Additional filter properties + * @param tenant optional tenant, provided as a path parameter. */ public record ListTasksParams( @Nullable String contextId, @@ -25,13 +25,16 @@ public record ListTasksParams( @Nullable Integer historyLength, @Nullable Instant lastUpdatedAfter, @Nullable Boolean includeArtifacts, - @Nullable Map metadata + String tenant ) { + public ListTasksParams { + Assert.checkNotNullParam("tenant", tenant); + } /** * Default constructor for listing all tasks. */ public ListTasksParams() { - this(null, null, null, null, null, null, null, null); + this(null, null, null, null, null, null, null, ""); } /** @@ -41,7 +44,7 @@ public ListTasksParams() { * @param pageToken Token for pagination */ public ListTasksParams(Integer pageSize, String pageToken) { - this(null, null, pageSize, pageToken, null, null, null, null); + this(null, null, pageSize, pageToken, null, null, null, ""); } /** @@ -100,7 +103,7 @@ public static class Builder { private Integer historyLength; private Instant lastUpdatedAfter; private Boolean includeArtifacts; - private Map metadata; + private String tenant; /** * Creates a new Builder with all fields unset. @@ -143,14 +146,14 @@ public Builder includeArtifacts(Boolean includeArtifacts) { return this; } - public Builder metadata(Map metadata) { - this.metadata = metadata; + public Builder tenant(String tenant) { + this.tenant = tenant; return this; } public ListTasksParams build() { return new ListTasksParams(contextId, status, pageSize, pageToken, historyLength, - lastUpdatedAfter, includeArtifacts, metadata); + lastUpdatedAfter, includeArtifacts, tenant); } } } diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index 409962d34..e3b863bde 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -17,16 +17,22 @@ * @param message the message to send to the agent (required) * @param configuration optional configuration for message processing behavior * @param metadata optional arbitrary key-value metadata for the request + * @param tenant optional tenant, provided as a path parameter. * @see SendMessageRequest for non-streaming message delivery * @see SendStreamingMessageRequest for streaming message delivery * @see MessageSendConfiguration for available configuration options * @see A2A Protocol Specification */ public record MessageSendParams(Message message, MessageSendConfiguration configuration, - Map metadata) { + Map metadata, String tenant) { public MessageSendParams { Assert.checkNotNullParam("message", message); + Assert.checkNotNullParam("tenant", tenant); + } + + public MessageSendParams(Message message, MessageSendConfiguration configuration, Map metadata) { + this(message, configuration, metadata, ""); } /** @@ -48,6 +54,7 @@ public static class Builder { Message message; MessageSendConfiguration configuration; Map metadata; + String tenant; /** * Creates a new Builder with all fields unset. @@ -88,6 +95,17 @@ public Builder metadata(Map metadata) { return this; } + /** + * Sets optional tenant for the request. + * + * @param tenant arbitrary key-value metadata + * @return this builder + */ + public Builder tenant(String tenant) { + this.tenant = tenant; + return this; + } + /** * Builds the {@link MessageSendParams}. * @@ -95,7 +113,7 @@ public Builder metadata(Map metadata) { * @throws IllegalArgumentException if message is null */ public MessageSendParams build() { - return new MessageSendParams(message, configuration, metadata); + return new MessageSendParams(message, configuration, metadata, tenant == null ? "" : tenant); } } } diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index 11a246676..d7f07a180 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import java.util.Map; - /** * Base class for content parts within {@link Message}s and {@link Artifact}s. *

    @@ -65,12 +63,4 @@ public String asString() { * @return the Part.Kind indicating the content type */ public abstract Kind getKind(); - - /** - * Returns optional metadata associated with this part. - * - * @return map of metadata key-value pairs, or null if no metadata - */ - public abstract Map getMetadata(); - } \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index f954dcaf5..b3b476c58 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import java.util.Map; - import io.a2a.util.Assert; /** @@ -12,18 +10,19 @@ * task-specific requests. It optionally includes metadata for additional context. * * @param id the unique task identifier (required) - * @param metadata optional arbitrary key-value metadata for the request + * @param tenant optional tenant, provided as a path parameter. * @see CancelTaskRequest for task cancellation * @see SubscribeToTaskRequest for task resubscription * @see A2A Protocol Specification */ -public record TaskIdParams(String id, Map metadata) { +public record TaskIdParams(String id, String tenant) { - public TaskIdParams { + public TaskIdParams { Assert.checkNotNullParam("id", id); + Assert.checkNotNullParam("tenant", tenant); } public TaskIdParams(String id) { - this(id, null); + this(id, ""); } } diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index 8e0d8a406..dad97becd 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -1,6 +1,7 @@ package io.a2a.spec; import io.a2a.util.Assert; +import org.jspecify.annotations.Nullable; /** * Associates a push notification configuration with a specific task. @@ -19,11 +20,11 @@ * @see SetTaskPushNotificationConfigRequest for setting push notifications * @see A2A Protocol Specification */ -public record TaskPushNotificationConfig(String taskId, PushNotificationConfig pushNotificationConfig) { +public record TaskPushNotificationConfig(String taskId, PushNotificationConfig pushNotificationConfig, @Nullable String tenant) { public TaskPushNotificationConfig { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("pushNotificationConfig", pushNotificationConfig); Assert.checkNotNullParam("configId", pushNotificationConfig.id()); - } + } } diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index f119bd14b..38df33e60 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -1,7 +1,6 @@ package io.a2a.spec; import io.a2a.util.Assert; -import java.util.Map; import org.jspecify.annotations.Nullable; /** @@ -9,22 +8,22 @@ * * @param id the ID for the task to be queried * @param historyLength the maximum number of items of history for the task to include in the response - * @param metadata additional properties + * @param tenant optional tenant, provided as a path parameter. */ -public record TaskQueryParams(String id, int historyLength, @Nullable Map metadata) { +public record TaskQueryParams(String id, @Nullable Integer historyLength, String tenant) { public TaskQueryParams { Assert.checkNotNullParam("id", id); - if (historyLength < 0) { + Assert.checkNotNullParam("tenant", tenant); + if (historyLength != null && historyLength < 0) { throw new IllegalArgumentException("Invalid history length"); } } - - public TaskQueryParams(String id) { - this(id, 0, null); + public TaskQueryParams(String id, Integer historyLength) { + this(id, historyLength, ""); } - public TaskQueryParams(String id, int historyLength) { - this(id, historyLength, null); + public TaskQueryParams(String id) { + this(id, 0, ""); } } diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index 8441db90c..de527a35c 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -1,10 +1,8 @@ package io.a2a.spec; -import java.util.Map; import io.a2a.util.Assert; -import static io.a2a.spec.TextPart.TEXT; /** * Represents a plain text content part within a {@link Message} or {@link Artifact}. @@ -29,17 +27,11 @@ public class TextPart extends Part { public static final String TEXT = "text"; private final String text; - private final Map metadata; private final Kind kind; public TextPart(String text) { - this(text, null); - } - - public TextPart(String text, Map metadata) { Assert.checkNotNullParam("text", text); this.text = text; - this.metadata = metadata; this.kind = Kind.TEXT; } @@ -51,9 +43,4 @@ public Kind getKind() { public String getText() { return text; } - - @Override - public Map getMetadata() { - return metadata; - } } \ No newline at end of file diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 2142c15bf..166e60ef2 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -266,7 +266,7 @@ public void testListTasksSuccess() throws Exception { try { // Test listing all tasks (no filters) - io.a2a.spec.ListTasksParams params = ListTasksParams.builder().build(); + io.a2a.spec.ListTasksParams params = ListTasksParams.builder().tenant("tenant").build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); assertNotNull(result); @@ -307,6 +307,7 @@ public void testListTasksFilterByContextId() throws Exception { // Filter by contextId io.a2a.spec.ListTasksParams params = ListTasksParams.builder() .contextId("context-filter-1") + .tenant("tenant") .build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); @@ -347,6 +348,7 @@ public void testListTasksFilterByStatus() throws Exception { // Filter by status WORKING io.a2a.spec.ListTasksParams params = ListTasksParams.builder() .status(TaskState.WORKING) + .tenant("tenant") .build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); @@ -390,6 +392,7 @@ public void testListTasksWithPagination() throws Exception { // Get first page with pageSize=2 io.a2a.spec.ListTasksParams params1 = ListTasksParams.builder() .contextId("page-context") + .tenant("tenant") .pageSize(2) .build(); io.a2a.spec.ListTasksResult result1 = getClient().listTasks(params1); @@ -402,6 +405,7 @@ public void testListTasksWithPagination() throws Exception { // Get second page using pageToken io.a2a.spec.ListTasksParams params2 = ListTasksParams.builder() .contextId("page-context") + .tenant("tenant") .pageSize(2) .pageToken(result1.nextPageToken()) .build(); @@ -438,6 +442,7 @@ public void testListTasksWithHistoryLimit() throws Exception { // List with history limited to 2 messages io.a2a.spec.ListTasksParams params = ListTasksParams.builder() .contextId("context-history") + .tenant("tenant") .historyLength(2) .build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); @@ -542,7 +547,7 @@ public void testSetPushNotificationSuccess() throws Exception { try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); + MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertEquals(MINIMAL_TASK.getId(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); @@ -561,7 +566,7 @@ public void testGetPushNotificationSuccess() throws Exception { try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); + MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertNotNull(setResult); @@ -611,7 +616,7 @@ public void testGetAgentCard() throws A2AClientException { assertTrue(agentCard.capabilities().streaming()); assertTrue(agentCard.capabilities().stateTransitionHistory()); assertTrue(agentCard.skills().isEmpty()); - assertFalse(agentCard.supportsAuthenticatedExtendedCard()); + assertFalse(agentCard.supportsExtendedAgentCard()); } @Test @@ -1067,8 +1072,8 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { List result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); assertEquals(2, result.size()); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1), result.get(0)); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2), result.get(1)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1, null), result.get(0)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2, null), result.get(1)); } catch (Exception e) { fail(); } finally { @@ -1102,7 +1107,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { .url("http://2.example.com") .id(MINIMAL_TASK.getId()) .build(); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), expectedNotificationConfig), + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), expectedNotificationConfig, null), result.get(0)); } catch (Exception e) { fail(); @@ -1580,7 +1585,7 @@ private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exc .contextId(MINIMAL_TASK.getContextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( - "1", new MessageSendParams(message, null, null)); + "1", new MessageSendParams(message, null, null, "")); CompletableFuture>> responseFuture = initialiseStreamingRequest(request, mediaType); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java index f472a37ab..8bff85c78 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java @@ -36,7 +36,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } else { // Second call: context.getTask() != null (existing task) updater.addArtifact( - List.of(new TextPart("Second message artifact", null)), + List.of(new TextPart("Second message artifact")), "artifact-2", "Second Artifact", null); updater.complete(); return; diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 3df35d7e1..8c62a81d6 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -239,7 +239,7 @@ public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig( // TODO: Add authentication (https://github.com/a2aproject/a2a-java/issues/77) public GetAuthenticatedExtendedCardResponse onGetAuthenticatedExtendedCardRequest( GetAuthenticatedExtendedCardRequest request, ServerCallContext context) { - if (!agentCard.supportsAuthenticatedExtendedCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { + if (!agentCard.supportsExtendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { return new GetAuthenticatedExtendedCardResponse(request.getId(), new AuthenticatedExtendedCardNotConfiguredError(null, "Authenticated Extended Card not configured", null)); } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 863e10d53..3852eb10a 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -186,8 +186,10 @@ public void testOnMessageNewMessageSuccessMocks() { SendMessageResponse response; try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, - (mock, context) -> {Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll(); - Mockito.doCallRealMethod().when(mock).createAgentRunnableDoneCallback();})){ + (mock, context) -> { + Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll(); + Mockito.doCallRealMethod().when(mock).createAgentRunnableDoneCallback(); + })) { response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); @@ -231,7 +233,8 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll(); + })) { response = handler.onMessageSend(request, callContext); } assertNull(response.getError()); @@ -271,7 +274,8 @@ public void testOnMessageErrorMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(new UnsupportedOperationError()))).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(new UnsupportedOperationError()))).when(mock).consumeAll(); + })) { response = handler.onMessageSend(request, callContext); } @@ -476,7 +480,8 @@ public void testOnMessageStreamNewMessageSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll(); + })) { response = handler.onMessageSendStream(request, callContext); } @@ -530,7 +535,6 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception .contextId(task.getContextId()) .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest( "1", new MessageSendParams(message, null, null)); Flow.Publisher response = handler.onMessageSendStream(request, callContext); @@ -623,7 +627,8 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll(); + })) { response = handler.onMessageSendStream(request, callContext); } @@ -664,21 +669,21 @@ public void onComplete() { Assertions.assertEquals(events, results); } - @Test public void testSetPushNotificationConfigSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com") - .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build()); + .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); - TaskPushNotificationConfig taskPushConfigResult = - new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com").id("c295ea44-7543-4f78-b524-7a38915ad6e4").build()); + TaskPushNotificationConfig taskPushConfigResult + = new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com").id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), null); + Assertions.assertEquals(taskPushConfigResult, response.getResult()); } @@ -690,21 +695,21 @@ public void testGetPushNotificationConfigSuccess() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() - .id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); + .id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); - GetTaskPushNotificationConfigRequest getRequest = - new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + GetTaskPushNotificationConfigRequest getRequest + = new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest, callContext); TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), - PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); + PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), null); + assertEquals(expectedConfig, getResponse.getResult()); } @@ -729,7 +734,6 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex .status(new TaskStatus(TaskState.COMPLETED)) .build()); - agentExecutorExecute = (context, eventQueue) -> { // Hardcode the events to send here for (Event event : events) { @@ -737,10 +741,10 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex } }; - TaskPushNotificationConfig config = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), - PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build()); + PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); + SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); assertNull(stpnResponse.getError()); @@ -802,7 +806,7 @@ public void onComplete() { assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); assertEquals(1, curr.getArtifacts().size()); assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals("text", ((TextPart) curr.getArtifacts().get(0).parts().get(0)).getText()); curr = httpClient.tasks.get(2); assertEquals(MINIMAL_TASK.getId(), curr.getId()); @@ -810,7 +814,7 @@ public void onComplete() { assertEquals(TaskState.COMPLETED, curr.getStatus().state()); assertEquals(1, curr.getArtifacts().size()); assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals("text", ((TextPart) curr.getArtifacts().get(0).parts().get(0)).getText()); } @Test @@ -835,8 +839,8 @@ public void testOnResubscribeExistingTaskSuccess() { .role(Message.Role.AGENT) .parts(new TextPart("text")) .build(); - SendMessageResponse smr = - handler.onMessageSend( + SendMessageResponse smr + = handler.onMessageSend( new SendMessageRequest("1", new MessageSendParams(message, null, null)), callContext); assertNull(smr.getError()); @@ -880,7 +884,6 @@ public void onComplete() { assertEquals(1, results.size()); } - @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); @@ -907,7 +910,8 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll();})){ + Mockito.doReturn(ZeroPublisher.fromIterable(events.stream().map(AbstractA2ARequestHandlerTest::wrapEvent).toList())).when(mock).consumeAll(); + })) { response = handler.onSubscribeToTask(request, callContext); } @@ -1013,6 +1017,7 @@ public void testStreamingNotSupportedError() { response.subscribe(new Flow.Subscriber() { private Flow.Subscription subscription; + @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; @@ -1059,6 +1064,7 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() { response.subscribe(new Flow.Subscriber() { private Flow.Subscription subscription; + @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; @@ -1091,20 +1097,19 @@ public void onComplete() { } } - @Test public void testPushNotificationsNotSupportedError() { AgentCard card = createAgentCard(true, false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - TaskPushNotificationConfig config = - new TaskPushNotificationConfig( + TaskPushNotificationConfig config + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("http://example.com") - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = SetTaskPushNotificationConfigRequest.builder() .params(config) @@ -1123,8 +1128,8 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { taskStore.save(MINIMAL_TASK); - GetTaskPushNotificationConfigRequest request = - new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + GetTaskPushNotificationConfigRequest request + = new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request, callContext); Assertions.assertNotNull(response.getError()); @@ -1142,13 +1147,13 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { taskStore.save(MINIMAL_TASK); - TaskPushNotificationConfig config = - new TaskPushNotificationConfig( + TaskPushNotificationConfig config + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("http://example.com") - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = SetTaskPushNotificationConfigRequest.builder() .params(config) @@ -1184,12 +1189,12 @@ public void testOnMessageStreamInternalError() { SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); Flow.Publisher response = handler.onMessageSendStream(request, callContext); - List results = new ArrayList<>(); AtomicReference error = new AtomicReference<>(); response.subscribe(new Flow.Subscriber() { private Flow.Subscription subscription; + @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; @@ -1245,12 +1250,12 @@ public void testOnMessageSendErrorHandling() { try (MockedConstruction mocked = Mockito.mockConstruction( ResultAggregator.class, (mock, context) -> { - Mockito.doThrow( - new UnsupportedOperationError()) - .when(mock).consumeAndBreakOnInterrupt( - Mockito.any(EventConsumer.class), - Mockito.anyBoolean()); - })){ + Mockito.doThrow( + new UnsupportedOperationError()) + .when(mock).consumeAndBreakOnInterrupt( + Mockito.any(EventConsumer.class), + Mockito.anyBoolean()); + })) { response = handler.onMessageSend(request, callContext); } @@ -1291,6 +1296,7 @@ public void testOnMessageStreamTaskIdMismatch() { response.subscribe(new Flow.Subscriber() { private Flow.Subscription subscription; + @Override public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; @@ -1332,22 +1338,26 @@ public void testListPushNotificationConfig() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() .url("http://example.com") .id(MINIMAL_TASK.getId()) - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); - - ListTaskPushNotificationConfigRequest listRequest = - new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + TaskPushNotificationConfig result = new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), PushNotificationConfig.builder() + .url("http://example.com") + .id(MINIMAL_TASK.getId()) + .build(), null); + ListTaskPushNotificationConfigRequest listRequest + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertEquals(1, listResponse.getResult().size()); - assertEquals(taskPushConfig, listResponse.getResult().get(0)); + assertEquals(result, listResponse.getResult().get(0)); } @Test @@ -1359,19 +1369,19 @@ public void testListPushNotificationConfigNotSupported() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() .url("http://example.com") .id(MINIMAL_TASK.getId()) - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); - ListTaskPushNotificationConfigRequest listRequest = - new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = - handler.listPushNotificationConfig(listRequest, callContext); + ListTaskPushNotificationConfigRequest listRequest + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse + = handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertNull(listResponse.getResult()); @@ -1388,10 +1398,10 @@ public void testListPushNotificationConfigNoPushConfigStore() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - ListTaskPushNotificationConfigRequest listRequest = - new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = - handler.listPushNotificationConfig(listRequest, callContext); + ListTaskPushNotificationConfigRequest listRequest + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse + = handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertNull(listResponse.getResult()); @@ -1405,10 +1415,10 @@ public void testListPushNotificationConfigTaskNotFound() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - ListTaskPushNotificationConfigRequest listRequest = - new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - ListTaskPushNotificationConfigResponse listResponse = - handler.listPushNotificationConfig(listRequest, callContext); + ListTaskPushNotificationConfigRequest listRequest + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + ListTaskPushNotificationConfigResponse listResponse + = handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); assertNull(listResponse.getResult()); @@ -1423,19 +1433,19 @@ public void testDeletePushNotificationConfig() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() .url("http://example.com") .id(MINIMAL_TASK.getId()) - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); - DeleteTaskPushNotificationConfigRequest deleteRequest = - new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); - DeleteTaskPushNotificationConfigResponse deleteResponse = - handler.deletePushNotificationConfig(deleteRequest, callContext); + DeleteTaskPushNotificationConfigRequest deleteRequest + = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + DeleteTaskPushNotificationConfigResponse deleteResponse + = handler.deletePushNotificationConfig(deleteRequest, callContext); assertEquals("111", deleteResponse.getId()); assertNull(deleteResponse.getError()); @@ -1451,19 +1461,19 @@ public void testDeletePushNotificationConfigNotSupported() { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), PushNotificationConfig.builder() .url("http://example.com") .id(MINIMAL_TASK.getId()) - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); - DeleteTaskPushNotificationConfigRequest deleteRequest = - new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); - DeleteTaskPushNotificationConfigResponse deleteResponse = - handler.deletePushNotificationConfig(deleteRequest, callContext); + DeleteTaskPushNotificationConfigRequest deleteRequest + = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + DeleteTaskPushNotificationConfigResponse deleteResponse + = handler.deletePushNotificationConfig(deleteRequest, callContext); assertEquals("111", deleteResponse.getId()); assertNull(deleteResponse.getResult()); @@ -1473,26 +1483,26 @@ public void testDeletePushNotificationConfigNotSupported() { @Test public void testDeletePushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + executor, taskStore, queueManager, null, null, internalExecutor); JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - TaskPushNotificationConfig taskPushConfig = - new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + TaskPushNotificationConfig taskPushConfig + = new TaskPushNotificationConfig( + MINIMAL_TASK.getId(), PushNotificationConfig.builder() .url("http://example.com") .id(MINIMAL_TASK.getId()) - .build()); + .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); - DeleteTaskPushNotificationConfigRequest deleteRequest = - new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); - DeleteTaskPushNotificationConfigResponse deleteResponse = - handler.deletePushNotificationConfig(deleteRequest, callContext); + DeleteTaskPushNotificationConfigRequest deleteRequest + = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + DeleteTaskPushNotificationConfigResponse deleteResponse + = handler.deletePushNotificationConfig(deleteRequest, callContext); assertEquals("111", deleteResponse.getId()); assertNull(deleteResponse.getResult()); @@ -1569,8 +1579,8 @@ public void onComplete() { }); // The main thread should not be blocked - we should be able to continue immediately - Assertions.assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), - "Streaming subscription should start quickly without blocking main thread"); + Assertions.assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), + "Streaming subscription should start quickly without blocking main thread"); // This proves the main thread is not blocked - we can do other work // Simulate main thread doing other work @@ -1579,8 +1589,8 @@ public void onComplete() { mainThreadBlocked.set(false); // If we get here, main thread was not blocked // Wait for the actual event processing to complete - Assertions.assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), - "Event should be processed within reasonable time"); + Assertions.assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), + "Event should be processed within reasonable time"); // Verify we received the event and main thread was not blocked Assertions.assertTrue(eventReceived.get(), "Should have received streaming event"); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 14c3e6b6d..d8f77f279 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -172,7 +172,7 @@ public HTTPRestResponse resubscribeTask(String taskId, ServerCallContext context public HTTPRestResponse getTask(String taskId, int historyLength, ServerCallContext context) { try { - TaskQueryParams params = new TaskQueryParams(taskId, historyLength); + TaskQueryParams params = new TaskQueryParams(taskId, historyLength, ""); Task task = requestHandler.onGetTask(params, context); if (task != null) { return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); @@ -188,7 +188,7 @@ public HTTPRestResponse getTask(String taskId, int historyLength, ServerCallCont public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String status, @Nullable Integer pageSize, @Nullable String pageToken, @Nullable Integer historyLength, @Nullable String lastUpdatedAfter, - @Nullable Boolean includeArtifacts, + @Nullable Boolean includeArtifacts, String tenant, ServerCallContext context) { try { // Build params @@ -208,6 +208,7 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s if (historyLength != null) { paramsBuilder.historyLength(historyLength); } + paramsBuilder.tenant(tenant); if (lastUpdatedAfter != null) { try { paramsBuilder.lastUpdatedAfter(Instant.parse(lastUpdatedAfter)); @@ -400,7 +401,7 @@ private int mapErrorToHttpStatus(JSONRPCError error) { public HTTPRestResponse getAuthenticatedExtendedCard() { try { - if (!agentCard.supportsAuthenticatedExtendedCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { + if (!agentCard.supportsExtendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { throw new AuthenticatedExtendedCardNotConfiguredError(null, "Authenticated Extended Card not configured", null); } return new HTTPRestResponse(200, "application/json", JsonUtil.toJson(extendedAgentCard.get())); From 6aad9cd3b3f242f916779c70a09d64ddb600342d Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Tue, 16 Dec 2025 14:21:19 +0100 Subject: [PATCH 232/493] feat: Add tenant parameter to REST push notification config endpoints (#542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tenant parameter to RestHandler.getTaskPushNotificationConfiguration to match other push notification methods - Add route with trailing slash to distinguish GET (single config) from LIST in A2AServerRoutes - Update RestTransport to use trailing slash when configId is null - Update tests to use new method signatures - Update REST routes to match the new teannt path element This ensures consistent tenant handling across all push notification endpoints and fixes routing ambiguity between GET and LIST operations. - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [C] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes #531 🦕 Signed-off-by: Emmanuel Hugonnet --- .../AuthenticationAuthorizationTest.java | 8 +- .../client/transport/rest/RestTransport.java | 61 +++++++--- .../transport/rest/RestTransportTest.java | 18 +-- .../server/rest/quarkus/A2AServerRoutes.java | 109 +++++++++++------- .../rest/quarkus/A2AServerRoutesTest.java | 53 +++++---- .../rest/quarkus/QuarkusA2ARestTest.java | 6 +- .../DefaultRequestHandler.java | 6 +- ...askPushNotificationConfigParamsMapper.java | 1 + ...askPushNotificationConfigParamsMapper.java | 1 + ...askPushNotificationConfigParamsMapper.java | 1 + .../a2a/grpc/mapper/TaskIdParamsMapper.java | 1 + .../grpc/mapper/TaskQueryParamsMapper.java | 1 + .../java/io/a2a/spec/TaskQueryParams.java | 4 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 16 ++- .../transport/rest/handler/RestHandler.java | 35 +++--- .../rest/handler/RestHandlerTest.java | 40 +++---- 16 files changed, 213 insertions(+), 148 deletions(-) diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java index 31c1ace88..a9bc60aa5 100644 --- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -194,7 +194,7 @@ public void testRestNonStreamingUnauthenticated() throws A2AClientException { server.when( request() .withMethod("POST") - .withPath("/v1/message:send") + .withPath("/message:send") ).respond( response() .withStatusCode(401) @@ -215,7 +215,7 @@ public void testRestNonStreamingUnauthorized() throws A2AClientException { server.when( request() .withMethod("POST") - .withPath("/v1/message:send") + .withPath("/message:send") ).respond( response() .withStatusCode(403) @@ -236,7 +236,7 @@ public void testRestStreamingUnauthenticated() throws Exception { server.when( request() .withMethod("POST") - .withPath("/v1/message:stream") + .withPath("/message:stream") ).respond( response() .withStatusCode(401) @@ -253,7 +253,7 @@ public void testRestStreamingUnauthorized() throws Exception { server.when( request() .withMethod("POST") - .withPath("/v1/message:stream") + .withPath("/message:stream") ).respond( response() .withStatusCode(403) diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 4af26a67f..82c17a244 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -83,7 +83,7 @@ public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable Clie io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, builder, agentCard, context); try { - String httpResponseBody = sendPostRequest(agentUrl + "/v1/message:send", payloadAndHeaders); + String httpResponseBody = sendPostRequest(buildBaseUrl(messageSendParams.tenant()) + "/message:send", payloadAndHeaders); io.a2a.grpc.SendMessageResponse.Builder responseBuilder = io.a2a.grpc.SendMessageResponse.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); if (responseBuilder.hasMsg()) { @@ -111,7 +111,7 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); try { - A2AHttpClient.PostBuilder postBuilder = createPostBuilder(agentUrl + "/v1/message:stream", payloadAndHeaders); + A2AHttpClient.PostBuilder postBuilder = createPostBuilder(buildBaseUrl(messageSendParams.tenant()) + "/message:stream", payloadAndHeaders); ref.set(postBuilder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), @@ -135,13 +135,13 @@ public Task getTask(TaskQueryParams taskQueryParams, @Nullable ClientCallContext PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, builder, agentCard, context); try { - String url; + StringBuilder url = new StringBuilder(buildBaseUrl(taskQueryParams.tenant())); if (taskQueryParams.historyLength() != null && taskQueryParams.historyLength() > 0) { - url = agentUrl + String.format("/v1/tasks/%1s?historyLength=%2d", taskQueryParams.id(), taskQueryParams.historyLength()); + url.append(String.format("/tasks/%1s?historyLength=%2d", taskQueryParams.id(), taskQueryParams.historyLength())); } else { - url = agentUrl + String.format("/v1/tasks/%1s", taskQueryParams.id()); + url.append(String.format("/tasks/%1s", taskQueryParams.id())); } - A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url.toString()); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { getBuilder.addHeader(entry.getKey(), entry.getValue()); @@ -170,7 +170,7 @@ public Task cancelTask(TaskIdParams taskIdParams, @Nullable ClientCallContext co PayloadAndHeaders payloadAndHeaders = applyInterceptors(CancelTaskRequest.METHOD, builder, agentCard, context); try { - String httpResponseBody = sendPostRequest(agentUrl + String.format("/v1/tasks/%1s:cancel", taskIdParams.id()), payloadAndHeaders); + String httpResponseBody = sendPostRequest(buildBaseUrl(taskIdParams.tenant()) + String.format("/tasks/%1s:cancel", taskIdParams.id()), payloadAndHeaders); io.a2a.grpc.Task.Builder responseBuilder = io.a2a.grpc.Task.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); return ProtoUtils.FromProto.task(responseBuilder); @@ -212,8 +212,8 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo try { // Build query string - StringBuilder urlBuilder = new StringBuilder(agentUrl); - urlBuilder.append("/v1/tasks"); + StringBuilder urlBuilder = new StringBuilder(buildBaseUrl(request.tenant())); + urlBuilder.append("/tasks"); String queryParams = buildListTasksQueryString(request); if (!queryParams.isEmpty()) { urlBuilder.append("?").append(queryParams); @@ -246,6 +246,25 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo } } + private String buildBaseUrl(@Nullable String tenant) { + StringBuilder urlBuilder = new StringBuilder(agentUrl); + urlBuilder.append(extractTenant(tenant)); + return urlBuilder.toString(); + } + + private String extractTenant(@Nullable String tenant) { + String tenantPath = tenant; + if (tenantPath == null || tenantPath.isBlank()) { + return ""; + } + if(! tenantPath.startsWith("/")){ + tenantPath = '/' + tenantPath; + } + if(tenantPath.endsWith("/")){ + tenantPath = tenantPath.substring(0, tenantPath.length() -1); + } + return tenantPath; + } private String buildListTasksQueryString(ListTasksParams request) { java.util.List queryParts = new java.util.ArrayList<>(); if (request.contextId() != null) { @@ -281,7 +300,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN } PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String httpResponseBody = sendPostRequest(agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs", request.taskId()), payloadAndHeaders); + String httpResponseBody = sendPostRequest(buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.taskId()), payloadAndHeaders); io.a2a.grpc.TaskPushNotificationConfig.Builder responseBuilder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); return ProtoUtils.FromProto.taskPushNotificationConfig(responseBuilder); @@ -297,12 +316,20 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu checkNotNullParam("request", request); io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder(); - builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId())); + StringBuilder url = new StringBuilder(buildBaseUrl(request.tenant())); + String configId = request.pushNotificationConfigId(); + if (configId != null && !configId.isEmpty()) { + builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), configId)); + url.append(builder.getName()); + } else { + // Use trailing slash to distinguish GET from LIST + builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/", request.id())); + url.append(builder.getName()); + } PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); - A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); + A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url.toString()); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { getBuilder.addHeader(entry.getKey(), entry.getValue()); @@ -332,7 +359,7 @@ public List listTaskPushNotificationConfigurations(L PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs", request.id()); + String url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.id()); A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { @@ -361,7 +388,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String url = agentUrl + String.format("/v1/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); + String url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); A2AHttpClient.DeleteBuilder deleteBuilder = httpClient.createDelete().url(url); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { @@ -390,7 +417,7 @@ public void resubscribe(TaskIdParams request, Consumer event AtomicReference> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); try { - String url = agentUrl + String.format("/v1/tasks/%1s:subscribe", request.id()); + String url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s:subscribe", request.id()); A2AHttpClient.PostBuilder postBuilder = createPostBuilder(url, payloadAndHeaders); ref.set(postBuilder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), @@ -421,7 +448,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli } PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, null, agentCard, context); - String url = agentUrl + String.format("/v1/card"); + String url = buildBaseUrl("") + String.format("/extendedAgentCard"); A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 4e5284470..6ed9a79e4 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -117,7 +117,7 @@ public void testSendMessage() throws Exception { this.server.when( request() .withMethod("POST") - .withPath("/v1/message:send") + .withPath("/message:send") .withBody(JsonBody.json(SEND_MESSAGE_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -160,7 +160,7 @@ public void testCancelTask() throws Exception { this.server.when( request() .withMethod("POST") - .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64:cancel") + .withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64:cancel") .withBody(JsonBody.json(CANCEL_TASK_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -186,7 +186,7 @@ public void testGetTask() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64") + .withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64") ) .respond( response() @@ -238,7 +238,7 @@ public void testSendMessageStreaming() throws Exception { this.server.when( request() .withMethod("POST") - .withPath("/v1/message:stream") + .withPath("/message:stream") .withBody(JsonBody.json(SEND_MESSAGE_STREAMING_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -290,7 +290,7 @@ public void testSetTaskPushNotificationConfiguration() throws Exception { this.server.when( request() .withMethod("POST") - .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs") + .withPath("/tenant/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs") .withBody(JsonBody.json(SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) .respond( @@ -324,7 +324,7 @@ public void testGetTaskPushNotificationConfiguration() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10") + .withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10") ) .respond( response() @@ -351,7 +351,7 @@ public void testListTaskPushNotificationConfigurations() throws Exception { this.server.when( request() .withMethod("GET") - .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs") + .withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs") ) .respond( response() @@ -388,7 +388,7 @@ public void testDeleteTaskPushNotificationConfigurations() throws Exception { this.server.when( request() .withMethod("DELETE") - .withPath("/v1/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10") + .withPath("/tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10") ) .respond( response() @@ -409,7 +409,7 @@ public void testResubscribe() throws Exception { this.server.when( request() .withMethod("POST") - .withPath("/v1/tasks/task-1234:subscribe") + .withPath("/tasks/task-1234:subscribe") ) .respond( response() diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 5d61a5f01..29f150943 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -76,12 +76,12 @@ public class A2AServerRoutes { @Inject Instance callContextFactory; - @Route(regex = "^/v1/message:send$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)message:send$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void sendMessage(@Body String body, RoutingContext rc) { ServerCallContext context = createCallContext(rc, SendMessageRequest.METHOD); HTTPRestResponse response = null; try { - response = jsonRestHandler.sendMessage(body, context); + response = jsonRestHandler.sendMessage(body, extractTenant(rc), context); } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { @@ -89,13 +89,13 @@ public void sendMessage(@Body String body, RoutingContext rc) { } } - @Route(regex = "^/v1/message:stream$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)message:stream$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void sendMessageStreaming(@Body String body, RoutingContext rc) { ServerCallContext context = createCallContext(rc, SendStreamingMessageRequest.METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { - HTTPRestResponse response = jsonRestHandler.sendStreamingMessage(body, context); + HTTPRestResponse response = jsonRestHandler.sendStreamingMessage(body, extractTenant(rc), context); if (response instanceof HTTPRestStreamingResponse hTTPRestStreamingResponse) { streamingResponse = hTTPRestStreamingResponse; } else { @@ -114,7 +114,7 @@ public void sendMessageStreaming(@Body String body, RoutingContext rc) { } } - @Route(path = "/v1/tasks", order = 0, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\??", order = 0, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void listTasks(RoutingContext rc) { ServerCallContext context = createCallContext(rc, ListTasksRequest.METHOD); HTTPRestResponse response = null; @@ -130,21 +130,16 @@ public void listTasks(RoutingContext rc) { String historyLengthStr = rc.request().params().get("historyLength"); String lastUpdatedAfter = rc.request().params().get("lastUpdatedAfter"); String includeArtifactsStr = rc.request().params().get("includeArtifacts"); - //TODO replace by path extraction - String tenant = rc.request().params().get("tenant"); - if (tenant == null) { - tenant = ""; - } // Parse optional parameters Integer pageSize = null; if (pageSizeStr != null && !pageSizeStr.isEmpty()) { - pageSize = Integer.parseInt(pageSizeStr); + pageSize = Integer.valueOf(pageSizeStr); } Integer historyLength = null; if (historyLengthStr != null && !historyLengthStr.isEmpty()) { - historyLength = Integer.parseInt(historyLengthStr); + historyLength = Integer.valueOf(historyLengthStr); } Boolean includeArtifacts = null; @@ -153,7 +148,7 @@ public void listTasks(RoutingContext rc) { } response = jsonRestHandler.listTasks(contextId, statusStr, pageSize, pageToken, - historyLength, lastUpdatedAfter, includeArtifacts, tenant, context); + historyLength, lastUpdatedAfter, includeArtifacts, extractTenant(rc), context); } catch (NumberFormatException e) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("Invalid number format in parameters")); } catch (IllegalArgumentException e) { @@ -165,20 +160,20 @@ public void listTasks(RoutingContext rc) { } } - @Route(path = "/v1/tasks/:id", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^:^/]+)$", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void getTask(RoutingContext rc) { - String taskId = rc.pathParam("id"); + String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, GetTaskRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - int historyLength = 0; + Integer historyLength = null; if (rc.request().params().contains("history_length")) { - historyLength = Integer.parseInt(rc.request().params().get("history_length")); + historyLength = Integer.valueOf(rc.request().params().get("history_length")); } - response = jsonRestHandler.getTask(taskId, historyLength, context); + response = jsonRestHandler.getTask(taskId, historyLength, extractTenant(rc), context); } } catch (NumberFormatException e) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad history_length")); @@ -189,16 +184,16 @@ public void getTask(RoutingContext rc) { } } - @Route(regex = "^/v1/tasks/([^/]+):cancel$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+):cancel$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void cancelTask(RoutingContext rc) { - String taskId = rc.pathParam("param0"); + String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, CancelTaskRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.cancelTask(taskId, context); + response = jsonRestHandler.cancelTask(taskId, extractTenant(rc), context); } } catch (Throwable t) { if (t instanceof JSONRPCError error) { @@ -222,9 +217,9 @@ private void sendResponse(RoutingContext rc, @Nullable HTTPRestResponse response } } - @Route(regex = "^/v1/tasks/([^/]+):subscribe$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) - public void resubscribeTask(RoutingContext rc) { - String taskId = rc.pathParam("param0"); + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+):subscribe$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) + public void subscribeToTask(RoutingContext rc) { + String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, SubscribeToTaskRequest.METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; @@ -232,7 +227,7 @@ public void resubscribeTask(RoutingContext rc) { if (taskId == null || taskId.isEmpty()) { error = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - HTTPRestResponse response = jsonRestHandler.resubscribeTask(taskId, context); + HTTPRestResponse response = jsonRestHandler.subscribeToTask(taskId, extractTenant(rc), context); if (response instanceof HTTPRestStreamingResponse hTTPRestStreamingResponse) { streamingResponse = hTTPRestStreamingResponse; } else { @@ -252,16 +247,16 @@ public void resubscribeTask(RoutingContext rc) { } } - @Route(path = "/v1/tasks/:id/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void setTaskPushNotificationConfiguration(@Body String body, RoutingContext rc) { - String taskId = rc.pathParam("id"); + String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, SetTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.setTaskPushNotificationConfiguration(taskId, body, context); + response = jsonRestHandler.setTaskPushNotificationConfiguration(taskId, body, extractTenant(rc), context); } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); @@ -270,9 +265,9 @@ public void setTaskPushNotificationConfiguration(@Body String body, RoutingConte } } - @Route(path = "/v1/tasks/:id/pushNotificationConfigs/:configId", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs\\/(?[^\\/]+)", order = 2, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void getTaskPushNotificationConfiguration(RoutingContext rc) { - String taskId = rc.pathParam("id"); + String taskId = rc.pathParam("taskId"); String configId = rc.pathParam("configId"); ServerCallContext context = createCallContext(rc, GetTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; @@ -280,7 +275,7 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, context); + response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, configId, extractTenant(rc), context); } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); @@ -289,16 +284,35 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) { } } - @Route(path = "/v1/tasks/:id/pushNotificationConfigs", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs\\/$", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) + public void getTaskPushNotificationConfigurationWithoutId(RoutingContext rc) { + String taskId = rc.pathParam("taskId"); + ServerCallContext context = createCallContext(rc, GetTaskPushNotificationConfigRequest.METHOD); + HTTPRestResponse response = null; + try { + if (taskId == null || taskId.isEmpty()) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); + } else { + // Call get with null configId - trailing slash distinguishes this from list + response = jsonRestHandler.getTaskPushNotificationConfiguration(taskId, null, extractTenant(rc), context); + } + } catch (Throwable t) { + response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); + } finally { + sendResponse(rc, response); + } + } + + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs", order = 3, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void listTaskPushNotificationConfigurations(RoutingContext rc) { - String taskId = rc.pathParam("id"); + String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, ListTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, context); + response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, extractTenant(rc), context); } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); @@ -307,9 +321,9 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { } } - @Route(path = "/v1/tasks/:id/pushNotificationConfigs/:configId", order = 1, methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) + @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs\\/(?[^/]+)", order = 1, methods = {Route.HttpMethod.DELETE}, type = Route.HandlerType.BLOCKING) public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { - String taskId = rc.pathParam("id"); + String taskId = rc.pathParam("taskId"); String configId = rc.pathParam("configId"); ServerCallContext context = createCallContext(rc, DeleteTaskPushNotificationConfigRequest.METHOD); HTTPRestResponse response = null; @@ -319,7 +333,7 @@ public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { } else if (configId == null || configId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad config id")); } else { - response = jsonRestHandler.deleteTaskPushNotificationConfiguration(taskId, configId, context); + response = jsonRestHandler.deleteTaskPushNotificationConfiguration(taskId, configId, extractTenant(rc), context); } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); @@ -328,6 +342,19 @@ public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { } } + private String extractTenant(RoutingContext rc) { + String tenantPath = rc.pathParam("tenant"); + if (tenantPath == null || tenantPath.isBlank()) { + return ""; + } + if (tenantPath.startsWith("/")) { + tenantPath = tenantPath.substring(1); + } + if (tenantPath.endsWith("/")) { + tenantPath = tenantPath.substring(0, tenantPath.length() - 1); + } + return tenantPath; + } /** * /** * Handles incoming GET requests to the agent card endpoint. @@ -342,13 +369,13 @@ public void getAgentCard(RoutingContext rc) { sendResponse(rc, response); } - @Route(path = "/v1/card", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) - public void getAuthenticatedExtendedCard(RoutingContext rc) { - HTTPRestResponse response = jsonRestHandler.getAuthenticatedExtendedCard(); + @Route(regex = "^\\/(?[^\\/]*\\/?)extendedAgentCard$", order = 1, methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) + public void getExtendedAgentCard(RoutingContext rc) { + HTTPRestResponse response = jsonRestHandler.getExtendedAgentCard(extractTenant(rc)); sendResponse(rc, response); } - @Route(path = "^/v1/.*", order = 100, methods = {Route.HttpMethod.DELETE, Route.HttpMethod.GET, Route.HttpMethod.HEAD, Route.HttpMethod.OPTIONS, Route.HttpMethod.POST, Route.HttpMethod.PUT}, produces = APPLICATION_JSON) + @Route(path = "^/.*", order = 100, methods = {Route.HttpMethod.DELETE, Route.HttpMethod.GET, Route.HttpMethod.HEAD, Route.HttpMethod.OPTIONS, Route.HttpMethod.POST, Route.HttpMethod.PUT}, produces = APPLICATION_JSON) public void methodNotFoundMessage(RoutingContext rc) { HTTPRestResponse response = jsonRestHandler.createErrorResponse(new MethodNotFoundError()); sendResponse(rc, response); diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java index 19367960b..84c7401e4 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -94,7 +93,7 @@ public void testSendMessage_MethodNameSetInContext() { when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.sendMessage(anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + when(mockRestHandler.sendMessage(anyString(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -102,7 +101,7 @@ public void testSendMessage_MethodNameSetInContext() { routes.sendMessage("{}", mockRoutingContext); // Assert - verify(mockRestHandler).sendMessage(eq("{}"), contextCaptor.capture()); + verify(mockRestHandler).sendMessage(eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(SendMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -115,7 +114,7 @@ public void testSendMessageStreaming_MethodNameSetInContext() { when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.sendStreamingMessage(anyString(), any(ServerCallContext.class))) + when(mockRestHandler.sendStreamingMessage(anyString(), anyString(), any(ServerCallContext.class))) .thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -124,7 +123,7 @@ public void testSendMessageStreaming_MethodNameSetInContext() { routes.sendMessageStreaming("{}", mockRoutingContext); // Assert - verify(mockRestHandler).sendStreamingMessage(eq("{}"), contextCaptor.capture()); + verify(mockRestHandler).sendStreamingMessage(eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(SendStreamingMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -133,12 +132,12 @@ public void testSendMessageStreaming_MethodNameSetInContext() { @Test public void testGetTask_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{test:value}"); - when(mockRestHandler.getTask(anyString(), anyInt(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + when(mockRestHandler.getTask(anyString(), any(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -146,7 +145,7 @@ public void testGetTask_MethodNameSetInContext() { routes.getTask(mockRoutingContext); // Assert - verify(mockRestHandler).getTask(eq("task123"), anyInt(), contextCaptor.capture()); + verify(mockRestHandler).getTask(eq("task123"), any(), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(GetTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -155,12 +154,12 @@ public void testGetTask_MethodNameSetInContext() { @Test public void testCancelTask_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("param0")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.cancelTask(anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); + when(mockRestHandler.cancelTask(anyString(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -168,7 +167,7 @@ public void testCancelTask_MethodNameSetInContext() { routes.cancelTask(mockRoutingContext); // Assert - verify(mockRestHandler).cancelTask(eq("task123"), contextCaptor.capture()); + verify(mockRestHandler).cancelTask(eq("task123"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(CancelTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -177,21 +176,21 @@ public void testCancelTask_MethodNameSetInContext() { @Test public void testResubscribeTask_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("param0")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.resubscribeTask(anyString(), any(ServerCallContext.class))) + when(mockRestHandler.subscribeToTask(anyString(), anyString(), any(ServerCallContext.class))) .thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); // Act - routes.resubscribeTask(mockRoutingContext); + routes.subscribeToTask(mockRoutingContext); // Assert - verify(mockRestHandler).resubscribeTask(eq("task123"), contextCaptor.capture()); + verify(mockRestHandler).subscribeToTask(eq("task123"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(SubscribeToTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -200,12 +199,12 @@ public void testResubscribeTask_MethodNameSetInContext() { @Test public void testSetTaskPushNotificationConfiguration_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.setTaskPushNotificationConfiguration(anyString(), anyString(), + when(mockRestHandler.setTaskPushNotificationConfiguration(anyString(), anyString(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -214,7 +213,7 @@ public void testSetTaskPushNotificationConfiguration_MethodNameSetInContext() { routes.setTaskPushNotificationConfiguration("{}", mockRoutingContext); // Assert - verify(mockRestHandler).setTaskPushNotificationConfiguration(eq("task123"), eq("{}"), contextCaptor.capture()); + verify(mockRestHandler).setTaskPushNotificationConfiguration(eq("task123"), eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(SetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -223,13 +222,13 @@ public void testSetTaskPushNotificationConfiguration_MethodNameSetInContext() { @Test public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); when(mockRoutingContext.pathParam("configId")).thenReturn("config456"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.getTaskPushNotificationConfiguration(anyString(), anyString(), + when(mockRestHandler.getTaskPushNotificationConfiguration(anyString(), anyString(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -238,7 +237,7 @@ public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() { routes.getTaskPushNotificationConfiguration(mockRoutingContext); // Assert - verify(mockRestHandler).getTaskPushNotificationConfiguration(eq("task123"), eq("config456"), + verify(mockRestHandler).getTaskPushNotificationConfiguration(eq("task123"), eq("config456"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); @@ -248,12 +247,12 @@ public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() { @Test public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.listTaskPushNotificationConfigurations(anyString(), any(ServerCallContext.class))) + when(mockRestHandler.listTaskPushNotificationConfigurations(anyString(), anyString(), any(ServerCallContext.class))) .thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -262,7 +261,7 @@ public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() routes.listTaskPushNotificationConfigurations(mockRoutingContext); // Assert - verify(mockRestHandler).listTaskPushNotificationConfigurations(eq("task123"), contextCaptor.capture()); + verify(mockRestHandler).listTaskPushNotificationConfigurations(eq("task123"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(ListTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); @@ -271,13 +270,13 @@ public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() @Test public void testDeleteTaskPushNotificationConfiguration_MethodNameSetInContext() { // Arrange - when(mockRoutingContext.pathParam("id")).thenReturn("task123"); + when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); when(mockRoutingContext.pathParam("configId")).thenReturn("config456"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.deleteTaskPushNotificationConfiguration(anyString(), anyString(), + when(mockRestHandler.deleteTaskPushNotificationConfiguration(anyString(), anyString(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -286,7 +285,7 @@ public void testDeleteTaskPushNotificationConfiguration_MethodNameSetInContext() routes.deleteTaskPushNotificationConfiguration(mockRoutingContext); // Assert - verify(mockRestHandler).deleteTaskPushNotificationConfiguration(eq("task123"), eq("config456"), + verify(mockRestHandler).deleteTaskPushNotificationConfiguration(eq("task123"), eq("config456"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java index f9af7b378..5790fc6c5 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java @@ -35,22 +35,22 @@ protected String getTransportUrl() { protected void configureTransport(ClientBuilder builder) { builder.withTransport(RestTransport.class, new RestTransportConfigBuilder()); } + @Test public void testMethodNotFound() throws Exception { - // Create the client HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .build(); // Create the request HttpRequest.Builder builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/v1/message:send")) + .uri(URI.create("http://localhost:" + serverPort + "/message:send")) .PUT(HttpRequest.BodyPublishers.ofString("test")) .header("Content-Type", APPLICATION_JSON); HttpResponse response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString()); Assertions.assertEquals(405, response.statusCode()); builder = HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + serverPort + "/v1/message:send")) + .uri(URI.create("http://localhost:" + serverPort + "/message:send")) .DELETE() .header("Content-Type", APPLICATION_JSON); response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString()); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 7a6911566..334c1f368 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -570,7 +570,7 @@ public TaskPushNotificationConfig onSetTaskPushNotificationConfig( } PushNotificationConfig pushNotificationConfig = pushConfigStore.setInfo(params.taskId(), params.pushNotificationConfig()); - return new TaskPushNotificationConfig(params.taskId(), pushNotificationConfig, null); + return new TaskPushNotificationConfig(params.taskId(), pushNotificationConfig, params.tenant()); } @Override @@ -590,7 +590,7 @@ public TaskPushNotificationConfig onGetTaskPushNotificationConfig( } @Nullable String configId = params.pushNotificationConfigId(); - return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, configId), null); + return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, configId), params.tenant()); } private PushNotificationConfig getPushNotificationConfig(List notificationConfigList, @@ -652,7 +652,7 @@ public List onListTaskPushNotificationConfig( List taskPushNotificationConfigList = new ArrayList<>(); if (pushNotificationConfigList != null) { for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) { - TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, null); + TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, params.tenant()); taskPushNotificationConfigList.add(taskPushNotificationConfig); } } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java index cd5c5980e..5ab14bf2c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DeleteTaskPushNotificationConfigParamsMapper.java @@ -23,6 +23,7 @@ public interface DeleteTaskPushNotificationConfigParamsMapper { @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName())[0])") @Mapping(target = "pushNotificationConfigId", expression = "java(ResourceNameParser.parseTaskPushNotificationConfigName(proto.getName())[1])") + @Mapping(target = "tenant", source = "tenant") DeleteTaskPushNotificationConfigParams fromProto(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java index b9bf656b1..2a4be5f34 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/GetTaskPushNotificationConfigParamsMapper.java @@ -26,6 +26,7 @@ public interface GetTaskPushNotificationConfigParamsMapper { @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.parseGetTaskPushNotificationConfigName(proto.getName())[0])") @Mapping(target = "pushNotificationConfigId", expression = "java(ResourceNameParser.parseGetTaskPushNotificationConfigName(proto.getName())[1])") + @Mapping(target = "tenant", source = "tenant") GetTaskPushNotificationConfigParams fromProto(io.a2a.grpc.GetTaskPushNotificationConfigRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java index d6aa3a9af..f0cf356ab 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java @@ -22,6 +22,7 @@ public interface ListTaskPushNotificationConfigParamsMapper { */ @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractParentId(proto.getParent()))") + @Mapping(target = "tenant", source = "tenant") ListTaskPushNotificationConfigParams fromProto(io.a2a.grpc.ListTaskPushNotificationConfigRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java index eb43bdc22..e83c90a42 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskIdParamsMapper.java @@ -39,6 +39,7 @@ public interface TaskIdParamsMapper { */ @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractTaskId(proto.getName()))") + @Mapping(target = "tenant", source = "tenant") TaskIdParams fromProtoSubscribeToTaskRequest(io.a2a.grpc.SubscribeToTaskRequest proto); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java index 46c8994c5..5c0ddabaa 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskQueryParamsMapper.java @@ -28,5 +28,6 @@ public interface TaskQueryParamsMapper { @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "name", expression = "java(ResourceNameParser.defineTaskName(domain.id()))") @Mapping(target = "historyLength", source = "historyLength") + @Mapping(target = "tenant", source = "tenant") io.a2a.grpc.GetTaskRequest toProto(TaskQueryParams domain); } diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index 38df33e60..7add2868f 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -19,11 +19,11 @@ public record TaskQueryParams(String id, @Nullable Integer historyLength, String throw new IllegalArgumentException("Invalid history length"); } } - public TaskQueryParams(String id, Integer historyLength) { + public TaskQueryParams(String id, @Nullable Integer historyLength) { this(id, historyLength, ""); } public TaskQueryParams(String id) { - this(id, 0, ""); + this(id, null, ""); } } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 3852eb10a..08e2ffec9 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -676,13 +676,17 @@ public void testSetPushNotificationConfigSuccess() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com") - .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), "tenant"); + MINIMAL_TASK.getId(), + PushNotificationConfig.builder().url("http://example.com") + .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), + "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); TaskPushNotificationConfig taskPushConfigResult - = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().url("http://example.com").id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), null); + = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), + PushNotificationConfig.builder().url("http://example.com") + .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), + "tenant"); Assertions.assertEquals(taskPushConfigResult, response.getResult()); } @@ -708,7 +712,7 @@ public void testGetPushNotificationConfigSuccess() { GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest, callContext); TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), - PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), null); + PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), ""); assertEquals(expectedConfig, getResponse.getResult()); } @@ -1350,7 +1354,7 @@ public void testListPushNotificationConfig() { MINIMAL_TASK.getId(), PushNotificationConfig.builder() .url("http://example.com") .id(MINIMAL_TASK.getId()) - .build(), null); + .build(), ""); ListTaskPushNotificationConfigRequest listRequest = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index d8f77f279..bd07b27f5 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -92,10 +92,11 @@ public RestHandler(AgentCard agentCard, RequestHandler requestHandler, Executor this.executor = executor; } - public HTTPRestResponse sendMessage(String body, ServerCallContext context) { + public HTTPRestResponse sendMessage(String body, String tenant, ServerCallContext context) { try { io.a2a.grpc.SendMessageRequest.Builder request = io.a2a.grpc.SendMessageRequest.newBuilder(); parseRequestBody(body, request); + request.setTenant(tenant); EventKind result = requestHandler.onMessageSend(ProtoUtils.FromProto.messageSendParams(request), context); return createSuccessResponse(200, io.a2a.grpc.SendMessageResponse.newBuilder(ProtoUtils.ToProto.taskOrMessage(result))); } catch (JSONRPCError e) { @@ -105,13 +106,14 @@ public HTTPRestResponse sendMessage(String body, ServerCallContext context) { } } - public HTTPRestResponse sendStreamingMessage(String body, ServerCallContext context) { + public HTTPRestResponse sendStreamingMessage(String body, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().streaming()) { return createErrorResponse(new InvalidRequestError("Streaming is not supported by the agent")); } io.a2a.grpc.SendMessageRequest.Builder request = io.a2a.grpc.SendMessageRequest.newBuilder(); parseRequestBody(body, request); + request.setTenant(tenant); Flow.Publisher publisher = requestHandler.onMessageSendStream(ProtoUtils.FromProto.messageSendParams(request), context); return createStreamingResponse(publisher); } catch (JSONRPCError e) { @@ -121,12 +123,12 @@ public HTTPRestResponse sendStreamingMessage(String body, ServerCallContext cont } } - public HTTPRestResponse cancelTask(String taskId, ServerCallContext context) { + public HTTPRestResponse cancelTask(String taskId, String tenant, ServerCallContext context) { try { if (taskId == null || taskId.isEmpty()) { throw new InvalidParamsError(); } - TaskIdParams params = new TaskIdParams(taskId); + TaskIdParams params = new TaskIdParams(taskId, tenant); Task task = requestHandler.onCancelTask(params, context); if (task != null) { return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); @@ -139,13 +141,14 @@ public HTTPRestResponse cancelTask(String taskId, ServerCallContext context) { } } - public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, String body, ServerCallContext context) { + public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, String body, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(body, builder); + builder.setTenant(tenant); TaskPushNotificationConfig result = requestHandler.onSetTaskPushNotificationConfig(ProtoUtils.FromProto.setTaskPushNotificationConfig(builder), context); return createSuccessResponse(201, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(result))); } catch (JSONRPCError e) { @@ -155,12 +158,12 @@ public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, Stri } } - public HTTPRestResponse resubscribeTask(String taskId, ServerCallContext context) { + public HTTPRestResponse subscribeToTask(String taskId, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().streaming()) { return createErrorResponse(new InvalidRequestError("Streaming is not supported by the agent")); } - TaskIdParams params = new TaskIdParams(taskId); + TaskIdParams params = new TaskIdParams(taskId, tenant); Flow.Publisher publisher = requestHandler.onResubscribeToTask(params, context); return createStreamingResponse(publisher); } catch (JSONRPCError e) { @@ -170,9 +173,9 @@ public HTTPRestResponse resubscribeTask(String taskId, ServerCallContext context } } - public HTTPRestResponse getTask(String taskId, int historyLength, ServerCallContext context) { + public HTTPRestResponse getTask(String taskId, @Nullable Integer historyLength, String tenant, ServerCallContext context) { try { - TaskQueryParams params = new TaskQueryParams(taskId, historyLength, ""); + TaskQueryParams params = new TaskQueryParams(taskId, historyLength, tenant); Task task = requestHandler.onGetTask(params, context); if (task != null) { return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); @@ -233,12 +236,12 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s } } - public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nullable String configId, ServerCallContext context) { + public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nullable String configId, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } - GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams(taskId, configId); + GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams(taskId, configId, tenant); TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(params, context); return createSuccessResponse(200, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(config))); } catch (JSONRPCError e) { @@ -248,12 +251,12 @@ public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nul } } - public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, ServerCallContext context) { + public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } - ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId); + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId,tenant); List configs = requestHandler.onListTaskPushNotificationConfig(params, context); return createSuccessResponse(200, io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(ProtoUtils.ToProto.listTaskPushNotificationConfigResponse(configs))); } catch (JSONRPCError e) { @@ -263,12 +266,12 @@ public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, Se } } - public HTTPRestResponse deleteTaskPushNotificationConfiguration(String taskId, String configId, ServerCallContext context) { + public HTTPRestResponse deleteTaskPushNotificationConfiguration(String taskId, String configId, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } - DeleteTaskPushNotificationConfigParams params = new DeleteTaskPushNotificationConfigParams(taskId, configId); + DeleteTaskPushNotificationConfigParams params = new DeleteTaskPushNotificationConfigParams(taskId, configId, tenant); requestHandler.onDeleteTaskPushNotificationConfig(params, context); return new HTTPRestResponse(204, "application/json", ""); } catch (JSONRPCError e) { @@ -399,7 +402,7 @@ private int mapErrorToHttpStatus(JSONRPCError error) { return 500; } - public HTTPRestResponse getAuthenticatedExtendedCard() { + public HTTPRestResponse getExtendedAgentCard(String tenant) { try { if (!agentCard.supportsExtendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { throw new AuthenticatedExtendedCardNotConfiguredError(null, "Authenticated Extended Card not configured", null); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 109bfc7a9..fd6ceaf8e 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -26,13 +26,13 @@ public void testGetTaskSuccess() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(), 0, callContext); + RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(), 0, "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); - response = handler.getTask(MINIMAL_TASK.getId(),2 , callContext); + response = handler.getTask(MINIMAL_TASK.getId(),2 , "",callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -43,7 +43,7 @@ public void testGetTaskSuccess() { public void testGetTaskNotFound() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - RestHandler.HTTPRestResponse response = handler.getTask("nonexistent", 0, callContext); + RestHandler.HTTPRestResponse response = handler.getTask("nonexistent", 0, "",callContext); Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -75,7 +75,7 @@ public void testSendMessage() throws InvalidProtocolBufferException { } }"""; - RestHandler.HTTPRestResponse response = handler.sendMessage(requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.sendMessage(requestBody, "", callContext); Assertions.assertEquals(200, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); Assertions.assertNotNull(response.getBody()); @@ -86,7 +86,7 @@ public void testSendMessageInvalidBody() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); String invalidBody = "invalid json"; - RestHandler.HTTPRestResponse response = handler.sendMessage(invalidBody, callContext); + RestHandler.HTTPRestResponse response = handler.sendMessage(invalidBody, "", callContext); Assertions.assertEquals(400, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -110,7 +110,7 @@ public void testSendMessageWrongValueBody() { } } }"""; - RestHandler.HTTPRestResponse response = handler.sendMessage(requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.sendMessage(requestBody, "", callContext); Assertions.assertEquals(422, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -121,7 +121,7 @@ public void testSendMessageWrongValueBody() { public void testSendMessageEmptyBody() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - RestHandler.HTTPRestResponse response = handler.sendMessage("", callContext); + RestHandler.HTTPRestResponse response = handler.sendMessage("", "", callContext); Assertions.assertEquals(400, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -142,7 +142,7 @@ public void testCancelTaskSuccess() { taskUpdater.cancel(); }; - RestHandler.HTTPRestResponse response = handler.cancelTask(MINIMAL_TASK.getId(), callContext); + RestHandler.HTTPRestResponse response = handler.cancelTask(MINIMAL_TASK.getId(), "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -153,7 +153,7 @@ public void testCancelTaskSuccess() { public void testCancelTaskNotFound() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - RestHandler.HTTPRestResponse response = handler.cancelTask("nonexistent", callContext); + RestHandler.HTTPRestResponse response = handler.cancelTask("nonexistent", "", callContext); Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -183,7 +183,7 @@ public void testSendStreamingMessageSuccess() { } }"""; - RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, "", callContext); Assertions.assertEquals(200, response.getStatusCode(), response.toString()); Assertions.assertInstanceOf(RestHandler.HTTPRestStreamingResponse.class, response); RestHandler.HTTPRestStreamingResponse streamingResponse = (RestHandler.HTTPRestStreamingResponse) response; @@ -206,7 +206,7 @@ public void testSendStreamingMessageNotSupported() { } """; - RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, "", callContext); Assertions.assertEquals(400, response.getStatusCode()); Assertions.assertTrue(response.getBody().contains("InvalidRequestError")); @@ -232,7 +232,7 @@ public void testPushNotificationConfigSuccess() { } }""".formatted(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration( MINIMAL_TASK.getId(), requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration( MINIMAL_TASK.getId(), requestBody, "", callContext); Assertions.assertEquals(201, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); @@ -253,7 +253,7 @@ public void testPushNotificationConfigNotSupported() { } """.formatted(MINIMAL_TASK.getId()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), requestBody, "", callContext); Assertions.assertEquals(501, response.getStatusCode()); Assertions.assertTrue(response.getBody().contains("PushNotificationNotSupportedError")); @@ -279,11 +279,11 @@ public void testGetPushNotificationConfig() { } } }""".formatted(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), createRequestBody, callContext); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), createRequestBody, "", callContext); Assertions.assertEquals(201, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); // Now get it - response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", callContext); + response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", "", callContext); Assertions.assertEquals(200, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); } @@ -292,7 +292,7 @@ public void testGetPushNotificationConfig() { public void testDeletePushNotificationConfig() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", callContext); + RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", "", callContext); Assertions.assertEquals(204, response.getStatusCode()); } @@ -301,7 +301,7 @@ public void testListPushNotificationConfigs() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.getId(), callContext); + RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.getId(), "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); @@ -313,11 +313,11 @@ public void testHttpStatusCodeMapping() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); // Test 400 for invalid request - RestHandler.HTTPRestResponse response = handler.sendMessage("", callContext); + RestHandler.HTTPRestResponse response = handler.sendMessage("", "", callContext); Assertions.assertEquals(400, response.getStatusCode()); // Test 404 for not found - response = handler.getTask("nonexistent", 0, callContext); + response = handler.getTask("nonexistent", 0, "", callContext); Assertions.assertEquals(404, response.getStatusCode()); } @@ -357,7 +357,7 @@ public void testStreamingDoesNotBlockMainThread() throws Exception { }"""; // Start streaming - RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, callContext); + RestHandler.HTTPRestResponse response = handler.sendStreamingMessage(requestBody, "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertInstanceOf(RestHandler.HTTPRestStreamingResponse.class, response); From f71c6988d8f2d6d3d95ee0427ccb54c610138859 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 17 Dec 2025 08:35:55 +0000 Subject: [PATCH 233/493] feat!: Align spec classes with the a2a.proto, removing compatibility code (#546) We do not need to stay compliant with 0.x.0 versions of the spec, and it should be more stable after 1.0. Note this does not remove the 'kind' field, which will be done in https://github.com/a2aproject/a2a-java/issues/544 --- .../io/a2a/grpc/mapper/AgentCardMapper.java | 7 +- .../mapper/TaskStatusUpdateEventMapper.java | 1 + spec/src/main/java/io/a2a/json/JsonUtil.java | 22 +++++ .../java/io/a2a/spec/A2AClientHTTPError.java | 7 ++ spec/src/main/java/io/a2a/spec/AgentCard.java | 50 +++++++--- .../java/io/a2a/spec/AgentCardSignature.java | 3 + .../main/java/io/a2a/spec/AgentExtension.java | 4 + .../main/java/io/a2a/spec/AgentInterface.java | 13 ++- .../main/java/io/a2a/spec/AgentProvider.java | 2 + .../src/main/java/io/a2a/spec/AgentSkill.java | 8 ++ spec/src/main/java/io/a2a/spec/Artifact.java | 6 ++ ...ticatedExtendedCardNotConfiguredError.java | 7 ++ .../java/io/a2a/spec/AuthenticationInfo.java | 2 + .../a2a/spec/AuthorizationCodeOAuthFlow.java | 4 + .../java/io/a2a/spec/CancelTaskRequest.java | 13 +-- .../java/io/a2a/spec/CancelTaskResponse.java | 20 ++++ .../a2a/spec/ClientCredentialsOAuthFlow.java | 3 + .../spec/ContentTypeNotSupportedError.java | 7 ++ spec/src/main/java/io/a2a/spec/DataPart.java | 6 ++ ...eleteTaskPushNotificationConfigParams.java | 3 + ...leteTaskPushNotificationConfigRequest.java | 12 --- spec/src/main/java/io/a2a/spec/FilePart.java | 11 +++ .../GetAuthenticatedExtendedCardRequest.java | 35 ++++++- .../GetAuthenticatedExtendedCardResponse.java | 20 ++++ .../GetTaskPushNotificationConfigParams.java | 45 +++++++++ .../GetTaskPushNotificationConfigRequest.java | 43 ++++++++- ...GetTaskPushNotificationConfigResponse.java | 20 ++++ .../main/java/io/a2a/spec/GetTaskRequest.java | 43 ++++++++- .../java/io/a2a/spec/GetTaskResponse.java | 20 ++++ .../io/a2a/spec/HTTPAuthSecurityScheme.java | 31 ++++++ .../io/a2a/spec/IdJsonMappingException.java | 24 +++++ .../java/io/a2a/spec/ImplicitOAuthFlow.java | 8 ++ .../main/java/io/a2a/spec/InternalError.java | 12 +++ .../a2a/spec/InvalidAgentResponseError.java | 7 ++ .../java/io/a2a/spec/InvalidParamsError.java | 15 +++ .../InvalidParamsJsonMappingException.java | 16 ++++ .../java/io/a2a/spec/InvalidRequestError.java | 15 +++ .../main/java/io/a2a/spec/JSONParseError.java | 15 +++ .../main/java/io/a2a/spec/JSONRPCError.java | 7 ++ .../main/java/io/a2a/spec/JSONRPCRequest.java | 16 ++++ .../java/io/a2a/spec/JSONRPCResponse.java | 26 +++++ .../ListTaskPushNotificationConfigParams.java | 12 +++ ...ListTaskPushNotificationConfigRequest.java | 43 ++++++++- ...istTaskPushNotificationConfigResponse.java | 20 ++++ .../java/io/a2a/spec/ListTasksParams.java | 69 +++++++++++++ .../java/io/a2a/spec/ListTasksRequest.java | 45 +++++++-- .../java/io/a2a/spec/ListTasksResponse.java | 20 ++++ .../java/io/a2a/spec/ListTasksResult.java | 43 +++++++++ spec/src/main/java/io/a2a/spec/Message.java | 82 ++++++++++++++++ .../io/a2a/spec/MessageSendConfiguration.java | 10 ++ .../java/io/a2a/spec/MessageSendParams.java | 16 ++++ .../java/io/a2a/spec/MethodNotFoundError.java | 10 ++ .../MethodNotFoundJsonMappingException.java | 23 +++++ .../io/a2a/spec/MutualTLSSecurityScheme.java | 27 ++++++ .../io/a2a/spec/OAuth2SecurityScheme.java | 64 +++++++++++++ .../src/main/java/io/a2a/spec/OAuthFlows.java | 32 +++++++ .../a2a/spec/OpenIdConnectSecurityScheme.java | 31 ++++++ .../java/io/a2a/spec/PasswordOAuthFlow.java | 8 ++ .../io/a2a/spec/PushNotificationConfig.java | 9 ++ .../PushNotificationNotSupportedError.java | 10 ++ .../java/io/a2a/spec/SendMessageRequest.java | 45 ++++++--- .../java/io/a2a/spec/SendMessageResponse.java | 20 ++++ .../a2a/spec/SendStreamingMessageRequest.java | 25 ++--- .../spec/SendStreamingMessageResponse.java | 20 ++++ .../SetTaskPushNotificationConfigRequest.java | 43 ++++++++- ...SetTaskPushNotificationConfigResponse.java | 20 ++++ .../io/a2a/spec/SubscribeToTaskRequest.java | 43 ++++++++- spec/src/main/java/io/a2a/spec/Task.java | 54 +++++++++++ .../io/a2a/spec/TaskArtifactUpdateEvent.java | 96 +++++++++++++++++++ .../main/java/io/a2a/spec/TaskIdParams.java | 12 +++ .../io/a2a/spec/TaskNotCancelableError.java | 15 +++ .../java/io/a2a/spec/TaskNotFoundError.java | 10 ++ .../a2a/spec/TaskPushNotificationConfig.java | 10 +- .../java/io/a2a/spec/TaskQueryParams.java | 21 ++++ .../src/main/java/io/a2a/spec/TaskStatus.java | 8 ++ .../io/a2a/spec/TaskStatusUpdateEvent.java | 86 +++++++++++++++++ spec/src/main/java/io/a2a/spec/TextPart.java | 13 +++ .../a2a/spec/UnsupportedOperationError.java | 10 ++ spec/src/main/java/io/a2a/util/Utils.java | 2 +- 79 files changed, 1649 insertions(+), 107 deletions(-) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java index 9f9f36ad7..7a27cb57d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java @@ -22,12 +22,13 @@ public interface AgentCardMapper { AgentCardMapper INSTANCE = A2AMappers.getMapper(AgentCardMapper.class); + // Deprecated proto fields - not present in spec API (removed in 1.0.0) + @Mapping(target = "url", ignore = true) + @Mapping(target = "preferredTransport", ignore = true) + @Mapping(target = "additionalInterfaces", ignore = true) @Mapping(target = "provider", source = "provider", conditionExpression = "java(domain.provider() != null)") @Mapping(target = "documentationUrl", source = "documentationUrl", conditionExpression = "java(domain.documentationUrl() != null)") @Mapping(target = "iconUrl", source = "iconUrl", conditionExpression = "java(domain.iconUrl() != null)") - @Mapping(target = "url", ignore = true) // Deprecated in proto, derived from supportedInterfaces[0] - @Mapping(target = "preferredTransport", ignore = true) // Deprecated in proto, derived from supportedInterfaces[0] - @Mapping(target = "additionalInterfaces", ignore = true) // Deprecated in proto, use supportedInterfaces instead io.a2a.grpc.AgentCard toProto(io.a2a.spec.AgentCard domain); @Mapping(target = "provider", source = "provider", conditionExpression = "java(proto.hasProvider())") diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java index fb6cce46f..bcda1795c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java @@ -20,6 +20,7 @@ public interface TaskStatusUpdateEventMapper { /** * Converts domain TaskStatusUpdateEvent to proto. * Uses declarative mapping with CommonFieldMapper for metadata conversion. + * Note: MapStruct derives property "final" from domain's isFinal() getter (strips "is" prefix). */ @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") @Mapping(target = "final", source = "final") diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/spec/src/main/java/io/a2a/json/JsonUtil.java index 82457cd12..2ea564483 100644 --- a/spec/src/main/java/io/a2a/json/JsonUtil.java +++ b/spec/src/main/java/io/a2a/json/JsonUtil.java @@ -58,6 +58,9 @@ import static io.a2a.json.JsonUtil.JSONRPCErrorTypeAdapter.THROWABLE_MARKER_FIELD; +/** + * Utility class for JSON operations. + */ public class JsonUtil { private static GsonBuilder createBaseGsonBuilder() { @@ -86,6 +89,15 @@ private static GsonBuilder createBaseGsonBuilder() { .registerTypeHierarchyAdapter(StreamingEventKind.class, new StreamingEventKindTypeAdapter()) .create(); + /** + * Deserializes JSON string to an object of the specified class. + * + * @param the type of the object to deserialize to + * @param json the JSON string to parse + * @param classOfT the class of the object to deserialize to + * @return the deserialized object + * @throws JsonProcessingException if JSON parsing fails + */ public static T fromJson(String json, Class classOfT) throws JsonProcessingException { try { return OBJECT_MAPPER.fromJson(json, classOfT); @@ -94,6 +106,15 @@ public static T fromJson(String json, Class classOfT) throws JsonProcessi } } + /** + * Deserializes JSON string to an object of the specified type. + * + * @param the type of the object to deserialize to + * @param json the JSON string to parse + * @param type the type of the object to deserialize to (supports generics) + * @return the deserialized object + * @throws JsonProcessingException if JSON parsing fails + */ public static T fromJson(String json, Type type) throws JsonProcessingException { try { return OBJECT_MAPPER.fromJson(json, type); @@ -110,6 +131,7 @@ public static T fromJson(String json, Type type) throws JsonProcessingExcept * * @param data the object to serialize * @return JSON string representation of the object + * @throws JsonProcessingException if conversion fails */ public static String toJson(Object data) throws JsonProcessingException { try { diff --git a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java index 278f01d5d..5e7cd33a1 100644 --- a/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java +++ b/spec/src/main/java/io/a2a/spec/A2AClientHTTPError.java @@ -30,7 +30,14 @@ * @see HTTP Status Codes */ public class A2AClientHTTPError extends A2AClientError { + /** + * The HTTP status code. + */ private final int code; + + /** + * The error message. + */ private final String message; /** diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index 31af2466c..68a0251f6 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -18,6 +18,10 @@ *

    * This class is immutable and uses the Builder pattern for construction to handle the mix of * required and optional fields defined by the specification. + *

    + * Important: The {@link #supportedInterfaces()} field specifies how clients can + * communicate with the agent. Each entry combines a protocol binding (e.g., "JSONRPC", + * "GRPC") with a URL endpoint. The first entry in the list is the preferred interface. * * @param name the human-readable name of the agent (required) * @param description a brief description of the agent's purpose and functionality (required) @@ -32,9 +36,10 @@ * @param securitySchemes map of security scheme names to their definitions (optional) * @param security list of security requirements for accessing the agent (optional) * @param iconUrl URL to an icon representing the agent (optional) - * @param supportedInterfaces ordered list of supported interfaces. First entry is preferred. (required) + * @param supportedInterfaces ordered list of protocol+URL interface combinations; first entry is preferred (required) * @param protocolVersion the version of the A2A Protocol this agent implements (defaults to {@link #DEFAULT_PROTOCOL_VERSION}) * @param signatures digital signatures verifying the authenticity of the agent card (optional) + * @see AgentInterface * @see A2A Protocol Specification */ public record AgentCard( @@ -61,6 +66,22 @@ public record AgentCard( /** * Compact constructor that validates required fields and sets defaults. * + * @param name the name parameter (see class-level JavaDoc) + * @param description the description parameter (see class-level JavaDoc) + * @param provider the provider parameter (see class-level JavaDoc) + * @param version the version parameter (see class-level JavaDoc) + * @param documentationUrl the documentationUrl parameter (see class-level JavaDoc) + * @param capabilities the capabilities parameter (see class-level JavaDoc) + * @param defaultInputModes the defaultInputModes parameter (see class-level JavaDoc) + * @param defaultOutputModes the defaultOutputModes parameter (see class-level JavaDoc) + * @param skills the skills parameter (see class-level JavaDoc) + * @param supportsExtendedAgentCard the supportsExtendedAgentCard parameter (see class-level JavaDoc) + * @param securitySchemes the securitySchemes parameter (see class-level JavaDoc) + * @param security the security parameter (see class-level JavaDoc) + * @param iconUrl the iconUrl parameter (see class-level JavaDoc) + * @param supportedInterfaces the supportedInterfaces parameter (see class-level JavaDoc) + * @param protocolVersion the protocolVersion parameter (see class-level JavaDoc) + * @param signatures the signatures parameter (see class-level JavaDoc) * @throws IllegalArgumentException if any required field is null */ public AgentCard { @@ -78,17 +99,6 @@ public record AgentCard( } } - /** - * Returns the list of additional interfaces. - * - * @return the list of supported interfaces - * @deprecated Use {@link #supportedInterfaces()} instead. This field has been renamed to 'supportedInterfaces'. - */ - @Deprecated(since = "0.4.0", forRemoval = true) - public List additionalInterfaces() { - return supportedInterfaces; - } - /** * Create a new Builder * @@ -362,11 +372,21 @@ public Builder iconUrl(String iconUrl) { } /** - * Sets the ordered list of supported interfaces (first entry is preferred). + * Sets the ordered list of supported protocol interfaces (first entry is preferred). + *

    + * Each interface defines a combination of protocol binding (e.g., "JSONRPC", "GRPC", "REST") + * and URL endpoint for accessing the agent. This is the primary field for declaring how + * clients can communicate with the agent as of protocol version 1.0.0. *

    - * Each interface defines a combination of protocol binding and URL for accessing the agent. + * Example: + *

    {@code
    +         * .supportedInterfaces(List.of(
    +         *     new AgentInterface("JSONRPC", "http://localhost:9999"),
    +         *     new AgentInterface("GRPC", "grpc://localhost:9090")
    +         * ))
    +         * }
    * - * @param supportedInterfaces the ordered list of supported interfaces (optional) + * @param supportedInterfaces the ordered list of supported interfaces (required) * @return this builder for method chaining * @see AgentInterface */ diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java index c9984dee9..aac74a412 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -37,6 +37,9 @@ public record AgentCardSignature(Map header, @SerializedName("pr /** * Compact constructor that validates required fields. * + * @param header the header parameter (see class-level JavaDoc) + * @param protectedHeader the protectedHeader parameter (see class-level JavaDoc) + * @param signature the signature parameter (see class-level JavaDoc) * @throws IllegalArgumentException if protectedHeader or signature is null */ public AgentCardSignature { diff --git a/spec/src/main/java/io/a2a/spec/AgentExtension.java b/spec/src/main/java/io/a2a/spec/AgentExtension.java index 60e541d65..7faf41d8f 100644 --- a/spec/src/main/java/io/a2a/spec/AgentExtension.java +++ b/spec/src/main/java/io/a2a/spec/AgentExtension.java @@ -28,6 +28,10 @@ public record AgentExtension (String description, Map params, bo /** * Compact constructor that validates required fields. * + * @param description the description parameter (see class-level JavaDoc) + * @param params the params parameter (see class-level JavaDoc) + * @param required the required parameter (see class-level JavaDoc) + * @param uri the uri parameter (see class-level JavaDoc) * @throws IllegalArgumentException if uri is null */ public AgentExtension { diff --git a/spec/src/main/java/io/a2a/spec/AgentInterface.java b/spec/src/main/java/io/a2a/spec/AgentInterface.java index 58d65a8da..042d8b56a 100644 --- a/spec/src/main/java/io/a2a/spec/AgentInterface.java +++ b/spec/src/main/java/io/a2a/spec/AgentInterface.java @@ -11,8 +11,8 @@ * are JSONRPC, GRPC, and HTTP+JSON. *

    * Agents may support multiple interfaces to allow flexibility in how clients communicate. - * The {@link AgentCard} includes a primary interface (url + preferredTransport) and may - * list additional interfaces for alternative access methods. + * The {@link AgentCard#supportedInterfaces()} field contains an ordered list of interfaces, + * with the first entry being the preferred method for accessing the agent. *

    * This class is immutable. * @@ -29,6 +29,9 @@ public record AgentInterface(String protocolBinding, String url, String tenant) /** * Compact constructor that validates required fields. * + * @param protocolBinding the protocolBinding parameter (see class-level JavaDoc) + * @param url the url parameter (see class-level JavaDoc) + * @param tenant the tenant parameter (see class-level JavaDoc) * @throws IllegalArgumentException if protocolBinding or url is null */ public AgentInterface { @@ -37,6 +40,12 @@ public record AgentInterface(String protocolBinding, String url, String tenant) Assert.checkNotNullParam("tenant", tenant); } + /** + * Convenience constructor for creating an AgentInterface without a tenant. + * + * @param protocolBinding the protocol binding (see class-level JavaDoc) + * @param url the endpoint URL (see class-level JavaDoc) + */ public AgentInterface(String protocolBinding, String url) { this(protocolBinding, url, ""); } diff --git a/spec/src/main/java/io/a2a/spec/AgentProvider.java b/spec/src/main/java/io/a2a/spec/AgentProvider.java index abab7dc50..b78a7702b 100644 --- a/spec/src/main/java/io/a2a/spec/AgentProvider.java +++ b/spec/src/main/java/io/a2a/spec/AgentProvider.java @@ -24,6 +24,8 @@ public record AgentProvider(String organization, String url) { /** * Compact constructor that validates required fields. * + * @param organization the organization parameter (see class-level JavaDoc) + * @param url the url parameter (see class-level JavaDoc) * @throws IllegalArgumentException if organization or url is null */ public AgentProvider { diff --git a/spec/src/main/java/io/a2a/spec/AgentSkill.java b/spec/src/main/java/io/a2a/spec/AgentSkill.java index 0140a2a7f..5eb8e1755 100644 --- a/spec/src/main/java/io/a2a/spec/AgentSkill.java +++ b/spec/src/main/java/io/a2a/spec/AgentSkill.java @@ -46,6 +46,14 @@ public record AgentSkill(String id, String name, String description, List schemes, String credentials) { /** * Compact constructor that validates required fields. * + * @param schemes the schemes parameter (see class-level JavaDoc) + * @param credentials the credentials parameter (see class-level JavaDoc) * @throws IllegalArgumentException if schemes is null */ public AuthenticationInfo { diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index 351de65e0..b895531c6 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -31,6 +31,10 @@ public record AuthorizationCodeOAuthFlow(String authorizationUrl, String refresh /** * Compact constructor that validates required fields. * + * @param authorizationUrl the authorizationUrl parameter (see class-level JavaDoc) + * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) + * @param scopes the scopes parameter (see class-level JavaDoc) + * @param tokenUrl the tokenUrl parameter (see class-level JavaDoc) * @throws IllegalArgumentException if authorizationUrl, scopes, or tokenUrl is null */ public AuthorizationCodeOAuthFlow { diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java index 0005c41c4..e2667fc45 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java @@ -79,6 +79,7 @@ private Builder() { * @param jsonrpc the JSON-RPC version (optional, defaults to "2.0") * @return this builder for method chaining */ + public CancelTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; @@ -95,23 +96,13 @@ public CancelTaskRequest.Builder id(Object id) { return this; } - /** - * Sets the JSON-RPC method name. - * - * @param method the method name (should be "CancelTask") - * @return this builder for method chaining - * @deprecated - */ - public CancelTaskRequest.Builder method(String method) { - return this; - } - /** * Sets the request parameters containing the task ID to cancel. * * @param params the request parameters (required) * @return this builder for method chaining */ + public CancelTaskRequest.Builder params(TaskIdParams params) { this.params = params; return this; diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java index e8e0ed346..62892641b 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/CancelTaskResponse.java @@ -18,15 +18,35 @@ public final class CancelTaskResponse extends JSONRPCResponse { + /** + * Constructs a CancelTaskResponse with full parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the task result + * @param error the error if any + */ public CancelTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError error) { super(jsonrpc, id, result, error, Task.class); } + /** + * Constructs a CancelTaskResponse with an error. + * + * @param id the request ID + * @param error the error + */ public CancelTaskResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs a CancelTaskResponse with a successful result. + * + * @param id the request ID + * @param result the task result + */ public CancelTaskResponse(Object id, Task result) { this(null, id, result, null); } diff --git a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java index e144559e4..88ce8a127 100644 --- a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java @@ -31,6 +31,9 @@ public record ClientCredentialsOAuthFlow(String refreshUrl, Map /** * Compact constructor that validates required fields. * + * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) + * @param scopes the scopes parameter (see class-level JavaDoc) + * @param tokenUrl the tokenUrl parameter (see class-level JavaDoc) * @throws IllegalArgumentException if scopes or tokenUrl is null */ public ClientCredentialsOAuthFlow { diff --git a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index b1047e728..9bd10192a 100644 --- a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -40,6 +40,13 @@ */ public class ContentTypeNotSupportedError extends JSONRPCError { + /** + * Constructs a content type not supported error. + * + * @param code the error code + * @param message the error message + * @param data additional error data + */ public ContentTypeNotSupportedError(Integer code, String message, Object data) { super(defaultIfNull(code, CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE), defaultIfNull(message, "Incompatible content types"), diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 007c7c2fd..91b16bc4f 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -42,6 +42,12 @@ public class DataPart extends Part> { private final Map data; private final Kind kind; + /** + * Constructs a DataPart with structured data. + * + * @param data the structured data map (required) + * @throws IllegalArgumentException if data is null + */ public DataPart(Map data) { Assert.checkNotNullParam("data", data); this.data = data; diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index 9205275d5..9745ccf2d 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -21,6 +21,9 @@ public record DeleteTaskPushNotificationConfigParams(String id, String pushNotif /** * Compact constructor that validates required fields. * + * @param id the id parameter (see class-level JavaDoc) + * @param pushNotificationConfigId the pushNotificationConfigId parameter (see class-level JavaDoc) + * @param tenant the tenant parameter (see class-level JavaDoc) * @throws IllegalArgumentException if id or pushNotificationConfigId is null */ public DeleteTaskPushNotificationConfigParams { diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java index 509d4d85d..9fcf3ace3 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java @@ -91,18 +91,6 @@ public Builder id(Object id) { return this; } - /** - * Sets the JSON-RPC method name. - * - * @param method the method name (should be "DeleteTaskPushNotificationConfig") - * @return this builder for method chaining - - * @deprecated - */ - public Builder method(String method) { - return this; - } - /** * Sets the request parameters. * diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 0661706d4..5d06e8f26 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -43,6 +43,12 @@ public class FilePart extends Part { private final Kind kind; + /** + * Constructs a FilePart with file content. + * + * @param file the file content (required, either FileWithBytes or FileWithUri) + * @throws IllegalArgumentException if file is null + */ public FilePart(FileContent file) { Assert.checkNotNullParam("file", file); this.file = file; @@ -54,6 +60,11 @@ public Kind getKind() { return kind; } + /** + * Gets the file content contained in this part. + * + * @return the file content (FileWithBytes or FileWithUri) + */ public FileContent getFile() { return file; } diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java index 4e1ffc1a0..894c3510e 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java @@ -28,12 +28,24 @@ */ public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name for getting extended agent card. */ public static final String METHOD = "GetExtendedAgentCard"; + /** + * Constructs request with full parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + */ public GetAuthenticatedExtendedCardRequest(String jsonrpc, Object id) { super(jsonrpc, METHOD, id); } + /** + * Constructs request with ID only (uses default JSON-RPC version). + * + * @param id the request ID + */ public GetAuthenticatedExtendedCardRequest(String id) { this(null, id); } @@ -47,6 +59,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -57,23 +72,33 @@ public static class Builder { private Builder() { } + /** + * Sets the JSON-RPC version. + * + * @param jsonrpc the JSON-RPC version + * @return this builder for method chaining + */ public GetAuthenticatedExtendedCardRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request ID. + * + * @param id the request ID + * @return this builder for method chaining + */ public GetAuthenticatedExtendedCardRequest.Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Builds the instance. + * + * @return a new instance */ - public GetAuthenticatedExtendedCardRequest.Builder method(String method) { - return this; - } - public GetAuthenticatedExtendedCardRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java index d02c11393..ea82af58e 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java @@ -18,14 +18,34 @@ */ public final class GetAuthenticatedExtendedCardResponse extends JSONRPCResponse { + /** + * Constructs response with full parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the agent card result + * @param error the error if any + */ public GetAuthenticatedExtendedCardResponse(String jsonrpc, Object id, AgentCard result, JSONRPCError error) { super(jsonrpc, id, result, error, AgentCard.class); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public GetAuthenticatedExtendedCardResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs successful response. + * + * @param id the request ID + * @param result the agent card result + */ public GetAuthenticatedExtendedCardResponse(Object id, AgentCard result) { this(null, id, result, null); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index 373e89b17..225aa4071 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -20,15 +20,34 @@ */ public record GetTaskPushNotificationConfigParams(String id, @Nullable String pushNotificationConfigId, String tenant) { + /** + * Compact constructor that validates required fields. + * + * @param id the id parameter (see class-level JavaDoc) + * @param pushNotificationConfigId the pushNotificationConfigId parameter (see class-level JavaDoc) + * @param tenant the tenant parameter (see class-level JavaDoc) + * @throws IllegalArgumentException if id or tenant is null + */ public GetTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("tenant", tenant); } + /** + * Convenience constructor for creating parameters with only task ID. + * + * @param id the task identifier (required) + */ public GetTaskPushNotificationConfigParams(String id) { this(id, null, ""); } + /** + * Convenience constructor for creating parameters without tenant. + * + * @param id the task identifier (required) + * @param pushNotificationConfigId optional configuration ID to retrieve + */ public GetTaskPushNotificationConfigParams(String id, String pushNotificationConfigId) { this(id, pushNotificationConfigId, ""); } @@ -42,6 +61,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing GetTaskPushNotificationConfigParams instances. + */ public static class Builder { String id; String pushNotificationConfigId; @@ -53,21 +75,44 @@ public static class Builder { private Builder() { } + /** + * Sets the task ID. + * + * @param id the task ID + * @return this builder for method chaining + */ public Builder id(String id) { this.id = id; return this; } + /** + * Sets the push notification configuration ID. + * + * @param pushNotificationConfigId the configuration ID + * @return this builder for method chaining + */ public Builder pushNotificationConfigId(String pushNotificationConfigId) { this.pushNotificationConfigId = pushNotificationConfigId; return this; } + /** + * Sets the tenant. + * + * @param tenant the tenant + * @return this builder for method chaining + */ public Builder tenant(String tenant) { this.tenant = tenant; return this; } + /** + * Builds the parameters instance. + * + * @return a new GetTaskPushNotificationConfigParams + */ public GetTaskPushNotificationConfigParams build() { return new GetTaskPushNotificationConfigParams(id, pushNotificationConfigId, tenant == null ? "" : tenant); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java index 99b5cd1f9..8023be203 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java @@ -17,12 +17,26 @@ */ public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name. */ public static final String METHOD = "GetTaskPushNotificationConfig"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public GetTaskPushNotificationConfigRequest(String jsonrpc, Object id, GetTaskPushNotificationConfigParams params) { super(jsonrpc, METHOD, id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param params the request parameters + */ public GetTaskPushNotificationConfigRequest(String id, GetTaskPushNotificationConfigParams params) { this(null, id, params); } @@ -36,6 +50,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -47,28 +64,44 @@ public static class Builder { private Builder() { } + /** + * Sets the jsonrpc. + * + * @param jsonrpc the jsonrpc + * @return this builder for method chaining + */ public GetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request ID. + * + * @param id the request ID + * @return this builder for method chaining + */ public GetTaskPushNotificationConfigRequest.Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Sets the request parameters. + * + * @param params the request parameters + * @return this builder for method chaining */ - public GetTaskPushNotificationConfigRequest.Builder method(String method) { - return this; - } - public GetTaskPushNotificationConfigRequest.Builder params(GetTaskPushNotificationConfigParams params) { this.params = params; return this; } + /** + * Builds the instance. + * + * @return a new instance + */ public GetTaskPushNotificationConfigRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java index a42cd98b0..b26d8edf1 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java @@ -15,14 +15,34 @@ */ public final class GetTaskPushNotificationConfigResponse extends JSONRPCResponse { + /** + * Constructs response with full parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the push notification config result + * @param error the error if any + */ public GetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, JSONRPCError error) { super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public GetTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs successful response. + * + * @param id the request ID + * @param result the push notification config result + */ public GetTaskPushNotificationConfigResponse(Object id, TaskPushNotificationConfig result) { this(null, id, result, null); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java index 540030853..12d39ff63 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskRequest.java @@ -18,12 +18,26 @@ */ public final class GetTaskRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name. */ public static final String METHOD = "GetTask"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public GetTaskRequest(String jsonrpc, Object id, TaskQueryParams params) { super(jsonrpc, METHOD, id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param params the request parameters + */ public GetTaskRequest(Object id, TaskQueryParams params) { this(null, id, params); } @@ -37,6 +51,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -48,28 +65,44 @@ public static class Builder { private Builder() { } + /** + * Sets the JSON-RPC version. + * + * @param jsonrpc the JSON-RPC version + * @return this builder for method chaining + */ public GetTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request ID. + * + * @param id the request ID + * @return this builder for method chaining + */ public GetTaskRequest.Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Sets the request parameters. + * + * @param params the request parameters + * @return this builder for method chaining */ - public GetTaskRequest.Builder method(String method) { - return this; - } - public GetTaskRequest.Builder params(TaskQueryParams params) { this.params = params; return this; } + /** + * Builds the instance. + * + * @return a new instance + */ public GetTaskRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java index db1f1b147..291c7309f 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskResponse.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskResponse.java @@ -16,14 +16,34 @@ */ public final class GetTaskResponse extends JSONRPCResponse { + /** + * Constructs response with full parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the task result + * @param error the error if any + */ public GetTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError error) { super(jsonrpc, id, result, error, Task.class); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public GetTaskResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs successful response. + * + * @param id the request ID + * @param result the task result + */ public GetTaskResponse(Object id, Task result) { this(null, id, result, null); } diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 9f7efdada..e2f6433cb 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -36,16 +36,32 @@ */ public final class HTTPAuthSecurityScheme implements SecurityScheme { + /** The HTTP security scheme type identifier. */ public static final String HTTP = "http"; private final String bearerFormat; private final String scheme; private final String description; private final String type; + /** + * Constructs HTTP auth security scheme without type. + * + * @param bearerFormat the bearer token format + * @param scheme the authentication scheme + * @param description the scheme description + */ public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String description) { this(bearerFormat, scheme, description, HTTP); } + /** + * Constructs HTTP auth security scheme with full parameters. + * + * @param bearerFormat the bearer token format + * @param scheme the authentication scheme + * @param description the scheme description + * @param type the security scheme type + */ public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String description, String type) { Assert.checkNotNullParam("scheme", scheme); Assert.checkNotNullParam("type", type); @@ -63,14 +79,29 @@ public String getDescription() { return description; } + /** + * Gets the bearer token format. + * + * @return the bearer format + */ public String getBearerFormat() { return bearerFormat; } + /** + * Gets the authentication scheme. + * + * @return the scheme + */ public String getScheme() { return scheme; } + /** + * Gets the security scheme type. + * + * @return the type + */ public String getType() { return type; } diff --git a/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java b/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java index 00270f909..6d0e1692f 100644 --- a/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java +++ b/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java @@ -2,20 +2,44 @@ import io.a2a.json.JsonMappingException; +/** + * JSON mapping exception that includes request ID for error tracking. + */ public class IdJsonMappingException extends JsonMappingException { + /** + * The JSON-RPC request ID associated with this exception. + */ Object id; + /** + * Constructs exception with message and ID. + * + * @param msg the error message + * @param id the request ID + */ public IdJsonMappingException(String msg, Object id) { super(msg); this.id = id; } + /** + * Constructs exception with message, cause, and ID. + * + * @param msg the error message + * @param cause the cause + * @param id the request ID + */ public IdJsonMappingException(String msg, Throwable cause, Object id) { super(msg, cause); this.id = id; } + /** + * Gets the request ID associated with this exception. + * + * @return the request ID + */ public Object getId() { return id; } diff --git a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java index 646d470cc..93d3f15ac 100644 --- a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java @@ -26,6 +26,14 @@ */ public record ImplicitOAuthFlow(String authorizationUrl, String refreshUrl, Map scopes) { + /** + * Compact constructor that validates required fields. + * + * @param authorizationUrl the authorizationUrl parameter (see class-level JavaDoc) + * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) + * @param scopes the scopes parameter (see class-level JavaDoc) + * @throws IllegalArgumentException if authorizationUrl or scopes is null + */ public ImplicitOAuthFlow { Assert.checkNotNullParam("authorizationUrl", authorizationUrl); Assert.checkNotNullParam("scopes", scopes); diff --git a/spec/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java index 6da5c7634..72f4f825e 100644 --- a/spec/src/main/java/io/a2a/spec/InternalError.java +++ b/spec/src/main/java/io/a2a/spec/InternalError.java @@ -29,6 +29,13 @@ */ public class InternalError extends JSONRPCError { + /** + * Constructs an internal error with full parameters. + * + * @param code the error code + * @param message the error message + * @param data additional error data + */ public InternalError(Integer code, String message, Object data) { super( defaultIfNull(code, INTERNAL_ERROR_CODE), @@ -36,6 +43,11 @@ public InternalError(Integer code, String message, Object data) { data); } + /** + * Constructs an internal error with a message. + * + * @param message the error message + */ public InternalError(String message) { this(null, message, null); } diff --git a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index 830f6e1a2..187260bd9 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -39,6 +39,13 @@ */ public class InvalidAgentResponseError extends JSONRPCError { + /** + * Constructs an invalid agent response error. + * + * @param code the error code + * @param message the error message + * @param data additional error data + */ public InvalidAgentResponseError(Integer code, String message, Object data) { super( defaultIfNull(code, INVALID_AGENT_RESPONSE_ERROR_CODE), diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java index 83e333de5..4556c8fff 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -32,6 +32,13 @@ */ public class InvalidParamsError extends JSONRPCError { + /** + * Constructs an invalid params error with full parameters. + * + * @param code the error code + * @param message the error message + * @param data additional error data + */ public InvalidParamsError(Integer code, String message, Object data) { super( defaultIfNull(code, INVALID_PARAMS_ERROR_CODE), @@ -39,10 +46,18 @@ public InvalidParamsError(Integer code, String message, Object data) { data); } + /** + * Constructs an invalid params error with a message. + * + * @param message the error message + */ public InvalidParamsError(String message) { this(null, message, null); } + /** + * Constructs an invalid params error with default message. + */ public InvalidParamsError() { this(null, null, null); } diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java b/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java index 41aa9a9bc..a3917effb 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java @@ -1,11 +1,27 @@ package io.a2a.spec; +/** + * Exception for invalid parameters during JSON mapping. + */ public class InvalidParamsJsonMappingException extends IdJsonMappingException { + /** + * Constructs exception with message and ID. + * + * @param msg the error message + * @param id the request ID + */ public InvalidParamsJsonMappingException(String msg, Object id) { super(msg, id); } + /** + * Constructs exception with message, cause, and ID. + * + * @param msg the error message + * @param cause the cause + * @param id the request ID + */ public InvalidParamsJsonMappingException(String msg, Throwable cause, Object id) { super(msg, cause, id); } diff --git a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java index 9ba2429fb..7f64a0ccc 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -33,10 +33,20 @@ */ public class InvalidRequestError extends JSONRPCError { + /** + * Constructs an invalid request error with default message. + */ public InvalidRequestError() { this(null, null, null); } + /** + * Constructs an invalid request error with full parameters. + * + * @param code the error code + * @param message the error message + * @param data additional error data + */ public InvalidRequestError(Integer code, String message, Object data) { super( defaultIfNull(code, INVALID_REQUEST_ERROR_CODE), @@ -44,6 +54,11 @@ public InvalidRequestError(Integer code, String message, Object data) { data); } + /** + * Constructs an invalid request error with a message. + * + * @param message the error message + */ public InvalidRequestError(String message) { this(null, message, null); } diff --git a/spec/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java index 07b1bf777..24c4c67d2 100644 --- a/spec/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -28,14 +28,29 @@ */ public class JSONParseError extends JSONRPCError implements A2AError { + /** + * Constructs a JSON parse error with default message. + */ public JSONParseError() { this(null, null, null); } + /** + * Constructs a JSON parse error with a message. + * + * @param message the error message + */ public JSONParseError(String message) { this(null, message, null); } + /** + * Constructs a JSON parse error with full parameters. + * + * @param code the error code + * @param message the error message + * @param data additional error data + */ public JSONParseError( Integer code, String message, diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCError.java b/spec/src/main/java/io/a2a/spec/JSONRPCError.java index 998fc443c..860fde68d 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCError.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCError.java @@ -35,7 +35,14 @@ */ public class JSONRPCError extends Error implements Event, A2AError { + /** + * The numeric error code (see JSON-RPC 2.0 spec for standard codes). + */ private final Integer code; + + /** + * Additional error information (structure defined by the error code). + */ private final Object data; /** diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java index 3c94eb0ac..b09d445e5 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java @@ -30,9 +30,16 @@ */ public abstract sealed class JSONRPCRequest implements JSONRPCMessage permits NonStreamingJSONRPCRequest, StreamingJSONRPCRequest { + /** The JSON-RPC protocol version. */ protected String jsonrpc; + + /** The request identifier. */ protected Object id; + + /** The method name to invoke. */ protected String method; + + /** The method parameters. */ protected T params; /** @@ -75,6 +82,15 @@ public T getParams() { return this.params; } + /** + * Validates and sets JSON-RPC parameters. + * + * @param jsonrpc the JSON-RPC version + * @param method the method name + * @param id the request ID + * @param params the parameters + * @param paramsIsRequired whether parameters are required + */ protected void validateAndSetJsonParameters(String jsonrpc, String method, Object id, T params, boolean paramsIsRequired) { this.jsonrpc = Utils.defaultIfNull(jsonrpc, JSONRPC_VERSION); if (!JSONRPC_VERSION.equals(this.jsonrpc)) { diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java index 0aaff24de..3c6685939 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java @@ -11,14 +11,30 @@ */ public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { + /** The JSON-RPC protocol version. */ protected String jsonrpc; + /** The request identifier this response corresponds to. */ protected Object id; + /** The result of the method invocation. */ protected T result; + /** The error object if the invocation failed. */ protected JSONRPCError error; + /** + * Default constructor for deserialization. + */ public JSONRPCResponse() { } + /** + * Constructs a JSON-RPC response. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the result value + * @param error the error if any + * @param resultType the result type class + */ public JSONRPCResponse(String jsonrpc, Object id, T result, JSONRPCError error, Class resultType) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); @@ -46,10 +62,20 @@ public Object getId() { return this.id; } + /** + * Gets the result value. + * + * @return the result + */ public T getResult() { return this.result; } + /** + * Gets the error object. + * + * @return the error if any + */ public JSONRPCError getError() { return this.error; } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index 564b2a13d..2f71bcceb 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -17,11 +17,23 @@ */ public record ListTaskPushNotificationConfigParams(String id, String tenant) { + /** + * Compact constructor for validation. + * Validates that required parameters are not null. + * + * @param id the task identifier + * @param tenant the tenant identifier + */ public ListTaskPushNotificationConfigParams { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("tenant", tenant); } + /** + * Convenience constructor with default tenant. + * + * @param id the task identifier (required) + */ public ListTaskPushNotificationConfigParams(String id) { this(id, ""); } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java index 5798fa7cb..8f7f3c11f 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java @@ -17,12 +17,26 @@ */ public final class ListTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name. */ public static final String METHOD = "ListTaskPushNotificationConfig"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public ListTaskPushNotificationConfigRequest(String jsonrpc, Object id, ListTaskPushNotificationConfigParams params) { super(jsonrpc, METHOD, id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param params the request parameters + */ public ListTaskPushNotificationConfigRequest(String id, ListTaskPushNotificationConfigParams params) { this(null, id, params); } @@ -36,6 +50,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -47,28 +64,44 @@ public static class Builder { private Builder() { } + /** + * Sets the jsonrpc. + * + * @param jsonrpc the jsonrpc + * @return this builder for method chaining + */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the id. + * + * @param id the id + * @return this builder for method chaining + */ public Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Sets the params. + * + * @param params the params + * @return this builder for method chaining */ - public Builder method(String method) { - return this; - } - public Builder params(ListTaskPushNotificationConfigParams params) { this.params = params; return this; } + /** + * Builds the instance. + * + * @return a new instance + */ public ListTaskPushNotificationConfigRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java index a8d1cc187..2e66b03c9 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java @@ -16,14 +16,34 @@ */ public final class ListTaskPushNotificationConfigResponse extends JSONRPCResponse> { + /** + * Constructs response with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the list of push notification configurations + * @param error the error (if any) + */ public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, List result, JSONRPCError error) { super(jsonrpc, id, result, error, (Class>) (Class) List.class); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public ListTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs success response. + * + * @param id the request ID + * @param result the list of push notification configurations + */ public ListTaskPushNotificationConfigResponse(Object id, List result) { this(null, id, result, null); } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index 1a40e87fc..4e2cc6432 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -27,6 +27,19 @@ public record ListTasksParams( @Nullable Boolean includeArtifacts, String tenant ) { + /** + * Compact constructor for validation. + * Validates that the tenant parameter is not null. + * + * @param contextId filter by context ID + * @param status filter by task status + * @param pageSize maximum number of results per page + * @param pageToken pagination token + * @param historyLength number of history items to include + * @param lastUpdatedAfter filter by last update timestamp + * @param includeArtifacts whether to include artifacts + * @param tenant the tenant identifier + */ public ListTasksParams { Assert.checkNotNullParam("tenant", tenant); } @@ -95,6 +108,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String contextId; private TaskState status; @@ -111,46 +127,99 @@ public static class Builder { private Builder() { } + /** + * Sets the contextId. + * + * @param contextId the contextId + * @return this builder for method chaining + */ public Builder contextId(String contextId) { this.contextId = contextId; return this; } + /** + * Sets the status. + * + * @param status the status + * @return this builder for method chaining + */ public Builder status(TaskState status) { this.status = status; return this; } + /** + * Sets the pageSize. + * + * @param pageSize the pageSize + * @return this builder for method chaining + */ public Builder pageSize(Integer pageSize) { this.pageSize = pageSize; return this; } + /** + * Sets the pageToken. + * + * @param pageToken the pageToken + * @return this builder for method chaining + */ public Builder pageToken(String pageToken) { this.pageToken = pageToken; return this; } + /** + * Sets the historyLength. + * + * @param historyLength the historyLength + * @return this builder for method chaining + */ public Builder historyLength(Integer historyLength) { this.historyLength = historyLength; return this; } + /** + * Sets the lastUpdatedAfter. + * + * @param lastUpdatedAfter the lastUpdatedAfter + * @return this builder for method chaining + */ public Builder lastUpdatedAfter(Instant lastUpdatedAfter) { this.lastUpdatedAfter = lastUpdatedAfter; return this; } + /** + * Sets the includeArtifacts. + * + * @param includeArtifacts the includeArtifacts + * @return this builder for method chaining + */ public Builder includeArtifacts(Boolean includeArtifacts) { this.includeArtifacts = includeArtifacts; return this; } + /** + * Sets the tenant. + * + * @param tenant the tenant + * @return this builder for method chaining + */ public Builder tenant(String tenant) { this.tenant = tenant; return this; } + /** + * Builds the ListTasksParams. + * + * @return a new ListTasksParams instance + */ public ListTasksParams build() { return new ListTasksParams(contextId, status, pageSize, pageToken, historyLength, lastUpdatedAfter, includeArtifacts, tenant); diff --git a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java index 2d855820c..a84a9a361 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksRequest.java @@ -7,12 +7,26 @@ */ public final class ListTasksRequest extends NonStreamingJSONRPCRequest { - public static final String METHOD = "ListTask"; + /** The JSON-RPC method name. */ + public static final String METHOD = "ListTasks"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public ListTasksRequest(String jsonrpc, Object id, ListTasksParams params) { super(jsonrpc, METHOD, id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param params the request parameters + */ public ListTasksRequest(Object id, ListTasksParams params) { this(JSONRPC_VERSION, id, params); } @@ -26,6 +40,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -37,28 +54,44 @@ public static class Builder { private Builder() { } + /** + * Sets the jsonrpc. + * + * @param jsonrpc the jsonrpc + * @return this builder for method chaining + */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the id. + * + * @param id the id + * @return this builder for method chaining + */ public Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Sets the params. + * + * @param params the params + * @return this builder for method chaining */ - public Builder method(String method) { - return this; - } - public Builder params(ListTasksParams params) { this.params = params; return this; } + /** + * Builds the instance. + * + * @return a new instance + */ public ListTasksRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResponse.java b/spec/src/main/java/io/a2a/spec/ListTasksResponse.java index b2540ee5d..2499d8ce1 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksResponse.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksResponse.java @@ -5,14 +5,34 @@ */ public final class ListTasksResponse extends JSONRPCResponse { + /** + * Constructs response with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the result + * @param error the error if any + */ public ListTasksResponse(String jsonrpc, Object id, ListTasksResult result, JSONRPCError error) { super(jsonrpc, id, result, error, ListTasksResult.class); } + /** + * Constructs successful response. + * + * @param id the request ID + * @param result the result + */ public ListTasksResponse(Object id, ListTasksResult result) { this(null, id, result, null); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public ListTasksResponse(Object id, JSONRPCError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResult.java b/spec/src/main/java/io/a2a/spec/ListTasksResult.java index 6a81c386e..959c547a6 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksResult.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksResult.java @@ -19,6 +19,16 @@ public record ListTasksResult( int pageSize, @Nullable String nextPageToken ) { + /** + * Compact constructor for validation. + * Validates parameters and creates a defensive copy of the tasks list. + * + * @param tasks the list of tasks + * @param totalSize total number of tasks available + * @param pageSize number of tasks in this page + * @param nextPageToken token for next page + * @throws IllegalArgumentException if validation fails + */ public ListTasksResult { Assert.checkNotNullParam("tasks", tasks); if (totalSize < 0) { @@ -61,6 +71,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private List tasks; private int totalSize; @@ -73,26 +86,56 @@ public static class Builder { private Builder() { } + /** + * Sets the tasks list. + * + * @param tasks the list of tasks + * @return this builder for method chaining + */ public Builder tasks(List tasks) { this.tasks = tasks; return this; } + /** + * Sets the totalSize. + * + * @param totalSize the totalSize + * @return this builder for method chaining + */ public Builder totalSize(int totalSize) { this.totalSize = totalSize; return this; } + /** + * Sets the pageSize. + * + * @param pageSize the pageSize + * @return this builder for method chaining + */ public Builder pageSize(int pageSize) { this.pageSize = pageSize; return this; } + /** + * Sets the nextPageToken. + * + * @param nextPageToken the nextPageToken + * @return this builder for method chaining + */ public Builder nextPageToken(String nextPageToken) { this.nextPageToken = nextPageToken; return this; } + /** + * Builds the ListTasksResult. + * + * @return a new ListTasksResult instance + * @throws IllegalArgumentException if validation fails + */ public ListTasksResult build() { return new ListTasksResult(tasks, totalSize, pageSize, nextPageToken); } diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index 23456344a..fb8825d48 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -29,6 +29,9 @@ */ public final class Message implements EventKind, StreamingEventKind { + /** + * The kind identifier for Message events: "message". + */ public static final String MESSAGE = "message"; private final Role role; private final List> parts; @@ -40,11 +43,36 @@ public final class Message implements EventKind, StreamingEventKind { private final List referenceTaskIds; private final List extensions; + /** + * Constructs a Message with default kind. + * + * @param role the message role (required) + * @param parts the message content parts (required) + * @param messageId the message identifier (required) + * @param contextId the context identifier (optional) + * @param taskId the task identifier (optional) + * @param referenceTaskIds list of related task IDs (optional) + * @param metadata additional metadata (optional) + * @param extensions list of protocol extensions (optional) + */ public Message(Role role, List> parts, String messageId, String contextId, String taskId, List referenceTaskIds, Map metadata, List extensions) { this(role, parts, messageId, contextId, taskId, referenceTaskIds, metadata, extensions, MESSAGE); } + /** + * Constructs a Message with all parameters. + * + * @param role the message role (required) + * @param parts the message content parts (required) + * @param messageId the message identifier (required) + * @param contextId the context identifier (optional) + * @param taskId the task identifier (optional) + * @param referenceTaskIds list of related task IDs (optional) + * @param metadata additional metadata (optional) + * @param extensions list of protocol extensions (optional) + * @param kind the event kind (must be "message") + */ public Message(Role role, List> parts, String messageId, String contextId, String taskId, List referenceTaskIds, @@ -71,42 +99,96 @@ public Message(Role role, List> parts, this.kind = kind; } + /** + * Returns the role of the message sender. + * + * @return the message role (USER or AGENT) + */ public Role getRole() { return role; } + /** + * Returns the content parts of this message. + * + * @return an immutable list of message parts + */ public List> getParts() { return parts; } + /** + * Returns the unique identifier for this message. + * + * @return the message ID + */ public String getMessageId() { return messageId; } + /** + * Returns the conversation context identifier. + * + * @return the context ID, or null if not set + */ public String getContextId() { return contextId; } + /** + * Returns the task identifier this message is associated with. + * + * @return the task ID, or null if not set + */ public String getTaskId() { return taskId; } + /** + * Returns the metadata associated with this message. + * + * @return a map of metadata key-value pairs, or null if not set + */ public Map getMetadata() { return metadata; } + /** + * Sets the task identifier for this message. + *

    + * This method allows associating the message with a task after construction. + * + * @param taskId the task ID to set + */ public void setTaskId(String taskId) { this.taskId = taskId; } + /** + * Sets the conversation context identifier for this message. + *

    + * This method allows associating the message with a context after construction. + * + * @param contextId the context ID to set + */ public void setContextId(String contextId) { this.contextId = contextId; } + /** + * Returns the list of reference task identifiers this message relates to. + * + * @return a list of task IDs, or null if not set + */ public List getReferenceTaskIds() { return referenceTaskIds; } + /** + * Returns the list of protocol extensions used in this message. + * + * @return a list of extension identifiers, or null if not set + */ public List getExtensions() { return extensions; } diff --git a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java index ee55964ca..4082d2501 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendConfiguration.java @@ -24,6 +24,16 @@ public record MessageSendConfiguration(List acceptedOutputModes, Integer historyLength, PushNotificationConfig pushNotificationConfig, Boolean blocking) { + /** + * Compact constructor for validation. + * Validates that historyLength is non-negative if provided. + * + * @param acceptedOutputModes list of accepted output modes + * @param historyLength maximum number of history items + * @param pushNotificationConfig push notification configuration + * @param blocking whether the request should block + * @throws IllegalArgumentException if historyLength is negative + */ public MessageSendConfiguration { if (historyLength != null && historyLength < 0) { throw new IllegalArgumentException("Invalid history length"); diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index e3b863bde..4dca5f806 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -26,11 +26,27 @@ public record MessageSendParams(Message message, MessageSendConfiguration configuration, Map metadata, String tenant) { + /** + * Compact constructor for validation. + * Validates that required parameters are not null. + * + * @param message the message to send + * @param configuration optional message send configuration + * @param metadata optional metadata + * @param tenant the tenant identifier + */ public MessageSendParams { Assert.checkNotNullParam("message", message); Assert.checkNotNullParam("tenant", tenant); } + /** + * Convenience constructor with default tenant. + * + * @param message the message to send (required) + * @param configuration optional configuration for message processing + * @param metadata optional metadata + */ public MessageSendParams(Message message, MessageSendConfiguration configuration, Map metadata) { this(message, configuration, metadata, ""); } diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java index 3f1fe36de..c56bbcf1d 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -24,6 +24,13 @@ */ public class MethodNotFoundError extends JSONRPCError { + /** + * Constructs error with all parameters. + * + * @param code the error code (defaults to -32601 if null) + * @param message the error message (defaults to "Method not found" if null) + * @param data additional error data (optional) + */ public MethodNotFoundError( Integer code, String message, @@ -34,6 +41,9 @@ public MethodNotFoundError( data); } + /** + * Constructs error with default message. + */ public MethodNotFoundError() { this(METHOD_NOT_FOUND_ERROR_CODE, null, null); } diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java index 7bd167ad1..dc1f13c03 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java @@ -1,11 +1,34 @@ package io.a2a.spec; +/** + * Exception thrown when JSON mapping fails due to a method not found error. + *

    + * This exception is used during JSON-RPC request deserialization when the requested + * method cannot be found or mapped. It extends {@link IdJsonMappingException} to + * preserve the request ID for error responses. + * + * @see IdJsonMappingException + * @see MethodNotFoundError + */ public class MethodNotFoundJsonMappingException extends IdJsonMappingException { + /** + * Constructs exception with message and request ID. + * + * @param msg the error message + * @param id the JSON-RPC request ID + */ public MethodNotFoundJsonMappingException(String msg, Object id) { super(msg, id); } + /** + * Constructs exception with message, cause, and request ID. + * + * @param msg the error message + * @param cause the underlying cause + * @param id the JSON-RPC request ID + */ public MethodNotFoundJsonMappingException(String msg, Throwable cause, Object id) { super(msg, cause, id); } diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index a2107b0b2..f7b9dc1f9 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -29,18 +29,35 @@ */ public final class MutualTLSSecurityScheme implements SecurityScheme { + /** + * The type identifier for mutual TLS security schemes: "mutualTLS". + */ public static final String MUTUAL_TLS = "mutualTLS"; private final String description; private final String type; + /** + * Constructs a mutual TLS security scheme with the specified description. + * + * @param description optional description of the security scheme + */ public MutualTLSSecurityScheme(String description) { this(description, MUTUAL_TLS); } + /** + * Constructs a mutual TLS security scheme with no description. + */ public MutualTLSSecurityScheme() { this(null, MUTUAL_TLS); } + /** + * Constructs a mutual TLS security scheme with the specified description and type. + * + * @param description optional description of the security scheme + * @param type the security scheme type (must be "mutualTLS") + */ public MutualTLSSecurityScheme(String description, String type) { Assert.checkNotNullParam("type", type); if (!type.equals(MUTUAL_TLS)) { @@ -50,11 +67,21 @@ public MutualTLSSecurityScheme(String description, String type) { this.type = type; } + /** + * Gets the Description. + * + * @return the Description + */ @Override public String getDescription() { return description; } + /** + * Gets the Type. + * + * @return the Type + */ public String getType() { return type; } diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 7ea145a40..9194069ef 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -20,16 +20,34 @@ */ public final class OAuth2SecurityScheme implements SecurityScheme { + /** + * The type identifier for OAuth 2.0 security schemes: "oauth2". + */ public static final String OAUTH2 = "oauth2"; private final OAuthFlows flows; private final String description; private final String type; private final String oauth2MetadataUrl; + /** + * Constructs an OAuth 2.0 security scheme. + * + * @param flows the OAuth 2.0 flow configuration (required) + * @param description optional description of the security scheme + * @param oauth2MetadataUrl optional URL to OAuth 2.0 metadata (RFC 8414) + */ public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2MetadataUrl) { this(flows, description, oauth2MetadataUrl, OAUTH2); } + /** + * Constructs an OAuth 2.0 security scheme with explicit type. + * + * @param flows the OAuth 2.0 flow configuration (required) + * @param description optional description of the security scheme + * @param oauth2MetadataUrl optional URL to OAuth 2.0 metadata (RFC 8414) + * @param type the security scheme type (must be "oauth2") + */ public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2MetadataUrl, String type) { Assert.checkNotNullParam("flows", flows); Assert.checkNotNullParam("type", type); @@ -42,19 +60,39 @@ public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2M this.type = type; } + /** + * Gets the Description. + * + * @return the Description + */ @Override public String getDescription() { return description; } + /** + * Returns the OAuth flows configuration. + * + * @return the OAuth flows + */ public OAuthFlows getFlows() { return flows; } + /** + * Gets the Type. + * + * @return the Type + */ public String getType() { return type; } + /** + * Gets the Oauth2MetadataUrl. + * + * @return the Oauth2MetadataUrl + */ public String getOauth2MetadataUrl() { return oauth2MetadataUrl; } @@ -68,6 +106,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private OAuthFlows flows; private String description; @@ -79,21 +120,44 @@ public static class Builder { private Builder() { } + /** + * Sets the flows. + * + * @param flows the flows + * @return this builder for method chaining + */ public Builder flows(OAuthFlows flows) { this.flows = flows; return this; } + /** + * Sets the description. + * + * @param description the description + * @return this builder for method chaining + */ public Builder description(String description) { this.description = description; return this; } + /** + * Sets the oauth2MetadataUrl. + * + * @param oauth2MetadataUrl the oauth2MetadataUrl + * @return this builder for method chaining + */ public Builder oauth2MetadataUrl(String oauth2MetadataUrl) { this.oauth2MetadataUrl = oauth2MetadataUrl; return this; } + /** + * Builds the OAuth2SecurityScheme. + * + * @return a new OAuth2SecurityScheme instance + */ public OAuth2SecurityScheme build() { return new OAuth2SecurityScheme(flows, description, oauth2MetadataUrl); } diff --git a/spec/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java index eda14adc5..bcddf7f43 100644 --- a/spec/src/main/java/io/a2a/spec/OAuthFlows.java +++ b/spec/src/main/java/io/a2a/spec/OAuthFlows.java @@ -28,6 +28,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing {@link OAuthFlows} instances. + */ public static class Builder { private AuthorizationCodeOAuthFlow authorizationCode; private ClientCredentialsOAuthFlow clientCredentials; @@ -40,26 +43,55 @@ public static class Builder { private Builder() { } + /** + * Sets the authorization code flow configuration. + * + * @param authorizationCode the authorization code flow (optional) + * @return this builder for method chaining + */ public Builder authorizationCode(AuthorizationCodeOAuthFlow authorizationCode) { this.authorizationCode = authorizationCode; return this; } + /** + * Sets the client credentials flow configuration. + * + * @param clientCredentials the client credentials flow (optional) + * @return this builder for method chaining + */ public Builder clientCredentials(ClientCredentialsOAuthFlow clientCredentials) { this.clientCredentials = clientCredentials; return this; } + /** + * Sets the implicit flow configuration. + * + * @param implicit the implicit flow (optional) + * @return this builder for method chaining + */ public Builder implicit(ImplicitOAuthFlow implicit) { this.implicit = implicit; return this; } + /** + * Sets the password flow configuration. + * + * @param password the password flow (optional) + * @return this builder for method chaining + */ public Builder password(PasswordOAuthFlow password) { this.password = password; return this; } + /** + * Builds a new immutable OAuthFlows instance. + * + * @return a new OAuthFlows instance + */ public OAuthFlows build() { return new OAuthFlows(authorizationCode, clientCredentials, implicit, password); } diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 050e660ac..1b971828b 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -30,15 +30,31 @@ */ public final class OpenIdConnectSecurityScheme implements SecurityScheme { + /** + * The type identifier for OpenID Connect security schemes: "openIdConnect". + */ public static final String OPENID_CONNECT = "openIdConnect"; private final String openIdConnectUrl; private final String description; private final String type; + /** + * Constructs an OpenID Connect security scheme. + * + * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) + * @param description optional description of the security scheme + */ public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description) { this(openIdConnectUrl, description, OPENID_CONNECT); } + /** + * Constructs an OpenID Connect security scheme with explicit type. + * + * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) + * @param description optional description of the security scheme + * @param type the security scheme type (must be "openIdConnect") + */ public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description, String type) { Assert.checkNotNullParam("type", type); Assert.checkNotNullParam("openIdConnectUrl", openIdConnectUrl); @@ -50,15 +66,30 @@ public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description, this.type = type; } + /** + * Gets the Description. + * + * @return the Description + */ @Override public String getDescription() { return description; } + /** + * Gets the OpenIdConnectUrl. + * + * @return the OpenIdConnectUrl + */ public String getOpenIdConnectUrl() { return openIdConnectUrl; } + /** + * Gets the Type. + * + * @return the Type + */ public String getType() { return type; } diff --git a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java index e8b8d0bc5..a6a3a8803 100644 --- a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java @@ -25,6 +25,14 @@ */ public record PasswordOAuthFlow(String refreshUrl, Map scopes, String tokenUrl) { + /** + * Compact constructor that validates required fields. + * + * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) + * @param scopes the scopes parameter (see class-level JavaDoc) + * @param tokenUrl the tokenUrl parameter (see class-level JavaDoc) + * @throws IllegalArgumentException if scopes or tokenUrl is null + */ public PasswordOAuthFlow { Assert.checkNotNullParam("scopes", scopes); Assert.checkNotNullParam("tokenUrl", tokenUrl); diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java index ed932ab9c..6fe62ff1c 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationConfig.java @@ -27,6 +27,15 @@ */ public record PushNotificationConfig(String url, String token, AuthenticationInfo authentication, String id) { + /** + * Compact constructor for validation. + * Validates that the URL is not null. + * + * @param url the notification endpoint URL + * @param token optional bearer token + * @param authentication optional authentication info + * @param id optional configuration identifier + */ public PushNotificationConfig { Assert.checkNotNullParam("url", url); } diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index 0cb7c1850..0dd571955 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -30,10 +30,20 @@ */ public class PushNotificationNotSupportedError extends JSONRPCError { + /** + * Constructs error with default message. + */ public PushNotificationNotSupportedError() { this(null, null, null); } + /** + * Constructs error with all parameters. + * + * @param code the error code (defaults to -32003 if null) + * @param message the error message (defaults to "Push Notification is not supported" if null) + * @param data additional error data (optional) + */ public PushNotificationNotSupportedError( Integer code, String message, diff --git a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java index aece5dc5b..23b44dbaa 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageRequest.java @@ -3,13 +3,13 @@ import java.util.UUID; /** - * JSON-RPC request for the {@code message/send} method in the A2A Protocol. + * JSON-RPC request for the {@code SendMessage} method in the A2A Protocol. *

    * This request initiates a new task or continues an existing task by sending a message * to an agent. The request returns a single response containing the final {@link Task} - * state once processing completes. + * state once processing completes (blocking/non-streaming mode). *

    - * The {@code message/send} method is used for: + * The {@code SendMessage} method is used for: *

      *
    • Creating new tasks with an initial user message
    • *
    • Continuing existing tasks with additional messages
    • @@ -17,9 +17,10 @@ *
    *

    * For real-time event streaming during task execution, use {@link SendStreamingMessageRequest} - * with the {@code message/stream} method instead. + * with the {@code SendStreamingMessage} method instead. *

    - * This class includes a fluent {@link Builder} for convenient request construction. + * This class includes a fluent {@link Builder} for convenient request construction. The method + * name is fixed as {@value #METHOD} and is set automatically by the constructor. * * @see SendStreamingMessageRequest * @see MessageSendParams @@ -70,7 +71,15 @@ public static Builder builder() { /** * Builder for constructing {@link SendMessageRequest} instances with a fluent API. *

    + * The method name is automatically set to {@value SendMessageRequest#METHOD} and cannot be changed. * If no ID is provided, a UUID will be auto-generated when {@link #build()} is called. + *

    + * Example usage: + *

    {@code
    +     * SendMessageRequest request = SendMessageRequest.builder()
    +     *     .params(new MessageSendParams(message, taskId, pushNotificationConfig))
    +     *     .build();
    +     * }
    */ public static class Builder { private String jsonrpc; @@ -83,28 +92,34 @@ public static class Builder { private Builder() { } - /** Sets the JSON-RPC version. */ + /** + * Sets the JSON-RPC version. + * + * @param jsonrpc the JSON-RPC version (typically "2.0") + * @return this builder for method chaining + */ public Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } - /** Sets the request correlation ID. */ + /** + * Sets the request correlation ID. + * + * @param id the request correlation identifier + * @return this builder for method chaining + */ public Builder id(Object id) { this.id = id; return this; } /** - * Sets the method name. + * Sets the message send parameters (required). * - * @deprecated - * */ - public Builder method(String method) { - return this; - } - - /** Sets the message send parameters (required). */ + * @param params the message send parameters + * @return this builder for method chaining + */ public Builder params(MessageSendParams params) { this.params = params; return this; diff --git a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java index 14d3d5197..da01aa827 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendMessageResponse.java @@ -5,14 +5,34 @@ */ public final class SendMessageResponse extends JSONRPCResponse { + /** + * Constructs response with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the result + * @param error the error if any + */ public SendMessageResponse(String jsonrpc, Object id, EventKind result, JSONRPCError error) { super(jsonrpc, id, result, error, EventKind.class); } + /** + * Constructs successful response. + * + * @param id the request ID + * @param result the result + */ public SendMessageResponse(Object id, EventKind result) { this(null, id, result, null); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public SendMessageResponse(Object id, JSONRPCError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java index 9390d6fc9..9148133c3 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java @@ -21,12 +21,26 @@ */ public final class SendStreamingMessageRequest extends StreamingJSONRPCRequest { + /** The JSON-RPC method name. */ public static final String METHOD = "SendStreamingMessage"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public SendStreamingMessageRequest(String jsonrpc, Object id, MessageSendParams params) { super(jsonrpc, METHOD, id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param params the request parameters + */ public SendStreamingMessageRequest(Object id, MessageSendParams params) { this(null, id, params); } @@ -79,17 +93,6 @@ public Builder id(Object id) { return this; } - /** - * Sets the JSON-RPC method name. - * - * @param method the method name (defaults to "SendStreamingMessage") - * @return this builder - * @deprecated - */ - public Builder method(String method) { - return this; - } - /** * Sets the message send parameters. * diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java index 271511b45..d442ea247 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java +++ b/spec/src/main/java/io/a2a/spec/SendStreamingMessageResponse.java @@ -19,14 +19,34 @@ */ public final class SendStreamingMessageResponse extends JSONRPCResponse { + /** + * Constructs response with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the result + * @param error the error if any + */ public SendStreamingMessageResponse(String jsonrpc, Object id, StreamingEventKind result, JSONRPCError error) { super(jsonrpc, id, result, error, StreamingEventKind.class); } + /** + * Constructs successful response. + * + * @param id the request ID + * @param result the result + */ public SendStreamingMessageResponse(Object id, StreamingEventKind result) { this(null, id, result, null); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public SendStreamingMessageResponse(Object id, JSONRPCError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java index e303ba2bd..b60c9731f 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigRequest.java @@ -18,12 +18,26 @@ */ public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { + /** The JSON-RPC method name. */ public static final String METHOD = "SetTaskPushNotificationConfig"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public SetTaskPushNotificationConfigRequest(String jsonrpc, Object id, TaskPushNotificationConfig params) { super(jsonrpc, METHOD, id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param taskPushConfig the task push notification configuration + */ public SetTaskPushNotificationConfigRequest(String id, TaskPushNotificationConfig taskPushConfig) { this(null, id, taskPushConfig); } @@ -37,6 +51,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -48,28 +65,44 @@ public static class Builder { private Builder() { } + /** + * Sets the JSON-RPC version. + * + * @param jsonrpc the JSON-RPC version + * @return this builder for method chaining + */ public SetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request ID. + * + * @param id the request ID + * @return this builder for method chaining + */ public SetTaskPushNotificationConfigRequest.Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Sets the request parameters. + * + * @param params the request parameters + * @return this builder for method chaining */ - public SetTaskPushNotificationConfigRequest.Builder method(String method) { - return this; - } - public SetTaskPushNotificationConfigRequest.Builder params(TaskPushNotificationConfig params) { this.params = params; return this; } + /** + * Builds the instance. + * + * @return a new instance + */ public SetTaskPushNotificationConfigRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java index 8cc710d54..752c0de03 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java @@ -17,14 +17,34 @@ */ public final class SetTaskPushNotificationConfigResponse extends JSONRPCResponse { + /** + * Constructs response with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param result the push notification configuration + * @param error the error (if any) + */ public SetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, JSONRPCError error) { super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } + /** + * Constructs error response. + * + * @param id the request ID + * @param error the error + */ public SetTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { this(null, id, null, error); } + /** + * Constructs success response. + * + * @param id the request ID + * @param result the push notification configuration + */ public SetTaskPushNotificationConfigResponse(Object id, TaskPushNotificationConfig result) { this(null, id, result, null); } diff --git a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java index 36de4e1ad..ba34bd4fd 100644 --- a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java +++ b/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java @@ -24,12 +24,26 @@ */ public final class SubscribeToTaskRequest extends StreamingJSONRPCRequest { + /** The JSON-RPC method name. */ public static final String METHOD = "SubscribeToTask"; + /** + * Constructs request with all parameters. + * + * @param jsonrpc the JSON-RPC version + * @param id the request ID + * @param params the request parameters + */ public SubscribeToTaskRequest(String jsonrpc, Object id, TaskIdParams params) { super(jsonrpc, METHOD, id == null ? UUID.randomUUID().toString() : id, params); } + /** + * Constructs request with ID and parameters. + * + * @param id the request ID + * @param params the request parameters + */ public SubscribeToTaskRequest(Object id, TaskIdParams params) { this(null, id, params); } @@ -43,6 +57,9 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for constructing instances. + */ public static class Builder { private String jsonrpc; private Object id; @@ -54,28 +71,44 @@ public static class Builder { private Builder() { } + /** + * Sets the JSON-RPC version. + * + * @param jsonrpc the JSON-RPC version + * @return this builder for method chaining + */ public SubscribeToTaskRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } + /** + * Sets the request ID. + * + * @param id the request ID + * @return this builder for method chaining + */ public SubscribeToTaskRequest.Builder id(Object id) { this.id = id; return this; } /** - * @deprecated + * Sets the request parameters. + * + * @param params the request parameters + * @return this builder for method chaining */ - public SubscribeToTaskRequest.Builder method(String method) { - return this; - } - public SubscribeToTaskRequest.Builder params(TaskIdParams params) { this.params = params; return this; } + /** + * Builds the instance. + * + * @return a new instance + */ public SubscribeToTaskRequest build() { if (id == null) { id = UUID.randomUUID().toString(); diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index f62ba95ed..383a3a8a8 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -43,6 +43,9 @@ @NullMarked public final class Task implements EventKind, StreamingEventKind { + /** + * The kind identifier for Task events: "task". + */ public static final String TASK = "task"; private final String id; private final String contextId; @@ -52,11 +55,32 @@ public final class Task implements EventKind, StreamingEventKind { private final Map metadata; private final String kind; + /** + * Constructs a Task with default kind. + * + * @param id the task identifier (required) + * @param contextId the context identifier (required) + * @param status the task status (required) + * @param artifacts the list of artifacts (optional) + * @param history the conversation history (optional) + * @param metadata additional metadata (optional) + */ public Task(String id, String contextId, TaskStatus status, List artifacts, List history, Map metadata) { this(id, contextId, status, artifacts, history, metadata, TASK); } + /** + * Constructs a Task with all parameters. + * + * @param id the task identifier (required) + * @param contextId the context identifier (required) + * @param status the task status (required) + * @param artifacts the list of artifacts (optional) + * @param history the conversation history (optional) + * @param metadata additional metadata (optional) + * @param kind the event kind (must be "task") + */ public Task(String id, String contextId, TaskStatus status, @Nullable List artifacts, @Nullable List history, Map metadata, String kind) { @@ -76,26 +100,56 @@ public Task(String id, String contextId, TaskStatus status, this.kind = kind; } + /** + * Returns the unique identifier for this task. + * + * @return the task ID + */ public String getId() { return id; } + /** + * Returns the conversation context identifier. + * + * @return the context ID + */ public String getContextId() { return contextId; } + /** + * Returns the current status of this task. + * + * @return the task status + */ public TaskStatus getStatus() { return status; } + /** + * Returns the artifacts produced by the agent during task execution. + * + * @return an immutable list of artifacts + */ public List getArtifacts() { return artifacts; } + /** + * Returns the conversation history for this task. + * + * @return an immutable list of messages + */ public List getHistory() { return history; } + /** + * Returns the metadata associated with this task. + * + * @return a map of metadata key-value pairs, or null if not set + */ public Map getMetadata() { return metadata; } diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 9bda83d29..11f9604ad 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -36,6 +36,9 @@ */ public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { + /** + * The kind identifier for artifact update events: "artifact-update". + */ public static final String ARTIFACT_UPDATE = "artifact-update"; private final String taskId; private final Boolean append; @@ -45,10 +48,31 @@ public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventK private final Map metadata; private final String kind; + /** + * Constructs a TaskArtifactUpdateEvent with default kind. + * + * @param taskId the task identifier (required) + * @param artifact the artifact being updated (required) + * @param contextId the context identifier (required) + * @param append whether to append to existing artifact (optional) + * @param lastChunk whether this is the final chunk (optional) + * @param metadata additional metadata (optional) + */ public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextId, Boolean append, Boolean lastChunk, Map metadata) { this(taskId, artifact, contextId, append, lastChunk, metadata, ARTIFACT_UPDATE); } + /** + * Constructs a TaskArtifactUpdateEvent with all parameters. + * + * @param taskId the task identifier (required) + * @param artifact the artifact being updated (required) + * @param contextId the context identifier (required) + * @param append whether to append to existing artifact (optional) + * @param lastChunk whether this is the final chunk (optional) + * @param metadata additional metadata (optional) + * @param kind the event kind (must be "artifact-update") + */ public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextId, Boolean append, Boolean lastChunk, Map metadata, String kind) { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("artifact", artifact); @@ -66,26 +90,56 @@ public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextI this.kind = kind; } + /** + * Returns the task identifier. + * + * @return the task ID + */ public String getTaskId() { return taskId; } + /** + * Returns the artifact being updated. + * + * @return the artifact + */ public Artifact getArtifact() { return artifact; } + /** + * Returns the context identifier. + * + * @return the context ID + */ public String getContextId() { return contextId; } + /** + * Returns whether this update should append to an existing artifact. + * + * @return true if appending, false or null for new artifact + */ public Boolean isAppend() { return append; } + /** + * Returns whether this is the final chunk for the artifact. + * + * @return true if final chunk, false or null otherwise + */ public Boolean isLastChunk() { return lastChunk; } + /** + * Returns the metadata associated with this event. + * + * @return a map of metadata key-value pairs, or null if not set + */ public Map getMetadata() { return metadata; } @@ -164,36 +218,78 @@ private Builder(TaskArtifactUpdateEvent existingTaskArtifactUpdateEvent) { this.metadata = existingTaskArtifactUpdateEvent.metadata; } + /** + * Sets the task identifier. + * + * @param taskId the task ID (required) + * @return this builder for method chaining + */ public Builder taskId(String taskId) { this.taskId = taskId; return this; } + /** + * Sets the artifact being updated. + * + * @param artifact the artifact (required) + * @return this builder for method chaining + */ public Builder artifact(Artifact artifact) { this.artifact = artifact; return this; } + /** + * Sets the context identifier. + * + * @param contextId the context ID (required) + * @return this builder for method chaining + */ public Builder contextId(String contextId) { this.contextId = contextId; return this; } + /** + * Sets whether this update should append to an existing artifact. + * + * @param append true to append, false or null for new artifact + * @return this builder for method chaining + */ public Builder append(Boolean append) { this.append = append; return this; } + /** + * Sets whether this is the final chunk for the artifact. + * + * @param lastChunk true if final chunk, false or null otherwise + * @return this builder for method chaining + */ public Builder lastChunk(Boolean lastChunk) { this.lastChunk = lastChunk; return this; } + /** + * Sets the metadata for this event. + * + * @param metadata map of metadata key-value pairs (optional) + * @return this builder for method chaining + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Builds the TaskArtifactUpdateEvent. + * + * @return a new TaskArtifactUpdateEvent instance + * @throws IllegalArgumentException if required fields are missing + */ public TaskArtifactUpdateEvent build() { return new TaskArtifactUpdateEvent(taskId, artifact, contextId, append, lastChunk, metadata); } diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index b3b476c58..584cca60c 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -17,11 +17,23 @@ */ public record TaskIdParams(String id, String tenant) { + /** + * Compact constructor for validation. + * Validates that required parameters are not null. + * + * @param id the task identifier + * @param tenant the tenant identifier + */ public TaskIdParams { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("tenant", tenant); } + /** + * Convenience constructor with default tenant. + * + * @param id the task identifier (required) + */ public TaskIdParams(String id) { this(id, ""); } diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index fad3c8cb7..ad39cda16 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -32,10 +32,20 @@ */ public class TaskNotCancelableError extends JSONRPCError { + /** + * Constructs error with default message. + */ public TaskNotCancelableError() { this(null, null, null); } + /** + * Constructs error with all parameters. + * + * @param code the error code (defaults to -32002 if null) + * @param message the error message (defaults to "Task cannot be canceled" if null) + * @param data additional error data (optional) + */ public TaskNotCancelableError( Integer code, String message, @@ -46,6 +56,11 @@ public TaskNotCancelableError( data); } + /** + * Constructs error with custom message. + * + * @param message the error message + */ public TaskNotCancelableError(String message) { this(null, message, null); } diff --git a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index 8c277e2e6..4366db5b8 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -35,10 +35,20 @@ */ public class TaskNotFoundError extends JSONRPCError { + /** + * Constructs error with default message. + */ public TaskNotFoundError() { this(null, null, null); } + /** + * Constructs error with all parameters. + * + * @param code the error code (defaults to -32001 if null) + * @param message the error message (defaults to "Task not found" if null) + * @param data additional error data (optional) + */ public TaskNotFoundError( Integer code, String message, diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index dad97becd..794a29e2a 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -22,9 +22,17 @@ */ public record TaskPushNotificationConfig(String taskId, PushNotificationConfig pushNotificationConfig, @Nullable String tenant) { + /** + * Compact constructor for validation. + * Validates that required parameters are not null. + * + * @param taskId the task identifier + * @param pushNotificationConfig the push notification configuration + * @param tenant the tenant identifier + */ public TaskPushNotificationConfig { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("pushNotificationConfig", pushNotificationConfig); Assert.checkNotNullParam("configId", pushNotificationConfig.id()); - } + } } diff --git a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java index 7add2868f..15717ee3b 100644 --- a/spec/src/main/java/io/a2a/spec/TaskQueryParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskQueryParams.java @@ -12,6 +12,15 @@ */ public record TaskQueryParams(String id, @Nullable Integer historyLength, String tenant) { + /** + * Compact constructor for validation. + * Validates that required parameters are not null and historyLength is non-negative if provided. + * + * @param id the task identifier + * @param historyLength maximum number of history items + * @param tenant the tenant identifier + * @throws IllegalArgumentException if historyLength is negative + */ public TaskQueryParams { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("tenant", tenant); @@ -19,10 +28,22 @@ public record TaskQueryParams(String id, @Nullable Integer historyLength, String throw new IllegalArgumentException("Invalid history length"); } } + + /** + * Convenience constructor with default tenant. + * + * @param id the task identifier (required) + * @param historyLength maximum number of history items to include (optional) + */ public TaskQueryParams(String id, @Nullable Integer historyLength) { this(id, historyLength, ""); } + /** + * Convenience constructor with defaults for tenant and historyLength. + * + * @param id the task identifier (required) + */ public TaskQueryParams(String id) { this(id, null, ""); } diff --git a/spec/src/main/java/io/a2a/spec/TaskStatus.java b/spec/src/main/java/io/a2a/spec/TaskStatus.java index 98ad686e4..0ce67f3df 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatus.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatus.java @@ -40,6 +40,14 @@ public record TaskStatus(TaskState state, Message message, OffsetDateTime timestamp) { + /** + * Compact constructor for validation and timestamp initialization. + * Validates that the state is not null and sets the timestamp to current UTC time if not provided. + * + * @param state the task state + * @param message optional status message + * @param timestamp the status timestamp + */ public TaskStatus { Assert.checkNotNullParam("state", state); timestamp = timestamp == null ? OffsetDateTime.now(ZoneOffset.UTC) : timestamp; diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index d59b206a0..43f5dbc23 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -13,6 +13,9 @@ */ public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { + /** + * The kind identifier for status update events: "status-update". + */ public static final String STATUS_UPDATE = "status-update"; private final String taskId; private final TaskStatus status; @@ -23,11 +26,30 @@ public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKin private final String kind; + /** + * Constructs a TaskStatusUpdateEvent with default kind. + * + * @param taskId the task identifier (required) + * @param status the task status (required) + * @param contextId the context identifier (required) + * @param isFinal whether this is a final status + * @param metadata additional metadata (optional) + */ public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, boolean isFinal, Map metadata) { this(taskId, status, contextId, isFinal, metadata, STATUS_UPDATE); } + /** + * Constructs a TaskStatusUpdateEvent with all parameters. + * + * @param taskId the task identifier (required) + * @param status the task status (required) + * @param contextId the context identifier (required) + * @param isFinal whether this is a final status + * @param metadata additional metadata (optional) + * @param kind the event kind (must be "status-update") + */ public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, boolean isFinal, Map metadata, String kind) { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("status", status); @@ -44,22 +66,47 @@ public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, this.kind = kind; } + /** + * Returns the task identifier. + * + * @return the task ID + */ public String getTaskId() { return taskId; } + /** + * Returns the task status. + * + * @return the task status + */ public TaskStatus getStatus() { return status; } + /** + * Returns the context identifier. + * + * @return the context ID + */ public String getContextId() { return contextId; } + /** + * Returns whether this is a final status. + * + * @return true if this is a final status + */ public boolean isFinal() { return isFinal; } + /** + * Returns the metadata. + * + * @return the metadata map + */ public Map getMetadata() { return metadata; } @@ -88,6 +135,9 @@ public static Builder builder(TaskStatusUpdateEvent event) { return new Builder(event); } + /** + * Builder for constructing {@link TaskStatusUpdateEvent} instances. + */ public static class Builder { private String taskId; private TaskStatus status; @@ -105,31 +155,67 @@ private Builder(TaskStatusUpdateEvent existingTaskStatusUpdateEvent) { this.isFinal = existingTaskStatusUpdateEvent.isFinal; this.metadata = existingTaskStatusUpdateEvent.metadata; } + + /** + * Sets the task identifier. + * + * @param id the task ID + * @return this builder for method chaining + */ public Builder taskId(String id) { this.taskId = id; return this; } + /** + * Sets the task status. + * + * @param status the task status + * @return this builder for method chaining + */ public Builder status(TaskStatus status) { this.status = status; return this; } + /** + * Sets the context identifier. + * + * @param contextId the context ID + * @return this builder for method chaining + */ public Builder contextId(String contextId) { this.contextId = contextId; return this; } + /** + * Sets whether this is a final status. + * + * @param isFinal true if this is a final status + * @return this builder for method chaining + */ public Builder isFinal(boolean isFinal) { this.isFinal = isFinal; return this; } + /** + * Sets the metadata. + * + * @param metadata the metadata map + * @return this builder for method chaining + */ public Builder metadata(Map metadata) { this.metadata = metadata; return this; } + /** + * Builds the TaskStatusUpdateEvent. + * + * @return a new TaskStatusUpdateEvent instance + */ public TaskStatusUpdateEvent build() { return new TaskStatusUpdateEvent(taskId, status, contextId, isFinal, metadata); } diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index de527a35c..b6356dd57 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -25,10 +25,18 @@ */ public class TextPart extends Part { + /** + * The kind identifier for text parts: "text". + */ public static final String TEXT = "text"; private final String text; private final Kind kind; + /** + * Constructs a TextPart with the specified text content. + * + * @param text the text content (required, must not be null) + */ public TextPart(String text) { Assert.checkNotNullParam("text", text); this.text = text; @@ -40,6 +48,11 @@ public Kind getKind() { return kind; } + /** + * Returns the text content. + * + * @return the text content + */ public String getText() { return text; } diff --git a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java index d95072623..b4bebd1d8 100644 --- a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -33,6 +33,13 @@ */ public class UnsupportedOperationError extends JSONRPCError { + /** + * Constructs error with all parameters. + * + * @param code the error code (defaults to -32004 if null) + * @param message the error message (defaults to "This operation is not supported" if null) + * @param data additional error data (optional) + */ public UnsupportedOperationError( Integer code, String message, @@ -43,6 +50,9 @@ public UnsupportedOperationError( data); } + /** + * Constructs error with default message. + */ public UnsupportedOperationError() { this(null, null, null); } diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 6d45ebe41..01d796d16 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -172,7 +172,7 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event * * @param agentCard the agentcard where the interfaces are defined. * @return the first defined URL in the supported interaces of the agent card. - * @throws A2AClientException + * @throws A2AClientException if no server interface is available in the AgentCard */ public static String getFavoriteInterface(AgentCard agentCard) throws A2AClientException { if (agentCard.supportedInterfaces() == null || agentCard.supportedInterfaces().isEmpty()) { From 487cd46b9c1d82e10d68565f191e7a223826db2a Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 17 Dec 2025 14:08:22 +0000 Subject: [PATCH 234/493] feat!: Unify spec domain classes as records (#548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Getter signatures are now changed. Errors are not converted since they need to extend RuntimeException etc. Similarly the Request/Response objects are not converted either, as we are evaluating moving them out of the spec package. However, first we need to double check if they are still used in the client facing API. Fixes #507 🦕 Replaces: #509 --- .../java/io/a2a/client/ClientTaskManager.java | 38 +- .../main/java/io/a2a/client/MessageEvent.java | 18 +- client/base/src/test/java/io/a2a/A2ATest.java | 78 ++--- .../JSONRPCTransportStreamingTest.java | 14 +- .../jsonrpc/JSONRPCTransportTest.java | 110 +++--- .../jsonrpc/sse/SSEEventListenerTest.java | 42 +-- .../transport/rest/RestTransportTest.java | 114 +++--- .../interceptors/auth/AuthInterceptor.java | 4 +- .../cloud/CloudAgentExecutorProducer.java | 6 +- .../examples/cloud/A2ACloudExampleClient.java | 16 +- .../examples/helloworld/HelloWorldClient.java | 6 +- ...otificationConfigStoreIntegrationTest.java | 8 +- ...ificationConfigStoreTestAgentExecutor.java | 6 +- .../JpaPushNotificationConfigStoreTest.java | 8 +- .../core/EventSerializationTest.java | 40 +-- ...MultiInstanceReplicationAgentExecutor.java | 8 +- .../MultiInstanceReplicationTest.java | 2 +- .../KafkaReplicationIntegrationTest.java | 28 +- .../tests/ReplicationTestAgentExecutor.java | 6 +- .../database/jpa/JpaDatabaseTaskStore.java | 32 +- .../taskstore/database/jpa/JpaTask.java | 18 +- .../JpaDatabaseTaskStoreIntegrationTest.java | 20 +- .../jpa/JpaDatabaseTaskStoreTest.java | 78 ++--- ...JpaDatabaseTaskStoreTestAgentExecutor.java | 4 +- .../server/agentexecution/RequestContext.java | 38 +- .../SimpleRequestContextBuilder.java | 4 +- .../io/a2a/server/events/EventConsumer.java | 2 +- .../DefaultRequestHandler.java | 72 ++-- .../tasks/BasePushNotificationSender.java | 6 +- .../a2a/server/tasks/InMemoryTaskStore.java | 40 +-- .../io/a2a/server/tasks/ResultAggregator.java | 8 +- .../java/io/a2a/server/tasks/TaskManager.java | 32 +- .../agentexecution/RequestContextTest.java | 40 ++- .../DefaultRequestHandlerTest.java | 38 +- ...MemoryPushNotificationConfigStoreTest.java | 12 +- .../server/tasks/InMemoryTaskStoreTest.java | 6 +- .../tasks/PushNotificationSenderTest.java | 16 +- .../io/a2a/server/tasks/TaskManagerTest.java | 184 +++++----- .../io/a2a/server/tasks/TaskUpdaterTest.java | 72 ++-- .../io/a2a/server/util/ArtifactUtilsTest.java | 4 +- .../mapper/APIKeySecuritySchemeMapper.java | 2 +- .../io/a2a/grpc/mapper/DataPartMapper.java | 21 +- .../io/a2a/grpc/mapper/FilePartMapper.java | 2 +- .../mapper/HTTPAuthSecuritySchemeMapper.java | 4 +- .../io/a2a/grpc/mapper/MessageMapper.java | 6 +- .../mapper/MutualTLSSecuritySchemeMapper.java | 2 +- .../mapper/OAuth2SecuritySchemeMapper.java | 4 +- .../OpenIdConnectSecuritySchemeMapper.java | 2 +- .../java/io/a2a/grpc/mapper/PartMapper.java | 2 +- .../mapper/TaskArtifactUpdateEventMapper.java | 4 +- .../java/io/a2a/grpc/mapper/TaskMapper.java | 4 +- .../mapper/TaskStatusUpdateEventMapper.java | 4 +- .../grpc/mapper/StreamResponseMapperTest.java | 38 +- .../java/io/a2a/grpc/utils/ToProtoTest.java | 2 +- .../io/a2a/json/JsonMappingException.java | 2 +- spec/src/main/java/io/a2a/json/JsonUtil.java | 47 ++- .../io/a2a/spec/APIKeySecurityScheme.java | 95 +---- spec/src/main/java/io/a2a/spec/DataPart.java | 59 +++- spec/src/main/java/io/a2a/spec/EventKind.java | 2 +- spec/src/main/java/io/a2a/spec/FilePart.java | 28 +- .../io/a2a/spec/HTTPAuthSecurityScheme.java | 76 +--- spec/src/main/java/io/a2a/spec/Message.java | 202 ++--------- .../io/a2a/spec/MutualTLSSecurityScheme.java | 57 +-- .../io/a2a/spec/OAuth2SecurityScheme.java | 104 ++---- .../a2a/spec/OpenIdConnectSecurityScheme.java | 80 +---- spec/src/main/java/io/a2a/spec/Part.java | 8 +- .../main/java/io/a2a/spec/SecurityScheme.java | 4 +- .../java/io/a2a/spec/StreamingEventKind.java | 7 +- spec/src/main/java/io/a2a/spec/Task.java | 163 ++------- .../io/a2a/spec/TaskArtifactUpdateEvent.java | 117 +------ .../io/a2a/spec/TaskStatusUpdateEvent.java | 106 +----- spec/src/main/java/io/a2a/spec/TextPart.java | 28 +- spec/src/main/java/io/a2a/util/Utils.java | 11 +- .../io/a2a/spec/TaskSerializationTest.java | 174 +++++----- .../a2a/tck/server/AgentExecutorProducer.java | 10 +- .../apps/common/AbstractA2AServerTest.java | 328 +++++++++--------- .../apps/common/AgentExecutorProducer.java | 4 +- .../grpc/handler/GrpcHandlerTest.java | 136 ++++---- .../jsonrpc/handler/JSONRPCHandlerTest.java | 236 ++++++------- .../rest/handler/RestHandlerTest.java | 30 +- 80 files changed, 1443 insertions(+), 2046 deletions(-) diff --git a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java index 7272e1e0b..ca29c2f6a 100644 --- a/client/base/src/main/java/io/a2a/client/ClientTaskManager.java +++ b/client/base/src/main/java/io/a2a/client/ClientTaskManager.java @@ -52,10 +52,10 @@ public Task saveTaskEvent(Task task) throws A2AClientInvalidArgsError { public Task saveTaskEvent(TaskStatusUpdateEvent taskStatusUpdateEvent) throws A2AClientError { if (taskId == null) { - taskId = taskStatusUpdateEvent.getTaskId(); + taskId = taskStatusUpdateEvent.taskId(); } if (contextId == null) { - contextId = taskStatusUpdateEvent.getContextId(); + contextId = taskStatusUpdateEvent.contextId(); } Task task = currentTask; if (task == null) { @@ -67,31 +67,31 @@ public Task saveTaskEvent(TaskStatusUpdateEvent taskStatusUpdateEvent) throws A2 } Task.Builder taskBuilder = Task.builder(task); - if (taskStatusUpdateEvent.getStatus().message() != null) { - if (task.getHistory() == null) { - taskBuilder.history(taskStatusUpdateEvent.getStatus().message()); + if (taskStatusUpdateEvent.status().message() != null) { + if (task.history() == null) { + taskBuilder.history(taskStatusUpdateEvent.status().message()); } else { - List history = new ArrayList<>(task.getHistory()); - history.add(taskStatusUpdateEvent.getStatus().message()); + List history = new ArrayList<>(task.history()); + history.add(taskStatusUpdateEvent.status().message()); taskBuilder.history(history); } } - if (taskStatusUpdateEvent.getMetadata() != null) { - Map newMetadata = task.getMetadata() != null ? new HashMap<>(task.getMetadata()) : new HashMap<>(); - newMetadata.putAll(taskStatusUpdateEvent.getMetadata()); + if (taskStatusUpdateEvent.metadata() != null) { + Map newMetadata = task.metadata() != null ? new HashMap<>(task.metadata()) : new HashMap<>(); + newMetadata.putAll(taskStatusUpdateEvent.metadata()); taskBuilder.metadata(newMetadata); } - taskBuilder.status(taskStatusUpdateEvent.getStatus()); + taskBuilder.status(taskStatusUpdateEvent.status()); currentTask = taskBuilder.build(); return currentTask; } public Task saveTaskEvent(TaskArtifactUpdateEvent taskArtifactUpdateEvent) { if (taskId == null) { - taskId = taskArtifactUpdateEvent.getTaskId(); + taskId = taskArtifactUpdateEvent.taskId(); } if (contextId == null) { - contextId = taskArtifactUpdateEvent.getContextId(); + contextId = taskArtifactUpdateEvent.contextId(); } Task task = currentTask; if (task == null) { @@ -115,13 +115,13 @@ public Task saveTaskEvent(TaskArtifactUpdateEvent taskArtifactUpdateEvent) { */ public Task updateWithMessage(Message message, Task task) { Task.Builder taskBuilder = Task.builder(task); - List history = task.getHistory(); + List history = task.history(); if (history == null) { history = new ArrayList<>(); } - if (task.getStatus().message() != null) { - history.add(task.getStatus().message()); - taskBuilder.status(new TaskStatus(task.getStatus().state(), null, task.getStatus().timestamp())); + if (task.status().message() != null) { + history.add(task.status().message()); + taskBuilder.status(new TaskStatus(task.status().state(), null, task.status().timestamp())); } history.add(message); taskBuilder.history(history); @@ -132,8 +132,8 @@ public Task updateWithMessage(Message message, Task task) { private void saveTask(Task task) { currentTask = task; if (taskId == null) { - taskId = currentTask.getId(); - contextId = currentTask.getContextId(); + taskId = currentTask.id(); + contextId = currentTask.contextId(); } } } \ No newline at end of file diff --git a/client/base/src/main/java/io/a2a/client/MessageEvent.java b/client/base/src/main/java/io/a2a/client/MessageEvent.java index 94c8a1058..9a0370995 100644 --- a/client/base/src/main/java/io/a2a/client/MessageEvent.java +++ b/client/base/src/main/java/io/a2a/client/MessageEvent.java @@ -25,15 +25,15 @@ public Message getMessage() { @Override public String toString() { String messageAsString = "{" - + "role=" + message.getRole() - + ", parts=" + message.getParts() - + ", messageId=" + message.getMessageId() - + ", contextId=" + message.getContextId() - + ", taskId=" + message.getTaskId() - + ", metadata=" + message.getMetadata() - + ", kind=" + message.getKind() - + ", referenceTaskIds=" + message.getReferenceTaskIds() - + ", extensions=" + message.getExtensions() + '}'; + + "role=" + message.role() + + ", parts=" + message.parts() + + ", messageId=" + message.messageId() + + ", contextId=" + message.contextId() + + ", taskId=" + message.taskId() + + ", metadata=" + message.metadata() + + ", kind=" + message.kind() + + ", referenceTaskIds=" + message.referenceTaskIds() + + ", extensions=" + message.extensions() + '}'; return "MessageEvent{" + "message=" + messageAsString + '}'; } } diff --git a/client/base/src/test/java/io/a2a/A2ATest.java b/client/base/src/test/java/io/a2a/A2ATest.java index f3c2f3a77..217bb18c9 100644 --- a/client/base/src/test/java/io/a2a/A2ATest.java +++ b/client/base/src/test/java/io/a2a/A2ATest.java @@ -20,12 +20,12 @@ public void testToUserMessage() { String text = "Hello, world!"; Message message = A2A.toUserMessage(text); - assertEquals(Message.Role.USER, message.getRole()); - assertEquals(1, message.getParts().size()); - assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); - assertNotNull(message.getMessageId()); - assertNull(message.getContextId()); - assertNull(message.getTaskId()); + assertEquals(Message.Role.USER, message.role()); + assertEquals(1, message.parts().size()); + assertEquals(text, ((TextPart) message.parts().get(0)).text()); + assertNotNull(message.messageId()); + assertNull(message.contextId()); + assertNull(message.taskId()); } @Test @@ -34,8 +34,8 @@ public void testToUserMessageWithId() { String messageId = "test-message-id"; Message message = A2A.toUserMessage(text, messageId); - assertEquals(Message.Role.USER, message.getRole()); - assertEquals(messageId, message.getMessageId()); + assertEquals(Message.Role.USER, message.role()); + assertEquals(messageId, message.messageId()); } @Test @@ -43,10 +43,10 @@ public void testToAgentMessage() { String text = "Hello, I'm an agent!"; Message message = A2A.toAgentMessage(text); - assertEquals(Message.Role.AGENT, message.getRole()); - assertEquals(1, message.getParts().size()); - assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); - assertNotNull(message.getMessageId()); + assertEquals(Message.Role.AGENT, message.role()); + assertEquals(1, message.parts().size()); + assertEquals(text, ((TextPart) message.parts().get(0)).text()); + assertNotNull(message.messageId()); } @Test @@ -55,8 +55,8 @@ public void testToAgentMessageWithId() { String messageId = "agent-message-id"; Message message = A2A.toAgentMessage(text, messageId); - assertEquals(Message.Role.AGENT, message.getRole()); - assertEquals(messageId, message.getMessageId()); + assertEquals(Message.Role.AGENT, message.role()); + assertEquals(messageId, message.messageId()); } @Test @@ -67,14 +67,14 @@ public void testCreateUserTextMessage() { Message message = A2A.createUserTextMessage(text, contextId, taskId); - assertEquals(Message.Role.USER, message.getRole()); - assertEquals(contextId, message.getContextId()); - assertEquals(taskId, message.getTaskId()); - assertEquals(1, message.getParts().size()); - assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); - assertNotNull(message.getMessageId()); - assertNull(message.getMetadata()); - assertNull(message.getReferenceTaskIds()); + assertEquals(Message.Role.USER, message.role()); + assertEquals(contextId, message.contextId()); + assertEquals(taskId, message.taskId()); + assertEquals(1, message.parts().size()); + assertEquals(text, ((TextPart) message.parts().get(0)).text()); + assertNotNull(message.messageId()); + assertNull(message.metadata()); + assertNull(message.referenceTaskIds()); } @Test @@ -83,11 +83,11 @@ public void testCreateUserTextMessageWithNullParams() { Message message = A2A.createUserTextMessage(text, null, null); - assertEquals(Message.Role.USER, message.getRole()); - assertNull(message.getContextId()); - assertNull(message.getTaskId()); - assertEquals(1, message.getParts().size()); - assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); + assertEquals(Message.Role.USER, message.role()); + assertNull(message.contextId()); + assertNull(message.taskId()); + assertEquals(1, message.parts().size()); + assertEquals(text, ((TextPart) message.parts().get(0)).text()); } @Test @@ -98,12 +98,12 @@ public void testCreateAgentTextMessage() { Message message = A2A.createAgentTextMessage(text, contextId, taskId); - assertEquals(Message.Role.AGENT, message.getRole()); - assertEquals(contextId, message.getContextId()); - assertEquals(taskId, message.getTaskId()); - assertEquals(1, message.getParts().size()); - assertEquals(text, ((TextPart) message.getParts().get(0)).getText()); - assertNotNull(message.getMessageId()); + assertEquals(Message.Role.AGENT, message.role()); + assertEquals(contextId, message.contextId()); + assertEquals(taskId, message.taskId()); + assertEquals(1, message.parts().size()); + assertEquals(text, ((TextPart) message.parts().get(0)).text()); + assertNotNull(message.messageId()); } @Test @@ -117,12 +117,12 @@ public void testCreateAgentPartsMessage() { Message message = A2A.createAgentPartsMessage(parts, contextId, taskId); - assertEquals(Message.Role.AGENT, message.getRole()); - assertEquals(contextId, message.getContextId()); - assertEquals(taskId, message.getTaskId()); - assertEquals(2, message.getParts().size()); - assertEquals("Part 1", ((TextPart) message.getParts().get(0)).getText()); - assertEquals("Part 2", ((TextPart) message.getParts().get(1)).getText()); + assertEquals(Message.Role.AGENT, message.role()); + assertEquals(contextId, message.contextId()); + assertEquals(taskId, message.taskId()); + assertEquals(2, message.parts().size()); + assertEquals("Part 1", ((TextPart) message.parts().get(0)).text()); + assertEquals("Part 2", ((TextPart) message.parts().get(1)).text()); } @Test diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index b2a6e23ec..a0c21c13e 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -74,8 +74,8 @@ public void testSendStreamingMessageParams() { assertNotNull(params); assertEquals(message, params.message()); assertEquals(configuration, params.configuration()); - assertEquals(Message.Role.USER, params.message().getRole()); - assertEquals("test message", ((TextPart) params.message().getParts().get(0)).getText()); + assertEquals(Message.Role.USER, params.message().role()); + assertEquals("test message", ((TextPart) params.message().parts().get(0)).text()); } @Test @@ -159,16 +159,16 @@ public void testA2AClientResubscribeToTask() throws Exception { assertNotNull(eventKind); assertInstanceOf(Task.class, eventKind); Task task = (Task) eventKind; - assertEquals("2", task.getId()); - assertEquals("context-1234", task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - List artifacts = task.getArtifacts(); + assertEquals("2", task.id()); + assertEquals("context-1234", task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + List artifacts = task.artifacts(); assertEquals(1, artifacts.size()); Artifact artifact = artifacts.get(0); assertEquals("artifact-1", artifact.artifactId()); assertEquals("joke", artifact.name()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).getText()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); } } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index f7947eb4f..dc9001871 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -122,18 +122,18 @@ public void testA2AClientSendMessage() throws Exception { EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertNotNull(task.getContextId()); - assertEquals(TaskState.COMPLETED,task.getStatus().state()); - assertEquals(1, task.getArtifacts().size()); - Artifact artifact = task.getArtifacts().get(0); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertNotNull(task.contextId()); + assertEquals(TaskState.COMPLETED,task.status().state()); + assertEquals(1, task.artifacts().size()); + Artifact artifact = task.artifacts().get(0); assertEquals("artifact-1", artifact.artifactId()); assertEquals("joke", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).getText()); - assertTrue(task.getMetadata().isEmpty()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); + assertTrue(task.metadata().isEmpty()); } @Test @@ -170,11 +170,11 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception { EventKind result = client.sendMessage(params, null); assertInstanceOf(Message.class, result); Message agentMessage = (Message) result; - assertEquals(Message.Role.AGENT, agentMessage.getRole()); - Part part = agentMessage.getParts().get(0); + assertEquals(Message.Role.AGENT, agentMessage.role()); + Part part = agentMessage.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).getText()); - assertEquals("msg-456", agentMessage.getMessageId()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); + assertEquals("msg-456", agentMessage.messageId()); } @@ -235,39 +235,39 @@ public void testA2AClientGetTask() throws Exception { JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Task task = client.getTask(new TaskQueryParams("de38c76d-d54c-436c-8b9f-4c2703648d64", 10), null); - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - assertEquals(1, task.getArtifacts().size()); - Artifact artifact = task.getArtifacts().get(0); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + assertEquals(1, task.artifacts().size()); + Artifact artifact = task.artifacts().get(0); assertEquals(1, artifact.parts().size()); assertEquals("artifact-1", artifact.artifactId()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).getText()); - assertTrue(task.getMetadata().isEmpty()); - List history = task.getHistory(); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); + assertTrue(task.metadata().isEmpty()); + List history = task.history(); assertNotNull(history); assertEquals(1, history.size()); Message message = history.get(0); - assertEquals(Message.Role.USER, message.getRole()); - List> parts = message.getParts(); + assertEquals(Message.Role.USER, message.role()); + List> parts = message.parts(); assertNotNull(parts); assertEquals(3, parts.size()); part = parts.get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("tell me a joke", ((TextPart)part).getText()); + assertEquals("tell me a joke", ((TextPart)part).text()); part = parts.get(1); assertEquals(Part.Kind.FILE, part.getKind()); - FileContent filePart = ((FilePart) part).getFile(); + FileContent filePart = ((FilePart) part).file(); assertEquals("file:///path/to/file.txt", ((FileWithUri) filePart).uri()); assertEquals("text/plain", filePart.mimeType()); part = parts.get(2); assertEquals(Part.Kind.FILE, part.getKind()); - filePart = ((FilePart) part).getFile(); + filePart = ((FilePart) part).file(); assertEquals("aGVsbG8=", ((FileWithBytes) filePart).bytes()); assertEquals("hello.txt", filePart.name()); - assertTrue(task.getMetadata().isEmpty()); + assertTrue(task.metadata().isEmpty()); } @Test @@ -287,10 +287,10 @@ public void testA2AClientCancelTask() throws Exception { JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); Task task = client.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64"), null); - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.getContextId()); - assertEquals(TaskState.CANCELED, task.getStatus().state()); - assertTrue(task.getMetadata().isEmpty()); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", task.contextId()); + assertEquals(TaskState.CANCELED, task.status().state()); + assertTrue(task.metadata().isEmpty()); } @Test @@ -380,8 +380,7 @@ public void testA2AClientGetAgentCard() throws Exception { Map securitySchemes = agentCard.securitySchemes(); assertNotNull(securitySchemes); OpenIdConnectSecurityScheme google = (OpenIdConnectSecurityScheme) securitySchemes.get("google"); - assertEquals("openIdConnect", google.getType()); - assertEquals("https://accounts.google.com/.well-known/openid-configuration", google.getOpenIdConnectUrl()); + assertEquals("https://accounts.google.com/.well-known/openid-configuration", google.openIdConnectUrl()); List>> security = agentCard.security(); assertEquals(1, security.size()); Map> securityMap = security.get(0); @@ -469,8 +468,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { Map securitySchemes = agentCard.securitySchemes(); assertNotNull(securitySchemes); OpenIdConnectSecurityScheme google = (OpenIdConnectSecurityScheme) securitySchemes.get("google"); - assertEquals("openIdConnect", google.getType()); - assertEquals("https://accounts.google.com/.well-known/openid-configuration", google.getOpenIdConnectUrl()); + assertEquals("https://accounts.google.com/.well-known/openid-configuration", google.openIdConnectUrl()); List>> security = agentCard.security(); assertEquals(1, security.size()); Map> securityMap = security.get(0); @@ -551,20 +549,20 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertNotNull(task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - assertEquals(1, task.getArtifacts().size()); - Artifact artifact = task.getArtifacts().get(0); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertNotNull(task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + assertEquals(1, task.artifacts().size()); + Artifact artifact = task.artifacts().get(0); assertEquals("artifact-1", artifact.artifactId()); assertEquals("image-analysis", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("This is an image of a cat sitting on a windowsill.", ((TextPart) part).getText()); - assertFalse(task.getMetadata().isEmpty()); - assertEquals(1, task.getMetadata().size()); - assertEquals("metadata-test", task.getMetadata().get("test")); + assertEquals("This is an image of a cat sitting on a windowsill.", ((TextPart) part).text()); + assertFalse(task.metadata().isEmpty()); + assertEquals(1, task.metadata().size()); + assertEquals("metadata-test", task.metadata().get("test")); } @Test @@ -611,18 +609,18 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertNotNull(task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - assertEquals(1, task.getArtifacts().size()); - Artifact artifact = task.getArtifacts().get(0); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertNotNull(task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + assertEquals(1, task.artifacts().size()); + Artifact artifact = task.artifacts().get(0); assertEquals("artifact-1", artifact.artifactId()); assertEquals("data-analysis", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco.", ((TextPart) part).getText()); - assertTrue(task.getMetadata().isEmpty()); + assertEquals("Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco.", ((TextPart) part).text()); + assertTrue(task.metadata().isEmpty()); } @Test @@ -669,17 +667,17 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { EventKind result = client.sendMessage(params, null); assertInstanceOf(Task.class, result); Task task = (Task) result; - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertNotNull(task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - assertEquals(1, task.getArtifacts().size()); - Artifact artifact = task.getArtifacts().get(0); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertNotNull(task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + assertEquals(1, task.artifacts().size()); + Artifact artifact = task.artifacts().get(0); assertEquals("artifact-1", artifact.artifactId()); assertEquals("mixed-analysis", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40].", ((TextPart) part).getText()); - assertTrue(task.getMetadata().isEmpty()); + assertEquals("Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40].", ((TextPart) part).text()); + assertTrue(task.metadata().isEmpty()); } } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 9c25ac95a..35aa6a27c 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -49,9 +49,9 @@ public void testOnEventWithTaskResult() throws Exception { assertNotNull(receivedEvent.get()); assertTrue(receivedEvent.get() instanceof Task); Task task = (Task) receivedEvent.get(); - assertEquals("task-123", task.getId()); - assertEquals("context-456", task.getContextId()); - assertEquals(TaskState.WORKING, task.getStatus().state()); + assertEquals("task-123", task.id()); + assertEquals("context-456", task.contextId()); + assertEquals(TaskState.WORKING, task.status().state()); } @Test @@ -74,12 +74,12 @@ public void testOnEventWithMessageResult() throws Exception { assertNotNull(receivedEvent.get()); assertTrue(receivedEvent.get() instanceof Message); Message message = (Message) receivedEvent.get(); - assertEquals(Message.Role.AGENT, message.getRole()); - assertEquals("msg-123", message.getMessageId()); - assertEquals("context-456", message.getContextId()); - assertEquals(1, message.getParts().size()); - assertTrue(message.getParts().get(0) instanceof TextPart); - assertEquals("Hello, world!", ((TextPart) message.getParts().get(0)).getText()); + assertEquals(Message.Role.AGENT, message.role()); + assertEquals("msg-123", message.messageId()); + assertEquals("context-456", message.contextId()); + assertEquals(1, message.parts().size()); + assertTrue(message.parts().get(0) instanceof TextPart); + assertEquals("Hello, world!", ((TextPart) message.parts().get(0)).text()); } @Test @@ -102,10 +102,10 @@ public void testOnEventWithTaskStatusUpdateEventEvent() throws Exception { assertNotNull(receivedEvent.get()); assertTrue(receivedEvent.get() instanceof TaskStatusUpdateEvent); TaskStatusUpdateEvent taskStatusUpdateEvent = (TaskStatusUpdateEvent) receivedEvent.get(); - assertEquals("1", taskStatusUpdateEvent.getTaskId()); - assertEquals("2", taskStatusUpdateEvent.getContextId()); + assertEquals("1", taskStatusUpdateEvent.taskId()); + assertEquals("2", taskStatusUpdateEvent.contextId()); assertFalse(taskStatusUpdateEvent.isFinal()); - assertEquals(TaskState.SUBMITTED, taskStatusUpdateEvent.getStatus().state()); + assertEquals(TaskState.SUBMITTED, taskStatusUpdateEvent.status().state()); } @Test @@ -129,15 +129,15 @@ public void testOnEventWithTaskArtifactUpdateEventEvent() throws Exception { assertTrue(receivedEvent.get() instanceof TaskArtifactUpdateEvent); TaskArtifactUpdateEvent taskArtifactUpdateEvent = (TaskArtifactUpdateEvent) receivedEvent.get(); - assertEquals("1", taskArtifactUpdateEvent.getTaskId()); - assertEquals("2", taskArtifactUpdateEvent.getContextId()); - assertFalse(taskArtifactUpdateEvent.isAppend()); - assertTrue(taskArtifactUpdateEvent.isLastChunk()); - Artifact artifact = taskArtifactUpdateEvent.getArtifact(); + assertEquals("1", taskArtifactUpdateEvent.taskId()); + assertEquals("2", taskArtifactUpdateEvent.contextId()); + assertFalse(taskArtifactUpdateEvent.append()); + assertTrue(taskArtifactUpdateEvent.lastChunk()); + Artifact artifact = taskArtifactUpdateEvent.artifact(); assertEquals("artifact-1", artifact.artifactId()); assertEquals(1, artifact.parts().size()); assertEquals(Part.Kind.TEXT, artifact.parts().get(0).getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).getText()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).text()); } @Test @@ -223,10 +223,10 @@ public void testOnEventWithFinalTaskStatusUpdateEventEventCancels() throws Excep assertNotNull(receivedEvent.get()); assertTrue(receivedEvent.get() instanceof TaskStatusUpdateEvent); TaskStatusUpdateEvent taskStatusUpdateEvent = (TaskStatusUpdateEvent) receivedEvent.get(); - assertEquals("1", taskStatusUpdateEvent.getTaskId()); - assertEquals("2", taskStatusUpdateEvent.getContextId()); + assertEquals("1", taskStatusUpdateEvent.taskId()); + assertEquals("2", taskStatusUpdateEvent.contextId()); assertTrue(taskStatusUpdateEvent.isFinal()); - assertEquals(TaskState.COMPLETED, taskStatusUpdateEvent.getStatus().state()); + assertEquals(TaskState.COMPLETED, taskStatusUpdateEvent.status().state()); assertTrue(future.cancelHandlerCalled); } diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 6ed9a79e4..1976367f6 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -130,26 +130,26 @@ public void testSendMessage() throws Exception { RestTransport instance = new RestTransport(CARD); EventKind result = instance.sendMessage(messageSendParams, context); - assertEquals("task", result.getKind()); + assertEquals("task", result.kind()); Task task = (Task) result; - assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", task.getId()); - assertEquals("context-1234", task.getContextId()); - assertEquals(TaskState.SUBMITTED, task.getStatus().state()); - assertNull(task.getStatus().message()); - assertNull(task.getMetadata()); - assertEquals(true, task.getArtifacts().isEmpty()); - assertEquals(1, task.getHistory().size()); - Message history = task.getHistory().get(0); - assertEquals("message", history.getKind()); - assertEquals(Message.Role.USER, history.getRole()); - assertEquals("context-1234", history.getContextId()); - assertEquals("message-1234", history.getMessageId()); - assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.getTaskId()); - assertEquals(1, history.getParts().size()); - assertEquals(Kind.TEXT, history.getParts().get(0).getKind()); - assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).getText()); - assertNull(task.getMetadata()); - assertNull(history.getReferenceTaskIds()); + assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", task.id()); + assertEquals("context-1234", task.contextId()); + assertEquals(TaskState.SUBMITTED, task.status().state()); + assertNull(task.status().message()); + assertNull(task.metadata()); + assertEquals(true, task.artifacts().isEmpty()); + assertEquals(1, task.history().size()); + Message history = task.history().get(0); + assertEquals("message", history.kind()); + assertEquals(Message.Role.USER, history.role()); + assertEquals("context-1234", history.contextId()); + assertEquals("message-1234", history.messageId()); + assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.taskId()); + assertEquals(1, history.parts().size()); + assertEquals(Kind.TEXT, history.parts().get(0).getKind()); + assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text()); + assertNull(task.metadata()); + assertNull(history.referenceTaskIds()); } /** @@ -171,11 +171,11 @@ public void testCancelTask() throws Exception { ClientCallContext context = null; RestTransport instance = new RestTransport(CARD); Task task = instance.cancelTask(new TaskIdParams("de38c76d-d54c-436c-8b9f-4c2703648d64"), context); - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertEquals(TaskState.CANCELED, task.getStatus().state()); - assertNull(task.getStatus().message()); - assertNotNull(task.getMetadata()); - assertTrue(task.getMetadata().isEmpty()); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertEquals(TaskState.CANCELED, task.status().state()); + assertNull(task.status().message()); + assertNotNull(task.metadata()); + assertTrue(task.metadata().isEmpty()); } /** @@ -197,37 +197,37 @@ public void testGetTask() throws Exception { TaskQueryParams request = new TaskQueryParams("de38c76d-d54c-436c-8b9f-4c2703648d64", 10); RestTransport instance = new RestTransport(CARD); Task task = instance.getTask(request, context); - assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.getId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - assertNull(task.getStatus().message()); - assertNotNull(task.getMetadata()); - assertTrue(task.getMetadata().isEmpty()); - assertEquals(false, task.getArtifacts().isEmpty()); - assertEquals(1, task.getArtifacts().size()); - Artifact artifact = task.getArtifacts().get(0); + assertEquals("de38c76d-d54c-436c-8b9f-4c2703648d64", task.id()); + assertEquals(TaskState.COMPLETED, task.status().state()); + assertNull(task.status().message()); + assertNotNull(task.metadata()); + assertTrue(task.metadata().isEmpty()); + assertEquals(false, task.artifacts().isEmpty()); + assertEquals(1, task.artifacts().size()); + Artifact artifact = task.artifacts().get(0); assertEquals("artifact-1", artifact.artifactId()); assertNull(artifact.name()); assertEquals(false, artifact.parts().isEmpty()); assertEquals(Kind.TEXT, artifact.parts().get(0).getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).getText()); - assertEquals(1, task.getHistory().size()); - Message history = task.getHistory().get(0); - assertEquals("message", history.getKind()); - assertEquals(Message.Role.USER, history.getRole()); - assertEquals("message-123", history.getMessageId()); - assertEquals(3, history.getParts().size()); - assertEquals(Kind.TEXT, history.getParts().get(0).getKind()); - assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).getText()); - assertEquals(Kind.FILE, history.getParts().get(1).getKind()); - FilePart part = (FilePart) history.getParts().get(1); - assertEquals("text/plain", part.getFile().mimeType()); - assertEquals("file:///path/to/file.txt", ((FileWithUri) part.getFile()).uri()); - part = (FilePart) history.getParts().get(2); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).text()); + assertEquals(1, task.history().size()); + Message history = task.history().get(0); + assertEquals("message", history.kind()); + assertEquals(Message.Role.USER, history.role()); + assertEquals("message-123", history.messageId()); + assertEquals(3, history.parts().size()); + assertEquals(Kind.TEXT, history.parts().get(0).getKind()); + assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text()); + assertEquals(Kind.FILE, history.parts().get(1).getKind()); + FilePart part = (FilePart) history.parts().get(1); + assertEquals("text/plain", part.file().mimeType()); + assertEquals("file:///path/to/file.txt", ((FileWithUri) part.file()).uri()); + part = (FilePart) history.parts().get(2); assertEquals(Kind.FILE, part.getKind()); - assertEquals("text/plain", part.getFile().mimeType()); - assertEquals("aGVsbG8=", ((FileWithBytes) part.getFile()).bytes()); - assertNull(history.getMetadata()); - assertNull(history.getReferenceTaskIds()); + assertEquals("text/plain", part.file().mimeType()); + assertEquals("aGVsbG8=", ((FileWithBytes) part.file()).bytes()); + assertNull(history.metadata()); + assertNull(history.referenceTaskIds()); } /** @@ -276,9 +276,9 @@ public void testSendMessageStreaming() throws Exception { boolean eventReceived = latch.await(10, TimeUnit.SECONDS); assertTrue(eventReceived); assertNotNull(receivedEvent.get()); - assertEquals("task", receivedEvent.get().getKind()); + assertEquals("task", receivedEvent.get().kind()); Task task = (Task) receivedEvent.get(); - assertEquals("2", task.getId()); + assertEquals("2", task.id()); } /** @@ -437,16 +437,16 @@ public void testResubscribe() throws Exception { assertNotNull(eventKind); assertInstanceOf(Task.class, eventKind); Task task = (Task) eventKind; - assertEquals("2", task.getId()); - assertEquals("context-1234", task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - List artifacts = task.getArtifacts(); + assertEquals("2", task.id()); + assertEquals("context-1234", task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + List artifacts = task.artifacts(); assertEquals(1, artifacts.size()); Artifact artifact = artifacts.get(0); assertEquals("artifact-1", artifact.artifactId()); assertEquals("joke", artifact.name()); Part part = artifact.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).getText()); + assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); } } diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java index 8fda4ca49..3573d6784 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java @@ -47,7 +47,7 @@ public PayloadAndHeaders intercept(String methodName, @Nullable Object payload, continue; } if (securityScheme instanceof HTTPAuthSecurityScheme httpAuthSecurityScheme) { - if (httpAuthSecurityScheme.getScheme().toLowerCase(Locale.ROOT).equals(BEARER_SCHEME)) { + if (httpAuthSecurityScheme.scheme().toLowerCase(Locale.ROOT).equals(BEARER_SCHEME)) { updatedHeaders.put(AUTHORIZATION, getBearerValue(credential)); return new PayloadAndHeaders(payload, updatedHeaders); } @@ -56,7 +56,7 @@ public PayloadAndHeaders intercept(String methodName, @Nullable Object payload, updatedHeaders.put(AUTHORIZATION, getBearerValue(credential)); return new PayloadAndHeaders(payload, updatedHeaders); } else if (securityScheme instanceof APIKeySecurityScheme apiKeySecurityScheme) { - updatedHeaders.put(apiKeySecurityScheme.getName(), credential); + updatedHeaders.put(apiKeySecurityScheme.name(), credential); return new PayloadAndHeaders(payload, updatedHeaders); } } diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java index 16632dedb..7ba9f1d76 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java @@ -119,10 +119,10 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC */ private String extractTextFromMessage(Message message) { StringBuilder textBuilder = new StringBuilder(); - if (message.getParts() != null) { - for (Part part : message.getParts()) { + if (message.parts() != null) { + for (Part part : message.parts()) { if (part instanceof TextPart textPart) { - textBuilder.append(textPart.getText()); + textBuilder.append(textPart.text()); } } } diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java index c4ce1ae36..73d080c45 100644 --- a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -156,9 +156,9 @@ private void sendStartMessage(String clientTaskId) { try { nonStreamingClient.sendMessage(startMessage, List.of((ClientEvent event, AgentCard card) -> { if (event instanceof TaskEvent te) { - serverTaskId = te.getTask().getId(); + serverTaskId = te.getTask().id(); System.out.println("✓ Task created: " + serverTaskId); - System.out.println(" State: " + te.getTask().getStatus().state()); + System.out.println(" State: " + te.getTask().status().state()); taskCreationLatch.countDown(); } }), error -> { @@ -239,8 +239,8 @@ private void handleSubscriptionEvent(ClientEvent event, AgentCard card) { } } else if (event instanceof TaskEvent te) { // Check for task completion - if (te.getTask().getStatus().state().isFinal()) { - System.out.println(" Task reached final state: " + te.getTask().getStatus().state()); + if (te.getTask().status().state().isFinal()) { + System.out.println(" Task reached final state: " + te.getTask().status().state()); completionLatch.countDown(); } } @@ -332,7 +332,7 @@ private void sendCompleteMessage() { try { completeClient.sendMessage(completeMessage, List.of((ClientEvent event, AgentCard card) -> { if (event instanceof TaskEvent te) { - System.out.println("✓ Complete message sent, task state: " + te.getTask().getStatus().state()); + System.out.println("✓ Complete message sent, task state: " + te.getTask().status().state()); } }), error -> { System.err.println("✗ Failed to send complete message: " + error.getMessage()); @@ -382,10 +382,10 @@ private void printResults() { private static String extractTextFromArtifact(TaskArtifactUpdateEvent event) { StringBuilder text = new StringBuilder(); - if (event.getArtifact() != null) { - for (Part part : event.getArtifact().parts()) { + if (event.artifact() != null) { + for (Part part : event.artifact().parts()) { if (part instanceof TextPart textPart) { - text.append(textPart.getText()); + text.append(textPart.text()); } } } diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index d08386cbd..413efb03c 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -61,10 +61,10 @@ public static void main(String[] args) { if (event instanceof MessageEvent messageEvent) { Message responseMessage = messageEvent.getMessage(); StringBuilder textBuilder = new StringBuilder(); - if (responseMessage.getParts() != null) { - for (Part part : responseMessage.getParts()) { + if (responseMessage.parts() != null) { + for (Part part : responseMessage.parts()) { if (part instanceof TextPart textPart) { - textBuilder.append(textPart.getText()); + textBuilder.append(textPart.text()); } } } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index 44642ed88..4092f4c1d 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -90,7 +90,7 @@ public void testDirectNotificationTrigger() { // Verify it was captured Queue captured = mockPushNotificationSender.getCapturedTasks(); assertEquals(1, captured.size()); - assertEquals("direct-test-task", captured.peek().getId()); + assertEquals("direct-test-task", captured.peek().id()); } @Test @@ -165,13 +165,13 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep // Verify the notification contains the correct task with artifacts Task notifiedTaskWithArtifact = capturedTasks.stream() - .filter(t -> taskId.equals(t.getId()) && t.getArtifacts() != null && t.getArtifacts().size() > 0) + .filter(t -> taskId.equals(t.id()) && t.artifacts() != null && t.artifacts().size() > 0) .findFirst() .orElse(null); assertNotNull(notifiedTaskWithArtifact, "Notification should contain the updated task with artifacts"); - assertEquals(taskId, notifiedTaskWithArtifact.getId()); - assertEquals(1, notifiedTaskWithArtifact.getArtifacts().size(), "Task should have one artifact from the update"); + assertEquals(taskId, notifiedTaskWithArtifact.id()); + assertEquals(1, notifiedTaskWithArtifact.artifacts().size(), "Task should have one artifact from the update"); // Step 7: Clean up - delete the push notification configuration client.deleteTaskPushNotificationConfigurations( diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java index 09211b4fc..d178b0189 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java @@ -63,12 +63,12 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC } private String getLastTextPart(Message message) throws JSONRPCError { - if (message.getParts() == null || message.getParts().isEmpty()) { + if (message.parts() == null || message.parts().isEmpty()) { return ""; } - Part part = message.getParts().get(message.getParts().size() - 1); + Part part = message.parts().get(message.parts().size() - 1); if (part.getKind() == Part.Kind.TEXT) { - return ((TextPart) part).getText(); + return ((TextPart) part).text(); } throw new InvalidRequestError("Last part is not text"); } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java index 9566453a2..2648cba87 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java @@ -254,8 +254,8 @@ public void testSendNotificationSuccess() throws Exception { // Verify the request body contains the task data String sentBody = bodyCaptor.getValue(); - assertTrue(sentBody.contains(task.getId())); - assertTrue(sentBody.contains(task.getStatus().state().asString())); + assertTrue(sentBody.contains(task.id())); + assertTrue(sentBody.contains(task.status().state().asString())); } @Test @@ -290,8 +290,8 @@ public void testSendNotificationWithToken() throws Exception { // Verify the request body contains the task data String sentBody = bodyCaptor.getValue(); - assertTrue(sentBody.contains(task.getId())); - assertTrue(sentBody.contains(task.getStatus().state().asString())); + assertTrue(sentBody.contains(task.id())); + assertTrue(sentBody.contains(task.status().state().asString())); } @Test diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index c53cb610d..8768269b7 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -61,10 +61,10 @@ public void testTaskSerialization() throws JsonProcessingException { assertInstanceOf(Task.class, deserializedEvent, "Should deserialize to Task"); Task deserializedTask = (Task) deserializedEvent; - assertEquals(originalTask.getId(), deserializedTask.getId()); - assertEquals(originalTask.getKind(), deserializedTask.getKind()); - assertEquals(originalTask.getContextId(), deserializedTask.getContextId()); - assertEquals(originalTask.getStatus().state(), deserializedTask.getStatus().state()); + assertEquals(originalTask.id(), deserializedTask.id()); + assertEquals(originalTask.kind(), deserializedTask.kind()); + assertEquals(originalTask.contextId(), deserializedTask.contextId()); + assertEquals(originalTask.status().state(), deserializedTask.status().state()); // Test as StreamingEventKind StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); @@ -92,10 +92,10 @@ public void testMessageSerialization() throws JsonProcessingException { assertInstanceOf(Message.class, deserializedEvent, "Should deserialize to Message"); Message deserializedMessage = (Message) deserializedEvent; - assertEquals(originalMessage.getTaskId(), deserializedMessage.getTaskId()); - assertEquals(originalMessage.getKind(), deserializedMessage.getKind()); - assertEquals(originalMessage.getRole(), deserializedMessage.getRole()); - assertEquals(originalMessage.getParts().size(), deserializedMessage.getParts().size()); + assertEquals(originalMessage.taskId(), deserializedMessage.taskId()); + assertEquals(originalMessage.kind(), deserializedMessage.kind()); + assertEquals(originalMessage.role(), deserializedMessage.role()); + assertEquals(originalMessage.parts().size(), deserializedMessage.parts().size()); // Test as StreamingEventKind StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); @@ -124,10 +124,10 @@ public void testTaskStatusUpdateEventSerialization() throws JsonProcessingExcept assertInstanceOf(TaskStatusUpdateEvent.class, deserializedEvent, "Should deserialize to TaskStatusUpdateEvent"); TaskStatusUpdateEvent deserializedStatusEvent = (TaskStatusUpdateEvent) deserializedEvent; - assertEquals(originalEvent.getTaskId(), deserializedStatusEvent.getTaskId()); - assertEquals(originalEvent.getKind(), deserializedStatusEvent.getKind()); - assertEquals(originalEvent.getContextId(), deserializedStatusEvent.getContextId()); - assertEquals(originalEvent.getStatus().state(), deserializedStatusEvent.getStatus().state()); + assertEquals(originalEvent.taskId(), deserializedStatusEvent.taskId()); + assertEquals(originalEvent.kind(), deserializedStatusEvent.kind()); + assertEquals(originalEvent.contextId(), deserializedStatusEvent.contextId()); + assertEquals(originalEvent.status().state(), deserializedStatusEvent.status().state()); assertEquals(originalEvent.isFinal(), deserializedStatusEvent.isFinal()); // Test as StreamingEventKind @@ -157,11 +157,11 @@ public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingExce assertInstanceOf(TaskArtifactUpdateEvent.class, deserializedEvent, "Should deserialize to TaskArtifactUpdateEvent"); TaskArtifactUpdateEvent deserializedArtifactEvent = (TaskArtifactUpdateEvent) deserializedEvent; - assertEquals(originalEvent.getTaskId(), deserializedArtifactEvent.getTaskId()); - assertEquals(originalEvent.getKind(), deserializedArtifactEvent.getKind()); - assertEquals(originalEvent.getContextId(), deserializedArtifactEvent.getContextId()); - assertEquals(originalEvent.getArtifact().artifactId(), deserializedArtifactEvent.getArtifact().artifactId()); - assertEquals(originalEvent.getArtifact().name(), deserializedArtifactEvent.getArtifact().name()); + assertEquals(originalEvent.taskId(), deserializedArtifactEvent.taskId()); + assertEquals(originalEvent.kind(), deserializedArtifactEvent.kind()); + assertEquals(originalEvent.contextId(), deserializedArtifactEvent.contextId()); + assertEquals(originalEvent.artifact().artifactId(), deserializedArtifactEvent.artifact().artifactId()); + assertEquals(originalEvent.artifact().name(), deserializedArtifactEvent.artifact().name()); // Test as StreamingEventKind StreamingEventKind deserializedAsStreaming = JsonUtil.fromJson(json, StreamingEventKind.class); @@ -229,9 +229,9 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc assertInstanceOf(TaskStatusUpdateEvent.class, retrievedEventAsEvent, "Should deserialize to TaskStatusUpdateEvent"); TaskStatusUpdateEvent retrievedStatusEvent = (TaskStatusUpdateEvent) retrievedEventAsEvent; - assertEquals(statusEvent.getTaskId(), retrievedStatusEvent.getTaskId()); - assertEquals(statusEvent.getContextId(), retrievedStatusEvent.getContextId()); - assertEquals(statusEvent.getStatus().state(), retrievedStatusEvent.getStatus().state()); + assertEquals(statusEvent.taskId(), retrievedStatusEvent.taskId()); + assertEquals(statusEvent.contextId(), retrievedStatusEvent.contextId()); + assertEquals(statusEvent.status().state(), retrievedStatusEvent.status().state()); assertEquals(statusEvent.isFinal(), retrievedStatusEvent.isFinal()); // Test helper methods diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java index a4b9ae4f2..51aa7ac0c 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java @@ -23,10 +23,10 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP TaskUpdater updater = new TaskUpdater(context, eventQueue); // Check if message contains "close" signal - boolean shouldClose = context.getMessage().getParts().stream() + boolean shouldClose = context.getMessage().parts().stream() .anyMatch(part -> part instanceof TextPart tp && - tp.getText() != null && - tp.getText().toLowerCase().contains("close")); + tp.text() != null && + tp.text().toLowerCase().contains("close")); if (shouldClose) { // Close the task @@ -36,7 +36,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP updater.submit(); } else { // Subsequent messages - add as artifact - updater.addArtifact(context.getMessage().getParts()); + updater.addArtifact(context.getMessage().parts()); } } diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java index 2f3a3dbaa..93093388d 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java @@ -276,7 +276,7 @@ public void testMultiInstanceEventReplication() throws Exception { assertNotNull(createdTask, "Task should be created"); // Task should be in a non-final state (SUBMITTED or WORKING are both valid) - TaskState state = createdTask.getStatus().state(); + TaskState state = createdTask.status().state(); assertTrue(state == TaskState.SUBMITTED || state == TaskState.WORKING, "Task should be in SUBMITTED or WORKING state, but was: " + state); nonStreamingClient.close(); diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 58fa7f886..029aa9a62 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -155,8 +155,8 @@ public void testA2AMessageReplicatedToKafka() throws Exception { Task task = createdTask.get(); assertNotNull(task, "Task should be created"); - assertEquals(taskId, task.getId()); - assertEquals(TaskState.SUBMITTED, task.getStatus().state()); + assertEquals(taskId, task.id()); + assertEquals(TaskState.SUBMITTED, task.status().state()); // Wait for the event to be replicated to Kafka ReplicatedEventQueueItem replicatedEvent = testConsumer.waitForEvent(taskId, 30); @@ -173,11 +173,11 @@ public void testA2AMessageReplicatedToKafka() throws Exception { TaskStatusUpdateEvent statusUpdateEvent = (TaskStatusUpdateEvent) receivedEvent; // Verify the event data is consistent with the task returned from the client - assertEquals(taskId, statusUpdateEvent.getTaskId(), "Event task ID should match original task ID"); - assertEquals(contextId, statusUpdateEvent.getContextId(), "Event context ID should match original context ID"); - assertEquals(TaskState.SUBMITTED, statusUpdateEvent.getStatus().state(), "Event should show SUBMITTED state"); + assertEquals(taskId, statusUpdateEvent.taskId(), "Event task ID should match original task ID"); + assertEquals(contextId, statusUpdateEvent.contextId(), "Event context ID should match original context ID"); + assertEquals(TaskState.SUBMITTED, statusUpdateEvent.status().state(), "Event should show SUBMITTED state"); assertFalse(statusUpdateEvent.isFinal(), "Event should show final:false"); - assertEquals("status-update", statusUpdateEvent.getKind(), "Event should indicate status-update type"); + assertEquals("status-update", statusUpdateEvent.kind(), "Event should indicate status-update type"); } @Test @@ -212,7 +212,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { assertTrue(createLatch.await(15, TimeUnit.SECONDS), "Task creation timed out"); Task initialTask = createdTask.get(); assertNotNull(initialTask, "Task should be created"); - assertEquals(TaskState.SUBMITTED, initialTask.getStatus().state(), "Initial task should be in SUBMITTED state"); + assertEquals(TaskState.SUBMITTED, initialTask.status().state(), "Initial task should be in SUBMITTED state"); // Add a small delay to ensure the task is fully processed before resubscription Thread.sleep(1000); @@ -227,7 +227,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { BiConsumer consumer = (event, agentCard) -> { if (event instanceof TaskUpdateEvent taskUpdateEvent) { if (taskUpdateEvent.getUpdateEvent() instanceof TaskStatusUpdateEvent statusEvent) { - if (statusEvent.getStatus().state() == TaskState.COMPLETED) { + if (statusEvent.status().state() == TaskState.COMPLETED) { receivedCompletedEvent.set(statusEvent); resubscribeLatch.countDown(); } @@ -277,10 +277,10 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { // Verify the received event TaskStatusUpdateEvent completedEvent = receivedCompletedEvent.get(); assertNotNull(completedEvent, "Should have received a TaskStatusUpdateEvent"); - assertEquals(TaskState.COMPLETED, completedEvent.getStatus().state(), "Event should show COMPLETED state"); + assertEquals(TaskState.COMPLETED, completedEvent.status().state(), "Event should show COMPLETED state"); assertTrue(completedEvent.isFinal(), "Event should be marked as final"); - assertEquals(taskId, completedEvent.getTaskId(), "Event should have correct task ID"); - assertEquals(contextId, completedEvent.getContextId(), "Event should have correct context ID"); + assertEquals(taskId, completedEvent.taskId(), "Event should have correct task ID"); + assertEquals(contextId, completedEvent.contextId(), "Event should have correct context ID"); // Note: We do NOT verify TaskStore state here because replicated events intentionally // skip TaskStore updates to avoid duplicates. The TaskStore is updated on the originating @@ -311,11 +311,11 @@ public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { pollingClient.sendMessage(workingMessage, List.of((ClientEvent event, AgentCard card) -> { if (event instanceof TaskEvent taskEvent) { - taskIdRef.set(taskEvent.getTask().getId()); + taskIdRef.set(taskEvent.getTask().id()); workingLatch.countDown(); } else if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { - if (status.getStatus().state() == TaskState.WORKING) { - taskIdRef.set(status.getTaskId()); + if (status.status().state() == TaskState.WORKING) { + taskIdRef.set(status.taskId()); workingLatch.countDown(); } } diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java index 4db27cb14..e00282cbe 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java @@ -64,12 +64,12 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC } private String getLastTextPart(Message message) throws JSONRPCError { - if (message.getParts().isEmpty()) { + if (message.parts().isEmpty()) { throw new InvalidRequestError("No parts in message"); } - Part part = message.getParts().get(message.getParts().size() - 1); + Part part = message.parts().get(message.parts().size() - 1); if (part.getKind() == Part.Kind.TEXT) { - return ((TextPart) part).getText(); + return ((TextPart) part).text(); } throw new InvalidRequestError("Last part is not text"); } diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index bbad23953..df65f290e 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -65,22 +65,22 @@ void initConfig() { @Transactional @Override public void save(Task task) { - LOGGER.debug("Saving task with ID: {}", task.getId()); + LOGGER.debug("Saving task with ID: {}", task.id()); try { JpaTask jpaTask = JpaTask.createFromTask(task); em.merge(jpaTask); - LOGGER.debug("Persisted/updated task with ID: {}", task.getId()); + LOGGER.debug("Persisted/updated task with ID: {}", task.id()); - if (task.getStatus() != null && task.getStatus().state() != null && task.getStatus().state().isFinal()) { + if (task.status() != null && task.status().state() != null && task.status().state().isFinal()) { // Fire CDI event if task reached final state // IMPORTANT: The event will be delivered AFTER transaction commits (AFTER_SUCCESS observers) // This ensures the task's final state is durably stored before the QueueClosedEvent poison pill is sent - LOGGER.debug("Task {} is in final state, firing TaskFinalizedEvent", task.getId()); - taskFinalizedEvent.fire(new TaskFinalizedEvent(task.getId())); + LOGGER.debug("Task {} is in final state, firing TaskFinalizedEvent", task.id()); + taskFinalizedEvent.fire(new TaskFinalizedEvent(task.id())); } } catch (JsonProcessingException e) { - LOGGER.error("Failed to serialize task with ID: {}", task.getId(), e); - throw new RuntimeException("Failed to serialize task with ID: " + task.getId(), e); + LOGGER.error("Failed to serialize task with ID: {}", task.id(), e); + throw new RuntimeException("Failed to serialize task with ID: " + task.id(), e); } } @@ -147,7 +147,7 @@ public boolean isTaskActive(String taskId) { Task task = jpaTask.getTask(); // Task is active if not in final state - if (task.getStatus() == null || task.getStatus().state() == null || !task.getStatus().state().isFinal()) { + if (task.status() == null || task.status().state() == null || !task.status().state().isFinal()) { LOGGER.debug("Task is not in final state, considering active: {}", taskId); return true; } @@ -207,9 +207,9 @@ public boolean isTaskFinalized(String taskId) { Task task = jpaTask.getTask(); // Task is finalized if in final state (ignore grace period) - boolean isFinalized = task.getStatus() != null - && task.getStatus().state() != null - && task.getStatus().state().isFinal(); + boolean isFinalized = task.status() != null + && task.status().state() != null + && task.status().state().isFinal(); LOGGER.debug("Task {} finalization check: {}", taskId, isFinalized); return isFinalized; @@ -343,8 +343,8 @@ public ListTasksResult list(ListTasksParams params) { if (hasMore && !tasks.isEmpty()) { Task lastTask = tasks.get(tasks.size() - 1); // All tasks have timestamps (TaskStatus canonical constructor ensures this) - long timestampMillis = lastTask.getStatus().timestamp().toInstant().toEpochMilli(); - nextPageToken = timestampMillis + ":" + lastTask.getId(); + long timestampMillis = lastTask.status().timestamp().toInstant().toEpochMilli(); + nextPageToken = timestampMillis + ":" + lastTask.id(); } // Apply post-processing transformations (history limiting, artifact removal) @@ -361,16 +361,16 @@ public ListTasksResult list(ListTasksParams params) { private Task transformTask(Task task, int historyLength, boolean includeArtifacts) { // Limit history if needed (keep most recent N messages) - List history = task.getHistory(); + List history = task.history(); if (historyLength > 0 && history != null && history.size() > historyLength) { history = history.subList(history.size() - historyLength, history.size()); } // Remove artifacts if not requested - List artifacts = includeArtifacts ? task.getArtifacts() : List.of(); + List artifacts = includeArtifacts ? task.artifacts() : List.of(); // If no transformation needed, return original task - if (history == task.getHistory() && artifacts == task.getArtifacts()) { + if (history == task.history() && artifacts == task.artifacts()) { return task; } diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java index 7253515ad..3aabad9d7 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -117,7 +117,7 @@ public Task getTask() throws JsonProcessingException { public void setTask(Task task) throws JsonProcessingException { taskJson = JsonUtil.toJson(task); if (id == null) { - id = task.getId(); + id = task.id(); } this.task = task; updateDenormalizedFields(task); @@ -126,7 +126,7 @@ public void setTask(Task task) throws JsonProcessingException { static JpaTask createFromTask(Task task) throws JsonProcessingException { String json = JsonUtil.toJson(task); - JpaTask jpaTask = new JpaTask(task.getId(), json); + JpaTask jpaTask = new JpaTask(task.id(), json); jpaTask.task = task; jpaTask.updateDenormalizedFields(task); jpaTask.updateFinalizedTimestamp(task); @@ -140,14 +140,14 @@ static JpaTask createFromTask(Task task) throws JsonProcessingException { * @param task the task to extract fields from */ private void updateDenormalizedFields(Task task) { - this.contextId = task.getContextId(); - if (task.getStatus() != null) { - io.a2a.spec.TaskState taskState = task.getStatus().state(); + this.contextId = task.contextId(); + if (task.status() != null) { + io.a2a.spec.TaskState taskState = task.status().state(); this.state = (taskState != null) ? taskState.asString() : null; // Extract status timestamp for efficient querying and sorting // Truncate to milliseconds for keyset pagination consistency (pageToken uses millis) - this.statusTimestamp = (task.getStatus().timestamp() != null) - ? task.getStatus().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) + this.statusTimestamp = (task.status().timestamp() != null) + ? task.status().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) : null; } else { this.state = null; @@ -162,8 +162,8 @@ private void updateDenormalizedFields(Task task) { * @param task the task to check for finalization */ private void updateFinalizedTimestamp(Task task) { - if (task.getStatus() != null && task.getStatus().state() != null) { - setFinalizedAt(Instant.now(), task.getStatus().state().isFinal()); + if (task.status() != null && task.status().state() != null) { + setFinalizedAt(Instant.now(), task.status().state().isFinal()); } } } diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java index a5d1aea91..1fa3c3e99 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreIntegrationTest.java @@ -99,8 +99,8 @@ public void testJpaDatabaseTaskStore() throws Exception { assertTrue(latch.await(10, TimeUnit.SECONDS), "Timeout waiting for task creation"); Task createdTask = taskRef.get(); assertNotNull(createdTask); - assertEquals(0, createdTask.getArtifacts().size()); - assertEquals(TaskState.SUBMITTED, createdTask.getStatus().state()); + assertEquals(0, createdTask.artifacts().size()); + assertEquals(TaskState.SUBMITTED, createdTask.status().state()); // Send a message updating the Task userMessage = Message.builder() @@ -128,24 +128,24 @@ public void testJpaDatabaseTaskStore() throws Exception { assertTrue(latch2.await(10, TimeUnit.SECONDS), "Timeout waiting for task creation"); Task updatedTask = taskRef2.get(); assertNotNull(updatedTask); - assertEquals(1, updatedTask.getArtifacts().size()); - assertEquals(TaskState.SUBMITTED, updatedTask.getStatus().state()); + assertEquals(1, updatedTask.artifacts().size()); + assertEquals(TaskState.SUBMITTED, updatedTask.status().state()); Task retrievedTask = client.getTask(new TaskQueryParams(taskId), null); assertNotNull(retrievedTask); - assertEquals(1, retrievedTask.getArtifacts().size()); - assertEquals(TaskState.SUBMITTED, retrievedTask.getStatus().state()); + assertEquals(1, retrievedTask.artifacts().size()); + assertEquals(TaskState.SUBMITTED, retrievedTask.status().state()); // Cancel the task Task cancelledTask = client.cancelTask(new TaskIdParams(taskId), null); assertNotNull(cancelledTask); - assertEquals(1, cancelledTask.getArtifacts().size()); - assertEquals(TaskState.CANCELED, cancelledTask.getStatus().state()); + assertEquals(1, cancelledTask.artifacts().size()); + assertEquals(TaskState.CANCELED, cancelledTask.status().state()); Task retrievedCancelledTask = client.getTask(new TaskQueryParams(taskId), null); assertNotNull(retrievedCancelledTask); - assertEquals(1, retrievedCancelledTask.getArtifacts().size()); - assertEquals(TaskState.CANCELED, retrievedCancelledTask.getStatus().state()); + assertEquals(1, retrievedCancelledTask.artifacts().size()); + assertEquals(TaskState.CANCELED, retrievedCancelledTask.status().state()); // None of the framework code deletes tasks, so just do this manually taskStore.delete(taskId); diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index 42c9304f0..cfb4eadc8 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -60,9 +60,9 @@ public void testSaveAndRetrieveTask() { Task retrieved = taskStore.get("test-task-1"); assertNotNull(retrieved); - assertEquals("test-task-1", retrieved.getId()); - assertEquals("test-context-1", retrieved.getContextId()); - assertEquals(TaskState.SUBMITTED, retrieved.getStatus().state()); + assertEquals("test-task-1", retrieved.id()); + assertEquals("test-context-1", retrieved.contextId()); + assertEquals(TaskState.SUBMITTED, retrieved.status().state()); } @Test @@ -90,12 +90,12 @@ public void testSaveAndRetrieveTaskWithHistory() { Task retrieved = taskStore.get("test-task-2"); assertNotNull(retrieved); - assertEquals("test-task-2", retrieved.getId()); - assertEquals("test-context-2", retrieved.getContextId()); - assertEquals(TaskState.WORKING, retrieved.getStatus().state()); - assertEquals(1, retrieved.getHistory().size()); - assertEquals("msg-1", retrieved.getHistory().get(0).getMessageId()); - assertEquals("Hello, agent!", ((TextPart) retrieved.getHistory().get(0).getParts().get(0)).getText()); + assertEquals("test-task-2", retrieved.id()); + assertEquals("test-context-2", retrieved.contextId()); + assertEquals(TaskState.WORKING, retrieved.status().state()); + assertEquals(1, retrieved.history().size()); + assertEquals("msg-1", retrieved.history().get(0).messageId()); + assertEquals("Hello, agent!", ((TextPart) retrieved.history().get(0).parts().get(0)).text()); } @Test @@ -123,8 +123,8 @@ public void testUpdateExistingTask() { Task retrieved = taskStore.get("test-task-3"); assertNotNull(retrieved); - assertEquals("test-task-3", retrieved.getId()); - assertEquals(TaskState.COMPLETED, retrieved.getStatus().state()); + assertEquals("test-task-3", retrieved.id()); + assertEquals(TaskState.COMPLETED, retrieved.status().state()); } @Test @@ -184,11 +184,11 @@ public void testTaskWithComplexMetadata() { Task retrieved = taskStore.get("test-task-5"); assertNotNull(retrieved); - assertEquals("test-task-5", retrieved.getId()); - assertNotNull(retrieved.getMetadata()); - assertEquals("value1", retrieved.getMetadata().get("key1")); - assertEquals(42, ((Number)retrieved.getMetadata().get("key2")).intValue()); - assertEquals(true, retrieved.getMetadata().get("key3")); + assertEquals("test-task-5", retrieved.id()); + assertNotNull(retrieved.metadata()); + assertEquals("value1", retrieved.metadata().get("key1")); + assertEquals(42, ((Number)retrieved.metadata().get("key2")).intValue()); + assertEquals(true, retrieved.metadata().get("key3")); } @Test @@ -336,7 +336,7 @@ public void testListTasksFilterByContextId() { assertEquals(2, result.totalSize()); assertEquals(2, result.pageSize()); assertEquals(2, result.tasks().size()); - assertTrue(result.tasks().stream().allMatch(t -> "context-A".equals(t.getContextId()))); + assertTrue(result.tasks().stream().allMatch(t -> "context-A".equals(t.contextId()))); } @Test @@ -376,7 +376,7 @@ public void testListTasksFilterByStatus() { assertEquals(1, result.totalSize()); assertEquals(1, result.pageSize()); assertEquals(1, result.tasks().size()); - assertEquals(TaskState.WORKING, result.tasks().get(0).getStatus().state()); + assertEquals(TaskState.WORKING, result.tasks().get(0).status().state()); } @Test @@ -415,9 +415,9 @@ public void testListTasksCombinedFilters() { assertEquals(1, result.totalSize()); assertEquals(1, result.pageSize()); - assertEquals("task-combined-2", result.tasks().get(0).getId()); - assertEquals("context-X", result.tasks().get(0).getContextId()); - assertEquals(TaskState.WORKING, result.tasks().get(0).getStatus().state()); + assertEquals("task-combined-2", result.tasks().get(0).id()); + assertEquals("context-X", result.tasks().get(0).contextId()); + assertEquals(TaskState.WORKING, result.tasks().get(0).status().state()); } @Test @@ -542,8 +542,8 @@ public void testListTasksPaginationWithDifferentTimestamps() { assertNotNull(result1.nextPageToken(), "Should have next page token"); // Verify first page order - assertEquals("task-diff-d", result1.tasks().get(0).getId(), "First task should be most recent"); - assertEquals("task-diff-e", result1.tasks().get(1).getId(), "Second task should be 1 min ago"); + assertEquals("task-diff-d", result1.tasks().get(0).id(), "First task should be most recent"); + assertEquals("task-diff-e", result1.tasks().get(1).id(), "Second task should be 1 min ago"); // Verify pageToken format: "timestamp_millis:taskId" assertTrue(result1.nextPageToken().contains(":"), "PageToken should have format timestamp:id"); @@ -565,8 +565,8 @@ public void testListTasksPaginationWithDifferentTimestamps() { assertNotNull(result2.nextPageToken(), "Should have next page token"); // Verify second page order (tasks with same timestamp, sorted by ID) - assertEquals("task-diff-b", result2.tasks().get(0).getId(), "Third task should be 5 min ago, ID 'b'"); - assertEquals("task-diff-c", result2.tasks().get(1).getId(), "Fourth task should be 5 min ago, ID 'c'"); + assertEquals("task-diff-b", result2.tasks().get(0).id(), "Third task should be 5 min ago, ID 'b'"); + assertEquals("task-diff-c", result2.tasks().get(1).id(), "Fourth task should be 5 min ago, ID 'c'"); // Page 3: Get last task ListTasksParams params3 = ListTasksParams.builder() @@ -582,13 +582,13 @@ public void testListTasksPaginationWithDifferentTimestamps() { assertNull(result3.nextPageToken(), "Last page should have no next page token"); // Verify last task - assertEquals("task-diff-a", result3.tasks().get(0).getId(), "Last task should be oldest"); + assertEquals("task-diff-a", result3.tasks().get(0).id(), "Last task should be oldest"); // Verify no duplicates across all pages List allTaskIds = new ArrayList<>(); - allTaskIds.addAll(result1.tasks().stream().map(Task::getId).toList()); - allTaskIds.addAll(result2.tasks().stream().map(Task::getId).toList()); - allTaskIds.addAll(result3.tasks().stream().map(Task::getId).toList()); + allTaskIds.addAll(result1.tasks().stream().map(Task::id).toList()); + allTaskIds.addAll(result2.tasks().stream().map(Task::id).toList()); + allTaskIds.addAll(result3.tasks().stream().map(Task::id).toList()); assertEquals(5, allTaskIds.size(), "Should have exactly 5 tasks across all pages"); assertEquals(5, allTaskIds.stream().distinct().count(), "Should have no duplicate tasks"); @@ -628,11 +628,11 @@ public void testListTasksHistoryLimiting() { assertEquals(1, result.tasks().size()); Task retrieved = result.tasks().get(0); - assertEquals(3, retrieved.getHistory().size()); + assertEquals(3, retrieved.history().size()); // Should have messages 8, 9, 10 (last 3) - assertEquals("msg-history-limit-8", retrieved.getHistory().get(0).getMessageId()); - assertEquals("msg-history-limit-9", retrieved.getHistory().get(1).getMessageId()); - assertEquals("msg-history-limit-10", retrieved.getHistory().get(2).getMessageId()); + assertEquals("msg-history-limit-8", retrieved.history().get(0).messageId()); + assertEquals("msg-history-limit-9", retrieved.history().get(1).messageId()); + assertEquals("msg-history-limit-10", retrieved.history().get(2).messageId()); } @Test @@ -664,7 +664,7 @@ public void testListTasksArtifactInclusion() { ListTasksResult resultWithout = taskStore.list(paramsWithoutArtifacts); assertEquals(1, resultWithout.tasks().size()); - assertTrue(resultWithout.tasks().get(0).getArtifacts().isEmpty(), + assertTrue(resultWithout.tasks().get(0).artifacts().isEmpty(), "By default, artifacts should be excluded"); // List with artifacts - filter by unique context @@ -676,9 +676,9 @@ public void testListTasksArtifactInclusion() { ListTasksResult resultWith = taskStore.list(paramsWithArtifacts); assertEquals(1, resultWith.tasks().size()); - assertEquals(1, resultWith.tasks().get(0).getArtifacts().size(), + assertEquals(1, resultWith.tasks().get(0).artifacts().size(), "When includeArtifacts=true, artifacts should be included"); - assertEquals("artifact-unique-1", resultWith.tasks().get(0).getArtifacts().get(0).artifactId()); + assertEquals("artifact-unique-1", resultWith.tasks().get(0).artifacts().get(0).artifactId()); } @Test @@ -789,8 +789,8 @@ public void testListTasksOrderingById() { ListTasksResult result = taskStore.list(params); assertEquals(3, result.tasks().size()); - assertEquals("task-order-a", result.tasks().get(0).getId()); - assertEquals("task-order-b", result.tasks().get(1).getId()); - assertEquals("task-order-c", result.tasks().get(2).getId()); + assertEquals("task-order-a", result.tasks().get(0).id()); + assertEquals("task-order-b", result.tasks().get(1).id()); + assertEquals("task-order-c", result.tasks().get(2).id()); } } diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java index 8e4a1c8eb..374eee549 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java @@ -55,9 +55,9 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC } private String getLastTextPart(Message message) throws JSONRPCError { - Part part = message.getParts().get(message.getParts().size() - 1); + Part part = message.parts().get(message.parts().size() - 1); if (part.getKind() == Part.Kind.TEXT) { - return ((TextPart) part).getText(); + return ((TextPart) part).text(); } throw new InvalidRequestError("No parts"); } diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index 308d1d44c..89d166801 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -42,12 +42,12 @@ public RequestContext( // If the taskId and contextId were specified, they must match the params if (params != null) { - if (taskId != null && !taskId.equals(params.message().getTaskId())) { + if (taskId != null && !taskId.equals(params.message().taskId())) { throw new InvalidParamsError("bad task id"); } else { checkOrGenerateTaskId(); } - if (contextId != null && !contextId.equals(params.message().getContextId())) { + if (contextId != null && !contextId.equals(params.message().contextId())) { throw new InvalidParamsError("bad context id"); } else { checkOrGenerateContextId(); @@ -105,11 +105,16 @@ private void checkOrGenerateTaskId() { if (params == null) { return; } - if (taskId == null && params.message().getTaskId() == null) { - params.message().setTaskId(UUID.randomUUID().toString()); - } - if (params.message().getTaskId() != null) { - this.taskId = params.message().getTaskId(); + if (taskId == null && params.message().taskId() == null) { + // Message is immutable, create new one with generated taskId + String generatedTaskId = UUID.randomUUID().toString(); + Message updatedMessage = Message.builder(params.message()) + .taskId(generatedTaskId) + .build(); + params = new MessageSendParams(updatedMessage, params.configuration(), params.metadata()); + this.taskId = generatedTaskId; + } else if (params.message().taskId() != null) { + this.taskId = params.message().taskId(); } } @@ -117,16 +122,21 @@ private void checkOrGenerateContextId() { if (params == null) { return; } - if (contextId == null && params.message().getContextId() == null) { - params.message().setContextId(UUID.randomUUID().toString()); - } - if (params.message().getContextId() != null) { - this.contextId = params.message().getContextId(); + if (contextId == null && params.message().contextId() == null) { + // Message is immutable, create new one with generated contextId + String generatedContextId = UUID.randomUUID().toString(); + Message updatedMessage = Message.builder(params.message()) + .contextId(generatedContextId) + .build(); + params = new MessageSendParams(updatedMessage, params.configuration(), params.metadata()); + this.contextId = generatedContextId; + } else if (params.message().contextId() != null) { + this.contextId = params.message().contextId(); } } private String getMessageText(Message message, String delimiter) { - List textParts = getTextParts(message.getParts()); + List textParts = getTextParts(message.parts()); return String.join(delimiter, textParts); } @@ -134,7 +144,7 @@ private List getTextParts(List> parts) { return parts.stream() .filter(part -> part.getKind() == Part.Kind.TEXT) .map(part -> (TextPart) part) - .map(TextPart::getText) + .map(TextPart::text) .collect(Collectors.toList()); } diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java b/server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java index 933258a45..573d0e879 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/SimpleRequestContextBuilder.java @@ -19,9 +19,9 @@ public SimpleRequestContextBuilder(TaskStore taskStore, boolean shouldPopulateRe public RequestContext build() { List relatedTasks = null; if (taskStore != null && shouldPopulateReferredTasks && getParams() != null - && getParams().message().getReferenceTaskIds() != null) { + && getParams().message().referenceTaskIds() != null) { relatedTasks = new ArrayList<>(); - for (String taskId : getParams().message().getReferenceTaskIds()) { + for (String taskId : getParams().message().referenceTaskIds()) { Task task = taskStore.get(taskId); if (task != null) { relatedTasks.add(task); diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index b6ff7abce..d4fd7b682 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -78,7 +78,7 @@ public Flow.Publisher consumeAll() { } else if (event instanceof Message) { isFinalEvent = true; } else if (event instanceof Task task) { - isFinalEvent = task.getStatus().state().isFinal(); + isFinalEvent = task.status().state().isFinal(); } else if (event instanceof QueueClosedEvent) { // Poison pill event - signals queue closure from remote node // Do NOT send to subscribers - just close the queue diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 334c1f368..00f4c3ad9 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -175,13 +175,13 @@ public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws * @return the task with limited history, or the original task if no limiting needed */ private static Task limitTaskHistory(Task task, @Nullable Integer historyLength) { - if (task.getHistory() == null || historyLength == null || historyLength >= task.getHistory().size()) { + if (task.history() == null || historyLength == null || historyLength >= task.history().size()) { return task; } // Keep only the most recent historyLength messages - List limitedHistory = task.getHistory().subList( - task.getHistory().size() - historyLength, - task.getHistory().size()); + List limitedHistory = task.history().subList( + task.history().size() - historyLength, + task.history().size()); return Task.builder(task) .history(limitedHistory) .build(); @@ -217,33 +217,33 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws } // Check if task is in a non-cancelable state (completed, canceled, failed, rejected) - if (task.getStatus().state().isFinal()) { + if (task.status().state().isFinal()) { throw new TaskNotCancelableError( - "Task cannot be canceled - current state: " + task.getStatus().state().asString()); + "Task cannot be canceled - current state: " + task.status().state().asString()); } TaskManager taskManager = new TaskManager( - task.getId(), - task.getContextId(), + task.id(), + task.contextId(), taskStore, null); ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); - EventQueue queue = queueManager.tap(task.getId()); + EventQueue queue = queueManager.tap(task.id()); if (queue == null) { - queue = queueManager.getEventQueueBuilder(task.getId()).build(); + queue = queueManager.getEventQueueBuilder(task.id()).build(); } agentExecutor.cancel( requestContextBuilder.get() - .setTaskId(task.getId()) - .setContextId(task.getContextId()) + .setTaskId(task.id()) + .setContextId(task.contextId()) .setTask(task) .setServerCallContext(context) .build(), queue); - Optional.ofNullable(runningAgents.get(task.getId())) + Optional.ofNullable(runningAgents.get(task.id())) .ifPresent(cf -> cf.cancel(true)); EventConsumer consumer = new EventConsumer(queue); @@ -253,9 +253,9 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws } // Verify task was actually canceled (not completed concurrently) - if (tempTask.getStatus().state() != TaskState.CANCELED) { + if (tempTask.status().state() != TaskState.CANCELED) { throw new TaskNotCancelableError( - "Task cannot be canceled - current state: " + tempTask.getStatus().state().asString()); + "Task cannot be canceled - current state: " + tempTask.status().state().asString()); } return tempTask; @@ -263,7 +263,7 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws @Override public EventKind onMessageSend(MessageSendParams params, ServerCallContext context) throws JSONRPCError { - LOGGER.debug("onMessageSend - task: {}; context {}", params.message().getTaskId(), params.message().getContextId()); + LOGGER.debug("onMessageSend - task: {}; context {}", params.message().taskId(), params.message().contextId()); MessageSendSetup mss = initMessageSend(params, context); String taskId = mss.requestContext.getTaskId(); @@ -313,8 +313,8 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte // Store push notification config for newly created tasks (mirrors streaming logic) // Only for NEW tasks - existing tasks are handled by initMessageSend() if (mss.task() == null && kind instanceof Task createdTask && shouldAddPushInfo(params)) { - LOGGER.debug("Storing push notification config for new task {}", createdTask.getId()); - pushConfigStore.setInfo(createdTask.getId(), params.configuration().pushNotificationConfig()); + LOGGER.debug("Storing push notification config for new task {}", createdTask.id()); + pushConfigStore.setInfo(createdTask.id(), params.configuration().pushNotificationConfig()); } if (blocking && interruptedOrNonBlocking) { @@ -371,12 +371,12 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte kind = updatedTask; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Fetched final task for {} with state {} and {} artifacts", - nonNullTaskId, updatedTask.getStatus().state(), - updatedTask.getArtifacts().size()); + nonNullTaskId, updatedTask.status().state(), + updatedTask.artifacts().size()); } } } - if (kind instanceof Task taskResult && !taskId.equals(taskResult.getId())) { + if (kind instanceof Task taskResult && !taskId.equals(taskResult.id())) { throw new InternalError("Task ID mismatch in agent response"); } @@ -400,7 +400,7 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte public Flow.Publisher onMessageSendStream( MessageSendParams params, ServerCallContext context) throws JSONRPCError { LOGGER.debug("onMessageSendStream START - task: {}; context: {}; runningAgents: {}; backgroundTasks: {}", - params.message().getTaskId(), params.message().getContextId(), runningAgents.size(), backgroundTasks.size()); + params.message().taskId(), params.message().contextId(), runningAgents.size(), backgroundTasks.size()); MessageSendSetup mss = initMessageSend(params, context); @Nullable String initialTaskId = mss.requestContext.getTaskId(); @@ -431,15 +431,15 @@ public Flow.Publisher onMessageSendStream( processor(createTubeConfig(), results, ((errorConsumer, item) -> { Event event = item.getEvent(); if (event instanceof Task createdTask) { - if (!Objects.equals(taskId.get(), createdTask.getId())) { + if (!Objects.equals(taskId.get(), createdTask.id())) { errorConsumer.accept(new InternalError("Task ID mismatch in agent response")); } // TODO the Python implementation no longer has the following block but removing it causes // failures here try { - queueManager.add(createdTask.getId(), queue); - taskId.set(createdTask.getId()); + queueManager.add(createdTask.id(), queue); + taskId.set(createdTask.id()); } catch (TaskQueueExistsException e) { // TODO Log } @@ -448,7 +448,7 @@ public Flow.Publisher onMessageSendStream( params.configuration().pushNotificationConfig() != null) { pushConfigStore.setInfo( - createdTask.getId(), + createdTask.id(), params.configuration().pushNotificationConfig()); } @@ -614,20 +614,20 @@ public Flow.Publisher onResubscribeToTask( throw new TaskNotFoundError(); } - TaskManager taskManager = new TaskManager(task.getId(), task.getContextId(), taskStore, null); + TaskManager taskManager = new TaskManager(task.id(), task.contextId(), taskStore, null); ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); - EventQueue queue = queueManager.tap(task.getId()); + EventQueue queue = queueManager.tap(task.id()); LOGGER.debug("onResubscribeToTask - tapped queue: {}", queue != null ? System.identityHashCode(queue) : "null"); if (queue == null) { // If task is in final state, queue legitimately doesn't exist anymore - if (task.getStatus().state().isFinal()) { + if (task.status().state().isFinal()) { throw new TaskNotFoundError(); } // For non-final tasks, recreate the queue so client can receive future events // (Note: historical events from before queue closed are not available) - LOGGER.debug("Queue not found for active task {}, creating new queue for future events", task.getId()); - queue = queueManager.createOrTap(task.getId()); + LOGGER.debug("Queue not found for active task {}, creating new queue for future events", task.id()); + queue = queueManager.createOrTap(task.id()); } EventConsumer consumer = new EventConsumer(queue); @@ -808,8 +808,8 @@ private CompletableFuture cleanupProducer(@Nullable CompletableFuture pushConfigs = configStore.getInfo(task.getId()); + List pushConfigs = configStore.getInfo(task.id()); if (pushConfigs == null || pushConfigs.isEmpty()) { return; } @@ -59,10 +59,10 @@ public void sendNotification(Task task) { try { boolean allSent = dispatchResult.get(); if (! allSent) { - LOGGER.warn("Some push notifications failed to send for taskId: " + task.getId()); + LOGGER.warn("Some push notifications failed to send for taskId: " + task.id()); } } catch (InterruptedException | ExecutionException e) { - LOGGER.warn("Some push notifications failed to send for taskId " + task.getId() + ": {}", e.getMessage(), e); + LOGGER.warn("Some push notifications failed to send for taskId " + task.id() + ": {}", e.getMessage(), e); } } diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index fbc424511..bb853e297 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -21,7 +21,7 @@ public class InMemoryTaskStore implements TaskStore, TaskStateProvider { @Override public void save(Task task) { - tasks.put(task.getId(), task); + tasks.put(task.id(), task); } @Override @@ -38,20 +38,20 @@ public void delete(String taskId) { public ListTasksResult list(ListTasksParams params) { // Filter and sort tasks in a single stream pipeline List allFilteredTasks = tasks.values().stream() - .filter(task -> params.contextId() == null || params.contextId().equals(task.getContextId())) + .filter(task -> params.contextId() == null || params.contextId().equals(task.contextId())) .filter(task -> params.status() == null || - (task.getStatus() != null && params.status().equals(task.getStatus().state()))) + (task.status() != null && params.status().equals(task.status().state()))) .filter(task -> params.lastUpdatedAfter() == null || - (task.getStatus() != null && - task.getStatus().timestamp() != null && - task.getStatus().timestamp().toInstant().isAfter(params.lastUpdatedAfter()))) + (task.status() != null && + task.status().timestamp() != null && + task.status().timestamp().toInstant().isAfter(params.lastUpdatedAfter()))) .sorted(Comparator.comparing( - (Task t) -> (t.getStatus() != null && t.getStatus().timestamp() != null) + (Task t) -> (t.status() != null && t.status().timestamp() != null) // Truncate to milliseconds for consistency with pageToken precision - ? t.getStatus().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) + ? t.status().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) : null, Comparator.nullsLast(Comparator.reverseOrder())) - .thenComparing(Task::getId)) + .thenComparing(Task::id)) .toList(); int totalSize = allFilteredTasks.size(); @@ -81,11 +81,11 @@ public ListTasksResult list(ListTasksParams params) { // All tasks have timestamps (TaskStatus canonical constructor ensures this) // Truncate to milliseconds for consistency with pageToken precision - java.time.Instant taskTimestamp = task.getStatus().timestamp().toInstant() + java.time.Instant taskTimestamp = task.status().timestamp().toInstant() .truncatedTo(java.time.temporal.ChronoUnit.MILLIS); int timestampCompare = taskTimestamp.compareTo(tokenTimestamp); - if (timestampCompare < 0 || (timestampCompare == 0 && task.getId().compareTo(tokenId) > 0)) { + if (timestampCompare < 0 || (timestampCompare == 0 && task.id().compareTo(tokenId) > 0)) { // This task is after the token, search left half right = mid; } else { @@ -115,8 +115,8 @@ public ListTasksResult list(ListTasksParams params) { if (endIndex < allFilteredTasks.size()) { Task lastTask = allFilteredTasks.get(endIndex - 1); // All tasks have timestamps (TaskStatus canonical constructor ensures this) - long timestampMillis = lastTask.getStatus().timestamp().toInstant().toEpochMilli(); - nextPageToken = timestampMillis + ":" + lastTask.getId(); + long timestampMillis = lastTask.status().timestamp().toInstant().toEpochMilli(); + nextPageToken = timestampMillis + ":" + lastTask.id(); } // Transform tasks: limit history and optionally remove artifacts @@ -132,16 +132,16 @@ public ListTasksResult list(ListTasksParams params) { private Task transformTask(Task task, int historyLength, boolean includeArtifacts) { // Limit history if needed (keep most recent N messages) - List history = task.getHistory(); + List history = task.history(); if (historyLength > 0 && history != null && history.size() > historyLength) { history = history.subList(history.size() - historyLength, history.size()); } // Remove artifacts if not requested - List artifacts = includeArtifacts ? task.getArtifacts() : List.of(); + List artifacts = includeArtifacts ? task.artifacts() : List.of(); // If no transformation needed, return original task - if (history == task.getHistory() && artifacts == task.getArtifacts()) { + if (history == task.history() && artifacts == task.artifacts()) { return task; } @@ -159,7 +159,7 @@ public boolean isTaskActive(String taskId) { return false; } // Task is active if not in final state - return task.getStatus() == null || task.getStatus().state() == null || !task.getStatus().state().isFinal(); + return task.status() == null || task.status().state() == null || !task.status().state().isFinal(); } @Override @@ -169,8 +169,8 @@ public boolean isTaskFinalized(String taskId) { return false; } // Task is finalized if in final state (ignores grace period) - return task.getStatus() != null - && task.getStatus().state() != null - && task.getStatus().state().isFinal(); + return task.status() != null + && task.status().state() != null + && task.status().state().isFinal(); } } diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index ffb9a3099..0fd62113f 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -161,10 +161,10 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, // Determine interrupt behavior boolean shouldInterrupt = false; boolean continueInBackground = false; - boolean isFinalEvent = (event instanceof Task task && task.getStatus().state().isFinal()) + boolean isFinalEvent = (event instanceof Task task && task.status().state().isFinal()) || (event instanceof TaskStatusUpdateEvent tsue && tsue.isFinal()); - boolean isAuthRequired = (event instanceof Task task && task.getStatus().state() == TaskState.AUTH_REQUIRED) - || (event instanceof TaskStatusUpdateEvent tsue && tsue.getStatus().state() == TaskState.AUTH_REQUIRED); + boolean isAuthRequired = (event instanceof Task task && task.status().state() == TaskState.AUTH_REQUIRED) + || (event instanceof TaskStatusUpdateEvent tsue && tsue.status().state() == TaskState.AUTH_REQUIRED); // Always interrupt on auth_required, as it needs external action. if (isAuthRequired) { @@ -286,7 +286,7 @@ private void callTaskManagerProcess(Event event) throws A2AServerException { private String taskIdForLogging() { Task task = taskManager.getTask(); - return task != null ? task.getId() : "unknown"; + return task != null ? task.id() : "unknown"; } public record EventTypeAndInterrupt(EventKind eventType, boolean interrupted, CompletableFuture consumptionFuture) { diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index d0644b5d0..55b24e409 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -61,28 +61,28 @@ public TaskManager(@Nullable String taskId, @Nullable String contextId, TaskStor } Task saveTaskEvent(Task task) throws A2AServerException { - checkIdsAndUpdateIfNecessary(task.getId(), task.getContextId()); + checkIdsAndUpdateIfNecessary(task.id(), task.contextId()); return saveTask(task); } Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { - checkIdsAndUpdateIfNecessary(event.getTaskId(), event.getContextId()); - Task task = ensureTask(event.getTaskId(), event.getContextId()); + checkIdsAndUpdateIfNecessary(event.taskId(), event.contextId()); + Task task = ensureTask(event.taskId(), event.contextId()); Task.Builder builder = Task.builder(task) - .status(event.getStatus()); + .status(event.status()); - if (task.getStatus().message() != null) { - List newHistory = task.getHistory() == null ? new ArrayList<>() : new ArrayList<>(task.getHistory()); - newHistory.add(task.getStatus().message()); + if (task.status().message() != null) { + List newHistory = task.history() == null ? new ArrayList<>() : new ArrayList<>(task.history()); + newHistory.add(task.status().message()); builder.history(newHistory); } // Handle metadata from the event - if (event.getMetadata() != null) { - Map metadata = task.getMetadata() == null ? new HashMap<>() : new HashMap<>(task.getMetadata()); - metadata.putAll(event.getMetadata()); + if (event.metadata() != null) { + Map metadata = task.metadata() == null ? new HashMap<>() : new HashMap<>(task.metadata()); + metadata.putAll(event.metadata()); builder.metadata(metadata); } @@ -91,8 +91,8 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { } Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { - checkIdsAndUpdateIfNecessary(event.getTaskId(), event.getContextId()); - Task task = ensureTask(event.getTaskId(), event.getContextId()); + checkIdsAndUpdateIfNecessary(event.taskId(), event.contextId()); + Task task = ensureTask(event.taskId(), event.contextId()); // taskId is guaranteed to be non-null after checkIdsAndUpdateIfNecessary String nonNullTaskId = taskId; if (nonNullTaskId == null) { @@ -114,9 +114,9 @@ public Event process(Event event) throws A2AServerException { } public Task updateWithMessage(Message message, Task task) { - List history = new ArrayList<>(task.getHistory()); + List history = new ArrayList<>(task.history()); - TaskStatus status = task.getStatus(); + TaskStatus status = task.status(); if (status.message() != null) { history.add(status.message()); status = new TaskStatus(status.state(), null, status.timestamp()); @@ -174,8 +174,8 @@ private Task createTask(String taskId, String contextId) { private Task saveTask(Task task) { taskStore.save(task); if (taskId == null) { - taskId = task.getId(); - contextId = task.getContextId(); + taskId = task.id(); + contextId = task.contextId(); } currentTask = task; return currentTask; diff --git a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index 7dfc0f6ac..d75829f0b 100644 --- a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -50,11 +50,12 @@ public void testInitWithParamsNoIds() { RequestContext context = new RequestContext(mockParams, null, null, null, null, null); - assertEquals(mockParams.message(), context.getMessage()); + // getMessage() returns a new Message with generated IDs, not the original + assertNotNull(context.getMessage()); assertEquals(taskId.toString(), context.getTaskId()); - assertEquals(mockParams.message().getTaskId(), taskId.toString()); + assertEquals(taskId.toString(), context.getMessage().taskId()); assertEquals(contextId.toString(), context.getContextId()); - assertEquals(mockParams.message().getContextId(), contextId.toString()); + assertEquals(contextId.toString(), context.getMessage().contextId()); } } @@ -67,7 +68,7 @@ public void testInitWithTaskId() { RequestContext context = new RequestContext(mockParams, taskId, null, null, null, null); assertEquals(taskId, context.getTaskId()); - assertEquals(taskId, mockParams.message().getTaskId()); + assertEquals(taskId, mockParams.message().taskId()); } @Test @@ -78,7 +79,7 @@ public void testInitWithContextId() { RequestContext context = new RequestContext(mockParams, null, contextId, null, null, null); assertEquals(contextId, context.getContextId()); - assertEquals(contextId, mockParams.message().getContextId()); + assertEquals(contextId, mockParams.message().contextId()); } @Test @@ -90,9 +91,9 @@ public void testInitWithBothIds() { RequestContext context = new RequestContext(mockParams, taskId, contextId, null, null, null); assertEquals(taskId, context.getTaskId()); - assertEquals(taskId, mockParams.message().getTaskId()); + assertEquals(taskId, mockParams.message().taskId()); assertEquals(contextId, context.getContextId()); - assertEquals(contextId, mockParams.message().getContextId()); + assertEquals(contextId, mockParams.message().contextId()); } @Test @@ -138,7 +139,7 @@ public void testCheckOrGenerateTaskIdWithExistingTaskId() { RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(existingId, context.getTaskId()); - assertEquals(existingId, mockParams.message().getTaskId()); + assertEquals(existingId, mockParams.message().taskId()); } @Test @@ -151,7 +152,7 @@ public void testCheckOrGenerateContextIdWithExistingContextId() { RequestContext context = new RequestContext(mockParams, null, null, null, null, null); assertEquals(existingId, context.getContextId()); - assertEquals(existingId, mockParams.message().getContextId()); + assertEquals(existingId, mockParams.message().contextId()); } @Test @@ -173,7 +174,7 @@ public void testInitRaisesErrorOnContextIdMismatch() { var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); InvalidParamsError error = assertThrows(InvalidParamsError.class, () -> - new RequestContext(mockParams, mockTask.getId(), "wrong-context-id", mockTask, null, null)); + new RequestContext(mockParams, mockTask.id(), "wrong-context-id", mockTask, null, null)); assertTrue(error.getMessage().contains("bad context id")); } @@ -204,7 +205,10 @@ public void testMessagePropertyWithParams() { var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); - assertEquals(mockParams.message(), context.getMessage()); + // getMessage() returns a new Message with generated IDs, not the original + assertNotNull(context.getMessage()); + assertEquals(mockMessage.role(), context.getMessage().role()); + assertEquals(mockMessage.parts(), context.getMessage().parts()); } @Test @@ -229,9 +233,9 @@ public void testInitWithTaskIdAndExistingTaskIdMatch() { var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); - RequestContext context = new RequestContext(mockParams, mockTask.getId(), null, mockTask, null, null); + RequestContext context = new RequestContext(mockParams, mockTask.id(), null, mockTask, null, null); - assertEquals(mockTask.getId(), context.getTaskId()); + assertEquals(mockTask.id(), context.getTaskId()); assertEquals(mockTask, context.getTask()); } @@ -242,9 +246,9 @@ public void testInitWithContextIdAndExistingContextIdMatch() { var mockTask = Task.builder().id("task-123").contextId("context-456").status(new TaskStatus(TaskState.COMPLETED)).build(); - RequestContext context = new RequestContext(mockParams, mockTask.getId(), mockTask.getContextId(), mockTask, null, null); + RequestContext context = new RequestContext(mockParams, mockTask.id(), mockTask.contextId(), mockTask, null, null); - assertEquals(mockTask.getContextId(), context.getContextId()); + assertEquals(mockTask.contextId(), context.getContextId()); assertEquals(mockTask, context.getTask()); } @@ -254,8 +258,8 @@ void testMessageBuilderGeneratesId() { var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); - assertNotNull(mockMessage.getMessageId()); - assertFalse(mockMessage.getMessageId().isEmpty()); + assertNotNull(mockMessage.messageId()); + assertFalse(mockMessage.messageId().isEmpty()); } @Test @@ -264,6 +268,6 @@ void testMessageBuilderUsesProvidedId() { var mockParams = MessageSendParams.builder().message(mockMessage).build(); RequestContext context = new RequestContext(mockParams, null, null, null, null, null); - assertEquals("123", mockMessage.getMessageId()); + assertEquals("123", mockMessage.messageId()); } } diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index ca6a210e2..abf881752 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -115,8 +115,8 @@ void testBlockingMessageContinueTask() throws Exception { assertTrue(result1 instanceof Task); Task task1 = (Task) result1; - assertTrue(task1.getId().equals(taskId)); - assertTrue(task1.getStatus().state() == TaskState.SUBMITTED); + assertTrue(task1.id().equals(taskId)); + assertTrue(task1.status().state() == TaskState.SUBMITTED); // Second blocking message to SAME taskId - should not hang Message message2 = Message.builder() @@ -414,9 +414,9 @@ void testDisconnectPersistsFinalTaskToStore() throws Exception { if (persistedTask != null) { // If task was persisted, it should have the final state assertTrue( - persistedTask.getStatus().state() == TaskState.COMPLETED || - persistedTask.getStatus().state() == TaskState.WORKING, - "Task should be persisted with working or completed state, got: " + persistedTask.getStatus().state() + persistedTask.status().state() == TaskState.COMPLETED || + persistedTask.status().state() == TaskState.WORKING, + "Task should be persisted with working or completed state, got: " + persistedTask.status().state() ); } // Note: In some architectures, the task might not be persisted if the @@ -480,15 +480,15 @@ void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception { Task returnedTask = (Task) result; // Verify task is in WORKING state (non-final, fire-and-forget) - assertEquals(TaskState.WORKING, returnedTask.getStatus().state(), - "Returned task should be WORKING (fire-and-forget), got: " + returnedTask.getStatus().state()); + assertEquals(TaskState.WORKING, returnedTask.status().state(), + "Returned task should be WORKING (fire-and-forget), got: " + returnedTask.status().state()); // Verify artifacts are included in the returned task - assertNotNull(returnedTask.getArtifacts(), + assertNotNull(returnedTask.artifacts(), "Returned task should have artifacts"); - assertTrue(returnedTask.getArtifacts().size() >= 1, + assertTrue(returnedTask.artifacts().size() >= 1, "Returned task should have at least 1 artifact, got: " + - returnedTask.getArtifacts().size()); + returnedTask.artifacts().size()); } /** @@ -569,8 +569,8 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { // Assertion 1: The immediate result should be the first event (WORKING) assertTrue(result instanceof Task, "Result should be a Task"); Task immediateTask = (Task) result; - assertEquals(TaskState.WORKING, immediateTask.getStatus().state(), - "Non-blocking should return immediately with WORKING state, got: " + immediateTask.getStatus().state()); + assertEquals(TaskState.WORKING, immediateTask.status().state(), + "Non-blocking should return immediately with WORKING state, got: " + immediateTask.status().state()); // At this point, the non-blocking call has returned, but the agent is still running @@ -586,7 +586,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { while (System.currentTimeMillis() - startTime < timeoutMs) { persistedTask = taskStore.get(taskId); - if (persistedTask != null && persistedTask.getStatus().state() == TaskState.COMPLETED) { + if (persistedTask != null && persistedTask.status().state() == TaskState.COMPLETED) { completedStateFound = true; break; } @@ -597,7 +597,7 @@ void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { assertTrue( completedStateFound, "Final task state should be COMPLETED (background consumption should have processed it), got: " + - (persistedTask != null ? persistedTask.getStatus().state() : "null") + + (persistedTask != null ? persistedTask.status().state() : "null") + " after " + (System.currentTimeMillis() - startTime) + "ms" ); } @@ -784,15 +784,15 @@ void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception { Task returnedTask = (Task) result; // Verify task is completed - assertEquals(TaskState.COMPLETED, returnedTask.getStatus().state(), + assertEquals(TaskState.COMPLETED, returnedTask.status().state(), "Returned task should be COMPLETED"); // Verify artifacts are included in the returned task - assertNotNull(returnedTask.getArtifacts(), + assertNotNull(returnedTask.artifacts(), "Returned task should have artifacts"); - assertTrue(returnedTask.getArtifacts().size() >= 2, + assertTrue(returnedTask.artifacts().size() >= 2, "Returned task should have at least 2 artifacts, got: " + - returnedTask.getArtifacts().size()); + returnedTask.artifacts().size()); } /** @@ -860,7 +860,7 @@ void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exceptio // Verify result is a task assertTrue(result instanceof Task, "Result should be a Task"); Task returnedTask = (Task) result; - assertEquals(taskId, returnedTask.getId()); + assertEquals(taskId, returnedTask.id()); // THE KEY ASSERTION: Verify pushNotificationConfig was stored List storedConfigs = pushConfigStore.getInfo(taskId); diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java index aabeeedde..d90629b09 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java @@ -67,8 +67,8 @@ private void verifyHttpCallWithoutToken(PushNotificationConfig config, Task task // Verify the request body contains the task data String sentBody = bodyCaptor.getValue(); - assertTrue(sentBody.contains(task.getId())); - assertTrue(sentBody.contains(task.getStatus().state().asString())); + assertTrue(sentBody.contains(task.id())); + assertTrue(sentBody.contains(task.status().state().asString())); } private Task createSampleTask(String taskId, TaskState state) { @@ -244,8 +244,8 @@ public void testSendNotificationSuccess() throws Exception { // Verify the request body contains the task data String sentBody = bodyCaptor.getValue(); - assertTrue(sentBody.contains(task.getId())); - assertTrue(sentBody.contains(task.getStatus().state().asString())); + assertTrue(sentBody.contains(task.id())); + assertTrue(sentBody.contains(task.status().state().asString())); } @Test @@ -276,8 +276,8 @@ public void testSendNotificationWithToken() throws Exception { // Verify the request body contains the task data String sentBody = bodyCaptor.getValue(); - assertTrue(sentBody.contains(task.getId())); - assertTrue(sentBody.contains(task.getStatus().state().asString())); + assertTrue(sentBody.contains(task.id())); + assertTrue(sentBody.contains(task.status().state().asString())); } @Test diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java index d49f6de4f..a042ef179 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java @@ -21,7 +21,7 @@ public void testSaveAndGet() throws Exception { InMemoryTaskStore store = new InMemoryTaskStore(); Task task = Utils.unmarshalFrom(TASK_JSON, Task.class); store.save(task); - Task retrieved = store.get(task.getId()); + Task retrieved = store.get(task.id()); assertSame(task, retrieved); } @@ -37,8 +37,8 @@ public void testDelete() throws Exception { InMemoryTaskStore store = new InMemoryTaskStore(); Task task = Utils.unmarshalFrom(TASK_JSON, Task.class); store.save(task); - store.delete(task.getId()); - Task retrieved = store.get(task.getId()); + store.delete(task.id()); + Task retrieved = store.get(task.id()); assertNull(retrieved); } diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java index 9a2186a68..fcc9e9cf0 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java @@ -159,7 +159,7 @@ private void testSendNotificationWithInvalidToken(String token, String testName) // Verify the task was sent via HTTP assertEquals(1, testHttpClient.tasks.size()); Task sentTask = testHttpClient.tasks.get(0); - assertEquals(taskData.getId(), sentTask.getId()); + assertEquals(taskData.id(), sentTask.id()); // Verify that no authentication header was sent (invalid token should not add header) assertEquals(1, testHttpClient.headers.size()); @@ -208,9 +208,9 @@ public void testSendNotificationSuccess() throws InterruptedException { // Verify the task was sent via HTTP assertEquals(1, testHttpClient.tasks.size()); Task sentTask = testHttpClient.tasks.get(0); - assertEquals(taskData.getId(), sentTask.getId()); - assertEquals(taskData.getContextId(), sentTask.getContextId()); - assertEquals(taskData.getStatus().state(), sentTask.getStatus().state()); + assertEquals(taskData.id(), sentTask.id()); + assertEquals(taskData.contextId(), sentTask.contextId()); + assertEquals(taskData.status().state(), sentTask.status().state()); } @Test @@ -233,7 +233,7 @@ public void testSendNotificationWithTokenSuccess() throws InterruptedException { // Verify the task was sent via HTTP assertEquals(1, testHttpClient.tasks.size()); Task sentTask = testHttpClient.tasks.get(0); - assertEquals(taskData.getId(), sentTask.getId()); + assertEquals(taskData.id(), sentTask.id()); // Verify that the X-A2A-Notification-Token header is sent with the correct token assertEquals(1, testHttpClient.headers.size()); @@ -295,9 +295,9 @@ public void testSendNotificationMultipleConfigs() throws InterruptedException { // Both tasks should be identical (same task sent to different endpoints) for (Task sentTask : testHttpClient.tasks) { - assertEquals(taskData.getId(), sentTask.getId()); - assertEquals(taskData.getContextId(), sentTask.getContextId()); - assertEquals(taskData.getStatus().state(), sentTask.getStatus().state()); + assertEquals(taskData.id(), sentTask.id()); + assertEquals(taskData.contextId(), sentTask.contextId()); + assertEquals(taskData.status().state(), sentTask.status().state()); } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index b2f440e95..0fc5648c9 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -43,7 +43,7 @@ public class TaskManagerTest { public void init() throws Exception { minimalTask = Utils.unmarshalFrom(TASK_JSON, Task.class); taskStore = new InMemoryTaskStore(); - taskManager = new TaskManager(minimalTask.getId(), minimalTask.getContextId(), taskStore, null); + taskManager = new TaskManager(minimalTask.id(), minimalTask.contextId(), taskStore, null); } @Test @@ -82,9 +82,9 @@ public void testSaveTaskEventStatusUpdate() throws A2AServerException { .build(), null); TaskStatusUpdateEvent event = new TaskStatusUpdateEvent( - minimalTask.getId(), + minimalTask.id(), newStatus, - minimalTask.getContextId(), + minimalTask.contextId(), false, new HashMap<>()); @@ -95,11 +95,11 @@ public void testSaveTaskEventStatusUpdate() throws A2AServerException { assertNotSame(initialTask, updated); assertSame(updated, saved); - assertEquals(initialTask.getId(), updated.getId()); - assertEquals(initialTask.getContextId(), updated.getContextId()); + assertEquals(initialTask.id(), updated.id()); + assertEquals(initialTask.contextId(), updated.contextId()); // TODO type does not get unmarshalled //assertEquals(initialTask.getType(), updated.getType()); - assertSame(newStatus, updated.getStatus()); + assertSame(newStatus, updated.status()); } @Test @@ -111,8 +111,8 @@ public void testSaveTaskEventArtifactUpdate() throws A2AServerException { .parts(Collections.singletonList(new TextPart("content"))) .build(); TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(newArtifact) .build(); Task saved = taskManager.saveTaskEvent(event); @@ -121,11 +121,11 @@ public void testSaveTaskEventArtifactUpdate() throws A2AServerException { assertSame(updatedTask, saved); assertNotSame(initialTask, updatedTask); - assertEquals(initialTask.getId(), updatedTask.getId()); - assertEquals(initialTask.getContextId(), updatedTask.getContextId()); - assertSame(initialTask.getStatus().state(), updatedTask.getStatus().state()); - assertEquals(1, updatedTask.getArtifacts().size()); - assertEquals(newArtifact, updatedTask.getArtifacts().get(0)); + assertEquals(initialTask.id(), updatedTask.id()); + assertEquals(initialTask.contextId(), updatedTask.contextId()); + assertSame(initialTask.status().state(), updatedTask.status().state()); + assertEquals(1, updatedTask.artifacts().size()); + assertEquals(newArtifact, updatedTask.artifacts().get(0)); } @Test @@ -146,13 +146,13 @@ public void testEnsureTaskNonExistentForStatusUpdate() throws A2AServerException .build(); Task task = taskManagerWithoutId.saveTaskEvent(event); - assertEquals(event.getTaskId(), taskManagerWithoutId.getTaskId()); - assertEquals(event.getContextId(), taskManagerWithoutId.getContextId()); + assertEquals(event.taskId(), taskManagerWithoutId.getTaskId()); + assertEquals(event.contextId(), taskManagerWithoutId.getContextId()); Task newTask = taskManagerWithoutId.getTask(); - assertEquals(event.getTaskId(), newTask.getId()); - assertEquals(event.getContextId(), newTask.getContextId()); - assertEquals(TaskState.SUBMITTED, newTask.getStatus().state()); + assertEquals(event.taskId(), newTask.id()); + assertEquals(event.contextId(), newTask.contextId()); + assertEquals(TaskState.SUBMITTED, newTask.status().state()); assertSame(newTask, task); } @@ -166,8 +166,8 @@ public void testSaveTaskEventNewTaskNoTaskId() throws A2AServerException { .build(); Task saved = taskManagerWithoutId.saveTaskEvent(task); - assertEquals(task.getId(), taskManagerWithoutId.getTaskId()); - assertEquals(task.getContextId(), taskManagerWithoutId.getContextId()); + assertEquals(task.id(), taskManagerWithoutId.getTaskId()); + assertEquals(task.contextId(), taskManagerWithoutId.getContextId()); Task retrieved = taskManagerWithoutId.getTask(); assertSame(task, retrieved); @@ -204,20 +204,20 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A .parts(Collections.singletonList(new TextPart("new content"))) .build(); TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(newArtifact) .append(true) .build(); Task updatedTask = taskManager.saveTaskEvent(event); - assertEquals(1, updatedTask.getArtifacts().size()); - Artifact updatedArtifact = updatedTask.getArtifacts().get(0); + assertEquals(1, updatedTask.artifacts().size()); + Artifact updatedArtifact = updatedTask.artifacts().get(0); assertEquals("artifact-id", updatedArtifact.artifactId()); assertEquals(2, updatedArtifact.parts().size()); - assertEquals("existing content", ((TextPart) updatedArtifact.parts().get(0)).getText()); - assertEquals("new content", ((TextPart) updatedArtifact.parts().get(1)).getText()); + assertEquals("existing content", ((TextPart) updatedArtifact.parts().get(0)).text()); + assertEquals("new content", ((TextPart) updatedArtifact.parts().get(1)).text()); } @Test @@ -233,8 +233,8 @@ public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throw .parts(Collections.singletonList(new TextPart("new content"))) .build(); TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(newArtifact) .append(true) .build(); @@ -243,7 +243,7 @@ public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throw Task updatedTask = taskManager.getTask(); // Should have no artifacts since append was ignored - assertEquals(0, updatedTask.getArtifacts().size()); + assertEquals(0, updatedTask.artifacts().size()); } @Test @@ -267,8 +267,8 @@ public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws .parts(Collections.singletonList(new TextPart("replacement content"))) .build(); TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(newArtifact) .append(false) .build(); @@ -276,11 +276,11 @@ public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws Task saved = taskManager.saveTaskEvent(event); Task updatedTask = taskManager.getTask(); - assertEquals(1, updatedTask.getArtifacts().size()); - Artifact updatedArtifact = updatedTask.getArtifacts().get(0); + assertEquals(1, updatedTask.artifacts().size()); + Artifact updatedArtifact = updatedTask.artifacts().get(0); assertEquals("artifact-id", updatedArtifact.artifactId()); assertEquals(1, updatedArtifact.parts().size()); - assertEquals("replacement content", ((TextPart) updatedArtifact.parts().get(0)).getText()); + assertEquals("replacement content", ((TextPart) updatedArtifact.parts().get(0)).text()); } @Test @@ -304,19 +304,19 @@ public void testTaskArtifactUpdateEventAppendNullWithExistingArtifact() throws A .parts(Collections.singletonList(new TextPart("replacement content"))) .build(); TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(newArtifact) .build(); // append is null Task saved = taskManager.saveTaskEvent(event); Task updatedTask = taskManager.getTask(); - assertEquals(1, updatedTask.getArtifacts().size()); - Artifact updatedArtifact = updatedTask.getArtifacts().get(0); + assertEquals(1, updatedTask.artifacts().size()); + Artifact updatedArtifact = updatedTask.artifacts().get(0); assertEquals("artifact-id", updatedArtifact.artifactId()); assertEquals(1, updatedArtifact.parts().size()); - assertEquals("replacement content", ((TextPart) updatedArtifact.parts().get(0)).getText()); + assertEquals("replacement content", ((TextPart) updatedArtifact.parts().get(0)).text()); } @Test @@ -397,12 +397,12 @@ public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException Task retrieved = taskManagerWithInitialMessage.getTask(); // Check that the task has the initial message in its history - assertNotNull(retrieved.getHistory()); - assertEquals(1, retrieved.getHistory().size()); - Message historyMessage = retrieved.getHistory().get(0); - assertEquals(initialMessage.getMessageId(), historyMessage.getMessageId()); - assertEquals(initialMessage.getRole(), historyMessage.getRole()); - assertEquals("initial message", ((TextPart) historyMessage.getParts().get(0)).getText()); + assertNotNull(retrieved.history()); + assertEquals(1, retrieved.history().size()); + Message historyMessage = retrieved.history().get(0); + assertEquals(initialMessage.messageId(), historyMessage.messageId()); + assertEquals(initialMessage.role(), historyMessage.role()); + assertEquals("initial message", ((TextPart) historyMessage.parts().get(0)).text()); } @Test @@ -435,13 +435,13 @@ public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerExcept // There should now be a history containing the initialMessage // But the current message (taskMessage) should be in the state, not in the history - assertNotNull(retrieved.getHistory()); - assertEquals(1, retrieved.getHistory().size()); - assertEquals("initial message", ((TextPart) retrieved.getHistory().get(0).getParts().get(0)).getText()); + assertNotNull(retrieved.history()); + assertEquals(1, retrieved.history().size()); + assertEquals("initial message", ((TextPart) retrieved.history().get(0).parts().get(0)).text()); // The message in the current state should be taskMessage - assertNotNull(retrieved.getStatus().message()); - assertEquals("task message", ((TextPart) retrieved.getStatus().message().getParts().get(0)).getText()); + assertNotNull(retrieved.status().message()); + assertEquals("task message", ((TextPart) retrieved.status().message().parts().get(0)).text()); } @Test @@ -457,8 +457,8 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException .parts(Collections.singletonList(new TextPart("content 1"))) .build(); TaskArtifactUpdateEvent event1 = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(artifact1) .build(); taskManager.saveTaskEvent(event1); @@ -470,18 +470,18 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException .parts(Collections.singletonList(new TextPart("content 2"))) .build(); TaskArtifactUpdateEvent event2 = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(artifact2) .build(); taskManager.saveTaskEvent(event2); Task updatedTask = taskManager.getTask(); - assertEquals(1, updatedTask.getArtifacts().size()); - Artifact finalArtifact = updatedTask.getArtifacts().get(0); + assertEquals(1, updatedTask.artifacts().size()); + Artifact finalArtifact = updatedTask.artifacts().get(0); assertEquals("artifact-id", finalArtifact.artifactId()); assertEquals("artifact-2", finalArtifact.name()); - assertEquals("content 2", ((TextPart) finalArtifact.parts().get(0)).getText()); + assertEquals("content 2", ((TextPart) finalArtifact.parts().get(0)).text()); } @Test @@ -497,8 +497,8 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce .parts(Collections.singletonList(new TextPart("content 1"))) .build(); TaskArtifactUpdateEvent event1 = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(artifact1) .build(); taskManager.saveTaskEvent(event1); @@ -510,24 +510,24 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce .parts(Collections.singletonList(new TextPart("content 2"))) .build(); TaskArtifactUpdateEvent event2 = TaskArtifactUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .artifact(artifact2) .build(); taskManager.saveTaskEvent(event2); Task updatedTask = taskManager.getTask(); - assertEquals(2, updatedTask.getArtifacts().size()); + assertEquals(2, updatedTask.artifacts().size()); // Verify both artifacts are present - List artifacts = updatedTask.getArtifacts(); + List artifacts = updatedTask.artifacts(); assertTrue(artifacts.stream() .anyMatch(a -> "artifact-id-1".equals(a.artifactId()) - && "content 1".equals(((TextPart) a.parts().get(0)).getText())) + && "content 1".equals(((TextPart) a.parts().get(0)).text())) , "Artifact 1 should be present"); assertTrue(artifacts.stream() .anyMatch(a -> "artifact-id-2".equals(a.artifactId()) - && "content 2".equals(((TextPart) a.parts().get(0)).getText())) + && "content 2".equals(((TextPart) a.parts().get(0)).text())) , "Artifact 2 should be present"); } @@ -552,8 +552,8 @@ public void testSaveTaskEventMetadataUpdate() throws A2AServerException { newMetadata.put("meta_key_test", "meta_value_test"); TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .status(new TaskStatus(TaskState.WORKING)) .isFinal(false) .metadata(newMetadata) @@ -562,7 +562,7 @@ public void testSaveTaskEventMetadataUpdate() throws A2AServerException { taskManager.saveTaskEvent(event); Task updatedTask = taskManager.getTask(); - assertEquals(newMetadata, updatedTask.getMetadata()); + assertEquals(newMetadata, updatedTask.metadata()); } @Test @@ -572,8 +572,8 @@ public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { taskStore.save(initialTask); TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .status(new TaskStatus(TaskState.WORKING)) .isFinal(false) .metadata(null) @@ -583,7 +583,7 @@ public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { Task updatedTask = taskManager.getTask(); // Should preserve original task's metadata (which is likely null for minimal task) - assertNull(updatedTask.getMetadata()); + assertNull(updatedTask.metadata()); } @Test @@ -601,8 +601,8 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { newMetadata.put("new_key", "new_value"); TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() - .taskId(minimalTask.getId()) - .contextId(minimalTask.getContextId()) + .taskId(minimalTask.id()) + .contextId(minimalTask.contextId()) .status(new TaskStatus(TaskState.WORKING)) .isFinal(false) .metadata(newMetadata) @@ -614,7 +614,7 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { Map mergedMetadata = new HashMap<>(originalMetadata); mergedMetadata.putAll(newMetadata); - assertEquals(mergedMetadata, updatedTask.getMetadata()); + assertEquals(mergedMetadata, updatedTask.metadata()); } @Test @@ -639,16 +639,16 @@ public void testCreateTaskWithInitialMessage() throws A2AServerException { // Verify task was created properly assertNotNull(savedTask); - assertEquals("new-task-id", savedTask.getId()); - assertEquals("some-context", savedTask.getContextId()); - assertEquals(TaskState.SUBMITTED, savedTask.getStatus().state()); + assertEquals("new-task-id", savedTask.id()); + assertEquals("some-context", savedTask.contextId()); + assertEquals(TaskState.SUBMITTED, savedTask.status().state()); // Verify initial message is in history - assertNotNull(savedTask.getHistory()); - assertEquals(1, savedTask.getHistory().size()); - Message historyMessage = savedTask.getHistory().get(0); - assertEquals("initial-msg-id", historyMessage.getMessageId()); - assertEquals("initial message", ((TextPart) historyMessage.getParts().get(0)).getText()); + assertNotNull(savedTask.history()); + assertEquals(1, savedTask.history().size()); + Message historyMessage = savedTask.history().get(0); + assertEquals("initial-msg-id", historyMessage.messageId()); + assertEquals("initial message", ((TextPart) historyMessage.parts().get(0)).text()); } @Test @@ -667,12 +667,12 @@ public void testCreateTaskWithoutInitialMessage() throws A2AServerException { // Verify task was created properly assertNotNull(savedTask); - assertEquals("new-task-id", savedTask.getId()); - assertEquals("some-context", savedTask.getContextId()); - assertEquals(TaskState.SUBMITTED, savedTask.getStatus().state()); + assertEquals("new-task-id", savedTask.id()); + assertEquals("some-context", savedTask.contextId()); + assertEquals(TaskState.SUBMITTED, savedTask.status().state()); // Verify no history since there was no initial message - assertTrue(savedTask.getHistory().isEmpty()); + assertTrue(savedTask.history().isEmpty()); } @Test @@ -728,13 +728,13 @@ public void testUpdateWithMessage() throws A2AServerException { Task updated = taskManagerWithInitialMessage.updateWithMessage(updateMessage, saved); // There should now be a history containing the initialMessage, task message and update message - assertNotNull(updated.getHistory()); - assertEquals(3, updated.getHistory().size()); - assertEquals("initial message", ((TextPart) updated.getHistory().get(0).getParts().get(0)).getText()); + assertNotNull(updated.history()); + assertEquals(3, updated.history().size()); + assertEquals("initial message", ((TextPart) updated.history().get(0).parts().get(0)).text()); // The message in the current state should be null - assertNull(updated.getStatus().message()); - assertEquals("task message", ((TextPart) updated.getHistory().get(1).getParts().get(0)).getText()); - assertEquals("update message", ((TextPart) updated.getHistory().get(2).getParts().get(0)).getText()); + assertNull(updated.status().message()); + assertEquals("task message", ((TextPart) updated.history().get(1).parts().get(0)).text()); + assertEquals("update message", ((TextPart) updated.history().get(2).parts().get(0)).text()); } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 842ce72d4..40f763569 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -61,11 +61,11 @@ public void testAddArtifactWithCustomIdAndName() throws Exception { assertInstanceOf(TaskArtifactUpdateEvent.class, event); TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; - assertEquals(TEST_TASK_ID, taue.getTaskId()); - assertEquals(TEST_TASK_CONTEXT_ID, taue.getContextId()); - assertEquals("custom-artifact-id", taue.getArtifact().artifactId()); - assertEquals("Custom Artifact", taue.getArtifact().name()); - assertSame(SAMPLE_PARTS, taue.getArtifact().parts()); + assertEquals(TEST_TASK_ID, taue.taskId()); + assertEquals(TEST_TASK_CONTEXT_ID, taue.contextId()); + assertEquals("custom-artifact-id", taue.artifact().artifactId()); + assertEquals("Custom Artifact", taue.artifact().name()); + assertSame(SAMPLE_PARTS, taue.artifact().parts()); assertNull(eventQueue.dequeueEventItem(0)); @@ -215,12 +215,12 @@ public void testNonTerminalStateUpdatesAllowed() throws Exception { public void testNewAgentMessage() throws Exception { Message message = taskUpdater.newAgentMessage(SAMPLE_PARTS, null); - assertEquals(AGENT, message.getRole()); - assertEquals(TEST_TASK_ID, message.getTaskId()); - assertEquals(TEST_TASK_CONTEXT_ID, message.getContextId()); - assertNotNull(message.getMessageId()); - assertEquals(SAMPLE_PARTS, message.getParts()); - assertNull(message.getMetadata()); + assertEquals(AGENT, message.role()); + assertEquals(TEST_TASK_ID, message.taskId()); + assertEquals(TEST_TASK_CONTEXT_ID, message.contextId()); + assertNotNull(message.messageId()); + assertEquals(SAMPLE_PARTS, message.parts()); + assertNull(message.metadata()); } @Test @@ -228,12 +228,12 @@ public void testNewAgentMessageWithMetadata() throws Exception { Map metadata = Map.of("key", "value"); Message message = taskUpdater.newAgentMessage(SAMPLE_PARTS, metadata); - assertEquals(AGENT, message.getRole()); - assertEquals(TEST_TASK_ID, message.getTaskId()); - assertEquals(TEST_TASK_CONTEXT_ID, message.getContextId()); - assertNotNull(message.getMessageId()); - assertEquals(SAMPLE_PARTS, message.getParts()); - assertEquals(metadata, message.getMetadata()); + assertEquals(AGENT, message.role()); + assertEquals(TEST_TASK_ID, message.taskId()); + assertEquals(TEST_TASK_CONTEXT_ID, message.contextId()); + assertNotNull(message.messageId()); + assertEquals(SAMPLE_PARTS, message.parts()); + assertEquals(metadata, message.metadata()); } @Test @@ -244,13 +244,13 @@ public void testAddArtifactWithAppendTrue() throws Exception { assertInstanceOf(TaskArtifactUpdateEvent.class, event); TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; - assertEquals(TEST_TASK_ID, taue.getTaskId()); - assertEquals(TEST_TASK_CONTEXT_ID, taue.getContextId()); - assertEquals("artifact-id", taue.getArtifact().artifactId()); - assertEquals("Test Artifact", taue.getArtifact().name()); - assertSame(SAMPLE_PARTS, taue.getArtifact().parts()); - assertEquals(true, taue.isAppend()); - assertNull(taue.isLastChunk()); + assertEquals(TEST_TASK_ID, taue.taskId()); + assertEquals(TEST_TASK_CONTEXT_ID, taue.contextId()); + assertEquals("artifact-id", taue.artifact().artifactId()); + assertEquals("Test Artifact", taue.artifact().name()); + assertSame(SAMPLE_PARTS, taue.artifact().parts()); + assertEquals(true, taue.append()); + assertNull(taue.lastChunk()); assertNull(eventQueue.dequeueEventItem(0)); } @@ -263,9 +263,9 @@ public void testAddArtifactWithLastChunkTrue() throws Exception { assertInstanceOf(TaskArtifactUpdateEvent.class, event); TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; - assertEquals("artifact-id", taue.getArtifact().artifactId()); - assertNull(taue.isAppend()); - assertEquals(true, taue.isLastChunk()); + assertEquals("artifact-id", taue.artifact().artifactId()); + assertNull(taue.append()); + assertEquals(true, taue.lastChunk()); assertNull(eventQueue.dequeueEventItem(0)); } @@ -278,8 +278,8 @@ public void testAddArtifactWithAppendAndLastChunk() throws Exception { assertInstanceOf(TaskArtifactUpdateEvent.class, event); TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; - assertEquals(true, taue.isAppend()); - assertEquals(false, taue.isLastChunk()); + assertEquals(true, taue.append()); + assertEquals(false, taue.lastChunk()); assertNull(eventQueue.dequeueEventItem(0)); } @@ -292,9 +292,9 @@ public void testAddArtifactGeneratesIdWhenNull() throws Exception { assertInstanceOf(TaskArtifactUpdateEvent.class, event); TaskArtifactUpdateEvent taue = (TaskArtifactUpdateEvent) event; - assertNotNull(taue.getArtifact().artifactId()); + assertNotNull(taue.artifact().artifactId()); // Check that it's a valid UUID format - String artifactId = taue.getArtifact().artifactId(); + String artifactId = taue.artifact().artifactId(); assertEquals(36, artifactId.length()); // Standard UUID length assertTrue(artifactId.matches("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")); @@ -389,7 +389,7 @@ public void testConcurrentCompletionAttempts() throws Exception { TaskStatusUpdateEvent tsue = (TaskStatusUpdateEvent) event; assertTrue(tsue.isFinal()); - assertTrue(tsue.getStatus().state() == TaskState.COMPLETED || tsue.getStatus().state() == TaskState.FAILED); + assertTrue(tsue.status().state() == TaskState.COMPLETED || tsue.status().state() == TaskState.FAILED); // No additional events should be queued assertNull(eventQueue.dequeueEventItem(0)); @@ -402,11 +402,11 @@ private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, assertInstanceOf(TaskStatusUpdateEvent.class, event); TaskStatusUpdateEvent tsue = (TaskStatusUpdateEvent) event; - assertEquals(TEST_TASK_ID, tsue.getTaskId()); - assertEquals(TEST_TASK_CONTEXT_ID, tsue.getContextId()); + assertEquals(TEST_TASK_ID, tsue.taskId()); + assertEquals(TEST_TASK_CONTEXT_ID, tsue.contextId()); assertEquals(isFinal, tsue.isFinal()); - assertEquals(state, tsue.getStatus().state()); - assertEquals(statusMessage, tsue.getStatus().message()); + assertEquals(state, tsue.status().state()); + assertEquals(statusMessage, tsue.status().message()); assertNull(eventQueue.dequeueEventItem(0)); diff --git a/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java b/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java index b41197bfc..8b4bd628c 100644 --- a/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java +++ b/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java @@ -77,7 +77,7 @@ void testNewTextArtifactPartContainsProvidedText() { // Then TextPart textPart = (TextPart) artifact.parts().get(0); - assertEquals(text, textPart.getText()); + assertEquals(text, textPart.text()); } @Test @@ -120,7 +120,7 @@ void testNewDataArtifactPartContainsProvidedData() { // Then DataPart dataPart = (DataPart) artifact.parts().get(0); - assertEquals(sampleData, dataPart.getData()); + assertEquals(sampleData, dataPart.data()); } @Test diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java index 8dd44a18a..2b1a068f8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/APIKeySecuritySchemeMapper.java @@ -14,7 +14,7 @@ public interface APIKeySecuritySchemeMapper { APIKeySecuritySchemeMapper INSTANCE = A2AMappers.getMapper(APIKeySecuritySchemeMapper.class); // location enum is converted to string via ProtoMapperConfig.map(Location) - @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.description() != null)") io.a2a.grpc.APIKeySecurityScheme toProto(io.a2a.spec.APIKeySecurityScheme domain); default io.a2a.spec.APIKeySecurityScheme fromProto(io.a2a.grpc.APIKeySecurityScheme proto) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java index 9ce364c0a..9ee0f94d3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java @@ -24,30 +24,13 @@ public interface DataPartMapper { * Uses CommonFieldMapper for Map → Struct conversion. * Metadata is ignored (not part of proto definition). */ - @Mapping(target = "data", source = "data", conditionExpression = "java(domain.getData() != null)", qualifiedByName = "mapToStruct") + @Mapping(target = "data", source = "data", conditionExpression = "java(domain.data() != null)", qualifiedByName = "mapToStruct") io.a2a.grpc.DataPart toProto(io.a2a.spec.DataPart domain); /** * Converts proto DataPart to domain DataPart. - * Uses CommonFieldMapper for Struct → Map conversion. - * Uses factory method to construct DataPart with single-arg constructor. - * Metadata is ignored (not part of proto definition). + * Uses CommonFieldMapper for Struct → Map conversion via Builder. */ @Mapping(target = "data", source = "data", qualifiedByName = "structToMap") io.a2a.spec.DataPart fromProto(io.a2a.grpc.DataPart proto); - - /** - * Object factory for creating DataPart instances. - *

    - * Resolves constructor ambiguity by explicitly using the single-arg constructor. - * The metadata field will be null (not part of proto definition). - * - * @param proto the proto DataPart - * @return new DataPart instance using single-arg constructor - */ - @ObjectFactory - default io.a2a.spec.DataPart createDataPart(io.a2a.grpc.DataPart proto) { - java.util.Map data = A2ACommonFieldMapper.INSTANCE.structToMap(proto.getData()); - return new io.a2a.spec.DataPart(data); - } } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java index 347a15684..2174f2dc8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java @@ -32,7 +32,7 @@ default io.a2a.grpc.FilePart toProto(io.a2a.spec.FilePart domain) { } io.a2a.grpc.FilePart.Builder builder = io.a2a.grpc.FilePart.newBuilder(); - FileContent fileContent = domain.getFile(); + FileContent fileContent = domain.file(); if (fileContent instanceof FileWithBytes fileWithBytes) { builder.setFileWithBytes(ByteString.copyFrom(Base64.getDecoder().decode(fileWithBytes.bytes()))); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java index 629fcdd37..4953635a1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/HTTPAuthSecuritySchemeMapper.java @@ -13,8 +13,8 @@ public interface HTTPAuthSecuritySchemeMapper { HTTPAuthSecuritySchemeMapper INSTANCE = A2AMappers.getMapper(HTTPAuthSecuritySchemeMapper.class); - @Mapping(target = "bearerFormat", source = "bearerFormat", conditionExpression = "java(domain.getBearerFormat() != null)") - @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + @Mapping(target = "bearerFormat", source = "bearerFormat", conditionExpression = "java(domain.bearerFormat() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.description() != null)") io.a2a.grpc.HTTPAuthSecurityScheme toProto(io.a2a.spec.HTTPAuthSecurityScheme domain); default io.a2a.spec.HTTPAuthSecurityScheme fromProto(io.a2a.grpc.HTTPAuthSecurityScheme proto) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java index 53dd56222..6888379f1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageMapper.java @@ -24,9 +24,9 @@ public interface MessageMapper { * Converts domain Message to proto Message. * Uses CommonFieldMapper for metadata conversion and ADDER_PREFERRED for lists. */ - @Mapping(target = "messageId", source = "messageId", conditionExpression = "java(domain.getMessageId() != null)") - @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(domain.getContextId() != null)") - @Mapping(target = "taskId", source = "taskId", conditionExpression = "java(domain.getTaskId() != null)") + @Mapping(target = "messageId", source = "messageId", conditionExpression = "java(domain.messageId() != null)") + @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(domain.contextId() != null)") + @Mapping(target = "taskId", source = "taskId", conditionExpression = "java(domain.taskId() != null)") @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") io.a2a.grpc.Message toProto(Message domain); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java index 2464d0c38..c6155d580 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MutualTLSSecuritySchemeMapper.java @@ -13,7 +13,7 @@ public interface MutualTLSSecuritySchemeMapper { MutualTLSSecuritySchemeMapper INSTANCE = A2AMappers.getMapper(MutualTLSSecuritySchemeMapper.class); - @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.description() != null)") io.a2a.grpc.MutualTlsSecurityScheme toProto(io.a2a.spec.MutualTLSSecurityScheme domain); default io.a2a.spec.MutualTLSSecurityScheme fromProto(io.a2a.grpc.MutualTlsSecurityScheme proto) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java index b762f720b..088cb509d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuth2SecuritySchemeMapper.java @@ -14,8 +14,8 @@ public interface OAuth2SecuritySchemeMapper { OAuth2SecuritySchemeMapper INSTANCE = A2AMappers.getMapper(OAuth2SecuritySchemeMapper.class); - @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") - @Mapping(target = "oauth2MetadataUrl", source = "oauth2MetadataUrl", conditionExpression = "java(domain.getOauth2MetadataUrl() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.description() != null)") + @Mapping(target = "oauth2MetadataUrl", source = "oauth2MetadataUrl", conditionExpression = "java(domain.oauth2MetadataUrl() != null)") io.a2a.grpc.OAuth2SecurityScheme toProto(io.a2a.spec.OAuth2SecurityScheme domain); default io.a2a.spec.OAuth2SecurityScheme fromProto(io.a2a.grpc.OAuth2SecurityScheme proto) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java index 14d848fd9..da1dd2d6b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OpenIdConnectSecuritySchemeMapper.java @@ -13,7 +13,7 @@ public interface OpenIdConnectSecuritySchemeMapper { OpenIdConnectSecuritySchemeMapper INSTANCE = A2AMappers.getMapper(OpenIdConnectSecuritySchemeMapper.class); - @Mapping(target = "description", source = "description", conditionExpression = "java(domain.getDescription() != null)") + @Mapping(target = "description", source = "description", conditionExpression = "java(domain.description() != null)") io.a2a.grpc.OpenIdConnectSecurityScheme toProto(io.a2a.spec.OpenIdConnectSecurityScheme domain); default io.a2a.spec.OpenIdConnectSecurityScheme fromProto(io.a2a.grpc.OpenIdConnectSecurityScheme proto) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java index a82d8527c..98c777256 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PartMapper.java @@ -37,7 +37,7 @@ default io.a2a.grpc.Part toProto(Part domain) { io.a2a.grpc.Part.Builder builder = io.a2a.grpc.Part.newBuilder(); if (domain instanceof TextPart textPart) { - builder.setText(textPart.getText()); + builder.setText(textPart.text()); } else if (domain instanceof FilePart filePart) { builder.setFile(FilePartMapper.INSTANCE.toProto(filePart)); } else if (domain instanceof DataPart dataPart) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java index 6c907d22b..2c52d80d2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskArtifactUpdateEventMapper.java @@ -20,8 +20,8 @@ public interface TaskArtifactUpdateEventMapper { * Converts domain TaskArtifactUpdateEvent to proto. * Uses declarative mapping with CommonFieldMapper for metadata conversion. */ - @Mapping(target = "append", source = "append", conditionExpression = "java(domain.isAppend() != null)") - @Mapping(target = "lastChunk", source = "lastChunk", conditionExpression = "java(domain.isLastChunk() != null)") + @Mapping(target = "append", source = "append", conditionExpression = "java(domain.append() != null)") + @Mapping(target = "lastChunk", source = "lastChunk", conditionExpression = "java(domain.lastChunk() != null)") @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") io.a2a.grpc.TaskArtifactUpdateEvent toProto(TaskArtifactUpdateEvent domain); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java index a76cb259b..20ece6064 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskMapper.java @@ -24,8 +24,8 @@ public interface TaskMapper { * Converts domain Task to proto Task. * Uses CommonFieldMapper for metadata conversion and ADDER_PREFERRED for lists. */ - @Mapping(target = "id", source = "id", conditionExpression = "java(domain.getId() != null)") - @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(domain.getContextId() != null)") + @Mapping(target = "id", source = "id", conditionExpression = "java(domain.id() != null)") + @Mapping(target = "contextId", source = "contextId", conditionExpression = "java(domain.contextId() != null)") @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") io.a2a.grpc.Task toProto(Task domain); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java index bcda1795c..8187df48a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/TaskStatusUpdateEventMapper.java @@ -20,10 +20,10 @@ public interface TaskStatusUpdateEventMapper { /** * Converts domain TaskStatusUpdateEvent to proto. * Uses declarative mapping with CommonFieldMapper for metadata conversion. - * Note: MapStruct derives property "final" from domain's isFinal() getter (strips "is" prefix). + * Maps record's isFinal field to proto's final field. */ @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") - @Mapping(target = "final", source = "final") + @Mapping(target = "final", source = "isFinal") io.a2a.grpc.TaskStatusUpdateEvent toProto(TaskStatusUpdateEvent domain); /** diff --git a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java index 34c856208..ba1496547 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java @@ -59,9 +59,9 @@ void testConvertTask_FromProto() { assertNotNull(result); assertInstanceOf(Task.class, result); Task task = (Task) result; - assertEquals("task-123", task.getId()); - assertEquals("context-456", task.getContextId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); + assertEquals("task-123", task.id()); + assertEquals("context-456", task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); } @Test @@ -106,9 +106,9 @@ void testConvertMessage_FromProto() { assertNotNull(result); assertInstanceOf(Message.class, result); Message message = (Message) result; - assertEquals("msg-123", message.getMessageId()); - assertEquals("context-456", message.getContextId()); - assertEquals(Message.Role.USER, message.getRole()); + assertEquals("msg-123", message.messageId()); + assertEquals("context-456", message.contextId()); + assertEquals(Message.Role.USER, message.role()); } @Test @@ -154,9 +154,9 @@ void testConvertTaskStatusUpdateEvent_FromProto() { assertNotNull(result); assertInstanceOf(TaskStatusUpdateEvent.class, result); TaskStatusUpdateEvent event = (TaskStatusUpdateEvent) result; - assertEquals("task-123", event.getTaskId()); - assertEquals("context-456", event.getContextId()); - assertEquals(TaskState.WORKING, event.getStatus().state()); + assertEquals("task-123", event.taskId()); + assertEquals("context-456", event.contextId()); + assertEquals(TaskState.WORKING, event.status().state()); assertEquals(false, event.isFinal()); } @@ -209,10 +209,10 @@ void testConvertTaskArtifactUpdateEvent_FromProto() { assertNotNull(result); assertInstanceOf(TaskArtifactUpdateEvent.class, result); TaskArtifactUpdateEvent event = (TaskArtifactUpdateEvent) result; - assertEquals("task-123", event.getTaskId()); - assertEquals("context-456", event.getContextId()); - assertEquals("artifact-1", event.getArtifact().artifactId()); - assertEquals("result", event.getArtifact().name()); + assertEquals("task-123", event.taskId()); + assertEquals("context-456", event.contextId()); + assertEquals("artifact-1", event.artifact().artifactId()); + assertEquals("result", event.artifact().name()); } @Test @@ -244,9 +244,9 @@ void testConvertStreamResponse_Roundtrip_Task() { assertNotNull(result); assertInstanceOf(Task.class, result); Task roundtrippedTask = (Task) result; - assertEquals(originalTask.getId(), roundtrippedTask.getId()); - assertEquals(originalTask.getContextId(), roundtrippedTask.getContextId()); - assertEquals(originalTask.getStatus().state(), roundtrippedTask.getStatus().state()); + assertEquals(originalTask.id(), roundtrippedTask.id()); + assertEquals(originalTask.contextId(), roundtrippedTask.contextId()); + assertEquals(originalTask.status().state(), roundtrippedTask.status().state()); } @Test @@ -267,8 +267,8 @@ void testConvertStreamResponse_Roundtrip_Message() { assertNotNull(result); assertInstanceOf(Message.class, result); Message roundtrippedMessage = (Message) result; - assertEquals(originalMessage.getMessageId(), roundtrippedMessage.getMessageId()); - assertEquals(originalMessage.getContextId(), roundtrippedMessage.getContextId()); - assertEquals(originalMessage.getRole(), roundtrippedMessage.getRole()); + assertEquals(originalMessage.messageId(), roundtrippedMessage.messageId()); + assertEquals(originalMessage.contextId(), roundtrippedMessage.contextId()); + assertEquals(originalMessage.role(), roundtrippedMessage.role()); } } diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index be5d358b2..d863efc17 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -289,7 +289,7 @@ public void convertTaskTimestampStatus() { io.a2a.grpc.Task grpcTask = ProtoUtils.ToProto.task(task); task = ProtoUtils.FromProto.task(grpcTask); - TaskStatus status = task.getStatus(); + TaskStatus status = task.status(); assertEquals(TaskState.COMPLETED, status.state()); assertNotNull(status.timestamp()); assertEquals(expectedTimestamp, status.timestamp()); diff --git a/spec/src/main/java/io/a2a/json/JsonMappingException.java b/spec/src/main/java/io/a2a/json/JsonMappingException.java index 550b7827c..8d3ad48e7 100644 --- a/spec/src/main/java/io/a2a/json/JsonMappingException.java +++ b/spec/src/main/java/io/a2a/json/JsonMappingException.java @@ -17,7 +17,7 @@ *

    {@code
      * try {
      *     Task task = JsonUtil.fromJson(json, Task.class);
    - *     if (task.getId() == null) {
    + *     if (task.id() == null) {
      *         throw new JsonMappingException(null, "Task ID cannot be null");
      *     }
      * } catch (JsonProcessingException e) {
    diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/spec/src/main/java/io/a2a/json/JsonUtil.java
    index 2ea564483..4fdc63878 100644
    --- a/spec/src/main/java/io/a2a/json/JsonUtil.java
    +++ b/spec/src/main/java/io/a2a/json/JsonUtil.java
    @@ -1,11 +1,5 @@
     package io.a2a.json;
     
    -import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
    -import static com.google.gson.stream.JsonToken.BEGIN_OBJECT;
    -import static com.google.gson.stream.JsonToken.BOOLEAN;
    -import static com.google.gson.stream.JsonToken.NULL;
    -import static com.google.gson.stream.JsonToken.NUMBER;
    -import static com.google.gson.stream.JsonToken.STRING;
     import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE;
     import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE;
     import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE;
    @@ -591,16 +585,28 @@ public void write(JsonWriter out, Part value) throws java.io.IOException {
                     out.nullValue();
                     return;
                 }
    -            // Delegate to Gson's default serialization for the concrete type
    +
    +            // Serialize the concrete type to a JsonElement
    +            com.google.gson.JsonElement jsonElement;
                 if (value instanceof TextPart textPart) {
    -                delegateGson.toJson(textPart, TextPart.class, out);
    +                jsonElement = delegateGson.toJsonTree(textPart, TextPart.class);
                 } else if (value instanceof FilePart filePart) {
    -                delegateGson.toJson(filePart, FilePart.class, out);
    +                jsonElement = delegateGson.toJsonTree(filePart, FilePart.class);
                 } else if (value instanceof DataPart dataPart) {
    -                delegateGson.toJson(dataPart, DataPart.class, out);
    +                jsonElement = delegateGson.toJsonTree(dataPart, DataPart.class);
                 } else {
                     throw new JsonSyntaxException("Unknown Part subclass: " + value.getClass().getName());
                 }
    +
    +
    +            // TODO temorary workaround to be fixed in https://github.com/a2aproject/a2a-java/issues/544
    +            // Add the "kind" field from getKind() method
    +            if (jsonElement.isJsonObject()) {
    +                jsonElement.getAsJsonObject().addProperty("kind", value.getKind().asString());
    +            }
    +
    +            // Write the modified JSON
    +            delegateGson.toJson(jsonElement, out);
             }
     
             @Override
    @@ -666,18 +672,29 @@ public void write(JsonWriter out, StreamingEventKind value) throws java.io.IOExc
                     out.nullValue();
                     return;
                 }
    -            // Delegate to Gson's default serialization for the concrete type
    +
    +            // Serialize the concrete type to a JsonElement
    +            com.google.gson.JsonElement jsonElement;
                 if (value instanceof Task task) {
    -                delegateGson.toJson(task, Task.class, out);
    +                jsonElement = delegateGson.toJsonTree(task, Task.class);
                 } else if (value instanceof Message message) {
    -                delegateGson.toJson(message, Message.class, out);
    +                jsonElement = delegateGson.toJsonTree(message, Message.class);
                 } else if (value instanceof TaskStatusUpdateEvent event) {
    -                delegateGson.toJson(event, TaskStatusUpdateEvent.class, out);
    +                jsonElement = delegateGson.toJsonTree(event, TaskStatusUpdateEvent.class);
                 } else if (value instanceof TaskArtifactUpdateEvent event) {
    -                delegateGson.toJson(event, TaskArtifactUpdateEvent.class, out);
    +                jsonElement = delegateGson.toJsonTree(event, TaskArtifactUpdateEvent.class);
                 } else {
                     throw new JsonSyntaxException("Unknown StreamingEventKind implementation: " + value.getClass().getName());
                 }
    +
    +            // TODO temorary workaround to be fixed in https://github.com/a2aproject/a2a-java/issues/544
    +            // Add the "kind" field from getKind() method
    +            if (jsonElement.isJsonObject()) {
    +                jsonElement.getAsJsonObject().addProperty("kind", value.kind());
    +            }
    +
    +            // Write the modified JSON
    +            delegateGson.toJson(jsonElement, out);
             }
     
             @Override
    diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java
    index 43141ce2e..01fc23509 100644
    --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java
    +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java
    @@ -12,18 +12,31 @@
      * 

    * Corresponds to the OpenAPI "apiKey" security scheme type. * + * @param location the location where the API key is sent (required) + * @param name the name of the API key parameter (required) + * @param description a human-readable description (optional) * @see SecurityScheme for the base interface * @see OpenAPI Security Scheme * @see A2A Protocol Specification */ -public final class APIKeySecurityScheme implements SecurityScheme { +public record APIKeySecurityScheme( + Location location, + String name, + String description +) implements SecurityScheme { /** The security scheme type identifier for API key authentication. */ public static final String API_KEY = "apiKey"; - private final Location location; - private final String name; - private final String type; - private final String description; + + /** + * Compact constructor with validation. + * + * @throws IllegalArgumentException if location or name is null + */ + public APIKeySecurityScheme { + Assert.checkNotNullParam("location", location); + Assert.checkNotNullParam("name", name); + } /** * Represents the location of the API key. @@ -76,78 +89,6 @@ public static Location fromString(String location) { } } - /** - * Creates a new APIKeySecurityScheme with the specified location, name, and description. - * - * @param location the location where the API key is sent (required) - * @param name the name of the API key parameter (required) - * @param description a human-readable description (optional) - * @throws IllegalArgumentException if location or name is null - */ - public APIKeySecurityScheme(Location location, String name, String description) { - this(location, name, description, API_KEY); - } - - /** - * Creates a new APIKeySecurityScheme with explicit type specification. - * - * @param location the location where the API key is sent (required) - * @param name the name of the API key parameter (required) - * @param description a human-readable description (optional) - * @param type the security scheme type (must be "apiKey") - * @throws IllegalArgumentException if location, name, or type is null, or if type is not "apiKey" - */ - public APIKeySecurityScheme(Location location, String name, - String description, String type) { - Assert.checkNotNullParam("location", location); - Assert.checkNotNullParam("name", name); - Assert.checkNotNullParam("type", type); - if (! type.equals(API_KEY)) { - throw new IllegalArgumentException("Invalid type for APIKeySecurityScheme"); - } - this.location = location; - this.name = name; - this.description = description; - this.type = type; - } - - /** - * Gets the human-readable description of this security scheme. - * - * @return the description, or null if not provided - */ - @Override - public String getDescription() { - return description; - } - - /** - * Gets the location where the API key is sent. - * - * @return the API key location - */ - public Location getLocation() { - return location; - } - - /** - * Gets the name of the API key parameter. - * - * @return the parameter name - */ - public String getName() { - return name; - } - - /** - * Gets the security scheme type. - * - * @return always returns "apiKey" - */ - public String getType() { - return type; - } - /** * Create a new Builder * diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 91b16bc4f..8cbc83af0 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -4,6 +4,8 @@ import io.a2a.util.Assert; +import static io.a2a.util.Utils.SPEC_VERSION_1_0; + /** * Represents a structured data content part within a {@link Message} or {@link Artifact}. @@ -31,40 +33,71 @@ * ); * }

    * + * @param data the structured data map (required, defensively copied for immutability) * @see Part * @see Message * @see Artifact */ -public class DataPart extends Part> { +public record DataPart(Map data) implements Part> { /** The type identifier for data parts in messages and artifacts. */ public static final String DATA = "data"; - private final Map data; - private final Kind kind; /** - * Constructs a DataPart with structured data. + * Compact constructor with validation and defensive copying. * - * @param data the structured data map (required) * @throws IllegalArgumentException if data is null */ - public DataPart(Map data) { + public DataPart { Assert.checkNotNullParam("data", data); - this.data = data; - this.kind = Kind.DATA; + data = Map.copyOf(data); } @Override public Kind getKind() { - return kind; + return Kind.DATA; } /** - * Gets the structured data contained in this part. + * Create a new Builder * - * @return a map of key-value pairs representing the data + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for constructing immutable {@link DataPart} instances. */ - public Map getData() { - return data; + public static class Builder { + private Map data; + + /** + * Creates a new Builder with all fields unset. + */ + private Builder() { + } + + /** + * Sets the structured data map. + * + * @param data the structured data (required) + * @return this builder for method chaining + */ + public Builder data(Map data) { + this.data = data; + return this; + } + + /** + * Builds a new immutable {@link DataPart} from the current builder state. + * + * @return a new DataPart instance + * @throws IllegalArgumentException if data is null + */ + public DataPart build() { + return new DataPart(data); + } } } diff --git a/spec/src/main/java/io/a2a/spec/EventKind.java b/spec/src/main/java/io/a2a/spec/EventKind.java index 7c53cf653..982bc82f7 100644 --- a/spec/src/main/java/io/a2a/spec/EventKind.java +++ b/spec/src/main/java/io/a2a/spec/EventKind.java @@ -26,5 +26,5 @@ public interface EventKind { * * @return the event kind string (e.g., "task", "message") */ - String getKind(); + String kind(); } diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index 5d06e8f26..f7c3e7426 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -3,6 +3,8 @@ import io.a2a.util.Assert; +import static io.a2a.util.Utils.SPEC_VERSION_1_0; + /** * Represents a file content part within a {@link Message} or {@link Artifact}. @@ -30,43 +32,29 @@ * ); * }
    * + * @param file the file content (required, either FileWithBytes or FileWithUri) * @see Part * @see FileContent * @see FileWithBytes * @see FileWithUri */ -public class FilePart extends Part { +public record FilePart(FileContent file) implements Part { /** The type identifier for file parts in messages and artifacts. */ public static final String FILE = "file"; - private final FileContent file; - private final Kind kind; - /** - * Constructs a FilePart with file content. + * Compact constructor with validation. * - * @param file the file content (required, either FileWithBytes or FileWithUri) * @throws IllegalArgumentException if file is null */ - public FilePart(FileContent file) { + public FilePart { Assert.checkNotNullParam("file", file); - this.file = file; - this.kind = Kind.FILE; } @Override public Kind getKind() { - return kind; - } - - /** - * Gets the file content contained in this part. - * - * @return the file content (FileWithBytes or FileWithUri) - */ - public FileContent getFile() { - return file; + return Kind.FILE; } -} \ No newline at end of file +} diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index e2f6433cb..76620d868 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -3,6 +3,7 @@ import io.a2a.util.Assert; import static io.a2a.spec.HTTPAuthSecurityScheme.HTTP; +import static io.a2a.util.Utils.SPEC_VERSION_1_0; /** * HTTP authentication security scheme for agent authentication. @@ -22,88 +23,37 @@ *

    * Example usage: *

    {@code
    - * HTTPAuthSecurityScheme scheme = new HTTPAuthSecurityScheme.Builder()
    + * HTTPAuthSecurityScheme scheme = HTTPAuthSecurityScheme.builder()
      *     .scheme("bearer")
      *     .bearerFormat("JWT")
      *     .description("JWT bearer token authentication")
      *     .build();
      * }
    * + * @param bearerFormat the bearer token format (optional) + * @param scheme the authentication scheme (required) + * @param description the scheme description (optional) * @see SecurityScheme for the base interface * @see OpenAPI Security Scheme * @see RFC 7235 - HTTP Authentication * @see A2A Protocol Specification */ -public final class HTTPAuthSecurityScheme implements SecurityScheme { +public record HTTPAuthSecurityScheme( + String bearerFormat, + String scheme, + String description +) implements SecurityScheme { /** The HTTP security scheme type identifier. */ public static final String HTTP = "http"; - private final String bearerFormat; - private final String scheme; - private final String description; - private final String type; /** - * Constructs HTTP auth security scheme without type. + * Compact constructor with validation. * - * @param bearerFormat the bearer token format - * @param scheme the authentication scheme - * @param description the scheme description + * @throws IllegalArgumentException if scheme is null */ - public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String description) { - this(bearerFormat, scheme, description, HTTP); - } - - /** - * Constructs HTTP auth security scheme with full parameters. - * - * @param bearerFormat the bearer token format - * @param scheme the authentication scheme - * @param description the scheme description - * @param type the security scheme type - */ - public HTTPAuthSecurityScheme(String bearerFormat, String scheme, String description, String type) { + public HTTPAuthSecurityScheme { Assert.checkNotNullParam("scheme", scheme); - Assert.checkNotNullParam("type", type); - if (! HTTP.equals(type)) { - throw new IllegalArgumentException("Invalid type for HTTPAuthSecurityScheme"); - } - this.bearerFormat = bearerFormat; - this.scheme = scheme; - this.description = description; - this.type = type; - } - - @Override - public String getDescription() { - return description; - } - - /** - * Gets the bearer token format. - * - * @return the bearer format - */ - public String getBearerFormat() { - return bearerFormat; - } - - /** - * Gets the authentication scheme. - * - * @return the scheme - */ - public String getScheme() { - return scheme; - } - - /** - * Gets the security scheme type. - * - * @return the type - */ - public String getType() { - return type; } /** diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index fb8825d48..b218b5396 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -7,6 +7,7 @@ import io.a2a.util.Assert; import static io.a2a.spec.Message.MESSAGE; +import static io.a2a.util.Utils.SPEC_VERSION_1_0; /** * Represents a single message in the conversation between a user and an agent in the A2A Protocol. @@ -25,183 +26,51 @@ * This class is mutable (allows setting taskId and contextId) to support post-construction correlation * with tasks and conversation contexts. Use the {@link Builder} for construction. * + * @param role the role of the message sender (user or agent) + * @param parts the content parts of the message (text, file, or data) + * @param messageId the unique identifier for this message + * @param contextId the conversation context identifier + * @param taskId the task identifier this message is associated with + * @param referenceTaskIds list of reference task identifiers + * @param metadata additional metadata for the message + * @param extensions list of protocol extensions used in this message * @see A2A Protocol Specification */ -public final class Message implements EventKind, StreamingEventKind { +public record Message(Role role, List> parts, + String messageId, String contextId, + String taskId, List referenceTaskIds, + Map metadata, List extensions +) implements EventKind, StreamingEventKind { - /** - * The kind identifier for Message events: "message". - */ public static final String MESSAGE = "message"; - private final Role role; - private final List> parts; - private final String messageId; - private String contextId; - private String taskId; - private final Map metadata; - private final String kind; - private final List referenceTaskIds; - private final List extensions; - - /** - * Constructs a Message with default kind. - * - * @param role the message role (required) - * @param parts the message content parts (required) - * @param messageId the message identifier (required) - * @param contextId the context identifier (optional) - * @param taskId the task identifier (optional) - * @param referenceTaskIds list of related task IDs (optional) - * @param metadata additional metadata (optional) - * @param extensions list of protocol extensions (optional) - */ - public Message(Role role, List> parts, String messageId, String contextId, String taskId, - List referenceTaskIds, Map metadata, List extensions) { - this(role, parts, messageId, contextId, taskId, referenceTaskIds, metadata, extensions, MESSAGE); - } /** - * Constructs a Message with all parameters. + * Compact constructor with validation and defensive copying. * - * @param role the message role (required) - * @param parts the message content parts (required) - * @param messageId the message identifier (required) - * @param contextId the context identifier (optional) - * @param taskId the task identifier (optional) - * @param referenceTaskIds list of related task IDs (optional) - * @param metadata additional metadata (optional) - * @param extensions list of protocol extensions (optional) - * @param kind the event kind (must be "message") + * @throws IllegalArgumentException if role, parts, or messageId is null, or if parts is empty */ - public Message(Role role, List> parts, - String messageId, String contextId, - String taskId, List referenceTaskIds, - Map metadata, List extensions, - String kind) { - Assert.checkNotNullParam("kind", kind); + public Message { + Assert.checkNotNullParam("role", role); Assert.checkNotNullParam("parts", parts); + Assert.checkNotNullParam("messageId", messageId); if (parts.isEmpty()) { throw new IllegalArgumentException("Parts cannot be empty"); } - Assert.checkNotNullParam("role", role); - if (! kind.equals(MESSAGE)) { - throw new IllegalArgumentException("Invalid Message"); - } - Assert.checkNotNullParam("messageId", messageId); - this.role = role; - this.parts = parts; - this.messageId = messageId; - this.contextId = contextId; - this.taskId = taskId; - this.referenceTaskIds = referenceTaskIds; - this.metadata = metadata; - this.extensions = extensions; - this.kind = kind; - } - - /** - * Returns the role of the message sender. - * - * @return the message role (USER or AGENT) - */ - public Role getRole() { - return role; - } - - /** - * Returns the content parts of this message. - * - * @return an immutable list of message parts - */ - public List> getParts() { - return parts; - } - - /** - * Returns the unique identifier for this message. - * - * @return the message ID - */ - public String getMessageId() { - return messageId; - } - - /** - * Returns the conversation context identifier. - * - * @return the context ID, or null if not set - */ - public String getContextId() { - return contextId; - } - - /** - * Returns the task identifier this message is associated with. - * - * @return the task ID, or null if not set - */ - public String getTaskId() { - return taskId; - } - - /** - * Returns the metadata associated with this message. - * - * @return a map of metadata key-value pairs, or null if not set - */ - public Map getMetadata() { - return metadata; - } - - /** - * Sets the task identifier for this message. - *

    - * This method allows associating the message with a task after construction. - * - * @param taskId the task ID to set - */ - public void setTaskId(String taskId) { - this.taskId = taskId; - } - - /** - * Sets the conversation context identifier for this message. - *

    - * This method allows associating the message with a context after construction. - * - * @param contextId the context ID to set - */ - public void setContextId(String contextId) { - this.contextId = contextId; - } - - /** - * Returns the list of reference task identifiers this message relates to. - * - * @return a list of task IDs, or null if not set - */ - public List getReferenceTaskIds() { - return referenceTaskIds; - } - - /** - * Returns the list of protocol extensions used in this message. - * - * @return a list of extension identifiers, or null if not set - */ - public List getExtensions() { - return extensions; + parts = List.copyOf(parts); + referenceTaskIds = referenceTaskIds != null ? List.copyOf(referenceTaskIds) : null; + extensions = extensions != null ? List.copyOf(extensions) : null; + metadata = (metadata != null) ? Map.copyOf(metadata) : null; } @Override - public String getKind() { - return kind; + public String kind() { + return MESSAGE; } /** * Creates a new Builder for constructing Message instances. * - * @return a new Message.Builder instance + * @return a Message.builder instance */ public static Builder builder() { return new Builder(); @@ -211,7 +80,6 @@ public static Builder builder() { * Creates a new Builder initialized with values from an existing Message. * * @param message the Message to copy values from - * @return the builder */ public static Builder builder(Message message) { return new Builder(message); @@ -260,7 +128,7 @@ public String asString() { *

    {@code
          * Message userMessage = Message.builder()
          *     .role(Message.Role.USER)
    -     *     .parts(List.of(new TextPart("Hello, agent!", null)))
    +     *     .parts(List.of(new TextPart("Hello, agent!")))
          *     .contextId("conv-123")
          *     .build();
          * }
    @@ -288,14 +156,14 @@ private Builder() { * @param message the Message to copy values from */ private Builder(Message message) { - role = message.role; - parts = message.parts; - messageId = message.messageId; - contextId = message.contextId; - taskId = message.taskId; - referenceTaskIds = message.referenceTaskIds; - metadata = message.metadata; - extensions = message.extensions; + role = message.role(); + parts = message.parts(); + messageId = message.messageId(); + contextId = message.contextId(); + taskId = message.taskId(); + referenceTaskIds = message.referenceTaskIds(); + metadata = message.metadata(); + extensions = message.extensions(); } /** diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index f7b9dc1f9..659df3c7a 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import io.a2a.util.Assert; - import static io.a2a.spec.MutualTLSSecurityScheme.MUTUAL_TLS; /** @@ -22,68 +20,17 @@ * ); * }
    * + * @param description optional description of the security scheme * @see SecurityScheme for the base interface * @see OpenAPI Security Scheme * @see RFC 8446 - TLS 1.3 Client Authentication * @see A2A Protocol Specification */ -public final class MutualTLSSecurityScheme implements SecurityScheme { +public record MutualTLSSecurityScheme(String description) implements SecurityScheme { /** * The type identifier for mutual TLS security schemes: "mutualTLS". */ public static final String MUTUAL_TLS = "mutualTLS"; - private final String description; - private final String type; - - /** - * Constructs a mutual TLS security scheme with the specified description. - * - * @param description optional description of the security scheme - */ - public MutualTLSSecurityScheme(String description) { - this(description, MUTUAL_TLS); - } - - /** - * Constructs a mutual TLS security scheme with no description. - */ - public MutualTLSSecurityScheme() { - this(null, MUTUAL_TLS); - } - - /** - * Constructs a mutual TLS security scheme with the specified description and type. - * - * @param description optional description of the security scheme - * @param type the security scheme type (must be "mutualTLS") - */ - public MutualTLSSecurityScheme(String description, String type) { - Assert.checkNotNullParam("type", type); - if (!type.equals(MUTUAL_TLS)) { - throw new IllegalArgumentException("Invalid type for MutualTLSSecurityScheme"); - } - this.description = description; - this.type = type; - } - - /** - * Gets the Description. - * - * @return the Description - */ - @Override - public String getDescription() { - return description; - } - - /** - * Gets the Type. - * - * @return the Type - */ - public String getType() { - return type; - } } diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 9194069ef..ee6557efc 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -13,88 +13,32 @@ *

    * Corresponds to the OpenAPI "oauth2" security scheme type. * + * @param flows the OAuth 2.0 flow configuration (required) + * @param description optional description of the security scheme + * @param oauth2MetadataUrl optional URL to OAuth 2.0 metadata (RFC 8414) * @see SecurityScheme for the base interface * @see OAuthFlows for flow configuration * @see OpenAPI Security Scheme * @see A2A Protocol Specification */ -public final class OAuth2SecurityScheme implements SecurityScheme { +public record OAuth2SecurityScheme( + OAuthFlows flows, + String description, + String oauth2MetadataUrl +) implements SecurityScheme { /** * The type identifier for OAuth 2.0 security schemes: "oauth2". */ public static final String OAUTH2 = "oauth2"; - private final OAuthFlows flows; - private final String description; - private final String type; - private final String oauth2MetadataUrl; /** - * Constructs an OAuth 2.0 security scheme. + * Compact constructor with validation. * - * @param flows the OAuth 2.0 flow configuration (required) - * @param description optional description of the security scheme - * @param oauth2MetadataUrl optional URL to OAuth 2.0 metadata (RFC 8414) + * @throws IllegalArgumentException if flows is null */ - public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2MetadataUrl) { - this(flows, description, oauth2MetadataUrl, OAUTH2); - } - - /** - * Constructs an OAuth 2.0 security scheme with explicit type. - * - * @param flows the OAuth 2.0 flow configuration (required) - * @param description optional description of the security scheme - * @param oauth2MetadataUrl optional URL to OAuth 2.0 metadata (RFC 8414) - * @param type the security scheme type (must be "oauth2") - */ - public OAuth2SecurityScheme(OAuthFlows flows, String description, String oauth2MetadataUrl, String type) { + public OAuth2SecurityScheme { Assert.checkNotNullParam("flows", flows); - Assert.checkNotNullParam("type", type); - if (!type.equals(OAUTH2)) { - throw new IllegalArgumentException("Invalid type for OAuth2SecurityScheme"); - } - this.flows = flows; - this.description = description; - this.oauth2MetadataUrl = oauth2MetadataUrl; - this.type = type; - } - - /** - * Gets the Description. - * - * @return the Description - */ - @Override - public String getDescription() { - return description; - } - - /** - * Returns the OAuth flows configuration. - * - * @return the OAuth flows - */ - public OAuthFlows getFlows() { - return flows; - } - - /** - * Gets the Type. - * - * @return the Type - */ - public String getType() { - return type; - } - - /** - * Gets the Oauth2MetadataUrl. - * - * @return the Oauth2MetadataUrl - */ - public String getOauth2MetadataUrl() { - return oauth2MetadataUrl; } /** @@ -107,7 +51,16 @@ public static Builder builder() { } /** - * Builder for constructing instances. + * Builder for constructing immutable {@link OAuth2SecurityScheme} instances. + *

    + * Example usage: + *

    {@code
    +     * OAuth2SecurityScheme scheme = OAuth2SecurityScheme.builder()
    +     *     .flows(flows)
    +     *     .description("OAuth 2.0 authentication")
    +     *     .oauth2MetadataUrl("https://example.com/.well-known/oauth-authorization-server")
    +     *     .build();
    +     * }
    */ public static class Builder { private OAuthFlows flows; @@ -121,9 +74,9 @@ private Builder() { } /** - * Sets the flows. + * Sets the OAuth flows configuration. * - * @param flows the flows + * @param flows the OAuth 2.0 flow configuration (required) * @return this builder for method chaining */ public Builder flows(OAuthFlows flows) { @@ -132,9 +85,9 @@ public Builder flows(OAuthFlows flows) { } /** - * Sets the description. + * Sets the human-readable description of the security scheme. * - * @param description the description + * @param description the description (optional) * @return this builder for method chaining */ public Builder description(String description) { @@ -143,9 +96,9 @@ public Builder description(String description) { } /** - * Sets the oauth2MetadataUrl. + * Sets the OAuth 2.0 metadata URL. * - * @param oauth2MetadataUrl the oauth2MetadataUrl + * @param oauth2MetadataUrl URL to OAuth 2.0 metadata document (RFC 8414) (optional) * @return this builder for method chaining */ public Builder oauth2MetadataUrl(String oauth2MetadataUrl) { @@ -154,9 +107,10 @@ public Builder oauth2MetadataUrl(String oauth2MetadataUrl) { } /** - * Builds the OAuth2SecurityScheme. + * Builds a new immutable {@link OAuth2SecurityScheme} from the current builder state. * * @return a new OAuth2SecurityScheme instance + * @throws IllegalArgumentException if flows is null */ public OAuth2SecurityScheme build() { return new OAuth2SecurityScheme(flows, description, oauth2MetadataUrl); diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 1b971828b..0ba446cad 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -17,81 +17,36 @@ *

    * Example usage: *

    {@code
    - * OpenIdConnectSecurityScheme scheme = new OpenIdConnectSecurityScheme.Builder()
    + * OpenIdConnectSecurityScheme scheme = OpenIdConnectSecurityScheme.builder()
      *     .openIdConnectUrl("https://example.com/.well-known/openid-configuration")
      *     .description("OpenID Connect authentication")
      *     .build();
      * }
    * + * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) + * @param description optional description of the security scheme * @see SecurityScheme for the base interface * @see OpenAPI Security Scheme * @see OpenID Connect Discovery * @see A2A Protocol Specification */ -public final class OpenIdConnectSecurityScheme implements SecurityScheme { +public record OpenIdConnectSecurityScheme( + String openIdConnectUrl, + String description +) implements SecurityScheme { /** * The type identifier for OpenID Connect security schemes: "openIdConnect". */ public static final String OPENID_CONNECT = "openIdConnect"; - private final String openIdConnectUrl; - private final String description; - private final String type; /** - * Constructs an OpenID Connect security scheme. + * Compact constructor with validation. * - * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) - * @param description optional description of the security scheme + * @throws IllegalArgumentException if openIdConnectUrl is null */ - public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description) { - this(openIdConnectUrl, description, OPENID_CONNECT); - } - - /** - * Constructs an OpenID Connect security scheme with explicit type. - * - * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) - * @param description optional description of the security scheme - * @param type the security scheme type (must be "openIdConnect") - */ - public OpenIdConnectSecurityScheme(String openIdConnectUrl, String description, String type) { - Assert.checkNotNullParam("type", type); + public OpenIdConnectSecurityScheme { Assert.checkNotNullParam("openIdConnectUrl", openIdConnectUrl); - if (!type.equals(OPENID_CONNECT)) { - throw new IllegalArgumentException("Invalid type for OpenIdConnectSecurityScheme"); - } - this.openIdConnectUrl = openIdConnectUrl; - this.description = description; - this.type = type; - } - - /** - * Gets the Description. - * - * @return the Description - */ - @Override - public String getDescription() { - return description; - } - - /** - * Gets the OpenIdConnectUrl. - * - * @return the OpenIdConnectUrl - */ - public String getOpenIdConnectUrl() { - return openIdConnectUrl; - } - - /** - * Gets the Type. - * - * @return the Type - */ - public String getType() { - return type; } /** @@ -104,7 +59,7 @@ public static Builder builder() { } /** - * Builder for constructing {@link OpenIdConnectSecurityScheme} instances. + * Builder for constructing immutable {@link OpenIdConnectSecurityScheme} instances. *

    * Provides a fluent API for creating OpenID Connect security schemes. * The {@code openIdConnectUrl} parameter is required. @@ -123,7 +78,7 @@ private Builder() { * Sets the OpenID Connect Discovery URL. * * @param openIdConnectUrl URL to the OpenID Connect Discovery document (required) - * @return this builder instance + * @return this builder for method chaining */ public Builder openIdConnectUrl(String openIdConnectUrl) { this.openIdConnectUrl = openIdConnectUrl; @@ -131,10 +86,10 @@ public Builder openIdConnectUrl(String openIdConnectUrl) { } /** - * Sets an optional description of the security scheme. + * Sets the human-readable description of the security scheme. * - * @param description human-readable description - * @return this builder instance + * @param description the description (optional) + * @return this builder for method chaining */ public Builder description(String description) { this.description = description; @@ -142,14 +97,13 @@ public Builder description(String description) { } /** - * Builds the {@link OpenIdConnectSecurityScheme} instance. + * Builds a new immutable {@link OpenIdConnectSecurityScheme} from the current builder state. * - * @return a new immutable OpenIdConnectSecurityScheme + * @return a new OpenIdConnectSecurityScheme instance * @throws IllegalArgumentException if openIdConnectUrl is null */ public OpenIdConnectSecurityScheme build() { return new OpenIdConnectSecurityScheme(openIdConnectUrl, description); } } - } diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index d7f07a180..345db0602 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -1,7 +1,7 @@ package io.a2a.spec; /** - * Base class for content parts within {@link Message}s and {@link Artifact}s. + * Base interface for content parts within {@link Message}s and {@link Artifact}s. *

    * Parts represent the fundamental content units in the A2A Protocol, allowing multi-modal * communication through different content types. A Part can be: @@ -21,11 +21,11 @@ * @see Artifact * @see A2A Protocol Specification */ -public abstract class Part { +public interface Part { /** * Enum defining the different types of content parts. */ - public enum Kind { + enum Kind { /** * Plain text content part. */ @@ -62,5 +62,5 @@ public String asString() { * * @return the Part.Kind indicating the content type */ - public abstract Kind getKind(); + Kind getKind(); } \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index 57b409a4c..2121a636e 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -26,9 +26,9 @@ public sealed interface SecurityScheme permits APIKeySecurityScheme, HTTPAuthSec OpenIdConnectSecurityScheme, MutualTLSSecurityScheme { /** - * Gets the human-readable description of this security scheme. + * Returns the human-readable description of this security scheme. * * @return the description, or null if not provided */ - String getDescription(); + String description(); } diff --git a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java index 561cba970..4b7751c71 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java +++ b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java @@ -1,10 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.Message.MESSAGE; -import static io.a2a.spec.Task.TASK; -import static io.a2a.spec.TaskArtifactUpdateEvent.ARTIFACT_UPDATE; -import static io.a2a.spec.TaskStatusUpdateEvent.STATUS_UPDATE; - /** * Sealed interface for events that can be emitted during streaming A2A Protocol operations. *

    @@ -41,5 +36,5 @@ public sealed interface StreamingEventKind extends Event permits Task, Message, * * @return the event kind string (e.g., "task", "message", "status-update", "artifact-update") */ - String getKind(); + String kind(); } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index 383a3a8a8..e949a2fe3 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -6,9 +6,7 @@ import io.a2a.util.Assert; import static io.a2a.spec.Task.TASK; - -import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; +import static io.a2a.util.Utils.SPEC_VERSION_1_0; /** * Represents a single, stateful operation or conversation between a client and an agent in the A2A Protocol. @@ -28,160 +26,76 @@ *

  • Streaming: Client subscribes to Task updates and receives incremental artifacts as they are produced
  • * *

    - * Tasks are immutable once created and use the Builder pattern for construction. Updates to a Task's + * Tasks are immutable once created (including their history, artifacts and metadata attributes). + * They use the Builder pattern for construction. Updates to a Task's * state are communicated via new Task instances or TaskStatusUpdateEvent/TaskArtifactUpdateEvent objects. *

    * This class implements {@link EventKind} and {@link StreamingEventKind}, allowing Task instances to * be transmitted as events in both blocking and streaming scenarios. * + * @param id the unique identifier for this task + * @param contextId the context identifier associating this task with a conversation or session + * @param status the current status of the task + * @param artifacts the list of artifacts produced by the agent during task execution + * @param history the conversation history for this task + * @param metadata arbitrary metadata associated with the task * @see TaskStatus * @see TaskState * @see Artifact * @see Message * @see A2A Protocol Specification */ -@NullMarked -public final class Task implements EventKind, StreamingEventKind { +public record Task( + String id, + String contextId, + TaskStatus status, + List artifacts, + List history, + Map metadata +) implements EventKind, StreamingEventKind { - /** - * The kind identifier for Task events: "task". - */ public static final String TASK = "task"; - private final String id; - private final String contextId; - private final TaskStatus status; - private final List artifacts; - private final List history; - private final Map metadata; - private final String kind; /** - * Constructs a Task with default kind. + * Compact constructor with validation and defensive copying. * - * @param id the task identifier (required) - * @param contextId the context identifier (required) - * @param status the task status (required) - * @param artifacts the list of artifacts (optional) - * @param history the conversation history (optional) - * @param metadata additional metadata (optional) + * @throws IllegalArgumentException if id, contextId, or status is null */ - public Task(String id, String contextId, TaskStatus status, List artifacts, - List history, Map metadata) { - this(id, contextId, status, artifacts, history, metadata, TASK); - } - - /** - * Constructs a Task with all parameters. - * - * @param id the task identifier (required) - * @param contextId the context identifier (required) - * @param status the task status (required) - * @param artifacts the list of artifacts (optional) - * @param history the conversation history (optional) - * @param metadata additional metadata (optional) - * @param kind the event kind (must be "task") - */ - public Task(String id, String contextId, TaskStatus status, - @Nullable List artifacts, @Nullable List history, - Map metadata, String kind) { + public Task { Assert.checkNotNullParam("id", id); Assert.checkNotNullParam("contextId", contextId); Assert.checkNotNullParam("status", status); - Assert.checkNotNullParam("kind", kind); - if (! TASK.equals(kind)) { - throw new IllegalArgumentException("Invalid Task"); - } - this.id = id; - this.contextId = contextId; - this.status = status; - this.artifacts = artifacts != null ? List.copyOf(artifacts) : List.of(); - this.history = history != null ? List.copyOf(history) : List.of(); - this.metadata = metadata; - this.kind = kind; - } - - /** - * Returns the unique identifier for this task. - * - * @return the task ID - */ - public String getId() { - return id; - } - - /** - * Returns the conversation context identifier. - * - * @return the context ID - */ - public String getContextId() { - return contextId; - } - - /** - * Returns the current status of this task. - * - * @return the task status - */ - public TaskStatus getStatus() { - return status; - } - - /** - * Returns the artifacts produced by the agent during task execution. - * - * @return an immutable list of artifacts - */ - public List getArtifacts() { - return artifacts; - } - - /** - * Returns the conversation history for this task. - * - * @return an immutable list of messages - */ - public List getHistory() { - return history; - } - - /** - * Returns the metadata associated with this task. - * - * @return a map of metadata key-value pairs, or null if not set - */ - public Map getMetadata() { - return metadata; + artifacts = artifacts != null ? List.copyOf(artifacts) : List.of(); + history = history != null ? List.copyOf(history) : List.of(); + metadata = (metadata != null) ? Map.copyOf(metadata) : null; } @Override - public String getKind() { - return kind; + public String kind() { + return TASK; } /** - * Create a new Builder + * Creates a new Builder for constructing Task instances. * - * @return the builder + * @return a new Task.Builder instance */ public static Builder builder() { return new Builder(); } /** - * Create a new Builder initialized with values from an existing Task. + * Creates a new Builder initialized with values from an existing Task. *

    - * This Builder allows for creating a modified copy of an existing Task + * This constructor allows for creating a modified copy of an existing Task * by copying all fields and then selectively updating specific values. * * @param task the Task to copy values from - * @return a new builder */ public static Builder builder(Task task) { return new Builder(task); } - /** * Builder for constructing immutable {@link Task} instances. *

    @@ -192,7 +106,7 @@ public static Builder builder(Task task) { *

    * Example usage: *

    {@code
    -     * Task task = new Task.Builder()
    +     * Task task = Task.builder()
          *     .id("task-123")
          *     .contextId("context-456")
          *     .status(new TaskStatus(TaskState.WORKING))
    @@ -202,7 +116,6 @@ public static Builder builder(Task task) {
          *     .build();
          * }
    */ - @SuppressWarnings("NullAway") public static class Builder { private String id; private String contextId; @@ -215,24 +128,20 @@ public static class Builder { * Creates a new Builder with all fields unset. */ private Builder() { - } /** * Creates a new Builder initialized with values from an existing Task. - *

    - * This constructor allows for creating a modified copy of an existing Task - * by copying all fields and then selectively updating specific values. * * @param task the Task to copy values from */ private Builder(Task task) { - id = task.id; - contextId = task.contextId; - status = task.status; - artifacts = task.artifacts; - history = task.history; - metadata = task.metadata; + id = task.id(); + contextId = task.contextId(); + status = task.status(); + artifacts = task.artifacts(); + history = task.history(); + metadata = task.metadata(); } diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 11f9604ad..5f492be3d 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -29,124 +29,45 @@ * The {@code lastChunk} flag indicates whether this is the final update for an artifact, * allowing clients to distinguish between intermediate and final states. * + * @param taskId the task identifier (required) + * @param artifact the artifact being updated (required) + * @param contextId the context identifier (required) + * @param append whether to append to existing artifact (optional) + * @param lastChunk whether this is the final chunk (optional) + * @param metadata additional metadata (optional) * @see UpdateEvent * @see StreamingEventKind * @see Artifact * @see Task */ -public final class TaskArtifactUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { +public record TaskArtifactUpdateEvent( + String taskId, + Artifact artifact, + String contextId, + Boolean append, + Boolean lastChunk, + Map metadata +) implements EventKind, StreamingEventKind, UpdateEvent { /** * The kind identifier for artifact update events: "artifact-update". */ public static final String ARTIFACT_UPDATE = "artifact-update"; - private final String taskId; - private final Boolean append; - private final Boolean lastChunk; - private final Artifact artifact; - private final String contextId; - private final Map metadata; - private final String kind; /** - * Constructs a TaskArtifactUpdateEvent with default kind. + * Compact constructor with validation. * - * @param taskId the task identifier (required) - * @param artifact the artifact being updated (required) - * @param contextId the context identifier (required) - * @param append whether to append to existing artifact (optional) - * @param lastChunk whether this is the final chunk (optional) - * @param metadata additional metadata (optional) + * @throws IllegalArgumentException if taskId, artifact, or contextId is null */ - public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextId, Boolean append, Boolean lastChunk, Map metadata) { - this(taskId, artifact, contextId, append, lastChunk, metadata, ARTIFACT_UPDATE); - } - - /** - * Constructs a TaskArtifactUpdateEvent with all parameters. - * - * @param taskId the task identifier (required) - * @param artifact the artifact being updated (required) - * @param contextId the context identifier (required) - * @param append whether to append to existing artifact (optional) - * @param lastChunk whether this is the final chunk (optional) - * @param metadata additional metadata (optional) - * @param kind the event kind (must be "artifact-update") - */ - public TaskArtifactUpdateEvent(String taskId, Artifact artifact, String contextId, Boolean append, Boolean lastChunk, Map metadata, String kind) { + public TaskArtifactUpdateEvent { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("artifact", artifact); Assert.checkNotNullParam("contextId", contextId); - Assert.checkNotNullParam("kind", kind); - if (! kind.equals(ARTIFACT_UPDATE)) { - throw new IllegalArgumentException("Invalid TaskArtifactUpdateEvent"); - } - this.taskId = taskId; - this.artifact = artifact; - this.contextId = contextId; - this.append = append; - this.lastChunk = lastChunk; - this.metadata = metadata; - this.kind = kind; - } - - /** - * Returns the task identifier. - * - * @return the task ID - */ - public String getTaskId() { - return taskId; - } - - /** - * Returns the artifact being updated. - * - * @return the artifact - */ - public Artifact getArtifact() { - return artifact; - } - - /** - * Returns the context identifier. - * - * @return the context ID - */ - public String getContextId() { - return contextId; - } - - /** - * Returns whether this update should append to an existing artifact. - * - * @return true if appending, false or null for new artifact - */ - public Boolean isAppend() { - return append; - } - - /** - * Returns whether this is the final chunk for the artifact. - * - * @return true if final chunk, false or null otherwise - */ - public Boolean isLastChunk() { - return lastChunk; - } - - /** - * Returns the metadata associated with this event. - * - * @return a map of metadata key-value pairs, or null if not set - */ - public Map getMetadata() { - return metadata; } @Override - public String getKind() { - return kind; + public String kind() { + return ARTIFACT_UPDATE; } /** diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 43f5dbc23..d9d791f9d 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -10,110 +10,38 @@ /** * An event sent by the agent to notify the client of a change in a task's status. * This is typically used in streaming or subscription models. + * + * @param taskId the task identifier (required) + * @param status the task status (required) + * @param contextId the context identifier (required) + * @param isFinal whether this is a final status + * @param metadata additional metadata (optional) */ -public final class TaskStatusUpdateEvent implements EventKind, StreamingEventKind, UpdateEvent { +public record TaskStatusUpdateEvent( + String taskId, + TaskStatus status, + String contextId, + @SerializedName("final") boolean isFinal, + Map metadata +) implements EventKind, StreamingEventKind, UpdateEvent { /** * The kind identifier for status update events: "status-update". */ public static final String STATUS_UPDATE = "status-update"; - private final String taskId; - private final TaskStatus status; - private final String contextId; - @SerializedName("final") - private final boolean isFinal; - private final Map metadata; - private final String kind; - /** - * Constructs a TaskStatusUpdateEvent with default kind. - * - * @param taskId the task identifier (required) - * @param status the task status (required) - * @param contextId the context identifier (required) - * @param isFinal whether this is a final status - * @param metadata additional metadata (optional) + * Compact constructor with validation. */ - public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, boolean isFinal, - Map metadata) { - this(taskId, status, contextId, isFinal, metadata, STATUS_UPDATE); - } - - /** - * Constructs a TaskStatusUpdateEvent with all parameters. - * - * @param taskId the task identifier (required) - * @param status the task status (required) - * @param contextId the context identifier (required) - * @param isFinal whether this is a final status - * @param metadata additional metadata (optional) - * @param kind the event kind (must be "status-update") - */ - public TaskStatusUpdateEvent(String taskId, TaskStatus status, String contextId, boolean isFinal, Map metadata, String kind) { + public TaskStatusUpdateEvent { Assert.checkNotNullParam("taskId", taskId); Assert.checkNotNullParam("status", status); Assert.checkNotNullParam("contextId", contextId); - Assert.checkNotNullParam("kind", kind); - if (! kind.equals(STATUS_UPDATE)) { - throw new IllegalArgumentException("Invalid TaskStatusUpdateEvent"); - } - this.taskId = taskId; - this.status = status; - this.contextId = contextId; - this.isFinal = isFinal; - this.metadata = metadata; - this.kind = kind; - } - - /** - * Returns the task identifier. - * - * @return the task ID - */ - public String getTaskId() { - return taskId; - } - - /** - * Returns the task status. - * - * @return the task status - */ - public TaskStatus getStatus() { - return status; - } - - /** - * Returns the context identifier. - * - * @return the context ID - */ - public String getContextId() { - return contextId; - } - - /** - * Returns whether this is a final status. - * - * @return true if this is a final status - */ - public boolean isFinal() { - return isFinal; - } - - /** - * Returns the metadata. - * - * @return the metadata map - */ - public Map getMetadata() { - return metadata; } @Override - public String getKind() { - return kind; + public String kind() { + return STATUS_UPDATE; } /** diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index b6356dd57..4010d0063 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -3,6 +3,8 @@ import io.a2a.util.Assert; +import static io.a2a.util.Utils.SPEC_VERSION_1_0; + /** * Represents a plain text content part within a {@link Message} or {@link Artifact}. @@ -19,41 +21,29 @@ * TextPart withMetadata = new TextPart("Bonjour!", Map.of("language", "fr")); * }

    * + * @param text the text content (required, must not be null) * @see Part * @see Message * @see Artifact */ -public class TextPart extends Part { +public record TextPart(String text) implements Part { /** * The kind identifier for text parts: "text". */ public static final String TEXT = "text"; - private final String text; - private final Kind kind; /** - * Constructs a TextPart with the specified text content. + * Compact constructor with validation. * - * @param text the text content (required, must not be null) + * @throws IllegalArgumentException if text is null */ - public TextPart(String text) { + public TextPart { Assert.checkNotNullParam("text", text); - this.text = text; - this.kind = Kind.TEXT; } @Override public Kind getKind() { - return kind; - } - - /** - * Returns the text content. - * - * @return the text content - */ - public String getText() { - return text; + return Kind.TEXT; } -} \ No newline at end of file +} diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 01d796d16..24b2e650d 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -40,6 +40,11 @@ public class Utils { private static final Logger log = Logger.getLogger(Utils.class.getName()); + /** + * Specification version constant for deprecation annotations. + */ + public static final String SPEC_VERSION_1_0 = "1.0"; + /** * Deserializes JSON string into a typed object using Gson. *

    @@ -113,11 +118,11 @@ public static void rethrow(Throwable t) throws T { */ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event, String taskId) { // Append artifacts - List artifacts = task.getArtifacts() == null ? new ArrayList<>() : new ArrayList<>(task.getArtifacts()); + List artifacts = task.artifacts() == null ? new ArrayList<>() : new ArrayList<>(task.artifacts()); - Artifact newArtifact = event.getArtifact(); + Artifact newArtifact = event.artifact(); String artifactId = newArtifact.artifactId(); - boolean appendParts = event.isAppend() != null && event.isAppend(); + boolean appendParts = event.append() != null && event.append(); Artifact existingArtifact = null; int existingArtifactIndex = -1; diff --git a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java index ff33b11d2..c74dd3999 100644 --- a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java +++ b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java @@ -39,8 +39,8 @@ void testBasicTaskSerialization() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify deserialized task matches original - assertEquals(task.getId(), deserialized.getId()); - assertEquals(task.getStatus().state(), deserialized.getStatus().state()); + assertEquals(task.id(), deserialized.id()); + assertEquals(task.status().state(), deserialized.status().state()); } @Test @@ -60,8 +60,8 @@ void testTaskWithTimestamp() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify OffsetDateTime timestamp is preserved - assertNotNull(deserialized.getStatus().timestamp()); - assertEquals(task.getStatus().timestamp(), deserialized.getStatus().timestamp()); + assertNotNull(deserialized.status().timestamp()); + assertEquals(task.status().timestamp(), deserialized.status().timestamp()); } @Test @@ -95,10 +95,10 @@ void testTaskWithArtifacts() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify artifacts are preserved - assertNotNull(deserialized.getArtifacts()); - assertEquals(1, deserialized.getArtifacts().size()); - assertEquals("artifact-1", deserialized.getArtifacts().get(0).artifactId()); - assertEquals(2, deserialized.getArtifacts().get(0).parts().size()); + assertNotNull(deserialized.artifacts()); + assertEquals(1, deserialized.artifacts().size()); + assertEquals("artifact-1", deserialized.artifacts().get(0).artifactId()); + assertEquals(2, deserialized.artifacts().get(0).parts().size()); } @Test @@ -126,10 +126,10 @@ void testTaskWithHistory() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify history is preserved - assertNotNull(deserialized.getHistory()); - assertEquals(1, deserialized.getHistory().size()); - assertEquals(Message.Role.USER, deserialized.getHistory().get(0).getRole()); - assertEquals(1, deserialized.getHistory().get(0).getParts().size()); + assertNotNull(deserialized.history()); + assertEquals(1, deserialized.history().size()); + assertEquals(Message.Role.USER, deserialized.history().get(0).role()); + assertEquals(1, deserialized.history().get(0).parts().size()); } @Test @@ -166,14 +166,14 @@ void testTaskWithAllFields() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify all fields are preserved - assertEquals(task.getId(), deserialized.getId()); - assertEquals(task.getContextId(), deserialized.getContextId()); - assertEquals(task.getStatus().state(), deserialized.getStatus().state()); - assertEquals(task.getStatus().timestamp(), deserialized.getStatus().timestamp()); - assertEquals(task.getHistory().size(), deserialized.getHistory().size()); - assertEquals(task.getArtifacts().size(), deserialized.getArtifacts().size()); - assertNotNull(deserialized.getMetadata()); - assertEquals("value1", deserialized.getMetadata().get("key1")); + assertEquals(task.id(), deserialized.id()); + assertEquals(task.contextId(), deserialized.contextId()); + assertEquals(task.status().state(), deserialized.status().state()); + assertEquals(task.status().timestamp(), deserialized.status().timestamp()); + assertEquals(task.history().size(), deserialized.history().size()); + assertEquals(task.artifacts().size(), deserialized.artifacts().size()); + assertNotNull(deserialized.metadata()); + assertEquals("value1", deserialized.metadata().get("key1")); } @Test @@ -195,7 +195,7 @@ void testTaskWithDifferentStates() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify state is preserved - assertEquals(state, deserialized.getStatus().state()); + assertEquals(state, deserialized.status().state()); } } @@ -215,15 +215,15 @@ void testTaskWithNullOptionalFields() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify required fields are present - assertEquals("task-123", deserialized.getId()); - assertEquals("context-456", deserialized.getContextId()); - assertEquals(TaskState.SUBMITTED, deserialized.getStatus().state()); + assertEquals("task-123", deserialized.id()); + assertEquals("context-456", deserialized.contextId()); + assertEquals(TaskState.SUBMITTED, deserialized.status().state()); // Verify optional lists default to empty - assertNotNull(deserialized.getArtifacts()); - assertEquals(0, deserialized.getArtifacts().size()); - assertNotNull(deserialized.getHistory()); - assertEquals(0, deserialized.getHistory().size()); + assertNotNull(deserialized.artifacts()); + assertEquals(0, deserialized.artifacts().size()); + assertNotNull(deserialized.history()); + assertEquals(0, deserialized.history().size()); } @Test @@ -254,11 +254,11 @@ void testTaskWithFilePartBytes() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify file part is preserved - Part part = deserialized.getArtifacts().get(0).parts().get(0); + Part part = deserialized.artifacts().get(0).parts().get(0); assertTrue(part instanceof FilePart); FilePart deserializedFilePart = (FilePart) part; - assertTrue(deserializedFilePart.getFile() instanceof FileWithBytes); - FileWithBytes fileWithBytes = (FileWithBytes) deserializedFilePart.getFile(); + assertTrue(deserializedFilePart.file() instanceof FileWithBytes); + FileWithBytes fileWithBytes = (FileWithBytes) deserializedFilePart.file(); assertEquals("document.pdf", fileWithBytes.name()); assertEquals("application/pdf", fileWithBytes.mimeType()); } @@ -289,11 +289,11 @@ void testTaskWithFilePartUri() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify file part URI is preserved - Part part = deserialized.getArtifacts().get(0).parts().get(0); + Part part = deserialized.artifacts().get(0).parts().get(0); assertTrue(part instanceof FilePart); FilePart deserializedFilePart = (FilePart) part; - assertTrue(deserializedFilePart.getFile() instanceof FileWithUri); - FileWithUri fileWithUri = (FileWithUri) deserializedFilePart.getFile(); + assertTrue(deserializedFilePart.file() instanceof FileWithUri); + FileWithUri fileWithUri = (FileWithUri) deserializedFilePart.file(); assertEquals("https://example.com/photo.png", fileWithUri.uri()); } @@ -324,10 +324,10 @@ void testTaskWithDataPart() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify data part is preserved - Part part = deserialized.getArtifacts().get(0).parts().get(0); + Part part = deserialized.artifacts().get(0).parts().get(0); assertTrue(part instanceof DataPart); DataPart deserializedDataPart = (DataPart) part; - assertNotNull(deserializedDataPart.getData()); + assertNotNull(deserializedDataPart.data()); } @Test @@ -371,11 +371,11 @@ void testTaskRoundTrip() throws JsonProcessingException { Task deserialized2 = JsonUtil.fromJson(json2, Task.class); // Verify multiple round-trips produce identical results - assertEquals(deserialized.getId(), deserialized2.getId()); - assertEquals(deserialized.getContextId(), deserialized2.getContextId()); - assertEquals(deserialized.getStatus().state(), deserialized2.getStatus().state()); - assertEquals(deserialized.getHistory().size(), deserialized2.getHistory().size()); - assertEquals(deserialized.getArtifacts().size(), deserialized2.getArtifacts().size()); + assertEquals(deserialized.id(), deserialized2.id()); + assertEquals(deserialized.contextId(), deserialized2.contextId()); + assertEquals(deserialized.status().state(), deserialized2.status().state()); + assertEquals(deserialized.history().size(), deserialized2.history().size()); + assertEquals(deserialized.artifacts().size(), deserialized2.artifacts().size()); } @Test @@ -402,10 +402,10 @@ void testTaskStatusWithMessage() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify status message is preserved - assertEquals(TaskState.COMPLETED, deserialized.getStatus().state()); - assertNotNull(deserialized.getStatus().message()); - assertEquals(Message.Role.AGENT, deserialized.getStatus().message().getRole()); - assertTrue(deserialized.getStatus().message().getParts().get(0) instanceof TextPart); + assertEquals(TaskState.COMPLETED, deserialized.status().state()); + assertNotNull(deserialized.status().message()); + assertEquals(Message.Role.AGENT, deserialized.status().message().role()); + assertTrue(deserialized.status().message().parts().get(0) instanceof TextPart); } @Test @@ -423,12 +423,12 @@ void testDeserializeTaskFromJson() throws JsonProcessingException { Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - assertEquals("context-456", task.getContextId()); - assertEquals(TaskState.SUBMITTED, task.getStatus().state()); - assertNull(task.getStatus().message()); + assertEquals("task-123", task.id()); + assertEquals("context-456", task.contextId()); + assertEquals(TaskState.SUBMITTED, task.status().state()); + assertNull(task.status().message()); // TaskStatus automatically sets timestamp to current time if not provided - assertNotNull(task.getStatus().timestamp()); + assertNotNull(task.status().timestamp()); } @Test @@ -458,14 +458,14 @@ void testDeserializeTaskWithArtifactsFromJson() throws JsonProcessingException { Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - assertEquals(TaskState.COMPLETED, task.getStatus().state()); - assertEquals(1, task.getArtifacts().size()); - assertEquals("artifact-1", task.getArtifacts().get(0).artifactId()); - assertEquals("Result", task.getArtifacts().get(0).name()); - assertEquals(1, task.getArtifacts().get(0).parts().size()); - assertTrue(task.getArtifacts().get(0).parts().get(0) instanceof TextPart); - assertEquals("Hello World", ((TextPart) task.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals("task-123", task.id()); + assertEquals(TaskState.COMPLETED, task.status().state()); + assertEquals(1, task.artifacts().size()); + assertEquals("artifact-1", task.artifacts().get(0).artifactId()); + assertEquals("Result", task.artifacts().get(0).name()); + assertEquals(1, task.artifacts().get(0).parts().size()); + assertTrue(task.artifacts().get(0).parts().get(0) instanceof TextPart); + assertEquals("Hello World", ((TextPart) task.artifacts().get(0).parts().get(0)).text()); } @Test @@ -498,13 +498,13 @@ void testDeserializeTaskWithFilePartBytesFromJson() throws JsonProcessingExcepti Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - assertEquals(1, task.getArtifacts().size()); - Part part = task.getArtifacts().get(0).parts().get(0); + assertEquals("task-123", task.id()); + assertEquals(1, task.artifacts().size()); + Part part = task.artifacts().get(0).parts().get(0); assertTrue(part instanceof FilePart); FilePart filePart = (FilePart) part; - assertTrue(filePart.getFile() instanceof FileWithBytes); - FileWithBytes fileWithBytes = (FileWithBytes) filePart.getFile(); + assertTrue(filePart.file() instanceof FileWithBytes); + FileWithBytes fileWithBytes = (FileWithBytes) filePart.file(); assertEquals("application/pdf", fileWithBytes.mimeType()); assertEquals("document.pdf", fileWithBytes.name()); assertEquals("base64encodeddata", fileWithBytes.bytes()); @@ -540,12 +540,12 @@ void testDeserializeTaskWithFilePartUriFromJson() throws JsonProcessingException Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - Part part = task.getArtifacts().get(0).parts().get(0); + assertEquals("task-123", task.id()); + Part part = task.artifacts().get(0).parts().get(0); assertTrue(part instanceof FilePart); FilePart filePart = (FilePart) part; - assertTrue(filePart.getFile() instanceof FileWithUri); - FileWithUri fileWithUri = (FileWithUri) filePart.getFile(); + assertTrue(filePart.file() instanceof FileWithUri); + FileWithUri fileWithUri = (FileWithUri) filePart.file(); assertEquals("image/png", fileWithUri.mimeType()); assertEquals("photo.png", fileWithUri.name()); assertEquals("https://example.com/photo.png", fileWithUri.uri()); @@ -580,11 +580,11 @@ void testDeserializeTaskWithDataPartFromJson() throws JsonProcessingException { Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - Part part = task.getArtifacts().get(0).parts().get(0); + assertEquals("task-123", task.id()); + Part part = task.artifacts().get(0).parts().get(0); assertTrue(part instanceof DataPart); DataPart dataPart = (DataPart) part; - assertNotNull(dataPart.getData()); + assertNotNull(dataPart.data()); } @Test @@ -604,7 +604,8 @@ void testDeserializeTaskWithHistoryFromJson() throws JsonProcessingException { "kind": "text", "text": "User message" } - ] + ], + "messageId": "msg-1" }, { "role": "agent", @@ -613,7 +614,8 @@ void testDeserializeTaskWithHistoryFromJson() throws JsonProcessingException { "kind": "text", "text": "Agent response" } - ] + ], + "messageId": "msg-2" } ], "kind": "task" @@ -622,12 +624,12 @@ void testDeserializeTaskWithHistoryFromJson() throws JsonProcessingException { Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - assertEquals(2, task.getHistory().size()); - assertEquals(Message.Role.USER, task.getHistory().get(0).getRole()); - assertEquals(Message.Role.AGENT, task.getHistory().get(1).getRole()); - assertTrue(task.getHistory().get(0).getParts().get(0) instanceof TextPart); - assertEquals("User message", ((TextPart) task.getHistory().get(0).getParts().get(0)).getText()); + assertEquals("task-123", task.id()); + assertEquals(2, task.history().size()); + assertEquals(Message.Role.USER, task.history().get(0).role()); + assertEquals(Message.Role.AGENT, task.history().get(1).role()); + assertTrue(task.history().get(0).parts().get(0) instanceof TextPart); + assertEquals("User message", ((TextPart) task.history().get(0).parts().get(0)).text()); } @Test @@ -646,10 +648,10 @@ void testDeserializeTaskWithTimestampFromJson() throws JsonProcessingException { Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - assertEquals(TaskState.WORKING, task.getStatus().state()); - assertNotNull(task.getStatus().timestamp()); - assertEquals("2023-10-01T12:00:00.234-05:00", task.getStatus().timestamp().toString()); + assertEquals("task-123", task.id()); + assertEquals(TaskState.WORKING, task.status().state()); + assertNotNull(task.status().timestamp()); + assertEquals("2023-10-01T12:00:00.234-05:00", task.status().timestamp().toString()); } @Test @@ -671,9 +673,9 @@ void testDeserializeTaskWithMetadataFromJson() throws JsonProcessingException { Task task = JsonUtil.fromJson(json, Task.class); - assertEquals("task-123", task.getId()); - assertNotNull(task.getMetadata()); - assertEquals("value1", task.getMetadata().get("key1")); + assertEquals("task-123", task.id()); + assertNotNull(task.metadata()); + assertEquals("value1", task.metadata().get("key1")); } @Test @@ -702,7 +704,7 @@ void testTaskWithMixedPartTypes() throws JsonProcessingException { Task deserialized = JsonUtil.fromJson(json, Task.class); // Verify all part types are preserved - List> parts = deserialized.getArtifacts().get(0).parts(); + List> parts = deserialized.artifacts().get(0).parts(); assertEquals(4, parts.size()); assertTrue(parts.get(0) instanceof TextPart); assertTrue(parts.get(1) instanceof FilePart); diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index adf0b5394..0adedad9a 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -50,7 +50,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } // Sleep to allow task state persistence before TCK resubscribe test - if (context.getMessage() != null && context.getMessage().getMessageId().startsWith("test-resubscribe-message-id")) { + if (context.getMessage() != null && context.getMessage().messageId().startsWith("test-resubscribe-message-id")) { int timeoutMs = Integer.parseInt(System.getenv().getOrDefault("RESUBSCRIBE_TIMEOUT_MS", "3000")); System.out.println("====> task id starts with test-resubscribe-message-id, sleeping for " + timeoutMs + " ms"); try { @@ -77,12 +77,12 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC System.out.println("====> No task found"); throw new TaskNotCancelableError(); } - if (task.getStatus().state() == TaskState.CANCELED) { + if (task.status().state() == TaskState.CANCELED) { System.out.println("====> task already canceled"); throw new TaskNotCancelableError(); } - if (task.getStatus().state() == TaskState.COMPLETED) { + if (task.status().state() == TaskState.COMPLETED) { System.out.println("====> task already completed"); throw new TaskNotCancelableError(); } @@ -90,8 +90,8 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC TaskUpdater updater = new TaskUpdater(context, eventQueue); updater.cancel(); eventQueue.enqueueEvent(TaskStatusUpdateEvent.builder() - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .status(new TaskStatus(TaskState.CANCELED)) .isFinal(true) .build()); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 166e60ef2..89c6de9b8 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -156,13 +156,13 @@ protected AbstractA2AServerTest(int serverPort) { public void testTaskStoreMethodsSanityTest() throws Exception { Task task = Task.builder(MINIMAL_TASK).id("abcde").build(); saveTaskInTaskStore(task); - Task saved = getTaskFromTaskStore(task.getId()); - assertEquals(task.getId(), saved.getId()); - assertEquals(task.getContextId(), saved.getContextId()); - assertEquals(task.getStatus().state(), saved.getStatus().state()); + Task saved = getTaskFromTaskStore(task.id()); + assertEquals(task.id(), saved.id()); + assertEquals(task.contextId(), saved.contextId()); + assertEquals(task.status().state(), saved.status().state()); - deleteTaskInTaskStore(task.getId()); - Task saved2 = getTaskFromTaskStore(task.getId()); + deleteTaskInTaskStore(task.id()); + Task saved2 = getTaskFromTaskStore(task.id()); assertNull(saved2); } @@ -178,14 +178,14 @@ private void testGetTask() throws Exception { private void testGetTask(String mediaType) throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.getId())); - assertEquals("task-123", response.getId()); - assertEquals("session-xyz", response.getContextId()); - assertEquals(TaskState.SUBMITTED, response.getStatus().state()); + Task response = getClient().getTask(new TaskQueryParams(MINIMAL_TASK.id())); + assertEquals("task-123", response.id()); + assertEquals("session-xyz", response.contextId()); + assertEquals(TaskState.SUBMITTED, response.status().state()); } catch (A2AClientException e) { fail("Unexpected exception during getTask: " + e.getMessage(), e); } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -205,14 +205,14 @@ public void testGetTaskNotFound() throws Exception { public void testCancelTaskSuccess() throws Exception { saveTaskInTaskStore(CANCEL_TASK); try { - Task task = getClient().cancelTask(new TaskIdParams(CANCEL_TASK.getId())); - assertEquals(CANCEL_TASK.getId(), task.getId()); - assertEquals(CANCEL_TASK.getContextId(), task.getContextId()); - assertEquals(TaskState.CANCELED, task.getStatus().state()); + Task task = getClient().cancelTask(new TaskIdParams(CANCEL_TASK.id())); + assertEquals(CANCEL_TASK.id(), task.id()); + assertEquals(CANCEL_TASK.contextId(), task.contextId()); + assertEquals(TaskState.CANCELED, task.status().state()); } catch (A2AClientException e) { fail("Unexpected exception during cancel task: " + e.getMessage(), e); } finally { - deleteTaskInTaskStore(CANCEL_TASK.getId()); + deleteTaskInTaskStore(CANCEL_TASK.id()); } } @@ -220,13 +220,13 @@ public void testCancelTaskSuccess() throws Exception { public void testCancelTaskNotSupported() throws Exception { saveTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED); try { - getClient().cancelTask(new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.getId())); + getClient().cancelTask(new TaskIdParams(CANCEL_TASK_NOT_SUPPORTED.id())); fail("Expected A2AClientException for unsupported cancel operation"); } catch (A2AClientException e) { // Expected - the client should throw an exception for unsupported operations assertInstanceOf(UnsupportedOperationError.class, e.getCause()); } finally { - deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.getId()); + deleteTaskInTaskStore(CANCEL_TASK_NOT_SUPPORTED.id()); } } @@ -275,9 +275,9 @@ public void testListTasksSuccess() throws Exception { assertEquals(result.tasks().size(), result.pageSize()); assertTrue(result.totalSize() >= 3, "Total size should be at least 3"); } finally { - deleteTaskInTaskStore(task1.getId()); - deleteTaskInTaskStore(task2.getId()); - deleteTaskInTaskStore(task3.getId()); + deleteTaskInTaskStore(task1.id()); + deleteTaskInTaskStore(task2.id()); + deleteTaskInTaskStore(task3.id()); } } @@ -314,11 +314,11 @@ public void testListTasksFilterByContextId() throws Exception { assertNotNull(result); assertNotNull(result.tasks()); assertEquals(2, result.tasks().size(), "Should have exactly 2 tasks with context-filter-1"); - assertTrue(result.tasks().stream().allMatch(t -> "context-filter-1".equals(t.getContextId()))); + assertTrue(result.tasks().stream().allMatch(t -> "context-filter-1".equals(t.contextId()))); } finally { - deleteTaskInTaskStore(task1.getId()); - deleteTaskInTaskStore(task2.getId()); - deleteTaskInTaskStore(task3.getId()); + deleteTaskInTaskStore(task1.id()); + deleteTaskInTaskStore(task2.id()); + deleteTaskInTaskStore(task3.id()); } } @@ -356,12 +356,12 @@ public void testListTasksFilterByStatus() throws Exception { assertNotNull(result.tasks()); assertTrue(result.tasks().size() >= 2, "Should have at least 2 WORKING tasks"); assertTrue(result.tasks().stream() - .filter(t -> t.getId().startsWith("list-task-status-")) - .allMatch(t -> TaskState.WORKING.equals(t.getStatus().state()))); + .filter(t -> t.id().startsWith("list-task-status-")) + .allMatch(t -> TaskState.WORKING.equals(t.status().state()))); } finally { - deleteTaskInTaskStore(task1.getId()); - deleteTaskInTaskStore(task2.getId()); - deleteTaskInTaskStore(task3.getId()); + deleteTaskInTaskStore(task1.id()); + deleteTaskInTaskStore(task2.id()); + deleteTaskInTaskStore(task3.id()); } } @@ -414,9 +414,9 @@ public void testListTasksWithPagination() throws Exception { assertNotNull(result2); assertTrue(result2.tasks().size() >= 1, "Second page should have at least 1 task"); } finally { - deleteTaskInTaskStore(task1.getId()); - deleteTaskInTaskStore(task2.getId()); - deleteTaskInTaskStore(task3.getId()); + deleteTaskInTaskStore(task1.id()); + deleteTaskInTaskStore(task2.id()); + deleteTaskInTaskStore(task3.id()); } } @@ -450,22 +450,22 @@ public void testListTasksWithHistoryLimit() throws Exception { assertNotNull(result); assertEquals(1, result.tasks().size()); Task task = result.tasks().get(0); - assertNotNull(task.getHistory()); - assertEquals(2, task.getHistory().size(), "History should be limited to 2 most recent messages"); + assertNotNull(task.history()); + assertEquals(2, task.history().size(), "History should be limited to 2 most recent messages"); // Verify we get the most recent messages (msg-3 and msg-4) - assertEquals("msg-3", task.getHistory().get(0).getMessageId()); - assertEquals("msg-4", task.getHistory().get(1).getMessageId()); + assertEquals("msg-3", task.history().get(0).messageId()); + assertEquals("msg-4", task.history().get(1).messageId()); } finally { - deleteTaskInTaskStore(taskWithHistory.getId()); + deleteTaskInTaskStore(taskWithHistory.id()); } } @Test public void testSendMessageNewMessageSuccess() throws Exception { - assertTrue(getTaskFromTaskStore(MINIMAL_TASK.getId()) == null); + assertTrue(getTaskFromTaskStore(MINIMAL_TASK.id()) == null); Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); CountDownLatch latch = new CountDownLatch(1); @@ -491,11 +491,11 @@ public void testSendMessageNewMessageSuccess() throws Exception { assertFalse(wasUnexpectedEvent.get()); Message messageResponse = receivedMessage.get(); assertNotNull(messageResponse); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); + assertEquals(MESSAGE.messageId(), messageResponse.messageId()); + assertEquals(MESSAGE.role(), messageResponse.role()); + Part part = messageResponse.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + assertEquals("test message", ((TextPart) part).text()); } @Test @@ -503,8 +503,8 @@ public void testSendMessageExistingTaskSuccess() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); CountDownLatch latch = new CountDownLatch(1); @@ -529,15 +529,15 @@ public void testSendMessageExistingTaskSuccess() throws Exception { assertTrue(latch.await(10, TimeUnit.SECONDS)); Message messageResponse = receivedMessage.get(); assertNotNull(messageResponse); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); + assertEquals(MESSAGE.messageId(), messageResponse.messageId()); + assertEquals(MESSAGE.role(), messageResponse.role()); + Part part = messageResponse.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + assertEquals("test message", ((TextPart) part).text()); } catch (A2AClientException e) { fail("Unexpected exception during sendMessage: " + e.getMessage(), e); } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -547,16 +547,16 @@ public void testSetPushNotificationSuccess() throws Exception { try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); + MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig); - assertEquals(MINIMAL_TASK.getId(), config.taskId()); + assertEquals(MINIMAL_TASK.id(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", config.pushNotificationConfig().id()); } catch (A2AClientException e) { fail("Unexpected exception during set push notification test: " + e.getMessage(), e); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "c295ea44-7543-4f78-b524-7a38915ad6e4"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "c295ea44-7543-4f78-b524-7a38915ad6e4"); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -566,28 +566,28 @@ public void testGetPushNotificationSuccess() throws Exception { try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); + MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertNotNull(setResult); TaskPushNotificationConfig config = getClient().getTaskPushNotificationConfiguration( - new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); - assertEquals(MINIMAL_TASK.getId(), config.taskId()); + new GetTaskPushNotificationConfigParams(MINIMAL_TASK.id())); + assertEquals(MINIMAL_TASK.id(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); } catch (A2AClientException e) { fail("Unexpected exception during get push notification test: " + e.getMessage(), e); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "c295ea44-7543-4f78-b524-7a38915ad6e4"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "c295ea44-7543-4f78-b524-7a38915ad6e4"); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @Test public void testError() throws A2AClientException { Message message = Message.builder(MESSAGE) - .taskId(SEND_MESSAGE_NOT_SUPPORTED.getId()) - .contextId(SEND_MESSAGE_NOT_SUPPORTED.getContextId()) + .taskId(SEND_MESSAGE_NOT_SUPPORTED.id()) + .contextId(SEND_MESSAGE_NOT_SUPPORTED.contextId()) .build(); try { @@ -637,7 +637,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap // does not work because after the message is sent, the queue becomes null but task resubscription // requires the queue to still be active - ensureQueueForTask(MINIMAL_TASK.getId()); + ensureQueueForTask(MINIMAL_TASK.id()); CountDownLatch eventLatch = new CountDownLatch(2); AtomicReference artifactUpdateEvent = new AtomicReference<>(); @@ -676,7 +676,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); // Resubscribe to the task with specific consumer and error handler - getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), List.of(consumer), errorHandler); + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.id()), List.of(consumer), errorHandler); // Wait for subscription to be established assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); @@ -684,16 +684,16 @@ public void testResubscribeExistingTaskSuccess() throws Exception { // Enqueue events on the server List events = List.of( TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build()); @@ -710,21 +710,21 @@ public void testResubscribeExistingTaskSuccess() throws Exception { // Verify artifact update event TaskArtifactUpdateEvent receivedArtifactEvent = artifactUpdateEvent.get(); assertNotNull(receivedArtifactEvent); - assertEquals(MINIMAL_TASK.getId(), receivedArtifactEvent.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), receivedArtifactEvent.getContextId()); - Part part = receivedArtifactEvent.getArtifact().parts().get(0); + assertEquals(MINIMAL_TASK.id(), receivedArtifactEvent.taskId()); + assertEquals(MINIMAL_TASK.contextId(), receivedArtifactEvent.contextId()); + Part part = receivedArtifactEvent.artifact().parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("text", ((TextPart) part).getText()); + assertEquals("text", ((TextPart) part).text()); // Verify status update event TaskStatusUpdateEvent receivedStatusEvent = statusUpdateEvent.get(); assertNotNull(receivedStatusEvent); - assertEquals(MINIMAL_TASK.getId(), receivedStatusEvent.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), receivedStatusEvent.getContextId()); - assertEquals(TaskState.COMPLETED, receivedStatusEvent.getStatus().state()); - assertNotNull(receivedStatusEvent.getStatus().timestamp()); + assertEquals(MINIMAL_TASK.id(), receivedStatusEvent.taskId()); + assertEquals(MINIMAL_TASK.contextId(), receivedStatusEvent.contextId()); + assertEquals(TaskState.COMPLETED, receivedStatusEvent.status().state()); + assertNotNull(receivedStatusEvent.status().timestamp()); } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -736,7 +736,7 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep // attempting to send a streaming message instead of explicitly calling queueManager#createOrTap // does not work because after the message is sent, the queue becomes null but task resubscription // requires the queue to still be active - ensureQueueForTask(MINIMAL_TASK.getId()); + ensureQueueForTask(MINIMAL_TASK.id()); CountDownLatch eventLatch = new CountDownLatch(2); AtomicReference artifactUpdateEvent = new AtomicReference<>(); @@ -782,7 +782,7 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep .whenComplete((unused, throwable) -> subscriptionLatch.countDown()); // Resubscribe to the task with the client consumer and error handler - clientWithConsumer.resubscribe(new TaskIdParams(MINIMAL_TASK.getId())); + clientWithConsumer.resubscribe(new TaskIdParams(MINIMAL_TASK.id())); // Wait for subscription to be established assertTrue(subscriptionLatch.await(15, TimeUnit.SECONDS)); @@ -790,16 +790,16 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep // Enqueue events on the server List events = List.of( TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build()); @@ -816,21 +816,21 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep // Verify artifact update event TaskArtifactUpdateEvent receivedArtifactEvent = artifactUpdateEvent.get(); assertNotNull(receivedArtifactEvent); - assertEquals(MINIMAL_TASK.getId(), receivedArtifactEvent.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), receivedArtifactEvent.getContextId()); - Part part = receivedArtifactEvent.getArtifact().parts().get(0); + assertEquals(MINIMAL_TASK.id(), receivedArtifactEvent.taskId()); + assertEquals(MINIMAL_TASK.contextId(), receivedArtifactEvent.contextId()); + Part part = receivedArtifactEvent.artifact().parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("text", ((TextPart) part).getText()); + assertEquals("text", ((TextPart) part).text()); // Verify status update event TaskStatusUpdateEvent receivedStatusEvent = statusUpdateEvent.get(); assertNotNull(receivedStatusEvent); - assertEquals(MINIMAL_TASK.getId(), receivedStatusEvent.getTaskId()); - assertEquals(MINIMAL_TASK.getContextId(), receivedStatusEvent.getContextId()); - assertEquals(TaskState.COMPLETED, receivedStatusEvent.getStatus().state()); - assertNotNull(receivedStatusEvent.getStatus().timestamp()); + assertEquals(MINIMAL_TASK.id(), receivedStatusEvent.taskId()); + assertEquals(MINIMAL_TASK.contextId(), receivedStatusEvent.contextId()); + assertEquals(TaskState.COMPLETED, receivedStatusEvent.status().state()); + assertNotNull(receivedStatusEvent.status().timestamp()); } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -901,7 +901,7 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti saveTaskInTaskStore(MINIMAL_TASK); try { // 1. Ensure queue exists for the task - ensureQueueForTask(MINIMAL_TASK.getId()); + ensureQueueForTask(MINIMAL_TASK.id()); // 2. First consumer subscribes and receives initial event CountDownLatch firstConsumerLatch = new CountDownLatch(1); @@ -930,7 +930,7 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti awaitStreamingSubscription() .whenComplete((unused, throwable) -> firstSubscriptionLatch.countDown()); - getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.id()), List.of(firstConsumer), firstErrorHandler); @@ -938,8 +938,8 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti // Enqueue first event TaskArtifactUpdateEvent event1 = TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("artifact-1") .parts(new TextPart("First artifact")) @@ -954,7 +954,7 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti assertNotNull(firstConsumerEvent.get()); // Verify we have multiple child queues (ensureQueue + first resubscribe) - int childCountBeforeSecond = getChildQueueCount(MINIMAL_TASK.getId()); + int childCountBeforeSecond = getChildQueueCount(MINIMAL_TASK.id()); assertTrue(childCountBeforeSecond >= 2, "Should have at least 2 child queues"); // 3. Second consumer resubscribes while first is still active @@ -989,21 +989,21 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti // This should succeed with reference counting because MainQueue stays alive // while first consumer's ChildQueue exists - getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.getId()), + getClient().resubscribe(new TaskIdParams(MINIMAL_TASK.id()), List.of(secondConsumer), secondErrorHandler); assertTrue(secondSubscriptionLatch.await(15, TimeUnit.SECONDS), "Second subscription should be established"); // Verify child queue count increased (now ensureQueue + first + second) - int childCountAfterSecond = getChildQueueCount(MINIMAL_TASK.getId()); + int childCountAfterSecond = getChildQueueCount(MINIMAL_TASK.id()); assertTrue(childCountAfterSecond > childCountBeforeSecond, "Child queue count should increase after second resubscription"); // 4. Enqueue second event - both consumers should receive it TaskArtifactUpdateEvent event2 = TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("artifact-2") .parts(new TextPart("Second artifact")) @@ -1019,11 +1019,11 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti TaskArtifactUpdateEvent receivedEvent = secondConsumerEvent.get(); assertNotNull(receivedEvent); - assertEquals("artifact-2", receivedEvent.getArtifact().artifactId()); - assertEquals("Second artifact", ((TextPart) receivedEvent.getArtifact().parts().get(0)).getText()); + assertEquals("artifact-2", receivedEvent.artifact().artifactId()); + assertEquals("Second artifact", ((TextPart) receivedEvent.artifact().parts().get(0)).text()); } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -1065,21 +1065,21 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { .url("http://example.com") .id("config2") .build(); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(2, result.size()); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig1, null), result.get(0)); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), notificationConfig2, null), result.get(1)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), notificationConfig1, null), result.get(0)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), notificationConfig2, null), result.get(1)); } catch (Exception e) { fail(); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "config2"); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -1094,26 +1094,26 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { = PushNotificationConfig.builder() .url("http://2.example.com") .build(); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig1); // will overwrite the previous one - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(1, result.size()); PushNotificationConfig expectedNotificationConfig = PushNotificationConfig.builder() .url("http://2.example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.getId(), expectedNotificationConfig, null), + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), expectedNotificationConfig, null), result.get(0)); } catch (Exception e) { fail(); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), MINIMAL_TASK.id()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -1133,12 +1133,12 @@ public void testListPushNotificationConfigEmptyList() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(0, result.size()); } catch (Exception e) { fail(e.getMessage()); } finally { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -1161,18 +1161,18 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception .url("http://example.com") .id("config2") .build(); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); savePushNotificationConfigInStore("task-456", notificationConfig1); try { // specify the config ID to delete getClient().deleteTaskPushNotificationConfigurations( - new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "config1")); + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), "config1")); // should now be 1 left List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(1, result.size()); // should remain unchanged, this is a different task @@ -1182,10 +1182,10 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception } catch (Exception e) { fail(e.getMessage()); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "config2"); deletePushNotificationConfigInStore("task-456", "config1"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); deleteTaskInTaskStore("task-456"); } } @@ -1203,23 +1203,23 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc .url("http://example.com") .id("config2") .build(); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); try { getClient().deleteTaskPushNotificationConfigurations( - new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), "non-existent-config-id")); + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), "non-existent-config-id")); // should remain unchanged List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(2, result.size()); } catch (Exception e) { fail(); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config1"); - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), "config2"); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "config1"); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), "config2"); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -1246,24 +1246,24 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio = PushNotificationConfig.builder() .url("http://2.example.com") .build(); - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig1); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig1); // this one will overwrite the previous one - savePushNotificationConfigInStore(MINIMAL_TASK.getId(), notificationConfig2); + savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); try { getClient().deleteTaskPushNotificationConfigurations( - new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), MINIMAL_TASK.id())); // should now be 0 List result = getClient().listTaskPushNotificationConfigurations( - new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId()), null); + new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id()), null); assertEquals(0, result.size()); } catch (Exception e) { fail(); } finally { - deletePushNotificationConfigInStore(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deletePushNotificationConfigInStore(MINIMAL_TASK.id(), MINIMAL_TASK.id()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } @@ -1282,10 +1282,10 @@ public void testNonBlockingWithMultipleMessages() throws Exception { BiConsumer firstMessageConsumer = (event, agentCard) -> { if (event instanceof TaskEvent te) { - taskIdRef.set(te.getTask().getId()); + taskIdRef.set(te.getTask().id()); firstTaskLatch.countDown(); } else if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskStatusUpdateEvent status) { - taskIdRef.set(status.getTaskId()); + taskIdRef.set(status.taskId()); firstTaskLatch.countDown(); } }; @@ -1395,18 +1395,18 @@ public void testNonBlockingWithMultipleMessages() throws Exception { .filter(e -> e instanceof TaskArtifactUpdateEvent) .findFirst() .orElseThrow(); - assertEquals("artifact-2", resubArtifact.getArtifact().artifactId()); + assertEquals("artifact-2", resubArtifact.artifact().artifactId()); assertEquals("Second message artifact", - ((TextPart) resubArtifact.getArtifact().parts().get(0)).getText()); + ((TextPart) resubArtifact.artifact().parts().get(0)).text()); // Verify artifact-2 details from streaming TaskArtifactUpdateEvent streamArtifact = (TaskArtifactUpdateEvent) streamReceivedEvents.stream() .filter(e -> e instanceof TaskArtifactUpdateEvent) .findFirst() .orElseThrow(); - assertEquals("artifact-2", streamArtifact.getArtifact().artifactId()); + assertEquals("artifact-2", streamArtifact.artifact().artifactId()); assertEquals("Second message artifact", - ((TextPart) streamArtifact.getArtifact().parts().get(0)).getText()); + ((TextPart) streamArtifact.artifact().parts().get(0)).text()); } @Test @@ -1581,8 +1581,8 @@ public void testStreamingMethodWithoutAcceptHeader() throws Exception { private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exception { Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( "1", new MessageSendParams(message, null, null, "")); @@ -1603,11 +1603,11 @@ private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exc if (jsonResponse != null) { assertNull(jsonResponse.getError()); Message messageResponse = (Message) jsonResponse.getResult(); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); + assertEquals(MESSAGE.messageId(), messageResponse.messageId()); + assertEquals(MESSAGE.role(), messageResponse.role()); + Part part = messageResponse.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + assertEquals("test message", ((TextPart) part).text()); latch.countDown(); } } catch (JsonProcessingException e) { @@ -1634,8 +1634,8 @@ public void testSendStreamingMessage(boolean createTask) throws Exception { } try { Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); CountDownLatch latch = new CountDownLatch(1); @@ -1669,16 +1669,16 @@ public void testSendStreamingMessage(boolean createTask) throws Exception { assertNull(errorRef.get()); Message messageResponse = receivedMessage.get(); assertNotNull(messageResponse); - assertEquals(MESSAGE.getMessageId(), messageResponse.getMessageId()); - assertEquals(MESSAGE.getRole(), messageResponse.getRole()); - Part part = messageResponse.getParts().get(0); + assertEquals(MESSAGE.messageId(), messageResponse.messageId()); + assertEquals(MESSAGE.role(), messageResponse.role()); + Part part = messageResponse.parts().get(0); assertEquals(Part.Kind.TEXT, part.getKind()); - assertEquals("test message", ((TextPart) part).getText()); + assertEquals("test message", ((TextPart) part).text()); } catch (A2AClientException e) { fail("Unexpected exception during sendMessage: " + e.getMessage(), e); } finally { if (createTask) { - deleteTaskInTaskStore(MINIMAL_TASK.getId()); + deleteTaskInTaskStore(MINIMAL_TASK.id()); } } } @@ -1809,9 +1809,9 @@ protected void ensureQueueForTask(String taskId) throws Exception { protected void enqueueEventOnServer(Event event) throws Exception { String path; if (event instanceof TaskArtifactUpdateEvent e) { - path = "test/queue/enqueueTaskArtifactUpdateEvent/" + e.getTaskId(); + path = "test/queue/enqueueTaskArtifactUpdateEvent/" + e.taskId(); } else if (event instanceof TaskStatusUpdateEvent e) { - path = "test/queue/enqueueTaskStatusUpdateEvent/" + e.getTaskId(); + path = "test/queue/enqueueTaskStatusUpdateEvent/" + e.taskId(); } else { throw new RuntimeException("Unknown event type " + event.getClass() + ". If you need the ability to" + " handle more types, please add the REST endpoints."); @@ -2158,7 +2158,7 @@ public void testMainQueueClosesForFinalizedTasks() throws Exception { BiConsumer consumer = (event, agentCard) -> { if (event instanceof TaskEvent te) { // Might get Task with final state - if (te.getTask().getStatus().state().isFinal()) { + if (te.getTask().status().state().isFinal()) { completionLatch.countDown(); } } else if (event instanceof MessageEvent me) { diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java index 8bff85c78..48daf8517 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java @@ -51,10 +51,10 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP @Override public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { - if (context.getTask().getId().equals("cancel-task-123")) { + if (context.getTask().id().equals("cancel-task-123")) { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); taskUpdater.cancel(); - } else if (context.getTask().getId().equals("cancel-task-not-supported-123")) { + } else if (context.getTask().id().equals("cancel-task-not-supported-123")) { throw new UnsupportedOperationError(); } } diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 0dc4c3a61..ff165a563 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -59,11 +59,11 @@ public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { private static final Message GRPC_MESSAGE = Message.newBuilder() - .setTaskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) - .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) - .setMessageId(AbstractA2ARequestHandlerTest.MESSAGE.getMessageId()) + .setTaskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) + .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) + .setMessageId(AbstractA2ARequestHandlerTest.MESSAGE.messageId()) .setRole(Role.ROLE_AGENT) - .addParts(Part.newBuilder().setText(((TextPart) AbstractA2ARequestHandlerTest.MESSAGE.getParts().get(0)).getText()).build()) + .addParts(Part.newBuilder().setText(((TextPart) AbstractA2ARequestHandlerTest.MESSAGE.parts().get(0)).text()).build()) .setMetadata(Struct.newBuilder().build()) .build(); @@ -73,7 +73,7 @@ public void testOnGetTaskSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); GetTaskRequest request = GetTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); @@ -85,8 +85,8 @@ public void testOnGetTaskSuccess() throws Exception { Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); Task task = result.get(0); - assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), task.getId()); - assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), task.getId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), task.getContextId()); assertEquals(TaskState.TASK_STATE_SUBMITTED, task.getStatus().getState()); } @@ -94,7 +94,7 @@ public void testOnGetTaskSuccess() throws Exception { public void testOnGetTaskNotFound() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); GetTaskRequest request = GetTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); @@ -119,7 +119,7 @@ public void testOnCancelTaskSuccess() throws Exception { }; CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.cancelTask(request, streamRecorder); @@ -130,8 +130,8 @@ public void testOnCancelTaskSuccess() throws Exception { Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); Task task = result.get(0); - assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), task.getId()); - assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), task.getContextId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), task.getId()); + assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), task.getContextId()); assertEquals(TaskState.TASK_STATE_CANCELLED, task.getStatus().getState()); } @@ -145,7 +145,7 @@ public void testOnCancelTaskNotSupported() throws Exception { }; CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.cancelTask(request, streamRecorder); @@ -158,7 +158,7 @@ public void testOnCancelTaskNotSupported() throws Exception { public void testOnCancelTaskNotFound() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); CancelTaskRequest request = CancelTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.cancelTask(request, streamRecorder); @@ -212,7 +212,7 @@ public void testOnMessageError() throws Exception { @Test public void testSetPushNotificationConfigSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + "config456"; StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); Assertions.assertNull(streamRecorder.getError()); @@ -235,7 +235,7 @@ public void testGetPushNotificationConfigSuccess() throws Exception { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + "config456"; + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + "config456"; // first set the task push notification config StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); @@ -260,7 +260,7 @@ public void testGetPushNotificationConfigSuccess() throws Exception { public void testPushNotificationsNotSupportedError() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } @@ -272,7 +272,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } @@ -284,7 +284,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { executor, taskStore, queueManager, null, null, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); assertGrpcError(streamRecorder, Status.Code.UNIMPLEMENTED); } @@ -348,8 +348,8 @@ public void onCompleted() { Task taskResponse = response.getTask(); Task expected = Task.newBuilder() - .setId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) - .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) + .setId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) + .setContextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) .addAllHistory(List.of(GRPC_MESSAGE)) .setStatus(TaskStatus.newBuilder().setStateValue(TaskState.TASK_STATE_SUBMITTED_VALUE)) .build(); @@ -371,16 +371,16 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep // This is used to send events from a mock List events = List.of( TaskArtifactUpdateEvent.builder() - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING)) .build()); @@ -397,15 +397,15 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep StreamResponse first = result.get(0); Assertions.assertTrue(first.hasArtifactUpdate()); io.a2a.grpc.TaskArtifactUpdateEvent taskArtifactUpdateEvent = first.getArtifactUpdate(); - assertEquals(task.getId(), taskArtifactUpdateEvent.getTaskId()); - assertEquals(task.getContextId(), taskArtifactUpdateEvent.getContextId()); + assertEquals(task.id(), taskArtifactUpdateEvent.getTaskId()); + assertEquals(task.contextId(), taskArtifactUpdateEvent.getContextId()); assertEquals("11", taskArtifactUpdateEvent.getArtifact().getArtifactId()); assertEquals("text", taskArtifactUpdateEvent.getArtifact().getParts(0).getText()); StreamResponse second = result.get(1); Assertions.assertTrue(second.hasStatusUpdate()); io.a2a.grpc.TaskStatusUpdateEvent taskStatusUpdateEvent = second.getStatusUpdate(); - assertEquals(task.getId(), taskStatusUpdateEvent.getTaskId()); - assertEquals(task.getContextId(), taskStatusUpdateEvent.getContextId()); + assertEquals(task.id(), taskStatusUpdateEvent.getTaskId()); + assertEquals(task.contextId(), taskStatusUpdateEvent.getContextId()); assertEquals(TaskState.TASK_STATE_WORKING, taskStatusUpdateEvent.getStatus().getState()); } @@ -415,16 +415,16 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex List events = List.of( AbstractA2ARequestHandlerTest.MINIMAL_TASK, TaskArtifactUpdateEvent.builder() - .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) - .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) - .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.COMPLETED)) .build()); @@ -436,7 +436,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex } }; - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder pushStreamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); Assertions.assertNull(pushStreamRecorder.getError()); @@ -467,33 +467,33 @@ public void onCompleted() { Assertions.assertEquals(3, httpClient.tasks.size()); io.a2a.spec.Task curr = httpClient.tasks.get(0); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), curr.getId()); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), curr.getContextId()); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - Assertions.assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), curr.id()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), curr.contextId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.status().state(), curr.status().state()); + Assertions.assertEquals(0, curr.artifacts() == null ? 0 : curr.artifacts().size()); curr = httpClient.tasks.get(1); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), curr.getId()); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), curr.getContextId()); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - Assertions.assertEquals(1, curr.getArtifacts().size()); - Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); - Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), curr.id()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), curr.contextId()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.status().state(), curr.status().state()); + Assertions.assertEquals(1, curr.artifacts().size()); + Assertions.assertEquals(1, curr.artifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.artifacts().get(0).parts().get(0)).text()); curr = httpClient.tasks.get(2); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(), curr.getId()); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId(), curr.getContextId()); - Assertions.assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.getStatus().state()); - Assertions.assertEquals(1, curr.getArtifacts().size()); - Assertions.assertEquals(1, curr.getArtifacts().get(0).parts().size()); - Assertions.assertEquals("text", ((TextPart)curr.getArtifacts().get(0).parts().get(0)).getText()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), curr.id()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), curr.contextId()); + Assertions.assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.status().state()); + Assertions.assertEquals(1, curr.artifacts().size()); + Assertions.assertEquals(1, curr.artifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.artifacts().get(0).parts().get(0)).text()); } @Test public void testOnResubscribeNoExistingTaskError() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.subscribeToTask(request, streamRecorder); @@ -505,7 +505,7 @@ public void testOnResubscribeNoExistingTaskError() throws Exception { public void testOnResubscribeExistingTaskSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); - queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); + queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); @@ -513,7 +513,7 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { StreamRecorder streamRecorder = StreamRecorder.create(); SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); handler.subscribeToTask(request, streamRecorder); @@ -540,26 +540,26 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); - queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()); + queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()); List events = List.of( TaskArtifactUpdateEvent.builder() - .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) - .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) - .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.getContextId()) + .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) + .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) .status(new io.a2a.spec.TaskStatus(io.a2a.spec.TaskState.WORKING)) .build()); StreamRecorder streamRecorder = StreamRecorder.create(); SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, @@ -594,7 +594,7 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() - .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.subscribeToTask(request, streamRecorder); @@ -619,12 +619,12 @@ public void testListPushNotificationConfig() throws Exception { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder pushRecorder = createTaskPushNotificationConfigRequest(handler, NAME); Assertions.assertNull(pushRecorder.getError()); ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -646,7 +646,7 @@ public void testListPushNotificationConfigNotSupported() throws Exception { }; ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -664,7 +664,7 @@ public void testListPushNotificationConfigNoPushConfigStore() { }; ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -679,7 +679,7 @@ public void testListPushNotificationConfigTaskNotFound() { }; ListTaskPushNotificationConfigRequest request = ListTaskPushNotificationConfigRequest.newBuilder() - .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId()) + .setParent("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.listTaskPushNotificationConfig(request, streamRecorder); @@ -694,7 +694,7 @@ public void testDeletePushNotificationConfig() throws Exception { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder pushRecorder = createTaskPushNotificationConfigRequest(handler, NAME); Assertions.assertNull(pushRecorder.getError()); @@ -717,7 +717,7 @@ public void testDeletePushNotificationConfigNotSupported() throws Exception { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) .build(); @@ -731,7 +731,7 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { DefaultRequestHandler requestHandler = DefaultRequestHandler.create( executor, taskStore, queueManager, null, null, internalExecutor); GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.getId(); + String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(NAME) .build(); @@ -831,7 +831,7 @@ private StreamRecorder createTaskPushNotificationCon SetTaskPushNotificationConfigRequest setRequest = SetTaskPushNotificationConfigRequest.newBuilder() .setConfig(taskPushNotificationConfig) .setConfigId("config456") - .setParent("tasks/" + MINIMAL_TASK.getId()) + .setParent("tasks/" + MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 08e2ffec9..b2f447d4b 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -83,7 +83,7 @@ public class JSONRPCHandlerTest extends AbstractA2ARequestHandlerTest { public void testOnGetTaskSuccess() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); + GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.id())); GetTaskResponse response = handler.onGetTask(request, callContext); assertEquals(request.getId(), response.getId()); Assertions.assertSame(MINIMAL_TASK, response.getResult()); @@ -93,7 +93,7 @@ public void testOnGetTaskSuccess() throws Exception { @Test public void testOnGetTaskNotFound() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.getId())); + GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.id())); GetTaskResponse response = handler.onGetTask(request, callContext); assertEquals(request.getId(), response.getId()); assertInstanceOf(TaskNotFoundError.class, response.getError()); @@ -114,15 +114,15 @@ public void testOnCancelTaskSuccess() throws Exception { taskUpdater.cancel(); }; - CancelTaskRequest request = new CancelTaskRequest("111", new TaskIdParams(MINIMAL_TASK.getId())); + CancelTaskRequest request = new CancelTaskRequest("111", new TaskIdParams(MINIMAL_TASK.id())); CancelTaskResponse response = handler.onCancelTask(request, callContext); assertNull(response.getError()); assertEquals(request.getId(), response.getId()); Task task = response.getResult(); - assertEquals(MINIMAL_TASK.getId(), task.getId()); - assertEquals(MINIMAL_TASK.getContextId(), task.getContextId()); - assertEquals(TaskState.CANCELED, task.getStatus().state()); + assertEquals(MINIMAL_TASK.id(), task.id()); + assertEquals(MINIMAL_TASK.contextId(), task.contextId()); + assertEquals(TaskState.CANCELED, task.status().state()); } @Test @@ -134,7 +134,7 @@ public void testOnCancelTaskNotSupported() { throw new UnsupportedOperationError(); }; - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); CancelTaskResponse response = handler.onCancelTask(request, callContext); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); @@ -144,7 +144,7 @@ public void testOnCancelTaskNotSupported() { @Test public void testOnCancelTaskNotFound() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + CancelTaskRequest request = new CancelTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); CancelTaskResponse response = handler.onCancelTask(request, callContext); assertEquals(request.getId(), response.getId()); assertNull(response.getResult()); @@ -158,8 +158,8 @@ public void testOnMessageNewMessageSuccess() { eventQueue.enqueueEvent(context.getMessage()); }; Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); @@ -178,8 +178,8 @@ public void testOnMessageNewMessageSuccessMocks() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); @@ -204,8 +204,8 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() { eventQueue.enqueueEvent(context.getMessage()); }; Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); @@ -225,8 +225,8 @@ public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { taskStore.save(MINIMAL_TASK); Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response; @@ -251,8 +251,8 @@ public void testOnMessageError() { eventQueue.enqueueEvent(new UnsupportedOperationError()); }; Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest( "1", new MessageSendParams(message, null, null)); @@ -265,8 +265,8 @@ public void testOnMessageError() { public void testOnMessageErrorMocks() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest( "1", new MessageSendParams(message, null, null)); @@ -291,8 +291,8 @@ public void testOnMessageStreamNewMessageSuccess() throws InterruptedException { }; Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( @@ -350,8 +350,8 @@ public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws Interrup .build(); TaskArtifactUpdateEvent artifactEvent = TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("artifact-1") .parts(new TextPart("Generated artifact content")) @@ -359,8 +359,8 @@ public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws Interrup .build(); TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .status(new TaskStatus(TaskState.COMPLETED)) .build(); @@ -375,8 +375,8 @@ public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws Interrup }; Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( @@ -431,21 +431,21 @@ public void onComplete() { // Verify the first event is the task Task receivedTask = assertInstanceOf(Task.class, results.get(0), "First event should be a Task"); - assertEquals(MINIMAL_TASK.getId(), receivedTask.getId()); - assertEquals(MINIMAL_TASK.getContextId(), receivedTask.getContextId()); - assertEquals(TaskState.WORKING, receivedTask.getStatus().state()); + assertEquals(MINIMAL_TASK.id(), receivedTask.id()); + assertEquals(MINIMAL_TASK.contextId(), receivedTask.contextId()); + assertEquals(TaskState.WORKING, receivedTask.status().state()); // Verify the second event is the artifact update TaskArtifactUpdateEvent receivedArtifact = assertInstanceOf(TaskArtifactUpdateEvent.class, results.get(1), "Second event should be a TaskArtifactUpdateEvent"); - assertEquals(MINIMAL_TASK.getId(), receivedArtifact.getTaskId()); - assertEquals("artifact-1", receivedArtifact.getArtifact().artifactId()); + assertEquals(MINIMAL_TASK.id(), receivedArtifact.taskId()); + assertEquals("artifact-1", receivedArtifact.artifact().artifactId()); // Verify the third event is the status update TaskStatusUpdateEvent receivedStatus = assertInstanceOf(TaskStatusUpdateEvent.class, results.get(2), "Third event should be a TaskStatusUpdateEvent"); - assertEquals(MINIMAL_TASK.getId(), receivedStatus.getTaskId()); - assertEquals(TaskState.COMPLETED, receivedStatus.getStatus().state()); + assertEquals(MINIMAL_TASK.id(), receivedStatus.taskId()); + assertEquals(TaskState.COMPLETED, receivedStatus.status().state()); } @Test @@ -456,22 +456,22 @@ public void testOnMessageStreamNewMessageSuccessMocks() { List events = List.of( MINIMAL_TASK, TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("art1") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .status(new TaskStatus(TaskState.COMPLETED)) .build()); Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( @@ -531,8 +531,8 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception taskStore.save(task); Message message = Message.builder(MESSAGE) - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( @@ -585,10 +585,10 @@ public void onComplete() { StreamingEventKind receivedType = results.get(0); assertInstanceOf(Task.class, receivedType); Task received = (Task) receivedType; - assertEquals(expected.getId(), received.getId()); - assertEquals(expected.getContextId(), received.getContextId()); - assertEquals(expected.getStatus(), received.getStatus()); - assertEquals(expected.getHistory(), received.getHistory()); + assertEquals(expected.id(), received.id()); + assertEquals(expected.contextId(), received.contextId()); + assertEquals(expected.status(), received.status()); + assertEquals(expected.history(), received.history()); } @Test @@ -603,22 +603,22 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { // This is used to send events from a mock List events = List.of( TaskArtifactUpdateEvent.builder() - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .status(new TaskStatus(TaskState.WORKING)) .build()); Message message = Message.builder(MESSAGE) - .taskId(task.getId()) - .contextId(task.getContextId()) + .taskId(task.id()) + .contextId(task.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest( @@ -676,14 +676,14 @@ public void testSetPushNotificationConfigSuccess() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), + MINIMAL_TASK.id(), PushNotificationConfig.builder().url("http://example.com") .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); TaskPushNotificationConfig taskPushConfigResult - = new TaskPushNotificationConfig( MINIMAL_TASK.getId(), + = new TaskPushNotificationConfig( MINIMAL_TASK.id(), PushNotificationConfig.builder().url("http://example.com") .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), "tenant"); @@ -701,17 +701,17 @@ public void testGetPushNotificationConfigSuccess() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); GetTaskPushNotificationConfigRequest getRequest - = new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + = new GetTaskPushNotificationConfigRequest("111", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.id())); GetTaskPushNotificationConfigResponse getResponse = handler.getPushNotificationConfig(getRequest, callContext); - TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.getId(), + TaskPushNotificationConfig expectedConfig = new TaskPushNotificationConfig(MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), ""); assertEquals(expectedConfig, getResponse.getResult()); @@ -725,16 +725,16 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex List events = List.of( MINIMAL_TASK, TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .status(new TaskStatus(TaskState.COMPLETED)) .build()); @@ -746,7 +746,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex }; TaskPushNotificationConfig config = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), + MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); @@ -754,7 +754,7 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex assertNull(stpnResponse.getError()); Message msg = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) + .taskId(MINIMAL_TASK.id()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(msg, null, null)); Flow.Publisher response = handler.onMessageSendStream(request, callContext); @@ -799,33 +799,33 @@ public void onComplete() { assertEquals(3, httpClient.tasks.size()); Task curr = httpClient.tasks.get(0); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - assertEquals(0, curr.getArtifacts() == null ? 0 : curr.getArtifacts().size()); + assertEquals(MINIMAL_TASK.id(), curr.id()); + assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); + assertEquals(MINIMAL_TASK.status().state(), curr.status().state()); + assertEquals(0, curr.artifacts() == null ? 0 : curr.artifacts().size()); curr = httpClient.tasks.get(1); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(MINIMAL_TASK.getStatus().state(), curr.getStatus().state()); - assertEquals(1, curr.getArtifacts().size()); - assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart) curr.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals(MINIMAL_TASK.id(), curr.id()); + assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); + assertEquals(MINIMAL_TASK.status().state(), curr.status().state()); + assertEquals(1, curr.artifacts().size()); + assertEquals(1, curr.artifacts().get(0).parts().size()); + assertEquals("text", ((TextPart) curr.artifacts().get(0).parts().get(0)).text()); curr = httpClient.tasks.get(2); - assertEquals(MINIMAL_TASK.getId(), curr.getId()); - assertEquals(MINIMAL_TASK.getContextId(), curr.getContextId()); - assertEquals(TaskState.COMPLETED, curr.getStatus().state()); - assertEquals(1, curr.getArtifacts().size()); - assertEquals(1, curr.getArtifacts().get(0).parts().size()); - assertEquals("text", ((TextPart) curr.getArtifacts().get(0).parts().get(0)).getText()); + assertEquals(MINIMAL_TASK.id(), curr.id()); + assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); + assertEquals(TaskState.COMPLETED, curr.status().state()); + assertEquals(1, curr.artifacts().size()); + assertEquals(1, curr.artifacts().get(0).parts().size()); + assertEquals("text", ((TextPart) curr.artifacts().get(0).parts().get(0)).text()); } @Test public void testOnResubscribeExistingTaskSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - queueManager.createOrTap(MINIMAL_TASK.getId()); + queueManager.createOrTap(MINIMAL_TASK.id()); agentExecutorExecute = (context, eventQueue) -> { // The only thing hitting the agent is the onMessageSend() and we should use the message @@ -833,13 +833,13 @@ public void testOnResubscribeExistingTaskSuccess() { //eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; - SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); Flow.Publisher response = handler.onSubscribeToTask(request, callContext); // We need to send some events in order for those to end up in the queue Message message = Message.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .role(Message.Role.AGENT) .parts(new TextPart("text")) .build(); @@ -892,24 +892,24 @@ public void onComplete() { public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - queueManager.createOrTap(MINIMAL_TASK.getId()); + queueManager.createOrTap(MINIMAL_TASK.id()); List events = List.of( TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .artifact(Artifact.builder() .artifactId("11") .parts(new TextPart("text")) .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .status(new TaskStatus(TaskState.WORKING)) .build()); - SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); Flow.Publisher response; try (MockedConstruction mocked = Mockito.mockConstruction( EventConsumer.class, @@ -965,7 +965,7 @@ public void onComplete() { public void testOnResubscribeNoExistingTaskError() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); Flow.Publisher response = handler.onSubscribeToTask(request, callContext); List results = new ArrayList<>(); @@ -1060,7 +1060,7 @@ public void testStreamingNotSupportedErrorOnResubscribeToTask() { AgentCard card = createAgentCard(false, true, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.getId())); + SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); Flow.Publisher response = handler.onSubscribeToTask(request, callContext); List results = new ArrayList<>(); @@ -1109,7 +1109,7 @@ public void testPushNotificationsNotSupportedError() { TaskPushNotificationConfig config = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), + MINIMAL_TASK.id(), PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("http://example.com") @@ -1133,7 +1133,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { taskStore.save(MINIMAL_TASK); GetTaskPushNotificationConfigRequest request - = new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + = new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.id())); GetTaskPushNotificationConfigResponse response = handler.getPushNotificationConfig(request, callContext); Assertions.assertNotNull(response.getError()); @@ -1153,7 +1153,7 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { TaskPushNotificationConfig config = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), + MINIMAL_TASK.id(), PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("http://example.com") @@ -1244,8 +1244,8 @@ public void testOnMessageSendErrorHandling() { taskStore.save(MINIMAL_TASK); Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); @@ -1344,19 +1344,19 @@ public void testListPushNotificationConfig() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .url("http://example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); TaskPushNotificationConfig result = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .url("http://example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(), ""); ListTaskPushNotificationConfigRequest listRequest - = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); assertEquals("111", listResponse.getId()); @@ -1375,15 +1375,15 @@ public void testListPushNotificationConfigNotSupported() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .url("http://example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); ListTaskPushNotificationConfigRequest listRequest - = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); @@ -1403,7 +1403,7 @@ public void testListPushNotificationConfigNoPushConfigStore() { }; ListTaskPushNotificationConfigRequest listRequest - = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); @@ -1420,7 +1420,7 @@ public void testListPushNotificationConfigTaskNotFound() { }; ListTaskPushNotificationConfigRequest listRequest - = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.getId())); + = new ListTaskPushNotificationConfigRequest("111", new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); ListTaskPushNotificationConfigResponse listResponse = handler.listPushNotificationConfig(listRequest, callContext); @@ -1439,15 +1439,15 @@ public void testDeletePushNotificationConfig() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .url("http://example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest - = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), MINIMAL_TASK.id())); DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); @@ -1467,15 +1467,15 @@ public void testDeletePushNotificationConfigNotSupported() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .url("http://example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest - = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), MINIMAL_TASK.id())); DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); @@ -1496,15 +1496,15 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.getId(), PushNotificationConfig.builder() + MINIMAL_TASK.id(), PushNotificationConfig.builder() .url("http://example.com") - .id(MINIMAL_TASK.getId()) + .id(MINIMAL_TASK.id()) .build(), "tenant"); SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest - = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.getId(), MINIMAL_TASK.getId())); + = new DeleteTaskPushNotificationConfigRequest("111", new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), MINIMAL_TASK.id())); DeleteTaskPushNotificationConfigResponse deleteResponse = handler.deletePushNotificationConfig(deleteRequest, callContext); @@ -1544,8 +1544,8 @@ public void testStreamingDoesNotBlockMainThread() throws Exception { }; Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.getId()) - .contextId(MINIMAL_TASK.getContextId()) + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) .build(); SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(message, null, null)); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index fd6ceaf8e..d4b326f17 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -26,17 +26,17 @@ public void testGetTaskSuccess() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.getId(), 0, "", callContext); + RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.id(), 0, "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); - Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id())); - response = handler.getTask(MINIMAL_TASK.getId(),2 , "",callContext); + response = handler.getTask(MINIMAL_TASK.id(),2 , "",callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); - Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id())); } @Test @@ -142,11 +142,11 @@ public void testCancelTaskSuccess() { taskUpdater.cancel(); }; - RestHandler.HTTPRestResponse response = handler.cancelTask(MINIMAL_TASK.getId(), "", callContext); + RestHandler.HTTPRestResponse response = handler.cancelTask(MINIMAL_TASK.id(), "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); - Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.getId())); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id())); } @Test @@ -230,9 +230,9 @@ public void testPushNotificationConfigSuccess() { } } } - }""".formatted(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); + }""".formatted(MINIMAL_TASK.id(), MINIMAL_TASK.id()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration( MINIMAL_TASK.getId(), requestBody, "", callContext); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration( MINIMAL_TASK.id(), requestBody, "", callContext); Assertions.assertEquals(201, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); @@ -251,9 +251,9 @@ public void testPushNotificationConfigNotSupported() { "url": "http://example.com" } } - """.formatted(MINIMAL_TASK.getId()); + """.formatted(MINIMAL_TASK.id()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), requestBody, "", callContext); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.id(), requestBody, "", callContext); Assertions.assertEquals(501, response.getStatusCode()); Assertions.assertTrue(response.getBody().contains("PushNotificationNotSupportedError")); @@ -278,12 +278,12 @@ public void testGetPushNotificationConfig() { } } } - }""".formatted(MINIMAL_TASK.getId(), MINIMAL_TASK.getId()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), createRequestBody, "", callContext); + }""".formatted(MINIMAL_TASK.id(), MINIMAL_TASK.id()); + RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.id(), createRequestBody, "", callContext); Assertions.assertEquals(201, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); // Now get it - response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", "", callContext); + response = handler.getTaskPushNotificationConfiguration(MINIMAL_TASK.id(), "default-config-id", "", callContext); Assertions.assertEquals(200, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); } @@ -292,7 +292,7 @@ public void testGetPushNotificationConfig() { public void testDeletePushNotificationConfig() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.getId(), "default-config-id", "", callContext); + RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.id(), "default-config-id", "", callContext); Assertions.assertEquals(204, response.getStatusCode()); } @@ -301,7 +301,7 @@ public void testListPushNotificationConfigs() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.getId(), "", callContext); + RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.id(), "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); From 5c29703fde28ab5fef69493e99faeba00d82aef4 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 17 Dec 2025 16:15:33 +0000 Subject: [PATCH 235/493] feat: Set current protocol version to 1.0 (#549) Update usage of protocol version to use a constant rather than hard coding --- README.md | 2 +- .../AuthenticationAuthorizationTest.java | 27 ++++++++++--------- .../java/io/a2a/client/ClientBuilderTest.java | 10 ++++--- .../jsonrpc/JSONRPCTransportTest.java | 8 +++--- .../transport/jsonrpc/JsonMessages.java | 22 ++++++++------- .../transport/rest/RestTransportTest.java | 24 +++++++++-------- .../cloud/CloudAgentCardProducer.java | 18 +++++++------ .../helloworld/AgentCardProducer.java | 4 ++- .../MultiInstanceReplicationAgentCards.java | 4 ++- .../ReplicationTestAgentCardProducer.java | 6 +++-- .../apps/quarkus/A2AServerRoutesTest.java | 5 ++-- .../AbstractA2ARequestHandlerTest.java | 13 ++++----- .../java/io/a2a/grpc/utils/ToProtoTest.java | 21 ++++++++------- spec/src/main/java/io/a2a/spec/AgentCard.java | 8 +++--- .../io/a2a/tck/server/AgentCardProducer.java | 4 ++- .../apps/common/AbstractA2AServerTest.java | 7 ++--- .../server/apps/common/AgentCardProducer.java | 5 ++-- 17 files changed, 106 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index b404d7af0..50a760b10 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ public class WeatherAgentCardProducer { .tags(Collections.singletonList("weather")) .examples(List.of("weather in LA, CA")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java index a9bc60aa5..0294b3dc4 100644 --- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -1,5 +1,18 @@ package io.a2a.client; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + import io.a2a.client.config.ClientConfig; import io.a2a.client.transport.grpc.GrpcTransport; import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; @@ -30,18 +43,6 @@ import org.junit.jupiter.api.Test; import org.mockserver.integration.ClientAndServer; -import java.io.IOException; -import java.util.Collections; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; - /** * Tests for handling HTTP 401 (Unauthorized) and 403 (Forbidden) responses * when the client sends streaming and non-streaming messages. @@ -89,7 +90,7 @@ public void setUp() { .description("Test skill") .tags(Collections.singletonList("test")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .supportedInterfaces(java.util.Arrays.asList( new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL), new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL), diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index 807eb5a43..8b9e4bba0 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -1,5 +1,10 @@ package io.a2a.client; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + +import java.util.Collections; +import java.util.List; + import io.a2a.client.config.ClientConfig; import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.client.transport.grpc.GrpcTransport; @@ -16,9 +21,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.Collections; -import java.util.List; - public class ClientBuilderTest { private AgentCard card = AgentCard.builder() @@ -40,7 +42,7 @@ public class ClientBuilderTest { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .supportedInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) .build(); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index dc9001871..d4e86cbc5 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -24,6 +24,7 @@ import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE; import static io.a2a.client.transport.jsonrpc.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -43,6 +44,7 @@ import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; +import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.DataPart; import io.a2a.spec.EventKind; import io.a2a.spec.FileContent; @@ -55,7 +57,6 @@ import io.a2a.spec.MessageSendParams; import io.a2a.spec.OpenIdConnectSecurityScheme; import io.a2a.spec.Part; -import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SecurityScheme; import io.a2a.spec.Task; @@ -66,7 +67,6 @@ import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; import io.a2a.util.Utils; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -417,7 +417,7 @@ public void testA2AClientGetAgentCard() throws Exception { assertEquals(outputModes, skills.get(1).outputModes()); assertFalse(agentCard.supportsExtendedAgentCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.9", agentCard.protocolVersion()); + assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.protocolVersion()); assertEquals("JSONRPC", agentCard.supportedInterfaces().get(0).protocolBinding()); List additionalInterfaces = agentCard.supportedInterfaces(); assertEquals(3, additionalInterfaces.size()); @@ -509,7 +509,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertEquals(List.of("extended"), skills.get(2).tags()); assertTrue(agentCard.supportsExtendedAgentCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals("0.2.5", agentCard.protocolVersion()); + assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.protocolVersion()); } @Test diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index d14d63b5c..3ad728e2d 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -1,14 +1,18 @@ package io.a2a.client.transport.jsonrpc; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + +import io.a2a.spec.AgentCard; + /** * Request and response messages used by the tests. These have been created following examples from * the A2A sample messages. */ public class JsonMessages { - static final String AGENT_CARD = """ + static final String AGENT_CARD = String.format(""" { - "protocolVersion": "0.2.9", + "protocolVersion": "%s", "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", "supportedInterfaces" : [ @@ -80,7 +84,7 @@ public class JsonMessages { "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" } ] - }"""; + }""", CURRENT_PROTOCOL_VERSION); static final String SEND_MESSAGE_TEST_REQUEST = """ { @@ -588,7 +592,7 @@ public class JsonMessages { } """; - static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = """ + static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = String.format(""" { "jsonrpc": "2.0", "id": "1", @@ -662,7 +666,7 @@ public class JsonMessages { } ], "supportsExtendedAgentCard": true, - "protocolVersion": "0.2.5", + "protocolVersion": "%s", "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdUI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", @@ -670,9 +674,9 @@ public class JsonMessages { } ] } - }"""; + }""", CURRENT_PROTOCOL_VERSION); - static final String AGENT_CARD_SUPPORTS_EXTENDED = """ + static final String AGENT_CARD_SUPPORTS_EXTENDED = String.format(""" { "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", @@ -737,6 +741,6 @@ public class JsonMessages { } ], "supportsExtendedAgentCard": true, - "protocolVersion": "1.0.0" - }"""; + "protocolVersion": "%s" + }""", CURRENT_PROTOCOL_VERSION); } diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 1976367f6..3fe2db40c 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -6,13 +6,14 @@ import static io.a2a.client.transport.rest.JsonRestMessages.GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static io.a2a.client.transport.rest.JsonRestMessages.GET_TASK_TEST_RESPONSE; import static io.a2a.client.transport.rest.JsonRestMessages.LIST_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; +import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST; import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_RESPONSE; import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_TEST_REQUEST; import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_TEST_RESPONSE; -import static io.a2a.client.transport.rest.JsonRestMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST; import static io.a2a.client.transport.rest.JsonRestMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.transport.rest.JsonRestMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static io.a2a.client.transport.rest.JsonRestMessages.TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -21,11 +22,21 @@ import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.logging.Logger; + import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; +import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; import io.a2a.spec.FilePart; @@ -38,7 +49,6 @@ import io.a2a.spec.MessageSendParams; import io.a2a.spec.Part; import io.a2a.spec.Part.Kind; -import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -47,14 +57,6 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import java.util.logging.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -86,7 +88,7 @@ public class RestTransportTest { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); @BeforeEach diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java index 69b7a9970..07c7aea04 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -1,19 +1,21 @@ package io.a2a.examples.cloud; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + +import java.util.Collections; +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Produces; -import jakarta.inject.Inject; - import org.eclipse.microprofile.config.inject.ConfigProperty; -import java.util.Collections; -import java.util.List; - /** * Producer for the cloud deployment example agent card. */ @@ -56,7 +58,7 @@ public AgentCard agentCard() { )) .build() )) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 83a08c8d1..66f1c7fc4 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -1,5 +1,7 @@ package io.a2a.examples.helloworld; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + import java.util.Collections; import java.util.List; @@ -41,7 +43,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java index eff9c5b39..9936ea4df 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java @@ -1,5 +1,7 @@ package io.a2a.extras.queuemanager.replicated.tests.multiinstance.common; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + import java.util.Collections; import java.util.List; @@ -46,7 +48,7 @@ public static AgentCard createAgentCard(int instanceNumber, int port) { .description("Fire-and-forget agent for testing replication") .tags(Collections.singletonList("test")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java index b28317735..7a0e9280e 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java @@ -1,13 +1,15 @@ package io.a2a.extras.queuemanager.replicated.tests; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + import java.util.List; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; import io.a2a.server.PublicAgentCard; -import io.a2a.spec.AgentCard; import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; import io.quarkus.arc.profile.IfBuildProfile; @@ -37,7 +39,7 @@ public AgentCard agentCard() { .skills(List.of()) .supportedInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) - .protocolVersion("0.2.5") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); } } \ No newline at end of file diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java index 4609118a0..8de9dd893 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -1,5 +1,6 @@ package io.a2a.server.apps.quarkus; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -29,10 +30,10 @@ import io.a2a.spec.GetTaskPushNotificationConfigRequest; import io.a2a.spec.GetTaskPushNotificationConfigResponse; import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.GetTaskResponse; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; import io.a2a.spec.SendStreamingMessageRequest; @@ -481,7 +482,7 @@ public void testGetAuthenticatedExtendedCard_MethodNameSetInContext() { .name("Test Agent") .description("Test agent description") .version("1.0.0") - .protocolVersion("1.0.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index b8f1f89ba..0020fe947 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -1,11 +1,14 @@ package io.a2a.server.requesthandlers; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -17,6 +20,8 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; @@ -31,18 +36,14 @@ import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; +import io.a2a.spec.Event; import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; -import io.a2a.spec.Event; import io.a2a.spec.TextPart; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; import io.quarkus.arc.profile.IfBuildProfile; -import java.util.Map; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -126,7 +127,7 @@ protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifi .defaultInputModes(new ArrayList<>()) .defaultOutputModes(new ArrayList<>()) .skills(new ArrayList<>()) - .protocolVersion("0.2.5"); + .protocolVersion(CURRENT_PROTOCOL_VERSION); return builder.build(); } diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index d863efc17..ec6d1cd86 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -2,22 +2,28 @@ import static io.a2a.grpc.Role.ROLE_AGENT; import static io.a2a.grpc.Role.ROLE_USER; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.time.OffsetDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import io.a2a.grpc.SendMessageConfiguration; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; +import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.HTTPAuthSecurityScheme; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; -import io.a2a.spec.AuthenticationInfo; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -26,11 +32,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; - -import java.time.OffsetDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Map; import org.junit.jupiter.api.Test; public class ToProtoTest { @@ -64,7 +65,7 @@ public void convertAgentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("1.0.0") + .protocolVersion("123") // Weird protool version on purpose to make sure non-default takes effect .build(); io.a2a.grpc.AgentCard result = ProtoUtils.ToProto.agentCard(agentCard); assertEquals("Hello World Agent", result.getName()); @@ -78,7 +79,7 @@ public void convertAgentCard() { assertEquals("text", result.getDefaultInputModes(0)); assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); - assertEquals("1.0.0", result.getProtocolVersion()); + assertEquals("123", result.getProtocolVersion()); agentCard = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") @@ -102,7 +103,7 @@ public void convertAgentCard() { // .iconUrl("http://example.com/icon.svg") .securitySchemes(Map.of("basic", HTTPAuthSecurityScheme.builder().scheme("basic").description("Basic Auth").build())) .security(List.of(Map.of("oauth", List.of("read")))) - .protocolVersion("1.0.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); result = ProtoUtils.ToProto.agentCard(agentCard); assertEquals("Hello World Agent", result.getName()); @@ -116,7 +117,7 @@ public void convertAgentCard() { assertEquals("text", result.getDefaultInputModes(0)); assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); - assertEquals("1.0.0", result.getProtocolVersion()); + assertEquals(CURRENT_PROTOCOL_VERSION, result.getProtocolVersion()); assertEquals(1, result.getSecurityCount()); assertEquals(1, result.getSecurity(0).getSchemesMap().size()); assertEquals(true, result.getSecurity(0).getSchemesMap().containsKey("oauth")); diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index 68a0251f6..d8ed1231a 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -37,7 +37,7 @@ * @param security list of security requirements for accessing the agent (optional) * @param iconUrl URL to an icon representing the agent (optional) * @param supportedInterfaces ordered list of protocol+URL interface combinations; first entry is preferred (required) - * @param protocolVersion the version of the A2A Protocol this agent implements (defaults to {@link #DEFAULT_PROTOCOL_VERSION}) + * @param protocolVersion the version of the A2A Protocol this agent implements (defaults to {@link #CURRENT_PROTOCOL_VERSION}) * @param signatures digital signatures verifying the authenticity of the agent card (optional) * @see AgentInterface * @see A2A Protocol Specification @@ -61,7 +61,7 @@ public record AgentCard( List signatures) { /** The default A2A Protocol version used when not explicitly specified. */ - public static final String DEFAULT_PROTOCOL_VERSION = "1.0.0"; + public static final String CURRENT_PROTOCOL_VERSION = "1.0"; /** * Compact constructor that validates required fields and sets defaults. @@ -95,7 +95,7 @@ public record AgentCard( Assert.checkNotNullParam("version", version); if (protocolVersion == null) { - protocolVersion = DEFAULT_PROTOCOL_VERSION; + protocolVersion = CURRENT_PROTOCOL_VERSION; } } @@ -398,7 +398,7 @@ public Builder supportedInterfaces(List supportedInterfaces) { /** * Sets the version of the A2A Protocol this agent implements. *

    - * If not set, defaults to {@link AgentCard#DEFAULT_PROTOCOL_VERSION}. + * If not set, defaults to {@link AgentCard#CURRENT_PROTOCOL_VERSION}. * * @param protocolVersion the protocol version string * @return this builder for method chaining diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index cdb00343b..d5b5d6ffd 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -1,5 +1,7 @@ package io.a2a.tck.server; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + import java.util.Collections; import java.util.List; @@ -48,7 +50,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("0.3.0") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 89c6de9b8..15b41a9c2 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1,5 +1,6 @@ package io.a2a.server.apps.common; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -30,7 +31,6 @@ import jakarta.ws.rs.core.MediaType; -import io.a2a.json.JsonProcessingException; import io.a2a.client.Client; import io.a2a.client.ClientBuilder; import io.a2a.client.ClientEvent; @@ -40,6 +40,8 @@ import io.a2a.client.config.ClientConfig; import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; @@ -74,7 +76,6 @@ import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.json.JsonUtil; import io.a2a.util.Utils; import io.restassured.RestAssured; import io.restassured.config.ObjectMapperConfig; @@ -1985,7 +1986,7 @@ private AgentCard createTestAgentCard() { .defaultOutputModes(List.of("text")) .skills(List.of()) .supportedInterfaces(List.of(new AgentInterface(getTransportProtocol(), getTransportUrl()))) - .protocolVersion("0.2.5") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .build(); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index 4ed3261a4..b8b9b376f 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -1,5 +1,7 @@ package io.a2a.server.apps.common; +import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; + import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -14,7 +16,6 @@ import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; -import io.a2a.spec.TransportProtocol; import io.quarkus.arc.profile.IfBuildProfile; import org.junit.jupiter.api.Assertions; @@ -44,7 +45,7 @@ public AgentCard agentCard() { .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(new ArrayList<>()) - .protocolVersion("0.2.5") + .protocolVersion(CURRENT_PROTOCOL_VERSION) .supportedInterfaces(Collections.singletonList(new AgentInterface(preferredTransport, "http://localhost:" + port))); return builder.build(); } From d1b47c6c85cfc1b013e5eddb75a0f6e3c25c3393 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Wed, 17 Dec 2025 18:26:33 +0100 Subject: [PATCH 236/493] feat: fixing issue 547 by using the tenant from the agent card on the (#550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit client side * tenant can be set as a request parameter. * the tenant defined in the agent card is the default one. * the tenant is validated to avoid security issues. # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes #547 🦕 Signed-off-by: Emmanuel Hugonnet --- client/base/src/main/java/io/a2a/A2A.java | 2 +- .../java/io/a2a/client/ClientBuilder.java | 2 +- .../client/transport/grpc/GrpcTransport.java | 99 ++++++--- .../transport/grpc/GrpcTransportProvider.java | 7 +- .../transport/jsonrpc/JSONRPCTransport.java | 41 ++-- .../jsonrpc/JSONRPCTransportProvider.java | 5 +- .../jsonrpc/JSONRPCTransportTest.java | 6 +- .../client/transport/rest/RestTransport.java | 51 ++--- .../transport/rest/RestTransportProvider.java | 5 +- .../spi/ClientTransportProvider.java | 6 +- ...otificationConfigStoreIntegrationTest.java | 2 +- .../io/a2a/client/http/A2ACardResolver.java | 67 ++++-- .../a2a/client/http/A2ACardResolverTest.java | 10 +- spec/src/main/java/io/a2a/util/Utils.java | 118 ++++++++++- spec/src/test/java/io/a2a/util/UtilsTest.java | 193 ++++++++++++++++++ .../apps/common/AbstractA2AServerTest.java | 18 +- 16 files changed, 493 insertions(+), 139 deletions(-) create mode 100644 spec/src/test/java/io/a2a/util/UtilsTest.java diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index a0ccd5353..086db1c6a 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -182,7 +182,7 @@ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, M * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema */ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl, String relativeCardPath, Map authHeaders) throws A2AClientError, A2AClientJSONError { - A2ACardResolver resolver = new A2ACardResolver(httpClient, agentUrl, relativeCardPath, authHeaders); + A2ACardResolver resolver = new A2ACardResolver(httpClient, agentUrl, "", relativeCardPath, authHeaders); return resolver.getAgentCard(); } } diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java index c62b49ca6..164d83aeb 100644 --- a/client/base/src/main/java/io/a2a/client/ClientBuilder.java +++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java @@ -105,7 +105,7 @@ private ClientTransport buildClientTransport() throws A2AClientException { throw new A2AClientException("Missing required TransportConfig for " + agentInterface.protocolBinding()); } - return clientTransportProvider.create(clientTransportConfig, agentCard, agentInterface.url()); + return clientTransportProvider.create(clientTransportConfig, agentCard, agentInterface); } private Map getServerPreferredTransports() throws A2AClientException { diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 7db87df44..101c599d1 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -62,25 +62,38 @@ public class GrpcTransport implements ClientTransport { private final A2AServiceStub asyncStub; private final @Nullable List interceptors; private AgentCard agentCard; + private final String agentTenant; public GrpcTransport(Channel channel, AgentCard agentCard) { - this(channel, agentCard, null); + this(channel, agentCard, "", null); } - public GrpcTransport(Channel channel, AgentCard agentCard, @Nullable List interceptors) { + public GrpcTransport(Channel channel, AgentCard agentCard, @Nullable String agentTenant, @Nullable List interceptors) { checkNotNullParam("channel", channel); checkNotNullParam("agentCard", agentCard); this.asyncStub = A2AServiceGrpc.newStub(channel); this.blockingStub = A2AServiceGrpc.newBlockingV2Stub(channel); this.agentCard = agentCard; this.interceptors = interceptors; + this.agentTenant = agentTenant == null || agentTenant.isBlank() ? "" : agentTenant; + } + + /** + * Resolves the tenant to use, preferring the request tenant over the agent default. + * + * @param requestTenant the tenant from the request, may be null or blank + * @return the tenant to use (request tenant if provided, otherwise agent default) + */ + private String resolveTenant(@Nullable String requestTenant) { + return (requestTenant == null || requestTenant.isBlank()) ? agentTenant : requestTenant; } @Override public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); + MessageSendParams tenantRequest = createRequestWithTenant(request); - io.a2a.grpc.SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest(request, context); + io.a2a.grpc.SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest(tenantRequest, context); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, sendMessageRequest, agentCard, context); @@ -101,10 +114,12 @@ public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallCont @Override public void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, - Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); - io.a2a.grpc.SendMessageRequest grpcRequest = createGrpcSendMessageRequest(request, context); + MessageSendParams tenantRequest = createRequestWithTenant(request); + + io.a2a.grpc.SendMessageRequest grpcRequest = createGrpcSendMessageRequest(tenantRequest, context); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD, grpcRequest, agentCard, context); StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); @@ -120,14 +135,12 @@ public void sendMessageStreaming(MessageSendParams request, Consumer listTaskPushNotificationConfigurations( io.a2a.grpc.ListTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder() .setParent("tasks/" + request.id()) + .setTenant(resolveTenant(request.tenant())) .build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, grpcRequest, agentCard, context); @@ -271,11 +289,12 @@ public List listTaskPushNotificationConfigurations( @Override public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, - @Nullable ClientCallContext context) throws A2AClientException { + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.DeleteTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder() .setName(getTaskPushNotificationConfigName(request.id(), request.pushNotificationConfigId())) + .setTenant(resolveTenant(request.tenant())) .build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, grpcRequest, agentCard, context); @@ -290,11 +309,12 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC @Override public void resubscribe(TaskIdParams request, Consumer eventConsumer, - Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { + Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); io.a2a.grpc.SubscribeToTaskRequest grpcRequest = io.a2a.grpc.SubscribeToTaskRequest.newBuilder() + .setTenant(resolveTenant(request.tenant())) .setName("tasks/" + request.id()) .build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD, @@ -310,6 +330,20 @@ public void resubscribe(TaskIdParams request, Consumer event } } + /** + * Ensure tenant is set, using agent default if not provided in request + * @param request the initial request. + * @return the updated request with the tenant set. + */ + private MessageSendParams createRequestWithTenant(MessageSendParams request) { + return MessageSendParams.builder() + .configuration(request.configuration()) + .message(request.message()) + .metadata(request.metadata()) + .tenant(resolveTenant(request.tenant())) + .build(); + } + @Override public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException { // TODO: Determine how to handle retrieving the authenticated extended agent card @@ -327,20 +361,21 @@ private io.a2a.grpc.SendMessageRequest createGrpcSendMessageRequest(MessageSendP /** * Creates gRPC metadata from ClientCallContext headers. * Extracts headers like X-A2A-Extensions and sets them as gRPC metadata. + * * @param context the client call context containing headers, may be null * @param payloadAndHeaders the payload and headers wrapper, may be null * @return the gRPC metadata */ private Metadata createGrpcMetadata(@Nullable ClientCallContext context, @Nullable PayloadAndHeaders payloadAndHeaders) { Metadata metadata = new Metadata(); - + if (context != null && context.getHeaders() != null) { // Set X-A2A-Extensions header if present String extensionsHeader = context.getHeaders().get(A2AHeaders.X_A2A_EXTENSIONS); if (extensionsHeader != null) { metadata.put(EXTENSIONS_KEY, extensionsHeader); } - + // Add other headers as needed in the future // For now, we only handle X-A2A-Extensions } @@ -349,7 +384,7 @@ private Metadata createGrpcMetadata(@Nullable ClientCallContext context, @Nullab for (Map.Entry headerEntry : payloadAndHeaders.getHeaders().entrySet()) { String headerName = headerEntry.getKey(); String headerValue = headerEntry.getValue(); - + if (headerValue != null) { // Use static key for common Authorization header, create dynamic keys for others if (AuthInterceptor.AUTHORIZATION.equals(headerName)) { @@ -362,19 +397,19 @@ private Metadata createGrpcMetadata(@Nullable ClientCallContext context, @Nullab } } } - + return metadata; } /** * Creates a blocking stub with metadata attached from the ClientCallContext. * - * @param context the client call context + * @param context the client call context * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors * @return blocking stub with metadata interceptor */ private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(@Nullable ClientCallContext context, - PayloadAndHeaders payloadAndHeaders) { + PayloadAndHeaders payloadAndHeaders) { Metadata metadata = createGrpcMetadata(context, payloadAndHeaders); return blockingStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); } @@ -382,12 +417,12 @@ private A2AServiceBlockingV2Stub createBlockingStubWithMetadata(@Nullable Client /** * Creates an async stub with metadata attached from the ClientCallContext. * - * @param context the client call context + * @param context the client call context * @param payloadAndHeaders the payloadAndHeaders after applying any interceptors * @return async stub with metadata interceptor */ private A2AServiceStub createAsyncStubWithMetadata(@Nullable ClientCallContext context, - PayloadAndHeaders payloadAndHeaders) { + PayloadAndHeaders payloadAndHeaders) { Metadata metadata = createGrpcMetadata(context, payloadAndHeaders); return asyncStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); } @@ -411,10 +446,10 @@ private String getTaskPushNotificationConfigName(String taskId, @Nullable String } private PayloadAndHeaders applyInterceptors(String methodName, Object payload, - AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { + AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, clientCallContext != null ? clientCallContext.getHeaders() : null); - if (interceptors != null && ! interceptors.isEmpty()) { + if (interceptors != null && !interceptors.isEmpty()) { for (ClientCallInterceptor interceptor : interceptors) { payloadAndHeaders = interceptor.intercept(methodName, payloadAndHeaders.getPayload(), payloadAndHeaders.getHeaders(), agentCard, clientCallContext); @@ -423,4 +458,4 @@ private PayloadAndHeaders applyInterceptors(String methodName, Object payload, return payloadAndHeaders; } -} \ No newline at end of file +} diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java index f087ac5d1..d331d0874 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportProvider.java @@ -3,6 +3,7 @@ import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; import io.grpc.Channel; @@ -12,12 +13,12 @@ public class GrpcTransportProvider implements ClientTransportProvider { @Override - public GrpcTransport create(GrpcTransportConfig grpcTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { + public GrpcTransport create(GrpcTransportConfig grpcTransportConfig, AgentCard agentCard, AgentInterface agentInterface) throws A2AClientException { // not making use of the interceptors for gRPC for now - Channel channel = grpcTransportConfig.getChannelFactory().apply(agentUrl); + Channel channel = grpcTransportConfig.getChannelFactory().apply(agentInterface.url()); if (channel != null) { - return new GrpcTransport(channel, agentCard, grpcTransportConfig.getInterceptors()); + return new GrpcTransport(channel, agentCard, agentInterface.tenant(), grpcTransportConfig.getInterceptors()); } throw new A2AClientException("Missing required GrpcTransportConfig"); diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 06172be88..0a453bff4 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -25,6 +25,7 @@ import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -66,13 +67,13 @@ public class JSONRPCTransport implements ClientTransport { private final A2AHttpClient httpClient; - private final String agentUrl; + private final AgentInterface agentInterface; private final @Nullable List interceptors; private @Nullable AgentCard agentCard; private boolean needsExtendedCard = false; public JSONRPCTransport(String agentUrl) { - this(null, null, agentUrl, null); + this(null, null, new AgentInterface("JSONRPC", agentUrl), null); } public JSONRPCTransport(AgentCard agentCard) { @@ -80,10 +81,10 @@ public JSONRPCTransport(AgentCard agentCard) { } public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard agentCard, - String agentUrl, @Nullable List interceptors) { + AgentInterface agentInterface, @Nullable List interceptors) { this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; this.agentCard = agentCard; - this.agentUrl = agentUrl; + this.agentInterface = agentInterface; this.interceptors = interceptors; this.needsExtendedCard = agentCard == null || agentCard.supportsExtendedAgentCard(); } @@ -95,7 +96,7 @@ public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallCont agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders, SendMessageRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SendMessageRequest.METHOD); SendMessageResponse response = unmarshalResponse(httpResponseBody, SendMessageRequest.METHOD); return response.getResult(); } catch (A2AClientException e) { @@ -117,7 +118,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), @@ -141,7 +142,7 @@ public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders, GetTaskRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, GetTaskRequest.METHOD); GetTaskResponse response = unmarshalResponse(httpResponseBody, GetTaskRequest.METHOD); return response.getResult(); } catch (A2AClientException e) { @@ -158,7 +159,7 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders, CancelTaskRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, CancelTaskRequest.METHOD); CancelTaskResponse response = unmarshalResponse(httpResponseBody, CancelTaskRequest.METHOD); return response.getResult(); } catch (A2AClientException e) { @@ -174,7 +175,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTasksRequest.METHOD, ProtoUtils.ToProto.listTasksParams(request), agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders, ListTasksRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, ListTasksRequest.METHOD); ListTasksResponse response = unmarshalResponse(httpResponseBody, ListTasksRequest.METHOD); return response.getResult(); } catch (IOException | InterruptedException | JsonProcessingException e) { @@ -190,7 +191,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN ProtoUtils.ToProto.setTaskPushNotificationConfigRequest(request), agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders, SetTaskPushNotificationConfigRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SetTaskPushNotificationConfigRequest.METHOD); SetTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, SetTaskPushNotificationConfigRequest.METHOD); return response.getResult(); @@ -209,7 +210,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu ProtoUtils.ToProto.getTaskPushNotificationConfigRequest(request), agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders,GetTaskPushNotificationConfigRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders,GetTaskPushNotificationConfigRequest.METHOD); GetTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, GetTaskPushNotificationConfigRequest.METHOD); return response.getResult(); @@ -229,7 +230,7 @@ public List listTaskPushNotificationConfigurations( ProtoUtils.ToProto.listTaskPushNotificationConfigRequest(request), agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders, ListTaskPushNotificationConfigRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, ListTaskPushNotificationConfigRequest.METHOD); ListTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, ListTaskPushNotificationConfigRequest.METHOD); return response.getResult(); @@ -248,7 +249,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC ProtoUtils.ToProto.deleteTaskPushNotificationConfigRequest(request), agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders,DeleteTaskPushNotificationConfigRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders,DeleteTaskPushNotificationConfigRequest.METHOD); DeleteTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, DeleteTaskPushNotificationConfigRequest.METHOD); // Response validated (no error), but no result to return } catch (A2AClientException e) { @@ -271,7 +272,7 @@ public void resubscribe(TaskIdParams request, Consumer event SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { - A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders,SubscribeToTaskRequest.METHOD); + A2AHttpClient.PostBuilder builder = createPostBuilder(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders,SubscribeToTaskRequest.METHOD); ref.set(builder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), @@ -293,7 +294,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli A2ACardResolver resolver; try { if (agentCard == null) { - resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context)); + resolver = new A2ACardResolver(httpClient, agentInterface.url(), agentInterface.tenant(), null, getHttpHeaders(context)); agentCard = resolver.getAgentCard(); needsExtendedCard = agentCard.supportsExtendedAgentCard(); } @@ -309,7 +310,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli ProtoUtils.ToProto.extendedAgentCard(getExtendedAgentCardRequest), agentCard, context); try { - String httpResponseBody = sendPostRequest(payloadAndHeaders,GetAuthenticatedExtendedCardRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, ""), payloadAndHeaders,GetAuthenticatedExtendedCardRequest.METHOD); GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody, GetAuthenticatedExtendedCardRequest.METHOD); agentCard = response.getResult(); @@ -340,8 +341,8 @@ private PayloadAndHeaders applyInterceptors(String methodName, @Nullable Object return payloadAndHeaders; } - private String sendPostRequest(PayloadAndHeaders payloadAndHeaders, String method) throws IOException, InterruptedException, JsonProcessingException { - A2AHttpClient.PostBuilder builder = createPostBuilder(payloadAndHeaders,method); + private String sendPostRequest(String url, PayloadAndHeaders payloadAndHeaders, String method) throws IOException, InterruptedException, JsonProcessingException { + A2AHttpClient.PostBuilder builder = createPostBuilder(url, payloadAndHeaders,method); A2AHttpResponse response = builder.post(); if (!response.success()) { throw new IOException("Request failed " + response.status()); @@ -349,9 +350,9 @@ private String sendPostRequest(PayloadAndHeaders payloadAndHeaders, String metho return response.body(); } - private A2AHttpClient.PostBuilder createPostBuilder(PayloadAndHeaders payloadAndHeaders, String method) throws JsonProcessingException { + private A2AHttpClient.PostBuilder createPostBuilder(String url, PayloadAndHeaders payloadAndHeaders, String method) throws JsonProcessingException { A2AHttpClient.PostBuilder postBuilder = httpClient.createPost() - .url(agentUrl) + .url(url) .addHeader("Content-Type", "application/json") .body(JSONRPCUtils.toJsonRPCRequest(null, method, (MessageOrBuilder) payloadAndHeaders.getPayload())); diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java index 6bec0fa21..5e9266a50 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -4,18 +4,19 @@ import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; import org.jspecify.annotations.Nullable; public class JSONRPCTransportProvider implements ClientTransportProvider { @Override - public JSONRPCTransport create(@Nullable JSONRPCTransportConfig clientTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { + public JSONRPCTransport create(@Nullable JSONRPCTransportConfig clientTransportConfig, AgentCard agentCard, AgentInterface agentInterface) throws A2AClientException { JSONRPCTransportConfig currentClientTransportConfig = clientTransportConfig; if (currentClientTransportConfig == null) { currentClientTransportConfig = new JSONRPCTransportConfig(new JdkA2AHttpClient()); } - return new JSONRPCTransport(currentClientTransportConfig.getHttpClient(), agentCard, agentUrl, currentClientTransportConfig.getInterceptors()); + return new JSONRPCTransport(currentClientTransportConfig.getHttpClient(), agentCard, agentInterface, currentClientTransportConfig.getInterceptors()); } @Override diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index d4e86cbc5..d949c91b9 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -342,7 +342,7 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { .url("https://example.com/callback") .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), null)) - .build(), "tenant"), null); + .build(), ""), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); @@ -369,7 +369,7 @@ public void testA2AClientGetAgentCard() throws Exception { AgentCard agentCard = client.getAgentCard(null); assertEquals("GeoSpatial Route Planner Agent", agentCard.name()); assertEquals("Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", agentCard.description()); - assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard)); + assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard).url()); assertEquals("Example Geo Services Inc.", agentCard.provider().organization()); assertEquals("https://www.examplegeoservices.com", agentCard.provider().url()); assertEquals("1.2.0", agentCard.version()); @@ -457,7 +457,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { AgentCard agentCard = client.getAgentCard(null); assertEquals("GeoSpatial Route Planner Agent Extended", agentCard.name()); assertEquals("Extended description", agentCard.description()); - assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard)); + assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard).url()); assertEquals("Example Geo Services Inc.", agentCard.provider().organization()); assertEquals("https://www.examplegeoservices.com", agentCard.provider().url()); assertEquals("1.2.0", agentCard.version()); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 82c17a244..41614744c 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -31,6 +31,7 @@ import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; @@ -60,7 +61,7 @@ public class RestTransport implements ClientTransport { private static final Logger log = Logger.getLogger(RestTransport.class.getName()); private final A2AHttpClient httpClient; - private final String agentUrl; + private final AgentInterface agentInterface; private @Nullable final List interceptors; private AgentCard agentCard; private boolean needsExtendedCard = false; @@ -70,10 +71,10 @@ public RestTransport(AgentCard agentCard) { } public RestTransport(@Nullable A2AHttpClient httpClient, AgentCard agentCard, - String agentUrl, @Nullable List interceptors) { + AgentInterface agentInterface, @Nullable List interceptors) { this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; this.agentCard = agentCard; - this.agentUrl = agentUrl.endsWith("/") ? agentUrl.substring(0, agentUrl.length() - 1) : agentUrl; + this.agentInterface = agentInterface; this.interceptors = interceptors; } @@ -83,7 +84,7 @@ public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable Clie io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, builder, agentCard, context); try { - String httpResponseBody = sendPostRequest(buildBaseUrl(messageSendParams.tenant()) + "/message:send", payloadAndHeaders); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, messageSendParams.tenant()) + "/message:send", payloadAndHeaders); io.a2a.grpc.SendMessageResponse.Builder responseBuilder = io.a2a.grpc.SendMessageResponse.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); if (responseBuilder.hasMsg()) { @@ -111,7 +112,7 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); try { - A2AHttpClient.PostBuilder postBuilder = createPostBuilder(buildBaseUrl(messageSendParams.tenant()) + "/message:stream", payloadAndHeaders); + A2AHttpClient.PostBuilder postBuilder = createPostBuilder(Utils.buildBaseUrl(agentInterface, messageSendParams.tenant()) + "/message:stream", payloadAndHeaders); ref.set(postBuilder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), @@ -135,7 +136,7 @@ public Task getTask(TaskQueryParams taskQueryParams, @Nullable ClientCallContext PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, builder, agentCard, context); try { - StringBuilder url = new StringBuilder(buildBaseUrl(taskQueryParams.tenant())); + StringBuilder url = new StringBuilder(Utils.buildBaseUrl(agentInterface, taskQueryParams.tenant())); if (taskQueryParams.historyLength() != null && taskQueryParams.historyLength() > 0) { url.append(String.format("/tasks/%1s?historyLength=%2d", taskQueryParams.id(), taskQueryParams.historyLength())); } else { @@ -170,7 +171,7 @@ public Task cancelTask(TaskIdParams taskIdParams, @Nullable ClientCallContext co PayloadAndHeaders payloadAndHeaders = applyInterceptors(CancelTaskRequest.METHOD, builder, agentCard, context); try { - String httpResponseBody = sendPostRequest(buildBaseUrl(taskIdParams.tenant()) + String.format("/tasks/%1s:cancel", taskIdParams.id()), payloadAndHeaders); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, taskIdParams.tenant()) + String.format("/tasks/%1s:cancel", taskIdParams.id()), payloadAndHeaders); io.a2a.grpc.Task.Builder responseBuilder = io.a2a.grpc.Task.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); return ProtoUtils.FromProto.task(responseBuilder); @@ -212,7 +213,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo try { // Build query string - StringBuilder urlBuilder = new StringBuilder(buildBaseUrl(request.tenant())); + StringBuilder urlBuilder = new StringBuilder(Utils.buildBaseUrl(agentInterface, request.tenant())); urlBuilder.append("/tasks"); String queryParams = buildListTasksQueryString(request); if (!queryParams.isEmpty()) { @@ -246,25 +247,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo } } - private String buildBaseUrl(@Nullable String tenant) { - StringBuilder urlBuilder = new StringBuilder(agentUrl); - urlBuilder.append(extractTenant(tenant)); - return urlBuilder.toString(); - } - - private String extractTenant(@Nullable String tenant) { - String tenantPath = tenant; - if (tenantPath == null || tenantPath.isBlank()) { - return ""; - } - if(! tenantPath.startsWith("/")){ - tenantPath = '/' + tenantPath; - } - if(tenantPath.endsWith("/")){ - tenantPath = tenantPath.substring(0, tenantPath.length() -1); - } - return tenantPath; - } + private String buildListTasksQueryString(ListTasksParams request) { java.util.List queryParts = new java.util.ArrayList<>(); if (request.contextId() != null) { @@ -300,7 +283,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN } PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String httpResponseBody = sendPostRequest(buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.taskId()), payloadAndHeaders); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.taskId()), payloadAndHeaders); io.a2a.grpc.TaskPushNotificationConfig.Builder responseBuilder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); return ProtoUtils.FromProto.taskPushNotificationConfig(responseBuilder); @@ -316,7 +299,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu checkNotNullParam("request", request); io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder(); - StringBuilder url = new StringBuilder(buildBaseUrl(request.tenant())); + StringBuilder url = new StringBuilder(Utils.buildBaseUrl(agentInterface, request.tenant())); String configId = request.pushNotificationConfigId(); if (configId != null && !configId.isEmpty()) { builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), configId)); @@ -359,7 +342,7 @@ public List listTaskPushNotificationConfigurations(L PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.id()); + String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.id()); A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { @@ -388,7 +371,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); try { - String url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); + String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); A2AHttpClient.DeleteBuilder deleteBuilder = httpClient.createDelete().url(url); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { @@ -417,7 +400,7 @@ public void resubscribe(TaskIdParams request, Consumer event AtomicReference> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); try { - String url = buildBaseUrl(request.tenant()) + String.format("/tasks/%1s:subscribe", request.id()); + String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s:subscribe", request.id()); A2AHttpClient.PostBuilder postBuilder = createPostBuilder(url, payloadAndHeaders); ref.set(postBuilder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), @@ -439,7 +422,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli A2ACardResolver resolver; try { if (agentCard == null) { - resolver = new A2ACardResolver(httpClient, agentUrl, null, getHttpHeaders(context)); + resolver = new A2ACardResolver(httpClient, agentInterface.url(), agentInterface.tenant(), null, getHttpHeaders(context)); agentCard = resolver.getAgentCard(); needsExtendedCard = agentCard.supportsExtendedAgentCard(); } @@ -448,7 +431,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli } PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, null, agentCard, context); - String url = buildBaseUrl("") + String.format("/extendedAgentCard"); + String url = Utils.buildBaseUrl(agentInterface, "") + "/extendedAgentCard"; A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); if (payloadAndHeaders.getHeaders() != null) { for (Map.Entry entry : payloadAndHeaders.getHeaders().entrySet()) { diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java index 99d155968..5b5af20c9 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java @@ -4,6 +4,7 @@ import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; public class RestTransportProvider implements ClientTransportProvider { @@ -14,12 +15,12 @@ public String getTransportProtocol() { } @Override - public RestTransport create(RestTransportConfig clientTransportConfig, AgentCard agentCard, String agentUrl) throws A2AClientException { + public RestTransport create(RestTransportConfig clientTransportConfig, AgentCard agentCard, AgentInterface agentInterface) throws A2AClientException { RestTransportConfig transportConfig = clientTransportConfig; if (transportConfig == null) { transportConfig = new RestTransportConfig(new JdkA2AHttpClient()); } - return new RestTransport(clientTransportConfig.getHttpClient(), agentCard, agentUrl, transportConfig.getInterceptors()); + return new RestTransport(clientTransportConfig.getHttpClient(), agentCard, agentInterface, transportConfig.getInterceptors()); } @Override diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java index 4026bcc8e..a6224c8ed 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportProvider.java @@ -2,6 +2,7 @@ import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; /** * Client transport provider interface. @@ -12,12 +13,11 @@ public interface ClientTransportProvider authHeaders) throws A2AClientError { + checkNotNullParam("httpClient", httpClient); + checkNotNullParam("baseUrl", baseUrl); + this.httpClient = httpClient; - String effectiveAgentCardPath = agentCardPath == null || agentCardPath.isEmpty() ? DEFAULT_AGENT_CARD_PATH : agentCardPath; + String effectiveAgentCardPath = (agentCardPath == null || agentCardPath.isEmpty()) ? DEFAULT_AGENT_CARD_PATH : agentCardPath; try { - this.url = new URI(baseUrl).resolve(effectiveAgentCardPath).toString(); + this.url = new URI(io.a2a.util.Utils.buildBaseUrl(new AgentInterface("JSONRPC", baseUrl, ""), tenant)).resolve(effectiveAgentCardPath).toString(); } catch (URISyntaxException e) { throw new A2AClientError("Invalid agent URL", e); } diff --git a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java index 1621544b1..242001d10 100644 --- a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java +++ b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java @@ -28,13 +28,13 @@ public void testConstructorStripsSlashes() throws Exception { TestHttpClient client = new TestHttpClient(); client.body = JsonMessages.AGENT_CARD; - A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/"); + A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/", ""); AgentCard card = resolver.getAgentCard(); assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); - resolver = new A2ACardResolver(client, "http://example.com"); + resolver = new A2ACardResolver(client, "http://example.com", ""); card = resolver.getAgentCard(); assertEquals("http://example.com" + AGENT_CARD_PATH, client.url); @@ -70,7 +70,7 @@ public void testGetAgentCardSuccess() throws Exception { TestHttpClient client = new TestHttpClient(); client.body = JsonMessages.AGENT_CARD; - A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/"); + A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/", ""); AgentCard card = resolver.getAgentCard(); AgentCard expectedCard = unmarshalFrom(JsonMessages.AGENT_CARD); @@ -95,7 +95,7 @@ public void testGetAgentCardJsonDecodeError() throws Exception { TestHttpClient client = new TestHttpClient(); client.body = "X" + JsonMessages.AGENT_CARD; - A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/"); + A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/", ""); boolean success = false; try { @@ -112,7 +112,7 @@ public void testGetAgentCardRequestError() throws Exception { TestHttpClient client = new TestHttpClient(); client.status = 503; - A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/"); + A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/", ""); String msg = null; try { diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 24b2e650d..038629397 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -8,14 +8,16 @@ import io.a2a.json.JsonUtil; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.Artifact; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.Part; import java.util.logging.Logger; +import org.jspecify.annotations.Nullable; - +import static io.a2a.util.Assert.checkNotNullParam; /** * Utility class providing common helper methods for A2A Protocol operations. @@ -37,7 +39,6 @@ */ public class Utils { - private static final Logger log = Logger.getLogger(Utils.class.getName()); /** @@ -179,11 +180,120 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event * @return the first defined URL in the supported interaces of the agent card. * @throws A2AClientException if no server interface is available in the AgentCard */ - public static String getFavoriteInterface(AgentCard agentCard) throws A2AClientException { + public static AgentInterface getFavoriteInterface(AgentCard agentCard) throws A2AClientException { if (agentCard.supportedInterfaces() == null || agentCard.supportedInterfaces().isEmpty()) { throw new A2AClientException("No server interface available in the AgentCard"); } - return agentCard.supportedInterfaces().get(0).url(); + return agentCard.supportedInterfaces().get(0); } + /** + * Validates that a tenant path is safe and well-formed. + *

    + * This method performs security validation to prevent: + *

      + *
    • Path traversal attacks (e.g., {@code ../../admin})
    • + *
    • Excessive length (max 256 characters)
    • + *
    • Invalid characters (only allows {@code /a-zA-Z0-9_-.})
    • + *
    + * + * @param tenant the tenant path to validate + * @throws IllegalArgumentException if the tenant is invalid or unsafe + */ + private static void validateTenant(String tenant) { + if (tenant.isEmpty()) { + return; // Empty string is valid (no tenant) + } + + if (tenant.length() > 256) { + throw new IllegalArgumentException("Tenant path exceeds maximum length of 256 characters"); + } + + if (tenant.contains("..")) { + throw new IllegalArgumentException("Tenant path contains invalid '..' sequence (path traversal attempt)"); + } + + if (tenant.contains("//")) { + throw new IllegalArgumentException("Tenant path contains invalid '//' sequence"); + } + + if (!tenant.matches("^[/a-zA-Z0-9_.\\-]+$")) { + throw new IllegalArgumentException("Tenant path contains invalid characters. Only /a-zA-Z0-9_-. are allowed"); + } + } + + /** + * Extracts and normalizes a tenant path, using the agent's default tenant if no override is provided. + *

    + * This method normalizes tenant paths by ensuring they: + *

      + *
    • Start with a forward slash ({@code /})
    • + *
    • Do not end with a forward slash (unless it's just {@code /})
    • + *
    • Are validated for security (no path traversal, length limits, valid characters)
    • + *
    + *

    + * If the provided {@code tenant} parameter is null or blank, the {@code agentTenant} is returned instead. + * + * @param agentTenant the default tenant from the agent card, must not be null + * @param tenant the tenant override from the request, may be null or blank + * @return the normalized tenant path + * @throws IllegalArgumentException if the tenant is invalid or unsafe + */ + private static String extractTenant(String agentTenant, @Nullable String tenant) { + checkNotNullParam("agentTenant", agentTenant); + + String tenantPath = tenant; + if (tenantPath == null || tenantPath.isBlank()) { + return agentTenant; + } + + // Normalize slashes + if (!tenantPath.startsWith("/")) { + tenantPath = '/' + tenantPath; + } + if (tenantPath.endsWith("/") && tenantPath.length() > 1) { + tenantPath = tenantPath.substring(0, tenantPath.length() - 1); + } + + // Validate for security + validateTenant(tenantPath); + + return tenantPath; + } + + /** + * Builds a base URL for A2A operations by combining the agent's URL with a tenant path. + *

    + * This method: + *

      + *
    • Uses the tenant from the {@link AgentInterface} as the default
    • + *
    • Allows overriding with a custom tenant path if provided
    • + *
    • Normalizes trailing slashes on the base URL
    • + *
    • Validates and normalizes the tenant path
    • + *
    + *

    + * Example: + *

    {@code
    +     * AgentInterface iface = new AgentInterface("jsonrpc", "http://example.com", "default-tenant");
    +     * String url = Utils.buildBaseUrl(iface, null);
    +     * // Returns: "http://example.com/default-tenant"
    +     *
    +     * String url2 = Utils.buildBaseUrl(iface, "custom-tenant");
    +     * // Returns: "http://example.com/custom-tenant"
    +     * }
    + * + * @param agentInterface the agent interface containing the base URL and default tenant, must not be null + * @param tenant the tenant override from the request, may be null to use the interface default + * @return the complete base URL with tenant path appended + * @throws IllegalArgumentException if agentInterface is null or tenant validation fails + */ + public static String buildBaseUrl(AgentInterface agentInterface, @Nullable String tenant) { + checkNotNullParam("agentInterface", agentInterface); + + String agentUrl = agentInterface.url(); + agentUrl = agentUrl.endsWith("/") ? agentUrl.substring(0, agentUrl.length() - 1) : agentUrl; + StringBuilder urlBuilder = new StringBuilder(agentUrl); + urlBuilder.append(extractTenant(agentInterface.tenant(), tenant)); + return urlBuilder.toString(); + } } diff --git a/spec/src/test/java/io/a2a/util/UtilsTest.java b/spec/src/test/java/io/a2a/util/UtilsTest.java new file mode 100644 index 000000000..d5695a8af --- /dev/null +++ b/spec/src/test/java/io/a2a/util/UtilsTest.java @@ -0,0 +1,193 @@ +package io.a2a.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.a2a.spec.AgentInterface; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link Utils} tenant-related methods. + */ +class UtilsTest { + + // ========== buildBaseUrl(AgentInterface, String) Tests ========== + + @Test + void testBuildBaseUrl_withAgentInterface_noTenant() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + String url = Utils.buildBaseUrl(iface, null); + assertEquals("http://example.com", url); + } + + @Test + void testBuildBaseUrl_withAgentInterface_withDefaultTenant() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", "/default-tenant"); + String url = Utils.buildBaseUrl(iface, null); + assertEquals("http://example.com/default-tenant", url); + } + + @Test + void testBuildBaseUrl_withAgentInterface_withOverrideTenant() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", "/default-tenant"); + String url = Utils.buildBaseUrl(iface, "/custom-tenant"); + assertEquals("http://example.com/custom-tenant", url); + } + + @Test + void testBuildBaseUrl_withAgentInterface_urlWithTrailingSlash() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com/", "/tenant"); + String url = Utils.buildBaseUrl(iface, null); + assertEquals("http://example.com/tenant", url); + } + + @Test + void testBuildBaseUrl_withAgentInterface_urlWithoutTrailingSlash() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", "/tenant"); + String url = Utils.buildBaseUrl(iface, null); + assertEquals("http://example.com/tenant", url); + } + + @Test + void testBuildBaseUrl_withAgentInterface_nullInterface_throws() { + assertThrows(IllegalArgumentException.class, () -> { + Utils.buildBaseUrl(null, "/tenant"); + }); + } + + // ========== Security Validation Tests ========== + + @Test + void testValidateTenant_pathTraversal_throws() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + Utils.buildBaseUrl(iface, "../../admin"); + }); + assertNotNull(ex.getMessage()); + assertEquals("Tenant path contains invalid '..' sequence (path traversal attempt)", ex.getMessage()); + } + + @Test + void testValidateTenant_pathTraversalWithSlash_throws() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + Utils.buildBaseUrl(iface, "/../admin"); + }); + assertNotNull(ex.getMessage()); + assertEquals("Tenant path contains invalid '..' sequence (path traversal attempt)", ex.getMessage()); + } + + @Test + void testValidateTenant_tooLong_throws() { + String longTenant = "/" + "a".repeat(256); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + Utils.buildBaseUrl(iface, longTenant); + }); + assertNotNull(ex.getMessage()); + assertEquals("Tenant path exceeds maximum length of 256 characters", ex.getMessage()); + } + + @Test + void testValidateTenant_maxLengthAllowed_succeeds() { + // 256 characters total (including leading slash) + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + String maxTenant = "/" + "a".repeat(255); + String url = Utils.buildBaseUrl(iface, maxTenant); + assertNotNull(url); + assertEquals("http://example.com/" + "a".repeat(255), url); + } + + @Test + void testValidateTenant_invalidCharactersSpace_throws() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + Utils.buildBaseUrl(iface, "/tenant with spaces"); + }); + assertNotNull(ex.getMessage()); + assertEquals("Tenant path contains invalid characters. Only /a-zA-Z0-9_-. are allowed", ex.getMessage()); + } + + @Test + void testValidateTenant_invalidCharactersSpecial_throws() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + Utils.buildBaseUrl(iface, "/tenant@123"); + }); + assertNotNull(ex.getMessage()); + assertEquals("Tenant path contains invalid characters. Only /a-zA-Z0-9_-. are allowed", ex.getMessage()); + } + + @Test + void testValidateTenant_invalidCharactersQuery_throws() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + Utils.buildBaseUrl(iface, "/tenant?param=value"); + }); + assertNotNull(ex.getMessage()); + assertEquals("Tenant path contains invalid characters. Only /a-zA-Z0-9_-. are allowed", ex.getMessage()); + } + + @Test + void testValidateTenant_validCharacters_succeeds() { + // Test all allowed characters: /a-zA-Z0-9_-. + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + String url1 = Utils.buildBaseUrl(iface, "/tenant-name"); + assertEquals("http://example.com/tenant-name", url1); + + String url2 = Utils.buildBaseUrl(iface, "/tenant_name"); + assertEquals("http://example.com/tenant_name", url2); + + String url3 = Utils.buildBaseUrl(iface, "/Tenant123"); + assertEquals("http://example.com/Tenant123", url3); + + String url4 = Utils.buildBaseUrl(iface, "/multi/level/tenant"); + assertEquals("http://example.com/multi/level/tenant", url4); + + String url5 = Utils.buildBaseUrl(iface, "/tenant.v1"); + assertEquals("http://example.com/tenant.v1", url5); + + String url6 = Utils.buildBaseUrl(iface, "/.well-known"); + assertEquals("http://example.com/.well-known", url6); + } + + @Test + void testValidateTenant_emptyString_succeeds() { + // Empty string is valid (no tenant) + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + String url = Utils.buildBaseUrl(iface, ""); + assertEquals("http://example.com", url); + } + + @Test + void testValidateTenant_multiLevelTenant_succeeds() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com", ""); + String url = Utils.buildBaseUrl(iface, "/org/team/tenant"); + assertEquals("http://example.com/org/team/tenant", url); + } + + // ========== Edge Case Tests ========== + + @Test + void testBuildBaseUrl_complexScenario() { + // Base URL with trailing slash, default tenant, custom override + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com:8080/api/", "/default"); + String url = Utils.buildBaseUrl(iface, "custom-tenant"); + assertEquals("http://example.com:8080/api/custom-tenant", url); + } + + @Test + void testBuildBaseUrl_urlWithPort() { + AgentInterface iface = new AgentInterface("JSONRPC", "http://example.com:9999", "/tenant"); + String url = Utils.buildBaseUrl(iface, null); + assertEquals("http://example.com:9999/tenant", url); + } + + @Test + void testBuildBaseUrl_httpsUrl() { + AgentInterface iface = new AgentInterface("JSONRPC", "https://secure.example.com", "/tenant"); + String url = Utils.buildBaseUrl(iface, null); + assertEquals("https://secure.example.com/tenant", url); + } +} diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 15b41a9c2..3dfdd9a9e 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -267,7 +267,7 @@ public void testListTasksSuccess() throws Exception { try { // Test listing all tasks (no filters) - io.a2a.spec.ListTasksParams params = ListTasksParams.builder().tenant("tenant").build(); + io.a2a.spec.ListTasksParams params = ListTasksParams.builder().tenant("").build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); assertNotNull(result); @@ -308,7 +308,7 @@ public void testListTasksFilterByContextId() throws Exception { // Filter by contextId io.a2a.spec.ListTasksParams params = ListTasksParams.builder() .contextId("context-filter-1") - .tenant("tenant") + .tenant("") .build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); @@ -349,7 +349,7 @@ public void testListTasksFilterByStatus() throws Exception { // Filter by status WORKING io.a2a.spec.ListTasksParams params = ListTasksParams.builder() .status(TaskState.WORKING) - .tenant("tenant") + .tenant("") .build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); @@ -393,7 +393,7 @@ public void testListTasksWithPagination() throws Exception { // Get first page with pageSize=2 io.a2a.spec.ListTasksParams params1 = ListTasksParams.builder() .contextId("page-context") - .tenant("tenant") + .tenant("") .pageSize(2) .build(); io.a2a.spec.ListTasksResult result1 = getClient().listTasks(params1); @@ -406,7 +406,7 @@ public void testListTasksWithPagination() throws Exception { // Get second page using pageToken io.a2a.spec.ListTasksParams params2 = ListTasksParams.builder() .contextId("page-context") - .tenant("tenant") + .tenant("") .pageSize(2) .pageToken(result1.nextPageToken()) .build(); @@ -443,7 +443,7 @@ public void testListTasksWithHistoryLimit() throws Exception { // List with history limited to 2 messages io.a2a.spec.ListTasksParams params = ListTasksParams.builder() .contextId("context-history") - .tenant("tenant") + .tenant("") .historyLength(2) .build(); io.a2a.spec.ListTasksResult result = getClient().listTasks(params); @@ -548,7 +548,7 @@ public void testSetPushNotificationSuccess() throws Exception { try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); + MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), ""); TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertEquals(MINIMAL_TASK.id(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); @@ -567,7 +567,7 @@ public void testGetPushNotificationSuccess() throws Exception { try { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( - MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); + MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), ""); TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig); assertNotNull(setResult); @@ -610,7 +610,7 @@ public void testGetAgentCard() throws A2AClientException { assertEquals("A test agent card", agentCard.description()); assertNotNull(agentCard.supportedInterfaces()); assertFalse(agentCard.supportedInterfaces().isEmpty()); - assertEquals(getTransportUrl(), Utils.getFavoriteInterface(agentCard)); + assertEquals(getTransportUrl(), Utils.getFavoriteInterface(agentCard).url()); assertEquals("1.0", agentCard.version()); assertEquals("http://example.com/docs", agentCard.documentationUrl()); assertTrue(agentCard.capabilities().pushNotifications()); From bed152d3e5be2d6a2d83dcb07887f8f18a5a0d05 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 18 Dec 2025 12:45:36 +0000 Subject: [PATCH 237/493] feat!: Remove kind discriminator as per the 1.0 specification (#551) Fixes #544 --- .../JSONRPCTransportStreamingTest.java | 2 +- .../jsonrpc/JSONRPCTransportTest.java | 18 +- .../jsonrpc/sse/SSEEventListenerTest.java | 2 +- .../transport/rest/RestTransportTest.java | 13 +- ...ificationConfigStoreTestAgentExecutor.java | 2 +- .../core/EventSerializationTest.java | 10 +- .../core/ReplicatedQueueManagerTest.java | 2 +- .../KafkaReplicationIntegrationTest.java | 2 +- .../tests/ReplicationTestAgentExecutor.java | 2 +- ...JpaDatabaseTaskStoreTestAgentExecutor.java | 2 +- .../server/agentexecution/RequestContext.java | 2 +- .../a2a/server/events/EventConsumerTest.java | 8 +- .../io/a2a/server/events/EventQueueTest.java | 8 +- .../server/tasks/InMemoryTaskStoreTest.java | 3 +- .../io/a2a/server/tasks/TaskManagerTest.java | 3 +- .../a2a/grpc/mapper/StreamResponseMapper.java | 27 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 38 ++- spec/src/main/java/io/a2a/json/JsonUtil.java | 210 ++++++-------- spec/src/main/java/io/a2a/spec/DataPart.java | 12 +- spec/src/main/java/io/a2a/spec/EventKind.java | 9 +- spec/src/main/java/io/a2a/spec/FilePart.java | 13 +- spec/src/main/java/io/a2a/spec/Message.java | 10 +- spec/src/main/java/io/a2a/spec/Part.java | 56 +--- .../java/io/a2a/spec/StreamingEventKind.java | 22 +- spec/src/main/java/io/a2a/spec/Task.java | 10 +- .../io/a2a/spec/TaskArtifactUpdateEvent.java | 8 +- .../io/a2a/spec/TaskStatusUpdateEvent.java | 8 +- spec/src/main/java/io/a2a/spec/TextPart.java | 10 +- .../StreamingEventKindSerializationTest.java | 257 ++++++++++++++++++ .../io/a2a/spec/TaskSerializationTest.java | 42 ++- .../apps/common/AbstractA2AServerTest.java | 12 +- 31 files changed, 496 insertions(+), 327 deletions(-) create mode 100644 spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index a0c21c13e..92541eb20 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -168,7 +168,7 @@ public void testA2AClientResubscribeToTask() throws Exception { assertEquals("artifact-1", artifact.artifactId()); assertEquals("joke", artifact.name()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); } } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index d949c91b9..078546860 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -131,7 +131,7 @@ public void testA2AClientSendMessage() throws Exception { assertEquals("joke", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); assertTrue(task.metadata().isEmpty()); } @@ -172,7 +172,7 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception { Message agentMessage = (Message) result; assertEquals(Message.Role.AGENT, agentMessage.role()); Part part = agentMessage.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); assertEquals("msg-456", agentMessage.messageId()); } @@ -243,7 +243,7 @@ public void testA2AClientGetTask() throws Exception { assertEquals(1, artifact.parts().size()); assertEquals("artifact-1", artifact.artifactId()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); assertTrue(task.metadata().isEmpty()); List history = task.history(); @@ -255,15 +255,15 @@ public void testA2AClientGetTask() throws Exception { assertNotNull(parts); assertEquals(3, parts.size()); part = parts.get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("tell me a joke", ((TextPart)part).text()); part = parts.get(1); - assertEquals(Part.Kind.FILE, part.getKind()); + assertTrue(part instanceof FilePart); FileContent filePart = ((FilePart) part).file(); assertEquals("file:///path/to/file.txt", ((FileWithUri) filePart).uri()); assertEquals("text/plain", filePart.mimeType()); part = parts.get(2); - assertEquals(Part.Kind.FILE, part.getKind()); + assertTrue(part instanceof FilePart); filePart = ((FilePart) part).file(); assertEquals("aGVsbG8=", ((FileWithBytes) filePart).bytes()); assertEquals("hello.txt", filePart.name()); @@ -558,7 +558,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception { assertEquals("image-analysis", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("This is an image of a cat sitting on a windowsill.", ((TextPart) part).text()); assertFalse(task.metadata().isEmpty()); assertEquals(1, task.metadata().size()); @@ -618,7 +618,7 @@ public void testA2AClientSendMessageWithDataPart() throws Exception { assertEquals("data-analysis", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("Processed weather data: Temperature is 25.5°C, humidity is 60.2% in San Francisco.", ((TextPart) part).text()); assertTrue(task.metadata().isEmpty()); } @@ -676,7 +676,7 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception { assertEquals("mixed-analysis", artifact.name()); assertEquals(1, artifact.parts().size()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("Analyzed chart image and data: Bar chart showing quarterly data with values [10, 20, 30, 40].", ((TextPart) part).text()); assertTrue(task.metadata().isEmpty()); } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 35aa6a27c..6855e1dcc 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -136,7 +136,7 @@ public void testOnEventWithTaskArtifactUpdateEventEvent() throws Exception { Artifact artifact = taskArtifactUpdateEvent.artifact(); assertEquals("artifact-1", artifact.artifactId()); assertEquals(1, artifact.parts().size()); - assertEquals(Part.Kind.TEXT, artifact.parts().get(0).getKind()); + assertTrue(artifact.parts().get(0) instanceof TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).text()); } diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 3fe2db40c..784ddb7e2 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -48,7 +48,6 @@ import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; import io.a2a.spec.Part; -import io.a2a.spec.Part.Kind; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -148,7 +147,7 @@ public void testSendMessage() throws Exception { assertEquals("message-1234", history.messageId()); assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.taskId()); assertEquals(1, history.parts().size()); - assertEquals(Kind.TEXT, history.parts().get(0).getKind()); + assertTrue(history.parts().get(0) instanceof io.a2a.spec.TextPart); assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text()); assertNull(task.metadata()); assertNull(history.referenceTaskIds()); @@ -210,7 +209,7 @@ public void testGetTask() throws Exception { assertEquals("artifact-1", artifact.artifactId()); assertNull(artifact.name()); assertEquals(false, artifact.parts().isEmpty()); - assertEquals(Kind.TEXT, artifact.parts().get(0).getKind()); + assertTrue(artifact.parts().get(0) instanceof io.a2a.spec.TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) artifact.parts().get(0)).text()); assertEquals(1, task.history().size()); Message history = task.history().get(0); @@ -218,14 +217,14 @@ public void testGetTask() throws Exception { assertEquals(Message.Role.USER, history.role()); assertEquals("message-123", history.messageId()); assertEquals(3, history.parts().size()); - assertEquals(Kind.TEXT, history.parts().get(0).getKind()); + assertTrue(history.parts().get(0) instanceof io.a2a.spec.TextPart); assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text()); - assertEquals(Kind.FILE, history.parts().get(1).getKind()); + assertTrue(history.parts().get(1) instanceof FilePart); FilePart part = (FilePart) history.parts().get(1); assertEquals("text/plain", part.file().mimeType()); assertEquals("file:///path/to/file.txt", ((FileWithUri) part.file()).uri()); part = (FilePart) history.parts().get(2); - assertEquals(Kind.FILE, part.getKind()); + assertTrue(part instanceof FilePart); assertEquals("text/plain", part.file().mimeType()); assertEquals("aGVsbG8=", ((FileWithBytes) part.file()).bytes()); assertNull(history.metadata()); @@ -448,7 +447,7 @@ public void testResubscribe() throws Exception { assertEquals("artifact-1", artifact.artifactId()); assertEquals("joke", artifact.name()); Part part = artifact.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof io.a2a.spec.TextPart); assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text()); } } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java index d178b0189..3c4242081 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java @@ -67,7 +67,7 @@ private String getLastTextPart(Message message) throws JSONRPCError { return ""; } Part part = message.parts().get(message.parts().size() - 1); - if (part.getKind() == Part.Kind.TEXT) { + if (part instanceof TextPart) { return ((TextPart) part).text(); } throw new InvalidRequestError("Last part is not text"); diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index 8768269b7..ae2679bdc 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -53,7 +53,7 @@ public void testTaskSerialization() throws JsonProcessingException { // Test serialization as Event String json = JsonUtil.toJson(originalTask); - assertTrue(json.contains("\"kind\":\"task\""), "JSON should contain task kind"); + assertTrue(json.contains("\"task\""), "JSON should contain task wrapper"); assertTrue(json.contains("\"id\":\"test-task-123\""), "JSON should contain task ID"); // Test deserialization back to StreamingEventKind @@ -84,7 +84,7 @@ public void testMessageSerialization() throws JsonProcessingException { // Test serialization as Event String json = JsonUtil.toJson(originalMessage); - assertTrue(json.contains("\"kind\":\"message\""), "JSON should contain message kind"); + assertTrue(json.contains("\"message\""), "JSON should contain message wrapper"); assertTrue(json.contains("\"taskId\":\"test-task-789\""), "JSON should contain task ID"); // Test deserialization back to StreamingEventKind @@ -115,7 +115,7 @@ public void testTaskStatusUpdateEventSerialization() throws JsonProcessingExcept // Test serialization as Event String json = JsonUtil.toJson((Event) originalEvent); - assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain status-update kind"); + assertTrue(json.contains("\"statusUpdate\""), "JSON should contain statusUpdate wrapper"); assertTrue(json.contains("\"taskId\":\"test-task-abc\""), "JSON should contain task ID"); assertTrue(json.contains("\"final\":true"), "JSON should contain final flag"); @@ -148,7 +148,7 @@ public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingExce // Test serialization as Event String json = JsonUtil.toJson((Event) originalEvent); - assertTrue(json.contains("\"kind\":\"artifact-update\""), "JSON should contain artifact-update kind"); + assertTrue(json.contains("\"artifactUpdate\""), "JSON should contain artifactUpdate wrapper"); assertTrue(json.contains("\"taskId\":\"test-task-xyz\""), "JSON should contain task ID"); assertTrue(json.contains("\"test-artifact-123\""), "JSON should contain artifact ID"); @@ -216,7 +216,7 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc String json = JsonUtil.toJson(originalReplicatedEvent); assertTrue(json.contains("\"taskId\":\"replicated-test-task\""), "JSON should contain task ID"); assertTrue(json.contains("\"event\""), "JSON should contain event field"); - assertTrue(json.contains("\"kind\":\"status-update\""), "JSON should contain the event kind"); + assertTrue(json.contains("\"statusUpdate\""), "JSON should contain the event type wrapper"); assertFalse(json.contains("\"error\""), "JSON should not contain error field"); // Deserialize the ReplicatedEventQueueItem diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 0915092ac..0a9326ea0 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -203,7 +203,7 @@ void testReplicatedEventJsonSerialization() throws Exception { assertNotNull(json); assertTrue(json.contains("json-test-task")); assertTrue(json.contains("\"event\":{")); - assertTrue(json.contains("\"kind\":\"status-update\"")); + assertTrue(json.contains("\"statusUpdate\"")); // Deserialize back ReplicatedEventQueueItem deserialized = JsonUtil.fromJson(json, ReplicatedEventQueueItem.class); diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 029aa9a62..f174303ac 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -177,7 +177,7 @@ public void testA2AMessageReplicatedToKafka() throws Exception { assertEquals(contextId, statusUpdateEvent.contextId(), "Event context ID should match original context ID"); assertEquals(TaskState.SUBMITTED, statusUpdateEvent.status().state(), "Event should show SUBMITTED state"); assertFalse(statusUpdateEvent.isFinal(), "Event should show final:false"); - assertEquals("status-update", statusUpdateEvent.kind(), "Event should indicate status-update type"); + assertEquals(TaskStatusUpdateEvent.STREAMING_EVENT_ID, statusUpdateEvent.kind(), "Event should indicate status-update type"); } @Test diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java index e00282cbe..603857ef1 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java @@ -68,7 +68,7 @@ private String getLastTextPart(Message message) throws JSONRPCError { throw new InvalidRequestError("No parts in message"); } Part part = message.parts().get(message.parts().size() - 1); - if (part.getKind() == Part.Kind.TEXT) { + if (part instanceof TextPart) { return ((TextPart) part).text(); } throw new InvalidRequestError("Last part is not text"); diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java index 374eee549..58e52e57d 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java @@ -56,7 +56,7 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPC private String getLastTextPart(Message message) throws JSONRPCError { Part part = message.parts().get(message.parts().size() - 1); - if (part.getKind() == Part.Kind.TEXT) { + if (part instanceof TextPart) { return ((TextPart) part).text(); } throw new InvalidRequestError("No parts"); diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index 89d166801..0aa775649 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -142,7 +142,7 @@ private String getMessageText(Message message, String delimiter) { private List getTextParts(List> parts) { return parts.stream() - .filter(part -> part.getKind() == Part.Kind.TEXT) + .filter(part -> part instanceof TextPart) .map(part -> (TextPart) part) .map(TextPart::text) .collect(Collectors.toList()); diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index b19534e0e..f7f95f01e 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -41,17 +41,15 @@ public class EventConsumerTest { { "id": "123", "contextId": "session-xyz", - "status": {"state": "submitted"}, - "kind": "task" + "status": {"state": "submitted"} } """; private static final String MESSAGE_PAYLOAD = """ { "role": "agent", - "parts": [{"kind": "text", "text": "test message"}], - "messageId": "111", - "kind": "message" + "parts": [{"text": "test message"}], + "messageId": "111" } """; diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 7754fc87b..6ac904b9e 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -34,17 +34,15 @@ public class EventQueueTest { { "id": "123", "contextId": "session-xyz", - "status": {"state": "submitted"}, - "kind": "task" + "status": {"state": "submitted"} } """; private static final String MESSAGE_PAYLOAD = """ { "role": "agent", - "parts": [{"kind": "text", "text": "test message"}], - "messageId": "111", - "kind": "message" + "parts": [{"text": "test message"}], + "messageId": "111" } """; diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java index a042ef179..05adb948c 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java @@ -12,8 +12,7 @@ public class InMemoryTaskStoreTest { { "id": "task-abc", "contextId" : "session-xyz", - "status": {"state": "submitted"}, - "kind": "task" + "status": {"state": "submitted"} }"""; @Test diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 0fc5648c9..f39382878 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -31,8 +31,7 @@ public class TaskManagerTest { { "id": "task-abc", "contextId" : "session-xyz", - "status": {"state": "submitted"}, - "kind": "task" + "status": {"state": "submitted"} }"""; Task minimalTask; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java index eb2abf624..2b91beff0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java @@ -40,25 +40,22 @@ default io.a2a.grpc.StreamResponse toProto(StreamingEventKind domain) { return null; } - if (domain instanceof Task task) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setTask(TaskMapper.INSTANCE.toProto(task)) + return switch (domain.kind()) { + case Task.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setTask(TaskMapper.INSTANCE.toProto((Task) domain)) .build(); - } else if (domain instanceof Message message) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setMsg(MessageMapper.INSTANCE.toProto(message)) + case Message.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setMsg(MessageMapper.INSTANCE.toProto((Message) domain)) .build(); - } else if (domain instanceof TaskStatusUpdateEvent statusUpdate) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setStatusUpdate(TaskStatusUpdateEventMapper.INSTANCE.toProto(statusUpdate)) + case TaskStatusUpdateEvent.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setStatusUpdate(TaskStatusUpdateEventMapper.INSTANCE.toProto((TaskStatusUpdateEvent) domain)) .build(); - } else if (domain instanceof TaskArtifactUpdateEvent artifactUpdate) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setArtifactUpdate(TaskArtifactUpdateEventMapper.INSTANCE.toProto(artifactUpdate)) + case TaskArtifactUpdateEvent.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setArtifactUpdate(TaskArtifactUpdateEventMapper.INSTANCE.toProto((TaskArtifactUpdateEvent) domain)) .build(); - } - - throw new IllegalArgumentException("Unknown StreamingEventKind type: " + domain.getClass().getName()); + default -> + throw new IllegalArgumentException("Unknown StreamingEventKind type: " + domain.getClass().getName()); + }; } /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 940addcf7..55be58228 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -139,39 +139,33 @@ public static StreamResponse streamResponse(StreamingEventKind streamingEventKin } public static io.a2a.grpc.SendMessageResponse taskOrMessage(EventKind eventKind) { - if (eventKind instanceof Task) { - return io.a2a.grpc.SendMessageResponse.newBuilder() + return switch (eventKind.kind()) { + case Task.STREAMING_EVENT_ID -> io.a2a.grpc.SendMessageResponse.newBuilder() .setTask(task((Task) eventKind)) .build(); - } else if (eventKind instanceof Message) { - return io.a2a.grpc.SendMessageResponse.newBuilder() + case Message.STREAMING_EVENT_ID -> io.a2a.grpc.SendMessageResponse.newBuilder() .setMsg(message((Message) eventKind)) .build(); - } else { - throw new IllegalArgumentException("Unsupported event type: " + eventKind); - } + default -> throw new IllegalArgumentException("Unsupported event type: " + eventKind); + }; } public static io.a2a.grpc.StreamResponse taskOrMessageStream(StreamingEventKind eventKind) { - if (eventKind instanceof Task task) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setTask(task(task)) + return switch (eventKind.kind()) { + case Task.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setTask(task((Task) eventKind)) .build(); - } else if (eventKind instanceof Message msg) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setMsg(message(msg)) + case Message.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setMsg(message((Message) eventKind)) .build(); - } else if (eventKind instanceof TaskArtifactUpdateEvent update) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setArtifactUpdate(taskArtifactUpdateEvent(update)) + case TaskStatusUpdateEvent.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setStatusUpdate(taskStatusUpdateEvent((TaskStatusUpdateEvent) eventKind)) .build(); - } else if (eventKind instanceof TaskStatusUpdateEvent update) { - return io.a2a.grpc.StreamResponse.newBuilder() - .setStatusUpdate(taskStatusUpdateEvent(update)) + case TaskArtifactUpdateEvent.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() + .setArtifactUpdate(taskArtifactUpdateEvent((TaskArtifactUpdateEvent) eventKind)) .build(); - } else { - throw new IllegalArgumentException("Unsupported event type: " + eventKind); - } + default -> throw new IllegalArgumentException("Unsupported event type: " + eventKind); + }; } public static io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfigResponse(TaskPushNotificationConfig config) { diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/spec/src/main/java/io/a2a/json/JsonUtil.java index 4fdc63878..ddd712a43 100644 --- a/spec/src/main/java/io/a2a/json/JsonUtil.java +++ b/spec/src/main/java/io/a2a/json/JsonUtil.java @@ -17,7 +17,9 @@ import com.google.gson.JsonSyntaxException; import com.google.gson.ToNumberPolicy; import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DataPart; @@ -45,6 +47,7 @@ import io.a2a.spec.UnsupportedOperationError; import java.io.StringReader; import java.lang.reflect.Type; +import java.util.Map; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -64,7 +67,6 @@ private static GsonBuilder createBaseGsonBuilder() { .registerTypeHierarchyAdapter(JSONRPCError.class, new JSONRPCErrorTypeAdapter()) .registerTypeAdapter(TaskState.class, new TaskStateTypeAdapter()) .registerTypeAdapter(Message.Role.class, new RoleTypeAdapter()) - .registerTypeAdapter(Part.Kind.class, new PartKindTypeAdapter()) .registerTypeHierarchyAdapter(FileContent.class, new FileContentTypeAdapter()); } @@ -512,59 +514,11 @@ public void write(JsonWriter out, Message.Role value) throws java.io.IOException } } - /** - * Gson TypeAdapter for serializing and deserializing {@link Part.Kind} enum. - *

    - * This adapter ensures that Part.Kind enum values are serialized using their - * wire format string representation (e.g., "text", "file", "data") rather than - * the Java enum constant name (e.g., "TEXT", "FILE", "DATA"). - *

    - * For serialization, it uses {@link Part.Kind#asString()} to get the wire format. - * For deserialization, it parses the string to the enum constant. - * - * @see Part.Kind - * @see Part.Kind#asString() - */ - static class PartKindTypeAdapter extends TypeAdapter { - - @Override - public void write(JsonWriter out, Part.Kind value) throws java.io.IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(value.asString()); - } - } - - @Override - public Part.@Nullable Kind read(JsonReader in) throws java.io.IOException { - if (in.peek() == com.google.gson.stream.JsonToken.NULL) { - in.nextNull(); - return null; - } - String kindString = in.nextString(); - try { - return switch (kindString) { - case "text" -> - Part.Kind.TEXT; - case "file" -> - Part.Kind.FILE; - case "data" -> - Part.Kind.DATA; - default -> - throw new IllegalArgumentException("Invalid Part.Kind: " + kindString); - }; - } catch (IllegalArgumentException e) { - throw new JsonSyntaxException("Invalid Part.Kind: " + kindString, e); - } - } - } - /** * Gson TypeAdapter for serializing and deserializing {@link Part} and its subclasses. *

    - * This adapter handles polymorphic deserialization based on the "kind" field, creating the - * appropriate subclass instance (TextPart, FilePart, or DataPart). + * This adapter handles polymorphic deserialization, creating the + * appropriate subclass instance (TextPart, FilePart, or DataPart) based on available fields. *

    * The adapter uses a two-pass approach: first reads the JSON as a tree to inspect the "kind" * field, then deserializes to the appropriate concrete type. @@ -586,61 +540,62 @@ public void write(JsonWriter out, Part value) throws java.io.IOException { return; } - // Serialize the concrete type to a JsonElement - com.google.gson.JsonElement jsonElement; + // Write wrapper object with member name as discriminator + out.beginObject(); + if (value instanceof TextPart textPart) { - jsonElement = delegateGson.toJsonTree(textPart, TextPart.class); + // TextPart: { "text": "value" } - direct string value + out.name("text"); + out.value(textPart.text()); } else if (value instanceof FilePart filePart) { - jsonElement = delegateGson.toJsonTree(filePart, FilePart.class); + // FilePart: { "file": {...} } + out.name("file"); + delegateGson.toJson(filePart.file(), FileContent.class, out); } else if (value instanceof DataPart dataPart) { - jsonElement = delegateGson.toJsonTree(dataPart, DataPart.class); + // DataPart: { "data": {...} } + out.name("data"); + delegateGson.toJson(dataPart.data(), Map.class, out); } else { throw new JsonSyntaxException("Unknown Part subclass: " + value.getClass().getName()); } - - // TODO temorary workaround to be fixed in https://github.com/a2aproject/a2a-java/issues/544 - // Add the "kind" field from getKind() method - if (jsonElement.isJsonObject()) { - jsonElement.getAsJsonObject().addProperty("kind", value.getKind().asString()); - } - - // Write the modified JSON - delegateGson.toJson(jsonElement, out); + out.endObject(); } @Override public @Nullable Part read(JsonReader in) throws java.io.IOException { - if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } - // Read the JSON as a tree so we can inspect the "kind" field + // Read the JSON as a tree to inspect the member name discriminator com.google.gson.JsonElement jsonElement = com.google.gson.JsonParser.parseReader(in); if (!jsonElement.isJsonObject()) { throw new JsonSyntaxException("Part must be a JSON object"); } com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); - com.google.gson.JsonElement kindElement = jsonObject.get("kind"); - if (kindElement == null || !kindElement.isJsonPrimitive()) { - throw new JsonSyntaxException("Part must have a 'kind' field"); - } - - String kind = kindElement.getAsString(); - // Use the delegate Gson to deserialize to the concrete type - return switch (kind) { - case "text" -> - delegateGson.fromJson(jsonElement, TextPart.class); - case "file" -> - delegateGson.fromJson(jsonElement, FilePart.class); - case "data" -> - delegateGson.fromJson(jsonElement, DataPart.class); - default -> - throw new JsonSyntaxException("Unknown Part kind: " + kind); - }; + + // Check for member name discriminators (v1.0 protocol) + if (jsonObject.has("text")) { + // TextPart: { "text": "value" } - direct string value + return new TextPart(jsonObject.get("text").getAsString()); + } else if (jsonObject.has("file")) { + // FilePart: { "file": {...} } + return new FilePart(delegateGson.fromJson(jsonObject.get("file"), FileContent.class)); + } else if (jsonObject.has("data")) { + // DataPart: { "data": {...} } + @SuppressWarnings("unchecked") + Map dataMap = delegateGson.fromJson( + jsonObject.get("data"), + new TypeToken>(){}.getType() + ); + return new DataPart(dataMap); + } else { + throw new JsonSyntaxException("Part must have one of: text, file, data (found: " + jsonObject.keySet() + ")"); + } } } @@ -673,64 +628,69 @@ public void write(JsonWriter out, StreamingEventKind value) throws java.io.IOExc return; } - // Serialize the concrete type to a JsonElement - com.google.gson.JsonElement jsonElement; - if (value instanceof Task task) { - jsonElement = delegateGson.toJsonTree(task, Task.class); - } else if (value instanceof Message message) { - jsonElement = delegateGson.toJsonTree(message, Message.class); - } else if (value instanceof TaskStatusUpdateEvent event) { - jsonElement = delegateGson.toJsonTree(event, TaskStatusUpdateEvent.class); - } else if (value instanceof TaskArtifactUpdateEvent event) { - jsonElement = delegateGson.toJsonTree(event, TaskArtifactUpdateEvent.class); - } else { - throw new JsonSyntaxException("Unknown StreamingEventKind implementation: " + value.getClass().getName()); - } + // Write wrapper object with member name as discriminator + out.beginObject(); - // TODO temorary workaround to be fixed in https://github.com/a2aproject/a2a-java/issues/544 - // Add the "kind" field from getKind() method - if (jsonElement.isJsonObject()) { - jsonElement.getAsJsonObject().addProperty("kind", value.kind()); - } + Type type = switch (value.kind()) { + case Task.STREAMING_EVENT_ID -> Task.class; + case Message.STREAMING_EVENT_ID -> Message.class; + case TaskStatusUpdateEvent.STREAMING_EVENT_ID -> TaskStatusUpdateEvent.class; + case TaskArtifactUpdateEvent.STREAMING_EVENT_ID -> TaskArtifactUpdateEvent.class; + default -> throw new JsonSyntaxException("Unknown StreamingEventKind implementation: " + value.getClass().getName()); + }; - // Write the modified JSON - delegateGson.toJson(jsonElement, out); + out.name(value.kind()); + delegateGson.toJson(value, type, out); + out.endObject(); } @Override public @Nullable StreamingEventKind read(JsonReader in) throws java.io.IOException { - if (in.peek() == com.google.gson.stream.JsonToken.NULL) { + if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } - // Read the JSON as a tree so we can inspect the "kind" field + // Read the JSON as a tree to inspect the member name discriminator com.google.gson.JsonElement jsonElement = com.google.gson.JsonParser.parseReader(in); if (!jsonElement.isJsonObject()) { throw new JsonSyntaxException("StreamingEventKind must be a JSON object"); } com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); - com.google.gson.JsonElement kindElement = jsonObject.get("kind"); - if (kindElement == null || !kindElement.isJsonPrimitive()) { - throw new JsonSyntaxException("StreamingEventKind must have a 'kind' field"); - } - - String kind = kindElement.getAsString(); - // Use the delegate Gson to deserialize to the concrete type - return switch (kind) { - case "task" -> - delegateGson.fromJson(jsonElement, Task.class); - case "message" -> - delegateGson.fromJson(jsonElement, Message.class); - case "status-update" -> - delegateGson.fromJson(jsonElement, TaskStatusUpdateEvent.class); - case "artifact-update" -> - delegateGson.fromJson(jsonElement, TaskArtifactUpdateEvent.class); - default -> - throw new JsonSyntaxException("Unknown StreamingEventKind kind: " + kind); - }; + + // Check for wrapped member name discriminators (v1.0 protocol - streaming format) + if (jsonObject.has(Task.STREAMING_EVENT_ID)) { + return delegateGson.fromJson(jsonObject.get(Task.STREAMING_EVENT_ID), Task.class); + } else if (jsonObject.has(Message.STREAMING_EVENT_ID)) { + return delegateGson.fromJson(jsonObject.get(Message.STREAMING_EVENT_ID), Message.class); + } else if (jsonObject.has(TaskStatusUpdateEvent.STREAMING_EVENT_ID)) { + return delegateGson.fromJson( + jsonObject.get(TaskStatusUpdateEvent.STREAMING_EVENT_ID), TaskStatusUpdateEvent.class); + } else if (jsonObject.has(TaskArtifactUpdateEvent.STREAMING_EVENT_ID)) { + return delegateGson.fromJson( + jsonObject.get(TaskArtifactUpdateEvent.STREAMING_EVENT_ID), TaskArtifactUpdateEvent.class); + } + + // Check for unwrapped format (direct Task/Message deserialization) + // Task objects have "id" and "contextId" fields + // Message objects have "role" and "messageId" fields + if (jsonObject.has("role") && jsonObject.has("messageId")) { + // This is an unwrapped Message + return delegateGson.fromJson(jsonObject, Message.class); + } else if (jsonObject.has("id") && jsonObject.has("contextId")) { + // This is an unwrapped Task + return delegateGson.fromJson(jsonObject, Task.class); + } else if (jsonObject.has("taskId") && jsonObject.has("status")) { + // This is an unwrapped TaskStatusUpdateEvent + return delegateGson.fromJson(jsonObject, TaskStatusUpdateEvent.class); + } else if (jsonObject.has("taskId") && jsonObject.has("artifact")) { + // This is an unwrapped TaskArtifactUpdateEvent + return delegateGson.fromJson(jsonObject, TaskArtifactUpdateEvent.class); + } else { + throw new JsonSyntaxException("StreamingEventKind must have wrapper (task/message/statusUpdate/artifactUpdate) or recognizable unwrapped fields (found: " + jsonObject.keySet() + ")"); + } } } diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index 8cbc83af0..d44e87151 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -40,7 +40,12 @@ */ public record DataPart(Map data) implements Part> { - /** The type identifier for data parts in messages and artifacts. */ + /** + * The JSON member name discriminator for data parts: "data". + *

    + * In protocol v1.0+, this constant defines the JSON member name used for serialization: + * {@code { "data": { "data": { "temperature": 22.5, "unit": "C" } } }} + */ public static final String DATA = "data"; /** @@ -53,11 +58,6 @@ public record DataPart(Map data) implements Part - * EventKind implementations use polymorphic JSON serialization with the "kind" discriminator - * to determine the concrete type during deserialization. + * EventKind implementations use polymorphic JSON serialization where the JSON member name + * itself acts as the type discriminator (e.g., "task", "message"). *

    * Permitted implementations: *

      @@ -22,7 +22,10 @@ public interface EventKind { /** - * Returns the kind identifier for this event. + * Returns the type identifier for this event. + *

      + * This method provides programmatic type discrimination for routing, logging, and debugging. + * NOTE: This value is NOT serialized to JSON in protocol v1.0+. * * @return the event kind string (e.g., "task", "message") */ diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index f7c3e7426..a1835109c 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -40,7 +40,12 @@ */ public record FilePart(FileContent file) implements Part { - /** The type identifier for file parts in messages and artifacts. */ + /** + * The JSON member name discriminator for file parts: "file". + *

      + * In protocol v1.0+, this constant defines the JSON member name used for serialization: + * {@code { "file": { "mediaType": "image/png", "name": "photo.png", ... } }} + */ public static final String FILE = "file"; /** @@ -51,10 +56,4 @@ public record FilePart(FileContent file) implements Part { public FilePart { Assert.checkNotNullParam("file", file); } - - @Override - public Kind getKind() { - return Kind.FILE; - } - } diff --git a/spec/src/main/java/io/a2a/spec/Message.java b/spec/src/main/java/io/a2a/spec/Message.java index b218b5396..9606cd15d 100644 --- a/spec/src/main/java/io/a2a/spec/Message.java +++ b/spec/src/main/java/io/a2a/spec/Message.java @@ -6,9 +6,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.Message.MESSAGE; -import static io.a2a.util.Utils.SPEC_VERSION_1_0; - /** * Represents a single message in the conversation between a user and an agent in the A2A Protocol. *

      @@ -42,7 +39,10 @@ public record Message(Role role, List> parts, Map metadata, List extensions ) implements EventKind, StreamingEventKind { - public static final String MESSAGE = "message"; + /** + * The identifier when used in streaming responses + */ + public static final String STREAMING_EVENT_ID = "message"; /** * Compact constructor with validation and defensive copying. @@ -64,7 +64,7 @@ public record Message(Role role, List> parts, @Override public String kind() { - return MESSAGE; + return STREAMING_EVENT_ID; } /** diff --git a/spec/src/main/java/io/a2a/spec/Part.java b/spec/src/main/java/io/a2a/spec/Part.java index 345db0602..631f87f88 100644 --- a/spec/src/main/java/io/a2a/spec/Part.java +++ b/spec/src/main/java/io/a2a/spec/Part.java @@ -11,10 +11,18 @@ *

    • {@link DataPart} - Structured data (JSON objects)
    • *
    *

    - * Parts use polymorphic JSON serialization with the "kind" discriminator property to - * determine the concrete type during deserialization. + * Parts use polymorphic JSON serialization where the JSON member name itself acts as the + * type discriminator (e.g., "text", "file", "data"). This aligns with Protocol Buffers' + * oneof semantics and modern API design practices. *

    - * Each Part can include optional metadata for additional context about the content. + * Use {@code instanceof} pattern matching to determine the concrete Part type at runtime: + *

    {@code
    + * if (part instanceof TextPart textPart) {
    + *     String text = textPart.text();
    + * } else if (part instanceof FilePart filePart) {
    + *     FileContent file = filePart.file();
    + * }
    + * }
    * * @param the type of content contained in this part * @see Message @@ -22,45 +30,5 @@ * @see A2A Protocol Specification */ public interface Part { - /** - * Enum defining the different types of content parts. - */ - enum Kind { - /** - * Plain text content part. - */ - TEXT("text"), - - /** - * File content part (bytes or URI). - */ - FILE("file"), - - /** - * Structured data content part (JSON). - */ - DATA("data"); - - private final String kind; - - Kind(String kind) { - this.kind = kind; - } - - /** - * Returns the string representation of the kind for JSON serialization. - * - * @return the kind as a string - */ - public String asString() { - return this.kind; - } - } - - /** - * Returns the kind of this part. - * - * @return the Part.Kind indicating the content type - */ - Kind getKind(); + // No methods - use instanceof for type discrimination } \ No newline at end of file diff --git a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java index 4b7751c71..a8d0dd628 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingEventKind.java +++ b/spec/src/main/java/io/a2a/spec/StreamingEventKind.java @@ -7,8 +7,9 @@ * agent task execution. Streaming allows agents to provide incremental feedback as work progresses, * rather than waiting until task completion. *

    - * Streaming events use polymorphic JSON serialization with the "kind" discriminator to determine - * the concrete type during deserialization. + * Streaming events use polymorphic JSON serialization where the JSON member name itself acts as + * the type discriminator (e.g., "task", "message", "statusUpdate", "artifactUpdate"). + * This aligns with Protocol Buffers' oneof semantics and modern API design practices. *

    * Permitted implementations: *

      @@ -32,9 +33,22 @@ public sealed interface StreamingEventKind extends Event permits Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent { /** - * Returns the kind identifier for this streaming event. + * Returns the type identifier for this streaming event. + *

      + * This method provides programmatic type discrimination for routing, logging, and debugging. + * NOTE: This value is NOT serialized to JSON in protocol v1.0+. + * For JSON serialization, the wrapper field name serves as the discriminator. + *

      + * Use {@code instanceof} pattern matching to determine the concrete event type: + *

      {@code
      +     * if (event instanceof Task task) {
      +     *     // Handle task
      +     * } else if (event instanceof TaskStatusUpdateEvent statusUpdate) {
      +     *     // Handle status update
      +     * }
      +     * }
      * - * @return the event kind string (e.g., "task", "message", "status-update", "artifact-update") + * @return the event type identifier (e.g., "task", "message", "statusUpdate", "artifactUpdate") */ String kind(); } diff --git a/spec/src/main/java/io/a2a/spec/Task.java b/spec/src/main/java/io/a2a/spec/Task.java index e949a2fe3..b9e37aabb 100644 --- a/spec/src/main/java/io/a2a/spec/Task.java +++ b/spec/src/main/java/io/a2a/spec/Task.java @@ -5,9 +5,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.Task.TASK; -import static io.a2a.util.Utils.SPEC_VERSION_1_0; - /** * Represents a single, stateful operation or conversation between a client and an agent in the A2A Protocol. *

      @@ -54,7 +51,10 @@ public record Task( Map metadata ) implements EventKind, StreamingEventKind { - public static final String TASK = "task"; + /** + * The identifier when used in streaming responses + */ + public static final String STREAMING_EVENT_ID = "task"; /** * Compact constructor with validation and defensive copying. @@ -72,7 +72,7 @@ public record Task( @Override public String kind() { - return TASK; + return STREAMING_EVENT_ID; } /** diff --git a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java index 5f492be3d..b8d468b78 100644 --- a/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskArtifactUpdateEvent.java @@ -4,8 +4,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.TaskArtifactUpdateEvent.ARTIFACT_UPDATE; - /** * Event notifying that a task artifact has been created, modified, or appended to. *

      @@ -50,9 +48,9 @@ public record TaskArtifactUpdateEvent( ) implements EventKind, StreamingEventKind, UpdateEvent { /** - * The kind identifier for artifact update events: "artifact-update". + * The identifier when used in streaming responses */ - public static final String ARTIFACT_UPDATE = "artifact-update"; + public static final String STREAMING_EVENT_ID = "artifactUpdate"; /** * Compact constructor with validation. @@ -67,7 +65,7 @@ public record TaskArtifactUpdateEvent( @Override public String kind() { - return ARTIFACT_UPDATE; + return STREAMING_EVENT_ID; } /** diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index d9d791f9d..2a34a2e03 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -5,8 +5,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.TaskStatusUpdateEvent.STATUS_UPDATE; - /** * An event sent by the agent to notify the client of a change in a task's status. * This is typically used in streaming or subscription models. @@ -26,9 +24,9 @@ public record TaskStatusUpdateEvent( ) implements EventKind, StreamingEventKind, UpdateEvent { /** - * The kind identifier for status update events: "status-update". + * The identifier when used in streaming responses */ - public static final String STATUS_UPDATE = "status-update"; + public static final String STREAMING_EVENT_ID = "statusUpdate"; /** * Compact constructor with validation. @@ -41,7 +39,7 @@ public record TaskStatusUpdateEvent( @Override public String kind() { - return STATUS_UPDATE; + return STREAMING_EVENT_ID; } /** diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index 4010d0063..57adef574 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -29,7 +29,10 @@ public record TextPart(String text) implements Part { /** - * The kind identifier for text parts: "text". + * The JSON member name discriminator for text parts: "text". + *

      + * In protocol v1.0+, this constant defines the JSON member name used for serialization: + * {@code { "text": "Hello, world!" }} */ public static final String TEXT = "text"; @@ -41,9 +44,4 @@ public record TextPart(String text) implements Part { public TextPart { Assert.checkNotNullParam("text", text); } - - @Override - public Kind getKind() { - return Kind.TEXT; - } } diff --git a/spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java b/spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java new file mode 100644 index 000000000..7c15447e1 --- /dev/null +++ b/spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java @@ -0,0 +1,257 @@ +package io.a2a.spec; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import io.a2a.json.JsonProcessingException; +import io.a2a.json.JsonUtil; +import org.junit.jupiter.api.Test; + +/** + * Tests for StreamingEventKind serialization and deserialization. + *

      + * Verifies that StreamingEventKind types (Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent) + * serialize using wrapper member names (e.g., {"task": {...}}) and do not contain "kind" fields. + */ +class StreamingEventKindSerializationTest { + + @Test + void testTaskSerialization() throws JsonProcessingException { + // Create a Task + Task task = Task.builder() + .id("task-123") + .contextId("context-456") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + + // Serialize as StreamingEventKind + String json = JsonUtil.toJson((StreamingEventKind) task); + + // Verify JSON contains task wrapper, not "kind" field + assertNotNull(json); + assertTrue(json.contains("\"task\"")); + assertTrue(json.contains("\"id\":\"task-123\"")); + assertTrue(json.contains("\"state\":\"submitted\"")); + assertFalse(json.contains("\"kind\"")); + + // Deserialize back to StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Verify it's a Task + assertInstanceOf(Task.class, deserialized); + Task deserializedTask = (Task) deserialized; + assertEquals(task.id(), deserializedTask.id()); + assertEquals(task.contextId(), deserializedTask.contextId()); + assertEquals(task.status().state(), deserializedTask.status().state()); + } + + @Test + void testMessageSerialization() throws JsonProcessingException { + // Create a Message + Message message = Message.builder() + .role(Message.Role.USER) + .parts(List.of(new TextPart("Hello, agent!"))) + .taskId("task-789") + .messageId("msg-123") + .contextId("context-456") + .build(); + + // Serialize as StreamingEventKind + String json = JsonUtil.toJson((StreamingEventKind) message); + + // Verify JSON contains message wrapper, not "kind" field + assertNotNull(json); + assertTrue(json.contains("\"message\"")); + assertTrue(json.contains("\"taskId\":\"task-789\"")); + assertTrue(json.contains("\"role\":\"user\"")); + assertTrue(json.contains("Hello, agent!")); + assertFalse(json.contains("\"kind\"")); + + // Deserialize back to StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Verify it's a Message + assertInstanceOf(Message.class, deserialized); + Message deserializedMessage = (Message) deserialized; + assertEquals(message.taskId(), deserializedMessage.taskId()); + assertEquals(message.role(), deserializedMessage.role()); + assertEquals(message.parts().size(), deserializedMessage.parts().size()); + } + + @Test + void testTaskStatusUpdateEventSerialization() throws JsonProcessingException { + // Create a TaskStatusUpdateEvent + TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder() + .taskId("task-abc") + .contextId("context-def") + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(false) + .build(); + + // Serialize as StreamingEventKind + String json = JsonUtil.toJson((StreamingEventKind) statusEvent); + + // Verify JSON contains statusUpdate wrapper, not "kind" field + assertNotNull(json); + assertTrue(json.contains("\"statusUpdate\"")); + assertTrue(json.contains("\"taskId\":\"task-abc\"")); + assertTrue(json.contains("\"state\":\"working\"")); + assertTrue(json.contains("\"final\":false")); + assertFalse(json.contains("\"kind\"")); + + // Deserialize back to StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Verify it's a TaskStatusUpdateEvent + assertInstanceOf(TaskStatusUpdateEvent.class, deserialized); + TaskStatusUpdateEvent deserializedEvent = (TaskStatusUpdateEvent) deserialized; + assertEquals(statusEvent.taskId(), deserializedEvent.taskId()); + assertEquals(statusEvent.status().state(), deserializedEvent.status().state()); + assertEquals(statusEvent.isFinal(), deserializedEvent.isFinal()); + } + + @Test + void testTaskArtifactUpdateEventSerialization() throws JsonProcessingException { + // Create a TaskArtifactUpdateEvent + Artifact artifact = Artifact.builder() + .artifactId("artifact-xyz") + .name("Test Artifact") + .parts(List.of(new TextPart("Artifact content"))) + .build(); + + TaskArtifactUpdateEvent artifactEvent = TaskArtifactUpdateEvent.builder() + .taskId("task-123") + .contextId("context-456") + .artifact(artifact) + .build(); + + // Serialize as StreamingEventKind + String json = JsonUtil.toJson((StreamingEventKind) artifactEvent); + + // Verify JSON contains artifactUpdate wrapper, not "kind" field + assertNotNull(json); + assertTrue(json.contains("\"artifactUpdate\"")); + assertTrue(json.contains("\"taskId\":\"task-123\"")); + assertTrue(json.contains("\"artifactId\":\"artifact-xyz\"")); + assertTrue(json.contains("Artifact content")); + assertFalse(json.contains("\"kind\"")); + + // Deserialize back to StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Verify it's a TaskArtifactUpdateEvent + assertInstanceOf(TaskArtifactUpdateEvent.class, deserialized); + TaskArtifactUpdateEvent deserializedEvent = (TaskArtifactUpdateEvent) deserialized; + assertEquals(artifactEvent.taskId(), deserializedEvent.taskId()); + assertEquals(artifactEvent.artifact().artifactId(), deserializedEvent.artifact().artifactId()); + } + + @Test + void testUnwrappedTaskDeserialization() throws JsonProcessingException { + // Test that unwrapped Task format (direct deserialization) still works + String json = """ + { + "id": "task-unwrapped", + "contextId": "context-999", + "status": { + "state": "completed" + } + } + """; + + // Deserialize as StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Should successfully deserialize as Task + assertInstanceOf(Task.class, deserialized); + Task task = (Task) deserialized; + assertEquals("task-unwrapped", task.id()); + assertEquals("context-999", task.contextId()); + assertEquals(TaskState.COMPLETED, task.status().state()); + } + + @Test + void testUnwrappedMessageDeserialization() throws JsonProcessingException { + // Test that unwrapped Message format (direct deserialization) still works + String json = """ + { + "role": "agent", + "parts": [ + { + "text": "Unwrapped message" + } + ], + "messageId": "msg-unwrapped", + "taskId": "task-999" + } + """; + + // Deserialize as StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Should successfully deserialize as Message + assertInstanceOf(Message.class, deserialized); + Message message = (Message) deserialized; + assertEquals("msg-unwrapped", message.messageId()); + assertEquals("task-999", message.taskId()); + assertEquals(Message.Role.AGENT, message.role()); + } + + @Test + void testUnwrappedTaskStatusUpdateEventDeserialization() throws JsonProcessingException { + // Test that unwrapped TaskStatusUpdateEvent format still works + String json = """ + { + "taskId": "task-status-unwrapped", + "contextId": "context-999", + "status": { + "state": "working" + }, + "final": false + } + """; + + // Deserialize as StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Should successfully deserialize as TaskStatusUpdateEvent + assertInstanceOf(TaskStatusUpdateEvent.class, deserialized); + TaskStatusUpdateEvent event = (TaskStatusUpdateEvent) deserialized; + assertEquals("task-status-unwrapped", event.taskId()); + assertEquals(TaskState.WORKING, event.status().state()); + assertFalse(event.isFinal()); + } + + @Test + void testUnwrappedTaskArtifactUpdateEventDeserialization() throws JsonProcessingException { + // Test that unwrapped TaskArtifactUpdateEvent format still works + String json = """ + { + "taskId": "task-artifact-unwrapped", + "contextId": "context-999", + "artifact": { + "artifactId": "artifact-unwrapped", + "parts": [ + { + "text": "Unwrapped artifact" + } + ] + } + } + """; + + // Deserialize as StreamingEventKind + StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class); + + // Should successfully deserialize as TaskArtifactUpdateEvent + assertInstanceOf(TaskArtifactUpdateEvent.class, deserialized); + TaskArtifactUpdateEvent event = (TaskArtifactUpdateEvent) deserialized; + assertEquals("task-artifact-unwrapped", event.taskId()); + assertEquals("artifact-unwrapped", event.artifact().artifactId()); + } +} diff --git a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java index c74dd3999..cded0e204 100644 --- a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java +++ b/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java @@ -1,6 +1,7 @@ package io.a2a.spec; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -245,8 +246,9 @@ void testTaskWithFilePartBytes() throws JsonProcessingException { // Serialize String json = JsonUtil.toJson(task); - // Verify JSON contains file part data - assertTrue(json.contains("\"kind\":\"file\"")); + // Verify JSON contains file part data (v1.0 format uses member name "file", not "kind") + assertTrue(json.contains("\"file\"")); + assertFalse(json.contains("\"kind\"")); assertTrue(json.contains("document.pdf")); assertTrue(json.contains("application/pdf")); @@ -284,6 +286,7 @@ void testTaskWithFilePartUri() throws JsonProcessingException { // Verify JSON contains URI assertTrue(json.contains("https://example.com/photo.png")); + assertFalse(json.contains("\"kind\"")); // Removed in spec 1.0 // Deserialize Task deserialized = JsonUtil.fromJson(json, Task.class); @@ -316,8 +319,9 @@ void testTaskWithDataPart() throws JsonProcessingException { // Serialize String json = JsonUtil.toJson(task); - // Verify JSON contains data part - assertTrue(json.contains("\"kind\":\"data\"")); + // Verify JSON contains data part (v1.0 format uses member name "data", not "kind") + assertTrue(json.contains("\"data\"")); + assertFalse(json.contains("\"kind\"")); assertTrue(json.contains("temperature")); // Deserialize @@ -416,8 +420,7 @@ void testDeserializeTaskFromJson() throws JsonProcessingException { "contextId": "context-456", "status": { "state": "submitted" - }, - "kind": "task" + } } """; @@ -446,13 +449,11 @@ void testDeserializeTaskWithArtifactsFromJson() throws JsonProcessingException { "name": "Result", "parts": [ { - "kind": "text", "text": "Hello World" } ] } - ], - "kind": "task" + ] } """; @@ -482,7 +483,6 @@ void testDeserializeTaskWithFilePartBytesFromJson() throws JsonProcessingExcepti "artifactId": "file-artifact", "parts": [ { - "kind": "file", "file": { "mimeType": "application/pdf", "name": "document.pdf", @@ -491,8 +491,7 @@ void testDeserializeTaskWithFilePartBytesFromJson() throws JsonProcessingExcepti } ] } - ], - "kind": "task" + ] } """; @@ -524,7 +523,6 @@ void testDeserializeTaskWithFilePartUriFromJson() throws JsonProcessingException "artifactId": "uri-artifact", "parts": [ { - "kind": "file", "file": { "mimeType": "image/png", "name": "photo.png", @@ -533,8 +531,7 @@ void testDeserializeTaskWithFilePartUriFromJson() throws JsonProcessingException } ] } - ], - "kind": "task" + ] } """; @@ -565,7 +562,6 @@ void testDeserializeTaskWithDataPartFromJson() throws JsonProcessingException { "artifactId": "data-artifact", "parts": [ { - "kind": "data", "data": { "temperature": 22.5, "humidity": 65 @@ -573,8 +569,7 @@ void testDeserializeTaskWithDataPartFromJson() throws JsonProcessingException { } ] } - ], - "kind": "task" + ] } """; @@ -601,7 +596,6 @@ void testDeserializeTaskWithHistoryFromJson() throws JsonProcessingException { "role": "user", "parts": [ { - "kind": "text", "text": "User message" } ], @@ -611,14 +605,12 @@ void testDeserializeTaskWithHistoryFromJson() throws JsonProcessingException { "role": "agent", "parts": [ { - "kind": "text", "text": "Agent response" } ], "messageId": "msg-2" } - ], - "kind": "task" + ] } """; @@ -641,8 +633,7 @@ void testDeserializeTaskWithTimestampFromJson() throws JsonProcessingException { "status": { "state": "working", "timestamp": "2023-10-01T12:00:00.234-05:00" - }, - "kind": "task" + } } """; @@ -666,8 +657,7 @@ void testDeserializeTaskWithMetadataFromJson() throws JsonProcessingException { "metadata": { "key1": "value1", "key2": 42 - }, - "kind": "task" + } } """; diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 3dfdd9a9e..938f2d3ad 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -495,7 +495,7 @@ public void testSendMessageNewMessageSuccess() throws Exception { assertEquals(MESSAGE.messageId(), messageResponse.messageId()); assertEquals(MESSAGE.role(), messageResponse.role()); Part part = messageResponse.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("test message", ((TextPart) part).text()); } @@ -533,7 +533,7 @@ public void testSendMessageExistingTaskSuccess() throws Exception { assertEquals(MESSAGE.messageId(), messageResponse.messageId()); assertEquals(MESSAGE.role(), messageResponse.role()); Part part = messageResponse.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("test message", ((TextPart) part).text()); } catch (A2AClientException e) { fail("Unexpected exception during sendMessage: " + e.getMessage(), e); @@ -714,7 +714,7 @@ public void testResubscribeExistingTaskSuccess() throws Exception { assertEquals(MINIMAL_TASK.id(), receivedArtifactEvent.taskId()); assertEquals(MINIMAL_TASK.contextId(), receivedArtifactEvent.contextId()); Part part = receivedArtifactEvent.artifact().parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("text", ((TextPart) part).text()); // Verify status update event @@ -820,7 +820,7 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep assertEquals(MINIMAL_TASK.id(), receivedArtifactEvent.taskId()); assertEquals(MINIMAL_TASK.contextId(), receivedArtifactEvent.contextId()); Part part = receivedArtifactEvent.artifact().parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("text", ((TextPart) part).text()); // Verify status update event @@ -1607,7 +1607,7 @@ private void testSendStreamingMessageWithHttpClient(String mediaType) throws Exc assertEquals(MESSAGE.messageId(), messageResponse.messageId()); assertEquals(MESSAGE.role(), messageResponse.role()); Part part = messageResponse.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("test message", ((TextPart) part).text()); latch.countDown(); } @@ -1673,7 +1673,7 @@ public void testSendStreamingMessage(boolean createTask) throws Exception { assertEquals(MESSAGE.messageId(), messageResponse.messageId()); assertEquals(MESSAGE.role(), messageResponse.role()); Part part = messageResponse.parts().get(0); - assertEquals(Part.Kind.TEXT, part.getKind()); + assertTrue(part instanceof TextPart); assertEquals("test message", ((TextPart) part).text()); } catch (A2AClientException e) { fail("Unexpected exception during sendMessage: " + e.getMessage(), e); From 6038ee247b6d898b16a78f6727118755571789ac Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 18 Dec 2025 20:11:17 +0100 Subject: [PATCH 238/493] feat!: Add pagination support to ListTaskPushNotificationConfig with Result wrapper (#552) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Implements pagination support for push notification config listing by introducing ListTaskPushNotificationConfigResult record, following the same pattern as ListTasksResult. This allows the API to return both the list of configs and a nextPageToken for pagination. Changes: - Created ListTaskPushNotificationConfigResult record with configs list and nextPageToken - Updated ListTaskPushNotificationConfigResponse to use Result type - Updated all server handlers to return Result wrapper - Updated ProtoUtils mappers for Result conversion - Updated all transport implementations (REST, gRPC, JSONRPC) on both client and server - Updated client interface and implementations - Fixed test to use Result type Updated all PushNotificationConfigStore implementations and tests to use the new ListTaskPushNotificationConfigParams/ListTaskPushNotificationConfigResult API instead of the old String-based getInfo method. Changes to implementations: - InMemoryPushNotificationConfigStore: Fixed bug where pageSize - 1 was used instead of pageSize, causing one less item to be returned than requested - JpaDatabasePushNotificationConfigStore: Implemented pagination logic matching the in-memory implementation - Added helper methods for pagination: findFirstIndex, convertPushNotificationConfig - Updated all test files with getInfoAsList helper method to maintain compatibility Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes #535 🦕 --------- Signed-off-by: Emmanuel Hugonnet Co-authored-by: Claude Sonnet 4.5 Co-authored-by: Kabir Khan Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../java/io/a2a/client/AbstractClient.java | 13 +- .../src/main/java/io/a2a/client/Client.java | 3 +- .../client/transport/grpc/GrpcTransport.java | 10 +- .../transport/jsonrpc/JSONRPCTransport.java | 3 +- .../client/transport/rest/RestTransport.java | 5 +- .../transport/rest/RestTransportTest.java | 9 +- .../client/transport/spi/ClientTransport.java | 7 +- ...paDatabasePushNotificationConfigStore.java | 55 ++- ...otificationConfigStoreIntegrationTest.java | 235 ++++++++++++ .../JpaPushNotificationConfigStoreTest.java | 99 +++--- .../apps/quarkus/A2AServerRoutesTest.java | 4 +- .../server/rest/quarkus/A2AServerRoutes.java | 30 +- .../rest/quarkus/A2AServerRoutesTest.java | 5 +- .../java/io/a2a/server/JSONRPCException.java | 17 + .../a2a/server/events/EventEnqueueHook.java | 10 + .../java/io/a2a/server/events/EventQueue.java | 142 ++++++++ .../events/EventQueueClosedException.java | 4 + .../server/events/NoTaskQueueException.java | 35 ++ .../DefaultRequestHandler.java | 32 +- .../requesthandlers/RequestHandler.java | 3 +- .../tasks/BasePushNotificationSender.java | 9 +- .../InMemoryPushNotificationConfigStore.java | 53 ++- .../a2a/server/tasks/InMemoryTaskStore.java | 11 + .../tasks/PushNotificationConfigStore.java | 10 +- .../java/io/a2a/server/tasks/TaskStore.java | 23 ++ .../io/a2a/server/util/async/Internal.java | 7 + .../DefaultRequestHandlerTest.java | 22 +- ...MemoryPushNotificationConfigStoreTest.java | 334 +++++++++++++++--- ...askPushNotificationConfigParamsMapper.java | 3 - .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 2 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 21 +- .../ListTaskPushNotificationConfigParams.java | 5 +- ...istTaskPushNotificationConfigResponse.java | 24 +- .../ListTaskPushNotificationConfigResult.java | 63 ++++ spec/src/main/java/io/a2a/util/Utils.java | 2 +- .../apps/common/AbstractA2AServerTest.java | 21 +- .../transport/grpc/handler/GrpcHandler.java | 11 +- .../jsonrpc/handler/JSONRPCHandler.java | 5 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 2 +- .../transport/rest/handler/RestHandler.java | 9 +- .../rest/handler/RestHandlerTest.java | 2 +- 41 files changed, 1142 insertions(+), 218 deletions(-) create mode 100644 spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index 37749533e..414c25f73 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -13,6 +13,7 @@ import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; @@ -271,26 +272,26 @@ public abstract TaskPushNotificationConfig getTaskPushNotificationConfiguration( @Nullable ClientCallContext context) throws A2AClientException; /** - * Retrieve the list of push notification configurations for a specific task. + * Retrieve the list of push notification configurations for a specific task with pagination support. * * @param request the parameters specifying which task's notification configs to retrieve - * @return the list of task push notification configs + * @return the result containing the list of task push notification configs and pagination information * @throws A2AClientException if getting the task push notification configs fails for any reason */ - public List listTaskPushNotificationConfigurations( + public ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request) throws A2AClientException { return listTaskPushNotificationConfigurations(request, null); } /** - * Retrieve the list of push notification configurations for a specific task. + * Retrieve the list of push notification configurations for a specific task with pagination support. * * @param request the parameters specifying which task's notification configs to retrieve * @param context optional client call context for the request (may be {@code null}) - * @return the list of task push notification configs + * @return the result containing the list of task push notification configs and pagination information * @throws A2AClientException if getting the task push notification configs fails for any reason */ - public abstract List listTaskPushNotificationConfigurations( + public abstract ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException; diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index 0ca73446d..cb8ef954b 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -16,6 +16,7 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; @@ -108,7 +109,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( } @Override - public List listTaskPushNotificationConfigurations( + public ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { return clientTransport.listTaskPushNotificationConfigurations(request, context); } diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 101c599d1..a846210d9 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -29,6 +29,7 @@ import io.a2a.spec.GetTaskRequest; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksRequest; import io.a2a.spec.ListTasksResult; @@ -265,7 +266,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( } @Override - public List listTaskPushNotificationConfigurations( + public ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); @@ -273,15 +274,16 @@ public List listTaskPushNotificationConfigurations( io.a2a.grpc.ListTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder() .setParent("tasks/" + request.id()) .setTenant(resolveTenant(request.tenant())) + .setPageSize(request.pageSize()) + .setPageToken(request.pageToken()) .build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, grpcRequest, agentCard, context); try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); - return stubWithMetadata.listTaskPushNotificationConfig(grpcRequest).getConfigsList().stream() - .map(FromProto::taskPushNotificationConfig) - .collect(Collectors.toList()); + io.a2a.grpc.ListTaskPushNotificationConfigResponse grpcResponse = stubWithMetadata.listTaskPushNotificationConfig(grpcRequest); + return FromProto.listTaskPushNotificationConfigResult(grpcResponse); } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to list task push notification config: "); } diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 0a453bff4..250503f96 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -45,6 +45,7 @@ import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksRequest; import io.a2a.spec.ListTasksResponse; @@ -222,7 +223,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu } @Override - public List listTaskPushNotificationConfigurations( + public ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 41614744c..500daf6f8 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -41,6 +41,7 @@ import io.a2a.spec.GetTaskRequest; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigRequest; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksRequest; import io.a2a.spec.ListTasksResult; @@ -334,7 +335,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu } @Override - public List listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { + public ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations(ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder(); @@ -356,7 +357,7 @@ public List listTaskPushNotificationConfigurations(L String httpResponseBody = response.body(); io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); - return ProtoUtils.FromProto.listTaskPushNotificationConfigParams(responseBuilder); + return ProtoUtils.FromProto.listTaskPushNotificationConfigResult(responseBuilder); } catch (A2AClientException e) { throw e; } catch (IOException | InterruptedException e) { diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 784ddb7e2..954fafcae 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -44,6 +44,7 @@ import io.a2a.spec.FileWithUri; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -361,10 +362,10 @@ public void testListTaskPushNotificationConfigurations() throws Exception { ); RestTransport client = new RestTransport(CARD); - List taskPushNotificationConfigs = client.listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = client.listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams("de38c76d-d54c-436c-8b9f-4c2703648d64"), null); - assertEquals(2, taskPushNotificationConfigs.size()); - PushNotificationConfig pushNotificationConfig = taskPushNotificationConfigs.get(0).pushNotificationConfig(); + assertEquals(2, result.configs().size()); + PushNotificationConfig pushNotificationConfig = result.configs().get(0).pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); assertEquals("10", pushNotificationConfig.id()); @@ -372,7 +373,7 @@ public void testListTaskPushNotificationConfigurations() throws Exception { assertTrue(authenticationInfo.schemes().size() == 1); assertEquals("jwt", authenticationInfo.schemes().get(0)); assertEquals("", authenticationInfo.credentials()); - pushNotificationConfig = taskPushNotificationConfigs.get(1).pushNotificationConfig(); + pushNotificationConfig = result.configs().get(1).pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://test.com/callback", pushNotificationConfig.url()); assertEquals("5", pushNotificationConfig.id()); diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 202e80492..4b978c717 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -10,6 +10,7 @@ import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; @@ -102,14 +103,14 @@ TaskPushNotificationConfig getTaskPushNotificationConfiguration( @Nullable ClientCallContext context) throws A2AClientException; /** - * Retrieve the list of push notification configurations for a specific task. + * Retrieve the list of push notification configurations for a specific task with pagination support. * * @param request the parameters specifying which task's notification configs to retrieve * @param context optional client call context for the request (may be {@code null}) - * @return the list of task push notification configs + * @return the result containing the list of task push notification configs and pagination information * @throws A2AClientException if getting the task push notification configs fails for any reason */ - List listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult listTaskPushNotificationConfigurations( ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException; diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index b2eb7ba18..357cf42e1 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -1,5 +1,7 @@ package io.a2a.extras.pushnotificationconfigstore.database.jpa; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import jakarta.annotation.Priority; @@ -11,7 +13,10 @@ import io.a2a.json.JsonProcessingException; import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.TaskPushNotificationConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +70,8 @@ public PushNotificationConfig setInfo(String taskId, PushNotificationConfig noti @Transactional @Override - public List getInfo(String taskId) { + public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConfigParams params) { + String taskId = params.id(); LOGGER.debug("Retrieving PushNotificationConfigs for Task '{}'", taskId); try { List jpaConfigs = em.createQuery( @@ -88,13 +94,58 @@ public List getInfo(String taskId) { .toList(); LOGGER.debug("Successfully retrieved {} PushNotificationConfigs for Task '{}'", configs.size(), taskId); - return configs; + + // Handle pagination + if (configs.isEmpty()) { + return new ListTaskPushNotificationConfigResult(Collections.emptyList()); + } + + if (params.pageSize() <= 0) { + return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs, params), null); + } + + // Apply pageToken filtering if provided + List paginatedConfigs = configs; + if (params.pageToken() != null && !params.pageToken().isBlank()) { + int index = findFirstIndex(configs, params.pageToken()); + if (index < configs.size()) { + paginatedConfigs = configs.subList(index, configs.size()); + } + } + + // Apply page size limit + if (paginatedConfigs.size() <= params.pageSize()) { + return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(paginatedConfigs, params), null); + } + + String nextToken = paginatedConfigs.get(params.pageSize()).token(); + return new ListTaskPushNotificationConfigResult( + convertPushNotificationConfig(paginatedConfigs.subList(0, params.pageSize()), params), + nextToken); } catch (Exception e) { LOGGER.error("Failed to retrieve PushNotificationConfigs for Task '{}'", taskId, e); throw e; } } + private int findFirstIndex(List configs, String token) { + for (int i = 0; i < configs.size(); i++) { + if (token.equals(configs.get(i).token())) { + return i; + } + } + return configs.size(); + } + + private List convertPushNotificationConfig(List pushNotificationConfigList, ListTaskPushNotificationConfigParams params) { + List taskPushNotificationConfigList = new ArrayList<>(pushNotificationConfigList.size()); + for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) { + TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, params.tenant()); + taskPushNotificationConfigList.add(taskPushNotificationConfig); + } + return taskPushNotificationConfigList; + } + @Transactional @Override public void deleteInfo(String taskId, String configId) { diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index e92a19b06..6871a0f09 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -3,10 +3,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import java.util.List; import java.util.Queue; @@ -23,6 +25,8 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.Message; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; @@ -182,4 +186,235 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep client.getTaskPushNotificationConfiguration(new GetTaskPushNotificationConfigParams(taskId)); }, "Getting a deleted config should throw an A2AClientException"); } + + private PushNotificationConfig createSamplePushConfig(String url, String configId, String token) { + return PushNotificationConfig.builder() + .url(url) + .id(configId) + .token(token) + .build(); + } + + @Test + @Transactional + public void testPaginationWithPageSize() { + String taskId = "task_pagination_" + System.currentTimeMillis(); + // Create 5 configs + for (int i = 0; i < 5; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + pushNotificationConfigStore.setInfo(taskId, config); + } + + // Request first page with pageSize=2 + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(2, result.configs().size(), "Should return 2 configs"); + assertNotNull(result.nextPageToken(), "Should have nextPageToken when more items exist"); + } + + @Test + @Transactional + public void testPaginationWithPageToken() { + String taskId = "task_pagination_token_" + System.currentTimeMillis(); + // Create 5 configs + createSamples(taskId, 5); + + // Get first page + ListTaskPushNotificationConfigParams firstPageParams = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult firstPage = pushNotificationConfigStore.getInfo(firstPageParams); + assertNotNull(firstPage.nextPageToken()); + + // Get second page using nextPageToken + ListTaskPushNotificationConfigParams secondPageParams = new ListTaskPushNotificationConfigParams( + taskId, 2, firstPage.nextPageToken(), ""); + ListTaskPushNotificationConfigResult secondPage = pushNotificationConfigStore.getInfo(secondPageParams); + + assertNotNull(secondPage); + assertEquals(2, secondPage.configs().size(), "Should return 2 configs for second page"); + assertNotNull(secondPage.nextPageToken(), "Should have nextPageToken when more items exist"); + + // Verify NO overlap between pages - collect all IDs from both pages + List firstPageIds = firstPage.configs().stream() + .map(c -> c.pushNotificationConfig().id()) + .toList(); + List secondPageIds = secondPage.configs().stream() + .map(c -> c.pushNotificationConfig().id()) + .toList(); + + // Check that no ID from first page appears in second page + for (String id : firstPageIds) { + assertTrue(!secondPageIds.contains(id), + "Config " + id + " appears in both pages - overlap detected!"); + } + + // Also verify the pages are sequential (first page ends before second page starts) + // Since configs are created in order, we can verify the IDs + assertEquals("cfg0", firstPageIds.get(0)); + assertEquals("cfg1", firstPageIds.get(1)); + assertEquals("cfg2", secondPageIds.get(0)); + assertEquals("cfg3", secondPageIds.get(1)); + } + + @Test + @Transactional + public void testPaginationLastPage() { + String taskId = "task_pagination_last_" + System.currentTimeMillis(); + // Create 5 configs + createSamples(taskId, 5); + + // Get first page (2 items) + ListTaskPushNotificationConfigParams firstPageParams = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult firstPage = pushNotificationConfigStore.getInfo(firstPageParams); + + // Get second page (2 items) + ListTaskPushNotificationConfigParams secondPageParams = new ListTaskPushNotificationConfigParams( + taskId, 2, firstPage.nextPageToken(), ""); + ListTaskPushNotificationConfigResult secondPage = pushNotificationConfigStore.getInfo(secondPageParams); + + // Get last page (1 item remaining) + ListTaskPushNotificationConfigParams lastPageParams = new ListTaskPushNotificationConfigParams( + taskId, 2, secondPage.nextPageToken(), ""); + ListTaskPushNotificationConfigResult lastPage = pushNotificationConfigStore.getInfo(lastPageParams); + + assertNotNull(lastPage); + assertEquals(1, lastPage.configs().size(), "Last page should have 1 remaining config"); + assertNull(lastPage.nextPageToken(), "Last page should not have nextPageToken"); + } + + @Test + @Transactional + public void testPaginationWithZeroPageSize() { + String taskId = "task_pagination_zero_" + System.currentTimeMillis(); + // Create 5 configs + createSamples(taskId, 5); + + // Request with pageSize=0 should return all configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 0, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(5, result.configs().size(), "Should return all 5 configs when pageSize=0"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when returning all"); + } + + @Test + @Transactional + public void testPaginationWithNegativePageSize() { + String taskId = "task_pagination_negative_" + System.currentTimeMillis(); + // Create 3 configs + createSamples(taskId, 3); + + // Request with negative pageSize should return all configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, -1, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(3, result.configs().size(), "Should return all configs when pageSize is negative"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when returning all"); + } + + @Test + @Transactional + public void testPaginationPageSizeLargerThanConfigs() { + String taskId = "task_pagination_large_" + System.currentTimeMillis(); + // Create 3 configs + createSamples(taskId, 3); + + // Request with pageSize larger than available configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 10, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(3, result.configs().size(), "Should return all 3 configs"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when all configs fit in one page"); + } + + @Test + @Transactional + public void testPaginationExactlyPageSize() { + String taskId = "task_pagination_exact_" + System.currentTimeMillis(); + // Create exactly 3 configs + createSamples(taskId, 3); + + // Request with pageSize equal to number of configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 3, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(3, result.configs().size(), "Should return all 3 configs"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when configs exactly match pageSize"); + } + + @Test + @Transactional + public void testPaginationWithInvalidToken() { + String taskId = "task_pagination_invalid_token_" + System.currentTimeMillis(); + // Create 5 configs + createSamples(taskId, 5); + + // Request with invalid pageToken - JPA implementation behavior is to start from beginning + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams( + taskId, 2, "invalid_token_that_does_not_exist", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + // When token is not found, implementation starts from beginning + assertEquals(2, result.configs().size(), "Should return first page when token is not found"); + assertNotNull(result.nextPageToken(), "Should have nextPageToken since more items exist"); + } + + @Test + @Transactional + public void testPaginationEmptyTaskWithPageSize() { + String taskId = "task_pagination_empty_" + System.currentTimeMillis(); + // No configs created + + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertTrue(result.configs().isEmpty(), "Should return empty list for non-existent task"); + assertNull(result.nextPageToken(), "Should not have nextPageToken for empty result"); + } + + @Test + @Transactional + public void testPaginationFullIteration() { + String taskId = "task_pagination_full_" + System.currentTimeMillis(); + // Create 7 configs + createSamples(taskId, 7); + + // Iterate through all pages with pageSize=3 + int totalCollected = 0; + String pageToken = ""; + int pageCount = 0; + + do { + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 3, pageToken, ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + totalCollected += result.configs().size(); + pageToken = result.nextPageToken(); + pageCount++; + + // Safety check to prevent infinite loop + assertTrue(pageCount <= 7, "Should not have more than 7 pages for 7 configs"); + + } while (pageToken != null); + + assertEquals(7, totalCollected, "Should collect all 7 configs across all pages"); + assertEquals(3, pageCount, "Should have exactly 3 pages (3+3+1)"); + } + + private void createSamples(String taskId, int size) { + // Create 7 configs + for (int i = 0; i < size; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + pushNotificationConfigStore.setInfo(taskId, config); + } + } } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java index 2648cba87..1074b7848 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java @@ -28,8 +28,11 @@ import io.a2a.client.http.A2AHttpResponse; import io.a2a.server.tasks.BasePushNotificationSender; import io.a2a.server.tasks.PushNotificationConfigStore; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; +import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.quarkus.test.junit.QuarkusTest; @@ -90,11 +93,11 @@ public void testSetInfoAddsNewConfig() { assertEquals(config.url(), result.url()); assertEquals(config.id(), result.id()); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(1, configs.size()); - assertEquals(config.url(), configs.get(0).url()); - assertEquals(config.id(), configs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); + assertEquals(config.url(), configResult.configs().get(0).pushNotificationConfig().url()); + assertEquals(config.id(), configResult.configs().get(0).pushNotificationConfig().id()); } @Test @@ -109,11 +112,14 @@ public void testSetInfoAppendsToExistingConfig() { "http://updated.url/callback", "cfg_updated", null); configStore.setInfo(taskId, updatedConfig); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(2, configs.size()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(2, configResult.configs().size()); // Find the configs by ID since order might vary + List configs = configResult.configs().stream() + .map(TaskPushNotificationConfig::pushNotificationConfig) + .toList(); PushNotificationConfig foundInitial = configs.stream() .filter(c -> "cfg_initial".equals(c.id())) .findFirst() @@ -140,9 +146,9 @@ public void testSetInfoWithoutConfigId() { PushNotificationConfig result = configStore.setInfo(taskId, initialConfig); assertEquals(taskId, result.id(), "Config ID should default to taskId when not provided"); - List configs = configStore.getInfo(taskId); - assertEquals(1, configs.size()); - assertEquals(taskId, configs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertEquals(1, configResult.configs().size()); + assertEquals(taskId, configResult.configs().get(0).pushNotificationConfig().id()); PushNotificationConfig updatedConfig = PushNotificationConfig.builder() .url("http://initial.url/callback_new") @@ -151,9 +157,9 @@ public void testSetInfoWithoutConfigId() { PushNotificationConfig updatedResult = configStore.setInfo(taskId, updatedConfig); assertEquals(taskId, updatedResult.id()); - configs = configStore.getInfo(taskId); - assertEquals(1, configs.size(), "Should replace existing config with same ID rather than adding new one"); - assertEquals(updatedConfig.url(), configs.get(0).url()); + configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertEquals(1, configResult.configs().size(), "Should replace existing config with same ID rather than adding new one"); + assertEquals(updatedConfig.url(), configResult.configs().get(0).pushNotificationConfig().url()); } @Test @@ -163,20 +169,20 @@ public void testGetInfoExistingConfig() { PushNotificationConfig config = createSamplePushConfig("http://get.this/callback", "cfg1", null); configStore.setInfo(taskId, config); - List retrievedConfigs = configStore.getInfo(taskId); - assertNotNull(retrievedConfigs); - assertEquals(1, retrievedConfigs.size()); - assertEquals(config.url(), retrievedConfigs.get(0).url()); - assertEquals(config.id(), retrievedConfigs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); + assertEquals(config.url(), configResult.configs().get(0).pushNotificationConfig().url()); + assertEquals(config.id(), configResult.configs().get(0).pushNotificationConfig().id()); } @Test @Transactional public void testGetInfoNonExistentConfig() { String taskId = "task_get_non_exist"; - List retrievedConfigs = configStore.getInfo(taskId); - assertNotNull(retrievedConfigs); - assertTrue(retrievedConfigs.isEmpty(), "Should return empty list for non-existent task ID"); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertTrue(configResult.configs().isEmpty(), "Should return empty list for non-existent task ID"); } @Test @@ -186,15 +192,15 @@ public void testDeleteInfoExistingConfig() { PushNotificationConfig config = createSamplePushConfig("http://delete.this/callback", "cfg1", null); configStore.setInfo(taskId, config); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(1, configs.size()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); configStore.deleteInfo(taskId, config.id()); - List configsAfterDelete = configStore.getInfo(taskId); + ListTaskPushNotificationConfigResult configsAfterDelete = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); assertNotNull(configsAfterDelete); - assertTrue(configsAfterDelete.isEmpty(), "Should return empty list when no configs remain after deletion"); + assertTrue(configsAfterDelete.configs().isEmpty(), "Should return empty list when no configs remain after deletion"); } @Test @@ -204,9 +210,9 @@ public void testDeleteInfoNonExistentConfig() { // Should not throw an error configStore.deleteInfo(taskId, "non_existent_id"); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertTrue(configs.isEmpty(), "Should return empty list for non-existent task ID"); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertTrue(configResult.configs().isEmpty(), "Should return empty list for non-existent task ID"); } @Test @@ -221,9 +227,9 @@ public void testDeleteInfoWithNullConfigId() { // Delete with null configId should use taskId configStore.deleteInfo(taskId, null); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertTrue(configs.isEmpty(), "Should return empty list after deletion when using taskId as configId"); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertTrue(configResult.configs().isEmpty(), "Should return empty list after deletion when using taskId as configId"); } @Test @@ -316,11 +322,14 @@ public void testMultipleConfigsForSameTask() { configStore.setInfo(taskId, config1); configStore.setInfo(taskId, config2); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(2, configs.size()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(2, configResult.configs().size()); // Verify both configs are present + List configs = configResult.configs().stream() + .map(TaskPushNotificationConfig::pushNotificationConfig) + .toList(); assertTrue(configs.stream().anyMatch(c -> "cfg1".equals(c.id()))); assertTrue(configs.stream().anyMatch(c -> "cfg2".equals(c.id()))); } @@ -338,10 +347,10 @@ public void testDeleteSpecificConfigFromMultiple() { // Delete only config1 configStore.deleteInfo(taskId, "cfg1"); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(1, configs.size()); - assertEquals("cfg2", configs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); + assertEquals("cfg2", configResult.configs().get(0).pushNotificationConfig().id()); } @Test @@ -355,13 +364,13 @@ public void testConfigStoreIntegration() { assertEquals(config.url(), storedConfig.url()); assertEquals(config.token(), storedConfig.token()); - List retrievedConfigs = configStore.getInfo(taskId); - assertEquals(1, retrievedConfigs.size()); - assertEquals(config.url(), retrievedConfigs.get(0).url()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertEquals(1, configResult.configs().size()); + assertEquals(config.url(), configResult.configs().get(0).pushNotificationConfig().url()); // Test deletion configStore.deleteInfo(taskId, storedConfig.id()); - List afterDeletion = configStore.getInfo(taskId); - assertTrue(afterDeletion.isEmpty()); + ListTaskPushNotificationConfigResult afterDeletion = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertTrue(afterDeletion.configs().isEmpty()); } } diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java index 8de9dd893..5c7582da5 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -2,6 +2,7 @@ import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; +import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -33,6 +34,7 @@ import io.a2a.spec.GetTaskResponse; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.SendMessageRequest; import io.a2a.spec.SendMessageResponse; @@ -421,7 +423,7 @@ public void testListTaskPushNotificationConfig_MethodNameSetInContext() { .build(), null ); - ListTaskPushNotificationConfigResponse realResponse = new ListTaskPushNotificationConfigResponse("1", Collections.singletonList(config)); + ListTaskPushNotificationConfigResponse realResponse = new ListTaskPushNotificationConfigResponse("1", new ListTaskPushNotificationConfigResult(singletonList(config))); when(mockJsonRpcHandler.listPushNotificationConfig(any(ListTaskPushNotificationConfigRequest.class), any(ServerCallContext.class))).thenReturn(realResponse); diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 29f150943..f3b5f076c 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -55,12 +55,17 @@ import io.a2a.spec.SendStreamingMessageRequest; import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.SubscribeToTaskRequest; +import io.a2a.util.Utils; import org.jspecify.annotations.Nullable; @Singleton @Authenticated public class A2AServerRoutes { + private static final String HISTORY_LENGTH_PARAM = "historyLength"; + private static final String PAGE_SIZE_PARAM = "pageSize"; + private static final String PAGE_TOKEN_PARAM = "pageToken"; + @Inject RestHandler jsonRestHandler; @@ -125,9 +130,9 @@ public void listTasks(RoutingContext rc) { if (statusStr != null && !statusStr.isEmpty()) { statusStr = statusStr.toUpperCase(); } - String pageSizeStr = rc.request().params().get("pageSize"); - String pageToken = rc.request().params().get("pageToken"); - String historyLengthStr = rc.request().params().get("historyLength"); + String pageSizeStr = rc.request().params().get(PAGE_SIZE_PARAM); + String pageToken = rc.request().params().get(PAGE_TOKEN_PARAM); + String historyLengthStr = rc.request().params().get(HISTORY_LENGTH_PARAM); String lastUpdatedAfter = rc.request().params().get("lastUpdatedAfter"); String includeArtifactsStr = rc.request().params().get("includeArtifacts"); @@ -170,13 +175,13 @@ public void getTask(RoutingContext rc) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { Integer historyLength = null; - if (rc.request().params().contains("history_length")) { - historyLength = Integer.valueOf(rc.request().params().get("history_length")); + if (rc.request().params().contains(HISTORY_LENGTH_PARAM)) { + historyLength = Integer.valueOf(rc.request().params().get(HISTORY_LENGTH_PARAM)); } response = jsonRestHandler.getTask(taskId, historyLength, extractTenant(rc), context); } } catch (NumberFormatException e) { - response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad history_length")); + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad historyLength")); } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { @@ -312,8 +317,18 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, extractTenant(rc), context); + int pageSize = 0; + if (rc.request().params().contains(PAGE_SIZE_PARAM)) { + pageSize = Integer.parseInt(rc.request().params().get(PAGE_SIZE_PARAM)); + } + String pageToken = ""; + if (rc.request().params().contains(PAGE_TOKEN_PARAM)) { + pageToken = Utils.defaultIfNull(rc.request().params().get(PAGE_TOKEN_PARAM), ""); + } + response = jsonRestHandler.listTaskPushNotificationConfigurations(taskId, pageSize, pageToken, extractTenant(rc), context); } + } catch (NumberFormatException e) { + response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad " + PAGE_SIZE_PARAM)); } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); } finally { @@ -355,6 +370,7 @@ private String extractTenant(RoutingContext rc) { } return tenantPath; } + /** * /** * Handles incoming GET requests to the agent card endpoint. diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java index 84c7401e4..6ff3792c2 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -252,7 +253,7 @@ public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.listTaskPushNotificationConfigurations(anyString(), anyString(), any(ServerCallContext.class))) + when(mockRestHandler.listTaskPushNotificationConfigurations(anyString(), anyInt(), anyString(), anyString(), any(ServerCallContext.class))) .thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -261,7 +262,7 @@ public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() routes.listTaskPushNotificationConfigurations(mockRoutingContext); // Assert - verify(mockRestHandler).listTaskPushNotificationConfigurations(eq("task123"), anyString(), contextCaptor.capture()); + verify(mockRestHandler).listTaskPushNotificationConfigurations(eq("task123"), anyInt(), anyString(), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(ListTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); diff --git a/server-common/src/main/java/io/a2a/server/JSONRPCException.java b/server-common/src/main/java/io/a2a/server/JSONRPCException.java index d7d7d2c8a..cf7416b91 100644 --- a/server-common/src/main/java/io/a2a/server/JSONRPCException.java +++ b/server-common/src/main/java/io/a2a/server/JSONRPCException.java @@ -2,13 +2,30 @@ import io.a2a.spec.JSONRPCError; +/** + * Exception wrapper for JSON-RPC protocol errors. + *

      + * This exception encapsulates a {@link JSONRPCError} for handling + * protocol-level errors during JSON-RPC request processing. + *

      + */ public class JSONRPCException extends Exception{ private final JSONRPCError error; + /** + * Creates a JSONRPCException wrapping the specified error. + * + * @param error the JSON-RPC error + */ public JSONRPCException(JSONRPCError error) { this.error = error; } + /** + * Returns the wrapped JSON-RPC error. + * + * @return the JSON-RPC error + */ public JSONRPCError getError() { return error; } diff --git a/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java b/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java index 7bc19bf71..e61284917 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java +++ b/server-common/src/main/java/io/a2a/server/events/EventEnqueueHook.java @@ -1,5 +1,15 @@ package io.a2a.server.events; +/** + * Hook interface for event queue enqueue operations. + * Implementations can be notified when items are enqueued to the event queue, + * allowing for custom behavior such as event replication or logging. + */ public interface EventEnqueueHook { + /** + * Called when an item is enqueued to the event queue. + * + * @param item the event queue item being enqueued + */ void onEnqueue(EventQueueItem item); } \ No newline at end of file diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 92cc015d9..7ce8ad3aa 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -16,21 +16,50 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Abstract base class for event queues that manage task event streaming. + *

      + * An EventQueue provides a thread-safe mechanism for enqueueing and dequeueing events + * related to task execution. It supports backpressure through semaphore-based throttling + * and hierarchical queue structures via MainQueue and ChildQueue implementations. + *

      + *

      + * Use {@link #builder()} to create configured instances or extend MainQueue/ChildQueue directly. + *

      + */ public abstract class EventQueue implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(EventQueue.class); + /** + * Default maximum queue size for event queues. + */ public static final int DEFAULT_QUEUE_SIZE = 1000; private final int queueSize; + /** + * Internal blocking queue for storing event queue items. + */ protected final BlockingQueue queue = new LinkedBlockingDeque<>(); + /** + * Semaphore for backpressure control, limiting the number of pending events. + */ protected final Semaphore semaphore; private volatile boolean closed = false; + /** + * Creates an EventQueue with the default queue size. + */ protected EventQueue() { this(DEFAULT_QUEUE_SIZE); } + /** + * Creates an EventQueue with the specified queue size. + * + * @param queueSize the maximum number of events that can be queued + * @throws IllegalArgumentException if queueSize is less than or equal to 0 + */ protected EventQueue(int queueSize) { if (queueSize <= 0) { throw new IllegalArgumentException("Queue size must be greater than 0"); @@ -40,6 +69,11 @@ protected EventQueue(int queueSize) { LOGGER.trace("Creating {} with queue size: {}", this, queueSize); } + /** + * Creates an EventQueue as a child of the specified parent queue. + * + * @param parent the parent event queue + */ protected EventQueue(EventQueue parent) { this(DEFAULT_QUEUE_SIZE); LOGGER.trace("Creating {}, parent: {}", this, parent); @@ -49,6 +83,13 @@ static EventQueueBuilder builder() { return new EventQueueBuilder(); } + /** + * Builder for creating configured EventQueue instances. + *

      + * Supports configuration of queue size, enqueue hooks, task association, + * close callbacks, and task state providers. + *

      + */ public static class EventQueueBuilder { private int queueSize = DEFAULT_QUEUE_SIZE; private @Nullable EventEnqueueHook hook; @@ -56,21 +97,45 @@ public static class EventQueueBuilder { private List onCloseCallbacks = new java.util.ArrayList<>(); private @Nullable TaskStateProvider taskStateProvider; + /** + * Sets the maximum queue size. + * + * @param queueSize the maximum number of events that can be queued + * @return this builder + */ public EventQueueBuilder queueSize(int queueSize) { this.queueSize = queueSize; return this; } + /** + * Sets the enqueue hook for event replication or logging. + * + * @param hook the hook to be invoked when items are enqueued + * @return this builder + */ public EventQueueBuilder hook(EventEnqueueHook hook) { this.hook = hook; return this; } + /** + * Associates this queue with a specific task ID. + * + * @param taskId the task identifier + * @return this builder + */ public EventQueueBuilder taskId(String taskId) { this.taskId = taskId; return this; } + /** + * Adds a callback to be executed when the queue is closed. + * + * @param onCloseCallback the callback to execute on close + * @return this builder + */ public EventQueueBuilder addOnCloseCallback(Runnable onCloseCallback) { if (onCloseCallback != null) { this.onCloseCallbacks.add(onCloseCallback); @@ -78,11 +143,22 @@ public EventQueueBuilder addOnCloseCallback(Runnable onCloseCallback) { return this; } + /** + * Sets the task state provider for tracking task finalization. + * + * @param taskStateProvider the task state provider + * @return this builder + */ public EventQueueBuilder taskStateProvider(TaskStateProvider taskStateProvider) { this.taskStateProvider = taskStateProvider; return this; } + /** + * Builds and returns the configured EventQueue. + * + * @return a new MainQueue instance + */ public EventQueue build() { if (hook != null || !onCloseCallbacks.isEmpty() || taskStateProvider != null) { return new MainQueue(queueSize, hook, taskId, onCloseCallbacks, taskStateProvider); @@ -92,18 +168,48 @@ public EventQueue build() { } } + /** + * Returns the configured queue size. + * + * @return the maximum number of events that can be queued + */ public int getQueueSize() { return queueSize; } + /** + * Waits for the queue poller to start consuming events. + * This method blocks until signaled by {@link #signalQueuePollerStarted()}. + * + * @throws InterruptedException if the thread is interrupted while waiting + */ public abstract void awaitQueuePollerStart() throws InterruptedException ; + /** + * Signals that the queue poller has started consuming events. + * This unblocks any threads waiting in {@link #awaitQueuePollerStart()}. + */ public abstract void signalQueuePollerStarted(); + /** + * Enqueues an event for processing. + * + * @param event the event to enqueue + */ public void enqueueEvent(Event event) { enqueueItem(new LocalEventQueueItem(event)); } + /** + * Enqueues an event queue item for processing. + *

      + * This method will block if the queue is full, waiting to acquire a semaphore permit. + * If the queue is closed, the event will not be enqueued and a warning will be logged. + *

      + * + * @param item the event queue item to enqueue + * @throws RuntimeException if interrupted while waiting to acquire the semaphore + */ public void enqueueItem(EventQueueItem item) { Event event = item.getEvent(); if (closed) { @@ -121,6 +227,16 @@ public void enqueueItem(EventQueueItem item) { LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); } + /** + * Creates a child queue that shares events with this queue. + *

      + * For MainQueue: creates a ChildQueue that receives all events enqueued to the parent. + * For ChildQueue: throws IllegalStateException (only MainQueue can be tapped). + *

      + * + * @return a new ChildQueue instance + * @throws IllegalStateException if called on a ChildQueue + */ public abstract EventQueue tap(); /** @@ -172,12 +288,24 @@ public void enqueueItem(EventQueueItem item) { } } + /** + * Placeholder method for task completion notification. + * Currently not used as BlockingQueue.poll()/take() automatically remove events. + */ public void taskDone() { // TODO Not sure if needed yet. BlockingQueue.poll()/.take() remove the events. } + /** + * Closes this event queue gracefully, allowing pending events to be consumed. + */ public abstract void close(); + /** + * Closes this event queue with control over immediate shutdown. + * + * @param immediate if true, clears all pending events immediately; if false, allows graceful drain + */ public abstract void close(boolean immediate); /** @@ -191,14 +319,28 @@ public void taskDone() { */ public abstract void close(boolean immediate, boolean notifyParent); + /** + * Checks if this queue has been closed. + * + * @return true if the queue is closed, false otherwise + */ public boolean isClosed() { return closed; } + /** + * Internal method to close the queue gracefully. + * Delegates to {@link #doClose(boolean)} with immediate=false. + */ protected void doClose() { doClose(false); } + /** + * Internal method to close the queue with control over immediate shutdown. + * + * @param immediate if true, clears all pending events immediately; if false, allows graceful drain + */ protected void doClose(boolean immediate) { synchronized (this) { if (closed) { diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java b/server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java index f8feaa824..5fe80637d 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueueClosedException.java @@ -1,4 +1,8 @@ package io.a2a.server.events; +/** + * Exception thrown when attempting to dequeue from a closed and empty event queue. + * This signals to consumers that no more events will be available from the queue. + */ public class EventQueueClosedException extends Exception { } diff --git a/server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java b/server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java index d97c3a261..5783dd214 100644 --- a/server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java +++ b/server-common/src/main/java/io/a2a/server/events/NoTaskQueueException.java @@ -1,21 +1,56 @@ package io.a2a.server.events; +/** + * Exception thrown when attempting to access a task queue that does not exist. + *

      + * This exception is typically thrown when trying to retrieve or operate on + * an event queue for a task that has not been created or has been removed. + *

      + */ public class NoTaskQueueException extends RuntimeException { + /** + * Creates a NoTaskQueueException with no message. + */ public NoTaskQueueException() { } + /** + * Creates a NoTaskQueueException with the specified detail message. + * + * @param message the detail message + */ public NoTaskQueueException(String message) { super(message); } + /** + * Creates a NoTaskQueueException with the specified detail message and cause. + * + * @param message the detail message + * @param cause the cause + */ public NoTaskQueueException(String message, Throwable cause) { super(message, cause); } + /** + * Creates a NoTaskQueueException with the specified cause. + * + * @param cause the cause + */ public NoTaskQueueException(Throwable cause) { super(cause); } + /** + * Creates a NoTaskQueueException with the specified message, cause, + * suppression enabled or disabled, and writable stack trace enabled or disabled. + * + * @param message the detail message + * @param cause the cause + * @param enableSuppression whether suppression is enabled or disabled + * @param writableStackTrace whether the stack trace should be writable + */ public NoTaskQueueException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 00f4c3ad9..39fb01104 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -6,7 +6,6 @@ import static java.util.concurrent.TimeUnit.*; import java.time.Instant; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,6 +51,7 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; @@ -584,25 +584,25 @@ public TaskPushNotificationConfig onGetTaskPushNotificationConfig( throw new TaskNotFoundError(); } - List pushNotificationConfigList = pushConfigStore.getInfo(params.id()); - if (pushNotificationConfigList == null || pushNotificationConfigList.isEmpty()) { + ListTaskPushNotificationConfigResult listTaskPushNotificationConfigResult = pushConfigStore.getInfo(new ListTaskPushNotificationConfigParams(params.id())); + if (listTaskPushNotificationConfigResult == null || listTaskPushNotificationConfigResult.isEmpty()) { throw new InternalError("No push notification config found"); } @Nullable String configId = params.pushNotificationConfigId(); - return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(pushNotificationConfigList, configId), params.tenant()); + return new TaskPushNotificationConfig(params.id(), getPushNotificationConfig(listTaskPushNotificationConfigResult, configId), params.tenant()); } - private PushNotificationConfig getPushNotificationConfig(List notificationConfigList, + private PushNotificationConfig getPushNotificationConfig(ListTaskPushNotificationConfigResult notificationConfigList, @Nullable String configId) { if (configId != null) { - for (PushNotificationConfig notificationConfig : notificationConfigList) { - if (configId.equals(notificationConfig.id())) { - return notificationConfig; + for (TaskPushNotificationConfig notificationConfig : notificationConfigList.configs()) { + if (configId.equals(notificationConfig.pushNotificationConfig().id())) { + return notificationConfig.pushNotificationConfig(); } } } - return notificationConfigList.get(0); + return notificationConfigList.configs().get(0).pushNotificationConfig(); } @Override @@ -637,26 +637,16 @@ public Flow.Publisher onResubscribeToTask( } @Override - public List onListTaskPushNotificationConfig( + public ListTaskPushNotificationConfigResult onListTaskPushNotificationConfig( ListTaskPushNotificationConfigParams params, ServerCallContext context) throws JSONRPCError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } - Task task = taskStore.get(params.id()); if (task == null) { throw new TaskNotFoundError(); } - - List pushNotificationConfigList = pushConfigStore.getInfo(params.id()); - List taskPushNotificationConfigList = new ArrayList<>(); - if (pushNotificationConfigList != null) { - for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) { - TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, params.tenant()); - taskPushNotificationConfigList.add(taskPushNotificationConfig); - } - } - return taskPushNotificationConfigList; + return pushConfigStore.getInfo(params); } @Override diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index 64ba8eea1..ff7384e75 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -9,6 +9,7 @@ import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; @@ -51,7 +52,7 @@ Flow.Publisher onResubscribeToTask( TaskIdParams params, ServerCallContext context) throws JSONRPCError; - List onListTaskPushNotificationConfig( + ListTaskPushNotificationConfigResult onListTaskPushNotificationConfig( ListTaskPushNotificationConfigParams params, ServerCallContext context) throws JSONRPCError; diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index 8c07d904c..c6c887577 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -12,11 +12,12 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import io.a2a.json.JsonProcessingException; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.json.JsonUtil; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; @@ -44,14 +45,14 @@ public BasePushNotificationSender(PushNotificationConfigStore configStore, A2AHt @Override public void sendNotification(Task task) { - List pushConfigs = configStore.getInfo(task.id()); + ListTaskPushNotificationConfigResult pushConfigs = configStore.getInfo(new ListTaskPushNotificationConfigParams(task.id())); if (pushConfigs == null || pushConfigs.isEmpty()) { return; } - List> dispatchResults = pushConfigs + List> dispatchResults = pushConfigs.configs() .stream() - .map(pushConfig -> dispatch(task, pushConfig)) + .map(pushConfig -> dispatch(task, pushConfig.pushNotificationConfig())) .toList(); CompletableFuture allFutures = CompletableFuture.allOf(dispatchResults.toArray(new CompletableFuture[0])); CompletableFuture dispatchResult = allFutures.thenApply(v -> dispatchResults.stream() diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index 69ec5e15d..b278d32d6 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -1,5 +1,7 @@ package io.a2a.server.tasks; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -10,14 +12,13 @@ import java.util.List; import java.util.Map; -import org.jspecify.annotations.Nullable; - import io.a2a.spec.PushNotificationConfig; +import io.a2a.spec.TaskPushNotificationConfig; /** * In-memory implementation of the PushNotificationConfigStore interface. * - * Stores push notification configurations in memory + * Stores push notification configurations in memory */ @ApplicationScoped public class InMemoryPushNotificationConfigStore implements PushNotificationConfigStore { @@ -51,8 +52,48 @@ public PushNotificationConfig setInfo(String taskId, PushNotificationConfig noti } @Override - public @Nullable List getInfo(String taskId) { - return pushNotificationInfos.get(taskId); + public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConfigParams params) { + List configs = pushNotificationInfos.get(params.id()); + if (configs == null) { + return new ListTaskPushNotificationConfigResult(Collections.emptyList()); + } + if (params.pageSize() <= 0) { + return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs, params), null); + } + if (params.pageToken() != null && !params.pageToken().isBlank()) { + //find first index + int index = findFirstIndex(configs, params.pageToken()); + if (index < configs.size()) { + configs = configs.subList(index, configs.size()); + } + } + if (configs.size() <= params.pageSize()) { + return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs, params), null); + } + String newToken = configs.get(params.pageSize()).token(); + return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs.subList(0, params.pageSize()), params), newToken); + } + + private int findFirstIndex(List configs, String token) { + //find first index + Iterator iter = configs.iterator(); + int index = 0; + while (iter.hasNext()) { + if (token.equals(iter.next().token())) { + return index; + } + index++; + } + return index; + } + + private List convertPushNotificationConfig(List pushNotificationConfigList, ListTaskPushNotificationConfigParams params) { + List taskPushNotificationConfigList = new ArrayList<>(pushNotificationConfigList.size()); + for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) { + TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, params.tenant()); + taskPushNotificationConfigList.add(taskPushNotificationConfig); + } + return taskPushNotificationConfigList; } @Override @@ -77,4 +118,4 @@ public void deleteInfo(String taskId, String configId) { pushNotificationInfos.remove(taskId); } } -} \ No newline at end of file +} diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index bb853e297..669fee0a8 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -14,6 +14,17 @@ import io.a2a.spec.Task; import org.jspecify.annotations.Nullable; +/** + * In-memory implementation of {@link TaskStore} and {@link TaskStateProvider}. + *

      + * This implementation uses a {@link ConcurrentHashMap} to store tasks in memory. + * Tasks are lost on application restart. For persistent storage, use a database-backed + * implementation such as the JPA TaskStore in the extras module. + *

      + *

      + * This is the default TaskStore used when no other implementation is provided. + *

      + */ @ApplicationScoped public class InMemoryTaskStore implements TaskStore, TaskStateProvider { diff --git a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java index 6e721a6ec..16a658f0e 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java @@ -1,8 +1,8 @@ package io.a2a.server.tasks; -import java.util.List; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; -import org.jspecify.annotations.Nullable; import io.a2a.spec.PushNotificationConfig; @@ -21,10 +21,10 @@ public interface PushNotificationConfigStore { /** * Retrieves the push notification configuration for a task. - * @param taskId the task ID - * @return the push notification configurations for a task, or null if not found + * @param params the parameters for listing push notification configurations + * @return the push notification configurations for a task, or with empty list if not found */ - @Nullable List getInfo(String taskId); + ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConfigParams params); /** * Deletes the push notification configuration for a task. diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java index 6d4df5dbd..7d15e032a 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java @@ -6,11 +6,34 @@ import io.a2a.spec.ListTasksResult; import io.a2a.spec.Task; +/** + * Storage interface for managing task persistence. + *

      + * Implementations can use in-memory storage, databases, or other persistence mechanisms. + * Default implementation is {@code InMemoryTaskStore}. + *

      + */ public interface TaskStore { + /** + * Saves or updates a task. + * + * @param task the task to save + */ void save(Task task); + /** + * Retrieves a task by its ID. + * + * @param taskId the task identifier + * @return the task if found, null otherwise + */ @Nullable Task get(String taskId); + /** + * Deletes a task by its ID. + * + * @param taskId the task identifier + */ void delete(String taskId); /** diff --git a/server-common/src/main/java/io/a2a/server/util/async/Internal.java b/server-common/src/main/java/io/a2a/server/util/async/Internal.java index 2b8cd100e..7774d4838 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/Internal.java +++ b/server-common/src/main/java/io/a2a/server/util/async/Internal.java @@ -5,6 +5,13 @@ import jakarta.inject.Qualifier; +/** + * CDI qualifier for internal async executor beans. + *

      + * This qualifier is used to distinguish internal async executors from + * application-level executors when multiple executor beans exist. + *

      + */ @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Internal { diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index abf881752..31a57f137 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -22,6 +22,8 @@ import io.a2a.server.tasks.InMemoryTaskStore; import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.JSONRPCError; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -863,12 +865,13 @@ void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exceptio assertEquals(taskId, returnedTask.id()); // THE KEY ASSERTION: Verify pushNotificationConfig was stored - List storedConfigs = pushConfigStore.getInfo(taskId); + ListTaskPushNotificationConfigResult storedConfigs = pushConfigStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); assertNotNull(storedConfigs, "Push notification config should be stored for new task"); assertEquals(1, storedConfigs.size(), "Should have exactly 1 push config stored"); - assertEquals("config-1", storedConfigs.get(0).id()); - assertEquals("https://example.com/webhook", storedConfigs.get(0).url()); + PushNotificationConfig storedConfig = storedConfigs.configs().get(0).pushNotificationConfig(); + assertEquals("config-1", storedConfig.id()); + assertEquals("https://example.com/webhook", storedConfig.url()); } /** @@ -940,13 +943,12 @@ void testBlockingMessageStoresPushNotificationConfigForExistingTask() throws Exc assertTrue(result instanceof Task, "Result should be a Task"); // Verify pushNotificationConfig was stored (initMessageSend path) - List storedConfigs = pushConfigStore.getInfo(taskId); - assertNotNull(storedConfigs, - "Push notification config should be stored for existing task"); - assertEquals(1, storedConfigs.size(), - "Should have exactly 1 push config stored"); - assertEquals("config-existing-1", storedConfigs.get(0).id()); - assertEquals("https://example.com/existing-webhook", storedConfigs.get(0).url()); + ListTaskPushNotificationConfigResult storedConfigs = pushConfigStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(storedConfigs,"Push notification config should be stored for existing task"); + assertEquals(1, storedConfigs.size(),"Should have exactly 1 push config stored"); + PushNotificationConfig storedConfig = storedConfigs.configs().get(0).pushNotificationConfig(); + assertEquals("config-existing-1", storedConfig.id()); + assertEquals("https://example.com/existing-webhook", storedConfig.url()); } /** diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java index d90629b09..c8ccd669f 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java @@ -16,8 +16,11 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.common.A2AHeaders; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; +import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import org.junit.jupiter.api.BeforeEach; @@ -98,11 +101,11 @@ public void testSetInfoAddsNewConfig() { assertEquals(config.url(), result.url()); assertEquals(config.id(), result.id()); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(1, configs.size()); - assertEquals(config.url(), configs.get(0).url()); - assertEquals(config.id(), configs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); + assertEquals(config.url(), configResult.configs().get(0).pushNotificationConfig().url()); + assertEquals(config.id(), configResult.configs().get(0).pushNotificationConfig().id()); } @Test @@ -116,11 +119,14 @@ public void testSetInfoAppendsToExistingConfig() { "http://updated.url/callback", "cfg_updated", null); configStore.setInfo(taskId, updatedConfig); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(2, configs.size()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(2, configResult.configs().size()); // Find the configs by ID since order might vary + List configs = configResult.configs().stream() + .map(TaskPushNotificationConfig::pushNotificationConfig) + .toList(); PushNotificationConfig foundInitial = configs.stream() .filter(c -> "cfg_initial".equals(c.id())) .findFirst() @@ -146,9 +152,9 @@ public void testSetInfoWithoutConfigId() { PushNotificationConfig result = configStore.setInfo(taskId, initialConfig); assertEquals(taskId, result.id(), "Config ID should default to taskId when not provided"); - List configs = configStore.getInfo(taskId); - assertEquals(1, configs.size()); - assertEquals(taskId, configs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertEquals(1, configResult.configs().size()); + assertEquals(taskId, configResult.configs().get(0).pushNotificationConfig().id()); PushNotificationConfig updatedConfig = PushNotificationConfig.builder() .url("http://initial.url/callback_new") @@ -157,9 +163,9 @@ public void testSetInfoWithoutConfigId() { PushNotificationConfig updatedResult = configStore.setInfo(taskId, updatedConfig); assertEquals(taskId, updatedResult.id()); - configs = configStore.getInfo(taskId); - assertEquals(1, configs.size(), "Should replace existing config with same ID rather than adding new one"); - assertEquals(updatedConfig.url(), configs.get(0).url()); + configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertEquals(1, configResult.configs().size(), "Should replace existing config with same ID rather than adding new one"); + assertEquals(updatedConfig.url(), configResult.configs().get(0).pushNotificationConfig().url()); } @Test @@ -168,18 +174,19 @@ public void testGetInfoExistingConfig() { PushNotificationConfig config = createSamplePushConfig("http://get.this/callback", "cfg1", null); configStore.setInfo(taskId, config); - List retrievedConfigs = configStore.getInfo(taskId); - assertNotNull(retrievedConfigs); - assertEquals(1, retrievedConfigs.size()); - assertEquals(config.url(), retrievedConfigs.get(0).url()); - assertEquals(config.id(), retrievedConfigs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); + assertEquals(config.url(), configResult.configs().get(0).pushNotificationConfig().url()); + assertEquals(config.id(), configResult.configs().get(0).pushNotificationConfig().id()); } @Test public void testGetInfoNonExistentConfig() { String taskId = "task_get_non_exist"; - List retrievedConfigs = configStore.getInfo(taskId); - assertNull(retrievedConfigs, "Should return null for non-existent task ID"); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertTrue(configResult.configs().isEmpty(), "Should return empty list for non-existent task ID"); } @Test @@ -188,14 +195,15 @@ public void testDeleteInfoExistingConfig() { PushNotificationConfig config = createSamplePushConfig("http://delete.this/callback", "cfg1", null); configStore.setInfo(taskId, config); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(1, configs.size()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); configStore.deleteInfo(taskId, config.id()); - List configsAfterDelete = configStore.getInfo(taskId); - assertNull(configsAfterDelete, "Should return null when no configs remain after deletion"); + ListTaskPushNotificationConfigResult configsAfterDelete = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configsAfterDelete); + assertTrue(configsAfterDelete.configs().isEmpty(), "Should return empty list when no configs remain after deletion"); } @Test @@ -204,8 +212,9 @@ public void testDeleteInfoNonExistentConfig() { // Should not throw an error configStore.deleteInfo(taskId, "non_existent_id"); - List configs = configStore.getInfo(taskId); - assertNull(configs, "Should still return null for non-existent task ID"); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertTrue(configResult.configs().isEmpty(), "Should return empty list for non-existent task ID"); } @Test @@ -219,8 +228,9 @@ public void testDeleteInfoWithNullConfigId() { // Delete with null configId should use taskId configStore.deleteInfo(taskId, null); - List configs = configStore.getInfo(taskId); - assertNull(configs, "Should return null after deletion when using taskId as configId"); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertTrue(configResult.configs().isEmpty(), "Should return empty list after deletion when using taskId as configId"); } @Test @@ -324,11 +334,14 @@ public void testMultipleConfigsForSameTask() { configStore.setInfo(taskId, config1); configStore.setInfo(taskId, config2); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(2, configs.size()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(2, configResult.configs().size()); // Verify both configs are present + List configs = configResult.configs().stream() + .map(TaskPushNotificationConfig::pushNotificationConfig) + .toList(); assertTrue(configs.stream().anyMatch(c -> "cfg1".equals(c.id()))); assertTrue(configs.stream().anyMatch(c -> "cfg2".equals(c.id()))); } @@ -345,10 +358,10 @@ public void testDeleteSpecificConfigFromMultiple() { // Delete only config1 configStore.deleteInfo(taskId, "cfg1"); - List configs = configStore.getInfo(taskId); - assertNotNull(configs); - assertEquals(1, configs.size()); - assertEquals("cfg2", configs.get(0).id()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(configResult); + assertEquals(1, configResult.configs().size()); + assertEquals("cfg2", configResult.configs().get(0).pushNotificationConfig().id()); } @Test @@ -361,14 +374,251 @@ public void testConfigStoreIntegration() { assertEquals(config.url(), storedConfig.url()); assertEquals(config.token(), storedConfig.token()); - List retrievedConfigs = configStore.getInfo(taskId); - assertEquals(1, retrievedConfigs.size()); - assertEquals(config.url(), retrievedConfigs.get(0).url()); + ListTaskPushNotificationConfigResult configResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertEquals(1, configResult.configs().size()); + assertEquals(config.url(), configResult.configs().get(0).pushNotificationConfig().url()); // Test deletion configStore.deleteInfo(taskId, storedConfig.id()); - List afterDeletion = configStore.getInfo(taskId); - assertNull(afterDeletion); + ListTaskPushNotificationConfigResult afterDeletion = configStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); + assertNotNull(afterDeletion); + assertTrue(afterDeletion.configs().isEmpty()); + } + + @Test + public void testPaginationWithPageSize() { + String taskId = "task_pagination"; + // Create 5 configs + for (int i = 0; i < 5; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Request first page with pageSize=2 + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + assertEquals(2, result.configs().size(), "Should return 2 configs"); + assertNotNull(result.nextPageToken(), "Should have nextPageToken when more items exist"); + } + + @Test + public void testPaginationWithPageToken() { + String taskId = "task_pagination_token"; + // Create 5 configs + for (int i = 0; i < 5; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Get first page + ListTaskPushNotificationConfigParams firstPageParams = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult firstPage = configStore.getInfo(firstPageParams); + assertNotNull(firstPage.nextPageToken()); + + // Get second page using nextPageToken + ListTaskPushNotificationConfigParams secondPageParams = new ListTaskPushNotificationConfigParams( + taskId, 2, firstPage.nextPageToken(), ""); + ListTaskPushNotificationConfigResult secondPage = configStore.getInfo(secondPageParams); + + assertNotNull(secondPage); + assertEquals(2, secondPage.configs().size(), "Should return 2 configs for second page"); + assertNotNull(secondPage.nextPageToken(), "Should have nextPageToken when more items exist"); + + // Verify NO overlap between pages - collect all IDs from both pages + List firstPageIds = firstPage.configs().stream() + .map(c -> c.pushNotificationConfig().id()) + .toList(); + List secondPageIds = secondPage.configs().stream() + .map(c -> c.pushNotificationConfig().id()) + .toList(); + + // Check that no ID from first page appears in second page + for (String id : firstPageIds) { + assertTrue(!secondPageIds.contains(id), + "Config " + id + " appears in both pages - overlap detected!"); + } + + // Also verify the pages are sequential (first page ends before second page starts) + // Since configs are created in order, we can verify the IDs + assertEquals("cfg0", firstPageIds.get(0)); + assertEquals("cfg1", firstPageIds.get(1)); + assertEquals("cfg2", secondPageIds.get(0)); + assertEquals("cfg3", secondPageIds.get(1)); + } + + @Test + public void testPaginationLastPage() { + String taskId = "task_pagination_last"; + // Create 5 configs + for (int i = 0; i < 5; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Get first page (2 items) + ListTaskPushNotificationConfigParams firstPageParams = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult firstPage = configStore.getInfo(firstPageParams); + + // Get second page (2 items) + ListTaskPushNotificationConfigParams secondPageParams = new ListTaskPushNotificationConfigParams( + taskId, 2, firstPage.nextPageToken(), ""); + ListTaskPushNotificationConfigResult secondPage = configStore.getInfo(secondPageParams); + + // Get last page (1 item remaining) + ListTaskPushNotificationConfigParams lastPageParams = new ListTaskPushNotificationConfigParams( + taskId, 2, secondPage.nextPageToken(), ""); + ListTaskPushNotificationConfigResult lastPage = configStore.getInfo(lastPageParams); + + assertNotNull(lastPage); + assertEquals(1, lastPage.configs().size(), "Last page should have 1 remaining config"); + assertNull(lastPage.nextPageToken(), "Last page should not have nextPageToken"); + } + + @Test + public void testPaginationWithZeroPageSize() { + String taskId = "task_pagination_zero"; + // Create 5 configs + for (int i = 0; i < 5; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Request with pageSize=0 should return all configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 0, "", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + assertEquals(5, result.configs().size(), "Should return all 5 configs when pageSize=0"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when returning all"); + } + + @Test + public void testPaginationWithNegativePageSize() { + String taskId = "task_pagination_negative"; + // Create 3 configs + for (int i = 0; i < 3; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Request with negative pageSize should return all configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, -1, "", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + assertEquals(3, result.configs().size(), "Should return all configs when pageSize is negative"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when returning all"); + } + + @Test + public void testPaginationPageSizeLargerThanConfigs() { + String taskId = "task_pagination_large"; + // Create 3 configs + for (int i = 0; i < 3; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Request with pageSize larger than available configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 10, "", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + assertEquals(3, result.configs().size(), "Should return all 3 configs"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when all configs fit in one page"); + } + + @Test + public void testPaginationExactlyPageSize() { + String taskId = "task_pagination_exact"; + // Create exactly 3 configs + for (int i = 0; i < 3; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Request with pageSize equal to number of configs + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 3, "", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + assertEquals(3, result.configs().size(), "Should return all 3 configs"); + assertNull(result.nextPageToken(), "Should not have nextPageToken when configs exactly match pageSize"); + } + + @Test + public void testPaginationWithInvalidToken() { + String taskId = "task_pagination_invalid_token"; + // Create 5 configs + for (int i = 0; i < 5; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Request with invalid pageToken - implementation behavior is to start from beginning + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams( + taskId, 2, "invalid_token_that_does_not_exist", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + // When token is not found, implementation starts from beginning + assertEquals(2, result.configs().size(), "Should return first page when token is not found"); + assertNotNull(result.nextPageToken(), "Should have nextPageToken since more items exist"); + } + + @Test + public void testPaginationEmptyTaskWithPageSize() { + String taskId = "task_pagination_empty"; + // No configs created + + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + assertNotNull(result); + assertTrue(result.configs().isEmpty(), "Should return empty list for non-existent task"); + assertNull(result.nextPageToken(), "Should not have nextPageToken for empty result"); + } + + @Test + public void testPaginationFullIteration() { + String taskId = "task_pagination_full"; + // Create 7 configs + for (int i = 0; i < 7; i++) { + PushNotificationConfig config = createSamplePushConfig( + "http://url" + i + ".com/callback", "cfg" + i, "token" + i); + configStore.setInfo(taskId, config); + } + + // Iterate through all pages with pageSize=3 + int totalCollected = 0; + String pageToken = ""; + int pageCount = 0; + + do { + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 3, pageToken, ""); + ListTaskPushNotificationConfigResult result = configStore.getInfo(params); + + totalCollected += result.configs().size(); + pageToken = result.nextPageToken(); + pageCount++; + + // Safety check to prevent infinite loop + assertTrue(pageCount <= 10, "Should not have more than 10 pages for 7 configs"); + + } while (pageToken != null); + + assertEquals(7, totalCollected, "Should collect all 7 configs across all pages"); + assertEquals(3, pageCount, "Should have exactly 3 pages (3+3+1)"); } } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java index f0cf356ab..1e653eea8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTaskPushNotificationConfigParamsMapper.java @@ -22,7 +22,6 @@ public interface ListTaskPushNotificationConfigParamsMapper { */ @BeanMapping(builder = @Builder(buildMethod = "build")) @Mapping(target = "id", expression = "java(ResourceNameParser.extractParentId(proto.getParent()))") - @Mapping(target = "tenant", source = "tenant") ListTaskPushNotificationConfigParams fromProto(io.a2a.grpc.ListTaskPushNotificationConfigRequest proto); /** @@ -30,7 +29,5 @@ public interface ListTaskPushNotificationConfigParamsMapper { * Constructs the parent field from task ID. */ @Mapping(target = "parent", expression = "java(ResourceNameParser.defineTaskName(domain.id()))") - @Mapping(target = "pageSize", ignore = true) - @Mapping(target = "pageToken", ignore = true) io.a2a.grpc.ListTaskPushNotificationConfigRequest toProto(ListTaskPushNotificationConfigParams domain); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index 6075c5316..85d5b9f2e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -304,7 +304,7 @@ public static JSONRPCResponse parseResponseBody(String body, String method) t case ListTaskPushNotificationConfigRequest.METHOD -> { io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); parseRequestBody(paramsNode, builder, id); - return new ListTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.listTaskPushNotificationConfigParams(builder)); + return new ListTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.listTaskPushNotificationConfigResult(builder)); } case DeleteTaskPushNotificationConfigRequest.METHOD -> { return new DeleteTaskPushNotificationConfigResponse(id); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 55be58228..6a0ac5409 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -29,6 +29,7 @@ import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; @@ -126,12 +127,16 @@ public static io.a2a.grpc.SendMessageRequest sendMessageRequest(MessageSendParam return MessageSendParamsMapper.INSTANCE.toProto(request); } - public static io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfigResponse(List configs) { - List confs = new ArrayList<>(configs.size()); - for (TaskPushNotificationConfig config : configs) { + public static io.a2a.grpc.ListTaskPushNotificationConfigResponse listTaskPushNotificationConfigResponse(ListTaskPushNotificationConfigResult result) { + List confs = new ArrayList<>(result.configs().size()); + for (TaskPushNotificationConfig config : result.configs()) { confs.add(taskPushNotificationConfig(config)); } - return io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder().addAllConfigs(confs).build(); + io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder().addAllConfigs(confs); + if (result.nextPageToken() != null) { + builder.setNextPageToken(result.nextPageToken()); + } + return builder.build(); } public static StreamResponse streamResponse(StreamingEventKind streamingEventKind) { @@ -254,13 +259,17 @@ public static TaskIdParams taskIdParams(io.a2a.grpc.SubscribeToTaskRequestOrBuil return convert(() -> TaskIdParamsMapper.INSTANCE.fromProtoSubscribeToTaskRequest(reqProto)); } - public static List listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigResponseOrBuilder response) { + public static ListTaskPushNotificationConfigResult listTaskPushNotificationConfigResult(io.a2a.grpc.ListTaskPushNotificationConfigResponseOrBuilder response) { List configs = response.getConfigsList(); List result = new ArrayList<>(configs.size()); for (io.a2a.grpc.TaskPushNotificationConfig config : configs) { result.add(taskPushNotificationConfig(config)); } - return result; + String nextPageToken = response.getNextPageToken(); + if (nextPageToken != null && nextPageToken.isEmpty()) { + nextPageToken = null; + } + return new ListTaskPushNotificationConfigResult(result, nextPageToken); } public static ListTaskPushNotificationConfigParams listTaskPushNotificationConfigParams(io.a2a.grpc.ListTaskPushNotificationConfigRequestOrBuilder request) { diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index 2f71bcceb..09967e378 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -1,6 +1,5 @@ package io.a2a.spec; - import io.a2a.util.Assert; /** @@ -15,7 +14,7 @@ * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -public record ListTaskPushNotificationConfigParams(String id, String tenant) { +public record ListTaskPushNotificationConfigParams(String id, int pageSize, String pageToken, String tenant) { /** * Compact constructor for validation. @@ -35,6 +34,6 @@ public record ListTaskPushNotificationConfigParams(String id, String tenant) { * @param id the task identifier (required) */ public ListTaskPushNotificationConfigParams(String id) { - this(id, ""); + this(id, 0, "", ""); } } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java index 2e66b03c9..098fabe1c 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java @@ -1,31 +1,31 @@ package io.a2a.spec; -import java.util.List; - /** - * JSON-RPC response containing all push notification configurations for a task. + * JSON-RPC response containing all push notification configurations for a task with pagination support. *

      - * This response returns a list of all {@link TaskPushNotificationConfig} entries - * configured for the requested task, showing all active notification endpoints. + * This response returns a {@link ListTaskPushNotificationConfigResult} containing + * {@link TaskPushNotificationConfig} entries configured for the requested task, + * showing all active notification endpoints with optional pagination information. *

      * If an error occurs, the error field will contain a {@link JSONRPCError}. * * @see ListTaskPushNotificationConfigRequest for the corresponding request + * @see ListTaskPushNotificationConfigResult for the result structure * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -public final class ListTaskPushNotificationConfigResponse extends JSONRPCResponse> { +public final class ListTaskPushNotificationConfigResponse extends JSONRPCResponse { /** * Constructs response with all parameters. * * @param jsonrpc the JSON-RPC version * @param id the request ID - * @param result the list of push notification configurations + * @param result the result containing list of push notification configurations and pagination info * @param error the error (if any) */ - public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, List result, JSONRPCError error) { - super(jsonrpc, id, result, error, (Class>) (Class) List.class); + public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, ListTaskPushNotificationConfigResult result, JSONRPCError error) { + super(jsonrpc, id, result, error, ListTaskPushNotificationConfigResult.class); } /** @@ -42,10 +42,10 @@ public ListTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { * Constructs success response. * * @param id the request ID - * @param result the list of push notification configurations + * @param result the result containing list of push notification configurations and pagination info */ - public ListTaskPushNotificationConfigResponse(Object id, List result) { - this(null, id, result, null); + public ListTaskPushNotificationConfigResponse(Object id, ListTaskPushNotificationConfigResult result) { + this(null, id, result, null); } } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java new file mode 100644 index 000000000..193b3ea9b --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java @@ -0,0 +1,63 @@ +package io.a2a.spec; + +import io.a2a.util.Assert; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +/** + * Result of listing push notification configurations for a task with pagination support. + * + * @param configs List of push notification configurations for the task + * @param nextPageToken Token for retrieving the next page of results (null if no more results) + */ +public record ListTaskPushNotificationConfigResult(List configs, + @Nullable String nextPageToken) { + /** + * Compact constructor for validation. + * Validates parameters and creates a defensive copy of the configs list. + * + * @param configs the list of push notification configurations + * @param nextPageToken token for next page + * @throws IllegalArgumentException if validation fails + */ + public ListTaskPushNotificationConfigResult { + Assert.checkNotNullParam("configs", configs); + // Make defensive copy + configs = List.copyOf(configs); + } + + /** + * Constructor for results without pagination. + * + * @param configs the list of push notification configurations + */ + public ListTaskPushNotificationConfigResult(List configs) { + this(configs, null); + } + + /** + * Returns whether there are more results available. + * + * @return true if there are more pages of results + */ + public boolean hasMoreResults() { + return nextPageToken != null && !nextPageToken.isEmpty(); + } + + /** + * Return the size of the configs. + * @return the size of the configs. + */ + public int size() { + return configs.size(); + } + + /** + * Return if the configs is empty or not. + * @return true if the configs is empty - false otherwise. + */ + public boolean isEmpty() { + return configs.isEmpty(); + } +} diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 038629397..858737375 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -72,7 +72,7 @@ public static T unmarshalFrom(String data, Class typeRef) throws JsonProc * @param defaultValue the default value to return if value is null * @return value if non-null, otherwise defaultValue */ - public static T defaultIfNull(T value, T defaultValue) { + public static T defaultIfNull(@Nullable T value, T defaultValue) { if (value == null) { return defaultValue; } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 938f2d3ad..8d52b61b1 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -55,6 +55,7 @@ import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCErrorResponse; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; @@ -1070,11 +1071,11 @@ public void testListPushNotificationConfigWithConfigId() throws Exception { savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); try { - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(2, result.size()); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), notificationConfig1, null), result.get(0)); - assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), notificationConfig2, null), result.get(1)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), notificationConfig1, null), result.configs().get(0)); + assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), notificationConfig2, null), result.configs().get(1)); } catch (Exception e) { fail(); } finally { @@ -1100,7 +1101,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { // will overwrite the previous one savePushNotificationConfigInStore(MINIMAL_TASK.id(), notificationConfig2); try { - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(1, result.size()); @@ -1109,7 +1110,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { .id(MINIMAL_TASK.id()) .build(); assertEquals(new TaskPushNotificationConfig(MINIMAL_TASK.id(), expectedNotificationConfig, null), - result.get(0)); + result.configs().get(0)); } catch (Exception e) { fail(); } finally { @@ -1121,7 +1122,7 @@ public void testListPushNotificationConfigWithoutConfigId() throws Exception { @Test public void testListPushNotificationConfigTaskNotFound() { try { - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams("non-existent-task")); fail(); } catch (A2AClientException e) { @@ -1133,7 +1134,7 @@ public void testListPushNotificationConfigTaskNotFound() { public void testListPushNotificationConfigEmptyList() throws Exception { saveTaskInTaskStore(MINIMAL_TASK); try { - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(0, result.size()); } catch (Exception e) { @@ -1172,7 +1173,7 @@ public void testDeletePushNotificationConfigWithValidConfigId() throws Exception new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), "config1")); // should now be 1 left - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(1, result.size()); @@ -1212,7 +1213,7 @@ public void testDeletePushNotificationConfigWithNonExistingConfigId() throws Exc new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), "non-existent-config-id")); // should remain unchanged - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id())); assertEquals(2, result.size()); } catch (Exception e) { @@ -1257,7 +1258,7 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio new DeleteTaskPushNotificationConfigParams(MINIMAL_TASK.id(), MINIMAL_TASK.id())); // should now be 0 - List result = getClient().listTaskPushNotificationConfigurations( + ListTaskPushNotificationConfigResult result = getClient().listTaskPushNotificationConfigurations( new ListTaskPushNotificationConfigParams(MINIMAL_TASK.id()), null); assertEquals(0, result.size()); } catch (Exception e) { diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index ea14783bd..1df1409f3 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -41,6 +41,7 @@ import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.PushNotificationNotSupportedError; @@ -208,13 +209,9 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC try { ServerCallContext context = createCallContext(responseObserver); ListTaskPushNotificationConfigParams params = FromProto.listTaskPushNotificationConfigParams(request); - List configList = getRequestHandler().onListTaskPushNotificationConfig(params, context); - io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder responseBuilder = - io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); - for (TaskPushNotificationConfig config : configList) { - responseBuilder.addConfigs(ToProto.taskPushNotificationConfig(config)); - } - responseObserver.onNext(responseBuilder.build()); + ListTaskPushNotificationConfigResult result = getRequestHandler().onListTaskPushNotificationConfig(params, context); + io.a2a.grpc.ListTaskPushNotificationConfigResponse response = ToProto.listTaskPushNotificationConfigResponse(result); + responseObserver.onNext(response); responseObserver.onCompleted(); } catch (JSONRPCError e) { handleError(responseObserver, e); diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 8c62a81d6..5c097c04c 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -34,6 +34,7 @@ import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResponse; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksRequest; import io.a2a.spec.ListTasksResponse; import io.a2a.spec.ListTasksResult; @@ -210,9 +211,9 @@ public ListTaskPushNotificationConfigResponse listPushNotificationConfig( new PushNotificationNotSupportedError()); } try { - List pushNotificationConfigList = + ListTaskPushNotificationConfigResult result = requestHandler.onListTaskPushNotificationConfig(request.getParams(), context); - return new ListTaskPushNotificationConfigResponse(request.getId(), pushNotificationConfigList); + return new ListTaskPushNotificationConfigResponse(request.getId(), result); } catch (JSONRPCError e) { return new ListTaskPushNotificationConfigResponse(request.getId(), e); } catch (Throwable t) { diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index b2f447d4b..acf62dcb7 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1361,7 +1361,7 @@ public void testListPushNotificationConfig() { assertEquals("111", listResponse.getId()); assertEquals(1, listResponse.getResult().size()); - assertEquals(result, listResponse.getResult().get(0)); + assertEquals(result, listResponse.getResult().configs().get(0)); } @Test diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index bd07b27f5..ea4d62ec7 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -36,6 +36,7 @@ import io.a2a.spec.JSONParseError; import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTasksResult; import io.a2a.spec.MethodNotFoundError; @@ -251,14 +252,14 @@ public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nul } } - public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, String tenant, ServerCallContext context) { + public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, int pageSize, String pageToken, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } - ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId,tenant); - List configs = requestHandler.onListTaskPushNotificationConfig(params, context); - return createSuccessResponse(200, io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(ProtoUtils.ToProto.listTaskPushNotificationConfigResponse(configs))); + ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, pageSize, pageToken, tenant); + ListTaskPushNotificationConfigResult result = requestHandler.onListTaskPushNotificationConfig(params, context); + return createSuccessResponse(200, io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(ProtoUtils.ToProto.listTaskPushNotificationConfigResponse(result))); } catch (JSONRPCError e) { return createErrorResponse(e); } catch (Throwable throwable) { diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index d4b326f17..5b309116d 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -301,7 +301,7 @@ public void testListPushNotificationConfigs() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK); - RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.id(), "", callContext); + RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.id(), 0, "", "", callContext); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals("application/json", response.getContentType()); From a2daec979fa18cb11cef79c6df87ad0736358a71 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 19 Dec 2025 11:03:44 +0100 Subject: [PATCH 239/493] fix: InMemoryPushNotificationConfigStore uses wrong pagination token. (#555) * Using the 'id' field instead of the 'token' field as the pagination token. Fixes #554 --- .../server/tasks/InMemoryPushNotificationConfigStore.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index b278d32d6..8dfeeebdb 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -70,16 +70,16 @@ public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConf if (configs.size() <= params.pageSize()) { return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs, params), null); } - String newToken = configs.get(params.pageSize()).token(); + String newToken = configs.get(params.pageSize()).id(); return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs.subList(0, params.pageSize()), params), newToken); } - private int findFirstIndex(List configs, String token) { + private int findFirstIndex(List configs, String id) { //find first index Iterator iter = configs.iterator(); int index = 0; while (iter.hasNext()) { - if (token.equals(iter.next().token())) { + if (id.equals(iter.next().id())) { return index; } index++; From a14fd2eb91d04f6409f62f77d0952f5cb84a6a74 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 19 Dec 2025 16:21:12 +0000 Subject: [PATCH 240/493] feat!: Trim io.a2a.apec package (#556) Introduce a new jsonrpc-common module, and move the io.a2a.spec Request and Response classes there since they are internal use now. The jsonrpc-common module also gets the JsonUtil and the various MappingExceptions. The spec package exceptions were tied to JSON-RPC. We decided to keep the `code` and `data` fields, which could be useful even when JSON-RPC is not used. But we got rid of the JSONRPCError class, and repurposes the A2AError interface to take its role. Also renamed JSONRPCRequest and JSONRPCResponse to be A2ARequest/-Response. Make A2AError extend RuntimeException rather than Error. --------- Signed-off-by: Emmanuel Hugonnet Co-authored-by: Emmanuel Hugonnet --- boms/sdk/pom.xml | 5 + .../java/io/a2a/client/AbstractClient.java | 2 +- .../src/main/java/io/a2a/client/Client.java | 9 +- .../java/io/a2a/client/ClientBuilder.java | 18 +- .../io/a2a/client/config/ClientConfig.java | 4 +- client/base/src/test/java/io/a2a/A2ATest.java | 14 +- client/transport/grpc/pom.xml | 5 + .../transport/grpc/EventStreamObserver.java | 8 +- .../client/transport/grpc/GrpcTransport.java | 50 ++--- .../transport/grpc/GrpcTransportConfig.java | 4 +- .../grpc/GrpcTransportConfigBuilder.java | 5 +- client/transport/jsonrpc/pom.xml | 5 + .../transport/jsonrpc/JSONRPCTransport.java | 128 ++++++----- .../jsonrpc/JSONRPCTransportConfig.java | 2 +- .../jsonrpc/sse/SSEEventListener.java | 11 +- .../JSONRPCTransportStreamingTest.java | 1 - .../transport/jsonrpc/JsonMessages.java | 2 - .../jsonrpc/sse/SSEEventListenerTest.java | 7 +- client/transport/rest/pom.xml | 5 + .../transport/rest/RestErrorMapper.java | 9 +- .../client/transport/rest/RestTransport.java | 54 +++-- client/transport/spi/pom.xml | 5 + .../client/transport/spi/ClientTransport.java | 3 +- .../transport/spi/ClientTransportConfig.java | 4 +- .../spi/ClientTransportConfigBuilder.java | 4 +- .../spi/interceptors/PayloadAndHeaders.java | 1 + .../auth/AuthInterceptorTest.java | 1 - .../cloud/CloudAgentExecutorProducer.java | 17 +- examples/helloworld/client/pom.xml | 5 + .../examples/helloworld/HelloWorldClient.java | 2 +- .../helloworld/AgentExecutorProducer.java | 8 +- .../pom.xml | 5 + ...paDatabasePushNotificationConfigStore.java | 2 +- .../jpa/JpaPushNotificationConfig.java | 4 +- ...otificationConfigStoreIntegrationTest.java | 6 +- ...ificationConfigStoreTestAgentExecutor.java | 8 +- .../JpaPushNotificationConfigStoreTest.java | 14 +- extras/queue-manager-replicated/core/pom.xml | 5 + .../core/ReplicatedEventQueueItem.java | 25 ++- .../core/ReplicatedQueueManager.java | 7 +- .../core/EventSerializationTest.java | 26 +-- .../core/ReplicatedQueueManagerTest.java | 2 +- .../replication-mp-reactive/pom.xml | 5 + .../ReactiveMessagingReplicationStrategy.java | 2 +- ...ctiveMessagingReplicationStrategyTest.java | 21 +- ...MultiInstanceReplicationAgentExecutor.java | 6 +- .../tests-single-instance/pom.xml | 5 + .../KafkaReplicationIntegrationTest.java | 2 +- .../tests/ReplicatedQueueManagerTest.java | 5 +- .../tests/ReplicationTestAgentExecutor.java | 8 +- .../tests/TestKafkaEventConsumer.java | 5 +- extras/task-store-database-jpa/pom.xml | 5 + .../database/jpa/JpaDatabaseTaskStore.java | 4 +- .../taskstore/database/jpa/JpaTask.java | 4 +- .../jpa/JpaDatabaseTaskStoreTest.java | 2 +- ...JpaDatabaseTaskStoreTestAgentExecutor.java | 8 +- .../io/a2a/client/http/A2ACardResolver.java | 8 +- .../a2a/client/http/A2ACardResolverTest.java | 4 +- jsonrpc-common/pom.xml | 41 ++++ .../common/json}/IdJsonMappingException.java | 6 +- .../InvalidParamsJsonMappingException.java | 2 +- .../common}/json/JsonMappingException.java | 2 +- .../common}/json/JsonProcessingException.java | 2 +- .../io/a2a/jsonrpc/common}/json/JsonUtil.java | 42 ++-- .../MethodNotFoundJsonMappingException.java | 4 +- .../jsonrpc/common}/json/package-info.java | 2 +- .../common/wrappers/A2AErrorResponse.java | 17 +- .../jsonrpc/common/wrappers/A2AMessage.java | 10 +- .../jsonrpc/common/wrappers/A2ARequest.java | 6 +- .../jsonrpc/common/wrappers/A2AResponse.java | 13 +- .../common/wrappers}/CancelTaskRequest.java | 13 +- .../common/wrappers}/CancelTaskResponse.java | 16 +- ...leteTaskPushNotificationConfigRequest.java | 12 +- ...eteTaskPushNotificationConfigResponse.java | 12 +- .../GetAuthenticatedExtendedCardRequest.java | 12 +- .../GetAuthenticatedExtendedCardResponse.java | 14 +- .../GetTaskPushNotificationConfigRequest.java | 12 +- ...GetTaskPushNotificationConfigResponse.java | 13 +- .../common/wrappers}/GetTaskRequest.java | 12 +- .../common/wrappers}/GetTaskResponse.java | 14 +- ...ListTaskPushNotificationConfigRequest.java | 13 +- ...istTaskPushNotificationConfigResponse.java | 14 +- .../common/wrappers}/ListTasksRequest.java | 11 +- .../common/wrappers}/ListTasksResponse.java | 10 +- .../common/wrappers}/ListTasksResult.java | 7 +- .../wrappers}/NonStreamingJSONRPCRequest.java | 4 +- .../common/wrappers}/SendMessageRequest.java | 18 +- .../common/wrappers}/SendMessageResponse.java | 11 +- .../SendStreamingMessageRequest.java | 12 +- .../SendStreamingMessageResponse.java | 17 +- .../SetTaskPushNotificationConfigRequest.java | 12 +- ...SetTaskPushNotificationConfigResponse.java | 14 +- .../wrappers}/StreamingJSONRPCRequest.java | 8 +- .../wrappers}/SubscribeToTaskRequest.java | 12 +- .../jsonrpc/common/wrappers/package-info.java | 14 ++ .../json/A2AErrorSerializationTest.java | 37 ++-- .../StreamingEventKindSerializationTest.java | 13 +- .../common/json}/TaskSerializationTest.java | 15 +- pom.xml | 6 + reference/grpc/pom.xml | 5 + .../quarkus/A2AExtensionsInterceptor.java | 5 +- .../grpc/quarkus/QuarkusGrpcHandler.java | 4 +- .../server/grpc/quarkus/A2ATestResource.java | 2 +- .../grpc/quarkus/QuarkusA2AGrpcTest.java | 1 - reference/jsonrpc/pom.xml | 5 + .../server/apps/quarkus/A2AServerRoutes.java | 114 +++++----- .../apps/quarkus/A2AServerRoutesTest.java | 71 ++++--- .../server/apps/quarkus/A2ATestRoutes.java | 2 +- reference/rest/pom.xml | 5 + .../server/rest/quarkus/A2AServerRoutes.java | 60 +++--- .../rest/quarkus/A2AServerRoutesTest.java | 36 ++-- .../server/rest/quarkus/A2ATestRoutes.java | 4 +- server-common/pom.xml | 5 + .../java/io/a2a/server/JSONRPCException.java | 10 +- .../server/agentexecution/AgentExecutor.java | 6 +- .../server/agentexecution/RequestContext.java | 3 +- .../io/a2a/server/events/EventConsumer.java | 3 +- .../java/io/a2a/server/events/EventQueue.java | 3 +- .../server/events/InMemoryQueueManager.java | 3 +- .../a2a/server/extensions/A2AExtensions.java | 3 +- .../DefaultRequestHandler.java | 33 ++- .../requesthandlers/RequestHandler.java | 25 ++- .../tasks/BasePushNotificationSender.java | 8 +- .../InMemoryPushNotificationConfigStore.java | 10 +- .../a2a/server/tasks/InMemoryTaskStore.java | 2 +- .../tasks/PushNotificationConfigStore.java | 2 - .../io/a2a/server/tasks/ResultAggregator.java | 9 +- .../java/io/a2a/server/tasks/TaskManager.java | 5 +- .../java/io/a2a/server/tasks/TaskStore.java | 5 +- .../java/io/a2a/server/tasks/TaskUpdater.java | 3 +- .../io/a2a/server/util/ArtifactUtils.java | 3 +- .../util/async/AsyncExecutorProducer.java | 3 +- .../io/a2a/server/util/async/AsyncUtils.java | 3 +- .../io/a2a/server/AgentCardValidatorTest.java | 18 +- .../io/a2a/server/ServerCallContextTest.java | 3 +- .../agentexecution/RequestContextTest.java | 10 +- .../a2a/server/events/EventConsumerTest.java | 21 +- .../io/a2a/server/events/EventQueueTest.java | 28 +-- .../server/extensions/A2AExtensionsTest.java | 3 +- .../AbstractA2ARequestHandlerTest.java | 12 +- .../DefaultRequestHandlerTest.java | 8 +- .../server/tasks/InMemoryTaskStoreTest.java | 6 +- .../tasks/PushNotificationSenderTest.java | 9 +- .../io/a2a/server/tasks/TaskManagerTest.java | 4 +- .../io/a2a/server/util/ArtifactUtilsTest.java | 5 +- spec-grpc/pom.xml | 6 +- .../a2a/grpc/mapper/A2ACommonFieldMapper.java | 2 +- .../io/a2a/grpc/mapper/DataPartMapper.java | 1 - .../io/a2a/grpc/mapper/FilePartMapper.java | 3 +- .../grpc/mapper/ListTasksResultMapper.java | 4 +- .../mapper/SubscribeToTaskRequestMapper.java | 4 +- .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 200 ++++++++++-------- .../java/io/a2a/grpc/utils/ProtoUtils.java | 10 +- spec-grpc/src/main/proto/a2a.proto | 4 +- .../grpc/mapper/StreamResponseMapperTest.java | 3 +- .../io/a2a/grpc/utils/JSONRPCUtilsTest.java | 30 +-- spec/src/main/java/io/a2a/spec/A2AError.java | 67 +++++- .../src/main/java/io/a2a/spec/A2AMethods.java | 17 ++ .../io/a2a/spec/APIKeySecurityScheme.java | 2 - .../java/io/a2a/spec/AgentCardSignature.java | 2 +- ...ticatedExtendedCardNotConfiguredError.java | 8 +- .../a2a/spec/AuthorizationCodeOAuthFlow.java | 1 - .../a2a/spec/ClientCredentialsOAuthFlow.java | 1 - .../spec/ContentTypeNotSupportedError.java | 6 +- spec/src/main/java/io/a2a/spec/DataPart.java | 2 - ...eleteTaskPushNotificationConfigParams.java | 1 - spec/src/main/java/io/a2a/spec/FilePart.java | 2 - .../GetTaskPushNotificationConfigParams.java | 1 - .../io/a2a/spec/HTTPAuthSecurityScheme.java | 3 - .../java/io/a2a/spec/ImplicitOAuthFlow.java | 1 - .../java/io/a2a/spec/IntegerJsonrpcId.java | 22 -- .../main/java/io/a2a/spec/InternalError.java | 4 +- .../a2a/spec/InvalidAgentResponseError.java | 4 +- .../java/io/a2a/spec/InvalidParamsError.java | 4 +- .../java/io/a2a/spec/InvalidRequestError.java | 4 +- .../java/io/a2a/spec/JSONErrorResponse.java | 19 -- .../main/java/io/a2a/spec/JSONParseError.java | 4 +- .../main/java/io/a2a/spec/JSONRPCError.java | 97 --------- spec/src/main/java/io/a2a/spec/JsonrpcId.java | 24 --- .../ListTaskPushNotificationConfigParams.java | 1 - .../ListTaskPushNotificationConfigResult.java | 4 +- .../java/io/a2a/spec/ListTasksParams.java | 4 +- .../java/io/a2a/spec/MessageSendParams.java | 5 +- .../java/io/a2a/spec/MethodNotFoundError.java | 4 +- .../io/a2a/spec/MutualTLSSecurityScheme.java | 2 - .../io/a2a/spec/OAuth2SecurityScheme.java | 2 - .../a2a/spec/OpenIdConnectSecurityScheme.java | 2 - .../PushNotificationNotSupportedError.java | 6 +- .../main/java/io/a2a/spec/SecurityScheme.java | 2 - .../java/io/a2a/spec/StringJsonrpcId.java | 22 -- .../main/java/io/a2a/spec/TaskIdParams.java | 5 +- .../io/a2a/spec/TaskNotCancelableError.java | 5 +- .../java/io/a2a/spec/TaskNotFoundError.java | 7 +- .../a2a/spec/TaskPushNotificationConfig.java | 1 - .../io/a2a/spec/TaskStatusUpdateEvent.java | 2 +- spec/src/main/java/io/a2a/spec/TextPart.java | 2 - .../a2a/spec/UnsupportedOperationError.java | 2 +- spec/src/main/java/io/a2a/util/Utils.java | 31 +-- .../main/java/io/a2a/util/package-info.java | 5 + .../a2a/tck/server/AgentExecutorProducer.java | 6 +- tests/server-common/pom.xml | 5 + .../apps/common/A2AGsonObjectMapper.java | 4 +- .../apps/common/AbstractA2AServerTest.java | 52 ++--- .../apps/common/AgentExecutorProducer.java | 10 +- .../server/apps/common/TestHttpClient.java | 6 +- transport/grpc/pom.xml | 5 + .../transport/grpc/handler/GrpcHandler.java | 38 ++-- .../grpc/handler/GrpcHandlerTest.java | 2 +- transport/jsonrpc/pom.xml | 5 + .../jsonrpc/handler/JSONRPCHandler.java | 81 ++++--- .../jsonrpc/handler/JSONRPCHandlerTest.java | 38 ++-- transport/rest/pom.xml | 5 + .../transport/rest/handler/RestHandler.java | 71 +++---- .../rest/handler/RestHandlerTest.java | 12 +- 214 files changed, 1390 insertions(+), 1336 deletions(-) create mode 100644 jsonrpc-common/pom.xml rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json}/IdJsonMappingException.java (92%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json}/InvalidParamsJsonMappingException.java (95%) rename {spec/src/main/java/io/a2a => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common}/json/JsonMappingException.java (99%) rename {spec/src/main/java/io/a2a => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common}/json/JsonProcessingException.java (97%) rename {spec/src/main/java/io/a2a => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common}/json/JsonUtil.java (97%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json}/MethodNotFoundJsonMappingException.java (93%) rename {spec/src/main/java/io/a2a => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common}/json/package-info.java (88%) rename spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AErrorResponse.java (81%) rename spec/src/main/java/io/a2a/spec/JSONRPCMessage.java => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AMessage.java (86%) rename spec/src/main/java/io/a2a/spec/JSONRPCRequest.java => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2ARequest.java (94%) rename spec/src/main/java/io/a2a/spec/JSONRPCResponse.java => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java (74%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/CancelTaskRequest.java (94%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/CancelTaskResponse.java (75%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/DeleteTaskPushNotificationConfigRequest.java (94%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/DeleteTaskPushNotificationConfigResponse.java (86%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/GetAuthenticatedExtendedCardRequest.java (92%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/GetAuthenticatedExtendedCardResponse.java (81%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/GetTaskPushNotificationConfigRequest.java (91%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/GetTaskPushNotificationConfigResponse.java (82%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/GetTaskRequest.java (93%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/GetTaskResponse.java (78%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/ListTaskPushNotificationConfigRequest.java (90%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/ListTaskPushNotificationConfigResponse.java (78%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/ListTasksRequest.java (92%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/ListTasksResponse.java (77%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/ListTasksResult.java (98%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/NonStreamingJSONRPCRequest.java (90%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SendMessageRequest.java (91%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SendMessageResponse.java (75%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SendStreamingMessageRequest.java (93%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SendStreamingMessageResponse.java (76%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SetTaskPushNotificationConfigRequest.java (91%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SetTaskPushNotificationConfigResponse.java (78%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/StreamingJSONRPCRequest.java (87%) rename {spec/src/main/java/io/a2a/spec => jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers}/SubscribeToTaskRequest.java (92%) create mode 100644 jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/package-info.java rename spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java => jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/A2AErrorSerializationTest.java (72%) rename {spec/src/test/java/io/a2a/spec => jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json}/StreamingEventKindSerializationTest.java (96%) rename {spec/src/test/java/io/a2a/spec => jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json}/TaskSerializationTest.java (98%) create mode 100644 spec/src/main/java/io/a2a/spec/A2AMethods.java delete mode 100644 spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java delete mode 100644 spec/src/main/java/io/a2a/spec/JSONErrorResponse.java delete mode 100644 spec/src/main/java/io/a2a/spec/JSONRPCError.java delete mode 100644 spec/src/main/java/io/a2a/spec/JsonrpcId.java delete mode 100644 spec/src/main/java/io/a2a/spec/StringJsonrpcId.java create mode 100644 spec/src/main/java/io/a2a/util/package-info.java diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml index c0b51b73a..c9354f558 100644 --- a/boms/sdk/pom.xml +++ b/boms/sdk/pom.xml @@ -39,6 +39,11 @@ a2a-java-sdk-common ${project.version} + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-http-client diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index 414c25f73..b61623668 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -8,6 +8,7 @@ import java.util.function.Consumer; import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -15,7 +16,6 @@ import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index cb8ef954b..2a82b872e 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -1,13 +1,16 @@ package io.a2a.client; +import static io.a2a.util.Assert.checkNotNullParam; + import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; import io.a2a.client.config.ClientConfig; -import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.A2AClientInvalidStateError; @@ -18,7 +21,6 @@ import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -30,9 +32,6 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskStatusUpdateEvent; - -import static io.a2a.util.Assert.checkNotNullParam; - import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/client/base/src/main/java/io/a2a/client/ClientBuilder.java b/client/base/src/main/java/io/a2a/client/ClientBuilder.java index 164d83aeb..b05b44ca1 100644 --- a/client/base/src/main/java/io/a2a/client/ClientBuilder.java +++ b/client/base/src/main/java/io/a2a/client/ClientBuilder.java @@ -1,5 +1,14 @@ package io.a2a.client; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + import io.a2a.client.config.ClientConfig; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.ClientTransportConfig; @@ -9,15 +18,6 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.TransportProtocol; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.function.BiConsumer; -import java.util.function.Consumer; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/client/base/src/main/java/io/a2a/client/config/ClientConfig.java b/client/base/src/main/java/io/a2a/client/config/ClientConfig.java index 0f4672aa9..823548c23 100644 --- a/client/base/src/main/java/io/a2a/client/config/ClientConfig.java +++ b/client/base/src/main/java/io/a2a/client/config/ClientConfig.java @@ -1,11 +1,11 @@ package io.a2a.client.config; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import io.a2a.spec.PushNotificationConfig; -import java.util.ArrayList; -import java.util.HashMap; import org.jspecify.annotations.Nullable; /** diff --git a/client/base/src/test/java/io/a2a/A2ATest.java b/client/base/src/test/java/io/a2a/A2ATest.java index 217bb18c9..0f4d3eaa5 100644 --- a/client/base/src/test/java/io/a2a/A2ATest.java +++ b/client/base/src/test/java/io/a2a/A2ATest.java @@ -1,17 +1,17 @@ package io.a2a; -import io.a2a.spec.Message; -import io.a2a.spec.Part; -import io.a2a.spec.TextPart; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Arrays; import java.util.Collections; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.TextPart; +import org.junit.jupiter.api.Test; public class A2ATest { diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 7965dcaa2..65c0b8514 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -25,6 +25,11 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-spec-grpc diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java index 627286607..d77b81824 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java @@ -1,14 +1,14 @@ package io.a2a.client.transport.grpc; -import io.a2a.grpc.StreamResponse; -import io.a2a.spec.StreamingEventKind; -import io.grpc.stub.StreamObserver; +import static io.a2a.grpc.utils.ProtoUtils.FromProto; import java.util.function.Consumer; import java.util.logging.Logger; -import static io.a2a.grpc.utils.ProtoUtils.FromProto; +import io.a2a.grpc.StreamResponse; +import io.a2a.spec.StreamingEventKind; +import io.grpc.stub.StreamObserver; public class EventStreamObserver implements StreamObserver { diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index a846210d9..207ccc45b 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -1,5 +1,15 @@ package io.a2a.client.transport.grpc; +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_TASK_METHOD; +import static io.a2a.spec.A2AMethods.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.LIST_TASK_METHOD; +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; import static io.a2a.util.Assert.checkNotNullParam; import java.util.List; @@ -7,8 +17,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.ClientTransport; +import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; import io.a2a.client.transport.spi.interceptors.auth.AuthInterceptor; @@ -18,27 +28,17 @@ import io.a2a.grpc.A2AServiceGrpc.A2AServiceStub; import io.a2a.grpc.utils.ProtoUtils.FromProto; import io.a2a.grpc.utils.ProtoUtils.ToProto; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; -import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskRequest; import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; @@ -95,7 +95,7 @@ public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallCont MessageSendParams tenantRequest = createRequestWithTenant(request); io.a2a.grpc.SendMessageRequest sendMessageRequest = createGrpcSendMessageRequest(tenantRequest, context); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, sendMessageRequest, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SEND_MESSAGE_METHOD, sendMessageRequest, agentCard, context); try { @@ -121,7 +121,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); @@ -143,7 +143,7 @@ public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context } requestBuilder.setTenant(resolveTenant(request.tenant())); io.a2a.grpc.GetTaskRequest getTaskRequest = requestBuilder.build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, getTaskRequest, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_TASK_METHOD, getTaskRequest, agentCard, context); try { @@ -162,8 +162,7 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context .setName("tasks/" + request.id()) .setTenant(resolveTenant(request.tenant())) .build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(CancelTaskRequest.METHOD, cancelTaskRequest, - agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(CANCEL_TASK_METHOD, cancelTaskRequest, agentCard, context); try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); @@ -201,8 +200,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo } requestBuilder.setTenant(resolveTenant(request.tenant())); io.a2a.grpc.ListTasksRequest listTasksRequest = requestBuilder.build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTasksRequest.METHOD, listTasksRequest, - agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(LIST_TASK_METHOD, listTasksRequest, agentCard, context); try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); @@ -233,8 +231,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN .setConfigId(configId != null ? configId : request.taskId()) .setTenant(resolveTenant(request.tenant())) .build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, - grpcRequest, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, grpcRequest, agentCard, context); try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); @@ -254,8 +251,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration( .setName(getTaskPushNotificationConfigName(request)) .setTenant(resolveTenant(request.tenant())) .build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskPushNotificationConfigRequest.METHOD, - grpcRequest, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, grpcRequest, agentCard, context); try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); @@ -277,7 +273,7 @@ public ListTaskPushNotificationConfigResult listTaskPushNotificationConfiguratio .setPageSize(request.pageSize()) .setPageToken(request.pageToken()) .build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, grpcRequest, agentCard, context); try { @@ -298,8 +294,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC .setName(getTaskPushNotificationConfigName(request.id(), request.pushNotificationConfigId())) .setTenant(resolveTenant(request.tenant())) .build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, - grpcRequest, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, grpcRequest, agentCard, context); try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); @@ -319,8 +314,7 @@ public void resubscribe(TaskIdParams request, Consumer event .setTenant(resolveTenant(request.tenant())) .setName("tasks/" + request.id()) .build(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD, - grpcRequest, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SUBSCRIBE_TO_TASK_METHOD, grpcRequest, agentCard, context); StreamObserver streamObserver = new EventStreamObserver(eventConsumer, errorConsumer); diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java index c6fe443d2..a1bc3373c 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfig.java @@ -1,11 +1,11 @@ package io.a2a.client.transport.grpc; +import java.util.function.Function; + import io.a2a.client.transport.spi.ClientTransportConfig; import io.a2a.util.Assert; import io.grpc.Channel; -import java.util.function.Function; - public class GrpcTransportConfig extends ClientTransportConfig { private final Function channelFactory; diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java index 6878ce7d4..138fadb1f 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransportConfigBuilder.java @@ -1,11 +1,10 @@ package io.a2a.client.transport.grpc; +import java.util.function.Function; + import io.a2a.client.transport.spi.ClientTransportConfigBuilder; import io.a2a.util.Assert; import io.grpc.Channel; - -import java.util.function.Function; - import org.jspecify.annotations.Nullable; public class GrpcTransportConfigBuilder extends ClientTransportConfigBuilder { diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 3961bdaf2..19084ad9d 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -33,6 +33,11 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-spec-grpc diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 250503f96..69e291988 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -1,5 +1,16 @@ package io.a2a.client.transport.jsonrpc; +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; +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; import static io.a2a.util.Assert.checkNotNullParam; import java.io.IOException; @@ -9,7 +20,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import io.a2a.json.JsonProcessingException; import com.google.protobuf.MessageOrBuilder; import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; @@ -22,42 +32,33 @@ import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; 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.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; +import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.EventKind; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.JSONRPCMessage; -import io.a2a.spec.JSONRPCResponse; import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.ListTasksResponse; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; @@ -93,12 +94,12 @@ public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard @Override public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, ProtoUtils.ToProto.sendMessageRequest(request), + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SEND_MESSAGE_METHOD, ProtoUtils.ToProto.sendMessageRequest(request), agentCard, context); try { - String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SendMessageRequest.METHOD); - SendMessageResponse response = unmarshalResponse(httpResponseBody, SendMessageRequest.METHOD); + String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SEND_MESSAGE_METHOD); + SendMessageResponse response = unmarshalResponse(httpResponseBody, SEND_MESSAGE_METHOD); return response.getResult(); } catch (A2AClientException e) { throw e; @@ -112,14 +113,14 @@ public void sendMessageStreaming(MessageSendParams request, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SEND_STREAMING_MESSAGE_METHOD, ProtoUtils.ToProto.sendMessageRequest(request), agentCard, context); final AtomicReference> ref = new AtomicReference<>(); SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { - A2AHttpClient.PostBuilder builder = createPostBuilder(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SendStreamingMessageRequest.METHOD); + A2AHttpClient.PostBuilder builder = createPostBuilder(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SEND_STREAMING_MESSAGE_METHOD); ref.set(builder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), @@ -139,12 +140,12 @@ public void sendMessageStreaming(MessageSendParams request, Consumer event checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); checkNotNullParam("errorConsumer", errorConsumer); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD, - ProtoUtils.ToProto.subscribeToTaskRequest(request), agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SUBSCRIBE_TO_TASK_METHOD, ProtoUtils.ToProto.subscribeToTaskRequest(request), agentCard, context); AtomicReference> ref = new AtomicReference<>(); SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { - A2AHttpClient.PostBuilder builder = createPostBuilder(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders,SubscribeToTaskRequest.METHOD); + A2AHttpClient.PostBuilder builder = createPostBuilder(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SUBSCRIBE_TO_TASK_METHOD); ref.set(builder.postAsyncSSE( msg -> sseEventListener.onMessage(msg, ref.get()), throwable -> sseEventListener.onError(throwable, ref.get()), @@ -304,16 +301,15 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli } GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = GetAuthenticatedExtendedCardRequest.builder() - .jsonrpc(JSONRPCMessage.JSONRPC_VERSION) + .jsonrpc(A2AMessage.JSONRPC_VERSION) .build(); // id will be randomly generated - PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetAuthenticatedExtendedCardRequest.METHOD, - ProtoUtils.ToProto.extendedAgentCard(getExtendedAgentCardRequest), agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, + ProtoUtils.ToProto.extendedAgentCard(), agentCard, context); try { - String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, ""), payloadAndHeaders,GetAuthenticatedExtendedCardRequest.METHOD); - GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody, - GetAuthenticatedExtendedCardRequest.METHOD); + 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; @@ -381,10 +377,10 @@ private A2AHttpClient.PostBuilder createPostBuilder(String url, PayloadAndHeader * @throws JsonProcessingException if the JSON cannot be processed */ @SuppressWarnings("unchecked") - private > T unmarshalResponse(String response, String method) + private > T unmarshalResponse(String response, String method) throws A2AClientException, JsonProcessingException { - JSONRPCResponse value = JSONRPCUtils.parseResponseBody(response, method); - JSONRPCError error = value.getError(); + A2AResponse value = JSONRPCUtils.parseResponseBody(response, method); + A2AError error = value.getError(); if (error != null) { throw new A2AClientException(error.getMessage() + (error.getData() != null ? ": " + error.getData() : ""), error); } diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java index 364cc4715..0705faf20 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfig.java @@ -1,7 +1,7 @@ package io.a2a.client.transport.jsonrpc; -import io.a2a.client.transport.spi.ClientTransportConfig; import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.transport.spi.ClientTransportConfig; import org.jspecify.annotations.Nullable; public class JSONRPCTransportConfig extends ClientTransportConfig { diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index f940f09d7..33025ed5e 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -1,10 +1,5 @@ package io.a2a.client.transport.jsonrpc.sse; -import io.a2a.json.JsonProcessingException; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.TaskStatusUpdateEvent; - import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.logging.Logger; @@ -12,6 +7,10 @@ import io.a2a.grpc.StreamResponse; import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.spec.A2AError; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.TaskStatusUpdateEvent; import org.jspecify.annotations.Nullable; public class SSEEventListener { @@ -70,7 +69,7 @@ private void handleMessage(String message, @Nullable Future future) { future.cancel(true); // close SSE channel } } - } catch (JSONRPCError error) { + } catch (A2AError error) { if (errorHandler != null) { errorHandler.accept(error); } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index 92541eb20..c994ff89d 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -28,7 +28,6 @@ import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index 3ad728e2d..27c27a7ab 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -2,8 +2,6 @@ import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; -import io.a2a.spec.AgentCard; - /** * Request and response messages used by the tests. These have been created following examples from * the A2A sample messages. diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 6855e1dcc..17f805312 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -14,10 +14,9 @@ import java.util.concurrent.atomic.AtomicReference; import io.a2a.client.transport.jsonrpc.JsonStreamingMessages; +import io.a2a.spec.A2AError; import io.a2a.spec.Artifact; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; -import io.a2a.spec.Part; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -158,8 +157,8 @@ public void testOnEventWithError() throws Exception { // Verify the error was processed correctly assertNotNull(receivedError.get()); - assertInstanceOf(JSONRPCError.class, receivedError.get()); - JSONRPCError jsonrpcError = (JSONRPCError) receivedError.get(); + assertInstanceOf(A2AError.class, receivedError.get()); + A2AError jsonrpcError = (A2AError) receivedError.get(); assertEquals(-32602, jsonrpcError.getCode()); assertEquals("Invalid parameters", jsonrpcError.getMessage()); assertEquals("\"Missing required field\"", jsonrpcError.getData()); diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 4353383c0..3ded0dee9 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -25,6 +25,11 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-spec-grpc diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java index c23eab4a0..c6cb181b4 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java @@ -1,9 +1,12 @@ package io.a2a.client.transport.rest; +import java.util.logging.Level; +import java.util.logging.Logger; + import com.google.gson.JsonObject; import io.a2a.client.http.A2AHttpResponse; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.A2AClientException; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; import io.a2a.spec.ContentTypeNotSupportedError; @@ -17,8 +20,6 @@ import io.a2a.spec.TaskNotCancelableError; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.UnsupportedOperationError; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Utility class to A2AHttpResponse to appropriate A2A error types diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 500daf6f8..0f9fcd5b9 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -1,5 +1,16 @@ package io.a2a.client.transport.rest; +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; +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; import static io.a2a.util.Assert.checkNotNullParam; import java.io.IOException; @@ -13,7 +24,6 @@ import java.util.function.Consumer; import java.util.logging.Logger; -import io.a2a.json.JsonProcessingException; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.util.JsonFormat; @@ -27,30 +37,21 @@ import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; import io.a2a.client.transport.spi.interceptors.PayloadAndHeaders; import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; -import io.a2a.spec.CancelTaskRequest; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskRequest; import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; import io.a2a.spec.TaskPushNotificationConfig; @@ -83,7 +84,7 @@ public RestTransport(@Nullable A2AHttpClient httpClient, AgentCard agentCard, public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("messageSendParams", messageSendParams); io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(ProtoUtils.ToProto.sendMessageRequest(messageSendParams)); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendMessageRequest.METHOD, builder, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SEND_MESSAGE_METHOD, builder, agentCard, context); try { String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, messageSendParams.tenant()) + "/message:send", payloadAndHeaders); io.a2a.grpc.SendMessageResponse.Builder responseBuilder = io.a2a.grpc.SendMessageResponse.newBuilder(); @@ -108,8 +109,7 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); try { @@ -134,8 +134,7 @@ public Task getTask(TaskQueryParams taskQueryParams, @Nullable ClientCallContext checkNotNullParam("taskQueryParams", taskQueryParams); io.a2a.grpc.GetTaskRequest.Builder builder = io.a2a.grpc.GetTaskRequest.newBuilder(); builder.setName("tasks/" + taskQueryParams.id()); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, builder, - agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_TASK_METHOD, builder, agentCard, context); try { StringBuilder url = new StringBuilder(Utils.buildBaseUrl(agentInterface, taskQueryParams.tenant())); if (taskQueryParams.historyLength() != null && taskQueryParams.historyLength() > 0) { @@ -169,8 +168,7 @@ public Task cancelTask(TaskIdParams taskIdParams, @Nullable ClientCallContext co checkNotNullParam("taskIdParams", taskIdParams); io.a2a.grpc.CancelTaskRequest.Builder builder = io.a2a.grpc.CancelTaskRequest.newBuilder(); builder.setName("tasks/" + taskIdParams.id()); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(CancelTaskRequest.METHOD, builder, - agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(CANCEL_TASK_METHOD, builder, agentCard, context); try { String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, taskIdParams.tenant()) + String.format("/tasks/%1s:cancel", taskIdParams.id()), payloadAndHeaders); io.a2a.grpc.Task.Builder responseBuilder = io.a2a.grpc.Task.newBuilder(); @@ -209,8 +207,7 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo builder.setIncludeArtifacts(true); } - PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTasksRequest.METHOD, builder, - agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(LIST_TASK_METHOD, builder, agentCard, context); try { // Build query string @@ -282,7 +279,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN if (request.pushNotificationConfig().id() != null) { builder.setConfigId(request.pushNotificationConfig().id()); } - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SetTaskPushNotificationConfigRequest.METHOD, builder, agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, builder, agentCard, context); try { String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.taskId()), payloadAndHeaders); io.a2a.grpc.TaskPushNotificationConfig.Builder responseBuilder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); @@ -310,7 +307,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(GetTaskPu builder.setName(String.format("/tasks/%1s/pushNotificationConfigs/", request.id())); url.append(builder.getName()); } - PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskPushNotificationConfigRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, builder, agentCard, context); try { A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url.toString()); @@ -340,7 +337,7 @@ public ListTaskPushNotificationConfigResult listTaskPushNotificationConfiguratio io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder(); builder.setParent(String.format("/tasks/%1s/pushNotificationConfigs", request.id())); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(ListTaskPushNotificationConfigRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, builder, agentCard, context); try { String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs", request.id()); @@ -369,7 +366,7 @@ public ListTaskPushNotificationConfigResult listTaskPushNotificationConfiguratio public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.DeleteTaskPushNotificationConfigRequestOrBuilder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(DeleteTaskPushNotificationConfigRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, builder, agentCard, context); try { String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s/pushNotificationConfigs/%2s", request.id(), request.pushNotificationConfigId()); @@ -396,7 +393,7 @@ public void resubscribe(TaskIdParams request, Consumer event checkNotNullParam("request", request); io.a2a.grpc.SubscribeToTaskRequest.Builder builder = io.a2a.grpc.SubscribeToTaskRequest.newBuilder(); builder.setName("tasks/" + request.id()); - PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD, builder, + PayloadAndHeaders payloadAndHeaders = applyInterceptors(SUBSCRIBE_TO_TASK_METHOD, builder, agentCard, context); AtomicReference> ref = new AtomicReference<>(); RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); @@ -430,8 +427,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli if (!needsExtendedCard) { return agentCard; } - PayloadAndHeaders payloadAndHeaders = applyInterceptors(GetTaskRequest.METHOD, null, - agentCard, context); + PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, null, agentCard, context); String url = Utils.buildBaseUrl(agentInterface, "") + "/extendedAgentCard"; A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); if (payloadAndHeaders.getHeaders() != null) { diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index bb806abb8..3230d21fe 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -21,6 +21,11 @@ io.github.a2asdk a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + org.junit.jupiter junit-jupiter-api diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 4b978c717..e0f4b3a0c 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -1,9 +1,9 @@ package io.a2a.client.transport.spi; -import java.util.List; import java.util.function.Consumer; import io.a2a.client.transport.spi.interceptors.ClientCallContext; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -12,7 +12,6 @@ import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java index 8efdb779b..c04b52882 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfig.java @@ -1,10 +1,10 @@ package io.a2a.client.transport.spi; -import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; import java.util.ArrayList; - import java.util.List; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; + /** * Configuration for an A2A client transport. */ diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java index 0858f910b..6dbd9ea60 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransportConfigBuilder.java @@ -1,10 +1,10 @@ package io.a2a.client.transport.spi; -import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; - import java.util.ArrayList; import java.util.List; +import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; + public abstract class ClientTransportConfigBuilder, B extends ClientTransportConfigBuilder> { diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java index 816ad3e5f..7f8075312 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/PayloadAndHeaders.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; + import org.jspecify.annotations.Nullable; public class PayloadAndHeaders { diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java index 642c1e0ee..0aeacff66 100644 --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java @@ -19,7 +19,6 @@ import io.a2a.spec.OAuthFlows; import io.a2a.spec.OpenIdConnectSecurityScheme; import io.a2a.spec.SecurityScheme; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java index 7ba9f1d76..d3d36044d 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentExecutorProducer.java @@ -1,21 +1,22 @@ package io.a2a.examples.cloud; +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.A2AError; import io.a2a.spec.InternalError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Part; import io.a2a.spec.TextPart; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Produces; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; - /** * Producer for the cloud deployment example agent executor. */ @@ -45,7 +46,7 @@ public AgentExecutor agentExecutor() { private static class CloudAgentExecutor implements AgentExecutor { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater updater = new TaskUpdater(context, eventQueue); try { @@ -98,7 +99,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP LOGGER.info("Artifact added on pod: {}", podName); } - } catch (JSONRPCError e) { + } catch (A2AError e) { LOGGER.error("JSONRPC error processing task", e); throw e; } catch (Exception e) { @@ -108,7 +109,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { LOGGER.info("Task cancellation requested"); TaskUpdater updater = new TaskUpdater(context, eventQueue); updater.cancel(); diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 8f5b63406..2e8a3f3f0 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -20,6 +20,11 @@ io.github.a2asdk a2a-java-sdk-client + + io.github.a2asdk + a2a-java-sdk-jsonrpc-common + ${project.version} + diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index 413efb03c..c3b1cb473 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -16,7 +16,7 @@ import io.a2a.client.http.A2ACardResolver; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfig; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; import io.a2a.spec.Part; diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java index 1d7519b60..4bba596db 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentExecutorProducer.java @@ -3,11 +3,11 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import io.a2a.A2A; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; -import io.a2a.A2A; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; import io.a2a.spec.UnsupportedOperationError; @ApplicationScoped @@ -17,12 +17,12 @@ public class AgentExecutorProducer { public AgentExecutor agentExecutor() { return new AgentExecutor() { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { eventQueue.enqueueEvent(A2A.toAgentMessage("Hello World")); } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { throw new UnsupportedOperationError(); } }; diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 821eaa5d9..f55cad367 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -22,6 +22,11 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + jakarta.annotation jakarta.annotation-api diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index 357cf42e1..ad7b125aa 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -11,7 +11,7 @@ import jakarta.persistence.PersistenceContext; import jakarta.transaction.Transactional; -import io.a2a.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java index 225936e47..468f8eddb 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java @@ -6,8 +6,8 @@ import jakarta.persistence.Table; import jakarta.persistence.Transient; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.PushNotificationConfig; @Entity diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index 6871a0f09..9df56edf8 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -7,14 +7,14 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import jakarta.inject.Inject; -import jakarta.transaction.Transactional; - import java.util.List; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + import io.a2a.client.Client; import io.a2a.client.config.ClientConfig; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java index 3c4242081..df29c1688 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreTestAgentExecutor.java @@ -11,8 +11,8 @@ import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.PushNotificationSender; import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.A2AError; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Part; import io.a2a.spec.TextPart; @@ -33,7 +33,7 @@ public class JpaDatabasePushNotificationConfigStoreTestAgentExecutor { public AgentExecutor agentExecutor() { return new AgentExecutor() { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); String command = getLastTextPart(context.getMessage()); @@ -55,14 +55,14 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); taskUpdater.cancel(); } }; } - private String getLastTextPart(Message message) throws JSONRPCError { + private String getLastTextPart(Message message) throws A2AError { if (message.parts() == null || message.parts().isEmpty()) { return ""; } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java index 1074b7848..6ecf045eb 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java @@ -11,16 +11,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.mockito.ArgumentCaptor; - import java.util.List; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import jakarta.inject.Inject; import jakarta.transaction.Transactional; @@ -36,6 +28,12 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @QuarkusTest public class JpaPushNotificationConfigStoreTest { diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index c5d1ee8bf..cdc6da7e9 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -20,6 +20,11 @@ a2a-java-sdk-server-common ${project.version} + + io.github.a2asdk + a2a-java-sdk-jsonrpc-common + ${project.version} + io.github.a2asdk a2a-java-extras-common diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java index 5eac92d4f..30527b797 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java @@ -4,10 +4,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonSetter; - import io.a2a.server.events.EventQueueItem; +import io.a2a.spec.A2AError; import io.a2a.spec.Event; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.StreamingEventKind; public class ReplicatedEventQueueItem implements EventQueueItem { @@ -17,7 +16,7 @@ public class ReplicatedEventQueueItem implements EventQueueItem { private StreamingEventKind event; @JsonInclude(JsonInclude.Include.NON_NULL) - private JSONRPCError error; + private A2AError error; private boolean closedEvent; @@ -32,8 +31,8 @@ public ReplicatedEventQueueItem(String taskId, StreamingEventKind event) { this.error = null; } - // Constructor for creating from A2A JSONRPCError objects - public ReplicatedEventQueueItem(String taskId, JSONRPCError error) { + // Constructor for creating from A2A A2AError objects + public ReplicatedEventQueueItem(String taskId, A2AError error) { this.taskId = taskId; this.event = null; this.error = error; @@ -50,12 +49,12 @@ public ReplicatedEventQueueItem(String taskId, Event event) { this.event = streamingEvent; this.error = null; this.closedEvent = false; - } else if (event instanceof JSONRPCError jsonRpcError) { + } else if (event instanceof A2AError jsonRpcError) { this.event = null; this.error = jsonRpcError; this.closedEvent = false; } else { - throw new IllegalArgumentException("Event must be StreamingEventKind, JSONRPCError, or QueueClosedEvent, got: " + event.getClass()); + throw new IllegalArgumentException("Event must be StreamingEventKind, A2AError, or QueueClosedEvent, got: " + event.getClass()); } } @@ -85,17 +84,17 @@ public void setEvent(StreamingEventKind event) { } /** - * Get the JSONRPCError field (for JSON serialization). - * @return the JSONRPCError or null + * Get the A2AError field (for JSON serialization). + * @return the A2AError or null */ @JsonGetter("error") @JsonInclude(JsonInclude.Include.NON_NULL) - public JSONRPCError getErrorObject() { + public A2AError getErrorObject() { return error; } @JsonSetter("error") - public void setError(JSONRPCError error) { + public void setError(A2AError error) { this.error = error; this.event = null; // Clear event when setting error } @@ -103,7 +102,7 @@ public void setError(JSONRPCError error) { /** * Get the contained event as the generic Event interface (implements EventQueueItem). * This is the method required by the EventQueueItem interface. - * @return the event (StreamingEventKind, JSONRPCError, or QueueClosedEvent) or null if none is set + * @return the event (StreamingEventKind, A2AError, or QueueClosedEvent) or null if none is set */ @JsonIgnore @Override @@ -137,7 +136,7 @@ public boolean hasEvent() { /** * Check if this ReplicatedEvent contains an error. - * @return true if it contains a JSONRPCError + * @return true if it contains a A2AError */ public boolean hasError() { return error != null; diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index 5e90ed00d..88c0c75ab 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -7,17 +7,16 @@ import jakarta.enterprise.inject.Alternative; import jakarta.inject.Inject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import io.a2a.extras.common.events.TaskFinalizedEvent; -import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.events.EventEnqueueHook; import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueFactory; import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.InMemoryQueueManager; import io.a2a.server.events.QueueManager; +import io.a2a.server.tasks.TaskStateProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @ApplicationScoped @Alternative diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index ae2679bdc..ca781b5b0 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -9,15 +9,16 @@ import java.util.List; -import io.a2a.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.events.QueueClosedEvent; +import io.a2a.spec.A2AError; import io.a2a.spec.Artifact; import io.a2a.spec.Event; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; @@ -32,12 +33,11 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.json.JsonUtil; import org.junit.jupiter.api.Test; /** * Comprehensive test for serialization/deserialization of all StreamingEventKind classes - * and JSONRPCError subclasses to ensure proper type handling in replication. + * and A2AError subclasses to ensure proper type handling in replication. */ public class EventSerializationTest { @@ -169,9 +169,9 @@ public void testTaskArtifactUpdateEventSerialization() throws JsonProcessingExce } @Test - public void testJSONRPCErrorSubclassesSerialization() throws JsonProcessingException { - // Test various JSONRPCError subclasses - JSONRPCError[] errors = { + public void testA2AErrorSubclassesSerialization() throws JsonProcessingException { + // Test various A2AError subclasses + A2AError[] errors = { new InvalidRequestError("Invalid request"), new MethodNotFoundError(), new InvalidParamsError("Invalid params"), @@ -184,13 +184,13 @@ public void testJSONRPCErrorSubclassesSerialization() throws JsonProcessingExcep // Note: ContentTypeNotSupportedError and InvalidAgentResponseError need specific constructor parameters }; - for (JSONRPCError originalError : errors) { + for (A2AError originalError : errors) { // Test serialization String json = JsonUtil.toJson(originalError); assertTrue(json.contains("\"message\""), "JSON should contain error message for " + originalError.getClass().getSimpleName()); - // Test deserialization - it's acceptable to deserialize as base JSONRPCError - JSONRPCError deserializedError = JsonUtil.fromJson(json, JSONRPCError.class); + // Test deserialization - it's acceptable to deserialize as base A2AError + A2AError deserializedError = JsonUtil.fromJson(json, A2AError.class); assertNotNull(deserializedError, "Should deserialize successfully for " + originalError.getClass().getSimpleName()); assertEquals(originalError.getMessage(), deserializedError.getMessage(), "Error message should match for " + originalError.getClass().getSimpleName()); assertEquals(originalError.getCode(), deserializedError.getCode(), "Error code should match for " + originalError.getClass().getSimpleName()); @@ -243,10 +243,10 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc @Test public void testReplicatedEventWithErrorSerialization() throws JsonProcessingException { - // Test that ReplicatedEventQueueItem can properly handle JSONRPCError + // Test that ReplicatedEventQueueItem can properly handle A2AError InvalidRequestError error = new InvalidRequestError("Invalid request for testing"); - // Create ReplicatedEventQueueItem with JSONRPCError + // Create ReplicatedEventQueueItem with A2AError ReplicatedEventQueueItem originalReplicatedEvent = new ReplicatedEventQueueItem("error-test-task", error); // Serialize the ReplicatedEventQueueItemQueueItem @@ -261,7 +261,7 @@ public void testReplicatedEventWithErrorSerialization() throws JsonProcessingExc assertEquals(originalReplicatedEvent.getTaskId(), deserializedReplicatedEvent.getTaskId()); // Should get the error back - JSONRPCError retrievedError = deserializedReplicatedEvent.getErrorObject(); + A2AError retrievedError = deserializedReplicatedEvent.getErrorObject(); assertNotNull(retrievedError); assertEquals(error.getMessage(), retrievedError.getMessage()); assertEquals(error.getCode(), retrievedError.getCode()); diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 0a9326ea0..43571cd30 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -17,6 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger; import io.a2a.extras.common.events.TaskFinalizedEvent; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueClosedException; import io.a2a.server.events.EventQueueItem; @@ -27,7 +28,6 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.json.JsonUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 77f51477d..2508821b6 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -20,6 +20,11 @@ a2a-java-queue-manager-replicated-core ${project.version} + + io.github.a2asdk + a2a-java-sdk-jsonrpc-common + ${project.version} + io.quarkus quarkus-messaging diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java index 26797bc5f..6f0ca9876 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/main/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategy.java @@ -6,7 +6,7 @@ import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.extras.queuemanager.replicated.core.ReplicationStrategy; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonUtil; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; import org.eclipse.microprofile.reactive.messaging.Incoming; diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java index 593371f28..4638f7385 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java @@ -1,11 +1,21 @@ package io.a2a.extras.queuemanager.replicated.mp_reactive; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import jakarta.enterprise.event.Event; +import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; +import io.a2a.jsonrpc.common.json.JsonUtil; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; import org.eclipse.microprofile.reactive.messaging.Emitter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,13 +24,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; -import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.TaskStatus; -import io.a2a.spec.TaskState; -import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.json.JsonUtil; - @ExtendWith(MockitoExtension.class) class ReactiveMessagingReplicationStrategyTest { diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java index 51aa7ac0c..546d22939 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentExecutor.java @@ -4,7 +4,7 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; import io.a2a.spec.Task; import io.a2a.spec.TextPart; @@ -18,7 +18,7 @@ */ public class MultiInstanceReplicationAgentExecutor implements AgentExecutor { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { Task task = context.getTask(); TaskUpdater updater = new TaskUpdater(context, eventQueue); @@ -41,7 +41,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater updater = new TaskUpdater(context, eventQueue); updater.cancel(); } diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index b92043be6..29cca7256 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -25,6 +25,11 @@ a2a-java-queue-manager-replication-mp-reactive ${project.version} + + io.github.a2asdk + a2a-java-sdk-jsonrpc-common + ${project.version} + diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index f174303ac..3825f3bad 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -28,6 +28,7 @@ import io.a2a.client.transport.jsonrpc.JSONRPCTransport; import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.PublicAgentCard; import io.a2a.server.events.QueueClosedEvent; import io.a2a.spec.A2AClientException; @@ -39,7 +40,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import io.a2a.json.JsonUtil; import io.quarkus.test.junit.QuarkusTest; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java index 83642b183..547a731fd 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicatedQueueManagerTest.java @@ -1,14 +1,13 @@ package io.a2a.extras.queuemanager.replicated.tests; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import jakarta.inject.Inject; -import org.junit.jupiter.api.Test; - import io.a2a.extras.queuemanager.replicated.core.ReplicatedQueueManager; import io.a2a.server.events.QueueManager; import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; /** * Basic test to verify the ReplicatedQueueManager is properly configured. diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java index 603857ef1..8e4db21dd 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentExecutor.java @@ -9,8 +9,8 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.A2AError; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Part; import io.a2a.spec.TextPart; @@ -28,7 +28,7 @@ public class ReplicationTestAgentExecutor { public AgentExecutor agentExecutor() { return new AgentExecutor() { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); String lastText = getLastTextPart(context.getMessage()); @@ -56,14 +56,14 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); taskUpdater.cancel(); } }; } - private String getLastTextPart(Message message) throws JSONRPCError { + private String getLastTextPart(Message message) throws A2AError { if (message.parts().isEmpty()) { throw new InvalidRequestError("No parts in message"); } diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java index 9e308ec42..a7edd13d3 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/TestKafkaEventConsumer.java @@ -6,11 +6,10 @@ import jakarta.enterprise.context.ApplicationScoped; -import org.eclipse.microprofile.reactive.messaging.Incoming; - import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.quarkus.arc.profile.IfBuildProfile; +import org.eclipse.microprofile.reactive.messaging.Incoming; /** * Test consumer for Kafka replicated events using reactive messaging. diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index 3f5aee4f1..7c3c37062 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -22,6 +22,11 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-extras-common diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index df65f290e..49dad1728 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -16,14 +16,14 @@ import jakarta.persistence.TypedQuery; import jakarta.transaction.Transactional; -import io.a2a.json.JsonProcessingException; import io.a2a.extras.common.events.TaskFinalizedEvent; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.server.config.A2AConfigProvider; import io.a2a.server.tasks.TaskStateProvider; import io.a2a.server.tasks.TaskStore; import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.Task; import org.slf4j.Logger; diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java index 3aabad9d7..021a693e3 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaTask.java @@ -8,8 +8,8 @@ import jakarta.persistence.Table; import jakarta.persistence.Transient; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.Task; @Entity diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index cfb4eadc8..ea77f73c7 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -15,10 +15,10 @@ import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.server.tasks.TaskStore; import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java index 58e52e57d..f3b310750 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTestAgentExecutor.java @@ -9,8 +9,8 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; +import io.a2a.spec.A2AError; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Part; import io.a2a.spec.TextPart; @@ -28,7 +28,7 @@ public class JpaDatabaseTaskStoreTestAgentExecutor { public AgentExecutor agentExecutor() { return new AgentExecutor() { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { System.out.println("TestAgentExecutor.execute() called for task: " + context.getTaskId()); System.out.println("Message " + context.getMessage()); @@ -47,14 +47,14 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); taskUpdater.cancel(); } }; } - private String getLastTextPart(Message message) throws JSONRPCError { + private String getLastTextPart(Message message) throws A2AError { Part part = message.parts().get(message.parts().size() - 1); if (part instanceof TextPart) { return ((TextPart) part).text(); diff --git a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java index 0aba695ff..6d2e5f55f 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java +++ b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java @@ -8,11 +8,11 @@ import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; import org.jspecify.annotations.Nullable; import static io.a2a.util.Assert.checkNotNullParam; @@ -135,9 +135,9 @@ public AgentCard getAgentCard() throws A2AClientError, A2AClientJSONError { try { io.a2a.grpc.AgentCard.Builder agentCardBuilder = io.a2a.grpc.AgentCard.newBuilder(); - JSONRPCUtils.parseJsonString(body, agentCardBuilder, null); + JSONRPCUtils.parseJsonString(body, agentCardBuilder, ""); return ProtoUtils.FromProto.agentCard(agentCardBuilder); - } catch (JSONRPCError | JsonProcessingException e) { + } catch (A2AError | JsonProcessingException e) { throw new A2AClientJSONError("Could not unmarshal agent card response", e); } } diff --git a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java index 242001d10..3254992ef 100644 --- a/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java +++ b/http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java @@ -12,7 +12,7 @@ import com.google.protobuf.util.JsonFormat; import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; @@ -82,7 +82,7 @@ public void testGetAgentCardSuccess() throws Exception { private AgentCard unmarshalFrom(String body) throws JsonProcessingException { io.a2a.grpc.AgentCard.Builder agentCardBuilder = io.a2a.grpc.AgentCard.newBuilder(); - JSONRPCUtils.parseJsonString(body, agentCardBuilder, null); + JSONRPCUtils.parseJsonString(body, agentCardBuilder, ""); return ProtoUtils.FromProto.agentCard(agentCardBuilder); } diff --git a/jsonrpc-common/pom.xml b/jsonrpc-common/pom.xml new file mode 100644 index 000000000..f566ad9a3 --- /dev/null +++ b/jsonrpc-common/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 0.4.0.Alpha1-SNAPSHOT + + a2a-java-sdk-jsonrpc-common + + jar + + Java SDK A2A JSONRPC Common + Java SDK for the Agent2Agent Protocol (A2A) - Internal implementation utilities (not part of public API) + + + + ${project.groupId} + a2a-java-sdk-spec + + + com.google.code.gson + gson + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + + diff --git a/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/IdJsonMappingException.java similarity index 92% rename from spec/src/main/java/io/a2a/spec/IdJsonMappingException.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/IdJsonMappingException.java index 6d0e1692f..de56d119c 100644 --- a/spec/src/main/java/io/a2a/spec/IdJsonMappingException.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/IdJsonMappingException.java @@ -1,6 +1,4 @@ -package io.a2a.spec; - -import io.a2a.json.JsonMappingException; +package io.a2a.jsonrpc.common.json; /** * JSON mapping exception that includes request ID for error tracking. @@ -10,7 +8,7 @@ public class IdJsonMappingException extends JsonMappingException { /** * The JSON-RPC request ID associated with this exception. */ - Object id; + private Object id; /** * Constructs exception with message and ID. diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/InvalidParamsJsonMappingException.java similarity index 95% rename from spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/InvalidParamsJsonMappingException.java index a3917effb..5ac8c769c 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsJsonMappingException.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/InvalidParamsJsonMappingException.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.json; /** * Exception for invalid parameters during JSON mapping. diff --git a/spec/src/main/java/io/a2a/json/JsonMappingException.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonMappingException.java similarity index 99% rename from spec/src/main/java/io/a2a/json/JsonMappingException.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonMappingException.java index 8d3ad48e7..dc85d6edb 100644 --- a/spec/src/main/java/io/a2a/json/JsonMappingException.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonMappingException.java @@ -1,4 +1,4 @@ -package io.a2a.json; +package io.a2a.jsonrpc.common.json; import org.jspecify.annotations.Nullable; diff --git a/spec/src/main/java/io/a2a/json/JsonProcessingException.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonProcessingException.java similarity index 97% rename from spec/src/main/java/io/a2a/json/JsonProcessingException.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonProcessingException.java index 9af50b7ce..cdbb7b4eb 100644 --- a/spec/src/main/java/io/a2a/json/JsonProcessingException.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonProcessingException.java @@ -1,4 +1,4 @@ -package io.a2a.json; +package io.a2a.jsonrpc.common.json; import org.jspecify.annotations.Nullable; diff --git a/spec/src/main/java/io/a2a/json/JsonUtil.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java similarity index 97% rename from spec/src/main/java/io/a2a/json/JsonUtil.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java index ddd712a43..c6de924bf 100644 --- a/spec/src/main/java/io/a2a/json/JsonUtil.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java @@ -1,5 +1,6 @@ -package io.a2a.json; +package io.a2a.jsonrpc.common.json; +import static io.a2a.jsonrpc.common.json.JsonUtil.A2AErrorTypeAdapter.THROWABLE_MARKER_FIELD; import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; @@ -12,6 +13,13 @@ import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Map; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; @@ -21,6 +29,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; +import io.a2a.spec.A2AError; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DataPart; import io.a2a.spec.FileContent; @@ -31,7 +40,6 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; @@ -45,16 +53,8 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import java.io.StringReader; -import java.lang.reflect.Type; -import java.util.Map; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; import org.jspecify.annotations.Nullable; -import static io.a2a.json.JsonUtil.JSONRPCErrorTypeAdapter.THROWABLE_MARKER_FIELD; - /** * Utility class for JSON operations. */ @@ -64,7 +64,7 @@ private static GsonBuilder createBaseGsonBuilder() { return new GsonBuilder() .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter()) - .registerTypeHierarchyAdapter(JSONRPCError.class, new JSONRPCErrorTypeAdapter()) + .registerTypeHierarchyAdapter(A2AError.class, new A2AErrorTypeAdapter()) .registerTypeAdapter(TaskState.class, new TaskStateTypeAdapter()) .registerTypeAdapter(Message.Role.class, new RoleTypeAdapter()) .registerTypeHierarchyAdapter(FileContent.class, new FileContentTypeAdapter()); @@ -261,7 +261,7 @@ Throwable read(JsonReader in) throws java.io.IOException { } /** - * Gson TypeAdapter for serializing and deserializing {@link JSONRPCError} and its subclasses. + * Gson TypeAdapter for serializing and deserializing {@link A2AError} and its subclasses. *

      * This adapter handles polymorphic deserialization based on the error code, creating the * appropriate subclass instance. @@ -279,12 +279,12 @@ Throwable read(JsonReader in) throws java.io.IOException { *

    • -32004: {@link UnsupportedOperationError}
    • *
    • -32005: {@link ContentTypeNotSupportedError}
    • *
    • -32006: {@link InvalidAgentResponseError}
    • - *
    • Other codes: {@link JSONRPCError}
    • + *
    • Other codes: {@link A2AError}
    • *
    * - * @see JSONRPCError + * @see A2AError */ - static class JSONRPCErrorTypeAdapter extends TypeAdapter { + static class A2AErrorTypeAdapter extends TypeAdapter { private static final ThrowableTypeAdapter THROWABLE_ADAPTER = new ThrowableTypeAdapter(); static final String THROWABLE_MARKER_FIELD = "__throwable"; @@ -294,7 +294,7 @@ static class JSONRPCErrorTypeAdapter extends TypeAdapter { private static final String TYPE_FIELD = "type"; @Override - public void write(JsonWriter out, JSONRPCError value) throws java.io.IOException { + public void write(JsonWriter out, A2AError value) throws java.io.IOException { if (value == null) { out.nullValue(); return; @@ -317,7 +317,7 @@ public void write(JsonWriter out, JSONRPCError value) throws java.io.IOException @Override public @Nullable - JSONRPCError read(JsonReader in) throws java.io.IOException { + A2AError read(JsonReader in) throws java.io.IOException { if (in.peek() == com.google.gson.stream.JsonToken.NULL) { in.nextNull(); return null; @@ -390,11 +390,11 @@ Object readDataValue(JsonReader in) throws java.io.IOException { } /** - * Creates the appropriate JSONRPCError subclass based on the error code. + * Creates the appropriate A2AError subclass based on the error code. */ - private JSONRPCError createErrorInstance(@Nullable Integer code, @Nullable String message, @Nullable Object data) { + private A2AError createErrorInstance(@Nullable Integer code, @Nullable String message, @Nullable Object data) { if (code == null) { - throw new JsonSyntaxException("JSONRPCError must have a code field"); + throw new JsonSyntaxException("A2AError must have a code field"); } return switch (code) { @@ -421,7 +421,7 @@ private JSONRPCError createErrorInstance(@Nullable Integer code, @Nullable Strin case INVALID_AGENT_RESPONSE_ERROR_CODE -> new InvalidAgentResponseError(code, message, data); default -> - new JSONRPCError(code, message, data); + new A2AError(code, message, data); }; } } diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/MethodNotFoundJsonMappingException.java similarity index 93% rename from spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/MethodNotFoundJsonMappingException.java index dc1f13c03..5bac1c026 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundJsonMappingException.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/MethodNotFoundJsonMappingException.java @@ -1,4 +1,6 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.json; + +import io.a2a.spec.MethodNotFoundError; /** * Exception thrown when JSON mapping fails due to a method not found error. diff --git a/spec/src/main/java/io/a2a/json/package-info.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/package-info.java similarity index 88% rename from spec/src/main/java/io/a2a/json/package-info.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/package-info.java index 81fe05a3a..e6f547b92 100644 --- a/spec/src/main/java/io/a2a/json/package-info.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/package-info.java @@ -5,4 +5,4 @@ * allowing the SDK to be independent of any specific JSON library implementation. */ @org.jspecify.annotations.NullMarked -package io.a2a.json; +package io.a2a.jsonrpc.common.json; diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AErrorResponse.java similarity index 81% rename from spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AErrorResponse.java index 445b6f330..e2f91c35a 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCErrorResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AErrorResponse.java @@ -1,5 +1,6 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; +import io.a2a.spec.A2AError; import io.a2a.util.Assert; /** @@ -9,18 +10,18 @@ * According to the JSON-RPC 2.0 specification, an error response must contain: *
      *
    • {@code jsonrpc} - Always "2.0"
    • - *
    • {@code error} - A {@link JSONRPCError} object describing the error
    • + *
    • {@code error} - A {@link A2AError} object describing the error
    • *
    • {@code id} - The request ID, or null if the ID could not be determined
    • *
    *

    * The {@code result} field must be absent or null in error responses. This class * enforces that constraint by fixing the result type parameter to {@code Void}. * - * @see JSONRPCError - * @see JSONRPCResponse + * @see A2AError + * @see A2AResponse * @see JSON-RPC 2.0 Response Object */ -public final class JSONRPCErrorResponse extends JSONRPCResponse { +public final class A2AErrorResponse extends A2AResponse { /** * Constructs a JSON-RPC error response with all fields. @@ -33,7 +34,7 @@ public final class JSONRPCErrorResponse extends JSONRPCResponse { * @param error the error object describing what went wrong (required) * @throws IllegalArgumentException if error is null */ - public JSONRPCErrorResponse(String jsonrpc, Object id, Void result, JSONRPCError error) { + public A2AErrorResponse(String jsonrpc, Object id, Void result, A2AError error) { super(jsonrpc, id, result, error, Void.class); Assert.checkNotNullParam("error", error); } @@ -46,7 +47,7 @@ public JSONRPCErrorResponse(String jsonrpc, Object id, Void result, JSONRPCError * @param id the request ID * @param error the error object (required) */ - public JSONRPCErrorResponse(Object id, JSONRPCError error) { + public A2AErrorResponse(Object id, A2AError error) { this(null, id, null, error); } @@ -58,7 +59,7 @@ public JSONRPCErrorResponse(Object id, JSONRPCError error) { * * @param error the error object (required) */ - public JSONRPCErrorResponse(JSONRPCError error) { + public A2AErrorResponse(A2AError error) { this(null, null, null, error); } } diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AMessage.java similarity index 86% rename from spec/src/main/java/io/a2a/spec/JSONRPCMessage.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AMessage.java index f3b8ed5fa..183b8359f 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCMessage.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AMessage.java @@ -1,23 +1,23 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; /** * Base interface for all JSON-RPC 2.0 protocol messages in the A2A Protocol. *

    * This sealed interface defines the fundamental structure shared by all JSON-RPC 2.0 * messages used in the A2A Protocol's JSON-RPC transport layer. It ensures type safety - * and exhaustiveness checking by permitting only {@link JSONRPCRequest} and {@link JSONRPCResponse} + * and exhaustiveness checking by permitting only {@link A2ARequest} and {@link A2AResponse} * as implementing types. *

    * According to the JSON-RPC 2.0 specification, all messages must include a {@code jsonrpc} * version field set to "2.0", and may optionally include an {@code id} field for correlation * between requests and responses. * - * @see JSONRPCRequest - * @see JSONRPCResponse + * @see A2ARequest + * @see A2AResponse * @see JSON-RPC 2.0 Specification * @see A2A Protocol Specification */ -public sealed interface JSONRPCMessage permits JSONRPCRequest, JSONRPCResponse { +public sealed interface A2AMessage permits A2ARequest, A2AResponse { /** * The JSON-RPC protocol version string as defined by the JSON-RPC 2.0 specification. diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2ARequest.java similarity index 94% rename from spec/src/main/java/io/a2a/spec/JSONRPCRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2ARequest.java index b09d445e5..e7200219a 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2ARequest.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; import io.a2a.util.Assert; import io.a2a.util.Utils; @@ -28,7 +28,7 @@ * @see JSON-RPC 2.0 Specification * @see A2A Protocol Specification */ -public abstract sealed class JSONRPCRequest implements JSONRPCMessage permits NonStreamingJSONRPCRequest, StreamingJSONRPCRequest { +public abstract sealed class A2ARequest implements A2AMessage permits NonStreamingJSONRPCRequest, StreamingJSONRPCRequest { /** The JSON-RPC protocol version. */ protected String jsonrpc; @@ -45,7 +45,7 @@ public abstract sealed class JSONRPCRequest implements JSONRPCMessage permits /** * Default constructor for JSON deserialization. */ - public JSONRPCRequest() { + public A2ARequest() { } /** diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java similarity index 74% rename from spec/src/main/java/io/a2a/spec/JSONRPCResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java index 3c6685939..73bb430eb 100644 --- a/spec/src/main/java/io/a2a/spec/JSONRPCResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java @@ -1,7 +1,8 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; import static io.a2a.util.Utils.defaultIfNull; +import io.a2a.spec.A2AError; import io.a2a.util.Assert; /** @@ -9,7 +10,7 @@ * * @param the type of the result value returned in successful responses */ -public abstract sealed class JSONRPCResponse implements JSONRPCMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { +public abstract sealed class A2AResponse implements A2AMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, A2AErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { /** The JSON-RPC protocol version. */ protected String jsonrpc; @@ -18,12 +19,12 @@ public abstract sealed class JSONRPCResponse implements JSONRPCMessage permit /** The result of the method invocation. */ protected T result; /** The error object if the invocation failed. */ - protected JSONRPCError error; + protected A2AError error; /** * Default constructor for deserialization. */ - public JSONRPCResponse() { + public A2AResponse() { } /** @@ -35,7 +36,7 @@ public JSONRPCResponse() { * @param error the error if any * @param resultType the result type class */ - public JSONRPCResponse(String jsonrpc, Object id, T result, JSONRPCError error, Class resultType) { + public A2AResponse(String jsonrpc, Object id, T result, A2AError error, Class resultType) { if (jsonrpc != null && ! jsonrpc.equals(JSONRPC_VERSION)) { throw new IllegalArgumentException("Invalid JSON-RPC protocol version"); } @@ -76,7 +77,7 @@ public T getResult() { * * @return the error if any */ - public JSONRPCError getError() { + public A2AError getError() { return this.error; } } diff --git a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CancelTaskRequest.java similarity index 94% rename from spec/src/main/java/io/a2a/spec/CancelTaskRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CancelTaskRequest.java index e2667fc45..836cb6e06 100644 --- a/spec/src/main/java/io/a2a/spec/CancelTaskRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CancelTaskRequest.java @@ -1,7 +1,13 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD; import java.util.UUID; +import io.a2a.spec.TaskIdParams; +import io.a2a.spec.TaskNotCancelableError; +import io.a2a.spec.TaskState; + /** * JSON-RPC request to cancel an in-progress task. *

    @@ -21,9 +27,6 @@ */ public final class CancelTaskRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name for canceling tasks. */ - public static final String METHOD = "CancelTask"; - /** * Creates a new CancelTaskRequest with the specified JSON-RPC parameters. * @@ -33,7 +36,7 @@ public final class CancelTaskRequest extends NonStreamingJSONRPCRequest * If the task cannot be canceled (e.g., already completed) or is not found, the error - * field will contain a {@link JSONRPCError} such as {@link TaskNotCancelableError} or + * field will contain a {@link io.a2a.spec.A2AError} such as {@link TaskNotCancelableError} or * {@link TaskNotFoundError}. * * @see CancelTaskRequest for the corresponding request @@ -16,7 +22,7 @@ * @see A2A Protocol Specification */ -public final class CancelTaskResponse extends JSONRPCResponse { +public final class CancelTaskResponse extends A2AResponse { /** * Constructs a CancelTaskResponse with full parameters. @@ -26,7 +32,7 @@ public final class CancelTaskResponse extends JSONRPCResponse { * @param result the task result * @param error the error if any */ - public CancelTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError error) { + public CancelTaskResponse(String jsonrpc, Object id, Task result, A2AError error) { super(jsonrpc, id, result, error, Task.class); } @@ -36,7 +42,7 @@ public CancelTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError e * @param id the request ID * @param error the error */ - public CancelTaskResponse(Object id, JSONRPCError error) { + public CancelTaskResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/DeleteTaskPushNotificationConfigRequest.java similarity index 94% rename from spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/DeleteTaskPushNotificationConfigRequest.java index 9fcf3ace3..64f983520 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/DeleteTaskPushNotificationConfigRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + import java.util.UUID; +import io.a2a.spec.DeleteTaskPushNotificationConfigParams; + +import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; + /** * JSON-RPC request to delete a push notification configuration from a task. *

    @@ -17,9 +22,6 @@ */ public final class DeleteTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name for deleting push notification configurations. */ - public static final String METHOD = "DeleteTaskPushNotificationConfig"; - /** * Creates a new DeleteTaskPushNotificationConfigRequest with the specified JSON-RPC parameters. * @@ -29,7 +31,7 @@ public final class DeleteTaskPushNotificationConfigRequest extends NonStreamingJ * @throws IllegalArgumentException if jsonrpc version is invalid, method is not "DeleteTaskPushNotificationConfig", or id is not a string/integer/null */ public DeleteTaskPushNotificationConfigRequest(String jsonrpc, Object id, DeleteTaskPushNotificationConfigParams params) { - super(jsonrpc, METHOD, id, params); + super(jsonrpc, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, id, params); } /** diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/DeleteTaskPushNotificationConfigResponse.java similarity index 86% rename from spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/DeleteTaskPushNotificationConfigResponse.java index 296e1dc28..d25674814 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/DeleteTaskPushNotificationConfigResponse.java @@ -1,4 +1,6 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.A2AError; /** * JSON-RPC response confirming deletion of a task's push notification configuration. @@ -6,12 +8,12 @@ * This response confirms successful deletion of the push notification configuration. * The result is void (no content) on success. *

    - * If an error occurs during deletion, the error field will contain a {@link JSONRPCError}. + * If an error occurs during deletion, the error field will contain a {@link A2AError}. * * @see DeleteTaskPushNotificationConfigRequest for the corresponding request * @see A2A Protocol Specification */ -public final class DeleteTaskPushNotificationConfigResponse extends JSONRPCResponse { +public final class DeleteTaskPushNotificationConfigResponse extends A2AResponse { /** * Creates a new DeleteTaskPushNotificationConfigResponse with full JSON-RPC parameters. @@ -21,7 +23,7 @@ public final class DeleteTaskPushNotificationConfigResponse extends JSONRPCRespo * @param result the result (always null/Void for this response type) * @param error the error if the request failed, null on success */ - public DeleteTaskPushNotificationConfigResponse(String jsonrpc, Object id, Void result,JSONRPCError error) { + public DeleteTaskPushNotificationConfigResponse(String jsonrpc, Object id, Void result,A2AError error) { super(jsonrpc, id, result, error, Void.class); } @@ -31,7 +33,7 @@ public DeleteTaskPushNotificationConfigResponse(String jsonrpc, Object id, Void * @param id the response identifier matching the request * @param error the error describing why the deletion failed */ - public DeleteTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { + public DeleteTaskPushNotificationConfigResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardRequest.java similarity index 92% rename from spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardRequest.java index 894c3510e..5fb2f00bb 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD; import java.util.UUID; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; + /** * JSON-RPC request to retrieve an agent's extended card with authenticated details. *

    @@ -28,9 +33,6 @@ */ public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name for getting extended agent card. */ - public static final String METHOD = "GetExtendedAgentCard"; - /** * Constructs request with full parameters. * @@ -38,7 +40,7 @@ public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONR * @param id the request ID */ public GetAuthenticatedExtendedCardRequest(String jsonrpc, Object id) { - super(jsonrpc, METHOD, id); + super(jsonrpc, GET_EXTENDED_AGENT_CARD_METHOD, id); } /** diff --git a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardResponse.java similarity index 81% rename from spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardResponse.java index ea82af58e..41e5c238b 100644 --- a/spec/src/main/java/io/a2a/spec/GetAuthenticatedExtendedCardResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardResponse.java @@ -1,4 +1,8 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.A2AError; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; /** * JSON-RPC response containing an agent's extended card with authenticated details. @@ -8,7 +12,7 @@ * security configurations, or other authenticated-only capabilities. *

    * If the agent doesn't support authenticated extended cards or authentication fails, - * the error field will contain a {@link JSONRPCError} such as + * the error field will contain a {@link A2AError} such as * {@link AuthenticatedExtendedCardNotConfiguredError}. * * @see GetAuthenticatedExtendedCardRequest for the corresponding request @@ -16,7 +20,7 @@ * @see AuthenticatedExtendedCardNotConfiguredError for the error when unsupported * @see A2A Protocol Specification */ -public final class GetAuthenticatedExtendedCardResponse extends JSONRPCResponse { +public final class GetAuthenticatedExtendedCardResponse extends A2AResponse { /** * Constructs response with full parameters. @@ -26,7 +30,7 @@ public final class GetAuthenticatedExtendedCardResponse extends JSONRPCResponse< * @param result the agent card result * @param error the error if any */ - public GetAuthenticatedExtendedCardResponse(String jsonrpc, Object id, AgentCard result, JSONRPCError error) { + public GetAuthenticatedExtendedCardResponse(String jsonrpc, Object id, AgentCard result, A2AError error) { super(jsonrpc, id, result, error, AgentCard.class); } @@ -36,7 +40,7 @@ public GetAuthenticatedExtendedCardResponse(String jsonrpc, Object id, AgentCard * @param id the request ID * @param error the error */ - public GetAuthenticatedExtendedCardResponse(Object id, JSONRPCError error) { + public GetAuthenticatedExtendedCardResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskPushNotificationConfigRequest.java similarity index 91% rename from spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskPushNotificationConfigRequest.java index 8023be203..e0f11be83 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskPushNotificationConfigRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; import java.util.UUID; +import io.a2a.spec.GetTaskPushNotificationConfigParams; +import io.a2a.spec.TaskPushNotificationConfig; + /** * JSON-RPC request to retrieve push notification configuration for a task. *

    @@ -17,9 +22,6 @@ */ public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "GetTaskPushNotificationConfig"; - /** * Constructs request with all parameters. * @@ -28,7 +30,7 @@ public final class GetTaskPushNotificationConfigRequest extends NonStreamingJSON * @param params the request parameters */ public GetTaskPushNotificationConfigRequest(String jsonrpc, Object id, GetTaskPushNotificationConfigParams params) { - super(jsonrpc, METHOD, id, params); + super(jsonrpc, GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, id, params); } /** diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskPushNotificationConfigResponse.java similarity index 82% rename from spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskPushNotificationConfigResponse.java index b26d8edf1..3aa71c8b6 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskPushNotificationConfigResponse.java @@ -1,4 +1,7 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.A2AError; +import io.a2a.spec.TaskPushNotificationConfig; /** * JSON-RPC response containing a task's push notification configuration. @@ -7,13 +10,13 @@ * showing the current push notification endpoint and authentication settings. *

    * If no configuration exists or an error occurs, the error field will contain a - * {@link JSONRPCError}. + * {@link A2AError}. * * @see GetTaskPushNotificationConfigRequest for the corresponding request * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -public final class GetTaskPushNotificationConfigResponse extends JSONRPCResponse { +public final class GetTaskPushNotificationConfigResponse extends A2AResponse { /** * Constructs response with full parameters. @@ -23,7 +26,7 @@ public final class GetTaskPushNotificationConfigResponse extends JSONRPCResponse * @param result the push notification config result * @param error the error if any */ - public GetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, JSONRPCError error) { + public GetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, A2AError error) { super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } @@ -33,7 +36,7 @@ public GetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPush * @param id the request ID * @param error the error */ - public GetTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { + public GetTaskPushNotificationConfigResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskRequest.java similarity index 93% rename from spec/src/main/java/io/a2a/spec/GetTaskRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskRequest.java index 12d39ff63..a08abe1be 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetTaskRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.GET_TASK_METHOD; import java.util.UUID; +import io.a2a.spec.Task; +import io.a2a.spec.TaskQueryParams; + /** * JSON-RPC request to retrieve task information by ID. *

    @@ -18,9 +23,6 @@ */ public final class GetTaskRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "GetTask"; - /** * Constructs request with all parameters. * @@ -29,7 +31,7 @@ public final class GetTaskRequest extends NonStreamingJSONRPCRequest * If the task is not found or an error occurs, the error field will be populated with - * a {@link JSONRPCError} (typically {@link TaskNotFoundError}) instead of a result. + * a {@link A2AError} (typically {@link TaskNotFoundError}) instead of a result. * * @see GetTaskRequest for the corresponding request * @see Task for the task structure * @see TaskNotFoundError for the common error case * @see A2A Protocol Specification */ -public final class GetTaskResponse extends JSONRPCResponse { +public final class GetTaskResponse extends A2AResponse { /** * Constructs response with full parameters. @@ -24,7 +28,7 @@ public final class GetTaskResponse extends JSONRPCResponse { * @param result the task result * @param error the error if any */ - public GetTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError error) { + public GetTaskResponse(String jsonrpc, Object id, Task result, A2AError error) { super(jsonrpc, id, result, error, Task.class); } @@ -34,7 +38,7 @@ public GetTaskResponse(String jsonrpc, Object id, Task result, JSONRPCError erro * @param id the request ID * @param error the error */ - public GetTaskResponse(Object id, JSONRPCError error) { + public GetTaskResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTaskPushNotificationConfigRequest.java similarity index 90% rename from spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTaskPushNotificationConfigRequest.java index 8f7f3c11f..ebd5c98e5 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTaskPushNotificationConfigRequest.java @@ -1,7 +1,13 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + import java.util.UUID; +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.TaskPushNotificationConfig; + +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; + /** * JSON-RPC request to list all push notification configurations for a task. *

    @@ -17,9 +23,6 @@ */ public final class ListTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "ListTaskPushNotificationConfig"; - /** * Constructs request with all parameters. * @@ -28,7 +31,7 @@ public final class ListTaskPushNotificationConfigRequest extends NonStreamingJSO * @param params the request parameters */ public ListTaskPushNotificationConfigRequest(String jsonrpc, Object id, ListTaskPushNotificationConfigParams params) { - super(jsonrpc, METHOD, id, params); + super(jsonrpc, LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, id, params); } /** diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTaskPushNotificationConfigResponse.java similarity index 78% rename from spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTaskPushNotificationConfigResponse.java index 098fabe1c..e5e24739f 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTaskPushNotificationConfigResponse.java @@ -1,4 +1,8 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.A2AError; +import io.a2a.spec.ListTaskPushNotificationConfigResult; +import io.a2a.spec.TaskPushNotificationConfig; /** * JSON-RPC response containing all push notification configurations for a task with pagination support. @@ -7,14 +11,14 @@ * {@link TaskPushNotificationConfig} entries configured for the requested task, * showing all active notification endpoints with optional pagination information. *

    - * If an error occurs, the error field will contain a {@link JSONRPCError}. + * If an error occurs, the error field will contain a {@link A2AError}. * * @see ListTaskPushNotificationConfigRequest for the corresponding request * @see ListTaskPushNotificationConfigResult for the result structure * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ -public final class ListTaskPushNotificationConfigResponse extends JSONRPCResponse { +public final class ListTaskPushNotificationConfigResponse extends A2AResponse { /** * Constructs response with all parameters. @@ -24,7 +28,7 @@ public final class ListTaskPushNotificationConfigResponse extends JSONRPCRespons * @param result the result containing list of push notification configurations and pagination info * @param error the error (if any) */ - public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, ListTaskPushNotificationConfigResult result, JSONRPCError error) { + public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, ListTaskPushNotificationConfigResult result, A2AError error) { super(jsonrpc, id, result, error, ListTaskPushNotificationConfigResult.class); } @@ -34,7 +38,7 @@ public ListTaskPushNotificationConfigResponse(String jsonrpc, Object id, ListTas * @param id the request ID * @param error the error */ - public ListTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { + public ListTaskPushNotificationConfigResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTasksRequest.java similarity index 92% rename from spec/src/main/java/io/a2a/spec/ListTasksRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTasksRequest.java index a84a9a361..aa8d656bc 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTasksRequest.java @@ -1,15 +1,16 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.LIST_TASK_METHOD; import java.util.UUID; +import io.a2a.spec.ListTasksParams; + /** * A list tasks request. */ public final class ListTasksRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "ListTasks"; - /** * Constructs request with all parameters. * @@ -18,7 +19,7 @@ public final class ListTasksRequest extends NonStreamingJSONRPCRequest { +public final class ListTasksResponse extends A2AResponse { /** * Constructs response with all parameters. @@ -13,7 +15,7 @@ public final class ListTasksResponse extends JSONRPCResponse { * @param result the result * @param error the error if any */ - public ListTasksResponse(String jsonrpc, Object id, ListTasksResult result, JSONRPCError error) { + public ListTasksResponse(String jsonrpc, Object id, ListTasksResult result, A2AError error) { super(jsonrpc, id, result, error, ListTasksResult.class); } @@ -33,7 +35,7 @@ public ListTasksResponse(Object id, ListTasksResult result) { * @param id the request ID * @param error the error */ - public ListTasksResponse(Object id, JSONRPCError error) { + public ListTasksResponse(Object id, A2AError error) { this(null, id, null, error); } } diff --git a/spec/src/main/java/io/a2a/spec/ListTasksResult.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTasksResult.java similarity index 98% rename from spec/src/main/java/io/a2a/spec/ListTasksResult.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTasksResult.java index 959c547a6..9cf2adc60 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksResult.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/ListTasksResult.java @@ -1,10 +1,11 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; +import java.util.List; + +import io.a2a.spec.Task; import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; -import java.util.List; - /** * Result of a list tasks request containing matching tasks and pagination information. * diff --git a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/NonStreamingJSONRPCRequest.java similarity index 90% rename from spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/NonStreamingJSONRPCRequest.java index 7264d7cdd..695b9969f 100644 --- a/spec/src/main/java/io/a2a/spec/NonStreamingJSONRPCRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/NonStreamingJSONRPCRequest.java @@ -1,11 +1,11 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; /** * Represents a non-streaming JSON-RPC request. * * @param the type of the request parameters */ -public abstract sealed class NonStreamingJSONRPCRequest extends JSONRPCRequest permits GetTaskRequest, +public abstract sealed class NonStreamingJSONRPCRequest extends A2ARequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest, GetAuthenticatedExtendedCardRequest, ListTasksRequest { diff --git a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendMessageRequest.java similarity index 91% rename from spec/src/main/java/io/a2a/spec/SendMessageRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendMessageRequest.java index 23b44dbaa..999361635 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendMessageRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; import java.util.UUID; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.Task; + /** * JSON-RPC request for the {@code SendMessage} method in the A2A Protocol. *

    @@ -20,7 +25,7 @@ * with the {@code SendStreamingMessage} method instead. *

    * This class includes a fluent {@link Builder} for convenient request construction. The method - * name is fixed as {@value #METHOD} and is set automatically by the constructor. + * name is fixed as "SendMessage" and is set automatically by the constructor. * * @see SendStreamingMessageRequest * @see MessageSendParams @@ -29,11 +34,6 @@ */ public final class SendMessageRequest extends NonStreamingJSONRPCRequest { - /** - * The JSON-RPC method name for sending a message: "SendMessage". - */ - public static final String METHOD = "SendMessage"; - /** * Constructs a SendMessageRequest with the specified JSON-RPC fields. *

    @@ -46,7 +46,7 @@ public final class SendMessageRequest extends NonStreamingJSONRPCRequest - * The method name is automatically set to {@value SendMessageRequest#METHOD} and cannot be changed. + * The method name is automatically set to "SendMessage" and cannot be changed. * If no ID is provided, a UUID will be auto-generated when {@link #build()} is called. *

    * Example usage: diff --git a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendMessageResponse.java similarity index 75% rename from spec/src/main/java/io/a2a/spec/SendMessageResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendMessageResponse.java index da01aa827..4f576dd01 100644 --- a/spec/src/main/java/io/a2a/spec/SendMessageResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendMessageResponse.java @@ -1,9 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.A2AError; +import io.a2a.spec.EventKind; /** * The response after receiving a send message request. */ -public final class SendMessageResponse extends JSONRPCResponse { +public final class SendMessageResponse extends A2AResponse { /** * Constructs response with all parameters. @@ -13,7 +16,7 @@ public final class SendMessageResponse extends JSONRPCResponse { * @param result the result * @param error the error if any */ - public SendMessageResponse(String jsonrpc, Object id, EventKind result, JSONRPCError error) { + public SendMessageResponse(String jsonrpc, Object id, EventKind result, A2AError error) { super(jsonrpc, id, result, error, EventKind.class); } @@ -33,7 +36,7 @@ public SendMessageResponse(Object id, EventKind result) { * @param id the request ID * @param error the error */ - public SendMessageResponse(Object id, JSONRPCError error) { + public SendMessageResponse(Object id, A2AError error) { this(null, id, null, error); } } diff --git a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendStreamingMessageRequest.java similarity index 93% rename from spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendStreamingMessageRequest.java index 9148133c3..704b70435 100644 --- a/spec/src/main/java/io/a2a/spec/SendStreamingMessageRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SendStreamingMessageRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; import java.util.UUID; +import io.a2a.spec.MessageSendParams; +import io.a2a.spec.StreamingEventKind; + /** * JSON-RPC request to initiate a new task with streaming event delivery. *

    @@ -21,9 +26,6 @@ */ public final class SendStreamingMessageRequest extends StreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "SendStreamingMessage"; - /** * Constructs request with all parameters. * @@ -32,7 +34,7 @@ public final class SendStreamingMessageRequest extends StreamingJSONRPCRequest * If an error occurs during request processing, the error field will be populated with - * a {@link JSONRPCError} instead of streaming events. + * a {@link A2AError} instead of streaming events. * * @see SendStreamingMessageRequest for the corresponding request * @see StreamingEventKind for the types of events that can be streamed * @see A2A Protocol Specification */ -public final class SendStreamingMessageResponse extends JSONRPCResponse { +public final class SendStreamingMessageResponse extends A2AResponse { /** * Constructs response with all parameters. @@ -27,7 +34,7 @@ public final class SendStreamingMessageResponse extends JSONRPCResponse @@ -18,9 +23,6 @@ */ public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "SetTaskPushNotificationConfig"; - /** * Constructs request with all parameters. * @@ -29,7 +31,7 @@ public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSON * @param params the request parameters */ public SetTaskPushNotificationConfigRequest(String jsonrpc, Object id, TaskPushNotificationConfig params) { - super(jsonrpc, METHOD, id, params); + super(jsonrpc, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, id, params); } /** diff --git a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigResponse.java similarity index 78% rename from spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigResponse.java index 752c0de03..0c39b9713 100644 --- a/spec/src/main/java/io/a2a/spec/SetTaskPushNotificationConfigResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigResponse.java @@ -1,4 +1,8 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.A2AError; +import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.TaskPushNotificationConfig; /** * JSON-RPC response confirming push notification configuration for a task. @@ -8,14 +12,14 @@ * as stored by the agent. *

    * If push notifications are not supported or an error occurs, the error field will contain - * a {@link JSONRPCError} (e.g., {@link PushNotificationNotSupportedError}). + * a {@link A2AError} (e.g., {@link PushNotificationNotSupportedError}). * * @see SetTaskPushNotificationConfigRequest for the corresponding request * @see TaskPushNotificationConfig for the configuration structure * @see PushNotificationNotSupportedError for the error when unsupported * @see A2A Protocol Specification */ -public final class SetTaskPushNotificationConfigResponse extends JSONRPCResponse { +public final class SetTaskPushNotificationConfigResponse extends A2AResponse { /** * Constructs response with all parameters. @@ -25,7 +29,7 @@ public final class SetTaskPushNotificationConfigResponse extends JSONRPCResponse * @param result the push notification configuration * @param error the error (if any) */ - public SetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, JSONRPCError error) { + public SetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPushNotificationConfig result, A2AError error) { super(jsonrpc, id, result, error, TaskPushNotificationConfig.class); } @@ -35,7 +39,7 @@ public SetTaskPushNotificationConfigResponse(String jsonrpc, Object id, TaskPush * @param id the request ID * @param error the error */ - public SetTaskPushNotificationConfigResponse(Object id, JSONRPCError error) { + public SetTaskPushNotificationConfigResponse(Object id, A2AError error) { this(null, id, null, error); } diff --git a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java similarity index 87% rename from spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java index 30033fc46..cbf33a72d 100644 --- a/spec/src/main/java/io/a2a/spec/StreamingJSONRPCRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java @@ -1,9 +1,11 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import io.a2a.spec.StreamingEventKind; /** * Base class for JSON-RPC requests that support streaming responses in the A2A Protocol. *

    - * This sealed class extends {@link JSONRPCRequest} to provide specialized support for + * This sealed class extends {@link A2ARequest} to provide specialized support for * A2A Protocol methods that return streaming responses. Streaming requests enable * server-sent events and real-time updates to be pushed to clients as they occur, * rather than waiting for a complete response. @@ -27,7 +29,7 @@ * @see StreamingEventKind * @see A2A Protocol Specification */ -public abstract sealed class StreamingJSONRPCRequest extends JSONRPCRequest permits SubscribeToTaskRequest, +public abstract sealed class StreamingJSONRPCRequest extends A2ARequest permits SubscribeToTaskRequest, SendStreamingMessageRequest { StreamingJSONRPCRequest(String jsonrpc, String method, Object id, T params) { diff --git a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java similarity index 92% rename from spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java index ba34bd4fd..27acb3a14 100644 --- a/spec/src/main/java/io/a2a/spec/SubscribeToTaskRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java @@ -1,7 +1,12 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.wrappers; + +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; import java.util.UUID; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.TaskIdParams; + /** * JSON-RPC request to resubscribe to an ongoing or completed task's event stream. *

    @@ -24,9 +29,6 @@ */ public final class SubscribeToTaskRequest extends StreamingJSONRPCRequest { - /** The JSON-RPC method name. */ - public static final String METHOD = "SubscribeToTask"; - /** * Constructs request with all parameters. * @@ -35,7 +37,7 @@ public final class SubscribeToTaskRequest extends StreamingJSONRPCRequest + * This package contains internal implementation classes that wrap A2A Protocol domain objects + * in JSON-RPC 2.0 message structures. These classes are used by JSON-RPC transport implementations + * and are not part of the public SDK API. + *

    + * Internal Use Only: Classes in this package are implementation details. + * The public API is in {@code io.a2a.spec}. + * + * @see io.a2a.spec + * @see JSON-RPC 2.0 Specification + */ +package io.a2a.jsonrpc.common.wrappers; diff --git a/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/A2AErrorSerializationTest.java similarity index 72% rename from spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java rename to jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/A2AErrorSerializationTest.java index 684f8a256..ff7ee5cc1 100644 --- a/spec/src/test/java/io/a2a/spec/JSONRPCErrorSerializationTest.java +++ b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/A2AErrorSerializationTest.java @@ -1,11 +1,5 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.json; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; @@ -17,19 +11,34 @@ import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import java.util.List; + +import io.a2a.spec.A2AError; +import io.a2a.spec.ContentTypeNotSupportedError; +import io.a2a.spec.InternalError; +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 org.junit.jupiter.api.Test; -public class JSONRPCErrorSerializationTest { +public class A2AErrorSerializationTest { @Test - public void shouldDeserializeToCorrectJSONRPCErrorSubclass() throws JsonProcessingException { + public void shouldDeserializeToCorrectA2AErrorSubclass() throws JsonProcessingException { String jsonTemplate = """ {"code": %s, "message": "error", "data": "anything"} """; - record ErrorCase(int code, Class clazz) {} + record ErrorCase(int code, Class clazz) {} List cases = List.of(new ErrorCase(JSON_PARSE_ERROR_CODE, JSONParseError.class), new ErrorCase(INVALID_REQUEST_ERROR_CODE, InvalidRequestError.class), @@ -42,12 +51,12 @@ record ErrorCase(int code, Class clazz) {} new ErrorCase(INVALID_AGENT_RESPONSE_ERROR_CODE, InvalidAgentResponseError.class), new ErrorCase(TASK_NOT_CANCELABLE_ERROR_CODE, TaskNotCancelableError.class), new ErrorCase(TASK_NOT_FOUND_ERROR_CODE, TaskNotFoundError.class), - new ErrorCase(Integer.MAX_VALUE, JSONRPCError.class) // Any unknown code will be treated as JSONRPCError + new ErrorCase(Integer.MAX_VALUE, A2AError.class) // Any unknown code will be treated as A2AError ); for (ErrorCase errorCase : cases) { String json = jsonTemplate.formatted(errorCase.code()); - JSONRPCError error = JsonUtil.fromJson(json, JSONRPCError.class); + A2AError error = JsonUtil.fromJson(json, A2AError.class); assertInstanceOf(errorCase.clazz(), error); assertEquals("error", error.getMessage()); assertEquals("anything", error.getData().toString()); diff --git a/spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java similarity index 96% rename from spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java rename to jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java index 7c15447e1..08eea2835 100644 --- a/spec/src/test/java/io/a2a/spec/StreamingEventKindSerializationTest.java +++ b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -8,8 +8,15 @@ import java.util.List; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.spec.Artifact; +import io.a2a.spec.Message; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; import org.junit.jupiter.api.Test; /** diff --git a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/TaskSerializationTest.java similarity index 98% rename from spec/src/test/java/io/a2a/spec/TaskSerializationTest.java rename to jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/TaskSerializationTest.java index cded0e204..940fe4531 100644 --- a/spec/src/test/java/io/a2a/spec/TaskSerializationTest.java +++ b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/TaskSerializationTest.java @@ -1,4 +1,4 @@ -package io.a2a.spec; +package io.a2a.jsonrpc.common.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -10,8 +10,17 @@ import java.util.List; import java.util.Map; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.spec.Artifact; +import io.a2a.spec.DataPart; +import io.a2a.spec.FilePart; +import io.a2a.spec.FileWithBytes; +import io.a2a.spec.FileWithUri; +import io.a2a.spec.Message; +import io.a2a.spec.Part; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TextPart; import org.junit.jupiter.api.Test; /** diff --git a/pom.xml b/pom.xml index 689e4f4ea..21ee72eb3 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,11 @@ a2a-java-sdk-http-client ${project.version} + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-spec @@ -484,6 +489,7 @@ extras/push-notification-config-store-database-jpa extras/queue-manager-replicated http-client + jsonrpc-common integrations/microprofile-config reference/common reference/grpc diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 0db7b0ca7..cbecf7470 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -31,6 +31,11 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-tests-server-common diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java index 9f0559cdb..a2c2b13c8 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/A2AExtensionsInterceptor.java @@ -1,14 +1,15 @@ package io.a2a.server.grpc.quarkus; import jakarta.enterprise.context.ApplicationScoped; + +import io.a2a.common.A2AHeaders; +import io.a2a.transport.grpc.context.GrpcContextKeys; import io.grpc.Context; import io.grpc.Contexts; import io.grpc.Metadata; import io.grpc.ServerCall; import io.grpc.ServerCallHandler; import io.grpc.ServerInterceptor; -import io.a2a.common.A2AHeaders; -import io.a2a.transport.grpc.context.GrpcContextKeys; /** * gRPC server interceptor that captures request metadata and context information, diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index 6e51eae85..edd346d50 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -1,10 +1,10 @@ package io.a2a.server.grpc.quarkus; +import java.util.concurrent.Executor; + import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import java.util.concurrent.Executor; - import io.a2a.server.PublicAgentCard; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.server.util.async.Internal; diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java index ef28ada75..4136eb70b 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/A2ATestResource.java @@ -18,13 +18,13 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.apps.common.TestUtilsBean; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.transport.grpc.handler.GrpcHandler; -import io.a2a.json.JsonUtil; @Path("/test") @ApplicationScoped diff --git a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java index b66f15b56..24320b94d 100644 --- a/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java +++ b/reference/grpc/src/test/java/io/a2a/server/grpc/quarkus/QuarkusA2AGrpcTest.java @@ -10,7 +10,6 @@ import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.quarkus.test.junit.QuarkusTest; - import org.junit.jupiter.api.AfterAll; @QuarkusTest diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 11fa35377..97983421e 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -30,6 +30,11 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-tests-server-common diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 4e4c6a294..699b003e0 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -6,7 +6,6 @@ import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.MediaType.SERVER_SENT_EVENTS; -import com.google.gson.JsonSyntaxException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,47 +19,48 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; -import io.a2a.json.JsonProcessingException; +import com.google.gson.JsonSyntaxException; import io.a2a.common.A2AHeaders; import io.a2a.grpc.utils.JSONRPCUtils; -import io.a2a.json.JsonMappingException; +import io.a2a.jsonrpc.common.json.IdJsonMappingException; +import io.a2a.jsonrpc.common.json.InvalidParamsJsonMappingException; +import io.a2a.jsonrpc.common.json.JsonMappingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.MethodNotFoundJsonMappingException; +import io.a2a.jsonrpc.common.wrappers.A2AErrorResponse; +import io.a2a.jsonrpc.common.wrappers.A2ARequest; +import io.a2a.jsonrpc.common.wrappers.A2AResponse; +import io.a2a.jsonrpc.common.wrappers.CancelTaskRequest; +import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; +import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; +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.GetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksRequest; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; +import io.a2a.jsonrpc.common.wrappers.NonStreamingJSONRPCRequest; +import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; import io.a2a.server.extensions.A2AExtensions; import io.a2a.server.util.async.Internal; +import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.IdJsonMappingException; import io.a2a.spec.InternalError; -import io.a2a.spec.InvalidParamsJsonMappingException; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.JSONRPCErrorResponse; -import io.a2a.spec.JSONRPCRequest; -import io.a2a.spec.JSONRPCResponse; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.ListTasksResponse; -import io.a2a.spec.MethodNotFoundJsonMappingException; -import io.a2a.spec.NonStreamingJSONRPCRequest; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.UnsupportedOperationError; import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import io.quarkus.security.Authenticated; @@ -97,11 +97,11 @@ public class A2AServerRoutes { public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { boolean streaming = false; ServerCallContext context = createCallContext(rc); - JSONRPCResponse nonStreamingResponse = null; - Multi> streamingResponse = null; - JSONRPCErrorResponse error = null; + A2AResponse nonStreamingResponse = null; + Multi> streamingResponse = null; + A2AErrorResponse error = null; try { - JSONRPCRequest request = JSONRPCUtils.parseRequestBody(body); + A2ARequest request = JSONRPCUtils.parseRequestBody(body); context.getState().put(METHOD_NAME_KEY, request.getMethod()); if (request instanceof NonStreamingJSONRPCRequest nonStreamingRequest) { nonStreamingResponse = processNonStreamingRequest(nonStreamingRequest, context); @@ -109,23 +109,23 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { streaming = true; streamingResponse = processStreamingRequest(request, context); } - } catch (JSONRPCError e) { - error = new JSONRPCErrorResponse(e); + } catch (A2AError e) { + error = new A2AErrorResponse(e); } catch (InvalidParamsJsonMappingException e) { - error = new JSONRPCErrorResponse(e.getId(), new io.a2a.spec.InvalidParamsError(null, e.getMessage(), null)); + error = new A2AErrorResponse(e.getId(), new io.a2a.spec.InvalidParamsError(null, e.getMessage(), null)); } catch (MethodNotFoundJsonMappingException e) { - error = new JSONRPCErrorResponse(e.getId(), new io.a2a.spec.MethodNotFoundError(null, e.getMessage(), null)); + error = new A2AErrorResponse(e.getId(), new io.a2a.spec.MethodNotFoundError(null, e.getMessage(), null)); } catch (IdJsonMappingException e) { - error = new JSONRPCErrorResponse(e.getId(), new io.a2a.spec.InvalidRequestError(null, e.getMessage(), null)); + error = new A2AErrorResponse(e.getId(), new io.a2a.spec.InvalidRequestError(null, e.getMessage(), null)); } catch (JsonMappingException e) { // General JsonMappingException - treat as InvalidRequest - error = new JSONRPCErrorResponse(new io.a2a.spec.InvalidRequestError(null, e.getMessage(), null)); + error = new A2AErrorResponse(new io.a2a.spec.InvalidRequestError(null, e.getMessage(), null)); } catch (JsonSyntaxException e) { - error = new JSONRPCErrorResponse(new JSONParseError(e.getMessage())); + error = new A2AErrorResponse(new JSONParseError(e.getMessage())); } catch (JsonProcessingException e) { - error = new JSONRPCErrorResponse(new JSONParseError(e.getMessage())); + error = new A2AErrorResponse(new JSONParseError(e.getMessage())); } catch (Throwable t) { - error = new JSONRPCErrorResponse(new InternalError(t.getMessage())); + error = new A2AErrorResponse(new InternalError(t.getMessage())); } finally { if (error != null) { rc.response() @@ -133,7 +133,7 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { .putHeader(CONTENT_TYPE, APPLICATION_JSON) .end(serializeResponse(error)); } else if (streaming) { - final Multi> finalStreamingResponse = streamingResponse; + final Multi> finalStreamingResponse = streamingResponse; executor.execute(() -> { MultiSseSupport.subscribeObject( finalStreamingResponse.map(i -> (Object) i), rc); @@ -160,7 +160,7 @@ public AgentCard getAgentCard() { return jsonRpcHandler.getAgentCard(); } - private JSONRPCResponse processNonStreamingRequest(NonStreamingJSONRPCRequest request, ServerCallContext context) { + private A2AResponse processNonStreamingRequest(NonStreamingJSONRPCRequest request, ServerCallContext context) { if (request instanceof GetTaskRequest req) { return jsonRpcHandler.onGetTask(req, context); } @@ -191,9 +191,9 @@ private JSONRPCResponse processNonStreamingRequest(NonStreamingJSONRPCRequest return generateErrorResponse(request, new UnsupportedOperationError()); } - private Multi> processStreamingRequest( - JSONRPCRequest request, ServerCallContext context) { - Flow.Publisher> publisher; + private Multi> processStreamingRequest( + A2ARequest request, ServerCallContext context) { + Flow.Publisher> publisher; if (request instanceof SendStreamingMessageRequest req) { publisher = jsonRpcHandler.onMessageSendStream(req, context); } else if (request instanceof SubscribeToTaskRequest req) { @@ -204,8 +204,8 @@ private Multi> processStreamingRequest( return Multi.createFrom().publisher(publisher); } - private JSONRPCResponse generateErrorResponse(JSONRPCRequest request, JSONRPCError error) { - return new JSONRPCErrorResponse(request.getId(), error); + private A2AResponse generateErrorResponse(A2ARequest request, A2AError error) { + return new A2AErrorResponse(request.getId(), error); } static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { @@ -252,9 +252,9 @@ public String getUsername() { } } - private static String serializeResponse(JSONRPCResponse response) { + private static String serializeResponse(A2AResponse response) { // For error responses, use Jackson serialization (errors are standardized) - if (response instanceof JSONRPCErrorResponse error) { + if (response instanceof A2AErrorResponse error) { return JSONRPCUtils.toJsonRPCErrorResponse(error.getId(), error.getError()); } if (response.getError() != null) { @@ -265,7 +265,7 @@ private static String serializeResponse(JSONRPCResponse response) { return JSONRPCUtils.toJsonRPCResultResponse(response.getId(), protoMessage); } - private static com.google.protobuf.MessageOrBuilder convertToProto(JSONRPCResponse response) { + private static com.google.protobuf.MessageOrBuilder convertToProto(A2AResponse response) { if (response instanceof GetTaskResponse r) { return io.a2a.grpc.utils.ProtoUtils.ToProto.task(r.getResult()); } else if (response instanceof CancelTaskResponse r) { @@ -366,10 +366,10 @@ public Buffer apply(Object o) { ReactiveRoutes.ServerSentEvent ev = (ReactiveRoutes.ServerSentEvent) o; long id = ev.id() != -1 ? ev.id() : count.getAndIncrement(); String e = ev.event() == null ? "" : "event: " + ev.event() + "\n"; - String data = serializeResponse((JSONRPCResponse) ev.data()); + String data = serializeResponse((A2AResponse) ev.data()); return Buffer.buffer(e + "data: " + data + "\nid: " + id + "\n\n"); } - String data = serializeResponse((JSONRPCResponse) o); + String data = serializeResponse((A2AResponse) o); return Buffer.buffer("data: " + data + "\nid: " + count.getAndIncrement() + "\n\n"); } }), rc); diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java index 5c7582da5..17edf650f 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -1,5 +1,8 @@ package io.a2a.server.apps.quarkus; +import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD; +import static io.a2a.spec.A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; import static java.util.Collections.singletonList; @@ -17,32 +20,32 @@ import jakarta.enterprise.inject.Instance; +import io.a2a.jsonrpc.common.wrappers.CancelTaskRequest; +import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; +import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; +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.GetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.AuthenticationInfo; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskState; @@ -57,6 +60,14 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_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_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; + /** * Unit test for JSON-RPC A2AServerRoutes that verifies the method names are properly set * in the ServerCallContext for all request types. @@ -154,7 +165,7 @@ public void testSendMessage_MethodNameSetInContext() { verify(mockJsonRpcHandler).onMessageSend(any(SendMessageRequest.class), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SendMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SEND_MESSAGE_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -201,7 +212,7 @@ public void testSendStreamingMessage_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SendStreamingMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SEND_STREAMING_MESSAGE_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -238,7 +249,7 @@ public void testGetTask_MethodNameSetInContext() { verify(mockJsonRpcHandler).onGetTask(any(GetTaskRequest.class), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(GetTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(GET_TASK_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -274,7 +285,7 @@ public void testCancelTask_MethodNameSetInContext() { verify(mockJsonRpcHandler).onCancelTask(any(CancelTaskRequest.class), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(CancelTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(CANCEL_TASK_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -306,7 +317,7 @@ public void testTaskResubscription_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SubscribeToTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SUBSCRIBE_TO_TASK_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -357,7 +368,7 @@ public void testSetTaskPushNotificationConfig_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -397,7 +408,7 @@ public void testGetTaskPushNotificationConfig_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(GetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -437,7 +448,7 @@ public void testListTaskPushNotificationConfig_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(ListTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -469,13 +480,13 @@ public void testDeleteTaskPushNotificationConfig_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(DeleteTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test public void testGetAuthenticatedExtendedCard_MethodNameSetInContext() { // Arrange - String jsonRpcRequest = "{\"jsonrpc\":\"2.0\",\"id\":\"5\",\"method\":\"" + GetAuthenticatedExtendedCardRequest.METHOD + String jsonRpcRequest = "{\"jsonrpc\":\"2.0\",\"id\":\"5\",\"method\":\"" + GET_EXTENDED_AGENT_CARD_METHOD + "\",\"id\":1}"; when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); @@ -506,7 +517,7 @@ public void testGetAuthenticatedExtendedCard_MethodNameSetInContext() { any(GetAuthenticatedExtendedCardRequest.class), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(GetAuthenticatedExtendedCardRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(GET_EXTENDED_AGENT_CARD_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } /** diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java index d3bfca712..12eae5a4d 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2ATestRoutes.java @@ -10,12 +10,12 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.apps.common.TestUtilsBean; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.json.JsonUtil; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index b1ef2b03c..ec7e0dc1b 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -30,6 +30,11 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-client-transport-rest diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index f3b5f076c..d17815b8b 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -1,11 +1,17 @@ package io.a2a.server.rest.quarkus; +import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; import static io.a2a.transport.rest.context.RestContextKeys.HEADERS_KEY; import static io.a2a.transport.rest.context.RestContextKeys.METHOD_NAME_KEY; import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.MediaType.SERVER_SENT_EVENTS; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicLong; @@ -20,14 +26,16 @@ import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; +import io.a2a.server.extensions.A2AExtensions; import io.a2a.server.util.async.Internal; +import io.a2a.spec.A2AError; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.MethodNotFoundError; import io.a2a.transport.rest.handler.RestHandler; import io.a2a.transport.rest.handler.RestHandler.HTTPRestResponse; import io.a2a.transport.rest.handler.RestHandler.HTTPRestStreamingResponse; +import io.a2a.util.Utils; import io.quarkus.security.Authenticated; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.ReactiveRoutes; @@ -39,25 +47,17 @@ import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import io.a2a.server.extensions.A2AExtensions; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SubscribeToTaskRequest; -import io.a2a.util.Utils; import org.jspecify.annotations.Nullable; +import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_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; +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; + @Singleton @Authenticated public class A2AServerRoutes { @@ -83,7 +83,7 @@ public class A2AServerRoutes { @Route(regex = "^\\/(?[^\\/]*\\/?)message:send$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void sendMessage(@Body String body, RoutingContext rc) { - ServerCallContext context = createCallContext(rc, SendMessageRequest.METHOD); + ServerCallContext context = createCallContext(rc, SEND_MESSAGE_METHOD); HTTPRestResponse response = null; try { response = jsonRestHandler.sendMessage(body, extractTenant(rc), context); @@ -96,7 +96,7 @@ public void sendMessage(@Body String body, RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)message:stream$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void sendMessageStreaming(@Body String body, RoutingContext rc) { - ServerCallContext context = createCallContext(rc, SendStreamingMessageRequest.METHOD); + ServerCallContext context = createCallContext(rc, SEND_STREAMING_MESSAGE_METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { @@ -121,7 +121,7 @@ public void sendMessageStreaming(@Body String body, RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\??", order = 0, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void listTasks(RoutingContext rc) { - ServerCallContext context = createCallContext(rc, ListTasksRequest.METHOD); + ServerCallContext context = createCallContext(rc, LIST_TASK_METHOD); HTTPRestResponse response = null; try { // Extract query parameters @@ -168,7 +168,7 @@ public void listTasks(RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^:^/]+)$", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void getTask(RoutingContext rc) { String taskId = rc.pathParam("taskId"); - ServerCallContext context = createCallContext(rc, GetTaskRequest.METHOD); + ServerCallContext context = createCallContext(rc, GET_TASK_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -192,7 +192,7 @@ public void getTask(RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+):cancel$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void cancelTask(RoutingContext rc) { String taskId = rc.pathParam("taskId"); - ServerCallContext context = createCallContext(rc, CancelTaskRequest.METHOD); + ServerCallContext context = createCallContext(rc, CANCEL_TASK_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -201,7 +201,7 @@ public void cancelTask(RoutingContext rc) { response = jsonRestHandler.cancelTask(taskId, extractTenant(rc), context); } } catch (Throwable t) { - if (t instanceof JSONRPCError error) { + if (t instanceof A2AError error) { response = jsonRestHandler.createErrorResponse(error); } else { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); @@ -225,7 +225,7 @@ private void sendResponse(RoutingContext rc, @Nullable HTTPRestResponse response @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+):subscribe$", order = 1, methods = {Route.HttpMethod.POST}, type = Route.HandlerType.BLOCKING) public void subscribeToTask(RoutingContext rc) { String taskId = rc.pathParam("taskId"); - ServerCallContext context = createCallContext(rc, SubscribeToTaskRequest.METHOD); + ServerCallContext context = createCallContext(rc, SUBSCRIBE_TO_TASK_METHOD); HTTPRestStreamingResponse streamingResponse = null; HTTPRestResponse error = null; try { @@ -255,7 +255,7 @@ public void subscribeToTask(RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) public void setTaskPushNotificationConfiguration(@Body String body, RoutingContext rc) { String taskId = rc.pathParam("taskId"); - ServerCallContext context = createCallContext(rc, SetTaskPushNotificationConfigRequest.METHOD); + ServerCallContext context = createCallContext(rc, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -274,7 +274,7 @@ public void setTaskPushNotificationConfiguration(@Body String body, RoutingConte public void getTaskPushNotificationConfiguration(RoutingContext rc) { String taskId = rc.pathParam("taskId"); String configId = rc.pathParam("configId"); - ServerCallContext context = createCallContext(rc, GetTaskPushNotificationConfigRequest.METHOD); + ServerCallContext context = createCallContext(rc, GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -292,7 +292,7 @@ public void getTaskPushNotificationConfiguration(RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs\\/$", order = 1, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void getTaskPushNotificationConfigurationWithoutId(RoutingContext rc) { String taskId = rc.pathParam("taskId"); - ServerCallContext context = createCallContext(rc, GetTaskPushNotificationConfigRequest.METHOD); + ServerCallContext context = createCallContext(rc, GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -311,7 +311,7 @@ public void getTaskPushNotificationConfigurationWithoutId(RoutingContext rc) { @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs", order = 3, methods = {Route.HttpMethod.GET}, type = Route.HandlerType.BLOCKING) public void listTaskPushNotificationConfigurations(RoutingContext rc) { String taskId = rc.pathParam("taskId"); - ServerCallContext context = createCallContext(rc, ListTaskPushNotificationConfigRequest.METHOD); + ServerCallContext context = createCallContext(rc, LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { @@ -340,7 +340,7 @@ public void listTaskPushNotificationConfigurations(RoutingContext rc) { public void deleteTaskPushNotificationConfiguration(RoutingContext rc) { String taskId = rc.pathParam("taskId"); String configId = rc.pathParam("configId"); - ServerCallContext context = createCallContext(rc, DeleteTaskPushNotificationConfigRequest.METHOD); + ServerCallContext context = createCallContext(rc, DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; try { if (taskId == null || taskId.isEmpty()) { diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java index 6ff3792c2..0f07b421a 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -1,5 +1,14 @@ package io.a2a.server.rest.quarkus; +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_TASK_METHOD; +import static io.a2a.spec.A2AMethods.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; import static io.a2a.transport.rest.context.RestContextKeys.METHOD_NAME_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -16,15 +25,6 @@ import jakarta.enterprise.inject.Instance; import io.a2a.server.ServerCallContext; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.transport.rest.handler.RestHandler; import io.a2a.transport.rest.handler.RestHandler.HTTPRestResponse; import io.vertx.core.Future; @@ -105,7 +105,7 @@ public void testSendMessage_MethodNameSetInContext() { verify(mockRestHandler).sendMessage(eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SendMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SEND_MESSAGE_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -127,7 +127,7 @@ public void testSendMessageStreaming_MethodNameSetInContext() { verify(mockRestHandler).sendStreamingMessage(eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SendStreamingMessageRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SEND_STREAMING_MESSAGE_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -149,7 +149,7 @@ public void testGetTask_MethodNameSetInContext() { verify(mockRestHandler).getTask(eq("task123"), any(), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(GetTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(GET_TASK_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -171,7 +171,7 @@ public void testCancelTask_MethodNameSetInContext() { verify(mockRestHandler).cancelTask(eq("task123"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(CancelTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(CANCEL_TASK_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -194,7 +194,7 @@ public void testResubscribeTask_MethodNameSetInContext() { verify(mockRestHandler).subscribeToTask(eq("task123"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SubscribeToTaskRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SUBSCRIBE_TO_TASK_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -217,7 +217,7 @@ public void testSetTaskPushNotificationConfiguration_MethodNameSetInContext() { verify(mockRestHandler).setTaskPushNotificationConfiguration(eq("task123"), eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(SetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -242,7 +242,7 @@ public void testGetTaskPushNotificationConfiguration_MethodNameSetInContext() { contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(GetTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -265,7 +265,7 @@ public void testListTaskPushNotificationConfigurations_MethodNameSetInContext() verify(mockRestHandler).listTaskPushNotificationConfigurations(eq("task123"), anyInt(), anyString(), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(ListTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } @Test @@ -290,7 +290,7 @@ public void testDeleteTaskPushNotificationConfiguration_MethodNameSetInContext() contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); - assertEquals(DeleteTaskPushNotificationConfigRequest.METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); + assertEquals(DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } /** diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java index ba5bd1cfd..57d6965a4 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2ATestRoutes.java @@ -1,7 +1,5 @@ package io.a2a.server.rest.quarkus; -import io.a2a.server.rest.quarkus.A2AServerRoutes; - import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; @@ -12,12 +10,12 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.apps.common.TestUtilsBean; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; -import io.a2a.json.JsonUtil; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; diff --git a/server-common/pom.xml b/server-common/pom.xml index 61aca7c9d..74cb6c7a1 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -21,6 +21,11 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-spec-grpc diff --git a/server-common/src/main/java/io/a2a/server/JSONRPCException.java b/server-common/src/main/java/io/a2a/server/JSONRPCException.java index cf7416b91..3911355d6 100644 --- a/server-common/src/main/java/io/a2a/server/JSONRPCException.java +++ b/server-common/src/main/java/io/a2a/server/JSONRPCException.java @@ -1,23 +1,23 @@ package io.a2a.server; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; /** * Exception wrapper for JSON-RPC protocol errors. *

    - * This exception encapsulates a {@link JSONRPCError} for handling + * This exception encapsulates a {@link A2AError} for handling * protocol-level errors during JSON-RPC request processing. *

    */ public class JSONRPCException extends Exception{ - private final JSONRPCError error; + private final A2AError error; /** * Creates a JSONRPCException wrapping the specified error. * * @param error the JSON-RPC error */ - public JSONRPCException(JSONRPCError error) { + public JSONRPCException(A2AError error) { this.error = error; } @@ -26,7 +26,7 @@ public JSONRPCException(JSONRPCError error) { * * @return the JSON-RPC error */ - public JSONRPCError getError() { + public A2AError getError() { return error; } } diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java b/server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java index f753fed54..caa0c5cbf 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/AgentExecutor.java @@ -1,10 +1,10 @@ package io.a2a.server.agentexecution; import io.a2a.server.events.EventQueue; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; public interface AgentExecutor { - void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError; + void execute(RequestContext context, EventQueue eventQueue) throws A2AError; - void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError; + void cancel(RequestContext context, EventQueue eventQueue) throws A2AError; } diff --git a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java index 0aa775649..0613d334c 100644 --- a/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java +++ b/server-common/src/main/java/io/a2a/server/agentexecution/RequestContext.java @@ -6,8 +6,6 @@ import java.util.UUID; import java.util.stream.Collectors; -import org.jspecify.annotations.Nullable; - import io.a2a.server.ServerCallContext; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.Message; @@ -16,6 +14,7 @@ import io.a2a.spec.Part; import io.a2a.spec.Task; import io.a2a.spec.TextPart; +import org.jspecify.annotations.Nullable; public class RequestContext { diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index d4fd7b682..7f1cbd4cc 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -2,8 +2,6 @@ import java.util.concurrent.Flow; -import org.jspecify.annotations.Nullable; - import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.Message; @@ -12,6 +10,7 @@ import mutiny.zero.BackpressureStrategy; import mutiny.zero.TubeConfiguration; import mutiny.zero.ZeroPublisher; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 7ce8ad3aa..a08f63084 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -9,10 +9,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.jspecify.annotations.Nullable; - import io.a2a.server.tasks.TaskStateProvider; import io.a2a.spec.Event; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index 2ab9070d3..19179f50d 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -6,9 +6,8 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.jspecify.annotations.Nullable; - import io.a2a.server.tasks.TaskStateProvider; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java index 39f65a994..a83128a1a 100644 --- a/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java +++ b/server-common/src/main/java/io/a2a/server/extensions/A2AExtensions.java @@ -4,10 +4,9 @@ import java.util.List; import java.util.Set; -import org.jspecify.annotations.Nullable; - import io.a2a.spec.AgentCard; import io.a2a.spec.AgentExtension; +import org.jspecify.annotations.Nullable; public class A2AExtensions { diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 39fb01104..f3cb6f02e 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -3,7 +3,7 @@ import static io.a2a.server.util.async.AsyncUtils.convertingProcessor; import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import static io.a2a.server.util.async.AsyncUtils.processor; -import static java.util.concurrent.TimeUnit.*; +import static java.util.concurrent.TimeUnit.SECONDS; import java.time.Instant; import java.util.HashMap; @@ -21,16 +21,16 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.jspecify.annotations.Nullable; -import org.jspecify.annotations.NonNull; - +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.server.ServerCallContext; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.agentexecution.SimpleRequestContextBuilder; +import io.a2a.server.config.A2AConfigProvider; import io.a2a.server.events.EnhancedRunnable; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; @@ -43,17 +43,16 @@ import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; import io.a2a.server.util.async.Internal; +import io.a2a.spec.A2AError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.Event; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; @@ -66,8 +65,8 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.server.config.A2AConfigProvider; -import jakarta.annotation.PostConstruct; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -155,7 +154,7 @@ public static DefaultRequestHandler create(AgentExecutor agentExecutor, TaskStor } @Override - public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws JSONRPCError { + public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws A2AError { LOGGER.debug("onGetTask {}", params.id()); Task task = taskStore.get(params.id()); if (task == null) { @@ -188,7 +187,7 @@ private static Task limitTaskHistory(Task task, @Nullable Integer historyLength) } @Override - public ListTasksResult onListTasks(ListTasksParams params, ServerCallContext context) throws JSONRPCError { + public ListTasksResult onListTasks(ListTasksParams params, ServerCallContext context) throws A2AError { LOGGER.debug("onListTasks with contextId={}, status={}, pageSize={}, pageToken={}, lastUpdatedAfter={}", params.contextId(), params.status(), params.pageSize(), params.pageToken(), params.lastUpdatedAfter()); @@ -210,7 +209,7 @@ public ListTasksResult onListTasks(ListTasksParams params, ServerCallContext con } @Override - public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws JSONRPCError { + public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws A2AError { Task task = taskStore.get(params.id()); if (task == null) { throw new TaskNotFoundError(); @@ -262,7 +261,7 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws } @Override - public EventKind onMessageSend(MessageSendParams params, ServerCallContext context) throws JSONRPCError { + public EventKind onMessageSend(MessageSendParams params, ServerCallContext context) throws A2AError { LOGGER.debug("onMessageSend - task: {}; context {}", params.message().taskId(), params.message().contextId()); MessageSendSetup mss = initMessageSend(params, context); @@ -398,7 +397,7 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte @Override public Flow.Publisher onMessageSendStream( - MessageSendParams params, ServerCallContext context) throws JSONRPCError { + MessageSendParams params, ServerCallContext context) throws A2AError { LOGGER.debug("onMessageSendStream START - task: {}; context: {}; runningAgents: {}; backgroundTasks: {}", params.message().taskId(), params.message().contextId(), runningAgents.size(), backgroundTasks.size()); MessageSendSetup mss = initMessageSend(params, context); @@ -560,7 +559,7 @@ private void startBackgroundConsumption() { @Override public TaskPushNotificationConfig onSetTaskPushNotificationConfig( - TaskPushNotificationConfig params, ServerCallContext context) throws JSONRPCError { + TaskPushNotificationConfig params, ServerCallContext context) throws A2AError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } @@ -575,7 +574,7 @@ public TaskPushNotificationConfig onSetTaskPushNotificationConfig( @Override public TaskPushNotificationConfig onGetTaskPushNotificationConfig( - GetTaskPushNotificationConfigParams params, ServerCallContext context) throws JSONRPCError { + GetTaskPushNotificationConfigParams params, ServerCallContext context) throws A2AError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } @@ -607,7 +606,7 @@ private PushNotificationConfig getPushNotificationConfig(ListTaskPushNotificatio @Override public Flow.Publisher onResubscribeToTask( - TaskIdParams params, ServerCallContext context) throws JSONRPCError { + TaskIdParams params, ServerCallContext context) throws A2AError { LOGGER.debug("onResubscribeToTask - taskId: {}", params.id()); Task task = taskStore.get(params.id()); if (task == null) { @@ -638,7 +637,7 @@ public Flow.Publisher onResubscribeToTask( @Override public ListTaskPushNotificationConfigResult onListTaskPushNotificationConfig( - ListTaskPushNotificationConfigParams params, ServerCallContext context) throws JSONRPCError { + ListTaskPushNotificationConfigParams params, ServerCallContext context) throws A2AError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index ff7384e75..6d99fe399 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -1,17 +1,16 @@ package io.a2a.server.requesthandlers; -import java.util.List; import java.util.concurrent.Flow; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.server.ServerCallContext; +import io.a2a.spec.A2AError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.MessageSendParams; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; @@ -22,41 +21,41 @@ public interface RequestHandler { Task onGetTask( TaskQueryParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; ListTasksResult onListTasks( ListTasksParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; Task onCancelTask( TaskIdParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; EventKind onMessageSend( MessageSendParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; Flow.Publisher onMessageSendStream( MessageSendParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; TaskPushNotificationConfig onSetTaskPushNotificationConfig( TaskPushNotificationConfig params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; TaskPushNotificationConfig onGetTaskPushNotificationConfig( GetTaskPushNotificationConfigParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; Flow.Publisher onResubscribeToTask( TaskIdParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; ListTaskPushNotificationConfigResult onListTaskPushNotificationConfig( ListTaskPushNotificationConfigParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; void onDeleteTaskPushNotificationConfig( DeleteTaskPushNotificationConfigParams params, - ServerCallContext context) throws JSONRPCError; + ServerCallContext context) throws A2AError; } diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index c6c887577..35a248373 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -4,23 +4,21 @@ import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE; import static io.a2a.common.A2AHeaders.X_A2A_NOTIFICATION_TOKEN; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - import java.io.IOException; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java index 8dfeeebdb..093ff910d 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStore.java @@ -1,10 +1,5 @@ package io.a2a.server.tasks; -import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigResult; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -12,6 +7,11 @@ import java.util.List; import java.util.Map; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.TaskPushNotificationConfig; diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 669fee0a8..d2d9362ca 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -7,9 +7,9 @@ import jakarta.enterprise.context.ApplicationScoped; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.Task; import org.jspecify.annotations.Nullable; diff --git a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java index 16a658f0e..222b06055 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/PushNotificationConfigStore.java @@ -2,8 +2,6 @@ import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; - - import io.a2a.spec.PushNotificationConfig; /** diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index 0fd62113f..95684e199 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -11,19 +11,18 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import org.jspecify.annotations.Nullable; - import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueueItem; +import io.a2a.spec.A2AError; import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.EventKind; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.util.Utils; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,7 +65,7 @@ public Flow.Publisher consumeAndEmit(EventConsumer consumer) { }); } - public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { + public EventKind consumeAll(EventConsumer consumer) throws A2AError { AtomicReference returnedEvent = new AtomicReference<>(); Flow.Publisher allItems = consumer.consumeAll(); AtomicReference error = new AtomicReference<>(); @@ -111,7 +110,7 @@ public EventKind consumeAll(EventConsumer consumer) throws JSONRPCError { return task; } - public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking) throws JSONRPCError { + public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking) throws A2AError { Flow.Publisher allItems = consumer.consumeAll(); AtomicReference message = new AtomicReference<>(); AtomicBoolean interrupted = new AtomicBoolean(false); diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index 55b24e409..fd3696a60 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -5,12 +5,11 @@ import static io.a2a.util.Utils.appendArtifactToTask; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.jspecify.annotations.Nullable; - import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.InvalidParamsError; @@ -19,7 +18,7 @@ import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; -import java.util.Collections; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java index 7d15e032a..00f9e56a2 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java @@ -1,10 +1,9 @@ package io.a2a.server.tasks; -import org.jspecify.annotations.Nullable; - +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Task; +import org.jspecify.annotations.Nullable; /** * Storage interface for managing task persistence. diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index c4044603d..022cde56b 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -5,8 +5,6 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import org.jspecify.annotations.Nullable; - import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.spec.Artifact; @@ -16,6 +14,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; +import org.jspecify.annotations.Nullable; public class TaskUpdater { private final EventQueue eventQueue; diff --git a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java index ada7a53fa..633b38ced 100644 --- a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java +++ b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java @@ -4,12 +4,11 @@ import java.util.Map; import java.util.UUID; -import org.jspecify.annotations.Nullable; - import io.a2a.spec.Artifact; import io.a2a.spec.DataPart; import io.a2a.spec.Part; import io.a2a.spec.TextPart; +import org.jspecify.annotations.Nullable; /** * Utility functions for creating A2A Artifact objects. diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index e4b135999..e26dd55fb 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -14,9 +14,8 @@ import jakarta.enterprise.inject.Produces; import jakarta.inject.Inject; -import org.jspecify.annotations.Nullable; - import io.a2a.server.config.A2AConfigProvider; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java index 8d39d2e8a..4d1d4f9c4 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncUtils.java @@ -7,14 +7,13 @@ import java.util.function.Consumer; import java.util.function.Function; -import org.jspecify.annotations.Nullable; - import io.a2a.util.Assert; import mutiny.zero.BackpressureStrategy; import mutiny.zero.Tube; import mutiny.zero.TubeConfiguration; import mutiny.zero.ZeroPublisher; import mutiny.zero.operators.Transform; +import org.jspecify.annotations.Nullable; public class AsyncUtils { diff --git a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java index 3ba6c4522..1c913ab7f 100644 --- a/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java +++ b/server-common/src/test/java/io/a2a/server/AgentCardValidatorTest.java @@ -1,10 +1,9 @@ package io.a2a.server; -import io.a2a.spec.AgentCapabilities; -import io.a2a.spec.AgentCard; -import io.a2a.spec.AgentInterface; -import io.a2a.spec.TransportProtocol; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import java.util.List; @@ -13,10 +12,11 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; +import io.a2a.spec.AgentCapabilities; +import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; +import org.junit.jupiter.api.Test; public class AgentCardValidatorTest { diff --git a/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java b/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java index c12a48e27..911b81540 100644 --- a/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java +++ b/server-common/src/test/java/io/a2a/server/ServerCallContextTest.java @@ -9,9 +9,8 @@ import java.util.Map; import java.util.Set; -import org.junit.jupiter.api.Test; - import io.a2a.server.auth.User; +import org.junit.jupiter.api.Test; class ServerCallContextTest { diff --git a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java index d75829f0b..433ebcac3 100644 --- a/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java +++ b/server-common/src/test/java/io/a2a/server/agentexecution/RequestContextTest.java @@ -9,20 +9,20 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + import io.a2a.spec.InvalidParamsError; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.Task; -import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; import io.a2a.spec.TextPart; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - public class RequestContextTest { @Test diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index f7f95f01e..6114e8f21 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -1,5 +1,6 @@ package io.a2a.server.events; +import static io.a2a.jsonrpc.common.json.JsonUtil.fromJson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -14,12 +15,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import io.a2a.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.spec.A2AError; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; import io.a2a.spec.Event; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -27,7 +27,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import io.a2a.util.Utils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,25 +60,25 @@ public void init() { @Test public void testConsumeOneTaskEvent() throws Exception { - Task event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Task event = fromJson(MINIMAL_TASK, Task.class); enqueueAndConsumeOneEvent(event); } @Test public void testConsumeOneMessageEvent() throws Exception { - Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); + Event event = fromJson(MESSAGE_PAYLOAD, Message.class); enqueueAndConsumeOneEvent(event); } @Test public void testConsumeOneA2AErrorEvent() throws Exception { - Event event = new A2AError() {}; + Event event = new A2AError(-1, "", null); enqueueAndConsumeOneEvent(event); } @Test public void testConsumeOneJsonRpcErrorEvent() throws Exception { - Event event = new JSONRPCError(123, "Some Error", null); + Event event = new A2AError(123, "Some Error", null); enqueueAndConsumeOneEvent(event); } @@ -91,7 +90,7 @@ public void testConsumeOneQueueEmpty() throws A2AServerException { @Test public void testConsumeAllMultipleEvents() throws JsonProcessingException { List events = List.of( - Utils.unmarshalFrom(MINIMAL_TASK, Task.class), + fromJson(MINIMAL_TASK, Task.class), TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") @@ -152,7 +151,7 @@ public void onComplete() { @Test public void testConsumeUntilMessage() throws Exception { List events = List.of( - Utils.unmarshalFrom(MINIMAL_TASK, Task.class), + fromJson(MINIMAL_TASK, Task.class), TaskArtifactUpdateEvent.builder() .taskId("task-123") .contextId("session-xyz") @@ -212,7 +211,7 @@ public void onComplete() { @Test public void testConsumeMessageEvents() throws Exception { - Message message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); + Message message = fromJson(MESSAGE_PAYLOAD, Message.class); Message message2 = Message.builder(message).build(); List events = List.of(message, message2); @@ -391,7 +390,7 @@ public void testConsumeAllHandlesQueueClosedException() throws Exception { EventConsumer consumer = new EventConsumer(queue); // Add a message event (which will complete the stream) - Event message = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); + Event message = fromJson(MESSAGE_PAYLOAD, Message.class); queue.enqueueEvent(message); // Close the queue before consuming diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 6ac904b9e..a3dc7d916 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -1,5 +1,6 @@ package io.a2a.server.events; +import static io.a2a.jsonrpc.common.json.JsonUtil.fromJson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -11,9 +12,9 @@ import java.util.List; +import io.a2a.spec.A2AError; import io.a2a.spec.Artifact; import io.a2a.spec.Event; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -22,7 +23,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import io.a2a.util.Utils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -98,7 +98,7 @@ public void testEnqueueEventPropagagesToChildren() throws Exception { EventQueue parentQueue = EventQueue.builder().build(); EventQueue childQueue = parentQueue.tap(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); parentQueue.enqueueEvent(event); // Event should be available in both parent and child queues @@ -115,8 +115,8 @@ public void testMultipleChildQueuesReceiveEvents() throws Exception { EventQueue childQueue1 = parentQueue.tap(); EventQueue childQueue2 = parentQueue.tap(); - Event event1 = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); - Event event2 = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); + Event event1 = fromJson(MINIMAL_TASK, Task.class); + Event event2 = fromJson(MESSAGE_PAYLOAD, Message.class); parentQueue.enqueueEvent(event1); parentQueue.enqueueEvent(event2); @@ -138,7 +138,7 @@ public void testChildQueueDequeueIndependently() throws Exception { EventQueue childQueue1 = parentQueue.tap(); EventQueue childQueue2 = parentQueue.tap(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); parentQueue.enqueueEvent(event); // Dequeue from child1 first @@ -161,7 +161,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { EventQueue childQueue = parentQueue.tap(); // Add events to both parent and child - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); parentQueue.enqueueEvent(event); assertFalse(childQueue.isClosed()); @@ -188,7 +188,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { @Test public void testEnqueueEventWhenClosed() throws Exception { EventQueue queue = EventQueue.builder().build(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); queue.close(); // Close the queue first assertTrue(queue.isClosed()); @@ -218,7 +218,7 @@ public void testDequeueEventWhenClosedAndEmpty() throws Exception { @Test public void testDequeueEventWhenClosedButHasEvents() throws Exception { EventQueue queue = EventQueue.builder().build(); - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); queue.enqueueEvent(event); queue.close(); // Graceful close - events should remain @@ -234,7 +234,7 @@ public void testDequeueEventWhenClosedButHasEvents() throws Exception { @Test public void testEnqueueAndDequeueEvent() throws Exception { - Event event = Utils.unmarshalFrom(MESSAGE_PAYLOAD, Message.class); + Event event = fromJson(MESSAGE_PAYLOAD, Message.class); eventQueue.enqueueEvent(event); Event dequeuedEvent = eventQueue.dequeueEventItem(200).getEvent(); assertSame(event, dequeuedEvent); @@ -242,7 +242,7 @@ public void testEnqueueAndDequeueEvent() throws Exception { @Test public void testDequeueEventNoWait() throws Exception { - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); Event dequeuedEvent = eventQueue.dequeueEventItem(-1).getEvent(); assertSame(event, dequeuedEvent); @@ -288,7 +288,7 @@ public void testTaskDone() throws Exception { public void testEnqueueDifferentEventTypes() throws Exception { List events = List.of( new TaskNotFoundError(), - new JSONRPCError(111, "rpc error", null)); + new A2AError(111, "rpc error", null)); for (Event event : events) { eventQueue.enqueueEvent(event); @@ -303,7 +303,7 @@ public void testEnqueueDifferentEventTypes() throws Exception { */ @Test public void testCloseGracefulSetsFlag() throws Exception { - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); eventQueue.close(false); // Graceful close @@ -316,7 +316,7 @@ public void testCloseGracefulSetsFlag() throws Exception { */ @Test public void testCloseImmediateClearsQueue() throws Exception { - Event event = Utils.unmarshalFrom(MINIMAL_TASK, Task.class); + Event event = fromJson(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); eventQueue.close(true); // Immediate close diff --git a/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java b/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java index 27d5ca08a..b9dec6364 100644 --- a/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java +++ b/server-common/src/test/java/io/a2a/server/extensions/A2AExtensionsTest.java @@ -9,12 +9,11 @@ import java.util.List; import java.util.Set; -import org.junit.jupiter.api.Test; - import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentExtension; import io.a2a.spec.AgentInterface; +import org.junit.jupiter.api.Test; class A2AExtensionsTest { diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 0020fe947..8038bc147 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -20,8 +20,8 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; @@ -33,11 +33,11 @@ import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.server.tasks.PushNotificationSender; import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.A2AError; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; import io.a2a.spec.Event; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; @@ -80,14 +80,14 @@ public class AbstractA2ARequestHandlerTest { public void init() { executor = new AgentExecutor() { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { if (agentExecutorExecute != null) { agentExecutorExecute.invoke(context, eventQueue); } } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { if (agentExecutorCancel != null) { agentExecutorCancel.invoke(context, eventQueue); } @@ -149,7 +149,7 @@ private static String loadPreferredTransportFromProperties() { } protected interface AgentExecutorMethod { - void invoke(RequestContext context, EventQueue eventQueue) throws JSONRPCError; + void invoke(RequestContext context, EventQueue eventQueue) throws A2AError; } /** diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 31a57f137..293babe4e 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -21,7 +21,7 @@ import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; import io.a2a.server.tasks.InMemoryTaskStore; import io.a2a.server.tasks.TaskUpdater; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.Message; @@ -959,7 +959,7 @@ private static class TestAgentExecutor implements AgentExecutor { private volatile boolean executing = false; interface ExecuteCallback { - void call(RequestContext context, EventQueue queue) throws JSONRPCError; + void call(RequestContext context, EventQueue queue) throws A2AError; } void setExecuteCallback(ExecuteCallback callback) { @@ -971,7 +971,7 @@ boolean isExecuting() { } @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { executing = true; try { if (executeCallback != null) { @@ -993,7 +993,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { // Simple cancel implementation executing = false; } diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java index 05adb948c..e814c1c15 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java @@ -1,10 +1,10 @@ package io.a2a.server.tasks; +import static io.a2a.jsonrpc.common.json.JsonUtil.fromJson; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import io.a2a.spec.Task; -import io.a2a.util.Utils; import org.junit.jupiter.api.Test; public class InMemoryTaskStoreTest { @@ -18,7 +18,7 @@ public class InMemoryTaskStoreTest { @Test public void testSaveAndGet() throws Exception { InMemoryTaskStore store = new InMemoryTaskStore(); - Task task = Utils.unmarshalFrom(TASK_JSON, Task.class); + Task task = fromJson(TASK_JSON, Task.class); store.save(task); Task retrieved = store.get(task.id()); assertSame(task, retrieved); @@ -34,7 +34,7 @@ public void testGetNonExistent() throws Exception { @Test public void testDelete() throws Exception { InMemoryTaskStore store = new InMemoryTaskStore(); - Task task = Utils.unmarshalFrom(TASK_JSON, Task.class); + Task task = fromJson(TASK_JSON, Task.class); store.save(task); store.delete(task.id()); Task retrieved = store.get(task.id()); diff --git a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java index fcc9e9cf0..7bb67f681 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java @@ -16,18 +16,17 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; import io.a2a.common.A2AHeaders; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class PushNotificationSenderTest { diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index f39382878..f14ebc0fe 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -1,5 +1,6 @@ package io.a2a.server.tasks; +import static io.a2a.jsonrpc.common.json.JsonUtil.fromJson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -22,7 +23,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import io.a2a.util.Utils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,7 +40,7 @@ public class TaskManagerTest { @BeforeEach public void init() throws Exception { - minimalTask = Utils.unmarshalFrom(TASK_JSON, Task.class); + minimalTask = fromJson(TASK_JSON, Task.class); taskStore = new InMemoryTaskStore(); taskManager = new TaskManager(minimalTask.id(), minimalTask.contextId(), taskStore, null); } diff --git a/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java b/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java index 8b4bd628c..34912efe6 100644 --- a/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java +++ b/server-common/src/test/java/io/a2a/server/util/ArtifactUtilsTest.java @@ -1,22 +1,21 @@ package io.a2a.server.util; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import java.util.List; import java.util.Map; import java.util.UUID; -import org.junit.jupiter.api.Test; - import io.a2a.spec.Artifact; import io.a2a.spec.DataPart; import io.a2a.spec.Part; import io.a2a.spec.TextPart; +import org.junit.jupiter.api.Test; class ArtifactUtilsTest { @Test diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 94b3fb939..8938cdf1d 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -24,6 +24,10 @@ ${project.groupId} a2a-java-sdk-spec + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + com.google.protobuf protobuf-java @@ -92,7 +96,7 @@ io.github.ascopes protobuf-maven-plugin - 4.0.2 + 4.0.3 ${skip.protobuf.generate} true diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java index b47a56e37..4c1728753 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java @@ -3,6 +3,7 @@ import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -10,7 +11,6 @@ import com.google.protobuf.Struct; import com.google.protobuf.Timestamp; import com.google.protobuf.Value; -import java.util.Collections; import org.mapstruct.Mapper; import org.mapstruct.Named; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java index 9ee0f94d3..68a79eea6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java @@ -2,7 +2,6 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.ObjectFactory; /** * Mapper between {@link io.a2a.spec.DataPart} and {@link io.a2a.grpc.DataPart}. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java index 2174f2dc8..2b1993756 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java @@ -1,12 +1,13 @@ package io.a2a.grpc.mapper; +import java.util.Base64; + import com.google.protobuf.ByteString; import io.a2a.spec.FileContent; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; import io.a2a.spec.InvalidRequestError; -import java.util.Base64; import org.mapstruct.Mapper; /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java index 6189daa4f..599877002 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksResultMapper.java @@ -1,11 +1,11 @@ package io.a2a.grpc.mapper; -import io.a2a.spec.ListTasksResult; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import org.mapstruct.Mapper; import org.mapstruct.Mapping; /** - * Mapper between {@link io.a2a.spec.ListTasksResult} and {@link io.a2a.grpc.ListTasksResponse}. + * Mapper between {@link ListTasksResult} and {@link io.a2a.grpc.ListTasksResponse}. *

    * Handles conversion with null handling for nextPageToken field. * Uses ADDER_PREFERRED strategy to avoid ProtocolMessageList instantiation issues. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java index 10faa303f..4768fd5c7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/SubscribeToTaskRequestMapper.java @@ -1,10 +1,10 @@ package io.a2a.grpc.mapper; -import io.a2a.spec.SubscribeToTaskRequest; +import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import org.mapstruct.Mapper; /** - * Mapper between {@link io.a2a.spec.SubscribeToTaskRequest} and {@link io.a2a.grpc.SubscribeToTaskRequest}. + * Mapper between {@link SubscribeToTaskRequest} and {@link io.a2a.grpc.SubscribeToTaskRequest}. *

    * The mapping handles the structural difference between domain and proto representations: *

      diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index 85d5b9f2e..37b1e9f34 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -1,6 +1,28 @@ package io.a2a.grpc.utils; -import io.a2a.json.JsonMappingException; +import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; +import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD; +import static io.a2a.spec.A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; @@ -11,65 +33,56 @@ import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; import io.a2a.grpc.StreamResponse; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; +import io.a2a.jsonrpc.common.json.IdJsonMappingException; +import io.a2a.jsonrpc.common.json.InvalidParamsJsonMappingException; +import io.a2a.jsonrpc.common.json.JsonMappingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.MethodNotFoundJsonMappingException; +import io.a2a.jsonrpc.common.wrappers.A2AMessage; +import io.a2a.jsonrpc.common.wrappers.A2ARequest; +import io.a2a.jsonrpc.common.wrappers.A2AResponse; +import io.a2a.jsonrpc.common.wrappers.CancelTaskRequest; +import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; +import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; +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.GetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksRequest; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; +import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; +import io.a2a.spec.A2AError; import io.a2a.spec.ContentTypeNotSupportedError; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; -import io.a2a.spec.IdJsonMappingException; import io.a2a.spec.InvalidAgentResponseError; import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.InvalidParamsJsonMappingException; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.JSONRPCMessage; -import io.a2a.spec.JSONRPCRequest; -import io.a2a.spec.JSONRPCResponse; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.ListTasksResponse; import io.a2a.spec.MethodNotFoundError; -import io.a2a.spec.MethodNotFoundJsonMappingException; import io.a2a.spec.PushNotificationNotSupportedError; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.TaskNotCancelableError; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.UnsupportedOperationError; -import java.io.IOException; -import java.io.StringWriter; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; +import io.a2a.util.Utils; import org.jspecify.annotations.Nullable; -import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INVALID_PARAMS_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.INVALID_REQUEST_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.METHOD_NOT_FOUND_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; - -import io.a2a.json.JsonProcessingException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_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; +import static io.a2a.spec.A2AMethods.LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SEND_MESSAGE_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; /** * Utilities for converting between JSON-RPC 2.0 messages and Protocol Buffer objects. @@ -150,8 +163,8 @@ * }
    * * @see ProtoUtils - * @see JSONRPCRequest - * @see JSONRPCResponse + * @see A2ARequest + * @see A2AResponse * @see JSON-RPC 2.0 Specification */ public class JSONRPCUtils { @@ -164,7 +177,7 @@ public class JSONRPCUtils { private static final Pattern EXTRACT_WRONG_TYPE = Pattern.compile("Expected (.*) but found \".*\""); static final String ERROR_MESSAGE = "Invalid request content: %s. Please verify the request matches the expected schema for this method."; - public static JSONRPCRequest parseRequestBody(String body) throws JsonMappingException, JsonProcessingException { + public static A2ARequest parseRequestBody(String body) throws JsonMappingException, JsonProcessingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); if (!jsonRpc.has("method")) { @@ -176,65 +189,64 @@ public static JSONRPCRequest parseRequestBody(String body) throws JsonMapping Object id = getAndValidateId(jsonRpc); String method = jsonRpc.get("method").getAsString(); JsonElement paramsNode = jsonRpc.get("params"); - try { return parseMethodRequest(version, id, method, paramsNode); } catch (InvalidParamsError e) { - throw new InvalidParamsJsonMappingException(e.getMessage(), id); + throw new InvalidParamsJsonMappingException(Utils.defaultIfNull(e.getMessage(), "Invalid parameters"), id); } } - private static JSONRPCRequest parseMethodRequest(String version, Object id, String method, JsonElement paramsNode) throws InvalidParamsError, MethodNotFoundJsonMappingException, JsonProcessingException { + private static A2ARequest parseMethodRequest(String version, Object id, String method, JsonElement paramsNode) throws InvalidParamsError, MethodNotFoundJsonMappingException, JsonProcessingException { switch (method) { - case GetTaskRequest.METHOD -> { + case GET_TASK_METHOD -> { io.a2a.grpc.GetTaskRequest.Builder builder = io.a2a.grpc.GetTaskRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new GetTaskRequest(version, id, ProtoUtils.FromProto.taskQueryParams(builder)); } - case CancelTaskRequest.METHOD -> { + case CANCEL_TASK_METHOD -> { io.a2a.grpc.CancelTaskRequest.Builder builder = io.a2a.grpc.CancelTaskRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new CancelTaskRequest(version, id, ProtoUtils.FromProto.taskIdParams(builder)); } - case ListTasksRequest.METHOD -> { + case LIST_TASK_METHOD -> { io.a2a.grpc.ListTasksRequest.Builder builder = io.a2a.grpc.ListTasksRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new ListTasksRequest(version, id, ProtoUtils.FromProto.listTasksParams(builder)); } - case SetTaskPushNotificationConfigRequest.METHOD -> { + case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new SetTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.setTaskPushNotificationConfig(builder)); } - case GetTaskPushNotificationConfigRequest.METHOD -> { + case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new GetTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.getTaskPushNotificationConfigParams(builder)); } - case SendMessageRequest.METHOD -> { + case SEND_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new SendMessageRequest(version, id, ProtoUtils.FromProto.messageSendParams(builder)); } - case ListTaskPushNotificationConfigRequest.METHOD -> { + case LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new ListTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.listTaskPushNotificationConfigParams(builder)); } - case DeleteTaskPushNotificationConfigRequest.METHOD -> { + case DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new DeleteTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.deleteTaskPushNotificationConfigParams(builder)); } - case GetAuthenticatedExtendedCardRequest.METHOD -> { + case GET_EXTENDED_AGENT_CARD_METHOD -> { return new GetAuthenticatedExtendedCardRequest(version, id); } - case SendStreamingMessageRequest.METHOD -> { + case SEND_STREAMING_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new SendStreamingMessageRequest(version, id, ProtoUtils.FromProto.messageSendParams(builder)); } - case SubscribeToTaskRequest.METHOD -> { + case SUBSCRIBE_TO_TASK_METHOD -> { io.a2a.grpc.SubscribeToTaskRequest.Builder builder = io.a2a.grpc.SubscribeToTaskRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); return new SubscribeToTaskRequest(version, id, ProtoUtils.FromProto.taskIdParams(builder)); @@ -258,7 +270,7 @@ public static StreamResponse parseResponseEvent(String body) throws JsonMappingE return builder.build(); } - public static JSONRPCResponse parseResponseBody(String body, String method) throws JsonMappingException, JsonProcessingException { + public static A2AResponse parseResponseBody(String body, String method) throws JsonMappingException, JsonProcessingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); String version = getAndValidateJsonrpc(jsonRpc); @@ -268,32 +280,32 @@ public static JSONRPCResponse parseResponseBody(String body, String method) t return parseError(jsonRpc.getAsJsonObject("error"), id, method); } switch (method) { - case GetTaskRequest.METHOD -> { + case GET_TASK_METHOD -> { io.a2a.grpc.Task.Builder builder = io.a2a.grpc.Task.newBuilder(); parseRequestBody(paramsNode, builder, id); return new GetTaskResponse(id, ProtoUtils.FromProto.task(builder)); } - case CancelTaskRequest.METHOD -> { + case CANCEL_TASK_METHOD -> { io.a2a.grpc.Task.Builder builder = io.a2a.grpc.Task.newBuilder(); parseRequestBody(paramsNode, builder, id); return new CancelTaskResponse(id, ProtoUtils.FromProto.task(builder)); } - case ListTasksRequest.METHOD -> { + case LIST_TASK_METHOD -> { io.a2a.grpc.ListTasksResponse.Builder builder = io.a2a.grpc.ListTasksResponse.newBuilder(); parseRequestBody(paramsNode, builder, id); return new ListTasksResponse(id, ProtoUtils.FromProto.listTasksResult(builder)); } - case SetTaskPushNotificationConfigRequest.METHOD -> { + case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); parseRequestBody(paramsNode, builder, id); return new SetTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.taskPushNotificationConfig(builder)); } - case GetTaskPushNotificationConfigRequest.METHOD -> { + case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); parseRequestBody(paramsNode, builder, id); return new GetTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.taskPushNotificationConfig(builder)); } - case SendMessageRequest.METHOD -> { + case SEND_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageResponse.Builder builder = io.a2a.grpc.SendMessageResponse.newBuilder(); parseRequestBody(paramsNode, builder, id); if (builder.hasMsg()) { @@ -301,15 +313,15 @@ public static JSONRPCResponse parseResponseBody(String body, String method) t } return new SendMessageResponse(id, ProtoUtils.FromProto.task(builder.getTask())); } - case ListTaskPushNotificationConfigRequest.METHOD -> { + case LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.ListTaskPushNotificationConfigResponse.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(); parseRequestBody(paramsNode, builder, id); return new ListTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.listTaskPushNotificationConfigResult(builder)); } - case DeleteTaskPushNotificationConfigRequest.METHOD -> { + case DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { return new DeleteTaskPushNotificationConfigResponse(id); } - case GetAuthenticatedExtendedCardRequest.METHOD -> { + case GET_EXTENDED_AGENT_CARD_METHOD -> { io.a2a.grpc.AgentCard.Builder builder = io.a2a.grpc.AgentCard.newBuilder(); parseRequestBody(paramsNode, builder, id); return new GetAuthenticatedExtendedCardResponse(id, ProtoUtils.FromProto.agentCard(builder)); @@ -319,31 +331,31 @@ public static JSONRPCResponse parseResponseBody(String body, String method) t } } - public static JSONRPCResponse parseError(JsonObject error, Object id, String method) throws JsonMappingException { - JSONRPCError rpcError = processError(error); + public static A2AResponse parseError(JsonObject error, Object id, String method) throws JsonMappingException { + A2AError rpcError = processError(error); switch (method) { - case GetTaskRequest.METHOD -> { + case GET_TASK_METHOD -> { return new GetTaskResponse(id, rpcError); } - case CancelTaskRequest.METHOD -> { + case CANCEL_TASK_METHOD -> { return new CancelTaskResponse(id, rpcError); } - case ListTasksRequest.METHOD -> { + case LIST_TASK_METHOD -> { return new ListTasksResponse(id, rpcError); } - case SetTaskPushNotificationConfigRequest.METHOD -> { + case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { return new SetTaskPushNotificationConfigResponse(id, rpcError); } - case GetTaskPushNotificationConfigRequest.METHOD -> { + case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { return new GetTaskPushNotificationConfigResponse(id, rpcError); } - case SendMessageRequest.METHOD -> { + case SEND_MESSAGE_METHOD -> { return new SendMessageResponse(id, rpcError); } - case ListTaskPushNotificationConfigRequest.METHOD -> { + case LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { return new ListTaskPushNotificationConfigResponse(id, rpcError); } - case DeleteTaskPushNotificationConfigRequest.METHOD -> { + case DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { return new DeleteTaskPushNotificationConfigResponse(id, rpcError); } default -> @@ -351,7 +363,7 @@ public static JSONRPCResponse parseError(JsonObject error, Object id, String } } - private static JSONRPCError processError(JsonObject error) { + private static A2AError processError(JsonObject error) { String message = error.has("message") ? error.get("message").getAsString() : null; Integer code = error.has("code") ? error.get("code").getAsInt() : null; String data = error.has("data") ? error.get("data").toString() : null; @@ -380,17 +392,17 @@ private static JSONRPCError processError(JsonObject error) { case TASK_NOT_FOUND_ERROR_CODE: return new TaskNotFoundError(code, message, data); default: - return new JSONRPCError(code, message, data); + return new A2AError(code, message, data); } } - return new JSONRPCError(code, message, data); + return new A2AError(code, message, data); } protected static void parseRequestBody(JsonElement jsonRpc, com.google.protobuf.Message.Builder builder, Object id) throws JsonProcessingException { parseJsonString(jsonRpc.toString(), builder, id); } - public static void parseJsonString(String body, com.google.protobuf.Message.Builder builder, @Nullable Object id) throws JsonProcessingException { + public static void parseJsonString(String body, com.google.protobuf.Message.Builder builder, Object id) throws JsonProcessingException { try { JsonFormat.parser().merge(body, builder); } catch (InvalidProtocolBufferException e) { @@ -424,7 +436,7 @@ public static void parseJsonString(String body, com.google.protobuf.Message.Buil * @param id the request ID if it could be extracted, null otherwise * @return an appropriate JsonProcessingException subtype based on the error and ID availability */ - private static JsonProcessingException convertProtoBufExceptionToJsonProcessingException(InvalidProtocolBufferException e, @Nullable Object id) { + private static JsonProcessingException convertProtoBufExceptionToJsonProcessingException(InvalidProtocolBufferException e, Object id) { // Log the original exception for debugging purposes log.log(Level.FINE, "Converting protobuf parsing exception to JSON-RPC error. Request ID: {0}", id); log.log(Level.FINE, "Original proto exception details", e); @@ -448,12 +460,12 @@ private static JsonProcessingException convertProtoBufExceptionToJsonProcessingE Matcher matcher = EXTRACT_WRONG_TYPE.matcher(message); if (matcher.matches() && matcher.group(1) != null) { // ID is null -> use empty string sentinel value (see javadoc above) - return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(matcher.group(1)), id == null ? "" : id); + return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(matcher.group(1)), Utils.defaultIfNull(id, "")); } matcher = EXTRACT_WRONG_VALUE.matcher(message); if (matcher.matches() && matcher.group(1) != null) { // ID is null -> use empty string sentinel value (see javadoc above) - return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(matcher.group(1)), id == null ? "" : id); + return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(matcher.group(1)), Utils.defaultIfNull(id, "")); } // Generic error - couldn't match specific patterns @@ -467,7 +479,7 @@ protected static String getAndValidateJsonrpc(JsonObject jsonRpc) throws JsonMap getIdIfPossible(jsonRpc)); } String version = jsonRpc.get("jsonrpc").getAsString(); - if (!JSONRPCMessage.JSONRPC_VERSION.equals(version)) { + if (!A2AMessage.JSONRPC_VERSION.equals(version)) { throw new IdJsonMappingException( "Unsupported JSON-RPC version: '" + version + "'. Expected version '2.0'", getIdIfPossible(jsonRpc)); @@ -558,7 +570,7 @@ public static String toJsonRPCResultResponse(Object requestId, com.google.protob } } - public static String toJsonRPCErrorResponse(Object requestId, JSONRPCError error) { + public static String toJsonRPCErrorResponse(Object requestId, A2AError error) { try (StringWriter result = new StringWriter(); JsonWriter output = GSON.newJsonWriter(result)) { output.beginObject(); output.name("jsonrpc").value("2.0"); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index 6a0ac5409..b4e9a1755 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -1,9 +1,10 @@ package io.a2a.grpc.utils; -import io.a2a.grpc.GetExtendedAgentCardRequest; -import io.a2a.grpc.StreamResponse; import java.util.ArrayList; import java.util.List; + +import io.a2a.grpc.GetExtendedAgentCardRequest; +import io.a2a.grpc.StreamResponse; import io.a2a.grpc.mapper.AgentCardMapper; import io.a2a.grpc.mapper.DeleteTaskPushNotificationConfigParamsMapper; import io.a2a.grpc.mapper.GetTaskPushNotificationConfigParamsMapper; @@ -22,16 +23,15 @@ import io.a2a.grpc.mapper.TaskQueryParamsMapper; import io.a2a.grpc.mapper.TaskStateMapper; import io.a2a.grpc.mapper.TaskStatusUpdateEventMapper; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.spec.AgentCard; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.Message; import io.a2a.spec.MessageSendConfiguration; import io.a2a.spec.MessageSendParams; @@ -55,7 +55,7 @@ public static io.a2a.grpc.AgentCard agentCard(AgentCard agentCard) { return AgentCardMapper.INSTANCE.toProto(agentCard); } - public static io.a2a.grpc.GetExtendedAgentCardRequest extendedAgentCard(GetAuthenticatedExtendedCardRequest request) { + public static io.a2a.grpc.GetExtendedAgentCardRequest extendedAgentCard() { return GetExtendedAgentCardRequest.newBuilder().build(); } diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index 7243ff40a..888fd18b8 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -2,8 +2,6 @@ syntax = "proto3"; package a2a.v1; -//From commit c196824396bb4af4c595f30e2c503a5ab1dbac4b - import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; @@ -11,6 +9,8 @@ import "google/protobuf/empty.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; +//From commit c196824396bb4af4c595f30e2c503a5ab1dbac4b + option csharp_namespace = "A2a.V1"; option go_package = "google.golang.org/a2a/v1"; option java_multiple_files = true; diff --git a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java index ba1496547..0619af352 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java @@ -5,6 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.Collections; + import io.a2a.spec.Artifact; import io.a2a.spec.Message; import io.a2a.spec.StreamingEventKind; @@ -14,7 +16,6 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; -import java.util.Collections; import org.junit.jupiter.api.Test; public class StreamResponseMapperTest { diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java index ffb048b28..61506c88e 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -1,23 +1,25 @@ package io.a2a.grpc.utils; import static io.a2a.grpc.utils.JSONRPCUtils.ERROR_MESSAGE; +import static io.a2a.spec.A2AMethods.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; +import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import io.a2a.json.JsonProcessingException; import com.google.gson.JsonSyntaxException; -import io.a2a.json.JsonMappingException; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.json.InvalidParamsJsonMappingException; +import io.a2a.jsonrpc.common.json.JsonMappingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.wrappers.A2ARequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; import io.a2a.spec.InvalidParamsError; -import io.a2a.spec.InvalidParamsJsonMappingException; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCRequest; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.TaskPushNotificationConfig; import org.junit.jupiter.api.Test; @@ -47,7 +49,7 @@ public void testParseSetTaskPushNotificationConfigRequest_ValidProtoFormat() thr } """; - JSONRPCRequest request = JSONRPCUtils.parseRequestBody(validRequest); + A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest); assertNotNull(request); assertInstanceOf(SetTaskPushNotificationConfigRequest.class, request); @@ -76,7 +78,7 @@ public void testParseGetTaskPushNotificationConfigRequest_ValidProtoFormat() thr } """; - JSONRPCRequest request = JSONRPCUtils.parseRequestBody(validRequest); + A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest); assertNotNull(request); assertInstanceOf(GetTaskPushNotificationConfigRequest.class, request); @@ -285,7 +287,7 @@ public void testGenerateSetTaskPushNotificationConfigResponse_Success() throws E """; SetTaskPushNotificationConfigResponse response = - (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, SetTaskPushNotificationConfigRequest.METHOD); + (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(1, response.getId()); @@ -311,7 +313,7 @@ public void testGenerateGetTaskPushNotificationConfigResponse_Success() throws E """; GetTaskPushNotificationConfigResponse response = - (GetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, GetTaskPushNotificationConfigRequest.METHOD); + (GetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(2, response.getId()); @@ -334,7 +336,7 @@ public void testParseErrorResponse_InvalidParams() throws Exception { """; SetTaskPushNotificationConfigResponse response = - (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SetTaskPushNotificationConfigRequest.METHOD); + (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(5, response.getId()); @@ -358,7 +360,7 @@ public void testParseErrorResponse_ParseError() throws Exception { """; SetTaskPushNotificationConfigResponse response = - (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SetTaskPushNotificationConfigRequest.METHOD); + (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(6, response.getId()); diff --git a/spec/src/main/java/io/a2a/spec/A2AError.java b/spec/src/main/java/io/a2a/spec/A2AError.java index 122b26a0d..4bbc8917b 100644 --- a/spec/src/main/java/io/a2a/spec/A2AError.java +++ b/spec/src/main/java/io/a2a/spec/A2AError.java @@ -1,5 +1,7 @@ package io.a2a.spec; +import io.a2a.util.Assert; + /** * Marker interface for A2A Protocol error events. *

    @@ -7,7 +9,7 @@ * in the A2A Protocol's event stream. All protocol-level errors implement this interface, * enabling uniform error handling across both streaming and non-streaming communication. *

    - * A2A errors typically extend {@link JSONRPCError} to provide JSON-RPC 2.0 compliant + * A2A errors typically extend {@link A2AError} to provide JSON-RPC 2.0 compliant * error responses with standard error codes, messages, and optional additional data. *

    * Common implementations include: @@ -21,9 +23,68 @@ * * * @see Event for the base event interface - * @see JSONRPCError for the base error implementation + * @see A2AError for the base error implementation * @see JSON-RPC 2.0 Error Object * @see A2A Protocol Specification */ -public interface A2AError extends Event { +public class A2AError extends RuntimeException implements Event { + /** + * The numeric error code (see JSON-RPC 2.0 spec for standard codes). + */ + private final Integer code; + + /** + * Additional error information (structure defined by the error code). + */ + private final Object data; + + /** + * Constructs a JSON-RPC error with the specified code, message, and optional data. + *

    + * This constructor is used by Jackson for JSON deserialization. + * + * @param code the numeric error code (required, see JSON-RPC 2.0 spec for standard codes) + * @param message the human-readable error message (required) + * @param data additional error information, structure defined by the error code (optional) + * @throws IllegalArgumentException if code or message is null + */ + public A2AError(Integer code, String message, Object data) { + super(message); + Assert.checkNotNullParam("code", code); + Assert.checkNotNullParam("message", message); + this.code = code; + this.data = data; + } + + /** + * Gets the numeric error code indicating the error type. + *

    + * Standard JSON-RPC 2.0 error codes: + *

      + *
    • -32700: Parse error
    • + *
    • -32600: Invalid Request
    • + *
    • -32601: Method not found
    • + *
    • -32602: Invalid params
    • + *
    • -32603: Internal error
    • + *
    • -32000 to -32099: Server error (implementation-defined)
    • + *
    + * + * @return the error code + */ + public Integer getCode() { + return code; + } + + /** + * Gets additional information about the error. + *

    + * The structure and type of the data field is defined by the specific error code. + * It may contain detailed debugging information, validation errors, or other + * context-specific data to help diagnose the error. + * + * @return the error data, or null if not provided + */ + public Object getData() { + return data; + } } diff --git a/spec/src/main/java/io/a2a/spec/A2AMethods.java b/spec/src/main/java/io/a2a/spec/A2AMethods.java new file mode 100644 index 000000000..0ffdf6e30 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/A2AMethods.java @@ -0,0 +1,17 @@ + +package io.a2a.spec; + +public interface A2AMethods { + String CANCEL_TASK_METHOD = "CancelTask"; + String DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "DeleteTaskPushNotificationConfig"; + String GET_EXTENDED_AGENT_CARD_METHOD = "GetExtendedAgentCard"; + String GET_TASK_METHOD = "GetTask"; + String GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "GetTaskPushNotificationConfig"; + String LIST_TASK_METHOD = "ListTasks"; + String LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "ListTaskPushNotificationConfig"; + String SEND_MESSAGE_METHOD = "SendMessage"; + String SEND_STREAMING_MESSAGE_METHOD = "SendStreamingMessage"; + String SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "SetTaskPushNotificationConfig"; + String SUBSCRIBE_TO_TASK_METHOD = "SubscribeToTask"; + +} diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index 01fc23509..a69c9a4ad 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -2,8 +2,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.APIKeySecurityScheme.API_KEY; - /** * API key security scheme for agent authentication. *

    diff --git a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java index aac74a412..cb6bc5c77 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCardSignature.java +++ b/spec/src/main/java/io/a2a/spec/AgentCardSignature.java @@ -1,8 +1,8 @@ package io.a2a.spec; -import com.google.gson.annotations.SerializedName; import java.util.Map; +import com.google.gson.annotations.SerializedName; import io.a2a.util.Assert; /** diff --git a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java index de99e54da..52650dc1f 100644 --- a/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java +++ b/spec/src/main/java/io/a2a/spec/AuthenticatedExtendedCardNotConfiguredError.java @@ -7,9 +7,8 @@ /** * A2A Protocol error indicating that the agent does not have an authenticated extended card configured. *

    - * This error is returned when a client attempts to retrieve an authenticated extended agent card - * via {@link GetAuthenticatedExtendedCardRequest}, but the agent has not configured authentication-protected - * extended card information. + * This error is returned when a client attempts to retrieve an authenticated extended agent card, + * but the agent has not configured authentication-protected extended card information. *

    * Extended cards may contain additional agent metadata, capabilities, or configuration that * should only be accessible to authenticated clients. Agents that don't implement this feature @@ -25,11 +24,10 @@ * } * }

    * - * @see GetAuthenticatedExtendedCardRequest for retrieving authenticated extended cards * @see AgentCard for the base agent card structure * @see A2A Protocol Specification */ -public class AuthenticatedExtendedCardNotConfiguredError extends JSONRPCError { +public class AuthenticatedExtendedCardNotConfiguredError extends A2AError { /** * Constructs an error for agents that don't support authenticated extended card retrieval. diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index b895531c6..c4db756a4 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -2,7 +2,6 @@ import java.util.Map; - import io.a2a.util.Assert; /** diff --git a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java index 88ce8a127..f248eaa70 100644 --- a/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ClientCredentialsOAuthFlow.java @@ -3,7 +3,6 @@ import java.util.Map; - import io.a2a.util.Assert; /** diff --git a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java index 9bd10192a..a696a891a 100644 --- a/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/ContentTypeNotSupportedError.java @@ -1,9 +1,7 @@ package io.a2a.spec; -import static io.a2a.util.Utils.defaultIfNull; - - import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; +import static io.a2a.util.Utils.defaultIfNull; /** * A2A Protocol error indicating incompatibility between requested content types and agent capabilities. @@ -38,7 +36,7 @@ * @see MessageSendConfiguration for client content type preferences * @see A2A Protocol Specification */ -public class ContentTypeNotSupportedError extends JSONRPCError { +public class ContentTypeNotSupportedError extends A2AError { /** * Constructs a content type not supported error. diff --git a/spec/src/main/java/io/a2a/spec/DataPart.java b/spec/src/main/java/io/a2a/spec/DataPart.java index d44e87151..163225429 100644 --- a/spec/src/main/java/io/a2a/spec/DataPart.java +++ b/spec/src/main/java/io/a2a/spec/DataPart.java @@ -4,8 +4,6 @@ import io.a2a.util.Assert; -import static io.a2a.util.Utils.SPEC_VERSION_1_0; - /** * Represents a structured data content part within a {@link Message} or {@link Artifact}. diff --git a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java index 9745ccf2d..75faf2e54 100644 --- a/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/DeleteTaskPushNotificationConfigParams.java @@ -13,7 +13,6 @@ * @param id the task identifier (required) * @param pushNotificationConfigId the specific configuration ID to delete (required) * @param tenant optional tenant, provided as a path parameter. - * @see DeleteTaskPushNotificationConfigRequest for the request using these parameters * @see A2A Protocol Specification */ public record DeleteTaskPushNotificationConfigParams(String id, String pushNotificationConfigId, String tenant) { diff --git a/spec/src/main/java/io/a2a/spec/FilePart.java b/spec/src/main/java/io/a2a/spec/FilePart.java index a1835109c..79b63fe64 100644 --- a/spec/src/main/java/io/a2a/spec/FilePart.java +++ b/spec/src/main/java/io/a2a/spec/FilePart.java @@ -3,8 +3,6 @@ import io.a2a.util.Assert; -import static io.a2a.util.Utils.SPEC_VERSION_1_0; - /** * Represents a file content part within a {@link Message} or {@link Artifact}. diff --git a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java index 225aa4071..36f2db3fb 100644 --- a/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/GetTaskPushNotificationConfigParams.java @@ -14,7 +14,6 @@ * @param id the task identifier (required) * @param pushNotificationConfigId optional specific configuration ID to retrieve * @param tenant optional tenant, provided as a path parameter. - * @see GetTaskPushNotificationConfigRequest for the request using these parameters * @see TaskPushNotificationConfig for the returned configuration structure * @see A2A Protocol Specification */ diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 76620d868..54858562f 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -2,9 +2,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.HTTPAuthSecurityScheme.HTTP; -import static io.a2a.util.Utils.SPEC_VERSION_1_0; - /** * HTTP authentication security scheme for agent authentication. *

    diff --git a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java index 93d3f15ac..06341ab2b 100644 --- a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java @@ -2,7 +2,6 @@ import java.util.Map; - import io.a2a.util.Assert; /** diff --git a/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java b/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java deleted file mode 100644 index 386a59118..000000000 --- a/spec/src/main/java/io/a2a/spec/IntegerJsonrpcId.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.a2a.spec; - -/** - * Integer-based implementation of JSON-RPC request/response correlation identifier. - *

    - * This class represents a JSON-RPC ID that uses an Integer value for correlation - * between requests and responses. According to the JSON-RPC 2.0 specification, - * Number IDs are one of the valid identifier types (along with String and NULL). - *

    - * Integer IDs are commonly used when: - *

      - *
    • Sequential request numbering is desired
    • - *
    • Memory efficiency is important (integers are smaller than strings)
    • - *
    • Simple monotonic correlation is sufficient
    • - *
    - * - * @see JsonrpcId - * @see StringJsonrpcId - * @see JSON-RPC 2.0 Request Object - */ -public class IntegerJsonrpcId implements JsonrpcId { -} diff --git a/spec/src/main/java/io/a2a/spec/InternalError.java b/spec/src/main/java/io/a2a/spec/InternalError.java index 72f4f825e..99aa71220 100644 --- a/spec/src/main/java/io/a2a/spec/InternalError.java +++ b/spec/src/main/java/io/a2a/spec/InternalError.java @@ -23,11 +23,9 @@ * } * }
    * - * @see JSONRPCError for the base error class - * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -public class InternalError extends JSONRPCError { +public class InternalError extends A2AError { /** * Constructs an internal error with full parameters. diff --git a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java index 187260bd9..303a3f548 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidAgentResponseError.java @@ -33,11 +33,9 @@ * } * }
    * - * @see JSONRPCResponse for response structure - * @see SendMessageResponse for message send response * @see A2A Protocol Specification */ -public class InvalidAgentResponseError extends JSONRPCError { +public class InvalidAgentResponseError extends A2AError { /** * Constructs an invalid agent response error. diff --git a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java index 4556c8fff..a7f3476c3 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidParamsError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidParamsError.java @@ -26,11 +26,9 @@ * throw new InvalidParamsError("taskId parameter is required"); * }
    * - * @see JSONRPCError for the base error class - * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -public class InvalidParamsError extends JSONRPCError { +public class InvalidParamsError extends A2AError { /** * Constructs an invalid params error with full parameters. diff --git a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java index 7f64a0ccc..98d0f65f7 100644 --- a/spec/src/main/java/io/a2a/spec/InvalidRequestError.java +++ b/spec/src/main/java/io/a2a/spec/InvalidRequestError.java @@ -27,11 +27,9 @@ * throw new InvalidRequestError("Missing 'method' field in request"); * }
    * - * @see JSONRPCError for the base error class - * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -public class InvalidRequestError extends JSONRPCError { +public class InvalidRequestError extends A2AError { /** * Constructs an invalid request error with default message. diff --git a/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java b/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java deleted file mode 100644 index 8823f9bd3..000000000 --- a/spec/src/main/java/io/a2a/spec/JSONErrorResponse.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.a2a.spec; - -/** - * A simplified error response wrapper for non-JSON-RPC error scenarios. - *

    - * This record provides a lightweight error response format for cases where - * a full JSON-RPC error structure is not appropriate, such as HTTP-level - * errors or transport-layer failures. - *

    - * Unlike {@link JSONRPCErrorResponse}, this is not part of the JSON-RPC 2.0 - * specification but serves as a utility for simpler error reporting in the - * A2A Java SDK implementation. - * - * @param error a human-readable error message - * @see JSONRPCErrorResponse - * @see JSONRPCError - */ -public record JSONErrorResponse(String error) { -} diff --git a/spec/src/main/java/io/a2a/spec/JSONParseError.java b/spec/src/main/java/io/a2a/spec/JSONParseError.java index 24c4c67d2..74da3ddf5 100644 --- a/spec/src/main/java/io/a2a/spec/JSONParseError.java +++ b/spec/src/main/java/io/a2a/spec/JSONParseError.java @@ -21,12 +21,12 @@ * } * }

    * - * @see JSONRPCError for the base error class + * @see A2AError for the base error class * @see A2AError for the error marker interface * @see InvalidRequestError for structurally valid but invalid requests * @see JSON-RPC 2.0 Error Codes */ -public class JSONParseError extends JSONRPCError implements A2AError { +public class JSONParseError extends A2AError { /** * Constructs a JSON parse error with default message. diff --git a/spec/src/main/java/io/a2a/spec/JSONRPCError.java b/spec/src/main/java/io/a2a/spec/JSONRPCError.java deleted file mode 100644 index 860fde68d..000000000 --- a/spec/src/main/java/io/a2a/spec/JSONRPCError.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.a2a.spec; - - -import io.a2a.util.Assert; - -/** - * Represents a JSON-RPC 2.0 error object as defined in the JSON-RPC 2.0 specification. - *

    - * This class encapsulates error information returned in JSON-RPC error responses. - * According to the JSON-RPC 2.0 specification, an error object must contain: - *

      - *
    • {@code code} - A number indicating the error type (required)
    • - *
    • {@code message} - A short description of the error (required)
    • - *
    • {@code data} - Additional information about the error (optional)
    • - *
    - *

    - * This class implements {@link Event} to allow errors to be streamed to clients, - * and {@link A2AError} to integrate with the A2A Protocol's error handling system. - * It extends {@link Error} to provide standard Java error semantics with a message. - *

    - * Standard error codes are defined in the JSON-RPC 2.0 specification: - *

      - *
    • -32700: Parse error
    • - *
    • -32600: Invalid Request
    • - *
    • -32601: Method not found
    • - *
    • -32602: Invalid params
    • - *
    • -32603: Internal error
    • - *
    • -32000 to -32099: Server error (implementation-defined)
    • - *
    - * - * @see Event - * @see A2AError - * @see JSONRPCErrorResponse - * @see JSON-RPC 2.0 Error Object - */ -public class JSONRPCError extends Error implements Event, A2AError { - - /** - * The numeric error code (see JSON-RPC 2.0 spec for standard codes). - */ - private final Integer code; - - /** - * Additional error information (structure defined by the error code). - */ - private final Object data; - - /** - * Constructs a JSON-RPC error with the specified code, message, and optional data. - *

    - * This constructor is used by Jackson for JSON deserialization. - * - * @param code the numeric error code (required, see JSON-RPC 2.0 spec for standard codes) - * @param message the human-readable error message (required) - * @param data additional error information, structure defined by the error code (optional) - * @throws IllegalArgumentException if code or message is null - */ - public JSONRPCError(Integer code, String message, Object data) { - super(message); - Assert.checkNotNullParam("code", code); - Assert.checkNotNullParam("message", message); - this.code = code; - this.data = data; - } - - /** - * Gets the numeric error code indicating the error type. - *

    - * Standard JSON-RPC 2.0 error codes: - *

      - *
    • -32700: Parse error
    • - *
    • -32600: Invalid Request
    • - *
    • -32601: Method not found
    • - *
    • -32602: Invalid params
    • - *
    • -32603: Internal error
    • - *
    • -32000 to -32099: Server error (implementation-defined)
    • - *
    - * - * @return the error code - */ - public Integer getCode() { - return code; - } - - /** - * Gets additional information about the error. - *

    - * The structure and type of the data field is defined by the specific error code. - * It may contain detailed debugging information, validation errors, or other - * context-specific data to help diagnose the error. - * - * @return the error data, or null if not provided - */ - public Object getData() { - return data; - } -} diff --git a/spec/src/main/java/io/a2a/spec/JsonrpcId.java b/spec/src/main/java/io/a2a/spec/JsonrpcId.java deleted file mode 100644 index 152c2a5b1..000000000 --- a/spec/src/main/java/io/a2a/spec/JsonrpcId.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.a2a.spec; - -/** - * Marker interface for JSON-RPC request/response correlation identifiers. - *

    - * According to the JSON-RPC 2.0 specification, the {@code id} field must be - * a String, Number, or NULL value. This interface serves as a type marker - * for implementations that represent valid JSON-RPC IDs. - *

    - * The A2A Java SDK provides two concrete implementations: - *

      - *
    • {@link StringJsonrpcId} - For string-based identifiers
    • - *
    • {@link IntegerJsonrpcId} - For numeric identifiers
    • - *
    - *

    - * Null IDs are also valid and represent notifications (requests that do not - * expect a response). - * - * @see StringJsonrpcId - * @see IntegerJsonrpcId - * @see JSON-RPC 2.0 Request Object - */ -public interface JsonrpcId { -} diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index 09967e378..f339afd88 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -10,7 +10,6 @@ * * @param id the task identifier (required) * @param tenant optional tenant, provided as a path parameter. - * @see ListTaskPushNotificationConfigRequest for the request using these parameters * @see TaskPushNotificationConfig for the configuration structure * @see A2A Protocol Specification */ diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java index 193b3ea9b..1ae125713 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigResult.java @@ -1,10 +1,10 @@ package io.a2a.spec; +import java.util.List; + import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; -import java.util.List; - /** * Result of listing push notification configurations for a task with pagination support. * diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index 4e2cc6432..6813dcf45 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -1,10 +1,10 @@ package io.a2a.spec; +import java.time.Instant; + import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; -import java.time.Instant; - /** * Parameters for listing tasks with optional filtering and pagination. * diff --git a/spec/src/main/java/io/a2a/spec/MessageSendParams.java b/spec/src/main/java/io/a2a/spec/MessageSendParams.java index 4dca5f806..1dcb6dd61 100644 --- a/spec/src/main/java/io/a2a/spec/MessageSendParams.java +++ b/spec/src/main/java/io/a2a/spec/MessageSendParams.java @@ -8,8 +8,7 @@ * Parameters for sending a message to an agent in the A2A Protocol. *

    * This record encapsulates the message content, optional configuration, and metadata for - * agent task requests. It is used by both {@link SendMessageRequest} and - * {@link SendStreamingMessageRequest} to define what message to send and how to process it. + * agent task requests. It is used to define what message to send and how to process it. *

    * The message can create a new task, continue an existing task (if it contains a task ID), * or restart a task depending on the agent's implementation and the message context. @@ -18,8 +17,6 @@ * @param configuration optional configuration for message processing behavior * @param metadata optional arbitrary key-value metadata for the request * @param tenant optional tenant, provided as a path parameter. - * @see SendMessageRequest for non-streaming message delivery - * @see SendStreamingMessageRequest for streaming message delivery * @see MessageSendConfiguration for available configuration options * @see A2A Protocol Specification */ diff --git a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java index c56bbcf1d..e38f7a605 100644 --- a/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/MethodNotFoundError.java @@ -18,11 +18,9 @@ * throw new MethodNotFoundError(); * }

    * - * @see JSONRPCError for the base error class - * @see A2AError for the error marker interface * @see JSON-RPC 2.0 Error Codes */ -public class MethodNotFoundError extends JSONRPCError { +public class MethodNotFoundError extends A2AError { /** * Constructs error with all parameters. diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index 659df3c7a..86480258b 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.MutualTLSSecurityScheme.MUTUAL_TLS; - /** * Mutual TLS (mTLS) security scheme for agent authentication. *

    diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index ee6557efc..f3179cc23 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -2,8 +2,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.OAuth2SecurityScheme.OAUTH2; - /** * OAuth 2.0 security scheme for agent authentication. *

    diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 0ba446cad..e7dfcfab3 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -2,8 +2,6 @@ import io.a2a.util.Assert; -import static io.a2a.spec.OpenIdConnectSecurityScheme.OPENID_CONNECT; - /** * OpenID Connect security scheme for agent authentication. *

    diff --git a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java index 0dd571955..1a336cc00 100644 --- a/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/PushNotificationNotSupportedError.java @@ -7,8 +7,7 @@ * A2A Protocol error indicating that the agent does not support push notifications. *

    * This error is returned when a client attempts push notification operations - * (such as {@link SetTaskPushNotificationConfigRequest}) on an agent that has - * {@link AgentCapabilities#pushNotifications()} set to {@code false}. + * on an agent that has {@link AgentCapabilities#pushNotifications()} set to {@code false}. *

    * Push notifications allow agents to proactively send task updates to clients via * HTTP callbacks. Agents that don't support this capability will return this error @@ -24,11 +23,10 @@ * }

    * * @see AgentCapabilities#pushNotifications() for push notification capability - * @see SetTaskPushNotificationConfigRequest for configuring push notifications * @see TaskPushNotificationConfig for push notification configuration * @see A2A Protocol Specification */ -public class PushNotificationNotSupportedError extends JSONRPCError { +public class PushNotificationNotSupportedError extends A2AError { /** * Constructs error with default message. diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index 2121a636e..322262eb9 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -1,7 +1,5 @@ package io.a2a.spec; -import static io.a2a.spec.APIKeySecurityScheme.API_KEY; - /** * Base interface for security schemes used to authenticate access to agent endpoints. *

    diff --git a/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java b/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java deleted file mode 100644 index 9a033a979..000000000 --- a/spec/src/main/java/io/a2a/spec/StringJsonrpcId.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.a2a.spec; - -/** - * String-based implementation of JSON-RPC request/response correlation identifier. - *

    - * This class represents a JSON-RPC ID that uses a String value for correlation - * between requests and responses. According to the JSON-RPC 2.0 specification, - * String IDs are one of the valid identifier types (along with Number and NULL). - *

    - * String IDs are commonly used when: - *

      - *
    • Client-generated correlation requires UUID or similar format
    • - *
    • IDs need to encode additional context or metadata
    • - *
    • Compatibility with systems that use string-based correlation
    • - *
    - * - * @see JsonrpcId - * @see IntegerJsonrpcId - * @see JSON-RPC 2.0 Request Object - */ -public class StringJsonrpcId implements JsonrpcId { -} diff --git a/spec/src/main/java/io/a2a/spec/TaskIdParams.java b/spec/src/main/java/io/a2a/spec/TaskIdParams.java index 584cca60c..d35eb37d9 100644 --- a/spec/src/main/java/io/a2a/spec/TaskIdParams.java +++ b/spec/src/main/java/io/a2a/spec/TaskIdParams.java @@ -6,13 +6,10 @@ * Parameters containing a task identifier for task-related operations. *

    * This simple parameter record is used by operations that only need a task ID, - * such as {@link CancelTaskRequest}, {@link SubscribeToTaskRequest}, and similar - * task-specific requests. It optionally includes metadata for additional context. + * and similar task-specific requests. It optionally includes metadata for additional context. * * @param id the unique task identifier (required) * @param tenant optional tenant, provided as a path parameter. - * @see CancelTaskRequest for task cancellation - * @see SubscribeToTaskRequest for task resubscription * @see A2A Protocol Specification */ public record TaskIdParams(String id, String tenant) { diff --git a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java index ad39cda16..86e7887e3 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotCancelableError.java @@ -6,7 +6,7 @@ /** * A2A Protocol error indicating that a task cannot be canceled in its current state. *

    - * This error is returned when a client attempts to cancel a task via {@link CancelTaskRequest} + * This error is returned when a client attempts to cancel a task * but the task is in a terminal state ({@link TaskState#COMPLETED}, {@link TaskState#FAILED}, * {@link TaskState#CANCELED}) where cancellation is not applicable. *

    @@ -25,12 +25,11 @@ * } * }

    * - * @see CancelTaskRequest for task cancellation * @see TaskState for task state definitions * @see TaskStatus#state() for current task state * @see A2A Protocol Specification */ -public class TaskNotCancelableError extends JSONRPCError { +public class TaskNotCancelableError extends A2AError { /** * Constructs error with default message. diff --git a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java index 4366db5b8..b1dd902d5 100644 --- a/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java +++ b/spec/src/main/java/io/a2a/spec/TaskNotFoundError.java @@ -6,8 +6,7 @@ /** * A2A Protocol error indicating that the requested task ID does not exist. *

    - * This error is returned when a client attempts to perform operations on a task (such as - * {@link GetTaskRequest}, {@link CancelTaskRequest}, or push notification operations) using + * This error is returned when a client attempts to perform operations on a task using * a task ID that is not found in the server's task store. *

    * Common causes: @@ -29,11 +28,9 @@ * }

    * * @see Task for task object definition - * @see GetTaskRequest for task retrieval - * @see CancelTaskRequest for task cancellation * @see A2A Protocol Specification */ -public class TaskNotFoundError extends JSONRPCError { +public class TaskNotFoundError extends A2AError { /** * Constructs error with default message. diff --git a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java index 794a29e2a..cf57a6f3c 100644 --- a/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java +++ b/spec/src/main/java/io/a2a/spec/TaskPushNotificationConfig.java @@ -17,7 +17,6 @@ * @param taskId the unique identifier of the task to receive push notifications for (required) * @param pushNotificationConfig the push notification endpoint and authentication configuration (required) * @see PushNotificationConfig for notification endpoint details - * @see SetTaskPushNotificationConfigRequest for setting push notifications * @see A2A Protocol Specification */ public record TaskPushNotificationConfig(String taskId, PushNotificationConfig pushNotificationConfig, @Nullable String tenant) { diff --git a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java index 2a34a2e03..8dc872bba 100644 --- a/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java +++ b/spec/src/main/java/io/a2a/spec/TaskStatusUpdateEvent.java @@ -1,8 +1,8 @@ package io.a2a.spec; -import com.google.gson.annotations.SerializedName; import java.util.Map; +import com.google.gson.annotations.SerializedName; import io.a2a.util.Assert; /** diff --git a/spec/src/main/java/io/a2a/spec/TextPart.java b/spec/src/main/java/io/a2a/spec/TextPart.java index 57adef574..4ab51f217 100644 --- a/spec/src/main/java/io/a2a/spec/TextPart.java +++ b/spec/src/main/java/io/a2a/spec/TextPart.java @@ -3,8 +3,6 @@ import io.a2a.util.Assert; -import static io.a2a.util.Utils.SPEC_VERSION_1_0; - /** * Represents a plain text content part within a {@link Message} or {@link Artifact}. diff --git a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java index b4bebd1d8..da2231e30 100644 --- a/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java +++ b/spec/src/main/java/io/a2a/spec/UnsupportedOperationError.java @@ -31,7 +31,7 @@ * @see MethodNotFoundError for unknown method errors * @see A2A Protocol Specification */ -public class UnsupportedOperationError extends JSONRPCError { +public class UnsupportedOperationError extends A2AError { /** * Constructs error with all parameters. diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 858737375..ca0610b1f 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -1,24 +1,21 @@ package io.a2a.util; +import static io.a2a.util.Assert.checkNotNullParam; + import java.util.ArrayList; import java.util.List; +import java.util.logging.Logger; import com.google.gson.Gson; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; - import io.a2a.spec.Artifact; +import io.a2a.spec.Part; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; -import io.a2a.spec.Part; -import java.util.logging.Logger; import org.jspecify.annotations.Nullable; -import static io.a2a.util.Assert.checkNotNullParam; - /** * Utility class providing common helper methods for A2A Protocol operations. *

    @@ -46,21 +43,6 @@ public class Utils { */ public static final String SPEC_VERSION_1_0 = "1.0"; - /** - * Deserializes JSON string into a typed object using Gson. - *

    - * This method uses the pre-configured {@link JsonUtil#fromJson(String, Class)} to parse JSON. - * - * @param the target type - * @param data JSON string to deserialize - * @param typeRef class reference specifying the target type - * @return deserialized object of type T - * @throws JsonProcessingException if JSON parsing fails - */ - public static T unmarshalFrom(String data, Class typeRef) throws JsonProcessingException { - return JsonUtil.fromJson(data, typeRef); - } - /** * Returns the provided value if non-null, otherwise returns the default value. *

    @@ -73,10 +55,7 @@ public static T unmarshalFrom(String data, Class typeRef) throws JsonProc * @return value if non-null, otherwise defaultValue */ public static T defaultIfNull(@Nullable T value, T defaultValue) { - if (value == null) { - return defaultValue; - } - return value; + return value == null ? defaultValue : value; } /** diff --git a/spec/src/main/java/io/a2a/util/package-info.java b/spec/src/main/java/io/a2a/util/package-info.java new file mode 100644 index 000000000..769e16337 --- /dev/null +++ b/spec/src/main/java/io/a2a/util/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.util; + +import org.jspecify.annotations.NullMarked; + diff --git a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java index 0adedad9a..d1f5af5a6 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentExecutorProducer.java @@ -8,7 +8,7 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; import io.a2a.spec.Task; import io.a2a.spec.TaskNotCancelableError; import io.a2a.spec.TaskState; @@ -27,7 +27,7 @@ public AgentExecutor agentExecutor() { private static class FireAndForgetAgentExecutor implements AgentExecutor { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { Task task = context.getTask(); if (task == null) { @@ -70,7 +70,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { System.out.println("====> task cancel request received"); Task task = context.getTask(); if (task == null) { diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index da377b10a..870c74fcc 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -30,6 +30,11 @@ ${project.groupId} a2a-java-sdk-server-common + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + jakarta.ws.rs jakarta.ws.rs-api diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java index c456ff3b1..c64e5051c 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/A2AGsonObjectMapper.java @@ -4,8 +4,8 @@ */ package io.a2a.server.apps.common; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.restassured.mapper.ObjectMapper; import io.restassured.mapper.ObjectMapperDeserializationContext; import io.restassured.mapper.ObjectMapperSerializationContext; diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 8d52b61b1..de1240c98 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1,5 +1,6 @@ package io.a2a.server.apps.common; +import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -40,8 +41,13 @@ import io.a2a.client.config.ClientConfig; import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.json.JsonProcessingException; -import io.a2a.json.JsonUtil; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; +import io.a2a.jsonrpc.common.wrappers.A2AErrorResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; +import io.a2a.jsonrpc.common.wrappers.StreamingJSONRPCRequest; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; @@ -53,7 +59,6 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCErrorResponse; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; @@ -62,9 +67,6 @@ import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.StreamingJSONRPCRequest; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskIdParams; @@ -269,7 +271,7 @@ public void testListTasksSuccess() throws Exception { try { // Test listing all tasks (no filters) io.a2a.spec.ListTasksParams params = ListTasksParams.builder().tenant("").build(); - io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + ListTasksResult result = getClient().listTasks(params); assertNotNull(result); assertNotNull(result.tasks()); @@ -311,7 +313,7 @@ public void testListTasksFilterByContextId() throws Exception { .contextId("context-filter-1") .tenant("") .build(); - io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + ListTasksResult result = getClient().listTasks(params); assertNotNull(result); assertNotNull(result.tasks()); @@ -352,7 +354,7 @@ public void testListTasksFilterByStatus() throws Exception { .status(TaskState.WORKING) .tenant("") .build(); - io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + ListTasksResult result = getClient().listTasks(params); assertNotNull(result); assertNotNull(result.tasks()); @@ -397,7 +399,7 @@ public void testListTasksWithPagination() throws Exception { .tenant("") .pageSize(2) .build(); - io.a2a.spec.ListTasksResult result1 = getClient().listTasks(params1); + ListTasksResult result1 = getClient().listTasks(params1); assertNotNull(result1); assertEquals(2, result1.tasks().size(), "First page should have 2 tasks"); @@ -411,7 +413,7 @@ public void testListTasksWithPagination() throws Exception { .pageSize(2) .pageToken(result1.nextPageToken()) .build(); - io.a2a.spec.ListTasksResult result2 = getClient().listTasks(params2); + ListTasksResult result2 = getClient().listTasks(params2); assertNotNull(result2); assertTrue(result2.tasks().size() >= 1, "Second page should have at least 1 task"); @@ -447,7 +449,7 @@ public void testListTasksWithHistoryLimit() throws Exception { .tenant("") .historyLength(2) .build(); - io.a2a.spec.ListTasksResult result = getClient().listTasks(params); + ListTasksResult result = getClient().listTasks(params); assertNotNull(result); assertEquals(1, result.tasks().size()); @@ -1419,7 +1421,7 @@ public void testMalformedJSONRPCRequest() { // missing closing bracket String malformedRequest = "{\"jsonrpc\": \"2.0\", \"method\": \"message/send\", \"params\": {\"foo\": \"bar\"}"; - JSONRPCErrorResponse response = given() + A2AErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(malformedRequest) .when() @@ -1427,7 +1429,7 @@ public void testMalformedJSONRPCRequest() { .then() .statusCode(200) .extract() - .as(JSONRPCErrorResponse.class); + .as(A2AErrorResponse.class); assertNotNull(response.getError()); assertEquals(new JSONParseError().getCode(), response.getError().getCode()); } @@ -1450,7 +1452,7 @@ public void testInvalidParamsJSONRPCRequest() { } private void testInvalidParams(String invalidParamsRequest) { - JSONRPCErrorResponse response = given() + A2AErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(invalidParamsRequest) .when() @@ -1458,7 +1460,7 @@ private void testInvalidParams(String invalidParamsRequest) { .then() .statusCode(200) .extract() - .as(JSONRPCErrorResponse.class); + .as(A2AErrorResponse.class); assertNotNull(response.getError()); assertEquals(new InvalidParamsError().getCode(), response.getError().getCode()); assertEquals("1", response.getId().toString()); @@ -1476,7 +1478,7 @@ public void testInvalidJSONRPCRequestMissingJsonrpc() { "params": {} } """; - JSONRPCErrorResponse response = given() + A2AErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(invalidRequest) .when() @@ -1484,7 +1486,7 @@ public void testInvalidJSONRPCRequestMissingJsonrpc() { .then() .statusCode(200) .extract() - .as(JSONRPCErrorResponse.class); + .as(A2AErrorResponse.class); assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } @@ -1498,7 +1500,7 @@ public void testInvalidJSONRPCRequestMissingMethod() { String invalidRequest = """ {"jsonrpc": "2.0", "params": {}} """; - JSONRPCErrorResponse response = given() + A2AErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(invalidRequest) .when() @@ -1506,7 +1508,7 @@ public void testInvalidJSONRPCRequestMissingMethod() { .then() .statusCode(200) .extract() - .as(JSONRPCErrorResponse.class); + .as(A2AErrorResponse.class); assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } @@ -1520,7 +1522,7 @@ public void testInvalidJSONRPCRequestInvalidId() { String invalidRequest = """ {"jsonrpc": "2.0", "method": "SendMessage", "params": {}, "id": {"bad": "type"}} """; - JSONRPCErrorResponse response = given() + A2AErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(invalidRequest) .when() @@ -1528,7 +1530,7 @@ public void testInvalidJSONRPCRequestInvalidId() { .then() .statusCode(200) .extract() - .as(JSONRPCErrorResponse.class); + .as(A2AErrorResponse.class); assertNotNull(response.getError()); assertEquals(new InvalidRequestError().getCode(), response.getError().getCode()); } @@ -1542,7 +1544,7 @@ public void testInvalidJSONRPCRequestNonExistentMethod() { String invalidRequest = """ {"jsonrpc": "2.0", "id":"5", "method" : "nonexistent/method", "params": {}} """; - JSONRPCErrorResponse response = given() + A2AErrorResponse response = given() .contentType(MediaType.APPLICATION_JSON) .body(invalidRequest) .when() @@ -1550,7 +1552,7 @@ public void testInvalidJSONRPCRequestNonExistentMethod() { .then() .statusCode(200) .extract() - .as(JSONRPCErrorResponse.class); + .as(A2AErrorResponse.class); assertNotNull(response.getError()); assertEquals(new MethodNotFoundError().getCode(), response.getError().getCode()); } @@ -1694,7 +1696,7 @@ private CompletableFuture>> initialiseStreamingReque .build(); String body = ""; if (request instanceof SendStreamingMessageRequest streamingRequest) { - body = JSONRPCUtils.toJsonRPCRequest((String) streamingRequest.getId(), SendStreamingMessageRequest.METHOD, ProtoUtils.ToProto.sendMessageRequest(streamingRequest.getParams())); + body = JSONRPCUtils.toJsonRPCRequest((String) streamingRequest.getId(), SEND_STREAMING_MESSAGE_METHOD, ProtoUtils.ToProto.sendMessageRequest(streamingRequest.getParams())); } // Create the request diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java index 48daf8517..a77bd30f1 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java @@ -1,15 +1,15 @@ package io.a2a.server.apps.common; +import java.util.List; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; -import java.util.List; - import io.a2a.server.agentexecution.AgentExecutor; import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; -import io.a2a.spec.JSONRPCError; +import io.a2a.spec.A2AError; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import io.quarkus.arc.profile.IfBuildProfile; @@ -22,7 +22,7 @@ public class AgentExecutorProducer { public AgentExecutor agentExecutor() { return new AgentExecutor() { @Override - public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { TaskUpdater updater = new TaskUpdater(context, eventQueue); String taskId = context.getTaskId(); @@ -50,7 +50,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws JSONRP } @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError { + public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { if (context.getTask().id().equals("cancel-task-123")) { TaskUpdater taskUpdater = new TaskUpdater(context, eventQueue); taskUpdater.cancel(); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java index 7cc62dcfe..9b8214ed7 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestHttpClient.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -13,10 +14,9 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpResponse; -import io.a2a.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.Task; -import io.a2a.json.JsonUtil; -import java.util.Map; @Dependent @Alternative diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 6fdf4fc97..6bbb973a4 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -32,6 +32,11 @@ ${project.groupId} a2a-java-sdk-spec-grpc + + ${project.groupId} + a2a-java-sdk-jsonrpc-common + ${project.version} + com.google.protobuf protobuf-java diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 1df1409f3..261ee14a8 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -3,8 +3,6 @@ import static io.a2a.grpc.utils.ProtoUtils.FromProto; import static io.a2a.grpc.utils.ProtoUtils.ToProto; -import jakarta.enterprise.inject.Vetoed; - import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -12,23 +10,24 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; - -import io.grpc.Context; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; +import jakarta.enterprise.inject.Vetoed; + import com.google.protobuf.Empty; import io.a2a.common.A2AErrorMessages; import io.a2a.grpc.A2AServiceGrpc; import io.a2a.grpc.StreamResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.server.AgentCardValidator; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.auth.User; import io.a2a.server.extensions.A2AExtensions; -import io.a2a.transport.grpc.context.GrpcContextKeys; import io.a2a.server.requesthandlers.RequestHandler; +import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -39,7 +38,6 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.MessageSendParams; @@ -53,6 +51,8 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.transport.grpc.context.GrpcContextKeys; +import io.grpc.Context; import io.grpc.Status; import io.grpc.stub.StreamObserver; @@ -81,7 +81,7 @@ public void sendMessage(io.a2a.grpc.SendMessageRequest request, io.a2a.grpc.SendMessageResponse response = ToProto.taskOrMessage(taskOrMessage); responseObserver.onNext(response); responseObserver.onCompleted(); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -103,7 +103,7 @@ public void getTask(io.a2a.grpc.GetTaskRequest request, } else { handleError(responseObserver, new TaskNotFoundError()); } - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -118,10 +118,10 @@ public void listTasks(io.a2a.grpc.ListTasksRequest request, try { ServerCallContext context = createCallContext(responseObserver); io.a2a.spec.ListTasksParams params = FromProto.listTasksParams(request); - io.a2a.spec.ListTasksResult result = getRequestHandler().onListTasks(params, context); + ListTasksResult result = getRequestHandler().onListTasks(params, context); responseObserver.onNext(ToProto.listTasksResult(result)); responseObserver.onCompleted(); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -143,7 +143,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, } else { handleError(responseObserver, new TaskNotFoundError()); } - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -166,7 +166,7 @@ public void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationCon TaskPushNotificationConfig responseConfig = getRequestHandler().onSetTaskPushNotificationConfig(config, context); responseObserver.onNext(ToProto.taskPushNotificationConfig(responseConfig)); responseObserver.onCompleted(); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -189,7 +189,7 @@ public void getTaskPushNotificationConfig(io.a2a.grpc.GetTaskPushNotificationCon TaskPushNotificationConfig config = getRequestHandler().onGetTaskPushNotificationConfig(params, context); responseObserver.onNext(ToProto.taskPushNotificationConfig(config)); responseObserver.onCompleted(); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -213,7 +213,7 @@ public void listTaskPushNotificationConfig(io.a2a.grpc.ListTaskPushNotificationC io.a2a.grpc.ListTaskPushNotificationConfigResponse response = ToProto.listTaskPushNotificationConfigResponse(result); responseObserver.onNext(response); responseObserver.onCompleted(); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -235,7 +235,7 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, MessageSendParams params = FromProto.messageSendParams(request); Flow.Publisher publisher = getRequestHandler().onMessageSendStream(params, context); convertToStreamResponse(publisher, responseObserver); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -257,7 +257,7 @@ public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, TaskIdParams params = FromProto.taskIdParams(request); Flow.Publisher publisher = getRequestHandler().onResubscribeToTask(params, context); convertToStreamResponse(publisher, responseObserver); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -297,7 +297,7 @@ public void onNext(StreamingEventKind event) { @Override public void onError(Throwable throwable) { - if (throwable instanceof JSONRPCError jsonrpcError) { + if (throwable instanceof A2AError jsonrpcError) { handleError(responseObserver, jsonrpcError); } else { handleInternalError(responseObserver, throwable); @@ -339,7 +339,7 @@ public void deleteTaskPushNotificationConfig(io.a2a.grpc.DeleteTaskPushNotificat // void response responseObserver.onNext(Empty.getDefaultInstance()); responseObserver.onCompleted(); - } catch (JSONRPCError e) { + } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { handleSecurityException(responseObserver, e); @@ -405,7 +405,7 @@ private ServerCallContext createCallContext(StreamObserver responseObserv } } - private void handleError(StreamObserver responseObserver, JSONRPCError error) { + private void handleError(StreamObserver responseObserver, A2AError error) { Status status; String description; if (error instanceof InvalidRequestError) { diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index ff165a563..0d8f6fb95 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -25,11 +25,11 @@ import io.a2a.grpc.SendMessageResponse; import io.a2a.grpc.SetTaskPushNotificationConfigRequest; import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.SubscribeToTaskRequest; import io.a2a.grpc.Task; import io.a2a.grpc.TaskPushNotificationConfig; import io.a2a.grpc.TaskState; import io.a2a.grpc.TaskStatus; -import io.a2a.grpc.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.events.EventConsumer; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index e5cf67dec..c7fa3d9f2 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -30,6 +30,11 @@ io.github.a2asdk a2a-java-sdk-spec + + io.github.a2asdk + a2a-java-sdk-jsonrpc-common + ${project.version} + com.google.protobuf protobuf-java-util diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 5c097c04c..6e78f84aa 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -2,55 +2,54 @@ import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.inject.Instance; -import jakarta.inject.Inject; - -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Flow; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import io.a2a.jsonrpc.common.wrappers.CancelTaskRequest; +import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; +import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; +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.GetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksRequest; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; +import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.AgentCardValidator; import io.a2a.server.ExtendedAgentCard; import io.a2a.server.PublicAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.requesthandlers.RequestHandler; +import io.a2a.server.util.async.Internal; +import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.EventKind; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.ListTaskPushNotificationConfigResult; -import io.a2a.spec.ListTasksRequest; -import io.a2a.spec.ListTasksResponse; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.PushNotificationNotSupportedError; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.server.util.async.Internal; -import io.a2a.spec.SubscribeToTaskRequest; import mutiny.zero.ZeroPublisher; import org.jspecify.annotations.Nullable; @@ -82,7 +81,7 @@ public SendMessageResponse onMessageSend(SendMessageRequest request, ServerCallC try { EventKind taskOrMessage = requestHandler.onMessageSend(request.getParams(), context); return new SendMessageResponse(request.getId(), taskOrMessage); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new SendMessageResponse(request.getId(), e); } catch (Throwable t) { return new SendMessageResponse(request.getId(), new InternalError(t.getMessage())); @@ -105,7 +104,7 @@ public Flow.Publisher onMessageSendStream( // We can't use the convertingProcessor convenience method since that propagates any errors as an error handled // via Subscriber.onError() rather than as part of the SendStreamingResponse payload return convertToSendStreamingMessageResponse(request.getId(), publisher); - } catch (JSONRPCError e) { + } catch (A2AError e) { return ZeroPublisher.fromItems(new SendStreamingMessageResponse(request.getId(), e)); } catch (Throwable throwable) { return ZeroPublisher.fromItems(new SendStreamingMessageResponse(request.getId(), new InternalError(throwable.getMessage()))); @@ -119,7 +118,7 @@ public CancelTaskResponse onCancelTask(CancelTaskRequest request, ServerCallCont return new CancelTaskResponse(request.getId(), task); } return new CancelTaskResponse(request.getId(), new TaskNotFoundError()); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new CancelTaskResponse(request.getId(), e); } catch (Throwable t) { return new CancelTaskResponse(request.getId(), new InternalError(t.getMessage())); @@ -141,7 +140,7 @@ public Flow.Publisher onSubscribeToTask( // We can't use the convertingProcessor convenience method since that propagates any errors as an error handled // via Subscriber.onError() rather than as part of the SendStreamingResponse payload return convertToSendStreamingMessageResponse(request.getId(), publisher); - } catch (JSONRPCError e) { + } catch (A2AError e) { return ZeroPublisher.fromItems(new SendStreamingMessageResponse(request.getId(), e)); } catch (Throwable throwable) { return ZeroPublisher.fromItems(new SendStreamingMessageResponse(request.getId(), new InternalError(throwable.getMessage()))); @@ -158,7 +157,7 @@ public GetTaskPushNotificationConfigResponse getPushNotificationConfig( TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(request.getParams(), context); return new GetTaskPushNotificationConfigResponse(request.getId(), config); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new GetTaskPushNotificationConfigResponse(request.getId().toString(), e); } catch (Throwable t) { return new GetTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); @@ -175,7 +174,7 @@ public SetTaskPushNotificationConfigResponse setPushNotificationConfig( TaskPushNotificationConfig config = requestHandler.onSetTaskPushNotificationConfig(request.getParams(), context); return new SetTaskPushNotificationConfigResponse(request.getId().toString(), config); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new SetTaskPushNotificationConfigResponse(request.getId(), e); } catch (Throwable t) { return new SetTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); @@ -186,7 +185,7 @@ public GetTaskResponse onGetTask(GetTaskRequest request, ServerCallContext conte try { Task task = requestHandler.onGetTask(request.getParams(), context); return new GetTaskResponse(request.getId(), task); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new GetTaskResponse(request.getId(), e); } catch (Throwable t) { return new GetTaskResponse(request.getId(), new InternalError(t.getMessage())); @@ -197,7 +196,7 @@ public ListTasksResponse onListTasks(ListTasksRequest request, ServerCallContext try { ListTasksResult result = requestHandler.onListTasks(request.getParams(), context); return new ListTasksResponse(request.getId(), result); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new ListTasksResponse(request.getId(), e); } catch (Throwable t) { return new ListTasksResponse(request.getId(), new InternalError(t.getMessage())); @@ -214,7 +213,7 @@ public ListTaskPushNotificationConfigResponse listPushNotificationConfig( ListTaskPushNotificationConfigResult result = requestHandler.onListTaskPushNotificationConfig(request.getParams(), context); return new ListTaskPushNotificationConfigResponse(request.getId(), result); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new ListTaskPushNotificationConfigResponse(request.getId(), e); } catch (Throwable t) { return new ListTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); @@ -230,7 +229,7 @@ public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig( try { requestHandler.onDeleteTaskPushNotificationConfig(request.getParams(), context); return new DeleteTaskPushNotificationConfigResponse(request.getId()); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new DeleteTaskPushNotificationConfigResponse(request.getId(), e); } catch (Throwable t) { return new DeleteTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); @@ -246,7 +245,7 @@ public GetAuthenticatedExtendedCardResponse onGetAuthenticatedExtendedCardReques } try { return new GetAuthenticatedExtendedCardResponse(request.getId(), extendedAgentCard.get()); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new GetAuthenticatedExtendedCardResponse(request.getId(), e); } catch (Throwable t) { return new GetAuthenticatedExtendedCardResponse(request.getId(), new InternalError(t.getMessage())); @@ -281,7 +280,7 @@ public void onNext(StreamingEventKind item) { @Override public void onError(Throwable throwable) { - if (throwable instanceof JSONRPCError jsonrpcError) { + if (throwable instanceof A2AError jsonrpcError) { tube.send(new SendStreamingMessageResponse(requestId, jsonrpcError)); } else { tube.send( diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index acf62dcb7..68fc677fe 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -17,6 +17,25 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import io.a2a.jsonrpc.common.wrappers.CancelTaskRequest; +import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; +import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; +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.GetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; @@ -27,34 +46,16 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.Artifact; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; -import io.a2a.spec.CancelTaskRequest; -import io.a2a.spec.CancelTaskResponse; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; -import io.a2a.spec.DeleteTaskPushNotificationConfigRequest; -import io.a2a.spec.DeleteTaskPushNotificationConfigResponse; import io.a2a.spec.Event; -import io.a2a.spec.GetAuthenticatedExtendedCardRequest; -import io.a2a.spec.GetAuthenticatedExtendedCardResponse; import io.a2a.spec.GetTaskPushNotificationConfigParams; -import io.a2a.spec.GetTaskPushNotificationConfigRequest; -import io.a2a.spec.GetTaskPushNotificationConfigResponse; -import io.a2a.spec.GetTaskRequest; -import io.a2a.spec.GetTaskResponse; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigRequest; -import io.a2a.spec.ListTaskPushNotificationConfigResponse; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.PushNotificationNotSupportedError; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import io.a2a.spec.SendStreamingMessageRequest; -import io.a2a.spec.SendStreamingMessageResponse; -import io.a2a.spec.SetTaskPushNotificationConfigRequest; -import io.a2a.spec.SetTaskPushNotificationConfigResponse; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -62,7 +63,6 @@ import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; -import io.a2a.spec.SubscribeToTaskRequest; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 749f97771..d39b64648 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -30,6 +30,11 @@ io.github.a2asdk a2a-java-sdk-spec + + io.github.a2asdk + a2a-java-sdk-jsonrpc-common + ${project.version} + ${project.groupId} a2a-java-sdk-server-common diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index ea4d62ec7..ab64cf167 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -3,26 +3,34 @@ import static io.a2a.server.util.async.AsyncUtils.createTubeConfig; import static io.a2a.spec.A2AErrorCodes.JSON_PARSE_ERROR_CODE; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.jsonrpc.common.json.JsonUtil; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.server.AgentCardValidator; import io.a2a.server.ExtendedAgentCard; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -import java.time.Instant; -import java.time.format.DateTimeParseException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Flow; - import io.a2a.server.PublicAgentCard; import io.a2a.server.ServerCallContext; import io.a2a.server.requesthandlers.RequestHandler; +import io.a2a.server.util.async.Internal; +import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; import io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError; import io.a2a.spec.ContentTypeNotSupportedError; @@ -34,11 +42,9 @@ import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; -import io.a2a.spec.JSONRPCError; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.ListTasksParams; -import io.a2a.spec.ListTasksResult; import io.a2a.spec.MethodNotFoundError; import io.a2a.spec.PushNotificationNotSupportedError; import io.a2a.spec.StreamingEventKind; @@ -50,13 +56,6 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.server.util.async.Internal; -import io.a2a.json.JsonUtil; -import jakarta.enterprise.inject.Instance; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.logging.Level; -import java.util.logging.Logger; import mutiny.zero.ZeroPublisher; import org.jspecify.annotations.Nullable; @@ -100,7 +99,7 @@ public HTTPRestResponse sendMessage(String body, String tenant, ServerCallContex request.setTenant(tenant); EventKind result = requestHandler.onMessageSend(ProtoUtils.FromProto.messageSendParams(request), context); return createSuccessResponse(200, io.a2a.grpc.SendMessageResponse.newBuilder(ProtoUtils.ToProto.taskOrMessage(result))); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -117,7 +116,7 @@ public HTTPRestResponse sendStreamingMessage(String body, String tenant, ServerC request.setTenant(tenant); Flow.Publisher publisher = requestHandler.onMessageSendStream(ProtoUtils.FromProto.messageSendParams(request), context); return createStreamingResponse(publisher); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(e).toJson())); } catch (Throwable throwable) { return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(new InternalError(throwable.getMessage())).toJson())); @@ -135,7 +134,7 @@ public HTTPRestResponse cancelTask(String taskId, String tenant, ServerCallConte return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); } throw new UnsupportedOperationError(); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -152,7 +151,7 @@ public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, Stri builder.setTenant(tenant); TaskPushNotificationConfig result = requestHandler.onSetTaskPushNotificationConfig(ProtoUtils.FromProto.setTaskPushNotificationConfig(builder), context); return createSuccessResponse(201, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(result))); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -167,7 +166,7 @@ public HTTPRestResponse subscribeToTask(String taskId, String tenant, ServerCall TaskIdParams params = new TaskIdParams(taskId, tenant); Flow.Publisher publisher = requestHandler.onResubscribeToTask(params, context); return createStreamingResponse(publisher); - } catch (JSONRPCError e) { + } catch (A2AError e) { return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(e).toJson())); } catch (Throwable throwable) { return new HTTPRestStreamingResponse(ZeroPublisher.fromItems(new HTTPRestErrorResponse(new InternalError(throwable.getMessage())).toJson())); @@ -182,7 +181,7 @@ public HTTPRestResponse getTask(String taskId, @Nullable Integer historyLength, return createSuccessResponse(200, io.a2a.grpc.Task.newBuilder(ProtoUtils.ToProto.task(task))); } throw new TaskNotFoundError(); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -230,7 +229,7 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s ListTasksResult result = requestHandler.onListTasks(params, context); return createSuccessResponse(200, io.a2a.grpc.ListTasksResponse.newBuilder(ProtoUtils.ToProto.listTasksResult(result))); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -245,7 +244,7 @@ public HTTPRestResponse getTaskPushNotificationConfiguration(String taskId, @Nul GetTaskPushNotificationConfigParams params = new GetTaskPushNotificationConfigParams(taskId, configId, tenant); TaskPushNotificationConfig config = requestHandler.onGetTaskPushNotificationConfig(params, context); return createSuccessResponse(200, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(config))); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -260,7 +259,7 @@ public HTTPRestResponse listTaskPushNotificationConfigurations(String taskId, in ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, pageSize, pageToken, tenant); ListTaskPushNotificationConfigResult result = requestHandler.onListTaskPushNotificationConfig(params, context); return createSuccessResponse(200, io.a2a.grpc.ListTaskPushNotificationConfigResponse.newBuilder(ProtoUtils.ToProto.listTaskPushNotificationConfigResponse(result))); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); @@ -275,14 +274,14 @@ public HTTPRestResponse deleteTaskPushNotificationConfiguration(String taskId, S DeleteTaskPushNotificationConfigParams params = new DeleteTaskPushNotificationConfigParams(taskId, configId, tenant); requestHandler.onDeleteTaskPushNotificationConfig(params, context); return new HTTPRestResponse(204, "application/json", ""); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable throwable) { return createErrorResponse(new InternalError(throwable.getMessage())); } } - private void parseRequestBody(String body, com.google.protobuf.Message.Builder builder) throws JSONRPCError { + private void parseRequestBody(String body, com.google.protobuf.Message.Builder builder) throws A2AError { try { if (body == null || body.trim().isEmpty()) { throw new InvalidRequestError("Request body is required"); @@ -313,12 +312,12 @@ private HTTPRestResponse createSuccessResponse(int statusCode, com.google.protob } } - public HTTPRestResponse createErrorResponse(JSONRPCError error) { + public HTTPRestResponse createErrorResponse(A2AError error) { int statusCode = mapErrorToHttpStatus(error); return createErrorResponse(statusCode, error); } - private HTTPRestResponse createErrorResponse(int statusCode, JSONRPCError error) { + private HTTPRestResponse createErrorResponse(int statusCode, A2AError error) { String jsonBody = new HTTPRestErrorResponse(error).toJson(); return new HTTPRestResponse(statusCode, "application/json", jsonBody); } @@ -358,7 +357,7 @@ public void onNext(StreamingEventKind item) { @Override public void onError(Throwable throwable) { - if (throwable instanceof JSONRPCError jsonrpcError) { + if (throwable instanceof A2AError jsonrpcError) { tube.send(new HTTPRestErrorResponse(jsonrpcError).toJson()); } else { tube.send(new HTTPRestErrorResponse(new InternalError(throwable.getMessage())).toJson()); @@ -375,7 +374,7 @@ public void onComplete() { }); } - private int mapErrorToHttpStatus(JSONRPCError error) { + private int mapErrorToHttpStatus(A2AError error) { if (error instanceof InvalidRequestError || error instanceof JSONParseError) { return 400; } @@ -409,7 +408,7 @@ public HTTPRestResponse getExtendedAgentCard(String tenant) { throw new AuthenticatedExtendedCardNotConfiguredError(null, "Authenticated Extended Card not configured", null); } return new HTTPRestResponse(200, "application/json", JsonUtil.toJson(extendedAgentCard.get())); - } catch (JSONRPCError e) { + } catch (A2AError e) { return createErrorResponse(e); } catch (Throwable t) { return createErrorResponse(500, new InternalError(t.getMessage())); @@ -474,7 +473,7 @@ private static class HTTPRestErrorResponse { private final @Nullable String message; - private HTTPRestErrorResponse(JSONRPCError jsonRpcError) { + private HTTPRestErrorResponse(A2AError jsonRpcError) { this.error = jsonRpcError.getClass().getName(); this.message = jsonRpcError.getMessage(); } diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 5b309116d..9dd4e9cdb 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -1,19 +1,19 @@ package io.a2a.transport.rest.handler; -import com.google.protobuf.InvalidProtocolBufferException; import java.util.HashSet; import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Flow; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import com.google.protobuf.InvalidProtocolBufferException; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; +import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.AgentCard; import io.a2a.spec.Task; -import io.a2a.server.tasks.TaskUpdater; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Flow; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; From 31884790bfb899693c046570c11c60027692cd61 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 23 Dec 2025 15:54:48 +0000 Subject: [PATCH 241/493] chore: Add proper testing of -P release of pull requests (#559) Remove the -Pjavadoc profile and workaround --- .github/workflows/build-and-test.yml | 2 +- .../build-with-release-profile-run.yml | 108 ++++++++++++++++++ .../workflows/build-with-release-profile.yml | 71 +++++------- pom.xml | 14 --- 4 files changed, 139 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/build-with-release-profile-run.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 4b5e89f8c..e2fb7253d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -26,7 +26,7 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven and run tests - run: mvn -Pjavadoc -B package --file pom.xml -fae + run: mvn -B package --file pom.xml -fae - name: Upload Test Reports if: failure() uses: actions/upload-artifact@v4 diff --git a/.github/workflows/build-with-release-profile-run.yml b/.github/workflows/build-with-release-profile-run.yml new file mode 100644 index 000000000..245bb5edb --- /dev/null +++ b/.github/workflows/build-with-release-profile-run.yml @@ -0,0 +1,108 @@ +name: Build with '-Prelease' (Run) + +# Workflow_run job for release profile build verification. +# This workflow has access to secrets and runs the actual build. +# Triggered by build-with-release-profile.yml completion. +# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests + +on: + workflow_run: + workflows: ["Build with '-Prelease' (Trigger)"] + types: + - completed + +permissions: {} + +jobs: + build: + # Only run for successful trigger workflow from main repository + if: > + ${{ github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.repository.full_name == 'a2aproject/a2a-java' }} + runs-on: ubuntu-latest + permissions: + contents: read + actions: read # Required to download artifacts + + steps: + - name: Download PR info + uses: actions/download-artifact@v4 + with: + name: pr-info + github-token: ${{ github.token }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Extract PR info + id: pr_info + run: | + if [ -f pr_number ]; then + PR_NUMBER=$(cat pr_number) + echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT + echo "PR Number: ${PR_NUMBER}" + else + echo "No PR number (push event)" + fi + + PR_SHA=$(cat pr_sha) + echo "pr_sha=${PR_SHA}" >> $GITHUB_OUTPUT + echo "PR SHA: ${PR_SHA}" + + PR_REF=$(cat pr_ref) + echo "pr_ref=${PR_REF}" >> $GITHUB_OUTPUT + echo "PR Ref: ${PR_REF}" + + - name: Checkout PR code + uses: actions/checkout@v4 + with: + # Checkout the exact commit from the PR (or push) + # This is safe because the workflow code (this file) is always from main + ref: ${{ steps.pr_info.outputs.pr_sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Use secrets to import GPG key + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} + passphrase: ${{ secrets.GPG_SIGNING_PASSPHRASE }} + + # Create settings.xml for Maven since it needs the 'central-a2asdk-temp' server. + # Populate with username and password from secrets + - name: Create settings.xml + run: | + mkdir -p ~/.m2 + echo "central-a2asdk-temp${{ secrets.CENTRAL_TOKEN_USERNAME }}${{ secrets.CENTRAL_TOKEN_PASSWORD }}" > ~/.m2/settings.xml + + # Build with the same settings as the deploy job + # -s uses the settings file we created. + - name: Build with same arguments as deploy job + run: > + mvn -B install + -s ~/.m2/settings.xml + -P release + -DskipTests + -Drelease.auto.publish=true + env: + # GPG passphrase is set as an environment variable for the gpg plugin to use + GPG_PASSPHRASE: ${{ secrets.GPG_SIGNING_PASSPHRASE }} + + - name: Build Summary + if: always() + run: | + if [ "${{ job.status }}" = "success" ]; then + echo "✅ Release profile build succeeded" + if [ -n "${{ steps.pr_info.outputs.pr_number }}" ]; then + echo " PR #${{ steps.pr_info.outputs.pr_number }} is ready for release" + fi + else + echo "❌ Release profile build failed" + if [ -n "${{ steps.pr_info.outputs.pr_number }}" ]; then + echo " PR #${{ steps.pr_info.outputs.pr_number }} has release profile issues" + fi + fi diff --git a/.github/workflows/build-with-release-profile.yml b/.github/workflows/build-with-release-profile.yml index 129833307..0f4c05687 100644 --- a/.github/workflows/build-with-release-profile.yml +++ b/.github/workflows/build-with-release-profile.yml @@ -1,12 +1,13 @@ -name: Build with '-Prelease' - -# Simply runs the build with -Prelease to avoid nasty surprises when running the release-to-maven-central workflow. +name: Build with '-Prelease' (Trigger) +# Trigger workflow for release profile build verification. +# This workflow runs on PRs and uploads the PR info for the workflow_run job. +# The actual build with secrets happens in build-with-release-profile-run.yml +# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests on: - # Handle all branches for now + pull_request: # Changed from pull_request_target for security push: - pull_request_target: workflow_dispatch: # Only run the latest job @@ -15,7 +16,7 @@ concurrency: cancel-in-progress: true jobs: - build: + trigger: # Only run this job for the main repository, not for forks if: github.repository == 'a2aproject/a2a-java' runs-on: ubuntu-latest @@ -23,39 +24,27 @@ jobs: contents: read steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: maven - - # Use secrets to import GPG key - - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v6 - with: - gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }} - passphrase: ${{ secrets.GPG_SIGNING_PASSPHRASE }} - - # Create settings.xml for Maven since it needs the 'central-a2asdk-temp' server. - # Populate wqith username and password from secrets - - name: Create settings.xml + - name: Prepare PR info run: | - mkdir -p ~/.m2 - echo "central-a2asdk-temp${{ secrets.CENTRAL_TOKEN_USERNAME }}${{ secrets.CENTRAL_TOKEN_PASSWORD }}" > ~/.m2/settings.xml - - # Build with the same settings as the deploy job - # -s uses the settings file we created. - - name: Build with same arguments as deploy job - run: > - mvn -B install - -s ~/.m2/settings.xml - -P release - -DskipTests - -Drelease.auto.publish=true - env: - # GPG passphrase is set as an environment variable for the gpg plugin to use - GPG_PASSPHRASE: ${{ secrets.GPG_SIGNING_PASSPHRASE }} \ No newline at end of file + mkdir -p pr_info + + # Store PR number for workflow_run job + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo ${{ github.event.number }} > pr_info/pr_number + echo ${{ github.event.pull_request.head.sha }} > pr_info/pr_sha + echo ${{ github.event.pull_request.head.ref }} > pr_info/pr_ref + else + # For push events, store the commit sha + echo ${{ github.sha }} > pr_info/pr_sha + echo ${{ github.ref }} > pr_info/pr_ref + fi + + echo "Event: ${{ github.event_name }}" + cat pr_info/* + + - name: Upload PR info + uses: actions/upload-artifact@v4 + with: + name: pr-info + path: pr_info/ + retention-days: 1 diff --git a/pom.xml b/pom.xml index 21ee72eb3..8242e9462 100644 --- a/pom.xml +++ b/pom.xml @@ -511,20 +511,6 @@ - - - javadoc - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - - - com.fasterxml.jackson.core - jackson-databind - org.slf4j slf4j-api diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java index 30527b797..87c10fb4e 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java @@ -1,9 +1,5 @@ package io.a2a.extras.queuemanager.replicated.core; -import com.fasterxml.jackson.annotation.JsonGetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonSetter; import io.a2a.server.events.EventQueueItem; import io.a2a.spec.A2AError; import io.a2a.spec.Event; @@ -12,10 +8,8 @@ public class ReplicatedEventQueueItem implements EventQueueItem { private String taskId; - @JsonInclude(JsonInclude.Include.NON_NULL) private StreamingEventKind event; - @JsonInclude(JsonInclude.Include.NON_NULL) private A2AError error; private boolean closedEvent; @@ -71,13 +65,10 @@ public void setTaskId(String taskId) { * Get the StreamingEventKind event field (for JSON serialization). * @return the StreamingEventKind event or null */ - @JsonGetter("event") - @JsonInclude(JsonInclude.Include.NON_NULL) public StreamingEventKind getStreamingEvent() { return event; } - @JsonSetter("event") public void setEvent(StreamingEventKind event) { this.event = event; this.error = null; // Clear error when setting event @@ -87,13 +78,10 @@ public void setEvent(StreamingEventKind event) { * Get the A2AError field (for JSON serialization). * @return the A2AError or null */ - @JsonGetter("error") - @JsonInclude(JsonInclude.Include.NON_NULL) public A2AError getErrorObject() { return error; } - @JsonSetter("error") public void setError(A2AError error) { this.error = error; this.event = null; // Clear event when setting error @@ -104,7 +92,6 @@ public void setError(A2AError error) { * This is the method required by the EventQueueItem interface. * @return the event (StreamingEventKind, A2AError, or QueueClosedEvent) or null if none is set */ - @JsonIgnore @Override public Event getEvent() { if (closedEvent) { @@ -120,7 +107,6 @@ public Event getEvent() { * Indicates this is a replicated event (implements EventQueueItem). * @return always true for replicated events */ - @JsonIgnore @Override public boolean isReplicated() { return true; @@ -147,7 +133,6 @@ public boolean hasError() { * For JSON serialization. * @return true if this is a queue closed event */ - @JsonGetter("closedEvent") public boolean isClosedEvent() { return closedEvent; } @@ -156,7 +141,6 @@ public boolean isClosedEvent() { * Set the closed event flag (for JSON deserialization). * @param closedEvent true if this is a queue closed event */ - @JsonSetter("closedEvent") public void setClosedEvent(boolean closedEvent) { this.closedEvent = closedEvent; if (closedEvent) { diff --git a/server-common/pom.xml b/server-common/pom.xml index e41442c96..1f8633c77 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -38,14 +38,6 @@ ${project.groupId} a2a-java-sdk-client-transport-jsonrpc - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - io.smallrye.reactive mutiny-zero diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 42987ee78..685de4977 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -56,10 +56,6 @@ mockito-core test - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - com.google.protobuf protobuf-java-util From cf823530287e1dcdf00b2de73bbc11401c3b06a1 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 9 Jan 2026 17:11:18 +0000 Subject: [PATCH 254/493] feat: Add nooargs constructors so we work in Jakarta (#580) --- .../core/ReplicatedQueueManager.java | 20 +++++++++-- .../server/events/InMemoryQueueManager.java | 19 ++++++++-- .../DefaultRequestHandler.java | 35 ++++++++++++++----- .../tasks/BasePushNotificationSender.java | 20 +++++++++-- .../transport/grpc/handler/GrpcHandler.java | 2 +- .../jsonrpc/handler/JSONRPCHandler.java | 19 +++++++++- .../transport/rest/handler/RestHandler.java | 11 +++++- 7 files changed, 108 insertions(+), 18 deletions(-) diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index 88c0c75ab..586ab11a7 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -24,12 +24,26 @@ public class ReplicatedQueueManager implements QueueManager { private static final Logger LOGGER = LoggerFactory.getLogger(ReplicatedQueueManager.class); - private final InMemoryQueueManager delegate; - + // Fields set by constructor injection cannot be final. We need a noargs constructor for + // Jakarta compatibility, and it seems that making fields set by constructor injection + // final, is not proxyable in all runtimes + private InMemoryQueueManager delegate; private ReplicationStrategy replicationStrategy; - private TaskStateProvider taskStateProvider; + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ + @SuppressWarnings("NullAway") + protected ReplicatedQueueManager() { + // For CDI proxy creation + this.delegate = null; + this.replicationStrategy = null; + this.taskStateProvider = null; + } + @Inject public ReplicatedQueueManager(ReplicationStrategy replicationStrategy, TaskStateProvider taskStateProvider) { this.replicationStrategy = replicationStrategy; diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index 19179f50d..e5a17e0e7 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -16,8 +16,23 @@ public class InMemoryQueueManager implements QueueManager { private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryQueueManager.class); private final ConcurrentMap queues = new ConcurrentHashMap<>(); - private final EventQueueFactory factory; - private final TaskStateProvider taskStateProvider; + // Fields set by constructor injection cannot be final. We need a noargs constructor for + // Jakarta compatibility, and it seems that making fields set by constructor injection + // final, is not proxyable in all runtimes + private EventQueueFactory factory; + private TaskStateProvider taskStateProvider; + + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ + @SuppressWarnings("NullAway") + protected InMemoryQueueManager() { + // For CDI proxy creation + this.factory = null; + this.taskStateProvider = null; + } @Inject public InMemoryQueueManager(TaskStateProvider taskStateProvider) { diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 61af1d43e..e19f7920d 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -56,7 +56,6 @@ import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.PushNotificationNotSupportedError; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskIdParams; @@ -208,17 +207,37 @@ public class DefaultRequestHandler implements RequestHandler { */ int consumptionCompletionTimeoutSeconds; - private final AgentExecutor agentExecutor; - private final TaskStore taskStore; - private final QueueManager queueManager; - private final PushNotificationConfigStore pushConfigStore; - private final PushNotificationSender pushSender; - private final Supplier requestContextBuilder; + // Fields set by constructor injection cannot be final. We need a noargs constructor for + // Jakarta compatibility, and it seems that making fields set by constructor injection + // final, is not proxyable in all runtimes + private AgentExecutor agentExecutor; + private TaskStore taskStore; + private QueueManager queueManager; + private PushNotificationConfigStore pushConfigStore; + private PushNotificationSender pushSender; + private Supplier requestContextBuilder; private final ConcurrentMap> runningAgents = new ConcurrentHashMap<>(); private final Set> backgroundTasks = ConcurrentHashMap.newKeySet(); - private final Executor executor; + private Executor executor; + + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ + @SuppressWarnings("NullAway") + protected DefaultRequestHandler() { + // For CDI proxy creation + this.agentExecutor = null; + this.taskStore = null; + this.queueManager = null; + this.pushConfigStore = null; + this.pushSender = null; + this.requestContextBuilder = null; + this.executor = null; + } @Inject public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index 35a248373..a59767d8f 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -27,8 +27,24 @@ public class BasePushNotificationSender implements PushNotificationSender { private static final Logger LOGGER = LoggerFactory.getLogger(BasePushNotificationSender.class); - private final A2AHttpClient httpClient; - private final PushNotificationConfigStore configStore; + // Fields set by constructor injection cannot be final. We need a noargs constructor for + // Jakarta compatibility, and it seems that making fields set by constructor injection + // final, is not proxyable in all runtimes + private A2AHttpClient httpClient; + private PushNotificationConfigStore configStore; + + + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ + @SuppressWarnings("NullAway") + protected BasePushNotificationSender() { + // For CDI proxy creation + this.httpClient = null; + this.configStore = null; + } @Inject public BasePushNotificationSender(PushNotificationConfigStore configStore) { diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 7a1e0a912..2998965ea 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -67,7 +67,7 @@ public abstract class GrpcHandler extends A2AServiceGrpc.A2AServiceImplBase { // Without this we get intermittent failures private static volatile Runnable streamingSubscribedRunnable; - private AtomicBoolean initialised = new AtomicBoolean(false); + private final AtomicBoolean initialised = new AtomicBoolean(false); private static final Logger LOGGER = Logger.getLogger(GrpcHandler.class.getName()); diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 06de11dcd..ca8149099 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -60,10 +60,27 @@ @ApplicationScoped public class JSONRPCHandler { + // Fields set by constructor injection cannot be final. We need a noargs constructor for + // Jakarta compatibility, and it seems that making fields set by constructor injection + // final, is not proxyable in all runtimes private AgentCard agentCard; private @Nullable Instance extendedAgentCard; private RequestHandler requestHandler; - private final Executor executor; + private Executor executor; + + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ + @SuppressWarnings("NullAway") + protected JSONRPCHandler() { + // For CDI proxy creation + this.agentCard = null; + this.extendedAgentCard = null; + this.requestHandler = null; + this.executor = null; + } @Inject public JSONRPCHandler(@PublicAgentCard AgentCard agentCard, @Nullable @ExtendedAgentCard Instance extendedAgentCard, diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 8c4050fd9..489bf909b 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -67,11 +67,20 @@ public class RestHandler { private static final Logger log = Logger.getLogger(RestHandler.class.getName()); + + // Fields set by constructor injection cannot be final. We need a noargs constructor for + // Jakarta compatibility, and it seems that making fields set by constructor injection + // final, is not proxyable in all runtimes private AgentCard agentCard; private @Nullable Instance extendedAgentCard; private RequestHandler requestHandler; - private final Executor executor; + private Executor executor; + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ @SuppressWarnings("NullAway") protected RestHandler() { // For CDI From 00e60938d8c9608e67ac6250fc5904bb0acd515e Mon Sep 17 00:00:00 2001 From: "Haeyoung,Jeong" Date: Wed, 14 Jan 2026 00:11:41 +0900 Subject: [PATCH 255/493] fix: handle listTasks status wire strings (#578) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What changed - Parse status in REST listTasks using TaskState.fromString - Return InvalidParamsError (422) for invalid status values - Add tests for valid wire strings and invalid status Why - REST API expects wire strings like working / input-required, but valueOf only accepts enum names, causing valid requests to return 500. Tests ```bash mvn -pl transport/rest -am test ``` --- # Description Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [x] Make your Pull Request title in the specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [x] Ensure the tests pass - [x] Appropriate READMEs were updated (if necessary) Fixes #577 🦕 --- .../transport/rest/handler/RestHandler.java | 18 ++++++++++++- .../rest/handler/RestHandlerTest.java | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 489bf909b..87e57f415 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -5,6 +5,7 @@ import java.time.Instant; import java.time.format.DateTimeParseException; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -12,6 +13,7 @@ import java.util.concurrent.Flow; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Instance; @@ -217,7 +219,21 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s paramsBuilder.contextId(contextId); } if (status != null) { - paramsBuilder.status(TaskState.valueOf(status)); + try { + paramsBuilder.status(TaskState.fromString(status)); + } catch (IllegalArgumentException e) { + try { + paramsBuilder.status(TaskState.valueOf(status)); + } catch (IllegalArgumentException valueOfError) { + String validStates = Arrays.stream(TaskState.values()) + .map(TaskState::asString) + .collect(Collectors.joining(", ")); + Map errorData = new HashMap<>(); + errorData.put("parameter", "status"); + errorData.put("reason", "Must be one of: " + validStates); + throw new InvalidParamsError(null, "Invalid params", errorData); + } + } } if (pageSize != null) { paramsBuilder.pageSize(pageSize); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index df2a5e7af..0487a6f54 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -58,6 +58,31 @@ public void testGetTaskNotFound() { Assertions.assertTrue(response.getBody().contains("TaskNotFoundError")); } + @Test + public void testListTasksStatusWireString() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + taskStore.save(MINIMAL_TASK); + + RestHandler.HTTPRestResponse response = handler.listTasks(null, "submitted", null, null, + null, null, null, "", callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id())); + } + + @Test + public void testListTasksInvalidStatus() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + + RestHandler.HTTPRestResponse response = handler.listTasks(null, "not-a-status", null, null, + null, null, null, "", callContext); + + Assertions.assertEquals(422, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("InvalidParamsError")); + } + @Test public void testSendMessage() throws InvalidProtocolBufferException { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); From 3947f268d7860dceff1f971840b398d85ea1a938 Mon Sep 17 00:00:00 2001 From: CHU Minh Toan <145227498+cmtoan@users.noreply.github.com> Date: Tue, 13 Jan 2026 16:12:22 +0100 Subject: [PATCH 256/493] fix: Event consumer should stop on input_required (#566) # Description Event consumer should stop on input_required task as fixed in the python version https://github.com/a2aproject/a2a-python/pull/167 --- .../io/a2a/server/events/EventConsumer.java | 20 +++- .../a2a/server/events/EventConsumerTest.java | 112 ++++++++---------- 2 files changed, 70 insertions(+), 62 deletions(-) diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index 7f1cbd4cc..d4fe5b395 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -6,6 +6,7 @@ import io.a2a.spec.Event; import io.a2a.spec.Message; import io.a2a.spec.Task; +import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; import mutiny.zero.BackpressureStrategy; import mutiny.zero.TubeConfiguration; @@ -77,7 +78,7 @@ public Flow.Publisher consumeAll() { } else if (event instanceof Message) { isFinalEvent = true; } else if (event instanceof Task task) { - isFinalEvent = task.status().state().isFinal(); + isFinalEvent = isStreamTerminatingTask(task); } else if (event instanceof QueueClosedEvent) { // Poison pill event - signals queue closure from remote node // Do NOT send to subscribers - just close the queue @@ -94,7 +95,7 @@ public Flow.Publisher consumeAll() { } if (isFinalEvent) { - LOGGER.debug("Final event detected, closing queue and breaking loop for queue {}", System.identityHashCode(queue)); + LOGGER.debug("Final or interrupted event detected, closing queue and breaking loop for queue {}", System.identityHashCode(queue)); queue.close(); LOGGER.debug("Queue closed, breaking loop for queue {}", System.identityHashCode(queue)); break; @@ -120,6 +121,21 @@ public Flow.Publisher consumeAll() { }); } + /** + * Determines if a task is in a state for terminating the stream. + *

    A task is terminating if:

    + *
      + *
    • Its state is final (e.g., completed, canceled, rejected, failed), OR
    • + *
    • Its state is interrupted (e.g., input-required)
    • + *
    + * @param task the task to check + * @return true if the task has a final state or an interrupted state, false otherwise + */ + private boolean isStreamTerminatingTask(Task task) { + TaskState state = task.status().state(); + return state.isFinal() || state == TaskState.INPUT_REQUIRED; + } + public EnhancedRunnable.DoneCallback createAgentRunnableDoneCallback() { return agentRunnable -> { if (agentRunnable.getError() != null) { diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 6114e8f21..4354f1639 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -114,32 +114,7 @@ public void testConsumeAllMultipleEvents() throws JsonProcessingException { final List receivedEvents = new ArrayList<>(); final AtomicReference error = new AtomicReference<>(); - publisher.subscribe(new Flow.Subscriber<>() { - private Flow.Subscription subscription; - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - subscription.request(1); - } - - @Override - public void onNext(EventQueueItem item) { - receivedEvents.add(item.getEvent()); - subscription.request(1); - - } - - @Override - public void onError(Throwable throwable) { - error.set(throwable); - } - - @Override - public void onComplete() { - subscription.cancel(); - } - }); + publisher.subscribe(getSubscriber(receivedEvents, error)); assertNull(error.get()); assertEquals(events.size(), receivedEvents.size()); @@ -175,32 +150,7 @@ public void testConsumeUntilMessage() throws Exception { final List receivedEvents = new ArrayList<>(); final AtomicReference error = new AtomicReference<>(); - publisher.subscribe(new Flow.Subscriber<>() { - private Flow.Subscription subscription; - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - subscription.request(1); - } - - @Override - public void onNext(EventQueueItem item) { - receivedEvents.add(item.getEvent()); - subscription.request(1); - - } - - @Override - public void onError(Throwable throwable) { - error.set(throwable); - } - - @Override - public void onComplete() { - subscription.cancel(); - } - }); + publisher.subscribe(getSubscriber(receivedEvents, error)); assertNull(error.get()); assertEquals(3, receivedEvents.size()); @@ -224,7 +174,55 @@ public void testConsumeMessageEvents() throws Exception { final List receivedEvents = new ArrayList<>(); final AtomicReference error = new AtomicReference<>(); - publisher.subscribe(new Flow.Subscriber<>() { + publisher.subscribe(getSubscriber(receivedEvents, error)); + + assertNull(error.get()); + // The stream is closed after the first Message + assertEquals(1, receivedEvents.size()); + assertSame(message, receivedEvents.get(0)); + } + + @Test + public void testConsumeTaskInputRequired() { + Task task = Task.builder() + .id("task-id") + .contextId("task-context") + .status(new TaskStatus(TaskState.INPUT_REQUIRED)) + .build(); + List events = List.of( + task, + TaskArtifactUpdateEvent.builder() + .taskId("task-123") + .contextId("session-xyz") + .artifact(Artifact.builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + TaskStatusUpdateEvent.builder() + .taskId("task-123") + .contextId("session-xyz") + .status(new TaskStatus(TaskState.WORKING)) + .isFinal(true) + .build()); + for (Event event : events) { + eventQueue.enqueueEvent(event); + } + + Flow.Publisher publisher = eventConsumer.consumeAll(); + final List receivedEvents = new ArrayList<>(); + final AtomicReference error = new AtomicReference<>(); + + publisher.subscribe(getSubscriber(receivedEvents, error)); + + assertNull(error.get()); + // The stream is closed after the input_required task + assertEquals(1, receivedEvents.size()); + assertSame(task, receivedEvents.get(0)); + } + + private Flow.Subscriber getSubscriber(List receivedEvents, AtomicReference error) { + return new Flow.Subscriber<>() { private Flow.Subscription subscription; @Override @@ -237,7 +235,6 @@ public void onSubscribe(Flow.Subscription subscription) { public void onNext(EventQueueItem item) { receivedEvents.add(item.getEvent()); subscription.request(1); - } @Override @@ -249,12 +246,7 @@ public void onError(Throwable throwable) { public void onComplete() { subscription.cancel(); } - }); - - assertNull(error.get()); - // The stream is closed after the first Message - assertEquals(1, receivedEvents.size()); - assertSame(message, receivedEvents.get(0)); + }; } @Test From 56ac4bec1a9f48e4f0991df51fcb8799f63c71b3 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Tue, 13 Jan 2026 17:00:56 +0000 Subject: [PATCH 257/493] chore: Use default protocol version in tests (#582) --- spec/src/main/java/io/a2a/util/Utils.java | 5 ----- .../java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java | 4 ---- .../io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java | 4 ---- .../java/io/a2a/transport/rest/handler/RestHandlerTest.java | 4 ---- 4 files changed, 17 deletions(-) diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index ca0610b1f..792365579 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -38,11 +38,6 @@ public class Utils { private static final Logger log = Logger.getLogger(Utils.class.getName()); - /** - * Specification version constant for deprecation annotations. - */ - public static final String SPEC_VERSION_1_0 = "1.0"; - /** * Returns the provided value if non-null, otherwise returns the default value. *

    diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index ad9b879f9..77a2c955f 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -966,7 +966,6 @@ public void testVersionNotSupportedErrorOnSendMessage() throws Exception { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); // Create handler that provides incompatible version 2.0 in the context @@ -1012,7 +1011,6 @@ public void testVersionNotSupportedErrorOnSendStreamingMessage() throws Exceptio .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); // Create handler that provides incompatible version 2.0 in the context @@ -1058,7 +1056,6 @@ public void testCompatibleVersionSuccess() throws Exception { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); // Create handler that provides compatible version 1.1 in the context @@ -1110,7 +1107,6 @@ public void testNoVersionDefaultsToCurrentVersionSuccess() throws Exception { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); // Create handler that provides null version (should default to 1.0) diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index c8e69f43f..a55233adb 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -1781,7 +1781,6 @@ public void testVersionNotSupportedErrorOnMessageSend() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); JSONRPCHandler handler = new JSONRPCHandler(agentCard, requestHandler, internalExecutor); @@ -1821,7 +1820,6 @@ public void testVersionNotSupportedErrorOnMessageSendStream() throws Exception { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); JSONRPCHandler handler = new JSONRPCHandler(agentCard, requestHandler, internalExecutor); @@ -1898,7 +1896,6 @@ public void testCompatibleVersionSuccess() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); JSONRPCHandler handler = new JSONRPCHandler(agentCard, requestHandler, internalExecutor); @@ -1942,7 +1939,6 @@ public void testNoVersionDefaultsToCurrentVersionSuccess() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); JSONRPCHandler handler = new JSONRPCHandler(agentCard, requestHandler, internalExecutor); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 0487a6f54..1ed1492e1 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -653,7 +653,6 @@ public void testVersionNotSupportedErrorOnSendMessage() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); RestHandler handler = new RestHandler(agentCard, requestHandler, internalExecutor); @@ -705,7 +704,6 @@ public void testVersionNotSupportedErrorOnSendStreamingMessage() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); RestHandler handler = new RestHandler(agentCard, requestHandler, internalExecutor); @@ -794,7 +792,6 @@ public void testCompatibleVersionSuccess() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); RestHandler handler = new RestHandler(agentCard, requestHandler, internalExecutor); @@ -850,7 +847,6 @@ public void testNoVersionDefaultsToCurrentVersionSuccess() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion("1.0") .build(); RestHandler handler = new RestHandler(agentCard, requestHandler, internalExecutor); From 5938c78271a9f28636d57c7fac77294226e4bf05 Mon Sep 17 00:00:00 2001 From: CHU Minh Toan <145227498+cmtoan@users.noreply.github.com> Date: Wed, 14 Jan 2026 09:46:03 +0100 Subject: [PATCH 258/493] fix: fix flaky test testNonBlockingWithMultipleMessages (#576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description fix flaky test testNonBlockingWithMultipleMessages Fixes #557 🦕 --- .../a2a/server/apps/common/AbstractA2AServerTest.java | 11 ++++++++--- .../a2a/server/apps/common/AgentExecutorProducer.java | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index de1240c98..5fab9d8d7 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1274,9 +1274,11 @@ public void testDeletePushNotificationConfigSetWithoutConfigId() throws Exceptio @Test @Timeout(value = 1, unit = TimeUnit.MINUTES) public void testNonBlockingWithMultipleMessages() throws Exception { + String multiEventTaskId = "multi-event-test-" + java.util.UUID.randomUUID(); + try { // 1. Send first non-blocking message to create task in WORKING state Message message1 = Message.builder(MESSAGE) - .taskId("multi-event-test") + .taskId(multiEventTaskId) .contextId("test-context") .parts(new TextPart("First request")) .build(); @@ -1301,7 +1303,7 @@ public void testNonBlockingWithMultipleMessages() throws Exception { assertTrue(firstTaskLatch.await(10, TimeUnit.SECONDS)); String taskId = taskIdRef.get(); assertNotNull(taskId); - assertEquals("multi-event-test", taskId); + assertEquals(multiEventTaskId, taskId); // 2. Resubscribe to task (queue should still be open) CountDownLatch resubEventLatch = new CountDownLatch(2); // artifact-2 + completion @@ -1337,7 +1339,7 @@ public void testNonBlockingWithMultipleMessages() throws Exception { // 3. Send second streaming message to same taskId Message message2 = Message.builder(MESSAGE) - .taskId("multi-event-test") // Same taskId + .taskId(multiEventTaskId) // Same taskId .contextId("test-context") .parts(new TextPart("Second request")) .build(); @@ -1411,6 +1413,9 @@ public void testNonBlockingWithMultipleMessages() throws Exception { assertEquals("artifact-2", streamArtifact.artifact().artifactId()); assertEquals("Second message artifact", ((TextPart) streamArtifact.artifact().parts().get(0)).text()); + } finally { + deleteTaskInTaskStore(multiEventTaskId); + } } @Test diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java index a77bd30f1..8b03a68f4 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java @@ -27,7 +27,7 @@ public void execute(RequestContext context, EventQueue eventQueue) throws A2AErr String taskId = context.getTaskId(); // Special handling for multi-event test - if ("multi-event-test".equals(taskId)) { + if (taskId != null && taskId.startsWith("multi-event-test")) { // First call: context.getTask() == null (new task) if (context.getTask() == null) { updater.startWork(); From 7f5c6c87a1ef0c575840eaf019212c0d514be3d6 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Thu, 15 Jan 2026 15:24:23 +0100 Subject: [PATCH 259/493] fix: Fix Security JSON serialization (#592) fix: Use GSON serialization for JSON-RPC Agent card endpoint fix: Fix Security JSON serialization Fixes #590 & #591 --------- Signed-off-by: Jeff Mesnil --- .../auth/AuthInterceptorTest.java | 6 +- .../io/a2a/jsonrpc/common/json/JsonUtil.java | 232 ++++++++++++--- .../json/SecuritySchemeSerializationTest.java | 276 ++++++++++++++++++ .../server/apps/quarkus/A2AServerRoutes.java | 5 +- .../io/a2a/spec/APIKeySecurityScheme.java | 7 +- .../io/a2a/spec/HTTPAuthSecurityScheme.java | 7 +- .../io/a2a/spec/MutualTLSSecurityScheme.java | 8 +- .../io/a2a/spec/OAuth2SecurityScheme.java | 9 +- .../a2a/spec/OpenIdConnectSecurityScheme.java | 7 +- .../main/java/io/a2a/spec/SecurityScheme.java | 5 + 10 files changed, 510 insertions(+), 52 deletions(-) create mode 100644 jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java index 0aeacff66..a9f07c83e 100644 --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java @@ -77,7 +77,7 @@ public void testAPIKeySecurityScheme() { AuthTestCase authTestCase = new AuthTestCase( "http://agent.com/rpc", "session-id", - APIKeySecurityScheme.API_KEY, + APIKeySecurityScheme.TYPE, "secret-api-key", new APIKeySecurityScheme(APIKeySecurityScheme.Location.HEADER, "x-api-key", "API Key authentication"), "x-api-key", @@ -91,7 +91,7 @@ public void testOAuth2SecurityScheme() { AuthTestCase authTestCase = new AuthTestCase( "http://agent.com/rpc", "session-id", - OAuth2SecurityScheme.OAUTH2, + OAuth2SecurityScheme.TYPE, "secret-oauth-access-token", new OAuth2SecurityScheme(OAuthFlows.builder().build(), "OAuth2 authentication", null), "Authorization", @@ -105,7 +105,7 @@ public void testOidcSecurityScheme() { AuthTestCase authTestCase = new AuthTestCase( "http://agent.com/rpc", "session-id", - OpenIdConnectSecurityScheme.OPENID_CONNECT, + OpenIdConnectSecurityScheme.TYPE, "secret-oidc-id-token", new OpenIdConnectSecurityScheme("http://provider.com/.well-known/openid-configuration", "OIDC authentication"), "Authorization", diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java index c6de924bf..c0aea8ad2 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java @@ -12,6 +12,10 @@ import static io.a2a.spec.A2AErrorCodes.TASK_NOT_CANCELABLE_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.TASK_NOT_FOUND_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.UNSUPPORTED_OPERATION_ERROR_CODE; +import static io.a2a.spec.DataPart.DATA; +import static io.a2a.spec.FilePart.FILE; +import static io.a2a.spec.TextPart.TEXT; +import static java.lang.String.format; import java.io.StringReader; import java.lang.reflect.Type; @@ -19,6 +23,7 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Map; +import java.util.Set; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -29,21 +34,28 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; + import io.a2a.spec.A2AError; +import io.a2a.spec.APIKeySecurityScheme; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DataPart; import io.a2a.spec.FileContent; import io.a2a.spec.FilePart; import io.a2a.spec.FileWithBytes; import io.a2a.spec.FileWithUri; +import io.a2a.spec.HTTPAuthSecurityScheme; import io.a2a.spec.InvalidAgentResponseError; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.JSONParseError; import io.a2a.spec.Message; import io.a2a.spec.MethodNotFoundError; +import io.a2a.spec.MutualTLSSecurityScheme; +import io.a2a.spec.OAuth2SecurityScheme; +import io.a2a.spec.OpenIdConnectSecurityScheme; import io.a2a.spec.Part; import io.a2a.spec.PushNotificationNotSupportedError; +import io.a2a.spec.SecurityScheme; import io.a2a.spec.StreamingEventKind; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -53,6 +65,7 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; + import org.jspecify.annotations.Nullable; /** @@ -83,6 +96,7 @@ private static GsonBuilder createBaseGsonBuilder() { public static final Gson OBJECT_MAPPER = createBaseGsonBuilder() .registerTypeHierarchyAdapter(Part.class, new PartTypeAdapter()) .registerTypeHierarchyAdapter(StreamingEventKind.class, new StreamingEventKindTypeAdapter()) + .registerTypeHierarchyAdapter(SecurityScheme.class, new SecuritySchemeTypeAdapter()) .create(); /** @@ -530,6 +544,8 @@ public void write(JsonWriter out, Message.Role value) throws java.io.IOException */ static class PartTypeAdapter extends TypeAdapter> { + private static final Set VALID_KEYS = Set.of(TEXT, FILE, DATA); + // Create separate Gson instance without the Part adapter to avoid recursion private final Gson delegateGson = createBaseGsonBuilder().create(); @@ -539,21 +555,20 @@ public void write(JsonWriter out, Part value) throws java.io.IOException { out.nullValue(); return; } - // Write wrapper object with member name as discriminator out.beginObject(); if (value instanceof TextPart textPart) { // TextPart: { "text": "value" } - direct string value - out.name("text"); + out.name(TEXT); out.value(textPart.text()); } else if (value instanceof FilePart filePart) { // FilePart: { "file": {...} } - out.name("file"); + out.name(FILE); delegateGson.toJson(filePart.file(), FileContent.class, out); } else if (value instanceof DataPart dataPart) { // DataPart: { "data": {...} } - out.name("data"); + out.name(DATA); delegateGson.toJson(dataPart.data(), Map.class, out); } else { throw new JsonSyntaxException("Unknown Part subclass: " + value.getClass().getName()); @@ -579,23 +594,27 @@ Part read(JsonReader in) throws java.io.IOException { com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); // Check for member name discriminators (v1.0 protocol) - if (jsonObject.has("text")) { - // TextPart: { "text": "value" } - direct string value - return new TextPart(jsonObject.get("text").getAsString()); - } else if (jsonObject.has("file")) { - // FilePart: { "file": {...} } - return new FilePart(delegateGson.fromJson(jsonObject.get("file"), FileContent.class)); - } else if (jsonObject.has("data")) { - // DataPart: { "data": {...} } - @SuppressWarnings("unchecked") - Map dataMap = delegateGson.fromJson( - jsonObject.get("data"), - new TypeToken>(){}.getType() - ); - return new DataPart(dataMap); - } else { - throw new JsonSyntaxException("Part must have one of: text, file, data (found: " + jsonObject.keySet() + ")"); - } + Set keys = jsonObject.keySet(); + if (keys.size() != 1) { + throw new JsonSyntaxException(format("Part object must have exactly one key, which must be one of: %s (found: %s)", VALID_KEYS, keys)); + } + + String discriminator = keys.iterator().next(); + + return switch (discriminator) { + case TEXT -> new TextPart(jsonObject.get(TEXT).getAsString()); + case FILE -> new FilePart(delegateGson.fromJson(jsonObject.get(FILE), FileContent.class)); + case DATA -> { + @SuppressWarnings("unchecked") + Map dataMap = delegateGson.fromJson( + jsonObject.get(DATA), + new TypeToken>(){}.getType() + ); + yield new DataPart(dataMap); + } + default -> + throw new JsonSyntaxException(format("Part must have one of: %s (found: %s)", VALID_KEYS, discriminator)); + }; } } @@ -627,20 +646,10 @@ public void write(JsonWriter out, StreamingEventKind value) throws java.io.IOExc out.nullValue(); return; } - // Write wrapper object with member name as discriminator out.beginObject(); - - Type type = switch (value.kind()) { - case Task.STREAMING_EVENT_ID -> Task.class; - case Message.STREAMING_EVENT_ID -> Message.class; - case TaskStatusUpdateEvent.STREAMING_EVENT_ID -> TaskStatusUpdateEvent.class; - case TaskArtifactUpdateEvent.STREAMING_EVENT_ID -> TaskArtifactUpdateEvent.class; - default -> throw new JsonSyntaxException("Unknown StreamingEventKind implementation: " + value.getClass().getName()); - }; - out.name(value.kind()); - delegateGson.toJson(value, type, out); + delegateGson.toJson(value, value.getClass(), out); out.endObject(); } @@ -714,7 +723,9 @@ StreamingEventKind read(JsonReader in) throws java.io.IOException { static class FileContentTypeAdapter extends TypeAdapter { // Create separate Gson instance without the FileContent adapter to avoid recursion - private final Gson delegateGson = new Gson(); + private final Gson delegateGson = new GsonBuilder() + .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter()) + .create(); @Override public void write(JsonWriter out, FileContent value) throws java.io.IOException { @@ -723,13 +734,7 @@ public void write(JsonWriter out, FileContent value) throws java.io.IOException return; } // Delegate to Gson's default serialization for the concrete type - if (value instanceof FileWithBytes fileWithBytes) { - delegateGson.toJson(fileWithBytes, FileWithBytes.class, out); - } else if (value instanceof FileWithUri fileWithUri) { - delegateGson.toJson(fileWithUri, FileWithUri.class, out); - } else { - throw new JsonSyntaxException("Unknown FileContent implementation: " + value.getClass().getName()); - } + delegateGson.toJson(value, value.getClass(), out); } @Override @@ -759,4 +764,151 @@ FileContent read(JsonReader in) throws java.io.IOException { } } + /** + * Gson TypeAdapter for serializing and deserializing {@link APIKeySecurityScheme.Location} enum. + *

    + * This adapter ensures that Location enum values are serialized using their + * wire format string representation (e.g., "header") rather than + * the Java enum constant name (e.g., "HEADER"). + *

    + * For serialization, it uses {@link APIKeySecurityScheme.Location#asString()} to get the wire format. + * For deserialization, it uses {@link APIKeySecurityScheme.Location#fromString(String)} to parse the + * wire format back to the enum constant. + * + * @see APIKeySecurityScheme.Location + */ + static class APIKeyLocationTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, APIKeySecurityScheme.Location value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.asString()); + } + + @Override + public APIKeySecurityScheme.@Nullable Location read(JsonReader in) throws java.io.IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String locationString = in.nextString(); + try { + return APIKeySecurityScheme.Location.fromString(locationString); + } catch (IllegalArgumentException e) { + throw new JsonSyntaxException("Invalid APIKeySecurityScheme.Location: " + locationString, e); + } + } + } + + /** + * Gson TypeAdapter for serializing and deserializing {@link SecurityScheme} and its implementations. + *

    + * This adapter handles polymorphic deserialization for the sealed SecurityScheme interface, + * which permits five implementations: + *

      + *
    • {@link APIKeySecurityScheme} - API key authentication
    • + *
    • {@link HTTPAuthSecurityScheme} - HTTP authentication (basic or bearer)
    • + *
    • {@link OAuth2SecurityScheme} - OAuth 2.0 flows
    • + *
    • {@link OpenIdConnectSecurityScheme} - OpenID Connect discovery
    • + *
    • {@link MutualTLSSecurityScheme} - Client certificate authentication
    • + *
    + *

    + * The adapter uses a wrapper object with the security scheme type as the discriminator field. + * Each SecurityScheme is serialized as a JSON object with a single field whose name identifies + * the security scheme type. + *

    + * Serialization format examples: + *

    {@code
    +     * // HTTPAuthSecurityScheme
    +     * {
    +     *   "httpAuthSecurityScheme": {
    +     *     "scheme": "bearer",
    +     *     "bearerFormat": "JWT",
    +     *     "description": "..."
    +     *   }
    +     * }
    +     *
    +     * // APIKeySecurityScheme
    +     * {
    +     *   "apiKeySecurityScheme": {
    +     *     "location": "header",
    +     *     "name": "X-API-Key",
    +     *     "description": "..."
    +     *   }
    +     * }
    +     * }
    + * + * @see SecurityScheme + * @see APIKeySecurityScheme + * @see HTTPAuthSecurityScheme + * @see OAuth2SecurityScheme + * @see OpenIdConnectSecurityScheme + * @see MutualTLSSecurityScheme + */ + static class SecuritySchemeTypeAdapter extends TypeAdapter { + + private static final Set VALID_KEYS = Set.of(APIKeySecurityScheme.TYPE, + HTTPAuthSecurityScheme.TYPE, + OAuth2SecurityScheme.TYPE, + OpenIdConnectSecurityScheme.TYPE, + MutualTLSSecurityScheme.TYPE); + + // Create separate Gson instance without the SecurityScheme adapter to avoid recursion + // Register custom adapter for APIKeySecurityScheme.Location enum + private final Gson delegateGson = createBaseGsonBuilder() + .registerTypeAdapter(APIKeySecurityScheme.Location.class, new APIKeyLocationTypeAdapter()) + .create(); + + @Override + public void write(JsonWriter out, SecurityScheme value) throws java.io.IOException { + if (value == null) { + out.nullValue(); + return; + } + + // Write wrapper object with member name as discriminator + out.beginObject(); + out.name(value.type()); + delegateGson.toJson(value, value.getClass(), out); + out.endObject(); + } + + @Override + public @Nullable + SecurityScheme read(JsonReader in) throws java.io.IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + // Read the JSON as a tree to inspect the member name discriminator + com.google.gson.JsonElement jsonElement = com.google.gson.JsonParser.parseReader(in); + if (!jsonElement.isJsonObject()) { + throw new JsonSyntaxException("SecurityScheme must be a JSON object"); + } + + com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject(); + + // Check for member name discriminators + Set keys = jsonObject.keySet(); + if (keys.size() != 1) { + throw new JsonSyntaxException(format("A SecurityScheme object must have exactly one key, which must be one of: %s (found: %s)", VALID_KEYS, keys)); + } + + String discriminator = keys.iterator().next(); + com.google.gson.JsonElement nestedObject = jsonObject.get(discriminator); + + return switch (discriminator) { + case APIKeySecurityScheme.TYPE -> delegateGson.fromJson(nestedObject, APIKeySecurityScheme.class); + case HTTPAuthSecurityScheme.TYPE -> delegateGson.fromJson(nestedObject, HTTPAuthSecurityScheme.class); + case OAuth2SecurityScheme.TYPE -> delegateGson.fromJson(nestedObject, OAuth2SecurityScheme.class); + case OpenIdConnectSecurityScheme.TYPE -> delegateGson.fromJson(nestedObject, OpenIdConnectSecurityScheme.class); + case MutualTLSSecurityScheme.TYPE -> delegateGson.fromJson(nestedObject, MutualTLSSecurityScheme.class); + default -> throw new JsonSyntaxException(format("Unknown SecurityScheme type. Must be one of: %s (found: %s)", VALID_KEYS, discriminator)); + }; + } + } } diff --git a/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java new file mode 100644 index 000000000..bbc297baa --- /dev/null +++ b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java @@ -0,0 +1,276 @@ +package io.a2a.jsonrpc.common.json; + +import static io.a2a.spec.APIKeySecurityScheme.Location.COOKIE; +import static io.a2a.spec.APIKeySecurityScheme.Location.HEADER; +import static io.a2a.spec.APIKeySecurityScheme.Location.QUERY; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.a2a.spec.APIKeySecurityScheme; +import io.a2a.spec.HTTPAuthSecurityScheme; +import io.a2a.spec.MutualTLSSecurityScheme; +import io.a2a.spec.OAuth2SecurityScheme; +import io.a2a.spec.OAuthFlows; +import io.a2a.spec.OpenIdConnectSecurityScheme; +import io.a2a.spec.PasswordOAuthFlow; +import io.a2a.spec.SecurityScheme; + +/** + * Tests for SecurityScheme serialization and deserialization using Gson. + */ +class SecuritySchemeSerializationTest { + + @Test + void testHTTPAuthSecuritySchemeSerialization() throws JsonProcessingException { + SecurityScheme scheme = HTTPAuthSecurityScheme.builder() + .scheme("basic") + .description("Basic HTTP authentication") + .build(); + + doTestSecuritySchemeSerialization(scheme, HTTPAuthSecurityScheme.TYPE, Map.of("scheme", "basic")); + + } + + @Test + void testHTTPAuthSecuritySchemeDeserialization() throws JsonProcessingException { + String json = """ + { + "httpAuthSecurityScheme" : { + "scheme": "basic" + } + }"""; + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(HTTPAuthSecurityScheme.class, securityScheme); + HTTPAuthSecurityScheme scheme = (HTTPAuthSecurityScheme) securityScheme; + assertEquals("basic", scheme.scheme()); + assertNull(scheme.bearerFormat()); + } + + @Test + void testHTTPAuthSecuritySchemeWithBearerFormatSerialization() throws JsonProcessingException { + SecurityScheme scheme = HTTPAuthSecurityScheme.builder() + .scheme("bearer") + .bearerFormat("JWT") + .description("JWT bearer token authentication") + .build(); + + doTestSecuritySchemeSerialization(scheme, HTTPAuthSecurityScheme.TYPE, + Map.of("scheme", "bearer", + "bearerFormat", "JWT", + "description", "JWT bearer token authentication")); + } + + @Test + void testHTTPAuthSecuritySchemeWithBearerFormatDeserialization() throws JsonProcessingException { + String json = """ + { + "httpAuthSecurityScheme" : { + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "JWT authentication" + } + }"""; + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(HTTPAuthSecurityScheme.class, securityScheme); + HTTPAuthSecurityScheme scheme = (HTTPAuthSecurityScheme) securityScheme; + assertEquals("bearer", scheme.scheme()); + assertEquals("JWT", scheme.bearerFormat()); + assertEquals("JWT authentication", scheme.description()); + } + + + @Test + void testAPIKeySecuritySchemeSerialization() throws JsonProcessingException { + SecurityScheme scheme = APIKeySecurityScheme.builder() + .location(HEADER) + .name("bar") + .build(); + + doTestSecuritySchemeSerialization(scheme, APIKeySecurityScheme.TYPE, Map.of("location", "header", + "name", "bar")); + } + + @Test + void testAPIKeySecuritySchemeDeserialization() throws JsonProcessingException { + String json = """ + { + "apiKeySecurityScheme" : { + "location": "cookie", + "name": "bar" + } + }"""; + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(APIKeySecurityScheme.class, securityScheme); + APIKeySecurityScheme scheme = (APIKeySecurityScheme) securityScheme; + assertEquals(COOKIE, scheme.location()); + assertEquals("bar", scheme.name()); + } + + @Test + void testAPIKeySecuritySchemeWithDescriptionSerialization() throws JsonProcessingException { + SecurityScheme scheme = APIKeySecurityScheme.builder() + .location(QUERY) + .name("api_key") + .description("API key authentication via query parameter") + .build(); + + doTestSecuritySchemeSerialization(scheme, APIKeySecurityScheme.TYPE, + Map.of("location", "query", + "name", "api_key", + "description", "API key authentication via query parameter")); + } + + @Test + void testOAuth2SecuritySchemeSerialization() throws JsonProcessingException { + PasswordOAuthFlow passwordFlow = new PasswordOAuthFlow( + "https://example.com/oauth/refresh", + Map.of("read", "Read access", "write", "Write access"), + "https://example.com/oauth/token" + ); + + OAuthFlows flows = OAuthFlows.builder() + .password(passwordFlow) + .build(); + + SecurityScheme scheme = OAuth2SecurityScheme.builder() + .flows(flows) + .description("OAuth 2.0 password flow") + .oauth2MetadataUrl("https://example.com/.well-known/oauth-authorization-server") + .build(); + + // Verify serialization with nested OAuth flow fields + String json = JsonUtil.toJson(scheme); + assertNotNull(json); + assertTrue(json.contains(OAuth2SecurityScheme.TYPE)); + assertTrue(json.contains("\"description\":\"OAuth 2.0 password flow\"")); + assertTrue(json.contains("\"oauth2MetadataUrl\":\"https://example.com/.well-known/oauth-authorization-server\"")); + assertTrue(json.contains("\"tokenUrl\":\"https://example.com/oauth/token\"")); + assertTrue(json.contains("\"read\":\"Read access\"")); + + SecurityScheme deserialized = JsonUtil.fromJson(json, SecurityScheme.class); + assertEquals(scheme, deserialized); + } + + @Test + void testOAuth2SecuritySchemeDeserialization() throws JsonProcessingException { + String json = """ + { + "oauth2SecurityScheme" : { + "flows": { + "password": { + "tokenUrl": "https://example.com/oauth/token", + "refreshUrl": "https://example.com/oauth/refresh", + "scopes": { + "read": "Read access", + "write": "Write access" + } + } + }, + "description": "OAuth 2.0 authentication" + } + }"""; + + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(OAuth2SecurityScheme.class, securityScheme); + OAuth2SecurityScheme scheme = (OAuth2SecurityScheme) securityScheme; + assertEquals("OAuth 2.0 authentication", scheme.description()); + assertNotNull(scheme.flows()); + assertNotNull(scheme.flows().password()); + assertEquals("https://example.com/oauth/token", scheme.flows().password().tokenUrl()); + assertEquals("https://example.com/oauth/refresh", scheme.flows().password().refreshUrl()); + assertEquals(2, scheme.flows().password().scopes().size()); + assertEquals("Read access", scheme.flows().password().scopes().get("read")); + } + + @Test + void testOpenIdConnectSecuritySchemeSerialization() throws JsonProcessingException { + SecurityScheme scheme = OpenIdConnectSecurityScheme.builder() + .openIdConnectUrl("https://example.com/.well-known/openid-configuration") + .description("OpenID Connect authentication") + .build(); + + doTestSecuritySchemeSerialization(scheme, OpenIdConnectSecurityScheme.TYPE, + Map.of("openIdConnectUrl", "https://example.com/.well-known/openid-configuration", + "description", "OpenID Connect authentication")); + } + + @Test + void testOpenIdConnectSecuritySchemeDeserialization() throws JsonProcessingException { + String json = """ + { + "openIdConnectSecurityScheme" : { + "openIdConnectUrl": "https://example.com/.well-known/openid-configuration", + "description": "OIDC authentication" + } + }"""; + + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(OpenIdConnectSecurityScheme.class, securityScheme); + OpenIdConnectSecurityScheme scheme = (OpenIdConnectSecurityScheme) securityScheme; + assertEquals("https://example.com/.well-known/openid-configuration", scheme.openIdConnectUrl()); + assertEquals("OIDC authentication", scheme.description()); + } + + @Test + void testMutualTLSSecuritySchemeSerialization() throws JsonProcessingException { + SecurityScheme scheme = new MutualTLSSecurityScheme("Client certificate authentication required"); + + doTestSecuritySchemeSerialization(scheme, MutualTLSSecurityScheme.TYPE, + Map.of("description", "Client certificate authentication required")); + } + + @Test + void testMutualTLSSecuritySchemeDeserialization() throws JsonProcessingException { + String json = """ + { + "mtlsSecurityScheme" : { + "description": "mTLS authentication" + } + }"""; + + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(MutualTLSSecurityScheme.class, securityScheme); + MutualTLSSecurityScheme scheme = (MutualTLSSecurityScheme) securityScheme; + assertEquals("mTLS authentication", scheme.description()); + } + + @Test + void testMutualTLSSecuritySchemeWithNullDescriptionDeserialization() throws JsonProcessingException { + String json = """ + { + "mtlsSecurityScheme" : { + } + }"""; + + SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); + assertInstanceOf(MutualTLSSecurityScheme.class, securityScheme); + MutualTLSSecurityScheme scheme = (MutualTLSSecurityScheme) securityScheme; + assertNull(scheme.description()); + } + + void doTestSecuritySchemeSerialization(SecurityScheme scheme, String schemeType, Map expectedFields) throws JsonProcessingException { + // Serialize to JSON + String json = JsonUtil.toJson(scheme); + + // Verify JSON contains expected fields + assertNotNull(json); + assertTrue(json.contains(schemeType)); + for (Map.Entry entry : expectedFields.entrySet()) { + String expectedField = format("\"%s\":\"%s\"", entry.getKey(), entry.getValue()); + assertTrue(json.contains(expectedField), expectedField + " not found in JSON"); + } + + // Deserialize back to Task + SecurityScheme deserialized = JsonUtil.fromJson(json, SecurityScheme.class); + + assertEquals(scheme, deserialized); + } +} \ No newline at end of file diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 1bf037e7f..9f4b31d83 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -26,6 +26,7 @@ import io.a2a.jsonrpc.common.json.InvalidParamsJsonMappingException; import io.a2a.jsonrpc.common.json.JsonMappingException; import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.jsonrpc.common.json.MethodNotFoundJsonMappingException; import io.a2a.jsonrpc.common.wrappers.A2AErrorResponse; import io.a2a.jsonrpc.common.wrappers.A2ARequest; @@ -156,8 +157,8 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { * @return the agent card */ @Route(path = "/.well-known/agent-card.json", methods = Route.HttpMethod.GET, produces = APPLICATION_JSON) - public AgentCard getAgentCard() { - return jsonRpcHandler.getAgentCard(); + public String getAgentCard() throws JsonProcessingException { + return JsonUtil.toJson(jsonRpcHandler.getAgentCard()); } private A2AResponse processNonStreamingRequest(NonStreamingJSONRPCRequest request, ServerCallContext context) { diff --git a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java index fb72a00e9..ffa886849 100644 --- a/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/APIKeySecurityScheme.java @@ -24,7 +24,12 @@ public record APIKeySecurityScheme( ) implements SecurityScheme { /** The security scheme type identifier for API key authentication. */ - public static final String API_KEY = "apiKey"; + public static final String TYPE = "apiKeySecurityScheme"; + + @Override + public String type() { + return TYPE; + } /** * Compact constructor with validation. diff --git a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java index 7e4a9a968..996a0c0a6 100644 --- a/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/HTTPAuthSecurityScheme.java @@ -42,7 +42,7 @@ public record HTTPAuthSecurityScheme( ) implements SecurityScheme { /** The HTTP security scheme type identifier. */ - public static final String HTTP = "http"; + public static final String TYPE = "httpAuthSecurityScheme"; /** * Compact constructor with validation. @@ -56,6 +56,11 @@ public record HTTPAuthSecurityScheme( Assert.checkNotNullParam("scheme", scheme); } + @Override + public String type() { + return TYPE; + } + /** * Create a new Builder * diff --git a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java index 86480258b..7210e1db7 100644 --- a/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/MutualTLSSecurityScheme.java @@ -27,8 +27,12 @@ public record MutualTLSSecurityScheme(String description) implements SecurityScheme { /** - * The type identifier for mutual TLS security schemes: "mutualTLS". + * The type identifier for mutual TLS security schemes: "mtlsSecurityScheme". */ - public static final String MUTUAL_TLS = "mutualTLS"; + public static final String TYPE = "mtlsSecurityScheme"; + @Override + public String type() { + return TYPE; + } } diff --git a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java index 58d969de2..ffaa323aa 100644 --- a/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OAuth2SecurityScheme.java @@ -26,9 +26,9 @@ public record OAuth2SecurityScheme( ) implements SecurityScheme { /** - * The type identifier for OAuth 2.0 security schemes: "oauth2". + * The type identifier for OAuth 2.0 security schemes: "oauth2SecurityScheme". */ - public static final String OAUTH2 = "oauth2"; + public static final String TYPE = "oauth2SecurityScheme"; /** * Compact constructor with validation. @@ -42,6 +42,11 @@ public record OAuth2SecurityScheme( Assert.checkNotNullParam("flows", flows); } + @Override + public String type() { + return TYPE; + } + /** * Create a new Builder * diff --git a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java index 6f73770ce..63700853a 100644 --- a/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/OpenIdConnectSecurityScheme.java @@ -36,7 +36,7 @@ public record OpenIdConnectSecurityScheme( /** * The type identifier for OpenID Connect security schemes: "openIdConnect". */ - public static final String OPENID_CONNECT = "openIdConnect"; + public static final String TYPE = "openIdConnectSecurityScheme"; /** * Compact constructor with validation. @@ -49,6 +49,11 @@ public record OpenIdConnectSecurityScheme( Assert.checkNotNullParam("openIdConnectUrl", openIdConnectUrl); } + @Override + public String type() { + return TYPE; + } + /** * Create a new Builder * diff --git a/spec/src/main/java/io/a2a/spec/SecurityScheme.java b/spec/src/main/java/io/a2a/spec/SecurityScheme.java index 322262eb9..b481b4c4a 100644 --- a/spec/src/main/java/io/a2a/spec/SecurityScheme.java +++ b/spec/src/main/java/io/a2a/spec/SecurityScheme.java @@ -29,4 +29,9 @@ public sealed interface SecurityScheme permits APIKeySecurityScheme, HTTPAuthSec * @return the description, or null if not provided */ String description(); + + /** + * Returns the type of the security scheme. + */ + String type(); } From 0c48f7238d226125e6d6334a8e92ae9e4ce71351 Mon Sep 17 00:00:00 2001 From: Sathiesh Veera Date: Fri, 16 Jan 2026 04:22:50 -0700 Subject: [PATCH 260/493] feat!: 553:Adding pagination to JpaDatabasePushNotificationConfigStore (#564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description - Added Pagination support to getInfo method in JpaDatabasePushNotificationConfigStore -- Follows the same pattern as that of JpaDatabaseTaskStore - Updated sendNotification(Task) in BasePushNotificationSender to paginate results if a nextToken is present Fixes #553 🦕 --- ...paDatabasePushNotificationConfigStore.java | 119 ++++++------ .../jpa/JpaPushNotificationConfig.java | 19 ++ ...otificationConfigStoreIntegrationTest.java | 176 ++++++++++++++++-- .../src/test/resources/application.properties | 3 + .../tasks/BasePushNotificationSender.java | 28 ++- .../ListTaskPushNotificationConfigParams.java | 12 ++ 6 files changed, 282 insertions(+), 75 deletions(-) diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index ad7b125aa..1df8b57f3 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -1,7 +1,7 @@ package io.a2a.extras.pushnotificationconfigstore.database.jpa; -import java.util.ArrayList; -import java.util.Collections; +import jakarta.persistence.TypedQuery; +import java.time.Instant; import java.util.List; import jakarta.annotation.Priority; @@ -17,6 +17,7 @@ import io.a2a.spec.ListTaskPushNotificationConfigResult; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.TaskPushNotificationConfig; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,7 +27,9 @@ public class JpaDatabasePushNotificationConfigStore implements PushNotificationConfigStore { private static final Logger LOGGER = LoggerFactory.getLogger(JpaDatabasePushNotificationConfigStore.class); - + + private static final Instant NULL_TIMESTAMP_SENTINEL = Instant.EPOCH; + @PersistenceContext(unitName = "a2a-java") EntityManager em; @@ -36,6 +39,8 @@ public PushNotificationConfig setInfo(String taskId, PushNotificationConfig noti // Ensure config has an ID - default to taskId if not provided (mirroring InMemoryPushNotificationConfigStore behavior) PushNotificationConfig.Builder builder = PushNotificationConfig.builder(notificationConfig); if (notificationConfig.id() == null || notificationConfig.id().isEmpty()) { + // This means the taskId and configId are same. This will not allow having multiple configs for a single Task. + // The configId is a required field in the spec and should not be empty builder.id(taskId); } notificationConfig = builder.build(); @@ -72,15 +77,61 @@ public PushNotificationConfig setInfo(String taskId, PushNotificationConfig noti @Override public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConfigParams params) { String taskId = params.id(); - LOGGER.debug("Retrieving PushNotificationConfigs for Task '{}'", taskId); + LOGGER.debug("Retrieving PushNotificationConfigs for Task '{}' with params: pageSize={}, pageToken={}", + taskId, params.pageSize(), params.pageToken()); try { - List jpaConfigs = em.createQuery( - "SELECT c FROM JpaPushNotificationConfig c WHERE c.id.taskId = :taskId", - JpaPushNotificationConfig.class) - .setParameter("taskId", taskId) - .getResultList(); + StringBuilder queryBuilder = new StringBuilder("SELECT c FROM JpaPushNotificationConfig c WHERE c.id.taskId = :taskId"); + + if (params.pageToken() != null && !params.pageToken().isEmpty()) { + String[] tokenParts = params.pageToken().split(":", 2); + if (tokenParts.length == 2) { + // Keyset pagination: get tasks where timestamp < tokenTimestamp OR (timestamp = tokenTimestamp AND id > tokenId) + // All tasks have timestamps (TaskStatus canonical constructor ensures this) + queryBuilder.append(" AND (COALESCE(c.createdAt, :nullSentinel) < :tokenTimestamp OR (COALESCE(c.createdAt, :nullSentinel) = :tokenTimestamp AND c.id.configId > :tokenId))"); + } else { + // Based on the comments in the test case, if the pageToken is invalid start from the beginning. + } + } + + queryBuilder.append(" ORDER BY COALESCE(c.createdAt, :nullSentinel) DESC, c.id.configId ASC"); + + TypedQuery query = em.createQuery(queryBuilder.toString(), JpaPushNotificationConfig.class); + query.setParameter("taskId", taskId); + query.setParameter("nullSentinel", NULL_TIMESTAMP_SENTINEL); + + if (params.pageToken() != null && !params.pageToken().isEmpty()) { + String[] tokenParts = params.pageToken().split(":", 2); + if (tokenParts.length == 2) { + try { + long timestampMillis = Long.parseLong(tokenParts[0]); + String tokenId = tokenParts[1]; + + Instant tokenTimestamp = Instant.ofEpochMilli(timestampMillis); + query.setParameter("tokenTimestamp", tokenTimestamp); + query.setParameter("tokenId", tokenId); + } catch (NumberFormatException e) { + // Malformed timestamp in pageToken + throw new io.a2a.spec.InvalidParamsError(null, + "Invalid pageToken format: timestamp must be numeric milliseconds", null); + } + } + } + + int pageSize = params.getEffectivePageSize(); + query.setMaxResults(pageSize + 1); + List jpaConfigsPage = query.getResultList(); + + String nextPageToken = null; + if (jpaConfigsPage.size() > pageSize) { + // There are more results than the page size, and in this case, a nextToken should be created with the last item. + // Format: "timestamp_millis:taskId" for keyset pagination + jpaConfigsPage = jpaConfigsPage.subList(0, pageSize); + JpaPushNotificationConfig lastConfig = jpaConfigsPage.get(jpaConfigsPage.size() - 1); + Instant timestamp = lastConfig.getCreatedAt() != null ? lastConfig.getCreatedAt() : NULL_TIMESTAMP_SENTINEL; + nextPageToken = timestamp.toEpochMilli() + ":" + lastConfig.getId().getConfigId(); + } - List configs = jpaConfigs.stream() + List configs = jpaConfigsPage.stream() .map(jpaConfig -> { try { return jpaConfig.getConfig(); @@ -95,57 +146,17 @@ public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConf LOGGER.debug("Successfully retrieved {} PushNotificationConfigs for Task '{}'", configs.size(), taskId); - // Handle pagination - if (configs.isEmpty()) { - return new ListTaskPushNotificationConfigResult(Collections.emptyList()); - } - - if (params.pageSize() <= 0) { - return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(configs, params), null); - } - - // Apply pageToken filtering if provided - List paginatedConfigs = configs; - if (params.pageToken() != null && !params.pageToken().isBlank()) { - int index = findFirstIndex(configs, params.pageToken()); - if (index < configs.size()) { - paginatedConfigs = configs.subList(index, configs.size()); - } - } - - // Apply page size limit - if (paginatedConfigs.size() <= params.pageSize()) { - return new ListTaskPushNotificationConfigResult(convertPushNotificationConfig(paginatedConfigs, params), null); - } + List taskPushNotificationConfigs = configs.stream() + .map(config -> new TaskPushNotificationConfig(params.id(), config, params.tenant())) + .collect(Collectors.toList()); - String nextToken = paginatedConfigs.get(params.pageSize()).token(); - return new ListTaskPushNotificationConfigResult( - convertPushNotificationConfig(paginatedConfigs.subList(0, params.pageSize()), params), - nextToken); + return new ListTaskPushNotificationConfigResult(taskPushNotificationConfigs, nextPageToken); } catch (Exception e) { LOGGER.error("Failed to retrieve PushNotificationConfigs for Task '{}'", taskId, e); throw e; } } - private int findFirstIndex(List configs, String token) { - for (int i = 0; i < configs.size(); i++) { - if (token.equals(configs.get(i).token())) { - return i; - } - } - return configs.size(); - } - - private List convertPushNotificationConfig(List pushNotificationConfigList, ListTaskPushNotificationConfigParams params) { - List taskPushNotificationConfigList = new ArrayList<>(pushNotificationConfigList.size()); - for (PushNotificationConfig pushNotificationConfig : pushNotificationConfigList) { - TaskPushNotificationConfig taskPushNotificationConfig = new TaskPushNotificationConfig(params.id(), pushNotificationConfig, params.tenant()); - taskPushNotificationConfigList.add(taskPushNotificationConfig); - } - return taskPushNotificationConfigList; - } - @Transactional @Override public void deleteInfo(String taskId, String configId) { diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java index 468f8eddb..d38b136ca 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfig.java @@ -3,12 +3,14 @@ import jakarta.persistence.Column; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; +import jakarta.persistence.PrePersist; import jakarta.persistence.Table; import jakarta.persistence.Transient; import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.PushNotificationConfig; +import java.time.Instant; @Entity @Table(name = "a2a_push_notification_configs") @@ -19,6 +21,9 @@ public class JpaPushNotificationConfig { @Column(name = "task_data", columnDefinition = "TEXT", nullable = false) private String configJson; + @Column(name = "created_at") + private Instant createdAt; + @Transient private PushNotificationConfig config; @@ -31,6 +36,12 @@ public JpaPushNotificationConfig(TaskConfigId id, String configJson) { this.configJson = configJson; } + @PrePersist + protected void onCreate() { + if (createdAt == null) { + createdAt = Instant.now(); + } + } public TaskConfigId getId() { return id; @@ -60,6 +71,14 @@ public void setConfig(PushNotificationConfig config) throws JsonProcessingExcept this.config = config; } + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + static JpaPushNotificationConfig createFromConfig(String taskId, PushNotificationConfig config) throws JsonProcessingException { String json = JsonUtil.toJson(config); JpaPushNotificationConfig jpaPushNotificationConfig = diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index 9df56edf8..a31beb631 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -200,12 +200,7 @@ private PushNotificationConfig createSamplePushConfig(String url, String configI public void testPaginationWithPageSize() { String taskId = "task_pagination_" + System.currentTimeMillis(); // Create 5 configs - for (int i = 0; i < 5; i++) { - PushNotificationConfig config = createSamplePushConfig( - "http://url" + i + ".com/callback", "cfg" + i, "token" + i); - pushNotificationConfigStore.setInfo(taskId, config); - } - + createSamples(taskId, 5); // Request first page with pageSize=2 ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 2, "", ""); ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); @@ -251,11 +246,13 @@ public void testPaginationWithPageToken() { } // Also verify the pages are sequential (first page ends before second page starts) - // Since configs are created in order, we can verify the IDs - assertEquals("cfg0", firstPageIds.get(0)); - assertEquals("cfg1", firstPageIds.get(1)); + // Since configs are created in order, we can verify the IDs. + // There is no spec about pagination for PushNotifications, hence following the Task List + // behavior by which recent notifications are returned first + assertEquals("cfg4", firstPageIds.get(0)); + assertEquals("cfg3", firstPageIds.get(1)); assertEquals("cfg2", secondPageIds.get(0)); - assertEquals("cfg3", secondPageIds.get(1)); + assertEquals("cfg1", secondPageIds.get(1)); } @Test @@ -409,12 +406,167 @@ public void testPaginationFullIteration() { assertEquals(3, pageCount, "Should have exactly 3 pages (3+3+1)"); } - private void createSamples(String taskId, int size) { - // Create 7 configs + @Test + @Transactional + public void testPageTokenWithNonNumericTimestamp() { + String taskId = "task_malformed_token_" + System.currentTimeMillis(); + createSamples(taskId, 3); + + ListTaskPushNotificationConfigParams params = + new ListTaskPushNotificationConfigParams(taskId, 2, "not_a_number:cfg1", ""); + + assertThrows(io.a2a.spec.InvalidParamsError.class, + () -> pushNotificationConfigStore.getInfo(params), + "Should throw InvalidParamsError for non-numeric timestamp in pageToken"); + } + + @Test + @Transactional + public void testPageTokenWithMissingColon() { + String taskId = "task_missing_colon_" + System.currentTimeMillis(); + createSamples(taskId, 5); + + ListTaskPushNotificationConfigParams params = + new ListTaskPushNotificationConfigParams(taskId, 2, "123456789cfg1", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(2, result.configs().size(), + "Should return first page when pageToken format is invalid (missing colon)"); + assertNotNull(result.nextPageToken(), "Should have nextPageToken since more items exist"); + } + + @Test + @Transactional + public void testPaginationBoundaryExactlyMaxResultsPlusOne() { + String taskId = "task_boundary_" + System.currentTimeMillis(); + + createSamples(taskId, 4); + + ListTaskPushNotificationConfigParams params = + new ListTaskPushNotificationConfigParams(taskId, 4, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertEquals(4, result.configs().size(), + "Should return all 4 configs when pageSize equals total count"); + assertNull(result.nextPageToken(), + "Should not have nextPageToken when count equals pageSize"); + } + + @Test + @Transactional + public void testMultipleTasksDoNotInterfere() { + String taskId1 = "task1_" + System.currentTimeMillis(); + String taskId2 = "task2_" + System.currentTimeMillis(); + + createSamples(taskId1, 3); + createSamples(taskId2, 2); + + ListTaskPushNotificationConfigResult result1 = + pushNotificationConfigStore.getInfo(new ListTaskPushNotificationConfigParams(taskId1)); + ListTaskPushNotificationConfigResult result2 = + pushNotificationConfigStore.getInfo(new ListTaskPushNotificationConfigParams(taskId2)); + + assertEquals(3, result1.configs().size(), "Task1 should have 3 configs"); + assertEquals(2, result2.configs().size(), "Task2 should have 2 configs"); + + List task1Ids = result1.configs().stream() + .map(c -> taskId1 + c.pushNotificationConfig().id()) + .toList(); + List task2Ids = result2.configs().stream() + .map(c -> taskId2 + c.pushNotificationConfig().id()) + .toList(); + + for (String id : task1Ids) { + assertTrue(!task2Ids.contains(id), + "Configs from different tasks should not overlap"); + } + } + + @Test + @Transactional + public void testGetInfoWithNonExistentTaskIdDoesNotThrow() { + String nonExistentTaskId = "non_existent_task_" + System.currentTimeMillis(); + + ListTaskPushNotificationConfigParams params = + new ListTaskPushNotificationConfigParams(nonExistentTaskId, 10, "", ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result, "Result should not be null"); + assertTrue(result.configs().isEmpty(), + "Should return empty list for non-existent task"); + assertNull(result.nextPageToken(), + "Should not have nextPageToken for empty result"); + } + + @Test + @Transactional + public void testGetInfoReturnsTenantFromParams() { + String taskId = "task_tenant_" + System.currentTimeMillis(); + String tenant = "test-tenant-123"; + + PushNotificationConfig config = createSamplePushConfig( + "http://url.com/callback", "cfg1", "token"); + pushNotificationConfigStore.setInfo(taskId, config); + + ListTaskPushNotificationConfigParams params = + new ListTaskPushNotificationConfigParams(taskId, 0, "", tenant); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + assertNotNull(result); + assertEquals(1, result.configs().size()); + assertEquals(tenant, result.configs().get(0).tenant(), + "Tenant from params should be returned in result"); + } + + @Test + @Transactional + public void testPaginationOrderingConsistency() { + String taskId = "task_ordering_consistency_" + System.currentTimeMillis(); + createSamples(taskId, 15); + + List allConfigIds = new java.util.ArrayList<>(); + String pageToken = ""; + int pageCount = 0; + + do { + ListTaskPushNotificationConfigParams params = + new ListTaskPushNotificationConfigParams(taskId, 3, pageToken, ""); + ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); + + result.configs().forEach(c -> + allConfigIds.add(c.pushNotificationConfig().id())); + pageToken = result.nextPageToken(); + pageCount++; + + assertTrue(pageCount <= 20, "Should not have more than 20 pages for 15 configs"); + + } while (pageToken != null); + + assertEquals(15, allConfigIds.size(), "Should retrieve all 15 configs"); + assertEquals(15, new java.util.HashSet<>(allConfigIds).size(), + "All config IDs should be unique - no duplicates"); + + assertEquals("cfg14", allConfigIds.get(0), + "First config should be most recently created (DESC order)"); + assertEquals("cfg0", allConfigIds.get(14), + "Last config should be oldest created"); + } + private void createSamples(String taskId, int size) { + // Create configs with slight delays to ensure unique timestamps for deterministic ordering for (int i = 0; i < size; i++) { PushNotificationConfig config = createSamplePushConfig( "http://url" + i + ".com/callback", "cfg" + i, "token" + i); pushNotificationConfigStore.setInfo(taskId, config); + + // Sleep briefly to ensure each config gets a unique timestamp + // This prevents non-deterministic ordering in pagination tests + try { + Thread.sleep(2); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while creating test samples", e); + } } } } diff --git a/extras/push-notification-config-store-database-jpa/src/test/resources/application.properties b/extras/push-notification-config-store-database-jpa/src/test/resources/application.properties index 1d1592ad9..d9300cd2b 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/resources/application.properties +++ b/extras/push-notification-config-store-database-jpa/src/test/resources/application.properties @@ -9,3 +9,6 @@ quarkus.datasource.password= quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.log.sql=true quarkus.hibernate-orm.log.format-sql=true + +# Transaction timeout (set to 30 minutes for debugging - 1800 seconds) +# quarkus.transaction-manager.default-transaction-timeout=1800s diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index a59767d8f..5f6ddec2e 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -4,13 +4,16 @@ import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE; import static io.a2a.common.A2AHeaders.X_A2A_NOTIFICATION_TOKEN; +import io.a2a.spec.TaskPushNotificationConfig; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.JdkA2AHttpClient; @@ -26,6 +29,7 @@ public class BasePushNotificationSender implements PushNotificationSender { private static final Logger LOGGER = LoggerFactory.getLogger(BasePushNotificationSender.class); + public static final int DEFAULT_PAGE_SIZE = 100; // Fields set by constructor injection cannot be final. We need a noargs constructor for // Jakarta compatibility, and it seems that making fields set by constructor injection @@ -59,12 +63,18 @@ public BasePushNotificationSender(PushNotificationConfigStore configStore, A2AHt @Override public void sendNotification(Task task) { - ListTaskPushNotificationConfigResult pushConfigs = configStore.getInfo(new ListTaskPushNotificationConfigParams(task.id())); - if (pushConfigs == null || pushConfigs.isEmpty()) { - return; - } - - List> dispatchResults = pushConfigs.configs() + List configs = new ArrayList<>(); + String nextPageToken = null; + do { + ListTaskPushNotificationConfigResult pageResult = configStore.getInfo(new ListTaskPushNotificationConfigParams(task.id(), + DEFAULT_PAGE_SIZE, nextPageToken, "")); + if (!pageResult.configs().isEmpty()) { + configs.addAll(pageResult.configs()); + } + nextPageToken = pageResult.nextPageToken(); + } while (nextPageToken != null); + + List> dispatchResults = configs .stream() .map(pushConfig -> dispatch(task, pushConfig.pushNotificationConfig())) .toList(); @@ -73,7 +83,7 @@ public void sendNotification(Task task) { .allMatch(CompletableFuture::join)); try { boolean allSent = dispatchResult.get(); - if (! allSent) { + if (!allSent) { LOGGER.warn("Some push notifications failed to send for taskId: " + task.id()); } } catch (InterruptedException | ExecutionException e) { diff --git a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java index 3c69f7d6e..644fed897 100644 --- a/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTaskPushNotificationConfigParams.java @@ -37,4 +37,16 @@ public record ListTaskPushNotificationConfigParams(String id, int pageSize, Stri public ListTaskPushNotificationConfigParams(String id) { this(id, 0, "", ""); } + + /** + * Validates and returns the effective page size (between 1 and 100, defaults to 100). + * + * @return the effective page size + */ + public int getEffectivePageSize() { + if (pageSize <= 0 || pageSize > 100) { + return 100; + } + return pageSize; + } } From 882f064371499588c0811fff5c3f7003f4927d37 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 16 Jan 2026 16:47:03 +0100 Subject: [PATCH 261/493] feat: Add pluggable A2AHttpClient with Vert.x implementation (#584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce ServiceLoader-based A2AHttpClient client architecture with two implementations: - JDK HttpClient (core, always available) - Vert.x WebClient (optional, high-performance) Changes: - Create http-client-vertx module for Vert.x implementation - Add ServiceLoader infrastructure (A2AHttpClientFactory, providers) - Update dependent modules to use Vert.x client by default - Add comprehensive tests and usage examples Users can choose implementation via Maven dependencies. Priority-based selection: Vert.x (100) > JDK (0). Fixes #583 🦕 Signed-off-by: Emmanuel Hugonnet --- .gitignore | 3 + boms/extras/pom.xml | 5 + boms/extras/src/it/extras-usage-test/pom.xml | 4 + client/base/pom.xml | 5 + client/base/src/main/java/io/a2a/A2A.java | 6 +- .../java/io/a2a/client/ClientBuilderTest.java | 4 +- .../transport/jsonrpc/JSONRPCTransport.java | 4 +- .../JSONRPCTransportConfigBuilder.java | 12 +- .../jsonrpc/JSONRPCTransportProvider.java | 4 +- .../client/transport/rest/RestTransport.java | 4 +- .../rest/RestTransportConfigBuilder.java | 12 +- .../transport/rest/RestTransportProvider.java | 6 +- extras/README.md | 16 +- extras/http-client-vertx/README.md | 398 +++++++++++++ extras/http-client-vertx/pom.xml | 44 ++ .../a2a/client/http/VertxA2AHttpClient.java | 554 ++++++++++++++++++ .../http/VertxA2AHttpClientProvider.java | 59 ++ .../io.a2a.client.http.A2AHttpClientProvider | 1 + .../http/VertxA2AHttpClientFactoryTest.java | 66 +++ .../VertxA2AHttpClientIntegrationTest.java | 212 +++++++ .../http/VertxA2AHttpClientSSETest.java | 253 ++++++++ .../client/http/VertxA2AHttpClientTest.java | 94 +++ .../io/a2a/client/http/A2ACardResolver.java | 8 +- .../io/a2a/client/http/A2AHttpClient.java | 2 + .../a2a/client/http/A2AHttpClientFactory.java | 89 +++ .../client/http/A2AHttpClientProvider.java | 48 ++ .../io/a2a/client/http/JdkA2AHttpClient.java | 4 +- .../client/http/JdkA2AHttpClientProvider.java | 27 + .../io.a2a.client.http.A2AHttpClientProvider | 1 + .../client/http/A2AHttpClientFactoryTest.java | 88 +++ .../A2AHttpClientFactoryUsageExample.java | 122 ++++ .../http/A2AHttpClientProviderTest.java | 28 + pom.xml | 8 +- reference/jsonrpc/pom.xml | 5 + .../quarkus/QuarkusA2AJSONRPCJdkTest.java | 16 + .../apps/quarkus/QuarkusA2AJSONRPCTest.java | 10 +- .../quarkus/QuarkusA2AJSONRPCVertxTest.java | 21 + reference/rest/pom.xml | 7 +- .../rest/quarkus/QuarkusA2ARestJdkTest.java | 16 + .../rest/quarkus/QuarkusA2ARestTest.java | 10 +- .../rest/quarkus/QuarkusA2ARestVertxTest.java | 21 + server-common/pom.xml | 5 + .../tasks/BasePushNotificationSender.java | 4 +- 43 files changed, 2251 insertions(+), 55 deletions(-) create mode 100644 extras/http-client-vertx/README.md create mode 100644 extras/http-client-vertx/pom.xml create mode 100644 extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClient.java create mode 100644 extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClientProvider.java create mode 100644 extras/http-client-vertx/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider create mode 100644 extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientFactoryTest.java create mode 100644 extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientIntegrationTest.java create mode 100644 extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientSSETest.java create mode 100644 extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientTest.java create mode 100644 http-client/src/main/java/io/a2a/client/http/A2AHttpClientFactory.java create mode 100644 http-client/src/main/java/io/a2a/client/http/A2AHttpClientProvider.java create mode 100644 http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClientProvider.java create mode 100644 http-client/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider create mode 100644 http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryTest.java create mode 100644 http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryUsageExample.java create mode 100644 http-client/src/test/java/io/a2a/client/http/A2AHttpClientProviderTest.java create mode 100644 reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCJdkTest.java create mode 100644 reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCVertxTest.java create mode 100644 reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestJdkTest.java create mode 100644 reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestVertxTest.java diff --git a/.gitignore b/.gitignore index dcc2f1cb8..82b1fecc1 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,10 @@ nb-configuration.xml /.quarkus/cli/plugins/ # TLS Certificates .certs/ + +#Apache NetBeans nbproject/ +nbactions.xml # Private Claude config .claude/ diff --git a/boms/extras/pom.xml b/boms/extras/pom.xml index c06575595..809a3a6a9 100644 --- a/boms/extras/pom.xml +++ b/boms/extras/pom.xml @@ -34,6 +34,11 @@ a2a-java-extras-common ${project.version} + + ${project.groupId} + a2a-java-sdk-http-client-vertx + ${project.version} + ${project.groupId} a2a-java-extras-task-store-database-jpa diff --git a/boms/extras/src/it/extras-usage-test/pom.xml b/boms/extras/src/it/extras-usage-test/pom.xml index 5beeb50ea..cd8b897d9 100644 --- a/boms/extras/src/it/extras-usage-test/pom.xml +++ b/boms/extras/src/it/extras-usage-test/pom.xml @@ -44,6 +44,10 @@ io.github.a2asdk a2a-java-extras-common + + io.github.a2asdk + a2a-java-sdk-http-client-vertx + io.github.a2asdk a2a-java-extras-task-store-database-jpa diff --git a/client/base/pom.xml b/client/base/pom.xml index 05b772931..bd16914a6 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -22,6 +22,11 @@ ${project.groupId} a2a-java-sdk-http-client + + ${project.groupId} + a2a-java-sdk-http-client-vertx + provided + ${project.groupId} a2a-java-sdk-client-transport-spi diff --git a/client/base/src/main/java/io/a2a/A2A.java b/client/base/src/main/java/io/a2a/A2A.java index 02f212656..b04b85df4 100644 --- a/client/base/src/main/java/io/a2a/A2A.java +++ b/client/base/src/main/java/io/a2a/A2A.java @@ -6,7 +6,7 @@ import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientJSONError; import io.a2a.spec.AgentCard; @@ -286,7 +286,7 @@ private static Message toMessage(List> parts, Message.Role role, String * @see AgentCard */ public static AgentCard getAgentCard(String agentUrl) throws A2AClientError, A2AClientJSONError { - return getAgentCard(new JdkA2AHttpClient(), agentUrl); + return getAgentCard(A2AHttpClientFactory.create(), agentUrl); } /** @@ -357,7 +357,7 @@ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl) * @throws io.a2a.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema */ public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, Map authHeaders) throws A2AClientError, A2AClientJSONError { - return getAgentCard(new JdkA2AHttpClient(), agentUrl, relativeCardPath, authHeaders); + return getAgentCard(A2AHttpClientFactory.create(), agentUrl, relativeCardPath, authHeaders); } /** diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index 8b9e4bba0..2c95c2619 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -6,7 +6,7 @@ import java.util.List; import io.a2a.client.config.ClientConfig; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.transport.grpc.GrpcTransport; import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder; import io.a2a.client.transport.jsonrpc.JSONRPCTransport; @@ -89,7 +89,7 @@ public void shouldCreateClient_differentConfigurations() throws A2AClientExcepti Client client = Client .builder(card) .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) - .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig(new JdkA2AHttpClient())) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig(A2AHttpClientFactory.create())) .build(); Assertions.assertNotNull(client); diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 69e291988..8c948c4a8 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -23,8 +23,8 @@ 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; -import io.a2a.client.http.JdkA2AHttpClient; import io.a2a.client.transport.jsonrpc.sse.SSEEventListener; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.interceptors.ClientCallContext; @@ -84,7 +84,7 @@ public JSONRPCTransport(AgentCard agentCard) { public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard agentCard, AgentInterface agentInterface, @Nullable List interceptors) { - this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; + this.httpClient = httpClient == null ? A2AHttpClientFactory.create() : httpClient; this.agentCard = agentCard; this.agentInterface = agentInterface; this.interceptors = interceptors; diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java index 9cd5fae5a..7f73b4c2d 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java @@ -1,7 +1,7 @@ package io.a2a.client.transport.jsonrpc; import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.transport.spi.ClientTransportConfigBuilder; import org.jspecify.annotations.Nullable; @@ -11,7 +11,7 @@ * This builder provides a fluent API for configuring the JSON-RPC transport protocol. * All configuration options are optional - if not specified, sensible defaults are used: *
      - *
    • HTTP client: {@link JdkA2AHttpClient} (JDK's built-in HTTP client)
    • + *
    • HTTP client: Auto-selected via {@link A2AHttpClientFactory} (prefers Vert.x, falls back to JDK)
    • *
    • Interceptors: None
    • *
    *

    @@ -78,7 +78,7 @@ public class JSONRPCTransportConfigBuilder extends ClientTransportConfigBuilder< *

  • Custom header handling
  • * *

    - * If not specified, the default {@link JdkA2AHttpClient} is used. + * If not specified, a client is auto-selected via {@link A2AHttpClientFactory}. *

    * Example: *

    {@code
    @@ -101,16 +101,16 @@ public JSONRPCTransportConfigBuilder httpClient(A2AHttpClient httpClient) {
         /**
          * Build the JSON-RPC transport configuration.
          * 

    - * If no HTTP client was configured, the default {@link JdkA2AHttpClient} is used. + * If no HTTP client was configured, one is auto-selected via {@link A2AHttpClientFactory}. * Any configured interceptors are transferred to the configuration. * * @return the configured JSON-RPC transport configuration */ @Override public JSONRPCTransportConfig build() { - // No HTTP client provided, fallback to the default one (JDK-based implementation) + // No HTTP client provided, use factory to get best available implementation if (httpClient == null) { - httpClient = new JdkA2AHttpClient(); + httpClient = A2AHttpClientFactory.create(); } JSONRPCTransportConfig config = new JSONRPCTransportConfig(httpClient); diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java index 5e9266a50..2dc1a9733 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java @@ -1,6 +1,6 @@ package io.a2a.client.transport.jsonrpc; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; @@ -14,7 +14,7 @@ public class JSONRPCTransportProvider implements ClientTransportProvider interceptors) { - this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient; + this.httpClient = httpClient == null ? A2AHttpClientFactory.create() : httpClient; this.agentCard = agentCard; this.agentInterface = agentInterface; this.interceptors = interceptors; diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java index 855de0ca6..257836199 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportConfigBuilder.java @@ -1,7 +1,7 @@ package io.a2a.client.transport.rest; import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.transport.spi.ClientTransportConfigBuilder; import org.jspecify.annotations.Nullable; @@ -11,7 +11,7 @@ * This builder provides a fluent API for configuring the REST transport protocol. * All configuration options are optional - if not specified, sensible defaults are used: *

      - *
    • HTTP client: {@link JdkA2AHttpClient} (JDK's built-in HTTP client)
    • + *
    • HTTP client: Auto-selected via {@link A2AHttpClientFactory} (prefers Vert.x, falls back to JDK)
    • *
    • Interceptors: None
    • *
    *

    @@ -78,7 +78,7 @@ public class RestTransportConfigBuilder extends ClientTransportConfigBuilderCustom header handling * *

    - * If not specified, the default {@link JdkA2AHttpClient} is used. + * If not specified, a client is auto-selected via {@link A2AHttpClientFactory}. *

    * Example: *

    {@code
    @@ -101,16 +101,16 @@ public RestTransportConfigBuilder httpClient(A2AHttpClient httpClient) {
         /**
          * Build the REST transport configuration.
          * 

    - * If no HTTP client was configured, the default {@link JdkA2AHttpClient} is used. + * If no HTTP client was configured, one is auto-selected via {@link A2AHttpClientFactory}. * Any configured interceptors are transferred to the configuration. * * @return the configured REST transport configuration */ @Override public RestTransportConfig build() { - // No HTTP client provided, fallback to the default one (JDK-based implementation) + // No HTTP client provided, use factory to get best available implementation if (httpClient == null) { - httpClient = new JdkA2AHttpClient(); + httpClient = A2AHttpClientFactory.create(); } RestTransportConfig config = new RestTransportConfig(httpClient); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java index 5b5af20c9..b6e6d7a47 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransportProvider.java @@ -1,6 +1,6 @@ package io.a2a.client.transport.rest; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.transport.spi.ClientTransportProvider; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; @@ -18,9 +18,9 @@ public String getTransportProtocol() { public RestTransport create(RestTransportConfig clientTransportConfig, AgentCard agentCard, AgentInterface agentInterface) throws A2AClientException { RestTransportConfig transportConfig = clientTransportConfig; if (transportConfig == null) { - transportConfig = new RestTransportConfig(new JdkA2AHttpClient()); + transportConfig = new RestTransportConfig(A2AHttpClientFactory.create()); } - return new RestTransport(clientTransportConfig.getHttpClient(), agentCard, agentInterface, transportConfig.getInterceptors()); + return new RestTransport(transportConfig.getHttpClient(), agentCard, agentInterface, transportConfig.getInterceptors()); } @Override diff --git a/extras/README.md b/extras/README.md index 3f85e4f90..e3692e49e 100644 --- a/extras/README.md +++ b/extras/README.md @@ -4,6 +4,16 @@ This directory contains additions to what is provided by the default SDK impleme Please see the README's of each child directory for more details. -[`task-store-database-jpa`](./task-store-database-jpa/README.md) - Replaces the default `InMemoryTaskStore` with a `TaskStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. -[`push-notification-config-store-database-jpa`](./push-notification-config-store-database-jpa/README.md) - Replaces the default `InMemoryPushNotificationConfigStore` with a `PushNotificationConfigStore` backed by a RDBMS. It uses JPA to interact with the RDBMS. -[`queue-manager-replicated`](./queue-manager-replicated/README.md) - Replaces the default `InMemoryQueueManager` with a `QueueManager` supporting replication to other A2A servers implementing the same agent. You can write your own `ReplicationStrategy`, or use the provided `MicroProfile Reactive Messaging implementation`. \ No newline at end of file +## HTTP Client + +[`http-client-vertx`](./http-client-vertx/README.md) - Vert.x WebClient-based implementation of `A2AHttpClient` for reactive, high-performance HTTP communication. Replaces the default JDK HttpClient with a non-blocking, event-loop based client. Uses SPI for automatic discovery - simply add this library as a dependency to use it. Recommended for reactive applications, Quarkus, and high-throughput scenarios. + +## Storage & Persistence + +[`task-store-database-jpa`](./task-store-database-jpa/README.md) - Replaces the default `InMemoryTaskStore` with a `TaskStore` backed by a RDBMS. It uses JPA to interact with the RDBMS, providing persistence across application restarts and shared state in multi-instance deployments. + +[`push-notification-config-store-database-jpa`](./push-notification-config-store-database-jpa/README.md) - Replaces the default `InMemoryPushNotificationConfigStore` with a `PushNotificationConfigStore` backed by a RDBMS. It uses JPA to interact with the RDBMS, ensuring push notification subscriptions survive restarts. + +## Distributed Systems + +[`queue-manager-replicated`](./queue-manager-replicated/README.md) - Replaces the default `InMemoryQueueManager` with a `QueueManager` supporting replication to other A2A servers implementing the same agent. Required for multi-instance deployments. You can write your own `ReplicationStrategy`, or use the provided MicroProfile Reactive Messaging implementation with Apache Kafka, Pulsar, or AMQP. \ No newline at end of file diff --git a/extras/http-client-vertx/README.md b/extras/http-client-vertx/README.md new file mode 100644 index 000000000..a1b48afbf --- /dev/null +++ b/extras/http-client-vertx/README.md @@ -0,0 +1,398 @@ +# A2A Java SDK - Vert.x HTTP Client + +This module provides a Vert.x WebClient-based implementation of the `A2AHttpClient` interface for reactive, high-performance HTTP communication in the A2A Java SDK. + +## Overview + +The A2A SDK uses an `A2AHttpClient` abstraction for all HTTP communication, including fetching agent cards and making REST transport calls. By default, the SDK uses a JDK 11+ HttpClient implementation. This module provides a drop-in replacement using **Vert.x WebClient**, offering: + +- **Reactive/Async Architecture**: Built on Vert.x's event loop for non-blocking I/O +- **Better Performance**: Lower resource usage and higher throughput than blocking JDK HttpClient +- **HTTP/2 Support**: Automatic HTTP/2 negotiation via ALPN +- **Seamless Integration**: Automatic discovery via Java SPI - no code changes required + +## What It Does + +Replaces the default `JdkA2AHttpClient` with `VertxA2AHttpClient`, which uses Vert.x WebClient for all HTTP operations: + +- GET requests (synchronous and async SSE streaming) +- POST requests (synchronous and async SSE streaming) +- DELETE requests +- Agent card fetching +- REST transport communication + +The implementation maintains the same API as the JDK client but uses Vert.x's reactive architecture under the hood. + +## Problem It Solves + +### Performance & Scalability +- **JDK HttpClient**: Uses platform threads for blocking I/O operations +- **Vert.x WebClient**: Uses event loop threads with non-blocking I/O +- **Result**: Lower memory footprint, higher concurrency, better throughput + +### Reactive Integration +- Applications already using Vert.x can share the same event loop +- Avoids mixing blocking and non-blocking I/O patterns +- Better integration with reactive frameworks (Quarkus, Vert.x, etc.) + +### Resource Efficiency +- Fewer threads needed for high-concurrency scenarios +- Better connection pooling and keep-alive management +- Lower latency for streaming operations (SSE) + +## When to Use + +✅ **Recommended for:** +- Quarkus applications (Vert.x is already included) +- Reactive applications using Vert.x or reactive frameworks +- High-throughput scenarios with many concurrent requests +- Applications requiring efficient SSE streaming +- Cloud-native deployments optimizing for resource usage + +❌ **Not needed for:** +- Simple, low-volume applications +- Applications without existing Vert.x dependency +- Environments where JDK HttpClient performs adequately + +## Quick Start + +### 1. Add Dependency + +Add this module to your project's `pom.xml`: + +```xml + + io.github.a2asdk + a2a-java-sdk-http-client-vertx + ${a2a.version} + +``` + +You also need the Vert.x WebClient dependency (if not already present): + +```xml + + io.vertx + vertx-web-client + +``` + +**For Quarkus**: Vert.x is already included, so you only need to add the `a2a-java-sdk-http-client-vertx` dependency. + +### 2. Automatic Discovery (No Code Changes) + +The Vert.x HTTP client is automatically discovered via **Java SPI (Service Provider Interface)**: + +```java +// No changes needed - A2A SDK automatically uses VertxA2AHttpClient +A2ACardResolver resolver = new A2ACardResolver("http://localhost:9999"); +AgentCard card = resolver.getAgentCard(); // Uses Vert.x under the hood + +// Client creation also uses Vert.x automatically +Client client = Client.builder(card) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .build(); +``` + +The `VertxA2AHttpClientProvider` has **priority 100**, which is higher than the JDK implementation's priority (50). The SDK's `A2AHttpClientFactory` uses `ServiceLoader` to discover and select the highest-priority provider available. + +### 3. No Configuration Required + +The module works out-of-the-box with sensible defaults: +- HTTP keep-alive enabled +- Automatic redirect following +- Automatic HTTP/2 negotiation + +## Usage Examples + +### Basic Usage (Automatic Discovery) + +```java +// The A2A SDK internally uses A2AHttpClient for all HTTP operations +// With vertx-http-client on the classpath, it automatically uses VertxA2AHttpClient + +// Example 1: Fetching agent card +A2ACardResolver resolver = new A2ACardResolver("http://localhost:9999"); +AgentCard card = resolver.getAgentCard(); + +// Example 2: Using REST transport (uses HTTP client internally) +Client client = Client.builder(card) + .withTransport(RESTTransport.class, new RESTTransportConfig()) + .build(); + +Message message = A2A.toUserMessage("Hello!"); +client.sendMessage(message); +``` + +### Direct Usage (Advanced) + +If you need direct access to the HTTP client (rare): + +```java +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; +import io.a2a.client.http.A2AHttpResponse; + +// Get the client via factory (returns VertxA2AHttpClient if available) +try (A2AHttpClient client = A2AHttpClientFactory.create()) { + // Simple GET request + A2AHttpResponse response = client.createGet() + .url("https://api.example.com/data") + .addHeader("Authorization", "Bearer token") + .get(); + + if (response.success()) { + System.out.println(response.body()); + } +} +``` + +### POST Request with JSON Body + +```java +try (A2AHttpClient client = A2AHttpClientFactory.create()) { + A2AHttpResponse response = client.createPost() + .url("https://api.example.com/submit") + .addHeader("Content-Type", "application/json") + .body("{\"key\":\"value\"}") + .post(); + + System.out.println("Status: " + response.status()); +} +``` + +### Server-Sent Events (SSE) Streaming + +```java +try (A2AHttpClient client = A2AHttpClientFactory.create()) { + CompletableFuture future = client.createGet() + .url("https://api.example.com/stream") + .getAsyncSSE( + message -> System.out.println("Received: " + message), + error -> error.printStackTrace(), + () -> System.out.println("Stream complete") + ); + + // Do other work while streaming... + future.join(); // Wait for completion if needed +} +``` + +## Advanced Configuration + +### Using an External Vert.x Instance + +In Quarkus or other CDI environments, you can inject an existing Vert.x instance: + +#### Quarkus Example + +```java +import io.vertx.core.Vertx; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class MyService { + + @Inject + Vertx vertx; + + public void doSomething() { + // VertxA2AHttpClient will automatically discover and use the CDI-managed Vertx + try (A2AHttpClient client = A2AHttpClientFactory.create()) { + // The client internally reuses the injected Vertx instance + A2AHttpResponse response = client.createGet() + .url("https://example.com") + .get(); + } + } +} +``` + +The `VertxA2AHttpClient` constructor automatically checks for a CDI-managed `Vertx` instance and reuses it if available. This ensures that your entire application shares the same Vert.x event loop. + +#### Manual Vertx Instance (Non-CDI) + +If you're not using CDI but want to share a Vert.x instance: + +```java +import io.a2a.client.http.VertxA2AHttpClient; +import io.vertx.core.Vertx; + +// Create Vertx instance once +Vertx vertx = Vertx.vertx(); + +try { + // Create client with shared Vertx instance + try (VertxA2AHttpClient client = new VertxA2AHttpClient(vertx)) { + A2AHttpResponse response = client.createGet() + .url("https://example.com") + .get(); + } + // Client is closed, but Vertx instance remains open +} finally { + // Close Vertx when application shuts down + vertx.close(); +} +``` + +### Custom WebClient Configuration + +For advanced use cases requiring custom Vert.x WebClient configuration, you can create your own provider: + +```java +import io.a2a.client.http.A2AHttpClient; +import io.a2a.client.http.VertxA2AHttpClient; +import io.vertx.core.Vertx; +import io.vertx.ext.web.client.WebClient; +import io.vertx.ext.web.client.WebClientOptions; + +// Create custom Vertx instance with specific options +Vertx vertx = Vertx.vertx(); + +// Note: VertxA2AHttpClient doesn't expose WebClient customization directly +// For custom WebClient options, you would need to extend VertxA2AHttpClient +// or configure Vert.x-level options +``` + +## How It Works + +### Service Provider Interface (SPI) + +The module uses Java's `ServiceLoader` mechanism for automatic discovery: + +1. **Provider Registration**: `META-INF/services/io.a2a.client.http.A2AHttpClientProvider` contains: + ``` + io.a2a.client.http.VertxA2AHttpClientProvider + ``` + +2. **Priority System**: Each provider has a priority: + - `VertxA2AHttpClientProvider`: **100** (when Vert.x is available) + - `JdkA2AHttpClientProvider`: **50** (always available) + +3. **Automatic Selection**: `A2AHttpClientFactory.create()` uses the highest-priority available provider + +4. **Graceful Fallback**: If Vert.x classes are not on the classpath, the provider returns priority `-1` and the SDK falls back to JDK HttpClient + +### Lifecycle Management + +#### Standalone Usage +```java +// Client owns Vertx instance +try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + // Use client +} // Both WebClient and Vertx are closed +``` + +#### CDI/Quarkus Usage +```java +// Client uses externally-managed Vertx +try (VertxA2AHttpClient client = new VertxA2AHttpClient(injectedVertx)) { + // Use client +} // Only WebClient is closed, Vertx remains open +``` + +### Thread Safety + +- **Client Instance**: Thread-safe - multiple threads can use the same client +- **Builder Instances**: NOT thread-safe - create separate builders per thread +- **Vertx Event Loop**: All I/O operations execute on Vert.x event loop threads + +## Performance Characteristics + +### Synchronous Methods (`.get()`, `.post()`, `.delete()`) + +Despite using Vert.x's async API internally, these methods block the calling thread: + +```java +A2AHttpResponse response = client.createGet() + .url("https://example.com") + .get(); // ← Blocks until response received +``` + +**Why block?** The `A2AHttpClient` interface is designed for synchronous operations to simplify SDK usage. Vert.x's async execution still provides benefits: +- Non-blocking I/O at the network layer +- Efficient connection pooling +- Lower thread usage overall + +### Async Methods (`.getAsyncSSE()`, `.postAsyncSSE()`) + +True async operation - returns immediately with a `CompletableFuture`: + +```java +CompletableFuture future = client.createGet() + .url("https://example.com/stream") + .getAsyncSSE( + message -> handleMessage(message), + error -> handleError(error), + () -> handleComplete() + ); // ← Returns immediately + +// Do other work +future.join(); // Optional: wait for completion +``` + +## Troubleshooting + +### Client Not Being Used + +**Symptom**: Logs show `JdkA2AHttpClient` instead of `VertxA2AHttpClient` + +**Cause**: Vert.x WebClient not on classpath or version incompatibility + +**Solution**: +1. Verify dependency is present: + ```bash + mvn dependency:tree | grep vertx-web-client + ``` + +2. For Quarkus, ensure Vert.x version matches: + ```xml + + io.quarkus + quarkus-vertx + + ``` + +### ClassNotFoundException for Vert.x + +**Symptom**: `ClassNotFoundException: io.vertx.core.Vertx` + +**Solution**: Add Vert.x WebClient dependency: +```xml + + io.vertx + vertx-web-client + 4.x.x + +``` + +### Memory Leaks + +**Symptom**: `Vertx` instances not being closed + +**Cause**: Not closing `VertxA2AHttpClient` when created with no-args constructor + +**Solution**: Always use try-with-resources: +```java +try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + // Use client +} // Automatically closed +``` + +## Version Compatibility + +- **Java**: 17+ (same as A2A SDK) +- **Vert.x**: 4.x (tested with 4.5.0+) +- **Quarkus**: Any version using Vert.x 4.x +- **Jakarta EE**: 9.0+ (for CDI discovery) + +## Additional Resources + +- [Vert.x WebClient Documentation](https://vertx.io/docs/vertx-web-client/java/) +- [A2A Protocol Specification](https://a2a-protocol.org/) +- [Quarkus Vert.x Guide](https://quarkus.io/guides/vertx) + +--- + +*This module is part of the A2A Java SDK extras and provides production-ready reactive HTTP support for high-performance A2A applications.* diff --git a/extras/http-client-vertx/pom.xml b/extras/http-client-vertx/pom.xml new file mode 100644 index 000000000..59bbf1141 --- /dev/null +++ b/extras/http-client-vertx/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + + io.github.a2asdk + a2a-java-sdk-parent + 1.0.0.Alpha1-SNAPSHOT + ../../pom.xml + + a2a-java-sdk-http-client-vertx + + jar + + Java SDK A2A HTTP Client - Vert.x Implementation + Vert.x implementation for A2A HTTP Client + + + + ${project.groupId} + a2a-java-sdk-http-client + + + io.vertx + vertx-web-client + provided + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mock-server + mockserver-netty + test + + + + diff --git a/extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClient.java b/extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClient.java new file mode 100644 index 000000000..b6afce027 --- /dev/null +++ b/extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClient.java @@ -0,0 +1,554 @@ +package io.a2a.client.http; + +import static java.net.HttpURLConnection.HTTP_FORBIDDEN; +import static java.net.HttpURLConnection.HTTP_MULT_CHOICE; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import org.jspecify.annotations.Nullable; + +import io.a2a.common.A2AErrorMessages; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.ext.web.client.HttpRequest; +import io.vertx.ext.web.client.HttpResponse; +import io.vertx.ext.web.client.WebClient; +import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.ext.web.codec.BodyCodec; +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.spi.Bean; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.CDI; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Vert.x WebClient-based implementation of {@link A2AHttpClient}. + * + *

    + * This implementation uses Vert.x's reactive HTTP client to execute requests. + * For synchronous methods ({@link GetBuilder#get()}, {@link PostBuilder#post()}, {@link DeleteBuilder#delete()}), + * the implementation blocks the calling thread until the asynchronous operation completes. + * For SSE streaming methods, the implementation returns immediately with a + * {@link CompletableFuture} and streams events asynchronously via callbacks. + * + *

    Lifecycle Management

    + *

    + * This client implements {@link AutoCloseable} and should be closed when no longer needed: + *

    {@code
    + * try (VertxA2AHttpClient client = new VertxA2AHttpClient()) {
    + *     A2AHttpResponse response = client.createGet()
    + *         .url("https://example.com/api")
    + *         .get();
    + *     // Use response
    + * }
    + * }
    + * + *

    + * If constructed with the no-args constructor, the client creates and owns a + * {@link Vertx} instance which will be closed when {@link #close()} is called. + * If constructed with an external {@link Vertx} instance, only the WebClient is + * closed, leaving the Vertx instance management to the caller. + * + *

    Thread Safety

    + *

    + * This client is thread-safe. Multiple threads can create and execute requests + * concurrently. However, individual builder instances are NOT thread-safe and should + * not be shared across threads. + * + *

    HTTP/2 Support

    + *

    + * Vert.x WebClient automatically negotiates HTTP/2 when supported by the server + * via ALPN. No explicit configuration is required. + * + *

    Usage Examples

    + * + *

    Simple GET Request

    + *
    {@code
    + * try (VertxA2AHttpClient client = new VertxA2AHttpClient()) {
    + *     A2AHttpResponse response = client.createGet()
    + *         .url("https://api.example.com/data")
    + *         .addHeader("Authorization", "Bearer token")
    + *         .get();
    + *
    + *     if (response.success()) {
    + *         System.out.println(response.body());
    + *     }
    + * }
    + * }
    + * + *

    POST Request with JSON Body

    + *
    {@code
    + * try (VertxA2AHttpClient client = new VertxA2AHttpClient()) {
    + *     A2AHttpResponse response = client.createPost()
    + *         .url("https://api.example.com/submit")
    + *         .addHeader("Content-Type", "application/json")
    + *         .body("{\"key\":\"value\"}")
    + *         .post();
    + *
    + *     System.out.println("Status: " + response.status());
    + * }
    + * }
    + * + *

    Async SSE Streaming

    + *
    {@code
    + * try (VertxA2AHttpClient client = new VertxA2AHttpClient()) {
    + *     CompletableFuture future = client.createGet()
    + *         .url("https://api.example.com/stream")
    + *         .getAsyncSSE(
    + *             message -> System.out.println("Received: " + message),
    + *             error -> error.printStackTrace(),
    + *             () -> System.out.println("Stream complete")
    + *         );
    + *
    + *     // Do other work while streaming...
    + *     future.join(); // Wait for completion if needed
    + * }
    + * }
    + */ +public class VertxA2AHttpClient implements A2AHttpClient, AutoCloseable { + + private final Vertx vertx; + private final WebClient webClient; + private boolean ownsVertx; + private static final Logger log = Logger.getLogger(VertxA2AHttpClient.class.getName()); + + /** + * Creates a new VertxA2AHttpClient with an internally managed Vert.x instance. + * + *

    + * The client creates a new {@link Vertx} instance and {@link WebClient} configured + * with HTTP keep-alive and automatic redirect following. When {@link #close()} is called, + * both the WebClient and Vertx instance are closed. + * + *

    + * Important: Always call {@link #close()} when done with this client + * to prevent resource leaks. + * + * @see #VertxA2AHttpClient(Vertx) for using an externally managed Vertx instance + */ + public VertxA2AHttpClient() { + this.vertx = createVertx(); + WebClientOptions options = new WebClientOptions() + .setFollowRedirects(true) + .setKeepAlive(true); + this.webClient = WebClient.create(vertx, options); + log.fine("Vert.x client is ready."); + } + + private Vertx createVertx() { + try { + BeanManager beanManager = CDI.current().getBeanManager(); + Set> beans = beanManager.getBeans(Vertx.class); + if (beans != null && !beans.isEmpty()) { + this.ownsVertx = false; + Bean bean = beans.iterator().next(); + CreationalContext context = beanManager.createCreationalContext(bean); + return (Vertx) beanManager.getReference(bean, Vertx.class, context); + } + } catch (Exception ex) { + log.log(Level.FINE, "Error loading vertx from CDI error details", ex); + } + this.ownsVertx = true; + return Vertx.vertx(); + } + + /** + * Creates a new VertxA2AHttpClient using an externally managed Vert.x instance. + * + *

    + * The client creates a {@link WebClient} using the provided {@link Vertx} instance. + * When {@link #close()} is called, only the WebClient is closed; the Vertx instance + * remains open and must be managed by the caller. + * + *

    + * This constructor is useful in environments where Vert.x is already managed, + * such as Quarkus applications. + * + * @param vertx the Vert.x instance to use; must not be null + * @throws NullPointerException if vertx is null + */ + public VertxA2AHttpClient(Vertx vertx) { + if (vertx == null) { + throw new NullPointerException("vertx must not be null"); + } + this.vertx = vertx; + this.ownsVertx = false; + WebClientOptions options = new WebClientOptions() + .setFollowRedirects(true) + .setKeepAlive(true); + this.webClient = WebClient.create(vertx, options); + log.fine("Vert.x client is ready."); + } + + /** + * Closes this HTTP client and releases associated resources. + * + *

    + * This method always closes the WebClient. If the client was created with the + * no-args constructor (and thus owns the Vert.x instance), the Vertx instance is + * also closed. Otherwise, the Vertx instance is left open for the caller to manage. + */ + @Override + public void close() { + webClient.close(); + if (ownsVertx) { + vertx.close(); + } + } + + @Override + public GetBuilder createGet() { + return new VertxGetBuilder(); + } + + @Override + public PostBuilder createPost() { + return new VertxPostBuilder(); + } + + @Override + public DeleteBuilder createDelete() { + return new VertxDeleteBuilder(); + } + + private abstract class VertxBuilder> implements Builder { + + protected String url = ""; + protected Map headers = new HashMap<>(); + + @Override + public T url(String url) { + this.url = url; + return self(); + } + + @Override + public T addHeader(String name, String value) { + headers.put(name, value); + return self(); + } + + @Override + public T addHeaders(Map headers) { + if (headers != null && !headers.isEmpty()) { + for (Map.Entry entry : headers.entrySet()) { + addHeader(entry.getKey(), entry.getValue()); + } + } + return self(); + } + + @SuppressWarnings("unchecked") + T self() { + return (T) this; + } + } + + /** + * Common method to execute synchronous HTTP requests (GET, POST, DELETE). + * + * @param request the HTTP request configured with method and URL + * @param headers custom headers to add to the request + * @param bodyBuffer optional body buffer for POST requests (null for GET/DELETE) + * @return the HTTP response + * @throws IOException if the request fails or returns 401/403 + * @throws InterruptedException if the thread is interrupted while waiting + */ + private A2AHttpResponse executeSyncRequest( + HttpRequest request, + Map headers, + @Nullable Buffer bodyBuffer) throws IOException, InterruptedException { + + // Add headers + for (Map.Entry entry : headers.entrySet()) { + request.putHeader(entry.getKey(), entry.getValue()); + } + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference responseRef = new AtomicReference<>(); + AtomicReference errorRef = new AtomicReference<>(); + + // Send with or without body + if (bodyBuffer != null) { + request.sendBuffer(bodyBuffer, ar -> handleResponse(ar, responseRef, errorRef, latch)); + } else { + request.send(ar -> handleResponse(ar, responseRef, errorRef, latch)); + } + + latch.await(); + + if (errorRef.get() != null) { + Throwable error = errorRef.get(); + if (error instanceof IOException) { + throw (IOException) error; + } + if (error instanceof InterruptedException) { + throw (InterruptedException) error; + } + throw new IOException("Request failed", error); + } + A2AHttpResponse finalResponse = responseRef.get(); + if(finalResponse == null) { + throw new IllegalStateException("No response from http request"); + } + return finalResponse; + } + + /** + * Handles the HTTP response callback, checking for auth errors and populating response/error refs. + */ + private void handleResponse( + io.vertx.core.AsyncResult> ar, + AtomicReference responseRef, + AtomicReference errorRef, + CountDownLatch latch) { + + if (ar.succeeded()) { + HttpResponse response = ar.result(); + int status = response.statusCode(); + + // Check for authentication/authorization errors + switch (status) { + case HTTP_UNAUTHORIZED -> errorRef.set(new IOException(A2AErrorMessages.AUTHENTICATION_FAILED)); + case HTTP_FORBIDDEN -> errorRef.set(new IOException(A2AErrorMessages.AUTHORIZATION_FAILED)); + default -> { + String body = response.bodyAsString(); + responseRef.set(new VertxHttpResponse(status, body != null ? body : "")); + } + } + } else { + errorRef.set(ar.cause()); + } + latch.countDown(); + } + + /** + * Common method to execute async SSE requests (GET or POST). + * + * @param baseRequest the base HTTP request (HttpRequest<Buffer>) configured with method and URL + * @param headers custom headers to add to the request + * @param bodyBuffer optional body buffer for POST requests (null for GET) + * @param messageConsumer callback for each SSE message received + * @param errorConsumer callback for errors + * @param completeRunnable callback when stream completes successfully + * @return CompletableFuture that completes when the stream ends + */ + private CompletableFuture executeAsyncSSE( + HttpRequest baseRequest, + Map headers, + @Nullable Buffer bodyBuffer, + Consumer messageConsumer, + Consumer errorConsumer, + Runnable completeRunnable) { + + CompletableFuture future = new CompletableFuture<>(); + AtomicBoolean successOccurred = new AtomicBoolean(false); + AtomicBoolean streamEnded = new AtomicBoolean(false); + AtomicBoolean futureCompleted = new AtomicBoolean(false); + + HttpRequest request = baseRequest + .putHeader(ACCEPT, EVENT_STREAM) + .as(BodyCodec.sseStream(stream -> { + stream.handler(event -> { + String data = event.data(); + if (data != null) { + data = data.trim(); + if (!data.isEmpty()) { + messageConsumer.accept(data); + } + } + }); + + stream.endHandler(v -> { + streamEnded.set(true); + // Only complete if we've validated success and haven't completed yet + if (successOccurred.get() && futureCompleted.compareAndSet(false, true)) { + completeRunnable.run(); + future.complete(null); + } + }); + + stream.exceptionHandler(error -> { + if (futureCompleted.compareAndSet(false, true)) { + errorConsumer.accept(error); + future.complete(null); + } + }); + })); + + // Add custom headers + for (Map.Entry entry : headers.entrySet()) { + request.putHeader(entry.getKey(), entry.getValue()); + } + + // Send with or without body + var sendFuture = (bodyBuffer != null) ? request.sendBuffer(bodyBuffer) : request.send(); + + sendFuture + .onSuccess(response -> { + // Validate status code manually since .expecting() doesn't work with SSE streams + int statusCode = response.statusCode(); + if (statusCode < 200 || statusCode >= 300) { + // Error - don't set successOccurred, just report error + if (futureCompleted.compareAndSet(false, true)) { + // Use same error messages as sync requests for consistency + IOException error = switch (statusCode) { + case HTTP_UNAUTHORIZED -> new IOException(A2AErrorMessages.AUTHENTICATION_FAILED); + case HTTP_FORBIDDEN -> new IOException(A2AErrorMessages.AUTHORIZATION_FAILED); + default -> new IOException("HTTP " + statusCode + ": " + response.bodyAsString()); + }; + errorConsumer.accept(error); + future.complete(null); + } + } else { + // Success - mark as successful + successOccurred.set(true); + // If stream already ended, complete now + if (streamEnded.get() && futureCompleted.compareAndSet(false, true)) { + completeRunnable.run(); + future.complete(null); + } + } + }) + .onFailure(cause -> { + if (futureCompleted.compareAndSet(false, true)) { + errorConsumer.accept(cause); + future.complete(null); + } + }); + + return future; + } + + private class VertxGetBuilder extends VertxBuilder implements A2AHttpClient.GetBuilder { + + /** + * {@inheritDoc} + * + *

    + * Implementation Note: This method blocks the calling thread until + * the asynchronous HTTP request completes. The underlying Vert.x operation executes + * asynchronously on the Vert.x event loop. + * + * @throws IOException if the request fails, including: + *

      + *
    • Network errors (connection refused, timeout, etc.)
    • + *
    • HTTP 401 Unauthorized - with message from {@link A2AErrorMessages#AUTHENTICATION_FAILED}
    • + *
    • HTTP 403 Forbidden - with message from {@link A2AErrorMessages#AUTHORIZATION_FAILED}
    • + *
    + * @throws InterruptedException if the thread is interrupted while waiting + */ + @Override + public A2AHttpResponse get() throws IOException, InterruptedException { + return executeSyncRequest(webClient.getAbs(url), headers, null); + } + + @Override + public CompletableFuture getAsyncSSE( + Consumer messageConsumer, + Consumer errorConsumer, + Runnable completeRunnable) throws IOException, InterruptedException { + + HttpRequest request = webClient.getAbs(url); + return executeAsyncSSE(request, headers, null, messageConsumer, errorConsumer, completeRunnable); + } + } + + private class VertxPostBuilder extends VertxBuilder implements A2AHttpClient.PostBuilder { + + private String body = ""; + + @Override + public PostBuilder body(String body) { + this.body = body; + return self(); + } + + /** + * {@inheritDoc} + * + *

    + * Implementation Note: This method blocks the calling thread until + * the asynchronous HTTP request completes. The underlying Vert.x operation executes + * asynchronously on the Vert.x event loop. + * + * @throws IOException if the request fails, including: + *

      + *
    • Network errors (connection refused, timeout, etc.)
    • + *
    • HTTP 401 Unauthorized - with message from {@link A2AErrorMessages#AUTHENTICATION_FAILED}
    • + *
    • HTTP 403 Forbidden - with message from {@link A2AErrorMessages#AUTHORIZATION_FAILED}
    • + *
    + * @throws InterruptedException if the thread is interrupted while waiting + */ + @Override + public A2AHttpResponse post() throws IOException, InterruptedException { + Buffer bodyBuffer = Buffer.buffer(body, StandardCharsets.UTF_8.name()); + return executeSyncRequest(webClient.postAbs(url), headers, bodyBuffer); + } + + @Override + public CompletableFuture postAsyncSSE( + Consumer messageConsumer, + Consumer errorConsumer, + Runnable completeRunnable) throws IOException, InterruptedException { + + HttpRequest request = webClient.postAbs(url); + Buffer bodyBuffer = Buffer.buffer(body, StandardCharsets.UTF_8.name()); + return executeAsyncSSE(request, headers, bodyBuffer, messageConsumer, errorConsumer, completeRunnable); + } + } + + private class VertxDeleteBuilder extends VertxBuilder implements A2AHttpClient.DeleteBuilder { + + /** + * {@inheritDoc} + * + *

    + * Implementation Note: This method blocks the calling thread until + * the asynchronous HTTP request completes. The underlying Vert.x operation executes + * asynchronously on the Vert.x event loop. + * + * @throws IOException if the request fails, including: + *

      + *
    • Network errors (connection refused, timeout, etc.)
    • + *
    • HTTP 401 Unauthorized - with message from {@link A2AErrorMessages#AUTHENTICATION_FAILED}
    • + *
    • HTTP 403 Forbidden - with message from {@link A2AErrorMessages#AUTHORIZATION_FAILED}
    • + *
    + * @throws InterruptedException if the thread is interrupted while waiting + */ + @Override + public A2AHttpResponse delete() throws IOException, InterruptedException { + return executeSyncRequest(webClient.deleteAbs(url), headers, null); + } + } + + private record VertxHttpResponse(int status, String body) implements A2AHttpResponse { + + @Override + public int status() { + return status; + } + + @Override + public boolean success() { + return status >= HTTP_OK && status < HTTP_MULT_CHOICE; + } + + @Override + public String body() { + return body; + } + } +} diff --git a/extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClientProvider.java b/extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClientProvider.java new file mode 100644 index 000000000..e802e1df1 --- /dev/null +++ b/extras/http-client-vertx/src/main/java/io/a2a/client/http/VertxA2AHttpClientProvider.java @@ -0,0 +1,59 @@ +package io.a2a.client.http; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Service provider for {@link VertxA2AHttpClient}. + * + *

    + * This provider has a higher priority (100) than the JDK implementation and will be + * preferred when the Vert.x dependencies are available on the classpath. + * + *

    + * If Vert.x classes are not available at runtime, this provider will check for their + * presence and throw an {@link IllegalStateException} when attempting to create a client. + * The ServiceLoader mechanism will skip this provider and fall back to the JDK implementation. + */ +public final class VertxA2AHttpClientProvider implements A2AHttpClientProvider { + + private static final boolean VERTX_AVAILABLE = isVertxAvailable(); + private static final Logger log = Logger.getLogger(VertxA2AHttpClientProvider.class.getName()); + + private static boolean isVertxAvailable() { + try { + Class.forName("io.vertx.core.Vertx"); + Class.forName("io.vertx.ext.web.client.WebClient"); + return true; + } catch (ClassNotFoundException ex) { + Logger.getLogger(VertxA2AHttpClientProvider.class.getName()).log(Level.FINE, "Vert.x classes are not available on the classpath. Falling back to other providers.", ex); + return false; + } + } + + @Override + public A2AHttpClient create() { + if (!VERTX_AVAILABLE) { + throw new IllegalStateException( + "Vert.x classes are not available on the classpath. " + + "Add io.vertx:vertx-web-client dependency or use the JDK HTTP client implementation."); + } + + try { + Class clientClass = Class.forName("io.a2a.client.http.VertxA2AHttpClient"); + return (A2AHttpClient) clientClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Failed to create VertxA2AHttpClient instance", e); + } + } + + @Override + public int priority() { + return VERTX_AVAILABLE ? 100 : -1; // Higher priority when available, negative when not + } + + @Override + public String name() { + return "vertx"; + } +} diff --git a/extras/http-client-vertx/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider b/extras/http-client-vertx/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider new file mode 100644 index 000000000..9d6a67c3b --- /dev/null +++ b/extras/http-client-vertx/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider @@ -0,0 +1 @@ +io.a2a.client.http.VertxA2AHttpClientProvider diff --git a/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientFactoryTest.java b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientFactoryTest.java new file mode 100644 index 000000000..2ac3eac94 --- /dev/null +++ b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientFactoryTest.java @@ -0,0 +1,66 @@ +package io.a2a.client.http; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class VertxA2AHttpClientFactoryTest { + + @Test + public void testCreateReturnsVertxClient() { + // When both JDK and Vertx are on classpath, Vertx should be preferred due to higher priority + A2AHttpClient client = A2AHttpClientFactory.create(); + assertNotNull(client); + assertInstanceOf(VertxA2AHttpClient.class, client, + "Factory should return VertxA2AHttpClient when Vertx is available"); + // Clean up + if (client instanceof AutoCloseable) { + try { + ((AutoCloseable) client).close(); + } catch (Exception e) { + fail("Failed to close client: " + e.getMessage()); + } + } + } + + @Test + public void testCreateWithVertxProviderName() { + A2AHttpClient client = A2AHttpClientFactory.create("vertx"); + assertNotNull(client); + assertInstanceOf(VertxA2AHttpClient.class, client, + "Factory should return VertxA2AHttpClient when 'vertx' provider is requested"); + // Clean up + if (client instanceof AutoCloseable) { + try { + ((AutoCloseable) client).close(); + } catch (Exception e) { + fail("Failed to close client: " + e.getMessage()); + } + } + } + + @Test + public void testVertxClientIsUsable() { + A2AHttpClient client = A2AHttpClientFactory.create("vertx"); + assertNotNull(client); + + // Verify we can create builders + A2AHttpClient.GetBuilder getBuilder = client.createGet(); + assertNotNull(getBuilder, "Should be able to create GET builder"); + + A2AHttpClient.PostBuilder postBuilder = client.createPost(); + assertNotNull(postBuilder, "Should be able to create POST builder"); + + A2AHttpClient.DeleteBuilder deleteBuilder = client.createDelete(); + assertNotNull(deleteBuilder, "Should be able to create DELETE builder"); + + // Clean up + if (client instanceof AutoCloseable) { + try { + ((AutoCloseable) client).close(); + } catch (Exception e) { + fail("Failed to close client: " + e.getMessage()); + } + } + } +} diff --git a/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientIntegrationTest.java b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientIntegrationTest.java new file mode 100644 index 000000000..f945aecde --- /dev/null +++ b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientIntegrationTest.java @@ -0,0 +1,212 @@ +package io.a2a.client.http; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import io.a2a.common.A2AErrorMessages; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; + +public class VertxA2AHttpClientIntegrationTest { + + private ClientAndServer mockServer; + private VertxA2AHttpClient client; + + @BeforeEach + public void setup() { + mockServer = ClientAndServer.startClientAndServer(0); // Use random port + client = new VertxA2AHttpClient(); + } + + @AfterEach + public void teardown() { + if (client != null) { + client.close(); + } + if (mockServer != null) { + mockServer.stop(); + } + } + + private String getBaseUrl() { + return "http://localhost:" + mockServer.getPort(); + } + + @Test + public void testGetRequestSuccess() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/test")) + .respond(response().withStatusCode(200).withBody("success")); + + A2AHttpResponse response = client.createGet() + .url(getBaseUrl() + "/test") + .get(); + + assertEquals(200, response.status()); + assertTrue(response.success()); + assertEquals("success", response.body()); + } + + @Test + public void testPostRequestSuccess() throws Exception { + mockServer + .when(request() + .withMethod("POST") + .withPath("/test") + .withBody("{\"key\":\"value\"}")) + .respond(response().withStatusCode(201).withBody("created")); + + A2AHttpResponse response = client.createPost() + .url(getBaseUrl() + "/test") + .body("{\"key\":\"value\"}") + .post(); + + assertEquals(201, response.status()); + assertTrue(response.success()); + assertEquals("created", response.body()); + } + + @Test + public void testDeleteRequestSuccess() throws Exception { + mockServer + .when(request().withMethod("DELETE").withPath("/test")) + .respond(response().withStatusCode(204)); + + A2AHttpResponse response = client.createDelete() + .url(getBaseUrl() + "/test") + .delete(); + + assertEquals(204, response.status()); + assertTrue(response.success()); + } + + @Test + public void test401AuthenticationErrorOnGet() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/test")) + .respond(response().withStatusCode(401)); + + Exception exception = assertThrows(java.io.IOException.class, () -> { + client.createGet() + .url(getBaseUrl() + "/test") + .get(); + }); + + assertEquals(A2AErrorMessages.AUTHENTICATION_FAILED, exception.getMessage()); + } + + @Test + public void test403AuthorizationErrorOnGet() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/test")) + .respond(response().withStatusCode(403)); + + Exception exception = assertThrows(java.io.IOException.class, () -> { + client.createGet() + .url(getBaseUrl() + "/test") + .get(); + }); + + assertEquals(A2AErrorMessages.AUTHORIZATION_FAILED, exception.getMessage()); + } + + @Test + public void test401AuthenticationErrorOnPost() throws Exception { + mockServer + .when(request().withMethod("POST").withPath("/test")) + .respond(response().withStatusCode(401)); + + Exception exception = assertThrows(java.io.IOException.class, () -> { + client.createPost() + .url(getBaseUrl() + "/test") + .body("{}") + .post(); + }); + + assertEquals(A2AErrorMessages.AUTHENTICATION_FAILED, exception.getMessage()); + } + + @Test + public void test403AuthorizationErrorOnPost() throws Exception { + mockServer + .when(request().withMethod("POST").withPath("/test")) + .respond(response().withStatusCode(403)); + + Exception exception = assertThrows(java.io.IOException.class, () -> { + client.createPost() + .url(getBaseUrl() + "/test") + .body("{}") + .post(); + }); + + assertEquals(A2AErrorMessages.AUTHORIZATION_FAILED, exception.getMessage()); + } + + @Test + public void test401AuthenticationErrorOnDelete() throws Exception { + mockServer + .when(request().withMethod("DELETE").withPath("/test")) + .respond(response().withStatusCode(401)); + + Exception exception = assertThrows(java.io.IOException.class, () -> { + client.createDelete() + .url(getBaseUrl() + "/test") + .delete(); + }); + + assertEquals(A2AErrorMessages.AUTHENTICATION_FAILED, exception.getMessage()); + } + + @Test + public void testHeaderPropagation() throws Exception { + mockServer + .when(request() + .withMethod("GET") + .withPath("/test") + .withHeader("Authorization", "Bearer token") + .withHeader("X-Custom-Header", "custom-value")) + .respond(response().withStatusCode(200).withBody("ok")); + + A2AHttpResponse response = client.createGet() + .url(getBaseUrl() + "/test") + .addHeader("Authorization", "Bearer token") + .addHeader("X-Custom-Header", "custom-value") + .get(); + + assertEquals(200, response.status()); + assertEquals("ok", response.body()); + } + + @Test + public void testNonSuccessStatusCode() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/test")) + .respond(response().withStatusCode(500).withBody("Internal Server Error")); + + A2AHttpResponse response = client.createGet() + .url(getBaseUrl() + "/test") + .get(); + + assertEquals(500, response.status()); + assertFalse(response.success()); + assertEquals("Internal Server Error", response.body()); + } + + @Test + public void test404NotFound() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/test")) + .respond(response().withStatusCode(404).withBody("Not Found")); + + A2AHttpResponse response = client.createGet() + .url(getBaseUrl() + "/test") + .get(); + + assertEquals(404, response.status()); + assertFalse(response.success()); + assertEquals("Not Found", response.body()); + } +} diff --git a/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientSSETest.java b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientSSETest.java new file mode 100644 index 000000000..64b1dec54 --- /dev/null +++ b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientSSETest.java @@ -0,0 +1,253 @@ +package io.a2a.client.http; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import io.a2a.common.A2AErrorMessages; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockserver.integration.ClientAndServer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class VertxA2AHttpClientSSETest { + + private ClientAndServer mockServer; + private VertxA2AHttpClient client; + + @BeforeEach + public void setup() { + mockServer = ClientAndServer.startClientAndServer(0); // Use random port + client = new VertxA2AHttpClient(); + } + + @AfterEach + public void teardown() { + if (client != null) { + client.close(); + } + if (mockServer != null) { + mockServer.stop(); + } + } + + private String getBaseUrl() { + return "http://localhost:" + mockServer.getPort(); + } + + @Test + public void testGetAsyncSSE() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/sse")) + .respond(response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody("data: event1\n\ndata: event2\n\ndata: event3\n\n")); + + CountDownLatch latch = new CountDownLatch(1); + List events = new ArrayList<>(); + AtomicReference error = new AtomicReference<>(); + + CompletableFuture future = client.createGet() + .url(getBaseUrl() + "/sse") + .getAsyncSSE( + events::add, + error::set, + latch::countDown + ); + + assertTrue(latch.await(5, TimeUnit.SECONDS), "Expected completion handler to be called"); + assertNull(error.get(), "Expected no errors"); + assertFalse(events.isEmpty(), "Expected to receive events"); + assertTrue(events.contains("event1"), "Expected event1"); + assertTrue(events.contains("event2"), "Expected event2"); + assertTrue(events.contains("event3"), "Expected event3"); + } + + @Test + public void testPostAsyncSSE() throws Exception { + mockServer + .when(request() + .withMethod("POST") + .withPath("/sse") + .withBody("{\"subscribe\":true}")) + .respond(response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody("data: message1\n\ndata: message2\n\n")); + + CountDownLatch latch = new CountDownLatch(1); + List events = new ArrayList<>(); + AtomicReference error = new AtomicReference<>(); + + CompletableFuture future = client.createPost() + .url(getBaseUrl() + "/sse") + .body("{\"subscribe\":true}") + .postAsyncSSE( + events::add, + error::set, + latch::countDown + ); + + assertTrue(latch.await(5, TimeUnit.SECONDS), "Expected completion handler to be called"); + assertNull(error.get(), "Expected no errors"); + assertFalse(events.isEmpty(), "Expected to receive events"); + assertTrue(events.contains("message1"), "Expected message1"); + assertTrue(events.contains("message2"), "Expected message2"); + } + + @Test + public void testSSEDataPrefixStripping() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/sse")) + .respond(response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody("data: content here\n\ndata:no space\n\ndata: extra spaces \n\n")); + + CountDownLatch latch = new CountDownLatch(1); + List events = new ArrayList<>(); + AtomicReference error = new AtomicReference<>(); + + CompletableFuture future = client.createGet() + .url(getBaseUrl() + "/sse") + .getAsyncSSE( + events::add, + error::set, + latch::countDown + ); + + assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertNull(error.get()); + assertTrue(events.contains("content here"), "Should have stripped 'data: ' prefix"); + assertTrue(events.contains("no space"), "Should handle 'data:' without space"); + assertTrue(events.contains("extra spaces"), "Should trim whitespace"); + } + + @Test + public void testSSEAuthenticationError() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/sse")) + .respond(response().withStatusCode(401)); + + CountDownLatch errorLatch = new CountDownLatch(1); + AtomicReference error = new AtomicReference<>(); + AtomicBoolean completed = new AtomicBoolean(false); + + CompletableFuture future = client.createGet() + .url(getBaseUrl() + "/sse") + .getAsyncSSE( + msg -> {}, + e -> { + error.set(e); + errorLatch.countDown(); + }, + () -> completed.set(true) + ); + + assertTrue(errorLatch.await(5, TimeUnit.SECONDS), "Expected error handler to be called"); + assertNotNull(error.get(), "Expected an error"); + assertTrue(error.get() instanceof IOException, "Expected IOException"); + assertTrue(error.get().getMessage().contains("Authentication failed"), + "Expected authentication error message but got: " + error.get().getMessage()); + assertFalse(completed.get(), "Should not call completion handler on error"); + } + + @Test + public void testSSEAuthorizationError() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/sse")) + .respond(response().withStatusCode(403)); + + CountDownLatch errorLatch = new CountDownLatch(1); + AtomicReference error = new AtomicReference<>(); + AtomicBoolean completed = new AtomicBoolean(false); + + CompletableFuture future = client.createGet() + .url(getBaseUrl() + "/sse") + .getAsyncSSE( + msg -> {}, + e -> { + error.set(e); + errorLatch.countDown(); + }, + () -> completed.set(true) + ); + + assertTrue(errorLatch.await(5, TimeUnit.SECONDS), "Expected error handler to be called"); + assertNotNull(error.get(), "Expected an error"); + assertTrue(error.get() instanceof IOException, "Expected IOException"); + assertTrue(error.get().getMessage().contains("Authorization failed"), + "Expected authorization error message but got: " + error.get().getMessage()); + assertFalse(completed.get(), "Should not call completion handler on error"); + } + + @Test + public void testSSEEmptyLinesIgnored() throws Exception { + mockServer + .when(request().withMethod("GET").withPath("/sse")) + .respond(response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody("data: first\n\n\n\ndata: second\n\ndata: \n\ndata: third\n\n")); + + CountDownLatch latch = new CountDownLatch(1); + List events = new ArrayList<>(); + AtomicReference error = new AtomicReference<>(); + + CompletableFuture future = client.createGet() + .url(getBaseUrl() + "/sse") + .getAsyncSSE( + events::add, + error::set, + latch::countDown + ); + + assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertNull(error.get()); + assertEquals(3, events.size(), "Should have received 3 non-empty events"); + assertTrue(events.contains("first")); + assertTrue(events.contains("second")); + assertTrue(events.contains("third")); + } + + @Test + public void testSSEHeaderPropagation() throws Exception { + mockServer + .when(request() + .withMethod("GET") + .withPath("/sse") + .withHeader("Accept", "text/event-stream") + .withHeader("Authorization", "Bearer token")) + .respond(response() + .withStatusCode(200) + .withHeader("Content-Type", "text/event-stream") + .withBody("data: authenticated\n\n")); + + CountDownLatch latch = new CountDownLatch(1); + List events = new ArrayList<>(); + AtomicReference error = new AtomicReference<>(); + + CompletableFuture future = client.createGet() + .url(getBaseUrl() + "/sse") + .addHeader("Authorization", "Bearer token") + .getAsyncSSE( + events::add, + error::set, + latch::countDown + ); + + assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertNull(error.get()); + assertTrue(events.contains("authenticated")); + } +} diff --git a/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientTest.java b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientTest.java new file mode 100644 index 000000000..55e9afd94 --- /dev/null +++ b/extras/http-client-vertx/src/test/java/io/a2a/client/http/VertxA2AHttpClientTest.java @@ -0,0 +1,94 @@ +package io.a2a.client.http; + +import static org.junit.jupiter.api.Assertions.*; + +import io.vertx.core.Vertx; +import org.junit.jupiter.api.Test; + +public class VertxA2AHttpClientTest { + + @Test + public void testNoArgsConstructor() { + VertxA2AHttpClient client = new VertxA2AHttpClient(); + assertNotNull(client); + client.close(); + } + + @Test + public void testVertxParameterConstructor() { + Vertx vertx = Vertx.vertx(); + VertxA2AHttpClient client = new VertxA2AHttpClient(vertx); + assertNotNull(client); + client.close(); + vertx.close(); + } + + @Test + public void testVertxParameterConstructorNullThrows() { + assertThrows(NullPointerException.class, () -> { + new VertxA2AHttpClient(null); + }); + } + + @Test + public void testCreateGet() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.GetBuilder builder = client.createGet(); + assertNotNull(builder); + } + } + + @Test + public void testCreatePost() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.PostBuilder builder = client.createPost(); + assertNotNull(builder); + } + } + + @Test + public void testCreateDelete() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.DeleteBuilder builder = client.createDelete(); + assertNotNull(builder); + } + } + + @Test + public void testBuilderUrlSetting() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.GetBuilder builder = client.createGet(); + A2AHttpClient.GetBuilder result = builder.url("https://example.com"); + assertSame(builder, result, "Builder should return itself for method chaining"); + } + } + + @Test + public void testBuilderHeaderSetting() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.GetBuilder builder = client.createGet(); + A2AHttpClient.GetBuilder result = builder.addHeader("Accept", "application/json"); + assertSame(builder, result, "Builder should return itself for method chaining"); + } + } + + @Test + public void testBuilderMethodChaining() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.GetBuilder builder = client.createGet() + .url("https://example.com") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer token"); + assertNotNull(builder); + } + } + + @Test + public void testPostBuilderBody() { + try (VertxA2AHttpClient client = new VertxA2AHttpClient()) { + A2AHttpClient.PostBuilder builder = client.createPost(); + A2AHttpClient.PostBuilder result = builder.body("{\"key\":\"value\"}"); + assertSame(builder, result, "Builder should return itself for method chaining"); + } + } +} diff --git a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java index 6d2e5f55f..b4847cd40 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java +++ b/http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java @@ -27,18 +27,18 @@ public class A2ACardResolver { private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent-card.json"; /** - * Get the agent card for an A2A agent. The {@code JdkA2AHttpClient} will be used to fetch the agent card. + * Get the agent card for an A2A agent. An HTTP client will be auto-selected via {@link A2AHttpClientFactory}. * * @param baseUrl the base URL for the agent whose agent card we want to retrieve, must not be null * @throws A2AClientError if the URL for the agent is invalid * @throws IllegalArgumentException if baseUrl is null */ public A2ACardResolver(String baseUrl) throws A2AClientError { - this(new JdkA2AHttpClient(), baseUrl, null, null); + this(A2AHttpClientFactory.create(), baseUrl, null, null); } /** - * Get the agent card for an A2A agent. The {@code JdkA2AHttpClient} will be used to fetch the agent card. + * Get the agent card for an A2A agent. An HTTP client will be auto-selected via {@link A2AHttpClientFactory}. * * @param baseUrl the base URL for the agent whose agent card we want to retrieve, must not be null * @param tenant the tenant path to use when fetching the agent card, may be null for no tenant @@ -46,7 +46,7 @@ public A2ACardResolver(String baseUrl) throws A2AClientError { * @throws IllegalArgumentException if baseUrl is null */ public A2ACardResolver(String baseUrl, @Nullable String tenant) throws A2AClientError { - this(new JdkA2AHttpClient(), baseUrl, tenant, null); + this(A2AHttpClientFactory.create(), baseUrl, tenant, null); } /** diff --git a/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java index 8d7f2d0f9..9e4f5f705 100644 --- a/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java @@ -9,6 +9,8 @@ public interface A2AHttpClient { String CONTENT_TYPE= "Content-Type"; String APPLICATION_JSON= "application/json"; + String ACCEPT = "Accept"; + String EVENT_STREAM = "text/event-stream"; GetBuilder createGet(); diff --git a/http-client/src/main/java/io/a2a/client/http/A2AHttpClientFactory.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpClientFactory.java new file mode 100644 index 000000000..05bac3112 --- /dev/null +++ b/http-client/src/main/java/io/a2a/client/http/A2AHttpClientFactory.java @@ -0,0 +1,89 @@ +package io.a2a.client.http; + +import java.util.Comparator; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +/** + * Factory for creating {@link A2AHttpClient} instances using the ServiceLoader mechanism. + * + *

    + * This factory discovers available {@link A2AHttpClientProvider} implementations at runtime + * and selects the one with the highest priority. If no providers are found, it falls back + * to creating a {@link JdkA2AHttpClient}. + * + *

    Usage

    + *
    {@code
    + * // Get the default client (highest priority provider)
    + * A2AHttpClient client = A2AHttpClientFactory.create();
    + *
    + * // Use with try-with-resources if the client implements AutoCloseable
    + * try (A2AHttpClient client = A2AHttpClientFactory.create()) {
    + *     A2AHttpResponse response = client.createGet()
    + *         .url("https://example.com")
    + *         .get();
    + * }
    + * }
    + * + *

    Priority System

    + *

    + * Providers are selected based on their priority value (higher is better): + *

      + *
    • JdkA2AHttpClient: priority 0 (fallback)
    • + *
    • VertxA2AHttpClient: priority 100 (preferred when available)
    • + *
    + * + *

    Custom Providers

    + *

    + * To add a custom provider, implement {@link A2AHttpClientProvider} and register it + * in {@code META-INF/services/io.a2a.client.http.A2AHttpClientProvider}. + */ +public final class A2AHttpClientFactory { + + private A2AHttpClientFactory() { + // Utility class + } + + /** + * Creates a new A2AHttpClient instance using the highest priority provider available. + * + *

    + * This method uses the ServiceLoader mechanism to discover providers at runtime. + * If no providers are found, it falls back to creating a {@link JdkA2AHttpClient}. + * + * @return a new A2AHttpClient instance + */ + public static A2AHttpClient create() { + ServiceLoader loader = ServiceLoader.load(A2AHttpClientProvider.class); + + return StreamSupport.stream(loader.spliterator(), false) + .max(Comparator.comparingInt(A2AHttpClientProvider::priority)) + .map(A2AHttpClientProvider::create) + .orElseGet(JdkA2AHttpClient::new); + } + + /** + * Creates a new A2AHttpClient instance using a specific provider by name. + * + *

    + * This method is useful for testing or when you need to force a specific implementation. + * + * @param providerName the name of the provider to use + * @return a new A2AHttpClient instance from the specified provider + * @throws IllegalArgumentException if no provider with the given name is found + */ + public static A2AHttpClient create(String providerName) { + if (providerName == null || providerName.isEmpty()) { + throw new IllegalArgumentException("Provider name must not be null or empty"); + } + + ServiceLoader loader = ServiceLoader.load(A2AHttpClientProvider.class); + + return StreamSupport.stream(loader.spliterator(), false) + .filter(provider -> providerName.equals(provider.name())) + .findFirst() + .map(A2AHttpClientProvider::create) + .orElseThrow(() -> new IllegalArgumentException( + "No A2AHttpClientProvider found with name: " + providerName)); + } +} diff --git a/http-client/src/main/java/io/a2a/client/http/A2AHttpClientProvider.java b/http-client/src/main/java/io/a2a/client/http/A2AHttpClientProvider.java new file mode 100644 index 000000000..8c33c66e6 --- /dev/null +++ b/http-client/src/main/java/io/a2a/client/http/A2AHttpClientProvider.java @@ -0,0 +1,48 @@ +package io.a2a.client.http; + +/** + * Service provider interface for creating {@link A2AHttpClient} instances. + * + *

    + * Implementations of this interface can be registered via the Java ServiceLoader + * mechanism. The {@link A2AHttpClientFactory} will discover and use the highest + * priority provider available. + * + *

    + * To register a provider, create a file named + * {@code META-INF/services/io.a2a.client.http.A2AHttpClientProvider} containing + * the fully qualified class name of your provider implementation. + */ +public interface A2AHttpClientProvider { + + /** + * Creates a new instance of an A2AHttpClient. + * + * @return a new A2AHttpClient instance + */ + A2AHttpClient create(); + + /** + * Returns the priority of this provider. Higher priority providers are + * preferred over lower priority ones. + * + *

    + * Default priorities: + *

      + *
    • JdkA2AHttpClient: 0 (fallback)
    • + *
    • VertxA2AHttpClient: 100 (preferred when available)
    • + *
    + * + * @return the priority value (higher is better) + */ + default int priority() { + return 0; + } + + /** + * Returns the name of this provider for logging and debugging purposes. + * + * @return the provider name + */ + String name(); +} diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java index 9b8003741..d5bc68651 100644 --- a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClient.java @@ -199,7 +199,7 @@ private class JdkGetBuilder extends JdkBuilder implements A2AHttpCli private HttpRequest.Builder createRequestBuilder(boolean SSE) throws IOException { HttpRequest.Builder builder = super.createRequestBuilder().GET(); if (SSE) { - builder.header("Accept", "text/event-stream"); + builder.header(ACCEPT, EVENT_STREAM); } return builder; } @@ -250,7 +250,7 @@ private HttpRequest.Builder createRequestBuilder(boolean SSE) throws IOException HttpRequest.Builder builder = super.createRequestBuilder() .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8)); if (SSE) { - builder.header("Accept", "text/event-stream"); + builder.header(ACCEPT, EVENT_STREAM); } return builder; } diff --git a/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClientProvider.java b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClientProvider.java new file mode 100644 index 000000000..1bf388286 --- /dev/null +++ b/http-client/src/main/java/io/a2a/client/http/JdkA2AHttpClientProvider.java @@ -0,0 +1,27 @@ +package io.a2a.client.http; + +/** + * Service provider for {@link JdkA2AHttpClient}. + * + *

    + * This provider has the lowest priority (0) and serves as the fallback implementation + * when no other providers are available. The JDK HTTP client is always available as it + * uses only standard Java libraries. + */ +public final class JdkA2AHttpClientProvider implements A2AHttpClientProvider { + + @Override + public A2AHttpClient create() { + return new JdkA2AHttpClient(); + } + + @Override + public int priority() { + return 0; // Lowest priority - fallback + } + + @Override + public String name() { + return "jdk"; + } +} diff --git a/http-client/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider b/http-client/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider new file mode 100644 index 000000000..78dbb361e --- /dev/null +++ b/http-client/src/main/resources/META-INF/services/io.a2a.client.http.A2AHttpClientProvider @@ -0,0 +1 @@ +io.a2a.client.http.JdkA2AHttpClientProvider diff --git a/http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryTest.java b/http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryTest.java new file mode 100644 index 000000000..72de52230 --- /dev/null +++ b/http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryTest.java @@ -0,0 +1,88 @@ +package io.a2a.client.http; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class A2AHttpClientFactoryTest { + + @Test + public void testCreateReturnsNonNull() { + A2AHttpClient client = A2AHttpClientFactory.create(); + assertNotNull(client, "Factory should return a non-null client"); + } + + @Test + public void testCreateReturnsJdkClient() { + // When Vertx is not on classpath, JDK client should be used + A2AHttpClient client = A2AHttpClientFactory.create(); + assertNotNull(client); + assertInstanceOf(JdkA2AHttpClient.class, client, + "Factory should return JdkA2AHttpClient when Vertx is not available"); + } + + @Test + public void testCreateWithJdkProviderName() { + A2AHttpClient client = A2AHttpClientFactory.create("jdk"); + assertNotNull(client); + assertInstanceOf(JdkA2AHttpClient.class, client, + "Factory should return JdkA2AHttpClient when 'jdk' provider is requested"); + } + + @Test + public void testCreateWithVertxProviderNameThrows() { + // Vertx provider is not available in the core module + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> A2AHttpClientFactory.create("vertx"), + "Factory should throw IllegalArgumentException when vertx provider is not found" + ); + assertTrue(exception.getMessage().contains("vertx"), + "Exception message should mention the provider name"); + } + + @Test + public void testCreateWithInvalidProviderNameThrows() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> A2AHttpClientFactory.create("nonexistent"), + "Factory should throw IllegalArgumentException for unknown provider" + ); + assertTrue(exception.getMessage().contains("nonexistent"), + "Exception message should mention the provider name"); + } + + @Test + public void testCreateWithNullProviderNameThrows() { + assertThrows( + IllegalArgumentException.class, + () -> A2AHttpClientFactory.create(null), + "Factory should throw IllegalArgumentException for null provider name" + ); + } + + @Test + public void testCreateWithEmptyProviderNameThrows() { + assertThrows( + IllegalArgumentException.class, + () -> A2AHttpClientFactory.create(""), + "Factory should throw IllegalArgumentException for empty provider name" + ); + } + + @Test + public void testCreatedClientIsUsable() { + A2AHttpClient client = A2AHttpClientFactory.create(); + assertNotNull(client); + + // Verify we can create builders + A2AHttpClient.GetBuilder getBuilder = client.createGet(); + assertNotNull(getBuilder, "Should be able to create GET builder"); + + A2AHttpClient.PostBuilder postBuilder = client.createPost(); + assertNotNull(postBuilder, "Should be able to create POST builder"); + + A2AHttpClient.DeleteBuilder deleteBuilder = client.createDelete(); + assertNotNull(deleteBuilder, "Should be able to create DELETE builder"); + } +} diff --git a/http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryUsageExample.java b/http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryUsageExample.java new file mode 100644 index 000000000..86da8c8cf --- /dev/null +++ b/http-client/src/test/java/io/a2a/client/http/A2AHttpClientFactoryUsageExample.java @@ -0,0 +1,122 @@ +package io.a2a.client.http; + +import java.io.IOException; + +/** + * Example demonstrating how to use {@link A2AHttpClientFactory} to obtain HTTP client instances. + * + *

    + * This class shows various usage patterns for the factory-based approach to creating + * A2AHttpClient instances. + */ +public class A2AHttpClientFactoryUsageExample { + + /** + * Example 1: Basic usage with automatic selection of best available client. + */ + public void basicUsage() throws IOException, InterruptedException { + // The factory automatically selects the best available implementation: + // - VertxA2AHttpClient (priority 100) if Vert.x is on the classpath + // - JdkA2AHttpClient (priority 0) as fallback + A2AHttpClient client = A2AHttpClientFactory.create(); + + try { + A2AHttpResponse response = client.createGet() + .url("https://api.example.com/data") + .addHeader("Accept", "application/json") + .get(); + + if (response.success()) { + System.out.println("Response: " + response.body()); + } + } finally { + // Close if the client supports AutoCloseable (Vertx does, JDK doesn't) + if (client instanceof AutoCloseable) { + try { + ((AutoCloseable) client).close(); + } catch (Exception e) { + // Handle close exception + } + } + } + } + + /** + * Example 2: Try-with-resources pattern (recommended for AutoCloseable clients). + */ + public void tryWithResourcesUsage() throws Exception { + A2AHttpClient client = A2AHttpClientFactory.create(); + + // Only use try-with-resources if the client is AutoCloseable + if (client instanceof AutoCloseable) { + try (AutoCloseable closeableClient = (AutoCloseable) client) { + A2AHttpResponse response = client.createPost() + .url("https://api.example.com/submit") + .addHeader("Content-Type", "application/json") + .body("{\"key\":\"value\"}") + .post(); + + System.out.println("Status: " + response.status()); + } + } else { + // Non-closeable client, use normally + A2AHttpResponse response = client.createPost() + .url("https://api.example.com/submit") + .addHeader("Content-Type", "application/json") + .body("{\"key\":\"value\"}") + .post(); + + System.out.println("Status: " + response.status()); + } + } + + /** + * Example 3: Explicitly selecting a specific implementation. + */ + public void specificProviderUsage() throws IOException, InterruptedException { + // Force the use of JDK client even if Vert.x is available + A2AHttpClient jdkClient = A2AHttpClientFactory.create("jdk"); + A2AHttpResponse response = jdkClient.createGet() + .url("https://api.example.com/data") + .get(); + + System.out.println("Using JDK client: " + response.status()); + + // Or explicitly use Vert.x client + try (AutoCloseable vertxClient = (AutoCloseable) A2AHttpClientFactory.create("vertx")) { + A2AHttpResponse vertxResponse = ((A2AHttpClient) vertxClient).createGet() + .url("https://api.example.com/data") + .get(); + + System.out.println("Using Vert.x client: " + vertxResponse.status()); + } catch (Exception e) { + // Handle exceptions + } + } + + /** + * Example 4: Defensive programming - handling unknown implementations. + */ + public void defensiveUsage() throws IOException, InterruptedException { + A2AHttpClient client = null; + try { + client = A2AHttpClientFactory.create(); + + A2AHttpResponse response = client.createGet() + .url("https://api.example.com/data") + .get(); + + System.out.println("Response: " + response.body()); + + } finally { + // Safely close if possible + if (client instanceof AutoCloseable) { + try { + ((AutoCloseable) client).close(); + } catch (Exception e) { + System.err.println("Warning: Failed to close client: " + e.getMessage()); + } + } + } + } +} diff --git a/http-client/src/test/java/io/a2a/client/http/A2AHttpClientProviderTest.java b/http-client/src/test/java/io/a2a/client/http/A2AHttpClientProviderTest.java new file mode 100644 index 000000000..b2e222517 --- /dev/null +++ b/http-client/src/test/java/io/a2a/client/http/A2AHttpClientProviderTest.java @@ -0,0 +1,28 @@ +package io.a2a.client.http; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +public class A2AHttpClientProviderTest { + + @Test + public void testJdkProviderCreatesClient() { + JdkA2AHttpClientProvider provider = new JdkA2AHttpClientProvider(); + A2AHttpClient client = provider.create(); + assertNotNull(client); + assertInstanceOf(JdkA2AHttpClient.class, client); + } + + @Test + public void testJdkProviderPriority() { + JdkA2AHttpClientProvider provider = new JdkA2AHttpClientProvider(); + assertEquals(0, provider.priority(), "JDK provider should have priority 0"); + } + + @Test + public void testJdkProviderName() { + JdkA2AHttpClientProvider provider = new JdkA2AHttpClientProvider(); + assertEquals("jdk", provider.name(), "JDK provider name should be 'jdk'"); + } +} diff --git a/pom.xml b/pom.xml index 643b56e53..9a712913a 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 1.7.1 4.33.1 0.6.1 - 3.30.1 + 3.30.6 5.5.1 2.0.17 1.5.18 @@ -119,6 +119,11 @@ a2a-java-sdk-http-client ${project.version} + + ${project.groupId} + a2a-java-sdk-http-client-vertx + ${project.version} + ${project.groupId} a2a-java-sdk-jsonrpc-common @@ -510,6 +515,7 @@ extras/task-store-database-jpa extras/push-notification-config-store-database-jpa extras/queue-manager-replicated + extras/http-client-vertx http-client jsonrpc-common integrations/microprofile-config diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 09f7cc517..d711c2773 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -35,6 +35,11 @@ a2a-java-sdk-jsonrpc-common ${project.version} + + ${project.groupId} + a2a-java-sdk-http-client-vertx + test + ${project.groupId} a2a-java-sdk-tests-server-common diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCJdkTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCJdkTest.java new file mode 100644 index 000000000..4f1e76251 --- /dev/null +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCJdkTest.java @@ -0,0 +1,16 @@ +package io.a2a.server.apps.quarkus; + +import io.a2a.client.ClientBuilder; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class QuarkusA2AJSONRPCJdkTest extends QuarkusA2AJSONRPCTest { + + @Override + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder().httpClient(new JdkA2AHttpClient())); + } +} diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java index 7426cb5bc..4123262cb 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCTest.java @@ -1,14 +1,10 @@ package io.a2a.server.apps.quarkus; import io.a2a.client.ClientBuilder; -import io.a2a.client.transport.jsonrpc.JSONRPCTransport; -import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.TransportProtocol; -import io.quarkus.test.junit.QuarkusTest; -@QuarkusTest -public class QuarkusA2AJSONRPCTest extends AbstractA2AServerTest { +public abstract class QuarkusA2AJSONRPCTest extends AbstractA2AServerTest { public QuarkusA2AJSONRPCTest() { super(8081); @@ -25,7 +21,5 @@ protected String getTransportUrl() { } @Override - protected void configureTransport(ClientBuilder builder) { - builder.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()); - } + protected abstract void configureTransport(ClientBuilder builder); } diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCVertxTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCVertxTest.java new file mode 100644 index 000000000..69370566b --- /dev/null +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/QuarkusA2AJSONRPCVertxTest.java @@ -0,0 +1,21 @@ +package io.a2a.server.apps.quarkus; + +import io.a2a.client.ClientBuilder; +import io.a2a.client.http.VertxA2AHttpClient; +import io.a2a.client.transport.jsonrpc.JSONRPCTransport; +import io.a2a.client.transport.jsonrpc.JSONRPCTransportConfigBuilder; +import io.quarkus.test.junit.QuarkusTest; +import io.vertx.core.Vertx; +import jakarta.inject.Inject; + +@QuarkusTest +public class QuarkusA2AJSONRPCVertxTest extends QuarkusA2AJSONRPCTest { + + @Inject + Vertx vertx; + + @Override + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder().httpClient(new VertxA2AHttpClient(vertx))); + } +} diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index b284e140d..6c45cb659 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -35,6 +35,11 @@ a2a-java-sdk-jsonrpc-common ${project.version} + + ${project.groupId} + a2a-java-sdk-http-client-vertx + test + ${project.groupId} a2a-java-sdk-client-transport-rest @@ -107,7 +112,7 @@ maven-surefire-plugin - 3.5.3 + 3.5.4 org.jboss.logmanager.LogManager diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestJdkTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestJdkTest.java new file mode 100644 index 000000000..74cdc958f --- /dev/null +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestJdkTest.java @@ -0,0 +1,16 @@ +package io.a2a.server.rest.quarkus; + +import io.a2a.client.ClientBuilder; +import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.transport.rest.RestTransport; +import io.a2a.client.transport.rest.RestTransportConfigBuilder; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class QuarkusA2ARestJdkTest extends QuarkusA2ARestTest { + + @Override + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(RestTransport.class, new RestTransportConfigBuilder().httpClient(new JdkA2AHttpClient())); + } +} diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java index 5790fc6c5..0940c5afc 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestTest.java @@ -6,16 +6,12 @@ import java.net.http.HttpResponse; import io.a2a.client.ClientBuilder; -import io.a2a.client.transport.rest.RestTransport; -import io.a2a.client.transport.rest.RestTransportConfigBuilder; import io.a2a.server.apps.common.AbstractA2AServerTest; import io.a2a.spec.TransportProtocol; -import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -@QuarkusTest -public class QuarkusA2ARestTest extends AbstractA2AServerTest { +public abstract class QuarkusA2ARestTest extends AbstractA2AServerTest { public QuarkusA2ARestTest() { super(8081); @@ -32,9 +28,7 @@ protected String getTransportUrl() { } @Override - protected void configureTransport(ClientBuilder builder) { - builder.withTransport(RestTransport.class, new RestTransportConfigBuilder()); - } + protected abstract void configureTransport(ClientBuilder builder); @Test public void testMethodNotFound() throws Exception { diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestVertxTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestVertxTest.java new file mode 100644 index 000000000..106c52fd2 --- /dev/null +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/QuarkusA2ARestVertxTest.java @@ -0,0 +1,21 @@ +package io.a2a.server.rest.quarkus; + +import io.a2a.client.ClientBuilder; +import io.a2a.client.http.VertxA2AHttpClient; +import io.a2a.client.transport.rest.RestTransport; +import io.a2a.client.transport.rest.RestTransportConfigBuilder; +import io.quarkus.test.junit.QuarkusTest; +import io.vertx.core.Vertx; +import jakarta.inject.Inject; + +@QuarkusTest +public class QuarkusA2ARestVertxTest extends QuarkusA2ARestTest { + + @Inject + Vertx vertx; + + @Override + protected void configureTransport(ClientBuilder builder) { + builder.withTransport(RestTransport.class, new RestTransportConfigBuilder().httpClient(new VertxA2AHttpClient(vertx))); + } +} diff --git a/server-common/pom.xml b/server-common/pom.xml index 1f8633c77..fb9a3fdfa 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -34,6 +34,11 @@ ${project.groupId} a2a-java-sdk-http-client + + ${project.groupId} + a2a-java-sdk-http-client-vertx + provided + ${project.groupId} a2a-java-sdk-client-transport-jsonrpc diff --git a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java index 5f6ddec2e..d6d4a2369 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java +++ b/server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java @@ -16,7 +16,7 @@ import io.a2a.client.http.A2AHttpClient; -import io.a2a.client.http.JdkA2AHttpClient; +import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; @@ -52,7 +52,7 @@ protected BasePushNotificationSender() { @Inject public BasePushNotificationSender(PushNotificationConfigStore configStore) { - this.httpClient = new JdkA2AHttpClient(); + this.httpClient = A2AHttpClientFactory.create(); this.configStore = configStore; } From c50990c1c8830677b61f3a7f8e595d1f4decb3bc Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 19 Jan 2026 09:26:53 +0000 Subject: [PATCH 262/493] fix: ListTasks validation and serialization across all transports (#460) These fixes are needed for the TCK tests in https://github.com/a2aproject/a2a-tck/pull/93 - Fix pageSize=0 validation using hasPageSize() instead of zeroToNull - Add includingDefaultValueFields() for complete JSON responses - Validate enum UNRECOGNIZED, negative timestamps across transports - Support multiple timestamp/status formats in REST - Ensure consistent InvalidParamsError behavior across JSON-RPC, gRPC, REST --- .gitignore | 1 + .../database/jpa/JpaDatabaseTaskStore.java | 61 +++++++-------- .../a2a/server/tasks/InMemoryTaskStore.java | 33 +++++--- .../a2a/grpc/mapper/A2ACommonFieldMapper.java | 46 +++++++++-- .../grpc/mapper/ListTasksParamsMapper.java | 3 +- .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 18 ++--- .../java/io/a2a/spec/ListTasksParams.java | 45 ++++++----- .../grpc/handler/GrpcHandlerTest.java | 49 ++++++++++++ .../jsonrpc/handler/JSONRPCHandlerTest.java | 28 +++++++ .../transport/rest/handler/RestHandler.java | 71 ++++++++++++----- .../rest/handler/RestHandlerTest.java | 78 +++++++++++++++++++ 11 files changed, 335 insertions(+), 98 deletions(-) diff --git a/.gitignore b/.gitignore index 82b1fecc1..c95331d77 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ pom.xml.releaseBackup pom.xml.versionsBackup release.properties .flattened-pom.xml +*.args # Eclipse .project diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 49dad1728..840773ea6 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -226,6 +226,25 @@ public ListTasksResult list(ListTasksParams params) { LOGGER.debug("Listing tasks with params: contextId={}, status={}, pageSize={}, pageToken={}", params.contextId(), params.status(), params.pageSize(), params.pageToken()); + // Parse pageToken once at the beginning + Instant tokenTimestamp = null; + String tokenId = null; + if (params.pageToken() != null && !params.pageToken().isEmpty()) { + String[] tokenParts = params.pageToken().split(":", 2); + if (tokenParts.length == 2) { + try { + long timestampMillis = Long.parseLong(tokenParts[0]); + tokenId = tokenParts[1]; + tokenTimestamp = Instant.ofEpochMilli(timestampMillis); + } catch (NumberFormatException e) { + throw new io.a2a.spec.InvalidParamsError(null, + "Invalid pageToken format: timestamp must be numeric milliseconds", null); + } + } else { + throw new io.a2a.spec.InvalidParamsError(null, "Invalid pageToken format: expected 'timestamp:id'", null); + } + } + // Build dynamic JPQL query with WHERE clauses for filtering StringBuilder queryBuilder = new StringBuilder("SELECT t FROM JpaTask t WHERE 1=1"); StringBuilder countQueryBuilder = new StringBuilder("SELECT COUNT(t) FROM JpaTask t WHERE 1=1"); @@ -249,18 +268,9 @@ public ListTasksResult list(ListTasksParams params) { } // Apply pagination cursor using keyset pagination for composite sort (timestamp DESC, id ASC) - // PageToken format: "timestamp_millis:taskId" (e.g., "1699999999000:task-123") - if (params.pageToken() != null && !params.pageToken().isEmpty()) { - String[] tokenParts = params.pageToken().split(":", 2); - if (tokenParts.length == 2) { - // Keyset pagination: get tasks where timestamp < tokenTimestamp OR (timestamp = tokenTimestamp AND id > tokenId) - // All tasks have timestamps (TaskStatus canonical constructor ensures this) - queryBuilder.append(" AND (t.statusTimestamp < :tokenTimestamp OR (t.statusTimestamp = :tokenTimestamp AND t.id > :tokenId))"); - } else { - // Legacy ID-only pageToken format is not supported with timestamp-based sorting - // Throw error to prevent incorrect pagination results - throw new io.a2a.spec.InvalidParamsError(null, "Invalid pageToken format: expected 'timestamp:id'", null); - } + if (tokenTimestamp != null) { + // Keyset pagination: get tasks where timestamp < tokenTimestamp OR (timestamp = tokenTimestamp AND id > tokenId) + queryBuilder.append(" AND (t.statusTimestamp < :tokenTimestamp OR (t.statusTimestamp = :tokenTimestamp AND t.id > :tokenId))"); } // Sort by status timestamp descending (most recent first), then by ID for stable ordering @@ -279,25 +289,9 @@ public ListTasksResult list(ListTasksParams params) { if (params.lastUpdatedAfter() != null) { query.setParameter("lastUpdatedAfter", params.lastUpdatedAfter()); } - if (params.pageToken() != null && !params.pageToken().isEmpty()) { - String[] tokenParts = params.pageToken().split(":", 2); - if (tokenParts.length == 2) { - // Parse keyset pagination parameters - try { - long timestampMillis = Long.parseLong(tokenParts[0]); - String tokenId = tokenParts[1]; - - // All tasks have timestamps (TaskStatus canonical constructor ensures this) - Instant tokenTimestamp = Instant.ofEpochMilli(timestampMillis); - query.setParameter("tokenTimestamp", tokenTimestamp); - query.setParameter("tokenId", tokenId); - } catch (NumberFormatException e) { - // Malformed timestamp in pageToken - throw new io.a2a.spec.InvalidParamsError(null, - "Invalid pageToken format: timestamp must be numeric milliseconds", null); - } - } - // Note: Legacy ID-only format already rejected in query building phase + if (tokenTimestamp != null) { + query.setParameter("tokenTimestamp", tokenTimestamp); + query.setParameter("tokenId", tokenId); } // Apply page size limit (+1 to check for next page) @@ -362,7 +356,10 @@ public ListTasksResult list(ListTasksParams params) { private Task transformTask(Task task, int historyLength, boolean includeArtifacts) { // Limit history if needed (keep most recent N messages) List history = task.history(); - if (historyLength > 0 && history != null && history.size() > historyLength) { + if (historyLength == 0) { + // When historyLength is 0, return empty history + history = List.of(); + } else if (historyLength > 0 && history != null && history.size() > historyLength) { history = history.subList(history.size() - historyLength, history.size()); } diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index d2d9362ca..f1243e272 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -90,18 +90,24 @@ public ListTasksResult list(ListTasksParams params) { int mid = left + (right - left) / 2; Task task = allFilteredTasks.get(mid); - // All tasks have timestamps (TaskStatus canonical constructor ensures this) - // Truncate to milliseconds for consistency with pageToken precision - java.time.Instant taskTimestamp = task.status().timestamp().toInstant() - .truncatedTo(java.time.temporal.ChronoUnit.MILLIS); - int timestampCompare = taskTimestamp.compareTo(tokenTimestamp); - - if (timestampCompare < 0 || (timestampCompare == 0 && task.id().compareTo(tokenId) > 0)) { - // This task is after the token, search left half - right = mid; - } else { - // This task is before or equal to token, search right half + java.time.Instant taskTimestamp = (task.status() != null && task.status().timestamp() != null) + ? task.status().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) + : null; + + if (taskTimestamp == null) { + // Task with null timestamp is always "before" a token with a timestamp, as they are sorted last. + // So, we search in the right half. left = mid + 1; + } else { + int timestampCompare = taskTimestamp.compareTo(tokenTimestamp); + + if (timestampCompare < 0 || (timestampCompare == 0 && task.id().compareTo(tokenId) > 0)) { + // This task is after the token, search left half + right = mid; + } else { + // This task is before or equal to token, search right half + left = mid + 1; + } } } startIndex = left; @@ -144,7 +150,10 @@ public ListTasksResult list(ListTasksParams params) { private Task transformTask(Task task, int historyLength, boolean includeArtifacts) { // Limit history if needed (keep most recent N messages) List history = task.history(); - if (historyLength > 0 && history != null && history.size() > historyLength) { + if (historyLength == 0) { + // When historyLength is 0, return empty history + history = List.of(); + } else if (historyLength > 0 && history != null && history.size() > historyLength) { history = history.subList(history.size() - historyLength, history.size()); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java index c3f031e97..723df2e53 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java @@ -285,7 +285,23 @@ default Map metadataFromProto(Struct struct) { */ @Named("zeroToNull") default Integer zeroToNull(int value) { - return value > 0 ? value : null; + return value != 0 ? value : null; + } + + /** + * Converts protobuf int to Integer, preserving all values including 0. + *

    + * Unlike zeroToNull, this method preserves 0 values, allowing compact constructor + * validation to catch invalid values (e.g., pageSize=0 must fail validation). + * For truly optional fields where 0 means "unset", use zeroToNull instead. + * Use this with {@code @Mapping(qualifiedByName = "intToIntegerOrNull")}. + * + * @param value the protobuf int value + * @return Integer (never null for primitive int input) + */ + @Named("intToIntegerOrNull") + default Integer intToIntegerOrNull(int value) { + return value; } /** @@ -337,13 +353,20 @@ default long instantToMillis(Instant instant) { * Converts protobuf milliseconds-since-epoch (int64) to domain Instant. *

    * Returns null if input is 0 (protobuf default for unset field). + * Throws InvalidParamsError for negative values (invalid timestamps). * Use this with {@code @Mapping(qualifiedByName = "millisToInstant")}. * * @param millis milliseconds since epoch * @return domain Instant, or null if millis is 0 + * @throws InvalidParamsError if millis is negative */ @Named("millisToInstant") default Instant millisToInstant(long millis) { + if (millis < 0L) { + throw new InvalidParamsError(null, + "Timestamp must be a non-negative number of milliseconds since epoch, but got: " + millis, + null); + } return millis > 0L ? Instant.ofEpochMilli(millis) : null; } @@ -351,21 +374,32 @@ default Instant millisToInstant(long millis) { // Enum Conversions (handling UNSPECIFIED/UNKNOWN) // ======================================================================== /** - * Converts protobuf TaskState to domain TaskState, treating UNSPECIFIED/UNKNOWN as null. + * Converts protobuf TaskState to domain TaskState, treating UNSPECIFIED as null. *

    - * Protobuf enums default to UNSPECIFIED (0 value) when unset. The domain may also have - * UNKNOWN for unparseable values. Both should map to null for optional fields. + * Protobuf enums default to UNSPECIFIED (0 value) when unset, which maps to null for optional fields. + * However, UNRECOGNIZED (invalid enum values from JSON) throws InvalidParamsError for proper validation. * Use this with {@code @Mapping(qualifiedByName = "taskStateOrNull")}. * * @param state the protobuf TaskState - * @return domain TaskState or null if UNSPECIFIED/UNKNOWN + * @return domain TaskState or null if UNSPECIFIED + * @throws InvalidParamsError if state is UNRECOGNIZED (invalid enum value) */ @Named("taskStateOrNull") default io.a2a.spec.TaskState taskStateOrNull(io.a2a.grpc.TaskState state) { if (state == null || state == io.a2a.grpc.TaskState.TASK_STATE_UNSPECIFIED) { return null; } + // Reject invalid enum values (e.g., "INVALID_STATUS" from JSON) + if (state == io.a2a.grpc.TaskState.UNRECOGNIZED) { + String validStates = java.util.Arrays.stream(io.a2a.spec.TaskState.values()) + .filter(s -> s != io.a2a.spec.TaskState.UNKNOWN) + .map(Enum::name) + .collect(java.util.stream.Collectors.joining(", ")); + throw new InvalidParamsError(null, + "Invalid task state value. Must be one of: " + validStates, + null); + } io.a2a.spec.TaskState result = TaskStateMapper.INSTANCE.fromProto(state); - return result == io.a2a.spec.TaskState.UNKNOWN ? null : result; + return result; } } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java index 05d45d68a..3e666a8e5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java @@ -38,7 +38,8 @@ public interface ListTasksParamsMapper { */ @Mapping(target = "contextId", source = "contextId", qualifiedByName = "emptyToNull") @Mapping(target = "status", source = "status", qualifiedByName = "taskStateOrNull") - @Mapping(target = "pageSize", source = "pageSize", qualifiedByName = "zeroToNull") + // pageSize: Check if field is set using hasPageSize() to distinguish unset (null) from explicit 0 (validation error) + @Mapping(target = "pageSize", expression = "java(request.hasPageSize() ? request.getPageSize() : null)") @Mapping(target = "pageToken", source = "pageToken", qualifiedByName = "emptyToNull") @Mapping(target = "historyLength", source = "historyLength", qualifiedByName = "zeroToNull") @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "millisToInstant") diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index ad46a8f38..c7d1df72f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -458,14 +458,12 @@ private static JsonProcessingException convertProtoBufExceptionToJsonProcessingE return new JsonProcessingException(ERROR_MESSAGE.formatted("unknown parsing error")); } - // Extract field name if present in error message - String prefix = "Cannot find field: "; - if (message.contains(prefix)) { - return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(message.substring(message.indexOf(prefix) + prefix.length())), id); - } - prefix = "Invalid value for"; - if (message.contains(prefix)) { - return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(message.substring(message.indexOf(prefix) + prefix.length())), id); + // Extract field name if present in error message - check common prefixes + String[] prefixes = {"Cannot find field: ", "Invalid value for", "Invalid enum value:"}; + for (String prefix : prefixes) { + if (message.contains(prefix)) { + return new InvalidParamsJsonMappingException(ERROR_MESSAGE.formatted(message.substring(message.indexOf(prefix) + prefix.length())), id); + } } // Try to extract specific error details using regex patterns @@ -548,7 +546,7 @@ public static String toJsonRPCRequest(@Nullable String requestId, String method, output.name("method").value(method); } if (payload != null) { - String resultValue = JsonFormat.printer().omittingInsignificantWhitespace().print(payload); + String resultValue = JsonFormat.printer().includingDefaultValueFields().omittingInsignificantWhitespace().print(payload); output.name("params").jsonValue(resultValue); } output.endObject(); @@ -571,7 +569,7 @@ public static String toJsonRPCResultResponse(Object requestId, com.google.protob output.name("id").value(number.longValue()); } } - String resultValue = JsonFormat.printer().omittingInsignificantWhitespace().print(builder); + String resultValue = JsonFormat.printer().includingDefaultValueFields().omittingInsignificantWhitespace().print(builder); output.name("result").jsonValue(resultValue); output.endObject(); return result.toString(); diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index 6813dcf45..6fb9ff5d1 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -2,7 +2,6 @@ import java.time.Instant; -import io.a2a.util.Assert; import org.jspecify.annotations.Nullable; /** @@ -27,9 +26,12 @@ public record ListTasksParams( @Nullable Boolean includeArtifacts, String tenant ) { + private static final int MIN_PAGE_SIZE = 1; + private static final int MAX_PAGE_SIZE = 100; + private static final int DEFAULT_PAGE_SIZE = 50; /** * Compact constructor for validation. - * Validates that the tenant parameter is not null. + * Validates that the tenant parameter is not null and parameters are within valid ranges. * * @param contextId filter by context ID * @param status filter by task status @@ -39,9 +41,24 @@ public record ListTasksParams( * @param lastUpdatedAfter filter by last update timestamp * @param includeArtifacts whether to include artifacts * @param tenant the tenant identifier + * @throws InvalidParamsError if tenant is null or if pageSize or historyLength are out of valid range */ public ListTasksParams { - Assert.checkNotNullParam("tenant", tenant); + if (tenant == null) { + throw new InvalidParamsError(null, "Parameter 'tenant' may not be null", null); + } + + // Validate pageSize (1-100) + if (pageSize != null && (pageSize < MIN_PAGE_SIZE || pageSize > MAX_PAGE_SIZE)) { + throw new InvalidParamsError(null, + "pageSize must be between " + MIN_PAGE_SIZE + " and " + MAX_PAGE_SIZE + ", got: " + pageSize, null); + } + + // Validate historyLength (>= 0) + if (historyLength != null && historyLength < 0) { + throw new InvalidParamsError(null, + "historyLength must be non-negative, got: " + historyLength, null); + } } /** * Default constructor for listing all tasks. @@ -61,33 +78,23 @@ public ListTasksParams(Integer pageSize, String pageToken) { } /** - * Validates and returns the effective page size (between 1 and 100, defaults to 50). + * Returns the effective page size (defaults to 50 if not specified). + * Values are validated in the constructor to be within the range [1, 100]. * * @return the effective page size */ public int getEffectivePageSize() { - if (pageSize == null) { - return 50; - } - if (pageSize < 1) { - return 1; - } - if (pageSize > 100) { - return 100; - } - return pageSize; + return pageSize != null ? pageSize : DEFAULT_PAGE_SIZE; } /** - * Returns the effective history length (non-negative, defaults to 0). + * Returns the effective history length (defaults to 0 if not specified). + * Values are validated in the constructor to be non-negative. * * @return the effective history length */ public int getEffectiveHistoryLength() { - if (historyLength == null || historyLength < 0) { - return 0; - } - return historyLength; + return historyLength != null ? historyLength : 0; } /** diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 77a2c955f..a3ce7ca2a 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -21,6 +21,8 @@ import io.a2a.grpc.GetTaskRequest; import io.a2a.grpc.ListTaskPushNotificationConfigRequest; import io.a2a.grpc.ListTaskPushNotificationConfigResponse; +import io.a2a.grpc.ListTasksRequest; +import io.a2a.grpc.ListTasksResponse; import io.a2a.grpc.Message; import io.a2a.grpc.Part; import io.a2a.grpc.PushNotificationConfig; @@ -1209,6 +1211,53 @@ private void assertGrpcError(StreamRecorder streamRecorder, Status.Code e Assertions.assertTrue(streamRecorder.getValues().isEmpty()); } + @Test + public void testListTasksNegativeTimestampReturnsInvalidArgument() { + TestGrpcHandler handler = new TestGrpcHandler(CARD, requestHandler, internalExecutor); + StreamRecorder responseObserver = StreamRecorder.create(); + + // Negative timestamp should trigger validation error + ListTasksRequest request = ListTasksRequest.newBuilder() + .setLastUpdatedAfter(-1L) + .setTenant("") + .build(); + + handler.listTasks(request, responseObserver); + + // Should return error with INVALID_ARGUMENT status + Assertions.assertTrue(responseObserver.getError() != null); + StatusRuntimeException error = (StatusRuntimeException) responseObserver.getError(); + assertEquals(Status.INVALID_ARGUMENT.getCode(), error.getStatus().getCode()); + } + + @Test + public void testListTasksEmptyResultIncludesAllFields() throws Exception { + TestGrpcHandler handler = new TestGrpcHandler(CARD, requestHandler, internalExecutor); + StreamRecorder responseObserver = StreamRecorder.create(); + + // Query for a context that doesn't exist - should return empty result + ListTasksRequest request = ListTasksRequest.newBuilder() + .setContextId("nonexistent-context-id") + .setTenant("") + .build(); + + handler.listTasks(request, responseObserver); + + // Should succeed with empty result + Assertions.assertNull(responseObserver.getError()); + List responses = responseObserver.getValues(); + assertEquals(1, responses.size()); + + ListTasksResponse response = responses.get(0); + // Verify all fields are present (even if empty/default) + Assertions.assertNotNull(response.getTasksList(), "tasks field should not be null"); + assertEquals(0, response.getTasksList().size(), "tasks should be empty list"); + assertEquals(0, response.getTotalSize(), "totalSize should be 0"); + assertEquals(0, response.getPageSize(), "pageSize should be 0"); + // nextPageToken will be empty string for empty results (protobuf default) + Assertions.assertNotNull(response.getNextPageToken()); + } + private static class TestGrpcHandler extends GrpcHandler { private final AgentCard card; private final RequestHandler handler; diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index a55233adb..3436a34b1 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -30,12 +30,15 @@ import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksRequest; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; @@ -56,7 +59,9 @@ import io.a2a.spec.Event; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; +import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; +import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; @@ -1959,4 +1964,27 @@ public void testNoVersionDefaultsToCurrentVersionSuccess() { assertNull(response.getError()); Assertions.assertSame(message, response.getResult()); } + + @Test + public void testListTasksEmptyResultIncludesAllFields() { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); + + // Query for a context that doesn't exist - should return empty result + ListTasksParams params = ListTasksParams.builder() + .contextId("nonexistent-context-id") + .tenant("") + .build(); + ListTasksRequest request = new ListTasksRequest("1", params); + ListTasksResponse response = handler.onListTasks(request, callContext); + + // Should return success with all fields present (not null) + assertNull(response.getError()); + ListTasksResult result = response.getResult(); + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.tasks(), "tasks field should not be null"); + assertEquals(0, result.tasks().size(), "tasks should be empty list"); + assertEquals(0, result.totalSize(), "totalSize should be 0"); + assertEquals(0, result.pageSize(), "pageSize should be 0"); + // nextPageToken can be null for empty results + } } diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 87e57f415..4d9a9667d 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -69,6 +69,7 @@ public class RestHandler { private static final Logger log = Logger.getLogger(RestHandler.class.getName()); + private static final String TASK_STATE_PREFIX = "TASK_STATE_"; // Fields set by constructor injection cannot be final. We need a noargs constructor for // Jakarta compatibility, and it seems that making fields set by constructor injection @@ -219,21 +220,41 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s paramsBuilder.contextId(contextId); } if (status != null) { + TaskState taskState = null; + + // Try JSON format first (e.g., "working", "completed") try { - paramsBuilder.status(TaskState.fromString(status)); + taskState = TaskState.fromString(status); } catch (IllegalArgumentException e) { - try { - paramsBuilder.status(TaskState.valueOf(status)); - } catch (IllegalArgumentException valueOfError) { - String validStates = Arrays.stream(TaskState.values()) - .map(TaskState::asString) - .collect(Collectors.joining(", ")); - Map errorData = new HashMap<>(); - errorData.put("parameter", "status"); - errorData.put("reason", "Must be one of: " + validStates); - throw new InvalidParamsError(null, "Invalid params", errorData); + // Try protobuf enum format (e.g., "TASK_STATE_WORKING") + if (status.startsWith(TASK_STATE_PREFIX)) { + String enumName = status.substring(TASK_STATE_PREFIX.length()); + try { + taskState = TaskState.valueOf(enumName); + } catch (IllegalArgumentException protoError) { + // Fall through to error handling below + } + } else { + // Try enum constant name directly (e.g., "WORKING") + try { + taskState = TaskState.valueOf(status); + } catch (IllegalArgumentException valueOfError) { + // Fall through to error handling below + } } } + + if (taskState == null) { + String validStates = Arrays.stream(TaskState.values()) + .map(TaskState::asString) + .collect(Collectors.joining(", ")); + Map errorData = new HashMap<>(); + errorData.put("parameter", "status"); + errorData.put("reason", "Must be one of: " + validStates); + throw new InvalidParamsError(null, "Invalid params", errorData); + } + + paramsBuilder.status(taskState); } if (pageSize != null) { paramsBuilder.pageSize(pageSize); @@ -247,12 +268,25 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s paramsBuilder.tenant(tenant); if (lastUpdatedAfter != null) { try { - paramsBuilder.lastUpdatedAfter(Instant.parse(lastUpdatedAfter)); - } catch (DateTimeParseException e) { - Map errorData = new HashMap<>(); - errorData.put("parameter", "lastUpdatedAfter"); - errorData.put("reason", "Must be valid ISO-8601 timestamp"); - throw new InvalidParamsError(null, "Invalid params", errorData); + // Try parsing as Unix milliseconds first (integer) + long millis = Long.parseLong(lastUpdatedAfter); + if (millis < 0L) { + Map errorData = new HashMap<>(); + errorData.put("parameter", "lastUpdatedAfter"); + errorData.put("reason", "Must be a non-negative timestamp value, got: " + millis); + throw new InvalidParamsError(null, "Invalid params", errorData); + } + paramsBuilder.lastUpdatedAfter(Instant.ofEpochMilli(millis)); + } catch (NumberFormatException nfe) { + // Fall back to ISO-8601 format + try { + paramsBuilder.lastUpdatedAfter(Instant.parse(lastUpdatedAfter)); + } catch (DateTimeParseException e) { + Map errorData = new HashMap<>(); + errorData.put("parameter", "lastUpdatedAfter"); + errorData.put("reason", "Must be valid Unix milliseconds or ISO-8601 timestamp"); + throw new InvalidParamsError(null, "Invalid params", errorData); + } } } if (includeArtifacts != null) { @@ -338,7 +372,8 @@ private void validate(String json) { private HTTPRestResponse createSuccessResponse(int statusCode, com.google.protobuf.Message.Builder builder) { try { - String jsonBody = JsonFormat.printer().print(builder); + // Include default value fields to ensure empty arrays, zeros, etc. are present in JSON + String jsonBody = JsonFormat.printer().includingDefaultValueFields().print(builder); return new HTTPRestResponse(statusCode, "application/json", jsonBody); } catch (InvalidProtocolBufferException e) { return createErrorResponse(new InternalError("Failed to serialize response: " + e.getMessage())); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 1ed1492e1..a2e6ef6e9 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -879,4 +879,82 @@ public void testNoVersionDefaultsToCurrentVersionSuccess() { Assertions.assertEquals("application/json", response.getContentType()); Assertions.assertNotNull(response.getBody()); } + + @Test + public void testListTasksNegativeTimestampReturns422() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + + // Negative timestamp should return 422 (Invalid params) + RestHandler.HTTPRestResponse response = handler.listTasks(null, null, null, null, + null, "-1", null, "", callContext); + + Assertions.assertEquals(422, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("InvalidParamsError")); + } + + @Test + public void testListTasksUnixMillisecondsTimestamp() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + taskStore.save(MINIMAL_TASK); + + // Unix milliseconds timestamp should be accepted + String timestampMillis = String.valueOf(System.currentTimeMillis() - 10000); + RestHandler.HTTPRestResponse response = handler.listTasks(null, null, null, null, + null, timestampMillis, null, "", callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains("tasks")); + } + + @Test + public void testListTasksProtobufEnumStatus() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + taskStore.save(MINIMAL_TASK); + + // Protobuf enum format (TASK_STATE_SUBMITTED) should be accepted + RestHandler.HTTPRestResponse response = handler.listTasks(null, "TASK_STATE_SUBMITTED", null, null, + null, null, null, "", callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id())); + } + + @Test + public void testListTasksEnumConstantStatus() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + taskStore.save(MINIMAL_TASK); + + // Enum constant format (SUBMITTED) should be accepted + RestHandler.HTTPRestResponse response = handler.listTasks(null, "SUBMITTED", null, null, + null, null, null, "", callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + Assertions.assertTrue(response.getBody().contains(MINIMAL_TASK.id())); + } + + @Test + public void testListTasksEmptyResultIncludesAllFields() { + RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); + + // Query for a context that doesn't exist - should return empty result with all fields + RestHandler.HTTPRestResponse response = handler.listTasks("nonexistent-context-id", null, null, null, + null, null, null, "", callContext); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertEquals("application/json", response.getContentType()); + + String body = response.getBody(); + // Verify all required fields are present (not missing) + Assertions.assertTrue(body.contains("\"tasks\""), "Response should contain tasks field"); + Assertions.assertTrue(body.contains("\"totalSize\""), "Response should contain totalSize field"); + Assertions.assertTrue(body.contains("\"pageSize\""), "Response should contain pageSize field"); + Assertions.assertTrue(body.contains("\"nextPageToken\""), "Response should contain nextPageToken field"); + // Verify empty array, not null + Assertions.assertTrue(body.contains("\"tasks\":[]") || body.contains("\"tasks\": []"), + "tasks should be empty array"); + } } From 41247716ed9772af01d8dbf95584b54974246fb1 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 19 Jan 2026 13:29:08 +0000 Subject: [PATCH 263/493] fix: Make historyLength consistent with pageSize in mapper (#600) --- .../main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java index 3e666a8e5..cdb501d9a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java @@ -41,7 +41,8 @@ public interface ListTasksParamsMapper { // pageSize: Check if field is set using hasPageSize() to distinguish unset (null) from explicit 0 (validation error) @Mapping(target = "pageSize", expression = "java(request.hasPageSize() ? request.getPageSize() : null)") @Mapping(target = "pageToken", source = "pageToken", qualifiedByName = "emptyToNull") - @Mapping(target = "historyLength", source = "historyLength", qualifiedByName = "zeroToNull") + // historyLength: Check if field is set using hasHistoryLength() for consistency with pageSize + @Mapping(target = "historyLength", expression = "java(request.hasHistoryLength() ? request.getHistoryLength() : null)") @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "millisToInstant") @Mapping(target = "includeArtifacts", source = "includeArtifacts", qualifiedByName = "falseToNull") io.a2a.spec.ListTasksParams fromProto(ListTasksRequest request); From 434b1becdf8fefc562185ab915ed351a0c70a1b8 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 19 Jan 2026 16:29:03 +0000 Subject: [PATCH 264/493] chore: Prepare 1.0.0.Alpha1 release (#602) --- boms/extras/pom.xml | 2 +- boms/reference/pom.xml | 2 +- boms/sdk/pom.xml | 2 +- boms/test-utils/pom.xml | 2 +- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/cloud-deployment/server/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/common/pom.xml | 2 +- extras/http-client-vertx/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests-multi-instance/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-1/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-2/pom.xml | 2 +- .../tests-multi-instance/quarkus-common/pom.xml | 2 +- .../tests-multi-instance/tests/pom.xml | 2 +- extras/queue-manager-replicated/tests-single-instance/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- integrations/microprofile-config/pom.xml | 2 +- jsonrpc-common/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 44 files changed, 45 insertions(+), 45 deletions(-) diff --git a/boms/extras/pom.xml b/boms/extras/pom.xml index 809a3a6a9..d1d84ddee 100644 --- a/boms/extras/pom.xml +++ b/boms/extras/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/boms/reference/pom.xml b/boms/reference/pom.xml index 1c09486fe..8ad8d20ac 100644 --- a/boms/reference/pom.xml +++ b/boms/reference/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml index 37ecc9d25..421b622d2 100644 --- a/boms/sdk/pom.xml +++ b/boms/sdk/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/boms/test-utils/pom.xml b/boms/test-utils/pom.xml index dd95cd7e4..4d3adb723 100644 --- a/boms/test-utils/pom.xml +++ b/boms/test-utils/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/client/base/pom.xml b/client/base/pom.xml index bd16914a6..ae353f327 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 2c184a7fe..1487b94a3 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index 0cbadd8ab..e6fa67b17 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index 2c682c888..ae0914821 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index ca1005e13..a32b8a516 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index f224e422f..7c7f20c2f 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-common diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index 4db8c96d8..f7fb0168f 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../../pom.xml diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index 0ac460bc5..cf2925b0d 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index 0c19ffad7..ad29c96e0 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:1.0.0.Alpha1-SNAPSHOT -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:1.0.0.Alpha1-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client:1.0.0.Alpha1 +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:1.0.0.Alpha1 //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index ddb562e60..d7d9e6bcf 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 584690dce..00ce3e013 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-examples-server diff --git a/extras/common/pom.xml b/extras/common/pom.xml index 29cedf9c5..13e298723 100644 --- a/extras/common/pom.xml +++ b/extras/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/extras/http-client-vertx/pom.xml b/extras/http-client-vertx/pom.xml index 59bbf1141..071b1beb6 100644 --- a/extras/http-client-vertx/pom.xml +++ b/extras/http-client-vertx/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-http-client-vertx diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index 0258dccc8..f3e35c91e 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 47e8c8bc0..45c7542a8 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index ba7a95b1d..42d58a0c4 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 8a1c461c4..919bf7445 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml index 52ff3c5cb..98255923c 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml index b6caa6a08..6c043636a 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml index 391dc16be..66f2d7e3d 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml index 67e7ee470..fac72e33b 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml index abd49a4c8..108d25f83 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index 72a4451ac..26775faf6 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index b0597ffa2..d8c0c1f49 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index e18e562a9..777c58210 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-http-client diff --git a/integrations/microprofile-config/pom.xml b/integrations/microprofile-config/pom.xml index 62d7da6cd..42338fd1e 100644 --- a/integrations/microprofile-config/pom.xml +++ b/integrations/microprofile-config/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-microprofile-config diff --git a/jsonrpc-common/pom.xml b/jsonrpc-common/pom.xml index ea8e7bae0..e1e41c426 100644 --- a/jsonrpc-common/pom.xml +++ b/jsonrpc-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-jsonrpc-common diff --git a/pom.xml b/pom.xml index 9a712913a..5676f3c15 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 3f3f26b7d..73b1c84a3 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 0c131756e..654025911 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index d711c2773..206cf50a2 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 6c45cb659..91df7052c 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index fb9a3fdfa..686a56325 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 9270b2751..4a850fb31 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index 2f04f5c2a..e55d56f23 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 9b41e49e3..25ff2fdc6 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index ff0298841..3529400f0 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index e1c7ccf05..52731b453 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 59498d8d7..5b0b68616 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index 685de4977..f6bda74b4 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1-SNAPSHOT + 1.0.0.Alpha1 ../../pom.xml a2a-java-sdk-transport-rest From 7059c125d11af8a2f1bcddda263e05e967c563d3 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 19 Jan 2026 17:04:20 +0000 Subject: [PATCH 265/493] chore: Next is 1.0.0.Alpha2 (#603) --- boms/extras/pom.xml | 2 +- boms/reference/pom.xml | 2 +- boms/sdk/pom.xml | 2 +- boms/test-utils/pom.xml | 2 +- client/base/pom.xml | 2 +- client/transport/grpc/pom.xml | 2 +- client/transport/jsonrpc/pom.xml | 2 +- client/transport/rest/pom.xml | 2 +- client/transport/spi/pom.xml | 2 +- common/pom.xml | 2 +- examples/cloud-deployment/server/pom.xml | 2 +- examples/helloworld/client/pom.xml | 2 +- .../java/io/a2a/examples/helloworld/HelloWorldRunner.java | 4 ++-- examples/helloworld/pom.xml | 2 +- examples/helloworld/server/pom.xml | 2 +- extras/common/pom.xml | 2 +- extras/http-client-vertx/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/queue-manager-replicated/core/pom.xml | 2 +- extras/queue-manager-replicated/pom.xml | 2 +- .../queue-manager-replicated/replication-mp-reactive/pom.xml | 2 +- extras/queue-manager-replicated/tests-multi-instance/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-1/pom.xml | 2 +- .../tests-multi-instance/quarkus-app-2/pom.xml | 2 +- .../tests-multi-instance/quarkus-common/pom.xml | 2 +- .../tests-multi-instance/tests/pom.xml | 2 +- extras/queue-manager-replicated/tests-single-instance/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- http-client/pom.xml | 2 +- integrations/microprofile-config/pom.xml | 2 +- jsonrpc-common/pom.xml | 2 +- pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/grpc/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- server-common/pom.xml | 2 +- spec-grpc/pom.xml | 2 +- spec/pom.xml | 2 +- tck/pom.xml | 2 +- tests/server-common/pom.xml | 2 +- transport/grpc/pom.xml | 2 +- transport/jsonrpc/pom.xml | 2 +- transport/rest/pom.xml | 2 +- 44 files changed, 45 insertions(+), 45 deletions(-) diff --git a/boms/extras/pom.xml b/boms/extras/pom.xml index d1d84ddee..7cf7a54a8 100644 --- a/boms/extras/pom.xml +++ b/boms/extras/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/boms/reference/pom.xml b/boms/reference/pom.xml index 8ad8d20ac..f25631bbd 100644 --- a/boms/reference/pom.xml +++ b/boms/reference/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/boms/sdk/pom.xml b/boms/sdk/pom.xml index 421b622d2..963dfc8b5 100644 --- a/boms/sdk/pom.xml +++ b/boms/sdk/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/boms/test-utils/pom.xml b/boms/test-utils/pom.xml index 4d3adb723..1c241831d 100644 --- a/boms/test-utils/pom.xml +++ b/boms/test-utils/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/client/base/pom.xml b/client/base/pom.xml index ae353f327..ecbaaf454 100644 --- a/client/base/pom.xml +++ b/client/base/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-client diff --git a/client/transport/grpc/pom.xml b/client/transport/grpc/pom.xml index 1487b94a3..32e077cab 100644 --- a/client/transport/grpc/pom.xml +++ b/client/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-grpc diff --git a/client/transport/jsonrpc/pom.xml b/client/transport/jsonrpc/pom.xml index e6fa67b17..4437d4fe1 100644 --- a/client/transport/jsonrpc/pom.xml +++ b/client/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-jsonrpc diff --git a/client/transport/rest/pom.xml b/client/transport/rest/pom.xml index ae0914821..ad4a14d56 100644 --- a/client/transport/rest/pom.xml +++ b/client/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-rest diff --git a/client/transport/spi/pom.xml b/client/transport/spi/pom.xml index a32b8a516..abba14137 100644 --- a/client/transport/spi/pom.xml +++ b/client/transport/spi/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../../pom.xml a2a-java-sdk-client-transport-spi diff --git a/common/pom.xml b/common/pom.xml index 7c7f20c2f..c83d33bf2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-common diff --git a/examples/cloud-deployment/server/pom.xml b/examples/cloud-deployment/server/pom.xml index f7fb0168f..d71434b81 100644 --- a/examples/cloud-deployment/server/pom.xml +++ b/examples/cloud-deployment/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../../pom.xml diff --git a/examples/helloworld/client/pom.xml b/examples/helloworld/client/pom.xml index cf2925b0d..a9e0cf0d4 100644 --- a/examples/helloworld/client/pom.xml +++ b/examples/helloworld/client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-examples-client diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java index ad29c96e0..138f7e4f7 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldRunner.java @@ -1,6 +1,6 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS io.github.a2asdk:a2a-java-sdk-client:1.0.0.Alpha1 -//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:1.0.0.Alpha1 +//DEPS io.github.a2asdk:a2a-java-sdk-client:1.0.0.Alpha2-SNAPSHOT +//DEPS io.github.a2asdk:a2a-java-sdk-client-transport-jsonrpc:1.0.0.Alpha2-SNAPSHOT //SOURCES HelloWorldClient.java /** diff --git a/examples/helloworld/pom.xml b/examples/helloworld/pom.xml index d7d9e6bcf..589e9c30d 100644 --- a/examples/helloworld/pom.xml +++ b/examples/helloworld/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 00ce3e013..4b0ea1564 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-examples-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-examples-server diff --git a/extras/common/pom.xml b/extras/common/pom.xml index 13e298723..55bb98e71 100644 --- a/extras/common/pom.xml +++ b/extras/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/extras/http-client-vertx/pom.xml b/extras/http-client-vertx/pom.xml index 071b1beb6..2bb4f1662 100644 --- a/extras/http-client-vertx/pom.xml +++ b/extras/http-client-vertx/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-http-client-vertx diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index f3e35c91e..b944295b3 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-extras-push-notification-config-store-database-jpa diff --git a/extras/queue-manager-replicated/core/pom.xml b/extras/queue-manager-replicated/core/pom.xml index 45c7542a8..91646ca0f 100644 --- a/extras/queue-manager-replicated/core/pom.xml +++ b/extras/queue-manager-replicated/core/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/pom.xml b/extras/queue-manager-replicated/pom.xml index 42d58a0c4..7ae399aff 100644 --- a/extras/queue-manager-replicated/pom.xml +++ b/extras/queue-manager-replicated/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml index 919bf7445..72d284cba 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/pom.xml +++ b/extras/queue-manager-replicated/replication-mp-reactive/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/pom.xml index 98255923c..31ea048fa 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml index 6c043636a..5f7dc9812 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml index 66f2d7e3d..40c45a964 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml index fac72e33b..b45243e7f 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml index 108d25f83..a269f6c48 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-tests-multi-instance-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/queue-manager-replicated/tests-single-instance/pom.xml b/extras/queue-manager-replicated/tests-single-instance/pom.xml index 26775faf6..c01e2c51e 100644 --- a/extras/queue-manager-replicated/tests-single-instance/pom.xml +++ b/extras/queue-manager-replicated/tests-single-instance/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-queue-manager-replicated-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../pom.xml diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index d8c0c1f49..b98aa7bf9 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-extras-task-store-database-jpa diff --git a/http-client/pom.xml b/http-client/pom.xml index 777c58210..af4dcb855 100644 --- a/http-client/pom.xml +++ b/http-client/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-http-client diff --git a/integrations/microprofile-config/pom.xml b/integrations/microprofile-config/pom.xml index 42338fd1e..3b82074e1 100644 --- a/integrations/microprofile-config/pom.xml +++ b/integrations/microprofile-config/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-microprofile-config diff --git a/jsonrpc-common/pom.xml b/jsonrpc-common/pom.xml index e1e41c426..dbbbf42cf 100644 --- a/jsonrpc-common/pom.xml +++ b/jsonrpc-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-jsonrpc-common diff --git a/pom.xml b/pom.xml index 5676f3c15..adf31c903 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT pom diff --git a/reference/common/pom.xml b/reference/common/pom.xml index 73b1c84a3..e05266922 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-common diff --git a/reference/grpc/pom.xml b/reference/grpc/pom.xml index 654025911..b12a68153 100644 --- a/reference/grpc/pom.xml +++ b/reference/grpc/pom.xml @@ -6,7 +6,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 206cf50a2..37252458e 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-jsonrpc diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 91df7052c..318c85e6c 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-reference-rest diff --git a/server-common/pom.xml b/server-common/pom.xml index 686a56325..9c3d594fe 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-server-common diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index 4a850fb31..b602f7613 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-spec-grpc diff --git a/spec/pom.xml b/spec/pom.xml index e55d56f23..d79c4fa07 100644 --- a/spec/pom.xml +++ b/spec/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-java-sdk-spec diff --git a/tck/pom.xml b/tck/pom.xml index 25ff2fdc6..39c289c72 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT a2a-tck-server diff --git a/tests/server-common/pom.xml b/tests/server-common/pom.xml index 3529400f0..fc5bbe52a 100644 --- a/tests/server-common/pom.xml +++ b/tests/server-common/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-tests-server-common diff --git a/transport/grpc/pom.xml b/transport/grpc/pom.xml index 52731b453..0c652c1c3 100644 --- a/transport/grpc/pom.xml +++ b/transport/grpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-grpc diff --git a/transport/jsonrpc/pom.xml b/transport/jsonrpc/pom.xml index 5b0b68616..bb551b930 100644 --- a/transport/jsonrpc/pom.xml +++ b/transport/jsonrpc/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-jsonrpc diff --git a/transport/rest/pom.xml b/transport/rest/pom.xml index f6bda74b4..3722ae44d 100644 --- a/transport/rest/pom.xml +++ b/transport/rest/pom.xml @@ -7,7 +7,7 @@ io.github.a2asdk a2a-java-sdk-parent - 1.0.0.Alpha1 + 1.0.0.Alpha2-SNAPSHOT ../../pom.xml a2a-java-sdk-transport-rest From 99db49f22a9f4f8ecdd394e668c1d3a0fe63a144 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Fri, 23 Jan 2026 12:09:24 +0100 Subject: [PATCH 266/493] feat!: update a2a.proto 0572953 (#606) based on commit https://github.com/a2aproject/A2A/commit/057295311b8ddda63bdda56c82a694c76d307e37 spec-grpc module has been regenerated from the updated a2a.proto Key changes: * Agent card * extendedAgentCard is not specified by capabilities (instead of `supportsExtendedAgentCard` field) * protocolVersions is a list (instead of a single `protocolVersion`) * SendMessage * `msg` field is now named `message` * ListTask * timestamp is now named statusTimestampAfter instead of `lastUpdatedAfter` * OAuthFlows * `deviceCode` has been added, `implicit` & `password` have been removed * AuthorizationCodeOAuthFlow * new `pkce_required` boolean field --------- Signed-off-by: Jeff Mesnil --- .../java/io/a2a/client/AbstractClient.java | 18 +- .../src/main/java/io/a2a/client/Client.java | 15 +- .../AuthenticationAuthorizationTest.java | 2 +- .../java/io/a2a/client/ClientBuilderTest.java | 2 +- .../transport/grpc/EventStreamObserver.java | 4 +- .../transport/grpc/GrpcErrorMapper.java | 4 +- .../client/transport/grpc/GrpcTransport.java | 32 +- .../transport/grpc/GrpcErrorMapperTest.java | 12 +- .../transport/jsonrpc/JSONRPCTransport.java | 32 +- .../jsonrpc/JSONRPCTransportTest.java | 96 +- .../transport/jsonrpc/JsonMessages.java | 18 +- .../transport/rest/RestErrorMapper.java | 5 +- .../client/transport/rest/RestTransport.java | 22 +- .../rest/sse/RestSSEEventListener.java | 4 +- .../transport/rest/RestTransportTest.java | 2 +- .../client/transport/spi/ClientTransport.java | 6 +- .../cloud/CloudAgentCardProducer.java | 2 +- .../examples/cloud/A2ACloudExampleClient.java | 8 +- .../examples/helloworld/HelloWorldClient.java | 6 +- .../helloworld/AgentCardProducer.java | 2 +- .../MultiInstanceReplicationAgentCards.java | 2 +- .../ReplicationTestAgentCardProducer.java | 2 +- .../database/jpa/JpaDatabaseTaskStore.java | 16 +- .../java/io/a2a/client/http/JsonMessages.java | 3 +- .../jsonrpc/common/wrappers/A2AResponse.java | 2 +- ....java => GetExtendedAgentCardRequest.java} | 25 +- ...java => GetExtendedAgentCardResponse.java} | 16 +- .../wrappers/NonStreamingJSONRPCRequest.java | 2 +- .../json/SecuritySchemeSerializationTest.java | 65 - .../grpc/quarkus/QuarkusGrpcHandler.java | 20 + .../server/apps/quarkus/A2AServerRoutes.java | 13 +- .../apps/quarkus/A2AServerRoutesTest.java | 18 +- .../DefaultRequestHandler.java | 18 +- .../a2a/server/tasks/InMemoryTaskStore.java | 4 +- .../server/version/A2AVersionValidator.java | 40 +- .../AbstractA2ARequestHandlerTest.java | 2 +- .../version/A2AVersionValidatorTest.java | 28 +- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 466 ++--- .../java/io/a2a/grpc/AgentCapabilities.java | 113 + .../a2a/grpc/AgentCapabilitiesOrBuilder.java | 19 + .../src/main/java/io/a2a/grpc/AgentCard.java | 1856 +++++------------ .../java/io/a2a/grpc/AgentCardOrBuilder.java | 194 +- .../a2a/grpc/AuthorizationCodeOAuthFlow.java | 87 + .../AuthorizationCodeOAuthFlowOrBuilder.java | 11 + ...AuthFlow.java => DeviceCodeOAuthFlow.java} | 404 ++-- ...java => DeviceCodeOAuthFlowOrBuilder.java} | 42 +- .../main/java/io/a2a/grpc/GetTaskRequest.java | 30 +- .../io/a2a/grpc/GetTaskRequestOrBuilder.java | 10 +- .../java/io/a2a/grpc/ImplicitOAuthFlow.java | 1027 --------- .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java | 106 - .../java/io/a2a/grpc/ListTasksRequest.java | 267 ++- .../a2a/grpc/ListTasksRequestOrBuilder.java | 29 +- .../src/main/java/io/a2a/grpc/Message.java | 26 +- .../src/main/java/io/a2a/grpc/OAuthFlows.java | 484 +---- .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java | 49 +- .../io/a2a/grpc/SendMessageConfiguration.java | 30 +- .../SendMessageConfigurationOrBuilder.java | 10 +- .../java/io/a2a/grpc/SendMessageRequest.java | 168 +- .../a2a/grpc/SendMessageRequestOrBuilder.java | 16 +- .../java/io/a2a/grpc/SendMessageResponse.java | 120 +- .../grpc/SendMessageResponseOrBuilder.java | 16 +- .../main/java/io/a2a/grpc/StreamResponse.java | 120 +- .../io/a2a/grpc/StreamResponseOrBuilder.java | 16 +- .../a2a/grpc/mapper/A2ACommonFieldMapper.java | 38 + .../grpc/mapper/AgentCapabilitiesMapper.java | 1 + .../io/a2a/grpc/mapper/AgentCardMapper.java | 4 - .../AuthorizationCodeOAuthFlowMapper.java | 1 + .../grpc/mapper/ImplicitOAuthFlowMapper.java | 18 - .../grpc/mapper/ListTasksParamsMapper.java | 4 +- .../grpc/mapper/MessageSendParamsMapper.java | 6 +- .../io/a2a/grpc/mapper/OAuthFlowsMapper.java | 5 +- .../grpc/mapper/PasswordOAuthFlowMapper.java | 18 - .../a2a/grpc/mapper/StreamResponseMapper.java | 6 +- .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 20 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 6 +- spec-grpc/src/main/proto/a2a.proto | 106 +- .../grpc/mapper/StreamResponseMapperTest.java | 10 +- .../java/io/a2a/grpc/utils/ToProtoTest.java | 11 +- .../main/java/io/a2a/spec/A2AErrorCodes.java | 4 +- .../java/io/a2a/spec/AgentCapabilities.java | 21 +- spec/src/main/java/io/a2a/spec/AgentCard.java | 46 +- .../a2a/spec/AuthorizationCodeOAuthFlow.java | 4 +- .../java/io/a2a/spec/DeviceCodeOAuthFlow.java | 37 + ... ExtendedAgentCardNotConfiguredError.java} | 12 +- .../java/io/a2a/spec/ImplicitOAuthFlow.java | 40 - .../java/io/a2a/spec/ListTasksParams.java | 18 +- .../src/main/java/io/a2a/spec/OAuthFlows.java | 28 +- .../java/io/a2a/spec/PasswordOAuthFlow.java | 40 - .../io/a2a/spec/VersionNotSupportedError.java | 2 +- .../io/a2a/tck/server/AgentCardProducer.java | 2 +- .../apps/common/AbstractA2AServerTest.java | 16 +- .../server/apps/common/AgentCardProducer.java | 11 +- .../transport/grpc/handler/GrpcHandler.java | 14 +- .../grpc/handler/GrpcHandlerTest.java | 56 +- .../jsonrpc/handler/JSONRPCHandler.java | 25 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 21 +- .../transport/rest/handler/RestHandler.java | 20 +- .../rest/handler/RestHandlerTest.java | 8 +- 98 files changed, 2486 insertions(+), 4477 deletions(-) rename jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/{GetAuthenticatedExtendedCardRequest.java => GetExtendedAgentCardRequest.java} (73%) rename jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/{GetAuthenticatedExtendedCardResponse.java => GetExtendedAgentCardResponse.java} (69%) rename spec-grpc/src/main/java/io/a2a/grpc/{PasswordOAuthFlow.java => DeviceCodeOAuthFlow.java} (69%) rename spec-grpc/src/main/java/io/a2a/grpc/{PasswordOAuthFlowOrBuilder.java => DeviceCodeOAuthFlowOrBuilder.java} (63%) delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java create mode 100644 spec/src/main/java/io/a2a/spec/DeviceCodeOAuthFlow.java rename spec/src/main/java/io/a2a/spec/{ExtendedCardNotConfiguredError.java => ExtendedAgentCardNotConfiguredError.java} (78%) delete mode 100644 spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java delete mode 100644 spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index 75fe48956..5d2785386 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -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. diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index 9976133e5..117351098 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -592,27 +592,28 @@ public void resubscribe(@NonNull TaskIdParams request, } /** - * Retrieve the agent's current agent card. + * Retrieve the agent's extended agent card. *

    - * 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. *

    * Example: *

    {@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());
          * }
    * * @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; } diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java index 0294b3dc4..58b528881 100644 --- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -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), diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index 2c95c2619..56eb7560d 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -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(); diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java index d77b81824..0c928bd75 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/EventStreamObserver.java @@ -25,8 +25,8 @@ public EventStreamObserver(Consumer eventHandler, Consumer interceptors; - private AgentCard agentCard; + private final AgentCard agentCard; private final String agentTenant; public GrpcTransport(Channel channel, AgentCard agentCard) { @@ -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 { @@ -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()); @@ -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 diff --git a/client/transport/grpc/src/test/java/io/a2a/client/transport/grpc/GrpcErrorMapperTest.java b/client/transport/grpc/src/test/java/io/a2a/client/transport/grpc/GrpcErrorMapperTest.java index b68f7c958..864e406d2 100644 --- a/client/transport/grpc/src/test/java/io/a2a/client/transport/grpc/GrpcErrorMapperTest.java +++ b/client/transport/grpc/src/test/java/io/a2a/client/transport/grpc/GrpcErrorMapperTest.java @@ -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; @@ -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")); } diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 8c948c4a8..e991eecb3 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -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; @@ -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; @@ -71,8 +69,7 @@ public class JSONRPCTransport implements ClientTransport { private final A2AHttpClient httpClient; private final AgentInterface agentInterface; private final @Nullable List 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); @@ -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 @@ -288,31 +284,15 @@ public void resubscribe(TaskIdParams request, Consumer 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); } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 3344420a0..9b6f7f017 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -353,96 +353,8 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { assertEquals("jwt", authenticationInfo.schemes().get(0)); } - @Test - public void testA2AClientGetAgentCard() throws Exception { - this.server.when( - request() - .withMethod("GET") - .withPath("/.well-known/agent-card.json") - ) - .respond( - response() - .withStatusCode(200) - .withBody(AGENT_CARD) - ); - - JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - AgentCard agentCard = client.getAgentCard(null); - assertEquals("GeoSpatial Route Planner Agent", agentCard.name()); - assertEquals("Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", agentCard.description()); - assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard).url()); - assertEquals("Example Geo Services Inc.", agentCard.provider().organization()); - assertEquals("https://www.examplegeoservices.com", agentCard.provider().url()); - assertEquals("1.2.0", agentCard.version()); - assertEquals("https://docs.examplegeoservices.com/georoute-agent/api", agentCard.documentationUrl()); - assertTrue(agentCard.capabilities().streaming()); - assertTrue(agentCard.capabilities().pushNotifications()); - assertFalse(agentCard.capabilities().stateTransitionHistory()); - Map securitySchemes = agentCard.securitySchemes(); - assertNotNull(securitySchemes); - OpenIdConnectSecurityScheme google = (OpenIdConnectSecurityScheme) securitySchemes.get("google"); - assertEquals("https://accounts.google.com/.well-known/openid-configuration", google.openIdConnectUrl()); - List>> security = agentCard.security(); - assertEquals(1, security.size()); - Map> securityMap = security.get(0); - List scopes = securityMap.get("google"); - List expectedScopes = List.of("openid", "profile", "email"); - assertEquals(expectedScopes, scopes); - List defaultInputModes = List.of("application/json", "text/plain"); - assertEquals(defaultInputModes, agentCard.defaultInputModes()); - List defaultOutputModes = List.of("application/json", "image/png"); - assertEquals(defaultOutputModes, agentCard.defaultOutputModes()); - List skills = agentCard.skills(); - assertEquals("route-optimizer-traffic", skills.get(0).id()); - assertEquals("Traffic-Aware Route Optimizer", skills.get(0).name()); - assertEquals("Calculates the optimal driving route between two or more locations, taking into account real-time traffic conditions, road closures, and user preferences (e.g., avoid tolls, prefer highways).", skills.get(0).description()); - List tags = List.of("maps", "routing", "navigation", "directions", "traffic"); - assertEquals(tags, skills.get(0).tags()); - List examples = List.of("Plan a route from '1600 Amphitheatre Parkway, Mountain View, CA' to 'San Francisco International Airport' avoiding tolls.", - "{\"origin\": {\"lat\": 37.422, \"lng\": -122.084}, \"destination\": {\"lat\": 37.7749, \"lng\": -122.4194}, \"preferences\": [\"avoid_ferries\"]}"); - assertEquals(examples, skills.get(0).examples()); - assertEquals(defaultInputModes, skills.get(0).inputModes()); - List outputModes = List.of("application/json", "application/vnd.geo+json", "text/html"); - assertEquals(outputModes, skills.get(0).outputModes()); - assertEquals("custom-map-generator", skills.get(1).id()); - assertEquals("Personalized Map Generator", skills.get(1).name()); - assertEquals("Creates custom map images or interactive map views based on user-defined points of interest, routes, and style preferences. Can overlay data layers.", skills.get(1).description()); - tags = List.of("maps", "customization", "visualization", "cartography"); - assertEquals(tags, skills.get(1).tags()); - examples = List.of("Generate a map of my upcoming road trip with all planned stops highlighted.", - "Show me a map visualizing all coffee shops within a 1-mile radius of my current location."); - assertEquals(examples, skills.get(1).examples()); - List inputModes = List.of("application/json"); - assertEquals(inputModes, skills.get(1).inputModes()); - outputModes = List.of("image/png", "image/jpeg", "application/json", "text/html"); - assertEquals(outputModes, skills.get(1).outputModes()); - assertFalse(agentCard.supportsExtendedAgentCard()); - assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.protocolVersion()); - assertEquals("JSONRPC", agentCard.supportedInterfaces().get(0).protocolBinding()); - List additionalInterfaces = agentCard.supportedInterfaces(); - assertEquals(3, additionalInterfaces.size()); - AgentInterface jsonrpc = new AgentInterface(TransportProtocol.JSONRPC.asString(), "https://georoute-agent.example.com/a2a/v1"); - AgentInterface grpc = new AgentInterface(TransportProtocol.GRPC.asString(), "https://georoute-agent.example.com/a2a/grpc"); - AgentInterface httpJson = new AgentInterface(TransportProtocol.HTTP_JSON.asString(), "https://georoute-agent.example.com/a2a/json"); - assertEquals(jsonrpc, additionalInterfaces.get(0)); - assertEquals(grpc, additionalInterfaces.get(1)); - assertEquals(httpJson, additionalInterfaces.get(2)); - } - - @Test - public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { - this.server.when( - request() - .withMethod("GET") - .withPath("/.well-known/agent-card.json") - ) - .respond( - response() - .withStatusCode(200) - .withBody(AGENT_CARD_SUPPORTS_EXTENDED) - ); + public void testA2AClientGetExtendedAgentCard() throws Exception { this.server.when( request() .withMethod("POST") @@ -456,7 +368,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - AgentCard agentCard = client.getAgentCard(null); + AgentCard agentCard = client.getExtendedAgentCard(null); assertEquals("GeoSpatial Route Planner Agent Extended", agentCard.name()); assertEquals("Extended description", agentCard.description()); assertEquals("https://georoute-agent.example.com/a2a/v1", Utils.getFavoriteInterface(agentCard).url()); @@ -467,6 +379,7 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertTrue(agentCard.capabilities().streaming()); assertTrue(agentCard.capabilities().pushNotifications()); assertFalse(agentCard.capabilities().stateTransitionHistory()); + assertTrue(agentCard.capabilities().extendedAgentCard()); Map securitySchemes = agentCard.securitySchemes(); assertNotNull(securitySchemes); OpenIdConnectSecurityScheme google = (OpenIdConnectSecurityScheme) securitySchemes.get("google"); @@ -509,9 +422,8 @@ public void testA2AClientGetAuthenticatedExtendedAgentCard() throws Exception { assertEquals("Extended Skill", skills.get(2).name()); assertEquals("This is an extended skill.", skills.get(2).description()); assertEquals(List.of("extended"), skills.get(2).tags()); - assertTrue(agentCard.supportsExtendedAgentCard()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.protocolVersion()); + assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.protocolVersions().get(0)); } @Test diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index 27c27a7ab..dfdbb9741 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -10,7 +10,7 @@ public class JsonMessages { static final String AGENT_CARD = String.format(""" { - "protocolVersion": "%s", + "protocolVersions": ["%s"], "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", "supportedInterfaces" : [ @@ -28,7 +28,8 @@ public class JsonMessages { "capabilities": { "streaming": true, "pushNotifications": true, - "stateTransitionHistory": false + "stateTransitionHistory": false, + "extendedAgentCard": false }, "securitySchemes": { "google": { @@ -75,7 +76,6 @@ public class JsonMessages { ] } ], - "supportsExtendedAgentCard": false, "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", @@ -610,7 +610,8 @@ public class JsonMessages { "capabilities": { "streaming": true, "pushNotifications": true, - "stateTransitionHistory": false + "stateTransitionHistory": false, + "extendedAgentCard": true }, "securitySchemes": { "google": { @@ -663,8 +664,7 @@ public class JsonMessages { "tags": ["extended"] } ], - "supportsExtendedAgentCard": true, - "protocolVersion": "%s", + "protocolVersions": ["%s"], "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdUI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", @@ -691,7 +691,8 @@ public class JsonMessages { "capabilities": { "streaming": true, "pushNotifications": true, - "stateTransitionHistory": false + "stateTransitionHistory": false, + "extendedAgentCard": true }, "securitySchemes": { "google": { @@ -738,7 +739,6 @@ public class JsonMessages { ] } ], - "supportsExtendedAgentCard": true, - "protocolVersion": "%s" + "protocolVersions": ["%s"] }""", CURRENT_PROTOCOL_VERSION); } diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java index 76e6df404..92ce45416 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestErrorMapper.java @@ -8,7 +8,7 @@ import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.spec.A2AClientException; -import io.a2a.spec.ExtendedCardNotConfiguredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.ExtensionSupportRequiredError; import io.a2a.spec.InternalError; @@ -50,8 +50,7 @@ public static A2AClientException mapRestError(String body, int code) { public static A2AClientException mapRestError(String className, String errorMessage, int code) { return switch (className) { case "io.a2a.spec.TaskNotFoundError" -> new A2AClientException(errorMessage, new TaskNotFoundError()); - case "io.a2a.spec.AuthenticatedExtendedCardNotConfiguredError", - "io.a2a.spec.ExtendedCardNotConfiguredError" -> new A2AClientException(errorMessage, new ExtendedCardNotConfiguredError(null, errorMessage, null)); + case "io.a2a.spec.ExtendedCardNotConfiguredError" -> new A2AClientException(errorMessage, new ExtendedAgentCardNotConfiguredError(null, errorMessage, null)); case "io.a2a.spec.ContentTypeNotSupportedError" -> new A2AClientException(errorMessage, new ContentTypeNotSupportedError(null, null, errorMessage)); case "io.a2a.spec.InternalError" -> new A2AClientException(errorMessage, new InternalError(errorMessage)); case "io.a2a.spec.InvalidAgentResponseError" -> new A2AClientException(errorMessage, new InvalidAgentResponseError(null, null, errorMessage)); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index d67972a07..6513086da 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -65,8 +65,7 @@ public class RestTransport implements ClientTransport { private final A2AHttpClient httpClient; private final AgentInterface agentInterface; private @Nullable final List interceptors; - private AgentCard agentCard; - private boolean needsExtendedCard = false; + private final AgentCard agentCard; public RestTransport(AgentCard agentCard) { this(null, agentCard, Utils.getFavoriteInterface(agentCard), null); @@ -89,8 +88,8 @@ public EventKind sendMessage(MessageSendParams messageSendParams, @Nullable Clie String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, messageSendParams.tenant()) + "/message:send", payloadAndHeaders); io.a2a.grpc.SendMessageResponse.Builder responseBuilder = io.a2a.grpc.SendMessageResponse.newBuilder(); JsonFormat.parser().merge(httpResponseBody, responseBuilder); - if (responseBuilder.hasMsg()) { - return ProtoUtils.FromProto.message(responseBuilder.getMsg()); + if (responseBuilder.hasMessage()) { + return ProtoUtils.FromProto.message(responseBuilder.getMessage()); } if (responseBuilder.hasTask()) { return ProtoUtils.FromProto.task(responseBuilder.getTask()); @@ -416,17 +415,8 @@ public void resubscribe(TaskIdParams request, Consumer 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; - } PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, null, agentCard, context); String url = Utils.buildBaseUrl(agentInterface, "") + "/extendedAgentCard"; A2AHttpClient.GetBuilder getBuilder = httpClient.createGet().url(url); @@ -440,9 +430,7 @@ public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2ACli throw RestErrorMapper.mapRestError(response); } String httpResponseBody = response.body(); - agentCard = JsonUtil.fromJson(httpResponseBody, AgentCard.class); - needsExtendedCard = false; - return agentCard; + return JsonUtil.fromJson(httpResponseBody, AgentCard.class); } catch (IOException | InterruptedException | JsonProcessingException e) { throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e); } catch (A2AClientError e) { diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java index 58a1a57f0..ec74d2fbc 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java @@ -47,8 +47,8 @@ public void onError(Throwable throwable, @Nullable Future future) { private void handleMessage(StreamResponse response) { StreamingEventKind event; switch (response.getPayloadCase()) { - case MSG -> - event = ProtoUtils.FromProto.message(response.getMsg()); + case MESSAGE -> + event = ProtoUtils.FromProto.message(response.getMessage()); case TASK -> event = ProtoUtils.FromProto.task(response.getTask()); case STATUS_UPDATE -> diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index eda760130..879363d9c 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -91,7 +91,7 @@ public class RestTransportTest { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); @BeforeEach diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index e0f4b3a0c..df95e9ffd 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -137,13 +137,13 @@ void resubscribe(TaskIdParams request, Consumer eventConsume Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException; /** - * Retrieve the AgentCard. + * Retrieve the extended AgentCard. * * @param context optional client call context for the request (may be {@code null}) - * @return the AgentCard + * @return the extended agent card * @throws A2AClientException if retrieving the agent card fails for any reason */ - AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException; + AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException; /** * Close the transport and release any associated resources. diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java index 07c7aea04..39e968ccf 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -58,7 +58,7 @@ public AgentCard agentCard() { )) .build() )) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java index 73d080c45..b29b0b55f 100644 --- a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -68,6 +68,8 @@ public class A2ACloudExampleClient { private Client nonStreamingClient; private ClientConfig nonStreamingConfig; + private AgentCard agentCard; + public static void main(String[] args) throws Exception { new A2ACloudExampleClient().run(); } @@ -75,7 +77,7 @@ public static void main(String[] args) throws Exception { private void run() throws Exception { printHeader(); - AgentCard agentCard = fetchAndConfigureAgentCard(); + agentCard = fetchAndConfigureAgentCard(); String clientTaskId = generateClientTaskId(); createClients(agentCard); @@ -273,7 +275,7 @@ private void sendProcessMessages() throws InterruptedException { final int messageNum = messageCount; // Create a new client for each request to force new HTTP connection - Client freshClient = Client.builder(streamingClient.getAgentCard()) + Client freshClient = Client.builder(agentCard) .clientConfig(nonStreamingConfig) .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) .build(); @@ -318,7 +320,7 @@ private void sendCompleteMessage() { System.out.println(); System.out.println("Step 4: Sending 'complete' to finalize task..."); - Client completeClient = Client.builder(streamingClient.getAgentCard()) + Client completeClient = Client.builder(agentCard) .clientConfig(nonStreamingConfig) .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder()) .build(); diff --git a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java index c3b1cb473..5b8a6d196 100644 --- a/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java +++ b/examples/helloworld/client/src/main/java/io/a2a/examples/helloworld/HelloWorldClient.java @@ -40,11 +40,11 @@ public static void main(String[] args) { System.out.println("Using public agent card for client initialization (default)."); finalAgentCard = publicAgentCard; - if (publicAgentCard.supportsExtendedAgentCard()) { - System.out.println("Public card supports authenticated extended card. Attempting to fetch from: " + SERVER_URL + "/agent/authenticatedExtendedCard"); + if (publicAgentCard.capabilities().extendedAgentCard()) { + System.out.println("Public card supports authenticated extended card. Attempting to fetch from: " + SERVER_URL + "/ExtendedAgentCard"); Map authHeaders = new HashMap<>(); authHeaders.put("Authorization", "Bearer dummy-token-for-extended-card"); - AgentCard extendedAgentCard = A2A.getAgentCard(SERVER_URL, "/agent/authenticatedExtendedCard", authHeaders); + AgentCard extendedAgentCard = A2A.getAgentCard(SERVER_URL, "/ExtendedAgentCard", authHeaders); System.out.println("Successfully fetched authenticated extended agent card:"); System.out.println(JsonUtil.toJson(extendedAgentCard)); System.out.println("Using AUTHENTICATED EXTENDED agent card for client initialization."); diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 66f1c7fc4..42f631924 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -43,7 +43,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java index 9936ea4df..f029fd120 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java @@ -48,7 +48,7 @@ public static AgentCard createAgentCard(int instanceNumber, int port) { .description("Fire-and-forget agent for testing replication") .tags(Collections.singletonList("test")) .build())) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java index 7a0e9280e..b476fc497 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java @@ -39,7 +39,7 @@ public AgentCard agentCard() { .skills(List.of()) .supportedInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } \ No newline at end of file diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 840773ea6..0db0e1548 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -261,10 +261,10 @@ public ListTasksResult list(ListTasksParams params) { countQueryBuilder.append(" AND t.state = :state"); } - // Apply lastUpdatedAfter filter using denormalized timestamp column - if (params.lastUpdatedAfter() != null) { - queryBuilder.append(" AND t.statusTimestamp > :lastUpdatedAfter"); - countQueryBuilder.append(" AND t.statusTimestamp > :lastUpdatedAfter"); + // Apply statusTimestampAfter filter using denormalized timestamp column + if (params.statusTimestampAfter() != null) { + queryBuilder.append(" AND t.statusTimestamp > :statusTimestampAfter"); + countQueryBuilder.append(" AND t.statusTimestamp > :statusTimestampAfter"); } // Apply pagination cursor using keyset pagination for composite sort (timestamp DESC, id ASC) @@ -286,8 +286,8 @@ public ListTasksResult list(ListTasksParams params) { if (params.status() != null) { query.setParameter("state", params.status().asString()); } - if (params.lastUpdatedAfter() != null) { - query.setParameter("lastUpdatedAfter", params.lastUpdatedAfter()); + if (params.statusTimestampAfter() != null) { + query.setParameter("statusTimestampAfter", params.statusTimestampAfter()); } if (tokenTimestamp != null) { query.setParameter("tokenTimestamp", tokenTimestamp); @@ -315,8 +315,8 @@ public ListTasksResult list(ListTasksParams params) { if (params.status() != null) { countQuery.setParameter("state", params.status().asString()); } - if (params.lastUpdatedAfter() != null) { - countQuery.setParameter("lastUpdatedAfter", params.lastUpdatedAfter()); + if (params.statusTimestampAfter() != null) { + countQuery.setParameter("statusTimestampAfter", params.statusTimestampAfter()); } int totalSize = countQuery.getSingleResult().intValue(); diff --git a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java index d889b8e2c..794017432 100644 --- a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java +++ b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java @@ -8,7 +8,7 @@ public class JsonMessages { static final String AGENT_CARD = """ { - "protocolVersion": "0.2.9", + "protocolVersions": ["0.2.9"], "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", "supportedInterfaces" : [ @@ -73,7 +73,6 @@ public class JsonMessages { ] } ], - "supportsExtendedAgentCard": true, "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java index 73bb430eb..530f55e9f 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/A2AResponse.java @@ -10,7 +10,7 @@ * * @param the type of the result value returned in successful responses */ -public abstract sealed class A2AResponse implements A2AMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetAuthenticatedExtendedCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, A2AErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { +public abstract sealed class A2AResponse implements A2AMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetExtendedAgentCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, A2AErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { /** The JSON-RPC protocol version. */ protected String jsonrpc; diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetExtendedAgentCardRequest.java similarity index 73% rename from jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetExtendedAgentCardRequest.java index dec5ec257..89e893363 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetExtendedAgentCardRequest.java @@ -4,8 +4,9 @@ import java.util.UUID; +import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; -import io.a2a.spec.ExtendedCardNotConfiguredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; /** * JSON-RPC request to retrieve an agent's extended card with authenticated details. @@ -20,18 +21,18 @@ * *

    * The agent must support authenticated extended cards (indicated by - * {@link AgentCard#supportsExtendedAgentCard()}) and the client must provide + * {@link AgentCapabilities#extendedAgentCard()} }) and the client must provide * valid authentication credentials for this request to succeed. *

    - * This class implements the JSON-RPC {@code agent/getAuthenticatedExtendedCard} method + * This class implements the JSON-RPC {@code GetExtendedAgentCard} method * as specified in the A2A Protocol. * - * @see GetAuthenticatedExtendedCardResponse for the corresponding response + * @see GetExtendedAgentCardResponse for the corresponding response * @see AgentCard for the card structure - * @see ExtendedCardNotConfiguredError for the error when unsupported + * @see ExtendedAgentCardNotConfiguredError for the error when unsupported * @see A2A Protocol Specification */ -public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONRPCRequest { +public final class GetExtendedAgentCardRequest extends NonStreamingJSONRPCRequest { /** * Constructs request with full parameters. @@ -39,7 +40,7 @@ public final class GetAuthenticatedExtendedCardRequest extends NonStreamingJSONR * @param jsonrpc the JSON-RPC version * @param id the request ID */ - public GetAuthenticatedExtendedCardRequest(String jsonrpc, Object id) { + public GetExtendedAgentCardRequest(String jsonrpc, Object id) { super(jsonrpc, GET_EXTENDED_AGENT_CARD_METHOD, id); } @@ -48,7 +49,7 @@ public GetAuthenticatedExtendedCardRequest(String jsonrpc, Object id) { * * @param id the request ID */ - public GetAuthenticatedExtendedCardRequest(String id) { + public GetExtendedAgentCardRequest(String id) { this(null, id); } @@ -80,7 +81,7 @@ private Builder() { * @param jsonrpc the JSON-RPC version * @return this builder for method chaining */ - public GetAuthenticatedExtendedCardRequest.Builder jsonrpc(String jsonrpc) { + public GetExtendedAgentCardRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } @@ -91,7 +92,7 @@ public GetAuthenticatedExtendedCardRequest.Builder jsonrpc(String jsonrpc) { * @param id the request ID * @return this builder for method chaining */ - public GetAuthenticatedExtendedCardRequest.Builder id(Object id) { + public GetExtendedAgentCardRequest.Builder id(Object id) { this.id = id; return this; } @@ -101,11 +102,11 @@ public GetAuthenticatedExtendedCardRequest.Builder id(Object id) { * * @return a new instance */ - public GetAuthenticatedExtendedCardRequest build() { + public GetExtendedAgentCardRequest build() { if (id == null) { id = UUID.randomUUID().toString(); } - return new GetAuthenticatedExtendedCardRequest(jsonrpc, id); + return new GetExtendedAgentCardRequest(jsonrpc, id); } } } diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetExtendedAgentCardResponse.java similarity index 69% rename from jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetExtendedAgentCardResponse.java index 63b4fd8eb..edf181dd6 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetAuthenticatedExtendedCardResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/GetExtendedAgentCardResponse.java @@ -2,7 +2,7 @@ import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; -import io.a2a.spec.ExtendedCardNotConfiguredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; /** * JSON-RPC response containing an agent's extended card with authenticated details. @@ -13,14 +13,14 @@ *

    * If the agent doesn't support authenticated extended cards or authentication fails, * the error field will contain a {@link A2AError} such as - * {@link ExtendedCardNotConfiguredError}. + * {@link ExtendedAgentCardNotConfiguredError}. * - * @see GetAuthenticatedExtendedCardRequest for the corresponding request + * @see GetExtendedAgentCardRequest for the corresponding request * @see AgentCard for the card structure - * @see ExtendedCardNotConfiguredError for the error when unsupported + * @see ExtendedAgentCardNotConfiguredError for the error when unsupported * @see A2A Protocol Specification */ -public final class GetAuthenticatedExtendedCardResponse extends A2AResponse { +public final class GetExtendedAgentCardResponse extends A2AResponse { /** * Constructs response with full parameters. @@ -30,7 +30,7 @@ public final class GetAuthenticatedExtendedCardResponse extends A2AResponse extends A2ARequest permits GetTaskRequest, CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest, - GetAuthenticatedExtendedCardRequest, ListTasksRequest { + GetExtendedAgentCardRequest, ListTasksRequest { NonStreamingJSONRPCRequest(String jsonrpc, String method, Object id, T params) { validateAndSetJsonParameters(jsonrpc, method, id, params, true); diff --git a/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java index bbc297baa..5a5f3d856 100644 --- a/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java +++ b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/SecuritySchemeSerializationTest.java @@ -17,10 +17,7 @@ import io.a2a.spec.APIKeySecurityScheme; import io.a2a.spec.HTTPAuthSecurityScheme; import io.a2a.spec.MutualTLSSecurityScheme; -import io.a2a.spec.OAuth2SecurityScheme; -import io.a2a.spec.OAuthFlows; import io.a2a.spec.OpenIdConnectSecurityScheme; -import io.a2a.spec.PasswordOAuthFlow; import io.a2a.spec.SecurityScheme; /** @@ -128,68 +125,6 @@ void testAPIKeySecuritySchemeWithDescriptionSerialization() throws JsonProcessin "description", "API key authentication via query parameter")); } - @Test - void testOAuth2SecuritySchemeSerialization() throws JsonProcessingException { - PasswordOAuthFlow passwordFlow = new PasswordOAuthFlow( - "https://example.com/oauth/refresh", - Map.of("read", "Read access", "write", "Write access"), - "https://example.com/oauth/token" - ); - - OAuthFlows flows = OAuthFlows.builder() - .password(passwordFlow) - .build(); - - SecurityScheme scheme = OAuth2SecurityScheme.builder() - .flows(flows) - .description("OAuth 2.0 password flow") - .oauth2MetadataUrl("https://example.com/.well-known/oauth-authorization-server") - .build(); - - // Verify serialization with nested OAuth flow fields - String json = JsonUtil.toJson(scheme); - assertNotNull(json); - assertTrue(json.contains(OAuth2SecurityScheme.TYPE)); - assertTrue(json.contains("\"description\":\"OAuth 2.0 password flow\"")); - assertTrue(json.contains("\"oauth2MetadataUrl\":\"https://example.com/.well-known/oauth-authorization-server\"")); - assertTrue(json.contains("\"tokenUrl\":\"https://example.com/oauth/token\"")); - assertTrue(json.contains("\"read\":\"Read access\"")); - - SecurityScheme deserialized = JsonUtil.fromJson(json, SecurityScheme.class); - assertEquals(scheme, deserialized); - } - - @Test - void testOAuth2SecuritySchemeDeserialization() throws JsonProcessingException { - String json = """ - { - "oauth2SecurityScheme" : { - "flows": { - "password": { - "tokenUrl": "https://example.com/oauth/token", - "refreshUrl": "https://example.com/oauth/refresh", - "scopes": { - "read": "Read access", - "write": "Write access" - } - } - }, - "description": "OAuth 2.0 authentication" - } - }"""; - - SecurityScheme securityScheme = JsonUtil.fromJson(json, SecurityScheme.class); - assertInstanceOf(OAuth2SecurityScheme.class, securityScheme); - OAuth2SecurityScheme scheme = (OAuth2SecurityScheme) securityScheme; - assertEquals("OAuth 2.0 authentication", scheme.description()); - assertNotNull(scheme.flows()); - assertNotNull(scheme.flows().password()); - assertEquals("https://example.com/oauth/token", scheme.flows().password().tokenUrl()); - assertEquals("https://example.com/oauth/refresh", scheme.flows().password().refreshUrl()); - assertEquals(2, scheme.flows().password().scopes().size()); - assertEquals("Read access", scheme.flows().password().scopes().get("read")); - } - @Test void testOpenIdConnectSecuritySchemeSerialization() throws JsonProcessingException { SecurityScheme scheme = OpenIdConnectSecurityScheme.builder() diff --git a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java index edd346d50..e5f7a27c2 100644 --- a/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java +++ b/reference/grpc/src/main/java/io/a2a/server/grpc/quarkus/QuarkusGrpcHandler.java @@ -5,6 +5,7 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; +import io.a2a.server.ExtendedAgentCard; import io.a2a.server.PublicAgentCard; import io.a2a.server.requesthandlers.RequestHandler; import io.a2a.server.util.async.Internal; @@ -14,6 +15,7 @@ import io.quarkus.grpc.GrpcService; import io.quarkus.grpc.RegisterInterceptor; import io.quarkus.security.Authenticated; +import org.jspecify.annotations.Nullable; @GrpcService @RegisterInterceptor(A2AExtensionsInterceptor.class) @@ -21,16 +23,29 @@ public class QuarkusGrpcHandler extends GrpcHandler { private final AgentCard agentCard; + private final AgentCard extendedAgentCard; private final RequestHandler requestHandler; private final Instance callContextFactoryInstance; private final Executor executor; + /** + * No-args constructor for CDI proxy creation. + * CDI requires a non-private constructor to create proxies for @ApplicationScoped beans. + * All fields are initialized by the @Inject constructor during actual bean creation. + */ + @Inject public QuarkusGrpcHandler(@PublicAgentCard AgentCard agentCard, + @ExtendedAgentCard Instance extendedAgentCard, RequestHandler requestHandler, Instance callContextFactoryInstance, @Internal Executor executor) { this.agentCard = agentCard; + if (extendedAgentCard != null && extendedAgentCard.isResolvable()) { + this.extendedAgentCard = extendedAgentCard.get(); + } else { + this.extendedAgentCard = null; + } this.requestHandler = requestHandler; this.callContextFactoryInstance = callContextFactoryInstance; this.executor = executor; @@ -46,6 +61,11 @@ protected AgentCard getAgentCard() { return agentCard; } + @Override + protected AgentCard getExtendedAgentCard() { + return extendedAgentCard; + } + @Override protected CallContextFactory getCallContextFactory() { return callContextFactoryInstance.isUnsatisfied() ? null : callContextFactoryInstance.get(); diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 9f4b31d83..18e18a2f1 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -35,8 +35,8 @@ import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; 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.GetExtendedAgentCardRequest; +import io.a2a.jsonrpc.common.wrappers.GetExtendedAgentCardResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; @@ -59,7 +59,6 @@ import io.a2a.server.extensions.A2AExtensions; import io.a2a.server.util.async.Internal; import io.a2a.spec.A2AError; -import io.a2a.spec.AgentCard; import io.a2a.spec.InternalError; import io.a2a.spec.JSONParseError; import io.a2a.spec.UnsupportedOperationError; @@ -186,8 +185,8 @@ private A2AResponse processNonStreamingRequest(NonStreamingJSONRPCRequest if (request instanceof DeleteTaskPushNotificationConfigRequest req) { return jsonRpcHandler.deletePushNotificationConfig(req, context); } - if (request instanceof GetAuthenticatedExtendedCardRequest req) { - return jsonRpcHandler.onGetAuthenticatedExtendedCardRequest(req, context); + if (request instanceof GetExtendedAgentCardRequest req) { + return jsonRpcHandler.onGetExtendedCardRequest(req, context); } return generateErrorResponse(request, new UnsupportedOperationError()); } @@ -287,8 +286,8 @@ private static com.google.protobuf.MessageOrBuilder convertToProto(A2AResponse contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -513,8 +513,8 @@ public void testGetAuthenticatedExtendedCard_MethodNameSetInContext() { routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); // Assert - verify(mockJsonRpcHandler).onGetAuthenticatedExtendedCardRequest( - any(GetAuthenticatedExtendedCardRequest.class), contextCaptor.capture()); + verify(mockJsonRpcHandler).onGetExtendedCardRequest( + any(GetExtendedAgentCardRequest.class), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(GET_EXTENDED_AGENT_CARD_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index e19f7920d..002acbafd 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -312,19 +312,27 @@ private static Task limitTaskHistory(Task task, @Nullable Integer historyLength) @Override public ListTasksResult onListTasks(ListTasksParams params, ServerCallContext context) throws A2AError { - LOGGER.debug("onListTasks with contextId={}, status={}, pageSize={}, pageToken={}, lastUpdatedAfter={}", - params.contextId(), params.status(), params.pageSize(), params.pageToken(), params.lastUpdatedAfter()); + LOGGER.debug("onListTasks with contextId={}, status={}, pageSize={}, pageToken={}, statusTimestampAfter={}", + params.contextId(), params.status(), params.pageSize(), params.pageToken(), params.statusTimestampAfter()); - // Validate lastUpdatedAfter timestamp if provided - if (params.lastUpdatedAfter() != null) { + // Validate statusTimestampAfter timestamp if provided + if (params.statusTimestampAfter() != null) { // Check if timestamp is in the future (optional validation per spec) Instant now = Instant.now(); - if (params.lastUpdatedAfter().isAfter(now)) { + if (params.statusTimestampAfter().isAfter(now)) { Map errorData = new HashMap<>(); errorData.put("parameter", "lastUpdatedAfter"); errorData.put("reason", "Timestamp cannot be in the future"); throw new InvalidParamsError(null, "Invalid params", errorData); } + // Check that timestamp is not negative + long millis = params.statusTimestampAfter().toEpochMilli(); + if (millis < 0L) { + Map errorData = new HashMap<>(); + errorData.put("parameter", "statusTimestampAfter"); + errorData.put("reason", "Must be a non-negative timestamp value, got: " + millis); + throw new InvalidParamsError(null, "Invalid params", errorData); + } } ListTasksResult result = taskStore.list(params); diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index f1243e272..8a3d72133 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -52,10 +52,10 @@ public ListTasksResult list(ListTasksParams params) { .filter(task -> params.contextId() == null || params.contextId().equals(task.contextId())) .filter(task -> params.status() == null || (task.status() != null && params.status().equals(task.status().state()))) - .filter(task -> params.lastUpdatedAfter() == null || + .filter(task -> params.statusTimestampAfter() == null || (task.status() != null && task.status().timestamp() != null && - task.status().timestamp().toInstant().isAfter(params.lastUpdatedAfter()))) + task.status().timestamp().toInstant().isAfter(params.statusTimestampAfter()))) .sorted(Comparator.comparing( (Task t) -> (t.status() != null && t.status().timestamp() != null) // Truncate to milliseconds for consistency with pageToken precision diff --git a/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java b/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java index 83a83122f..91159428a 100644 --- a/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java +++ b/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java @@ -1,5 +1,7 @@ package io.a2a.server.version; +import java.util.List; + import io.a2a.server.ServerCallContext; import io.a2a.spec.AgentCard; import io.a2a.spec.VersionNotSupportedError; @@ -35,13 +37,13 @@ public static void validateProtocolVersion(AgentCard agentCard, ServerCallContex requestedVersion = AgentCard.CURRENT_PROTOCOL_VERSION; } - String supportedVersion = agentCard.protocolVersion(); + List supportedVersions = agentCard.protocolVersions(); - if (!isVersionCompatible(supportedVersion, requestedVersion)) { + if (!isVersionCompatible(supportedVersions, requestedVersion)) { throw new VersionNotSupportedError( null, "Protocol version '" + requestedVersion + "' is not supported. " + - "Supported version: " + supportedVersion, + "Supported versions: " + supportedVersions, null); } } @@ -55,22 +57,30 @@ public static void validateProtocolVersion(AgentCard agentCard, ServerCallContex *

  • Minor versions are compatible (any x.Y works with x.Z)
  • * * - * @param supported the version supported by the agent (e.g., "1.0") - * @param requested the version requested by the client (e.g., "1.1") + * @param supportedVersions the version supported by the agent (e.g., ["1.0", "1.1"]) + * @param requestedVersion the version requested by the client (e.g., "1.1") * @return true if versions are compatible, false otherwise */ - static boolean isVersionCompatible(String supported, String requested) { - try { - VersionParts supportedParts = parseVersion(supported); - VersionParts requestedParts = parseVersion(requested); - - // Major versions must match exactly - return supportedParts.major == requestedParts.major; - // Minor versions are compatible - any 1.x can talk to any 1.y - } catch (IllegalArgumentException e) { - // If we can't parse the version, consider it incompatible + static boolean isVersionCompatible(List supportedVersions, String requestedVersion) { + if (supportedVersions == null) { return false; } + for (String supportedVersion : supportedVersions) { + try { + VersionParts supportedParts = parseVersion(supportedVersion); + VersionParts requestedParts = parseVersion(requestedVersion); + + // Major versions must match exactly + if (supportedParts.major == requestedParts.major) { + return true; + } + // Minor versions are compatible - any 1.x can talk to any 1.y + } catch (IllegalArgumentException e) { + // If we can't parse the version, consider it incompatible + return false; + } + } + return false; } /** diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 8038bc147..ea5bbe797 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -127,7 +127,7 @@ protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifi .defaultInputModes(new ArrayList<>()) .defaultOutputModes(new ArrayList<>()) .skills(new ArrayList<>()) - .protocolVersion(CURRENT_PROTOCOL_VERSION); + .protocolVersions(CURRENT_PROTOCOL_VERSION); return builder.build(); } diff --git a/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java b/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java index 528017731..43e0f55dd 100644 --- a/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java +++ b/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java @@ -18,34 +18,34 @@ public class A2AVersionValidatorTest { @Test public void testIsVersionCompatible_SameMajorMinor() { - assertTrue(A2AVersionValidator.isVersionCompatible("1.0", "1.0")); + assertTrue(A2AVersionValidator.isVersionCompatible(List.of("1.0"), "1.0")); } @Test public void testIsVersionCompatible_SameMajorDifferentMinor() { // Major versions match, minor versions can differ - assertTrue(A2AVersionValidator.isVersionCompatible("1.0", "1.1")); - assertTrue(A2AVersionValidator.isVersionCompatible("1.1", "1.0")); - assertTrue(A2AVersionValidator.isVersionCompatible("1.5", "1.9")); + assertTrue(A2AVersionValidator.isVersionCompatible(List.of("1.0"), "1.1")); + assertTrue(A2AVersionValidator.isVersionCompatible(List.of("1.1"), "1.0")); + assertTrue(A2AVersionValidator.isVersionCompatible(List.of("1.5"), "1.9")); } @Test public void testIsVersionCompatible_DifferentMajor() { // Major versions must match exactly - assertFalse(A2AVersionValidator.isVersionCompatible("1.0", "2.0")); - assertFalse(A2AVersionValidator.isVersionCompatible("2.0", "1.0")); - assertFalse(A2AVersionValidator.isVersionCompatible("1.5", "2.5")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("1.0"), "2.0")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("2.0"), "1.0")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("1.5"), "2.5")); } @Test public void testIsVersionCompatible_InvalidFormat() { // Invalid version formats should return false - assertFalse(A2AVersionValidator.isVersionCompatible("1.0", "invalid")); - assertFalse(A2AVersionValidator.isVersionCompatible("invalid", "1.0")); - assertFalse(A2AVersionValidator.isVersionCompatible("1", "1.0")); - assertFalse(A2AVersionValidator.isVersionCompatible("1.0", "")); - assertFalse(A2AVersionValidator.isVersionCompatible("", "1.0")); - assertFalse(A2AVersionValidator.isVersionCompatible("1.0", null)); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("1.0"), "invalid")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("invalid"), "1.0")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("1"), "1.0")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("1.0"), "")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of(""), "1.0")); + assertFalse(A2AVersionValidator.isVersionCompatible(List.of("1.0"), null)); assertFalse(A2AVersionValidator.isVersionCompatible(null, "1.0")); } @@ -153,7 +153,7 @@ private AgentCard createAgentCard(String protocolVersion) { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(Collections.emptyList()) - .protocolVersion(protocolVersion) + .protocolVersions(protocolVersion) .build(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index bc454f7e1..0fa06787b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -202,25 +202,15 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable; + internal_static_a2a_v1_DeviceCodeOAuthFlow_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor; + internal_static_a2a_v1_DeviceCodeOAuthFlow_ScopesEntry_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable; - static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_PasswordOAuthFlow_descriptor; - static final - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable; - static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor; - static final - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable; + internal_static_a2a_v1_DeviceCodeOAuthFlow_ScopesEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_SendMessageRequest_descriptor; static final @@ -352,199 +342,191 @@ public static void registerAllExtensions( "\n\007schemes\030\001 \003(\tB\003\340A\002\022\023\n\013credentials\030\002 \001(" + "\t\"Q\n\016AgentInterface\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\035\n" + "\020protocol_binding\030\002 \001(\tB\003\340A\002\022\016\n\006tenant\030\003" + - " \001(\t\"\255\007\n\tAgentCard\022\"\n\020protocol_version\030\020" + - " \001(\tB\003\340A\002H\000\210\001\001\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\030\n\013des" + - "cription\030\002 \001(\tB\003\340A\002\0224\n\024supported_interfa" + - "ces\030\023 \003(\0132\026.a2a.v1.AgentInterface\022\024\n\003url" + - "\030\003 \001(\tB\002\030\001H\001\210\001\001\022$\n\023preferred_transport\030\016" + - " \001(\tB\002\030\001H\002\210\001\001\0229\n\025additional_interfaces\030\017" + - " \003(\0132\026.a2a.v1.AgentInterfaceB\002\030\001\022\'\n\010prov" + - "ider\030\004 \001(\0132\025.a2a.v1.AgentProvider\022\024\n\007ver" + - "sion\030\005 \001(\tB\003\340A\002\022\036\n\021documentation_url\030\006 \001" + - "(\tH\003\210\001\001\0224\n\014capabilities\030\007 \001(\0132\031.a2a.v1.A" + - "gentCapabilitiesB\003\340A\002\022@\n\020security_scheme" + - "s\030\010 \003(\0132&.a2a.v1.AgentCard.SecuritySchem" + - "esEntry\022\"\n\010security\030\t \003(\0132\020.a2a.v1.Secur" + - "ity\022 \n\023default_input_modes\030\n \003(\tB\003\340A\002\022!\n" + - "\024default_output_modes\030\013 \003(\tB\003\340A\002\022\'\n\006skil" + - "ls\030\014 \003(\0132\022.a2a.v1.AgentSkillB\003\340A\002\022)\n\034sup" + - "ports_extended_agent_card\030\r \001(\010H\004\210\001\001\022.\n\n" + - "signatures\030\021 \003(\0132\032.a2a.v1.AgentCardSigna" + - "ture\022\025\n\010icon_url\030\022 \001(\tH\005\210\001\001\032N\n\024SecurityS" + - "chemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132" + - "\026.a2a.v1.SecurityScheme:\0028\001B\023\n\021_protocol" + - "_versionB\006\n\004_urlB\026\n\024_preferred_transport" + - "B\024\n\022_documentation_urlB\037\n\035_supports_exte" + - "nded_agent_cardB\013\n\t_icon_url\"<\n\rAgentPro" + - "vider\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\031\n\014organization\030" + - "\002 \001(\tB\003\340A\002\"\341\001\n\021AgentCapabilities\022\026\n\tstre" + - "aming\030\001 \001(\010H\000\210\001\001\022\037\n\022push_notifications\030\002" + - " \001(\010H\001\210\001\001\022*\n\nextensions\030\003 \003(\0132\026.a2a.v1.A" + - "gentExtension\022%\n\030state_transition_histor" + - "y\030\004 \001(\010H\002\210\001\001B\014\n\n_streamingB\025\n\023_push_noti" + - "ficationsB\033\n\031_state_transition_history\"m" + - "\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013descrip" + - "tion\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006params\030\004" + - " \001(\0132\027.google.protobuf.Struct\"\276\001\n\nAgentS" + - "kill\022\017\n\002id\030\001 \001(\tB\003\340A\002\022\021\n\004name\030\002 \001(\tB\003\340A\002" + - "\022\030\n\013description\030\003 \001(\tB\003\340A\002\022\021\n\004tags\030\004 \003(\t" + - "B\003\340A\002\022\020\n\010examples\030\005 \003(\t\022\023\n\013input_modes\030\006" + - " \003(\t\022\024\n\014output_modes\030\007 \003(\t\022\"\n\010security\030\010" + - " \003(\0132\020.a2a.v1.Security\"m\n\022AgentCardSigna" + - "ture\022\026\n\tprotected\030\001 \001(\tB\003\340A\002\022\026\n\tsignatur" + - "e\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\0132\027.google.pr" + - "otobuf.Struct\"v\n\032TaskPushNotificationCon" + - "fig\022\021\n\004name\030\001 \001(\tB\003\340A\002\022E\n\030push_notificat" + - "ion_config\030\002 \001(\0132\036.a2a.v1.PushNotificati" + - "onConfigB\003\340A\002\"\032\n\nStringList\022\014\n\004list\030\001 \003(" + - "\t\"~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035.a2a.v1." + - "Security.SchemesEntry\032B\n\014SchemesEntry\022\013\n" + - "\003key\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1.Strin" + - "gList:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027api_key_" + - "security_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySec" + - "uritySchemeH\000\022C\n\031http_auth_security_sche" + - "me\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecurityScheme" + - "H\000\022>\n\026oauth2_security_scheme\030\003 \001(\0132\034.a2a" + - ".v1.OAuth2SecuritySchemeH\000\022N\n\037open_id_co" + - "nnect_security_scheme\030\004 \001(\0132#.a2a.v1.Ope" + - "nIdConnectSecuritySchemeH\000\022?\n\024mtls_secur" + - "ity_scheme\030\005 \001(\0132\037.a2a.v1.MutualTlsSecur" + - "itySchemeH\000B\010\n\006scheme\"U\n\024APIKeySecurityS" + - "cheme\022\023\n\013description\030\001 \001(\t\022\025\n\010location\030\002" + - " \001(\tB\003\340A\002\022\021\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026HTTPAuth" + - "SecurityScheme\022\023\n\013description\030\001 \001(\t\022\023\n\006s" + - "cheme\030\002 \001(\tB\003\340A\002\022\025\n\rbearer_format\030\003 \001(\t\"" + - "p\n\024OAuth2SecurityScheme\022\023\n\013description\030\001" + - " \001(\t\022&\n\005flows\030\002 \001(\0132\022.a2a.v1.OAuthFlowsB" + - "\003\340A\002\022\033\n\023oauth2_metadata_url\030\003 \001(\t\"T\n\033Ope" + - "nIdConnectSecurityScheme\022\023\n\013description\030" + - "\001 \001(\t\022 \n\023open_id_connect_url\030\002 \001(\tB\003\340A\002\"" + - ".\n\027MutualTlsSecurityScheme\022\023\n\013descriptio" + - "n\030\001 \001(\t\"\366\001\n\nOAuthFlows\022@\n\022authorization_" + - "code\030\001 \001(\0132\".a2a.v1.AuthorizationCodeOAu" + - "thFlowH\000\022@\n\022client_credentials\030\002 \001(\0132\".a" + - "2a.v1.ClientCredentialsOAuthFlowH\000\022-\n\010im" + - "plicit\030\003 \001(\0132\031.a2a.v1.ImplicitOAuthFlowH" + - "\000\022-\n\010password\030\004 \001(\0132\031.a2a.v1.PasswordOAu" + - "thFlowH\000B\006\n\004flow\"\335\001\n\032AuthorizationCodeOA" + - "uthFlow\022\036\n\021authorization_url\030\001 \001(\tB\003\340A\002\022" + - "\026\n\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003" + - " \001(\t\022C\n\006scopes\030\004 \003(\0132..a2a.v1.Authorizat" + - "ionCodeOAuthFlow.ScopesEntryB\003\340A\002\032-\n\013Sco" + - "pesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001" + - "\"\275\001\n\032ClientCredentialsOAuthFlow\022\026\n\ttoken" + - "_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022C\n\006" + - "scopes\030\003 \003(\0132..a2a.v1.ClientCredentialsO" + - "AuthFlow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry" + - "\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\263\001\n\021Imp" + - "licitOAuthFlow\022\036\n\021authorization_url\030\001 \001(" + - "\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022:\n\006scopes\030\003 " + - "\003(\0132%.a2a.v1.ImplicitOAuthFlow.ScopesEnt" + + " \001(\t\"\310\005\n\tAgentCard\022\036\n\021protocol_versions\030" + + "\020 \003(\tB\003\340A\002\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\030\n\013descrip" + + "tion\030\002 \001(\tB\003\340A\002\0229\n\024supported_interfaces\030" + + "\023 \003(\0132\026.a2a.v1.AgentInterfaceB\003\340A\002\022\'\n\010pr" + + "ovider\030\004 \001(\0132\025.a2a.v1.AgentProvider\022\024\n\007v" + + "ersion\030\005 \001(\tB\003\340A\002\022\036\n\021documentation_url\030\006" + + " \001(\tH\000\210\001\001\0224\n\014capabilities\030\007 \001(\0132\031.a2a.v1" + + ".AgentCapabilitiesB\003\340A\002\022@\n\020security_sche" + + "mes\030\010 \003(\0132&.a2a.v1.AgentCard.SecuritySch" + + "emesEntry\022\"\n\010security\030\t \003(\0132\020.a2a.v1.Sec" + + "urity\022 \n\023default_input_modes\030\n \003(\tB\003\340A\002\022" + + "!\n\024default_output_modes\030\013 \003(\tB\003\340A\002\022\'\n\006sk" + + "ills\030\014 \003(\0132\022.a2a.v1.AgentSkillB\003\340A\002\022.\n\ns" + + "ignatures\030\021 \003(\0132\032.a2a.v1.AgentCardSignat" + + "ure\022\025\n\010icon_url\030\022 \001(\tH\001\210\001\001\032N\n\024SecuritySc" + + "hemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132\026" + + ".a2a.v1.SecurityScheme:\0028\001B\024\n\022_documenta" + + "tion_urlB\013\n\t_icon_urlJ\004\010\003\020\004J\004\010\016\020\017J\004\010\017\020\020\"" + + "<\n\rAgentProvider\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\031\n\014or" + + "ganization\030\002 \001(\tB\003\340A\002\"\233\002\n\021AgentCapabilit" + + "ies\022\026\n\tstreaming\030\001 \001(\010H\000\210\001\001\022\037\n\022push_noti" + + "fications\030\002 \001(\010H\001\210\001\001\022*\n\nextensions\030\003 \003(\013" + + "2\026.a2a.v1.AgentExtension\022%\n\030state_transi" + + "tion_history\030\004 \001(\010H\002\210\001\001\022 \n\023extended_agen" + + "t_card\030\005 \001(\010H\003\210\001\001B\014\n\n_streamingB\025\n\023_push" + + "_notificationsB\033\n\031_state_transition_hist" + + "oryB\026\n\024_extended_agent_card\"m\n\016AgentExte" + + "nsion\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030\002 \001(\t\022" + + "\020\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132\027.goog" + + "le.protobuf.Struct\"\276\001\n\nAgentSkill\022\017\n\002id\030" + + "\001 \001(\tB\003\340A\002\022\021\n\004name\030\002 \001(\tB\003\340A\002\022\030\n\013descrip" + + "tion\030\003 \001(\tB\003\340A\002\022\021\n\004tags\030\004 \003(\tB\003\340A\002\022\020\n\010ex" + + "amples\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014out" + + "put_modes\030\007 \003(\t\022\"\n\010security\030\010 \003(\0132\020.a2a." + + "v1.Security\"m\n\022AgentCardSignature\022\026\n\tpro" + + "tected\030\001 \001(\tB\003\340A\002\022\026\n\tsignature\030\002 \001(\tB\003\340A" + + "\002\022\'\n\006header\030\003 \001(\0132\027.google.protobuf.Stru" + + "ct\"v\n\032TaskPushNotificationConfig\022\021\n\004name" + + "\030\001 \001(\tB\003\340A\002\022E\n\030push_notification_config\030" + + "\002 \001(\0132\036.a2a.v1.PushNotificationConfigB\003\340" + + "A\002\"\032\n\nStringList\022\014\n\004list\030\001 \003(\t\"~\n\010Securi" + + "ty\022.\n\007schemes\030\001 \003(\0132\035.a2a.v1.Security.Sc" + + "hemesEntry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001(\t\022" + + "!\n\005value\030\002 \001(\0132\022.a2a.v1.StringList:\0028\001\"\361" + + "\002\n\016SecurityScheme\022?\n\027api_key_security_sc" + + "heme\030\001 \001(\0132\034.a2a.v1.APIKeySecurityScheme" + + "H\000\022C\n\031http_auth_security_scheme\030\002 \001(\0132\036." + + "a2a.v1.HTTPAuthSecuritySchemeH\000\022>\n\026oauth" + + "2_security_scheme\030\003 \001(\0132\034.a2a.v1.OAuth2S" + + "ecuritySchemeH\000\022N\n\037open_id_connect_secur" + + "ity_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConnectS" + + "ecuritySchemeH\000\022?\n\024mtls_security_scheme\030" + + "\005 \001(\0132\037.a2a.v1.MutualTlsSecuritySchemeH\000" + + "B\010\n\006scheme\"U\n\024APIKeySecurityScheme\022\023\n\013de" + + "scription\030\001 \001(\t\022\025\n\010location\030\002 \001(\tB\003\340A\002\022\021" + + "\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026HTTPAuthSecuritySch" + + "eme\022\023\n\013description\030\001 \001(\t\022\023\n\006scheme\030\002 \001(\t" + + "B\003\340A\002\022\025\n\rbearer_format\030\003 \001(\t\"p\n\024OAuth2Se" + + "curityScheme\022\023\n\013description\030\001 \001(\t\022&\n\005flo" + + "ws\030\002 \001(\0132\022.a2a.v1.OAuthFlowsB\003\340A\002\022\033\n\023oau" + + "th2_metadata_url\030\003 \001(\t\"T\n\033OpenIdConnectS" + + "ecurityScheme\022\023\n\013description\030\001 \001(\t\022 \n\023op" + + "en_id_connect_url\030\002 \001(\tB\003\340A\002\".\n\027MutualTl" + + "sSecurityScheme\022\023\n\013description\030\001 \001(\t\"\330\001\n" + + "\nOAuthFlows\022@\n\022authorization_code\030\001 \001(\0132" + + "\".a2a.v1.AuthorizationCodeOAuthFlowH\000\022@\n" + + "\022client_credentials\030\002 \001(\0132\".a2a.v1.Clien" + + "tCredentialsOAuthFlowH\000\0222\n\013device_code\030\005" + + " \001(\0132\033.a2a.v1.DeviceCodeOAuthFlowH\000B\006\n\004f" + + "lowJ\004\010\003\020\004J\004\010\004\020\005\"\364\001\n\032AuthorizationCodeOAu" + + "thFlow\022\036\n\021authorization_url\030\001 \001(\tB\003\340A\002\022\026" + + "\n\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003 " + + "\001(\t\022C\n\006scopes\030\004 \003(\0132..a2a.v1.Authorizati" + + "onCodeOAuthFlow.ScopesEntryB\003\340A\002\022\025\n\rpkce" + + "_required\030\005 \001(\010\032-\n\013ScopesEntry\022\013\n\003key\030\001 " + + "\001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\275\001\n\032ClientCredent" + + "ialsOAuthFlow\022\026\n\ttoken_url\030\001 \001(\tB\003\340A\002\022\023\n" + + "\013refresh_url\030\002 \001(\t\022C\n\006scopes\030\003 \003(\0132..a2a" + + ".v1.ClientCredentialsOAuthFlow.ScopesEnt" + "ryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005v" + - "alue\030\002 \001(\t:\0028\001\"\253\001\n\021PasswordOAuthFlow\022\026\n\t" + - "token_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(" + - "\t\022:\n\006scopes\030\003 \003(\0132%.a2a.v1.PasswordOAuth" + - "Flow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003" + - "key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\270\001\n\022SendMes" + - "sageRequest\022\016\n\006tenant\030\004 \001(\t\022.\n\007request\030\001" + - " \001(\0132\017.a2a.v1.MessageB\003\340A\002R\007message\0227\n\rc" + - "onfiguration\030\002 \001(\0132 .a2a.v1.SendMessageC" + - "onfiguration\022)\n\010metadata\030\003 \001(\0132\027.google." + - "protobuf.Struct\"c\n\016GetTaskRequest\022\016\n\006ten" + - "ant\030\003 \001(\t\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\033\n\016history_" + - "length\030\002 \001(\005H\000\210\001\001B\021\n\017_history_length\"\225\002\n" + - "\020ListTasksRequest\022\016\n\006tenant\030\t \001(\t\022\022\n\ncon" + - "text_id\030\001 \001(\t\022!\n\006status\030\002 \001(\0162\021.a2a.v1.T" + - "askState\022\026\n\tpage_size\030\003 \001(\005H\000\210\001\001\022\022\n\npage" + - "_token\030\004 \001(\t\022\033\n\016history_length\030\005 \001(\005H\001\210\001" + - "\001\022\032\n\022last_updated_after\030\006 \001(\003\022\036\n\021include" + - "_artifacts\030\007 \001(\010H\002\210\001\001B\014\n\n_page_sizeB\021\n\017_" + - "history_lengthB\024\n\022_include_artifacts\"\204\001\n" + - "\021ListTasksResponse\022 \n\005tasks\030\001 \003(\0132\014.a2a." + - "v1.TaskB\003\340A\002\022\034\n\017next_page_token\030\002 \001(\tB\003\340" + - "A\002\022\026\n\tpage_size\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_size" + - "\030\004 \001(\005B\003\340A\002\"1\n\021CancelTaskRequest\022\016\n\006tena" + - "nt\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"D\n$GetTaskPushNot" + - "ificationConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\014" + - "\n\004name\030\001 \001(\t\"G\n\'DeleteTaskPushNotificati" + - "onConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name\030" + - "\001 \001(\t\"\234\001\n$SetTaskPushNotificationConfigR" + - "equest\022\016\n\006tenant\030\004 \001(\t\022\023\n\006parent\030\001 \001(\tB\003" + - "\340A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 " + - "\001(\0132\".a2a.v1.TaskPushNotificationConfigB" + - "\003\340A\002\"6\n\026SubscribeToTaskRequest\022\016\n\006tenant" + - "\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"n\n%ListTaskPushNoti" + - "ficationConfigRequest\022\016\n\006tenant\030\004 \001(\t\022\016\n" + - "\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npage" + - "_token\030\003 \001(\t\"-\n\033GetExtendedAgentCardRequ" + - "est\022\016\n\006tenant\030\001 \001(\t\"g\n\023SendMessageRespon" + - "se\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\'\n\003msg\030" + - "\002 \001(\0132\017.a2a.v1.MessageH\000R\007messageB\t\n\007pay" + - "load\"\326\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\0132\014." + - "a2a.v1.TaskH\000\022\'\n\003msg\030\002 \001(\0132\017.a2a.v1.Mess" + - "ageH\000R\007message\0226\n\rstatus_update\030\003 \001(\0132\035." + - "a2a.v1.TaskStatusUpdateEventH\000\022:\n\017artifa" + - "ct_update\030\004 \001(\0132\037.a2a.v1.TaskArtifactUpd" + - "ateEventH\000B\t\n\007payload\"v\n&ListTaskPushNot" + - "ificationConfigResponse\0223\n\007configs\030\001 \003(\013" + - "2\".a2a.v1.TaskPushNotificationConfig\022\027\n\017" + - "next_page_token\030\002 \001(\t*\372\001\n\tTaskState\022\032\n\026T" + - "ASK_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STATE_SU" + - "BMITTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024TAS" + - "K_STATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FAILED" + - "\020\004\022\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK_STA" + - "TE_INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_REJECT" + - "ED\020\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;\n\004Ro" + - "le\022\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USER\020\001\022" + - "\016\n\nROLE_AGENT\020\0022\276\016\n\nA2AService\022}\n\013SendMe" + - "ssage\022\032.a2a.v1.SendMessageRequest\032\033.a2a." + - "v1.SendMessageResponse\"5\202\323\344\223\002/\"\r/message" + - ":send:\001*Z\033\"\026/{tenant}/message:send:\001*\022\207\001" + - "\n\024SendStreamingMessage\022\032.a2a.v1.SendMess" + - "ageRequest\032\026.a2a.v1.StreamResponse\"9\202\323\344\223" + - "\0023\"\017/message:stream:\001*Z\035\"\030/{tenant}/mess" + - "age:stream:\001*0\001\022k\n\007GetTask\022\026.a2a.v1.GetT" + - "askRequest\032\014.a2a.v1.Task\":\332A\004name\202\323\344\223\002-\022" + - "\017/{name=tasks/*}Z\032\022\030/{tenant}/{name=task" + - "s/*}\022c\n\tListTasks\022\030.a2a.v1.ListTasksRequ" + - "est\032\031.a2a.v1.ListTasksResponse\"!\202\323\344\223\002\033\022\006" + - "/tasksZ\021\022\017/{tenant}/tasks\022~\n\nCancelTask\022" + - "\031.a2a.v1.CancelTaskRequest\032\014.a2a.v1.Task" + - "\"G\202\323\344\223\002A\"\026/{name=tasks/*}:cancel:\001*Z$\"\037/" + - "{tenant}/{name=tasks/*}:cancel:\001*\022\224\001\n\017Su" + - "bscribeToTask\022\036.a2a.v1.SubscribeToTaskRe" + - "quest\032\026.a2a.v1.StreamResponse\"G\202\323\344\223\002A\022\031/" + - "{name=tasks/*}:subscribeZ$\022\"/{tenant}/{n" + - "ame=tasks/*}:subscribe0\001\022\373\001\n\035SetTaskPush" + - "NotificationConfig\022,.a2a.v1.SetTaskPushN" + - "otificationConfigRequest\032\".a2a.v1.TaskPu" + - "shNotificationConfig\"\207\001\332A\rparent,config\202" + - "\323\344\223\002q\")/{parent=tasks/*/pushNotification" + - "Configs}:\006configZ<\"2/{tenant}/{parent=ta" + - "sks/*/pushNotificationConfigs}:\006config\022\341" + - "\001\n\035GetTaskPushNotificationConfig\022,.a2a.v" + - "1.GetTaskPushNotificationConfigRequest\032\"" + - ".a2a.v1.TaskPushNotificationConfig\"n\332A\004n" + - "ame\202\323\344\223\002a\022)/{name=tasks/*/pushNotificati" + - "onConfigs/*}Z4\0222/{tenant}/{name=tasks/*/" + - "pushNotificationConfigs/*}\022\361\001\n\036ListTaskP" + - "ushNotificationConfig\022-.a2a.v1.ListTaskP" + - "ushNotificationConfigRequest\032..a2a.v1.Li" + - "stTaskPushNotificationConfigResponse\"p\332A" + - "\006parent\202\323\344\223\002a\022)/{parent=tasks/*}/pushNot" + - "ificationConfigsZ4\0222/{tenant}/{parent=ta" + - "sks/*}/pushNotificationConfigs\022\211\001\n\024GetEx" + - "tendedAgentCard\022#.a2a.v1.GetExtendedAgen" + - "tCardRequest\032\021.a2a.v1.AgentCard\"9\202\323\344\223\0023\022" + - "\022/extendedAgentCardZ\035\022\033/{tenant}/extende" + - "dAgentCard\022\333\001\n DeleteTaskPushNotificatio" + - "nConfig\022/.a2a.v1.DeleteTaskPushNotificat" + - "ionConfigRequest\032\026.google.protobuf.Empty" + - "\"n\332A\004name\202\323\344\223\002a*)/{name=tasks/*/pushNoti" + - "ficationConfigs/*}Z4*2/{tenant}/{name=ta" + - "sks/*/pushNotificationConfigs/*}B7\n\013io.a" + - "2a.grpcB\003A2AP\001Z\030google.golang.org/a2a/v1" + - "\252\002\006A2a.V1b\006proto3" + "alue\030\002 \001(\t:\0028\001\"\326\001\n\023DeviceCodeOAuthFlow\022%" + + "\n\030device_authorization_url\030\001 \001(\tB\003\340A\002\022\026\n" + + "\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003 \001" + + "(\t\022<\n\006scopes\030\004 \003(\0132\'.a2a.v1.DeviceCodeOA" + + "uthFlow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry\022" + + "\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\257\001\n\022Send" + + "MessageRequest\022\016\n\006tenant\030\004 \001(\t\022%\n\007messag" + + "e\030\001 \001(\0132\017.a2a.v1.MessageB\003\340A\002\0227\n\rconfigu" + + "ration\030\002 \001(\0132 .a2a.v1.SendMessageConfigu" + + "ration\022)\n\010metadata\030\003 \001(\0132\027.google.protob" + + "uf.Struct\"c\n\016GetTaskRequest\022\016\n\006tenant\030\003 " + + "\001(\t\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\033\n\016history_length" + + "\030\002 \001(\005H\000\210\001\001B\021\n\017_history_length\"\265\002\n\020ListT" + + "asksRequest\022\016\n\006tenant\030\t \001(\t\022\022\n\ncontext_i" + + "d\030\001 \001(\t\022!\n\006status\030\002 \001(\0162\021.a2a.v1.TaskSta" + + "te\022\026\n\tpage_size\030\003 \001(\005H\000\210\001\001\022\022\n\npage_token" + + "\030\004 \001(\t\022\033\n\016history_length\030\005 \001(\005H\001\210\001\001\022:\n\026s" + + "tatus_timestamp_after\030\006 \001(\0132\032.google.pro" + + "tobuf.Timestamp\022\036\n\021include_artifacts\030\007 \001" + + "(\010H\002\210\001\001B\014\n\n_page_sizeB\021\n\017_history_length" + + "B\024\n\022_include_artifacts\"\204\001\n\021ListTasksResp" + + "onse\022 \n\005tasks\030\001 \003(\0132\014.a2a.v1.TaskB\003\340A\002\022\034" + + "\n\017next_page_token\030\002 \001(\tB\003\340A\002\022\026\n\tpage_siz" + + "e\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_size\030\004 \001(\005B\003\340A\002\"1\n" + + "\021CancelTaskRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004na" + + "me\030\001 \001(\t\"D\n$GetTaskPushNotificationConfi" + + "gRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"G" + + "\n\'DeleteTaskPushNotificationConfigReques" + + "t\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"\234\001\n$SetT" + + "askPushNotificationConfigRequest\022\016\n\006tena" + + "nt\030\004 \001(\t\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\tconfig_" + + "id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a2a.v1.T" + + "askPushNotificationConfigB\003\340A\002\"6\n\026Subscr" + + "ibeToTaskRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name" + + "\030\001 \001(\t\"n\n%ListTaskPushNotificationConfig" + + "Request\022\016\n\006tenant\030\004 \001(\t\022\016\n\006parent\030\001 \001(\t\022" + + "\021\n\tpage_size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t\"-" + + "\n\033GetExtendedAgentCardRequest\022\016\n\006tenant\030" + + "\001 \001(\t\"b\n\023SendMessageResponse\022\034\n\004task\030\001 \001" + + "(\0132\014.a2a.v1.TaskH\000\022\"\n\007message\030\002 \001(\0132\017.a2" + + "a.v1.MessageH\000B\t\n\007payload\"\321\001\n\016StreamResp" + + "onse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\"\n\007me" + + "ssage\030\002 \001(\0132\017.a2a.v1.MessageH\000\0226\n\rstatus" + + "_update\030\003 \001(\0132\035.a2a.v1.TaskStatusUpdateE" + + "ventH\000\022:\n\017artifact_update\030\004 \001(\0132\037.a2a.v1" + + ".TaskArtifactUpdateEventH\000B\t\n\007payload\"v\n" + + "&ListTaskPushNotificationConfigResponse\022" + + "3\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPushNotifi" + + "cationConfig\022\027\n\017next_page_token\030\002 \001(\t*\372\001" + + "\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020\000\022" + + "\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STATE_" + + "WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n\021T" + + "ASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCELL" + + "ED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n\023T" + + "ASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUTH_" + + "REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020\000" + + "\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\276\016\n\nA2AS" + + "ervice\022}\n\013SendMessage\022\032.a2a.v1.SendMessa" + + "geRequest\032\033.a2a.v1.SendMessageResponse\"5" + + "\202\323\344\223\002/\"\r/message:send:\001*Z\033\"\026/{tenant}/me" + + "ssage:send:\001*\022\207\001\n\024SendStreamingMessage\022\032" + + ".a2a.v1.SendMessageRequest\032\026.a2a.v1.Stre" + + "amResponse\"9\202\323\344\223\0023\"\017/message:stream:\001*Z\035" + + "\"\030/{tenant}/message:stream:\001*0\001\022k\n\007GetTa" + + "sk\022\026.a2a.v1.GetTaskRequest\032\014.a2a.v1.Task" + + "\":\332A\004name\202\323\344\223\002-\022\017/{name=tasks/*}Z\032\022\030/{te" + + "nant}/{name=tasks/*}\022c\n\tListTasks\022\030.a2a." + + "v1.ListTasksRequest\032\031.a2a.v1.ListTasksRe" + + "sponse\"!\202\323\344\223\002\033\022\006/tasksZ\021\022\017/{tenant}/task" + + "s\022~\n\nCancelTask\022\031.a2a.v1.CancelTaskReque" + + "st\032\014.a2a.v1.Task\"G\202\323\344\223\002A\"\026/{name=tasks/*" + + "}:cancel:\001*Z$\"\037/{tenant}/{name=tasks/*}:" + + "cancel:\001*\022\224\001\n\017SubscribeToTask\022\036.a2a.v1.S" + + "ubscribeToTaskRequest\032\026.a2a.v1.StreamRes" + + "ponse\"G\202\323\344\223\002A\022\031/{name=tasks/*}:subscribe" + + "Z$\022\"/{tenant}/{name=tasks/*}:subscribe0\001" + + "\022\373\001\n\035SetTaskPushNotificationConfig\022,.a2a" + + ".v1.SetTaskPushNotificationConfigRequest" + + "\032\".a2a.v1.TaskPushNotificationConfig\"\207\001\332" + + "A\rparent,config\202\323\344\223\002q\")/{parent=tasks/*/" + + "pushNotificationConfigs}:\006configZ<\"2/{te" + + "nant}/{parent=tasks/*/pushNotificationCo" + + "nfigs}:\006config\022\341\001\n\035GetTaskPushNotificati" + + "onConfig\022,.a2a.v1.GetTaskPushNotificatio" + + "nConfigRequest\032\".a2a.v1.TaskPushNotifica" + + "tionConfig\"n\332A\004name\202\323\344\223\002a\022)/{name=tasks/" + + "*/pushNotificationConfigs/*}Z4\0222/{tenant" + + "}/{name=tasks/*/pushNotificationConfigs/" + + "*}\022\361\001\n\036ListTaskPushNotificationConfig\022-." + + "a2a.v1.ListTaskPushNotificationConfigReq" + + "uest\032..a2a.v1.ListTaskPushNotificationCo" + + "nfigResponse\"p\332A\006parent\202\323\344\223\002a\022)/{parent=" + + "tasks/*}/pushNotificationConfigsZ4\0222/{te" + + "nant}/{parent=tasks/*}/pushNotificationC" + + "onfigs\022\211\001\n\024GetExtendedAgentCard\022#.a2a.v1" + + ".GetExtendedAgentCardRequest\032\021.a2a.v1.Ag" + + "entCard\"9\202\323\344\223\0023\022\022/extendedAgentCardZ\035\022\033/" + + "{tenant}/extendedAgentCard\022\333\001\n DeleteTas" + + "kPushNotificationConfig\022/.a2a.v1.DeleteT" + + "askPushNotificationConfigRequest\032\026.googl" + + "e.protobuf.Empty\"n\332A\004name\202\323\344\223\002a*)/{name=" + + "tasks/*/pushNotificationConfigs/*}Z4*2/{" + + "tenant}/{name=tasks/*/pushNotificationCo" + + "nfigs/*}B7\n\013io.a2a.grpcB\003A2AP\001Z\030google.g" + + "olang.org/a2a/v1\252\002\006A2a.V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -639,7 +621,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCard_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCard_descriptor, - new java.lang.String[] { "ProtocolVersion", "Name", "Description", "SupportedInterfaces", "Url", "PreferredTransport", "AdditionalInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "SupportsExtendedAgentCard", "Signatures", "IconUrl", }); + new java.lang.String[] { "ProtocolVersions", "Name", "Description", "SupportedInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "Signatures", "IconUrl", }); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor = internal_static_a2a_v1_AgentCard_descriptor.getNestedType(0); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new @@ -657,7 +639,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCapabilities_descriptor, - new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "StateTransitionHistory", }); + new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "StateTransitionHistory", "ExtendedAgentCard", }); internal_static_a2a_v1_AgentExtension_descriptor = getDescriptor().getMessageType(16); internal_static_a2a_v1_AgentExtension_fieldAccessorTable = new @@ -741,13 +723,13 @@ public static void registerAllExtensions( internal_static_a2a_v1_OAuthFlows_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OAuthFlows_descriptor, - new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "Implicit", "Password", "Flow", }); + new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "DeviceCode", "Flow", }); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor = getDescriptor().getMessageType(29); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor, - new java.lang.String[] { "AuthorizationUrl", "TokenUrl", "RefreshUrl", "Scopes", }); + new java.lang.String[] { "AuthorizationUrl", "TokenUrl", "RefreshUrl", "Scopes", "PkceRequired", }); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor = internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor.getNestedType(0); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_fieldAccessorTable = new @@ -766,110 +748,98 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); - internal_static_a2a_v1_ImplicitOAuthFlow_descriptor = + internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor = getDescriptor().getMessageType(31); - internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable = new + internal_static_a2a_v1_DeviceCodeOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_ImplicitOAuthFlow_descriptor, - new java.lang.String[] { "AuthorizationUrl", "RefreshUrl", "Scopes", }); - internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor = - internal_static_a2a_v1_ImplicitOAuthFlow_descriptor.getNestedType(0); - internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable = new + internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor, + new java.lang.String[] { "DeviceAuthorizationUrl", "TokenUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_DeviceCodeOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor.getNestedType(0); + internal_static_a2a_v1_DeviceCodeOAuthFlow_ScopesEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, - new java.lang.String[] { "Key", "Value", }); - internal_static_a2a_v1_PasswordOAuthFlow_descriptor = - getDescriptor().getMessageType(32); - internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_PasswordOAuthFlow_descriptor, - new java.lang.String[] { "TokenUrl", "RefreshUrl", "Scopes", }); - internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor = - internal_static_a2a_v1_PasswordOAuthFlow_descriptor.getNestedType(0); - internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, + internal_static_a2a_v1_DeviceCodeOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_SendMessageRequest_descriptor = - getDescriptor().getMessageType(33); + getDescriptor().getMessageType(32); internal_static_a2a_v1_SendMessageRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SendMessageRequest_descriptor, - new java.lang.String[] { "Tenant", "Request", "Configuration", "Metadata", }); + new java.lang.String[] { "Tenant", "Message", "Configuration", "Metadata", }); internal_static_a2a_v1_GetTaskRequest_descriptor = - getDescriptor().getMessageType(34); + getDescriptor().getMessageType(33); internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskRequest_descriptor, new java.lang.String[] { "Tenant", "Name", "HistoryLength", }); internal_static_a2a_v1_ListTasksRequest_descriptor = - getDescriptor().getMessageType(35); + getDescriptor().getMessageType(34); internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTasksRequest_descriptor, - new java.lang.String[] { "Tenant", "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "LastUpdatedAfter", "IncludeArtifacts", }); + new java.lang.String[] { "Tenant", "ContextId", "Status", "PageSize", "PageToken", "HistoryLength", "StatusTimestampAfter", "IncludeArtifacts", }); internal_static_a2a_v1_ListTasksResponse_descriptor = - getDescriptor().getMessageType(36); + getDescriptor().getMessageType(35); internal_static_a2a_v1_ListTasksResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTasksResponse_descriptor, new java.lang.String[] { "Tasks", "NextPageToken", "PageSize", "TotalSize", }); internal_static_a2a_v1_CancelTaskRequest_descriptor = - getDescriptor().getMessageType(37); + getDescriptor().getMessageType(36); internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CancelTaskRequest_descriptor, new java.lang.String[] { "Tenant", "Name", }); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageType(38); + getDescriptor().getMessageType(37); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Tenant", "Name", }); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageType(39); + getDescriptor().getMessageType(38); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Tenant", "Name", }); internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageType(40); + getDescriptor().getMessageType(39); internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Tenant", "Parent", "ConfigId", "Config", }); internal_static_a2a_v1_SubscribeToTaskRequest_descriptor = - getDescriptor().getMessageType(41); + getDescriptor().getMessageType(40); internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SubscribeToTaskRequest_descriptor, new java.lang.String[] { "Tenant", "Name", }); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor = - getDescriptor().getMessageType(42); + getDescriptor().getMessageType(41); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Tenant", "Parent", "PageSize", "PageToken", }); internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor = - getDescriptor().getMessageType(43); + getDescriptor().getMessageType(42); internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor, new java.lang.String[] { "Tenant", }); internal_static_a2a_v1_SendMessageResponse_descriptor = - getDescriptor().getMessageType(44); + getDescriptor().getMessageType(43); internal_static_a2a_v1_SendMessageResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SendMessageResponse_descriptor, - new java.lang.String[] { "Task", "Msg", "Payload", }); + new java.lang.String[] { "Task", "Message", "Payload", }); internal_static_a2a_v1_StreamResponse_descriptor = - getDescriptor().getMessageType(45); + getDescriptor().getMessageType(44); internal_static_a2a_v1_StreamResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_StreamResponse_descriptor, - new java.lang.String[] { "Task", "Msg", "StatusUpdate", "ArtifactUpdate", "Payload", }); + new java.lang.String[] { "Task", "Message", "StatusUpdate", "ArtifactUpdate", "Payload", }); internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor = - getDescriptor().getMessageType(46); + getDescriptor().getMessageType(45); internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigResponse_descriptor, diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java index 84ef70448..2e6c44ab8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -192,6 +192,33 @@ public boolean getStateTransitionHistory() { return stateTransitionHistory_; } + public static final int EXTENDED_AGENT_CARD_FIELD_NUMBER = 5; + private boolean extendedAgentCard_ = false; + /** + *
    +   * Indicates if the agent supports providing an extended agent card when authenticated.
    +   * 
    + * + * optional bool extended_agent_card = 5; + * @return Whether the extendedAgentCard field is set. + */ + @java.lang.Override + public boolean hasExtendedAgentCard() { + return ((bitField0_ & 0x00000008) != 0); + } + /** + *
    +   * Indicates if the agent supports providing an extended agent card when authenticated.
    +   * 
    + * + * optional bool extended_agent_card = 5; + * @return The extendedAgentCard. + */ + @java.lang.Override + public boolean getExtendedAgentCard() { + return extendedAgentCard_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -218,6 +245,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000004) != 0)) { output.writeBool(4, stateTransitionHistory_); } + if (((bitField0_ & 0x00000008) != 0)) { + output.writeBool(5, extendedAgentCard_); + } getUnknownFields().writeTo(output); } @@ -243,6 +273,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeBoolSize(4, stateTransitionHistory_); } + if (((bitField0_ & 0x00000008) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, extendedAgentCard_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -275,6 +309,11 @@ public boolean equals(final java.lang.Object obj) { if (getStateTransitionHistory() != other.getStateTransitionHistory()) return false; } + if (hasExtendedAgentCard() != other.hasExtendedAgentCard()) return false; + if (hasExtendedAgentCard()) { + if (getExtendedAgentCard() + != other.getExtendedAgentCard()) return false; + } if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -305,6 +344,11 @@ public int hashCode() { hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( getStateTransitionHistory()); } + if (hasExtendedAgentCard()) { + hash = (37 * hash) + EXTENDED_AGENT_CARD_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getExtendedAgentCard()); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -451,6 +495,7 @@ public Builder clear() { } bitField0_ = (bitField0_ & ~0x00000004); stateTransitionHistory_ = false; + extendedAgentCard_ = false; return this; } @@ -510,6 +555,10 @@ private void buildPartial0(io.a2a.grpc.AgentCapabilities result) { result.stateTransitionHistory_ = stateTransitionHistory_; to_bitField0_ |= 0x00000004; } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.extendedAgentCard_ = extendedAgentCard_; + to_bitField0_ |= 0x00000008; + } result.bitField0_ |= to_bitField0_; } @@ -560,6 +609,9 @@ public Builder mergeFrom(io.a2a.grpc.AgentCapabilities other) { if (other.hasStateTransitionHistory()) { setStateTransitionHistory(other.getStateTransitionHistory()); } + if (other.hasExtendedAgentCard()) { + setExtendedAgentCard(other.getExtendedAgentCard()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -614,6 +666,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000008; break; } // case 32 + case 40: { + extendedAgentCard_ = input.readBool(); + bitField0_ |= 0x00000010; + break; + } // case 40 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -1111,6 +1168,62 @@ public Builder clearStateTransitionHistory() { return this; } + private boolean extendedAgentCard_ ; + /** + *
    +     * Indicates if the agent supports providing an extended agent card when authenticated.
    +     * 
    + * + * optional bool extended_agent_card = 5; + * @return Whether the extendedAgentCard field is set. + */ + @java.lang.Override + public boolean hasExtendedAgentCard() { + return ((bitField0_ & 0x00000010) != 0); + } + /** + *
    +     * Indicates if the agent supports providing an extended agent card when authenticated.
    +     * 
    + * + * optional bool extended_agent_card = 5; + * @return The extendedAgentCard. + */ + @java.lang.Override + public boolean getExtendedAgentCard() { + return extendedAgentCard_; + } + /** + *
    +     * Indicates if the agent supports providing an extended agent card when authenticated.
    +     * 
    + * + * optional bool extended_agent_card = 5; + * @param value The extendedAgentCard to set. + * @return This builder for chaining. + */ + public Builder setExtendedAgentCard(boolean value) { + + extendedAgentCard_ = value; + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
    +     * Indicates if the agent supports providing an extended agent card when authenticated.
    +     * 
    + * + * optional bool extended_agent_card = 5; + * @return This builder for chaining. + */ + public Builder clearExtendedAgentCard() { + bitField0_ = (bitField0_ & ~0x00000010); + extendedAgentCard_ = false; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentCapabilities) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java index df5d9277c..b084db7ee 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java @@ -110,4 +110,23 @@ io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( * @return The stateTransitionHistory. */ boolean getStateTransitionHistory(); + + /** + *
    +   * Indicates if the agent supports providing an extended agent card when authenticated.
    +   * 
    + * + * optional bool extended_agent_card = 5; + * @return Whether the extendedAgentCard field is set. + */ + boolean hasExtendedAgentCard(); + /** + *
    +   * Indicates if the agent supports providing an extended agent card when authenticated.
    +   * 
    + * + * optional bool extended_agent_card = 5; + * @return The extendedAgentCard. + */ + boolean getExtendedAgentCard(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index b352e84f5..5111e4f12 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -36,13 +36,11 @@ private AgentCard(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private AgentCard() { - protocolVersion_ = ""; + protocolVersions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); name_ = ""; description_ = ""; supportedInterfaces_ = java.util.Collections.emptyList(); - url_ = ""; - preferredTransport_ = ""; - additionalInterfaces_ = java.util.Collections.emptyList(); version_ = ""; documentationUrl_ = ""; security_ = java.util.Collections.emptyList(); @@ -81,66 +79,69 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl } private int bitField0_; - public static final int PROTOCOL_VERSION_FIELD_NUMBER = 16; + public static final int PROTOCOL_VERSIONS_FIELD_NUMBER = 16; @SuppressWarnings("serial") - private volatile java.lang.Object protocolVersion_ = ""; + private com.google.protobuf.LazyStringArrayList protocolVersions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); /** *
    -   * The version of the A2A protocol this agent supports.
    -   * Default: "1.0"
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
        * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return Whether the protocolVersion field is set. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return A list containing the protocolVersions. */ - @java.lang.Override - public boolean hasProtocolVersion() { - return ((bitField0_ & 0x00000001) != 0); + public com.google.protobuf.ProtocolStringList + getProtocolVersionsList() { + return protocolVersions_; } /** *
    -   * The version of the A2A protocol this agent supports.
    -   * Default: "1.0"
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
        * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The protocolVersion. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return The count of protocolVersions. */ - @java.lang.Override - public java.lang.String getProtocolVersion() { - java.lang.Object ref = protocolVersion_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - protocolVersion_ = s; - return s; - } + public int getProtocolVersionsCount() { + return protocolVersions_.size(); } /** *
    -   * The version of the A2A protocol this agent supports.
    -   * Default: "1.0"
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
        * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for protocolVersion. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index of the element to return. + * @return The protocolVersions at the given index. + */ + public java.lang.String getProtocolVersions(int index) { + return protocolVersions_.get(index); + } + /** + *
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
    +   * 
    + * + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index of the value to return. + * @return The bytes of the protocolVersions at the given index. */ - @java.lang.Override public com.google.protobuf.ByteString - getProtocolVersionBytes() { - java.lang.Object ref = protocolVersion_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - protocolVersion_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } + getProtocolVersionsBytes(int index) { + return protocolVersions_.getByteString(index); } public static final int NAME_FIELD_NUMBER = 1; @@ -251,7 +252,7 @@ public java.lang.String getDescription() { * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List getSupportedInterfacesList() { @@ -262,7 +263,7 @@ public java.util.List getSupportedInterfacesList() { * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.List @@ -274,7 +275,7 @@ public java.util.List getSupportedInterfacesList() { * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public int getSupportedInterfacesCount() { @@ -285,7 +286,7 @@ public int getSupportedInterfacesCount() { * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.AgentInterface getSupportedInterfaces(int index) { @@ -296,7 +297,7 @@ public io.a2a.grpc.AgentInterface getSupportedInterfaces(int index) { * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( @@ -304,197 +305,6 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( return supportedInterfaces_.get(index); } - public static final int URL_FIELD_NUMBER = 3; - @SuppressWarnings("serial") - private volatile java.lang.Object url_ = ""; - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return Whether the url field is set. - */ - @java.lang.Override - @java.lang.Deprecated public boolean hasUrl() { - return ((bitField0_ & 0x00000002) != 0); - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return The url. - */ - @java.lang.Override - @java.lang.Deprecated public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - url_ = s; - return s; - } - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return The bytes for url. - */ - @java.lang.Override - @java.lang.Deprecated public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int PREFERRED_TRANSPORT_FIELD_NUMBER = 14; - @SuppressWarnings("serial") - private volatile java.lang.Object preferredTransport_ = ""; - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return Whether the preferredTransport field is set. - */ - @java.lang.Override - @java.lang.Deprecated public boolean hasPreferredTransport() { - return ((bitField0_ & 0x00000004) != 0); - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return The preferredTransport. - */ - @java.lang.Override - @java.lang.Deprecated public java.lang.String getPreferredTransport() { - java.lang.Object ref = preferredTransport_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - preferredTransport_ = s; - return s; - } - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return The bytes for preferredTransport. - */ - @java.lang.Override - @java.lang.Deprecated public com.google.protobuf.ByteString - getPreferredTransportBytes() { - java.lang.Object ref = preferredTransport_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - preferredTransport_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int ADDITIONAL_INTERFACES_FIELD_NUMBER = 15; - @SuppressWarnings("serial") - private java.util.List additionalInterfaces_; - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Override - @java.lang.Deprecated public java.util.List getAdditionalInterfacesList() { - return additionalInterfaces_; - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Override - @java.lang.Deprecated public java.util.List - getAdditionalInterfacesOrBuilderList() { - return additionalInterfaces_; - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Override - @java.lang.Deprecated public int getAdditionalInterfacesCount() { - return additionalInterfaces_.size(); - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Override - @java.lang.Deprecated public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { - return additionalInterfaces_.get(index); - } - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Override - @java.lang.Deprecated public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( - int index) { - return additionalInterfaces_.get(index); - } - public static final int PROVIDER_FIELD_NUMBER = 4; private io.a2a.grpc.AgentProvider provider_; /** @@ -507,7 +317,7 @@ public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( */ @java.lang.Override public boolean hasProvider() { - return ((bitField0_ & 0x00000008) != 0); + return ((bitField0_ & 0x00000001) != 0); } /** *
    @@ -595,7 +405,7 @@ public java.lang.String getVersion() {
        */
       @java.lang.Override
       public boolean hasDocumentationUrl() {
    -    return ((bitField0_ & 0x00000010) != 0);
    +    return ((bitField0_ & 0x00000002) != 0);
       }
       /**
        * 
    @@ -653,7 +463,7 @@ public java.lang.String getDocumentationUrl() {
        */
       @java.lang.Override
       public boolean hasCapabilities() {
    -    return ((bitField0_ & 0x00000020) != 0);
    +    return ((bitField0_ & 0x00000004) != 0);
       }
       /**
        * 
    @@ -1025,33 +835,6 @@ public io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder(
         return skills_.get(index);
       }
     
    -  public static final int SUPPORTS_EXTENDED_AGENT_CARD_FIELD_NUMBER = 13;
    -  private boolean supportsExtendedAgentCard_ = false;
    -  /**
    -   * 
    -   * Whether the agent supports providing an extended agent card when authenticated.
    -   * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return Whether the supportsExtendedAgentCard field is set. - */ - @java.lang.Override - public boolean hasSupportsExtendedAgentCard() { - return ((bitField0_ & 0x00000040) != 0); - } - /** - *
    -   * Whether the agent supports providing an extended agent card when authenticated.
    -   * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return The supportsExtendedAgentCard. - */ - @java.lang.Override - public boolean getSupportsExtendedAgentCard() { - return supportsExtendedAgentCard_; - } - public static final int SIGNATURES_FIELD_NUMBER = 17; @SuppressWarnings("serial") private java.util.List signatures_; @@ -1126,7 +909,7 @@ public io.a2a.grpc.AgentCardSignatureOrBuilder getSignaturesOrBuilder( */ @java.lang.Override public boolean hasIconUrl() { - return ((bitField0_ & 0x00000080) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** *
    @@ -1192,19 +975,16 @@ public void writeTo(com.google.protobuf.CodedOutputStream output)
         if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) {
           com.google.protobuf.GeneratedMessage.writeString(output, 2, description_);
         }
    -    if (((bitField0_ & 0x00000002) != 0)) {
    -      com.google.protobuf.GeneratedMessage.writeString(output, 3, url_);
    -    }
    -    if (((bitField0_ & 0x00000008) != 0)) {
    +    if (((bitField0_ & 0x00000001) != 0)) {
           output.writeMessage(4, getProvider());
         }
         if (!com.google.protobuf.GeneratedMessage.isStringEmpty(version_)) {
           com.google.protobuf.GeneratedMessage.writeString(output, 5, version_);
         }
    -    if (((bitField0_ & 0x00000010) != 0)) {
    +    if (((bitField0_ & 0x00000002) != 0)) {
           com.google.protobuf.GeneratedMessage.writeString(output, 6, documentationUrl_);
         }
    -    if (((bitField0_ & 0x00000020) != 0)) {
    +    if (((bitField0_ & 0x00000004) != 0)) {
           output.writeMessage(7, getCapabilities());
         }
         com.google.protobuf.GeneratedMessage
    @@ -1225,22 +1005,13 @@ public void writeTo(com.google.protobuf.CodedOutputStream output)
         for (int i = 0; i < skills_.size(); i++) {
           output.writeMessage(12, skills_.get(i));
         }
    -    if (((bitField0_ & 0x00000040) != 0)) {
    -      output.writeBool(13, supportsExtendedAgentCard_);
    -    }
    -    if (((bitField0_ & 0x00000004) != 0)) {
    -      com.google.protobuf.GeneratedMessage.writeString(output, 14, preferredTransport_);
    -    }
    -    for (int i = 0; i < additionalInterfaces_.size(); i++) {
    -      output.writeMessage(15, additionalInterfaces_.get(i));
    -    }
    -    if (((bitField0_ & 0x00000001) != 0)) {
    -      com.google.protobuf.GeneratedMessage.writeString(output, 16, protocolVersion_);
    +    for (int i = 0; i < protocolVersions_.size(); i++) {
    +      com.google.protobuf.GeneratedMessage.writeString(output, 16, protocolVersions_.getRaw(i));
         }
         for (int i = 0; i < signatures_.size(); i++) {
           output.writeMessage(17, signatures_.get(i));
         }
    -    if (((bitField0_ & 0x00000080) != 0)) {
    +    if (((bitField0_ & 0x00000008) != 0)) {
           com.google.protobuf.GeneratedMessage.writeString(output, 18, iconUrl_);
         }
         for (int i = 0; i < supportedInterfaces_.size(); i++) {
    @@ -1261,20 +1032,17 @@ public int getSerializedSize() {
         if (!com.google.protobuf.GeneratedMessage.isStringEmpty(description_)) {
           size += com.google.protobuf.GeneratedMessage.computeStringSize(2, description_);
         }
    -    if (((bitField0_ & 0x00000002) != 0)) {
    -      size += com.google.protobuf.GeneratedMessage.computeStringSize(3, url_);
    -    }
    -    if (((bitField0_ & 0x00000008) != 0)) {
    +    if (((bitField0_ & 0x00000001) != 0)) {
           size += com.google.protobuf.CodedOutputStream
             .computeMessageSize(4, getProvider());
         }
         if (!com.google.protobuf.GeneratedMessage.isStringEmpty(version_)) {
           size += com.google.protobuf.GeneratedMessage.computeStringSize(5, version_);
         }
    -    if (((bitField0_ & 0x00000010) != 0)) {
    +    if (((bitField0_ & 0x00000002) != 0)) {
           size += com.google.protobuf.GeneratedMessage.computeStringSize(6, documentationUrl_);
         }
    -    if (((bitField0_ & 0x00000020) != 0)) {
    +    if (((bitField0_ & 0x00000004) != 0)) {
           size += com.google.protobuf.CodedOutputStream
             .computeMessageSize(7, getCapabilities());
         }
    @@ -1312,25 +1080,19 @@ public int getSerializedSize() {
           size += com.google.protobuf.CodedOutputStream
             .computeMessageSize(12, skills_.get(i));
         }
    -    if (((bitField0_ & 0x00000040) != 0)) {
    -      size += com.google.protobuf.CodedOutputStream
    -        .computeBoolSize(13, supportsExtendedAgentCard_);
    -    }
    -    if (((bitField0_ & 0x00000004) != 0)) {
    -      size += com.google.protobuf.GeneratedMessage.computeStringSize(14, preferredTransport_);
    -    }
    -    for (int i = 0; i < additionalInterfaces_.size(); i++) {
    -      size += com.google.protobuf.CodedOutputStream
    -        .computeMessageSize(15, additionalInterfaces_.get(i));
    -    }
    -    if (((bitField0_ & 0x00000001) != 0)) {
    -      size += com.google.protobuf.GeneratedMessage.computeStringSize(16, protocolVersion_);
    +    {
    +      int dataSize = 0;
    +      for (int i = 0; i < protocolVersions_.size(); i++) {
    +        dataSize += computeStringSizeNoTag(protocolVersions_.getRaw(i));
    +      }
    +      size += dataSize;
    +      size += 2 * getProtocolVersionsList().size();
         }
         for (int i = 0; i < signatures_.size(); i++) {
           size += com.google.protobuf.CodedOutputStream
             .computeMessageSize(17, signatures_.get(i));
         }
    -    if (((bitField0_ & 0x00000080) != 0)) {
    +    if (((bitField0_ & 0x00000008) != 0)) {
           size += com.google.protobuf.GeneratedMessage.computeStringSize(18, iconUrl_);
         }
         for (int i = 0; i < supportedInterfaces_.size(); i++) {
    @@ -1352,29 +1114,14 @@ public boolean equals(final java.lang.Object obj) {
         }
         io.a2a.grpc.AgentCard other = (io.a2a.grpc.AgentCard) obj;
     
    -    if (hasProtocolVersion() != other.hasProtocolVersion()) return false;
    -    if (hasProtocolVersion()) {
    -      if (!getProtocolVersion()
    -          .equals(other.getProtocolVersion())) return false;
    -    }
    +    if (!getProtocolVersionsList()
    +        .equals(other.getProtocolVersionsList())) return false;
         if (!getName()
             .equals(other.getName())) return false;
         if (!getDescription()
             .equals(other.getDescription())) return false;
         if (!getSupportedInterfacesList()
             .equals(other.getSupportedInterfacesList())) return false;
    -    if (hasUrl() != other.hasUrl()) return false;
    -    if (hasUrl()) {
    -      if (!getUrl()
    -          .equals(other.getUrl())) return false;
    -    }
    -    if (hasPreferredTransport() != other.hasPreferredTransport()) return false;
    -    if (hasPreferredTransport()) {
    -      if (!getPreferredTransport()
    -          .equals(other.getPreferredTransport())) return false;
    -    }
    -    if (!getAdditionalInterfacesList()
    -        .equals(other.getAdditionalInterfacesList())) return false;
         if (hasProvider() != other.hasProvider()) return false;
         if (hasProvider()) {
           if (!getProvider()
    @@ -1402,11 +1149,6 @@ public boolean equals(final java.lang.Object obj) {
             .equals(other.getDefaultOutputModesList())) return false;
         if (!getSkillsList()
             .equals(other.getSkillsList())) return false;
    -    if (hasSupportsExtendedAgentCard() != other.hasSupportsExtendedAgentCard()) return false;
    -    if (hasSupportsExtendedAgentCard()) {
    -      if (getSupportsExtendedAgentCard()
    -          != other.getSupportsExtendedAgentCard()) return false;
    -    }
         if (!getSignaturesList()
             .equals(other.getSignaturesList())) return false;
         if (hasIconUrl() != other.hasIconUrl()) return false;
    @@ -1425,9 +1167,9 @@ public int hashCode() {
         }
         int hash = 41;
         hash = (19 * hash) + getDescriptor().hashCode();
    -    if (hasProtocolVersion()) {
    -      hash = (37 * hash) + PROTOCOL_VERSION_FIELD_NUMBER;
    -      hash = (53 * hash) + getProtocolVersion().hashCode();
    +    if (getProtocolVersionsCount() > 0) {
    +      hash = (37 * hash) + PROTOCOL_VERSIONS_FIELD_NUMBER;
    +      hash = (53 * hash) + getProtocolVersionsList().hashCode();
         }
         hash = (37 * hash) + NAME_FIELD_NUMBER;
         hash = (53 * hash) + getName().hashCode();
    @@ -1437,18 +1179,6 @@ public int hashCode() {
           hash = (37 * hash) + SUPPORTED_INTERFACES_FIELD_NUMBER;
           hash = (53 * hash) + getSupportedInterfacesList().hashCode();
         }
    -    if (hasUrl()) {
    -      hash = (37 * hash) + URL_FIELD_NUMBER;
    -      hash = (53 * hash) + getUrl().hashCode();
    -    }
    -    if (hasPreferredTransport()) {
    -      hash = (37 * hash) + PREFERRED_TRANSPORT_FIELD_NUMBER;
    -      hash = (53 * hash) + getPreferredTransport().hashCode();
    -    }
    -    if (getAdditionalInterfacesCount() > 0) {
    -      hash = (37 * hash) + ADDITIONAL_INTERFACES_FIELD_NUMBER;
    -      hash = (53 * hash) + getAdditionalInterfacesList().hashCode();
    -    }
         if (hasProvider()) {
           hash = (37 * hash) + PROVIDER_FIELD_NUMBER;
           hash = (53 * hash) + getProvider().hashCode();
    @@ -1483,11 +1213,6 @@ public int hashCode() {
           hash = (37 * hash) + SKILLS_FIELD_NUMBER;
           hash = (53 * hash) + getSkillsList().hashCode();
         }
    -    if (hasSupportsExtendedAgentCard()) {
    -      hash = (37 * hash) + SUPPORTS_EXTENDED_AGENT_CARD_FIELD_NUMBER;
    -      hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(
    -          getSupportsExtendedAgentCard());
    -    }
         if (getSignaturesCount() > 0) {
           hash = (37 * hash) + SIGNATURES_FIELD_NUMBER;
           hash = (53 * hash) + getSignaturesList().hashCode();
    @@ -1657,7 +1382,6 @@ private void maybeForceBuilderInitialization() {
           if (com.google.protobuf.GeneratedMessage
                   .alwaysUseFieldBuilders) {
             internalGetSupportedInterfacesFieldBuilder();
    -        internalGetAdditionalInterfacesFieldBuilder();
             internalGetProviderFieldBuilder();
             internalGetCapabilitiesFieldBuilder();
             internalGetSecurityFieldBuilder();
    @@ -1669,7 +1393,8 @@ private void maybeForceBuilderInitialization() {
         public Builder clear() {
           super.clear();
           bitField0_ = 0;
    -      protocolVersion_ = "";
    +      protocolVersions_ =
    +          com.google.protobuf.LazyStringArrayList.emptyList();
           name_ = "";
           description_ = "";
           if (supportedInterfacesBuilder_ == null) {
    @@ -1679,15 +1404,6 @@ public Builder clear() {
             supportedInterfacesBuilder_.clear();
           }
           bitField0_ = (bitField0_ & ~0x00000008);
    -      url_ = "";
    -      preferredTransport_ = "";
    -      if (additionalInterfacesBuilder_ == null) {
    -        additionalInterfaces_ = java.util.Collections.emptyList();
    -      } else {
    -        additionalInterfaces_ = null;
    -        additionalInterfacesBuilder_.clear();
    -      }
    -      bitField0_ = (bitField0_ & ~0x00000040);
           provider_ = null;
           if (providerBuilder_ != null) {
             providerBuilder_.dispose();
    @@ -1707,7 +1423,7 @@ public Builder clear() {
             security_ = null;
             securityBuilder_.clear();
           }
    -      bitField0_ = (bitField0_ & ~0x00001000);
    +      bitField0_ = (bitField0_ & ~0x00000200);
           defaultInputModes_ =
               com.google.protobuf.LazyStringArrayList.emptyList();
           defaultOutputModes_ =
    @@ -1718,15 +1434,14 @@ public Builder clear() {
             skills_ = null;
             skillsBuilder_.clear();
           }
    -      bitField0_ = (bitField0_ & ~0x00008000);
    -      supportsExtendedAgentCard_ = false;
    +      bitField0_ = (bitField0_ & ~0x00001000);
           if (signaturesBuilder_ == null) {
             signatures_ = java.util.Collections.emptyList();
           } else {
             signatures_ = null;
             signaturesBuilder_.clear();
           }
    -      bitField0_ = (bitField0_ & ~0x00020000);
    +      bitField0_ = (bitField0_ & ~0x00002000);
           iconUrl_ = "";
           return this;
         }
    @@ -1770,37 +1485,28 @@ private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) {
           } else {
             result.supportedInterfaces_ = supportedInterfacesBuilder_.build();
           }
    -      if (additionalInterfacesBuilder_ == null) {
    -        if (((bitField0_ & 0x00000040) != 0)) {
    -          additionalInterfaces_ = java.util.Collections.unmodifiableList(additionalInterfaces_);
    -          bitField0_ = (bitField0_ & ~0x00000040);
    -        }
    -        result.additionalInterfaces_ = additionalInterfaces_;
    -      } else {
    -        result.additionalInterfaces_ = additionalInterfacesBuilder_.build();
    -      }
           if (securityBuilder_ == null) {
    -        if (((bitField0_ & 0x00001000) != 0)) {
    +        if (((bitField0_ & 0x00000200) != 0)) {
               security_ = java.util.Collections.unmodifiableList(security_);
    -          bitField0_ = (bitField0_ & ~0x00001000);
    +          bitField0_ = (bitField0_ & ~0x00000200);
             }
             result.security_ = security_;
           } else {
             result.security_ = securityBuilder_.build();
           }
           if (skillsBuilder_ == null) {
    -        if (((bitField0_ & 0x00008000) != 0)) {
    +        if (((bitField0_ & 0x00001000) != 0)) {
               skills_ = java.util.Collections.unmodifiableList(skills_);
    -          bitField0_ = (bitField0_ & ~0x00008000);
    +          bitField0_ = (bitField0_ & ~0x00001000);
             }
             result.skills_ = skills_;
           } else {
             result.skills_ = skillsBuilder_.build();
           }
           if (signaturesBuilder_ == null) {
    -        if (((bitField0_ & 0x00020000) != 0)) {
    +        if (((bitField0_ & 0x00002000) != 0)) {
               signatures_ = java.util.Collections.unmodifiableList(signatures_);
    -          bitField0_ = (bitField0_ & ~0x00020000);
    +          bitField0_ = (bitField0_ & ~0x00002000);
             }
             result.signatures_ = signatures_;
           } else {
    @@ -1810,10 +1516,9 @@ private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) {
     
         private void buildPartial0(io.a2a.grpc.AgentCard result) {
           int from_bitField0_ = bitField0_;
    -      int to_bitField0_ = 0;
           if (((from_bitField0_ & 0x00000001) != 0)) {
    -        result.protocolVersion_ = protocolVersion_;
    -        to_bitField0_ |= 0x00000001;
    +        protocolVersions_.makeImmutable();
    +        result.protocolVersions_ = protocolVersions_;
           }
           if (((from_bitField0_ & 0x00000002) != 0)) {
             result.name_ = name_;
    @@ -1821,51 +1526,40 @@ private void buildPartial0(io.a2a.grpc.AgentCard result) {
           if (((from_bitField0_ & 0x00000004) != 0)) {
             result.description_ = description_;
           }
    +      int to_bitField0_ = 0;
           if (((from_bitField0_ & 0x00000010) != 0)) {
    -        result.url_ = url_;
    -        to_bitField0_ |= 0x00000002;
    -      }
    -      if (((from_bitField0_ & 0x00000020) != 0)) {
    -        result.preferredTransport_ = preferredTransport_;
    -        to_bitField0_ |= 0x00000004;
    -      }
    -      if (((from_bitField0_ & 0x00000080) != 0)) {
             result.provider_ = providerBuilder_ == null
                 ? provider_
                 : providerBuilder_.build();
    -        to_bitField0_ |= 0x00000008;
    +        to_bitField0_ |= 0x00000001;
           }
    -      if (((from_bitField0_ & 0x00000100) != 0)) {
    +      if (((from_bitField0_ & 0x00000020) != 0)) {
             result.version_ = version_;
           }
    -      if (((from_bitField0_ & 0x00000200) != 0)) {
    +      if (((from_bitField0_ & 0x00000040) != 0)) {
             result.documentationUrl_ = documentationUrl_;
    -        to_bitField0_ |= 0x00000010;
    +        to_bitField0_ |= 0x00000002;
           }
    -      if (((from_bitField0_ & 0x00000400) != 0)) {
    +      if (((from_bitField0_ & 0x00000080) != 0)) {
             result.capabilities_ = capabilitiesBuilder_ == null
                 ? capabilities_
                 : capabilitiesBuilder_.build();
    -        to_bitField0_ |= 0x00000020;
    +        to_bitField0_ |= 0x00000004;
           }
    -      if (((from_bitField0_ & 0x00000800) != 0)) {
    +      if (((from_bitField0_ & 0x00000100) != 0)) {
             result.securitySchemes_ = internalGetSecuritySchemes().build(SecuritySchemesDefaultEntryHolder.defaultEntry);
           }
    -      if (((from_bitField0_ & 0x00002000) != 0)) {
    +      if (((from_bitField0_ & 0x00000400) != 0)) {
             defaultInputModes_.makeImmutable();
             result.defaultInputModes_ = defaultInputModes_;
           }
    -      if (((from_bitField0_ & 0x00004000) != 0)) {
    +      if (((from_bitField0_ & 0x00000800) != 0)) {
             defaultOutputModes_.makeImmutable();
             result.defaultOutputModes_ = defaultOutputModes_;
           }
    -      if (((from_bitField0_ & 0x00010000) != 0)) {
    -        result.supportsExtendedAgentCard_ = supportsExtendedAgentCard_;
    -        to_bitField0_ |= 0x00000040;
    -      }
    -      if (((from_bitField0_ & 0x00040000) != 0)) {
    +      if (((from_bitField0_ & 0x00004000) != 0)) {
             result.iconUrl_ = iconUrl_;
    -        to_bitField0_ |= 0x00000080;
    +        to_bitField0_ |= 0x00000008;
           }
           result.bitField0_ |= to_bitField0_;
         }
    @@ -1882,9 +1576,14 @@ public Builder mergeFrom(com.google.protobuf.Message other) {
     
         public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
           if (other == io.a2a.grpc.AgentCard.getDefaultInstance()) return this;
    -      if (other.hasProtocolVersion()) {
    -        protocolVersion_ = other.protocolVersion_;
    -        bitField0_ |= 0x00000001;
    +      if (!other.protocolVersions_.isEmpty()) {
    +        if (protocolVersions_.isEmpty()) {
    +          protocolVersions_ = other.protocolVersions_;
    +          bitField0_ |= 0x00000001;
    +        } else {
    +          ensureProtocolVersionsIsMutable();
    +          protocolVersions_.addAll(other.protocolVersions_);
    +        }
             onChanged();
           }
           if (!other.getName().isEmpty()) {
    @@ -1923,53 +1622,17 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
               }
             }
           }
    -      if (other.hasUrl()) {
    -        url_ = other.url_;
    -        bitField0_ |= 0x00000010;
    -        onChanged();
    +      if (other.hasProvider()) {
    +        mergeProvider(other.getProvider());
           }
    -      if (other.hasPreferredTransport()) {
    -        preferredTransport_ = other.preferredTransport_;
    +      if (!other.getVersion().isEmpty()) {
    +        version_ = other.version_;
             bitField0_ |= 0x00000020;
             onChanged();
           }
    -      if (additionalInterfacesBuilder_ == null) {
    -        if (!other.additionalInterfaces_.isEmpty()) {
    -          if (additionalInterfaces_.isEmpty()) {
    -            additionalInterfaces_ = other.additionalInterfaces_;
    -            bitField0_ = (bitField0_ & ~0x00000040);
    -          } else {
    -            ensureAdditionalInterfacesIsMutable();
    -            additionalInterfaces_.addAll(other.additionalInterfaces_);
    -          }
    -          onChanged();
    -        }
    -      } else {
    -        if (!other.additionalInterfaces_.isEmpty()) {
    -          if (additionalInterfacesBuilder_.isEmpty()) {
    -            additionalInterfacesBuilder_.dispose();
    -            additionalInterfacesBuilder_ = null;
    -            additionalInterfaces_ = other.additionalInterfaces_;
    -            bitField0_ = (bitField0_ & ~0x00000040);
    -            additionalInterfacesBuilder_ = 
    -              com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
    -                 internalGetAdditionalInterfacesFieldBuilder() : null;
    -          } else {
    -            additionalInterfacesBuilder_.addAllMessages(other.additionalInterfaces_);
    -          }
    -        }
    -      }
    -      if (other.hasProvider()) {
    -        mergeProvider(other.getProvider());
    -      }
    -      if (!other.getVersion().isEmpty()) {
    -        version_ = other.version_;
    -        bitField0_ |= 0x00000100;
    -        onChanged();
    -      }
           if (other.hasDocumentationUrl()) {
             documentationUrl_ = other.documentationUrl_;
    -        bitField0_ |= 0x00000200;
    +        bitField0_ |= 0x00000040;
             onChanged();
           }
           if (other.hasCapabilities()) {
    @@ -1977,12 +1640,12 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
           }
           internalGetMutableSecuritySchemes().mergeFrom(
               other.internalGetSecuritySchemes());
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000100;
           if (securityBuilder_ == null) {
             if (!other.security_.isEmpty()) {
               if (security_.isEmpty()) {
                 security_ = other.security_;
    -            bitField0_ = (bitField0_ & ~0x00001000);
    +            bitField0_ = (bitField0_ & ~0x00000200);
               } else {
                 ensureSecurityIsMutable();
                 security_.addAll(other.security_);
    @@ -1995,7 +1658,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
                 securityBuilder_.dispose();
                 securityBuilder_ = null;
                 security_ = other.security_;
    -            bitField0_ = (bitField0_ & ~0x00001000);
    +            bitField0_ = (bitField0_ & ~0x00000200);
                 securityBuilder_ = 
                   com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
                      internalGetSecurityFieldBuilder() : null;
    @@ -2007,7 +1670,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
           if (!other.defaultInputModes_.isEmpty()) {
             if (defaultInputModes_.isEmpty()) {
               defaultInputModes_ = other.defaultInputModes_;
    -          bitField0_ |= 0x00002000;
    +          bitField0_ |= 0x00000400;
             } else {
               ensureDefaultInputModesIsMutable();
               defaultInputModes_.addAll(other.defaultInputModes_);
    @@ -2017,7 +1680,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
           if (!other.defaultOutputModes_.isEmpty()) {
             if (defaultOutputModes_.isEmpty()) {
               defaultOutputModes_ = other.defaultOutputModes_;
    -          bitField0_ |= 0x00004000;
    +          bitField0_ |= 0x00000800;
             } else {
               ensureDefaultOutputModesIsMutable();
               defaultOutputModes_.addAll(other.defaultOutputModes_);
    @@ -2028,7 +1691,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
             if (!other.skills_.isEmpty()) {
               if (skills_.isEmpty()) {
                 skills_ = other.skills_;
    -            bitField0_ = (bitField0_ & ~0x00008000);
    +            bitField0_ = (bitField0_ & ~0x00001000);
               } else {
                 ensureSkillsIsMutable();
                 skills_.addAll(other.skills_);
    @@ -2041,7 +1704,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
                 skillsBuilder_.dispose();
                 skillsBuilder_ = null;
                 skills_ = other.skills_;
    -            bitField0_ = (bitField0_ & ~0x00008000);
    +            bitField0_ = (bitField0_ & ~0x00001000);
                 skillsBuilder_ = 
                   com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
                      internalGetSkillsFieldBuilder() : null;
    @@ -2050,14 +1713,11 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
               }
             }
           }
    -      if (other.hasSupportsExtendedAgentCard()) {
    -        setSupportsExtendedAgentCard(other.getSupportsExtendedAgentCard());
    -      }
           if (signaturesBuilder_ == null) {
             if (!other.signatures_.isEmpty()) {
               if (signatures_.isEmpty()) {
                 signatures_ = other.signatures_;
    -            bitField0_ = (bitField0_ & ~0x00020000);
    +            bitField0_ = (bitField0_ & ~0x00002000);
               } else {
                 ensureSignaturesIsMutable();
                 signatures_.addAll(other.signatures_);
    @@ -2070,7 +1730,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
                 signaturesBuilder_.dispose();
                 signaturesBuilder_ = null;
                 signatures_ = other.signatures_;
    -            bitField0_ = (bitField0_ & ~0x00020000);
    +            bitField0_ = (bitField0_ & ~0x00002000);
                 signaturesBuilder_ = 
                   com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
                      internalGetSignaturesFieldBuilder() : null;
    @@ -2081,7 +1741,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) {
           }
           if (other.hasIconUrl()) {
             iconUrl_ = other.iconUrl_;
    -        bitField0_ |= 0x00040000;
    +        bitField0_ |= 0x00004000;
             onChanged();
           }
           this.mergeUnknownFields(other.getUnknownFields());
    @@ -2120,33 +1780,28 @@ public Builder mergeFrom(
                   bitField0_ |= 0x00000004;
                   break;
                 } // case 18
    -            case 26: {
    -              url_ = input.readStringRequireUtf8();
    -              bitField0_ |= 0x00000010;
    -              break;
    -            } // case 26
                 case 34: {
                   input.readMessage(
                       internalGetProviderFieldBuilder().getBuilder(),
                       extensionRegistry);
    -              bitField0_ |= 0x00000080;
    +              bitField0_ |= 0x00000010;
                   break;
                 } // case 34
                 case 42: {
                   version_ = input.readStringRequireUtf8();
    -              bitField0_ |= 0x00000100;
    +              bitField0_ |= 0x00000020;
                   break;
                 } // case 42
                 case 50: {
                   documentationUrl_ = input.readStringRequireUtf8();
    -              bitField0_ |= 0x00000200;
    +              bitField0_ |= 0x00000040;
                   break;
                 } // case 50
                 case 58: {
                   input.readMessage(
                       internalGetCapabilitiesFieldBuilder().getBuilder(),
                       extensionRegistry);
    -              bitField0_ |= 0x00000400;
    +              bitField0_ |= 0x00000080;
                   break;
                 } // case 58
                 case 66: {
    @@ -2155,7 +1810,7 @@ public Builder mergeFrom(
                       SecuritySchemesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry);
                   internalGetMutableSecuritySchemes().ensureBuilderMap().put(
                       securitySchemes__.getKey(), securitySchemes__.getValue());
    -              bitField0_ |= 0x00000800;
    +              bitField0_ |= 0x00000100;
                   break;
                 } // case 66
                 case 74: {
    @@ -2196,32 +1851,10 @@ public Builder mergeFrom(
                   }
                   break;
                 } // case 98
    -            case 104: {
    -              supportsExtendedAgentCard_ = input.readBool();
    -              bitField0_ |= 0x00010000;
    -              break;
    -            } // case 104
    -            case 114: {
    -              preferredTransport_ = input.readStringRequireUtf8();
    -              bitField0_ |= 0x00000020;
    -              break;
    -            } // case 114
    -            case 122: {
    -              io.a2a.grpc.AgentInterface m =
    -                  input.readMessage(
    -                      io.a2a.grpc.AgentInterface.parser(),
    -                      extensionRegistry);
    -              if (additionalInterfacesBuilder_ == null) {
    -                ensureAdditionalInterfacesIsMutable();
    -                additionalInterfaces_.add(m);
    -              } else {
    -                additionalInterfacesBuilder_.addMessage(m);
    -              }
    -              break;
    -            } // case 122
                 case 130: {
    -              protocolVersion_ = input.readStringRequireUtf8();
    -              bitField0_ |= 0x00000001;
    +              java.lang.String s = input.readStringRequireUtf8();
    +              ensureProtocolVersionsIsMutable();
    +              protocolVersions_.add(s);
                   break;
                 } // case 130
                 case 138: {
    @@ -2239,7 +1872,7 @@ public Builder mergeFrom(
                 } // case 138
                 case 146: {
                   iconUrl_ = input.readStringRequireUtf8();
    -              bitField0_ |= 0x00040000;
    +              bitField0_ |= 0x00004000;
                   break;
                 } // case 146
                 case 154: {
    @@ -2272,110 +1905,175 @@ public Builder mergeFrom(
         }
         private int bitField0_;
     
    -    private java.lang.Object protocolVersion_ = "";
    +    private com.google.protobuf.LazyStringArrayList protocolVersions_ =
    +        com.google.protobuf.LazyStringArrayList.emptyList();
    +    private void ensureProtocolVersionsIsMutable() {
    +      if (!protocolVersions_.isModifiable()) {
    +        protocolVersions_ = new com.google.protobuf.LazyStringArrayList(protocolVersions_);
    +      }
    +      bitField0_ |= 0x00000001;
    +    }
         /**
          * 
    -     * The version of the A2A protocol this agent supports.
    -     * Default: "1.0"
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
          * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return Whether the protocolVersion field is set. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return A list containing the protocolVersions. */ - public boolean hasProtocolVersion() { - return ((bitField0_ & 0x00000001) != 0); + public com.google.protobuf.ProtocolStringList + getProtocolVersionsList() { + protocolVersions_.makeImmutable(); + return protocolVersions_; } /** *
    -     * The version of the A2A protocol this agent supports.
    -     * Default: "1.0"
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
          * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The protocolVersion. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return The count of protocolVersions. */ - public java.lang.String getProtocolVersion() { - java.lang.Object ref = protocolVersion_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - protocolVersion_ = s; - return s; - } else { - return (java.lang.String) ref; - } + public int getProtocolVersionsCount() { + return protocolVersions_.size(); + } + /** + *
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
    +     * 
    + * + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index of the element to return. + * @return The protocolVersions at the given index. + */ + public java.lang.String getProtocolVersions(int index) { + return protocolVersions_.get(index); } /** *
    -     * The version of the A2A protocol this agent supports.
    -     * Default: "1.0"
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
          * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for protocolVersion. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index of the value to return. + * @return The bytes of the protocolVersions at the given index. */ public com.google.protobuf.ByteString - getProtocolVersionBytes() { - java.lang.Object ref = protocolVersion_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - protocolVersion_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } + getProtocolVersionsBytes(int index) { + return protocolVersions_.getByteString(index); + } + /** + *
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
    +     * 
    + * + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index to set the value at. + * @param value The protocolVersions to set. + * @return This builder for chaining. + */ + public Builder setProtocolVersions( + int index, java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + ensureProtocolVersionsIsMutable(); + protocolVersions_.set(index, value); + bitField0_ |= 0x00000001; + onChanged(); + return this; } /** *
    -     * The version of the A2A protocol this agent supports.
    -     * Default: "1.0"
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
          * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param value The protocolVersion to set. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param value The protocolVersions to add. * @return This builder for chaining. */ - public Builder setProtocolVersion( + public Builder addProtocolVersions( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - protocolVersion_ = value; + ensureProtocolVersionsIsMutable(); + protocolVersions_.add(value); + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
    +     * 
    + * + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param values The protocolVersions to add. + * @return This builder for chaining. + */ + public Builder addAllProtocolVersions( + java.lang.Iterable values) { + ensureProtocolVersionsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, protocolVersions_); bitField0_ |= 0x00000001; onChanged(); return this; } /** *
    -     * The version of the A2A protocol this agent supports.
    -     * Default: "1.0"
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
          * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearProtocolVersion() { - protocolVersion_ = getDefaultInstance().getProtocolVersion(); - bitField0_ = (bitField0_ & ~0x00000001); + public Builder clearProtocolVersions() { + protocolVersions_ = + com.google.protobuf.LazyStringArrayList.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001);; onChanged(); return this; } /** *
    -     * The version of the A2A protocol this agent supports.
    -     * Default: "1.0"
    +     * The versions of the A2A protocol this agent supports.
    +     * For stable versions (1.x+), list only the latest supported minor version per major version.
    +     * For legacy experimental versions (0.x), explicitly list each supported version.
    +     * Default: ["1.0"]
          * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes for protocolVersion to set. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes of the protocolVersions to add. * @return This builder for chaining. */ - public Builder setProtocolVersionBytes( + public Builder addProtocolVersionsBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - protocolVersion_ = value; + ensureProtocolVersionsIsMutable(); + protocolVersions_.add(value); bitField0_ |= 0x00000001; onChanged(); return this; @@ -2472,572 +2170,30 @@ public Builder setNameBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; - bitField0_ |= 0x00000002; - onChanged(); - return this; - } - - private java.lang.Object description_ = ""; - /** - *
    -     * A human-readable description of the agent, assisting users and other agents
    -     * in understanding its purpose.
    -     * Example: "Agent that helps users with recipes and cooking."
    -     * 
    - * - * string description = 2 [(.google.api.field_behavior) = REQUIRED]; - * @return The description. - */ - public java.lang.String getDescription() { - java.lang.Object ref = description_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - description_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * A human-readable description of the agent, assisting users and other agents
    -     * in understanding its purpose.
    -     * Example: "Agent that helps users with recipes and cooking."
    -     * 
    - * - * string description = 2 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for description. - */ - public com.google.protobuf.ByteString - getDescriptionBytes() { - java.lang.Object ref = description_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - description_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * A human-readable description of the agent, assisting users and other agents
    -     * in understanding its purpose.
    -     * Example: "Agent that helps users with recipes and cooking."
    -     * 
    - * - * string description = 2 [(.google.api.field_behavior) = REQUIRED]; - * @param value The description to set. - * @return This builder for chaining. - */ - public Builder setDescription( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - description_ = value; - bitField0_ |= 0x00000004; - onChanged(); - return this; - } - /** - *
    -     * A human-readable description of the agent, assisting users and other agents
    -     * in understanding its purpose.
    -     * Example: "Agent that helps users with recipes and cooking."
    -     * 
    - * - * string description = 2 [(.google.api.field_behavior) = REQUIRED]; - * @return This builder for chaining. - */ - public Builder clearDescription() { - description_ = getDefaultInstance().getDescription(); - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - return this; - } - /** - *
    -     * A human-readable description of the agent, assisting users and other agents
    -     * in understanding its purpose.
    -     * Example: "Agent that helps users with recipes and cooking."
    -     * 
    - * - * string description = 2 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes for description to set. - * @return This builder for chaining. - */ - public Builder setDescriptionBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - description_ = value; - bitField0_ |= 0x00000004; - onChanged(); - return this; - } - - private java.util.List supportedInterfaces_ = - java.util.Collections.emptyList(); - private void ensureSupportedInterfacesIsMutable() { - if (!((bitField0_ & 0x00000008) != 0)) { - supportedInterfaces_ = new java.util.ArrayList(supportedInterfaces_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> supportedInterfacesBuilder_; - - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public java.util.List getSupportedInterfacesList() { - if (supportedInterfacesBuilder_ == null) { - return java.util.Collections.unmodifiableList(supportedInterfaces_); - } else { - return supportedInterfacesBuilder_.getMessageList(); - } - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public int getSupportedInterfacesCount() { - if (supportedInterfacesBuilder_ == null) { - return supportedInterfaces_.size(); - } else { - return supportedInterfacesBuilder_.getCount(); - } - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public io.a2a.grpc.AgentInterface getSupportedInterfaces(int index) { - if (supportedInterfacesBuilder_ == null) { - return supportedInterfaces_.get(index); - } else { - return supportedInterfacesBuilder_.getMessage(index); - } - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder setSupportedInterfaces( - int index, io.a2a.grpc.AgentInterface value) { - if (supportedInterfacesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.set(index, value); - onChanged(); - } else { - supportedInterfacesBuilder_.setMessage(index, value); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder setSupportedInterfaces( - int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { - if (supportedInterfacesBuilder_ == null) { - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.set(index, builderForValue.build()); - onChanged(); - } else { - supportedInterfacesBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder addSupportedInterfaces(io.a2a.grpc.AgentInterface value) { - if (supportedInterfacesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.add(value); - onChanged(); - } else { - supportedInterfacesBuilder_.addMessage(value); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder addSupportedInterfaces( - int index, io.a2a.grpc.AgentInterface value) { - if (supportedInterfacesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.add(index, value); - onChanged(); - } else { - supportedInterfacesBuilder_.addMessage(index, value); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder addSupportedInterfaces( - io.a2a.grpc.AgentInterface.Builder builderForValue) { - if (supportedInterfacesBuilder_ == null) { - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.add(builderForValue.build()); - onChanged(); - } else { - supportedInterfacesBuilder_.addMessage(builderForValue.build()); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder addSupportedInterfaces( - int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { - if (supportedInterfacesBuilder_ == null) { - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.add(index, builderForValue.build()); - onChanged(); - } else { - supportedInterfacesBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder addAllSupportedInterfaces( - java.lang.Iterable values) { - if (supportedInterfacesBuilder_ == null) { - ensureSupportedInterfacesIsMutable(); - com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, supportedInterfaces_); - onChanged(); - } else { - supportedInterfacesBuilder_.addAllMessages(values); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder clearSupportedInterfaces() { - if (supportedInterfacesBuilder_ == null) { - supportedInterfaces_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - supportedInterfacesBuilder_.clear(); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public Builder removeSupportedInterfaces(int index) { - if (supportedInterfacesBuilder_ == null) { - ensureSupportedInterfacesIsMutable(); - supportedInterfaces_.remove(index); - onChanged(); - } else { - supportedInterfacesBuilder_.remove(index); - } - return this; - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public io.a2a.grpc.AgentInterface.Builder getSupportedInterfacesBuilder( - int index) { - return internalGetSupportedInterfacesFieldBuilder().getBuilder(index); - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( - int index) { - if (supportedInterfacesBuilder_ == null) { - return supportedInterfaces_.get(index); } else { - return supportedInterfacesBuilder_.getMessageOrBuilder(index); - } - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public java.util.List - getSupportedInterfacesOrBuilderList() { - if (supportedInterfacesBuilder_ != null) { - return supportedInterfacesBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(supportedInterfaces_); - } - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder() { - return internalGetSupportedInterfacesFieldBuilder().addBuilder( - io.a2a.grpc.AgentInterface.getDefaultInstance()); - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( - int index) { - return internalGetSupportedInterfacesFieldBuilder().addBuilder( - index, io.a2a.grpc.AgentInterface.getDefaultInstance()); - } - /** - *
    -     * Ordered list of supported interfaces. First entry is preferred.
    -     * 
    - * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; - */ - public java.util.List - getSupportedInterfacesBuilderList() { - return internalGetSupportedInterfacesFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> - internalGetSupportedInterfacesFieldBuilder() { - if (supportedInterfacesBuilder_ == null) { - supportedInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder>( - supportedInterfaces_, - ((bitField0_ & 0x00000008) != 0), - getParentForChildren(), - isClean()); - supportedInterfaces_ = null; - } - return supportedInterfacesBuilder_; - } - - private java.lang.Object url_ = ""; - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return Whether the url field is set. - */ - @java.lang.Deprecated public boolean hasUrl() { - return ((bitField0_ & 0x00000010) != 0); - } - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return The url. - */ - @java.lang.Deprecated public java.lang.String getUrl() { - java.lang.Object ref = url_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - url_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return The bytes for url. - */ - @java.lang.Deprecated public com.google.protobuf.ByteString - getUrlBytes() { - java.lang.Object ref = url_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - url_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @param value The url to set. - * @return This builder for chaining. - */ - @java.lang.Deprecated public Builder setUrl( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - url_ = value; - bitField0_ |= 0x00000010; - onChanged(); - return this; - } - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return This builder for chaining. - */ - @java.lang.Deprecated public Builder clearUrl() { - url_ = getDefaultInstance().getUrl(); - bitField0_ = (bitField0_ & ~0x00000010); - onChanged(); - return this; - } - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @param value The bytes for url to set. - * @return This builder for chaining. - */ - @java.lang.Deprecated public Builder setUrlBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - url_ = value; - bitField0_ |= 0x00000010; - onChanged(); - return this; - } - - private java.lang.Object preferredTransport_ = ""; - /** - *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    -     * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return Whether the preferredTransport field is set. - */ - @java.lang.Deprecated public boolean hasPreferredTransport() { - return ((bitField0_ & 0x00000020) != 0); + name_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; } + + private java.lang.Object description_ = ""; /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * A human-readable description of the agent, assisting users and other agents
    +     * in understanding its purpose.
    +     * Example: "Agent that helps users with recipes and cooking."
          * 
    * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return The preferredTransport. + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The description. */ - @java.lang.Deprecated public java.lang.String getPreferredTransport() { - java.lang.Object ref = preferredTransport_; + public java.lang.String getDescription() { + java.lang.Object ref = description_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - preferredTransport_ = s; + description_ = s; return s; } else { return (java.lang.String) ref; @@ -3045,22 +2201,22 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * A human-readable description of the agent, assisting users and other agents
    +     * in understanding its purpose.
    +     * Example: "Agent that helps users with recipes and cooking."
          * 
    * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return The bytes for preferredTransport. + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for description. */ - @java.lang.Deprecated public com.google.protobuf.ByteString - getPreferredTransportBytes() { - java.lang.Object ref = preferredTransport_; + public com.google.protobuf.ByteString + getDescriptionBytes() { + java.lang.Object ref = description_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - preferredTransport_ = b; + description_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -3068,370 +2224,370 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * A human-readable description of the agent, assisting users and other agents
    +     * in understanding its purpose.
    +     * Example: "Agent that helps users with recipes and cooking."
          * 
    * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @param value The preferredTransport to set. + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; + * @param value The description to set. * @return This builder for chaining. */ - @java.lang.Deprecated public Builder setPreferredTransport( + public Builder setDescription( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - preferredTransport_ = value; - bitField0_ |= 0x00000020; + description_ = value; + bitField0_ |= 0x00000004; onChanged(); return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * A human-readable description of the agent, assisting users and other agents
    +     * in understanding its purpose.
    +     * Example: "Agent that helps users with recipes and cooking."
          * 
    * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - @java.lang.Deprecated public Builder clearPreferredTransport() { - preferredTransport_ = getDefaultInstance().getPreferredTransport(); - bitField0_ = (bitField0_ & ~0x00000020); + public Builder clearDescription() { + description_ = getDefaultInstance().getDescription(); + bitField0_ = (bitField0_ & ~0x00000004); onChanged(); return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * A human-readable description of the agent, assisting users and other agents
    +     * in understanding its purpose.
    +     * Example: "Agent that helps users with recipes and cooking."
          * 
    * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @param value The bytes for preferredTransport to set. + * string description = 2 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for description to set. * @return This builder for chaining. */ - @java.lang.Deprecated public Builder setPreferredTransportBytes( + public Builder setDescriptionBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - preferredTransport_ = value; - bitField0_ |= 0x00000020; + description_ = value; + bitField0_ |= 0x00000004; onChanged(); return this; } - private java.util.List additionalInterfaces_ = + private java.util.List supportedInterfaces_ = java.util.Collections.emptyList(); - private void ensureAdditionalInterfacesIsMutable() { - if (!((bitField0_ & 0x00000040) != 0)) { - additionalInterfaces_ = new java.util.ArrayList(additionalInterfaces_); - bitField0_ |= 0x00000040; + private void ensureSupportedInterfacesIsMutable() { + if (!((bitField0_ & 0x00000008) != 0)) { + supportedInterfaces_ = new java.util.ArrayList(supportedInterfaces_); + bitField0_ |= 0x00000008; } } private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> additionalInterfacesBuilder_; + io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> supportedInterfacesBuilder_; /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public java.util.List getAdditionalInterfacesList() { - if (additionalInterfacesBuilder_ == null) { - return java.util.Collections.unmodifiableList(additionalInterfaces_); + public java.util.List getSupportedInterfacesList() { + if (supportedInterfacesBuilder_ == null) { + return java.util.Collections.unmodifiableList(supportedInterfaces_); } else { - return additionalInterfacesBuilder_.getMessageList(); + return supportedInterfacesBuilder_.getMessageList(); } } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public int getAdditionalInterfacesCount() { - if (additionalInterfacesBuilder_ == null) { - return additionalInterfaces_.size(); + public int getSupportedInterfacesCount() { + if (supportedInterfacesBuilder_ == null) { + return supportedInterfaces_.size(); } else { - return additionalInterfacesBuilder_.getCount(); + return supportedInterfacesBuilder_.getCount(); } } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index) { - if (additionalInterfacesBuilder_ == null) { - return additionalInterfaces_.get(index); + public io.a2a.grpc.AgentInterface getSupportedInterfaces(int index) { + if (supportedInterfacesBuilder_ == null) { + return supportedInterfaces_.get(index); } else { - return additionalInterfacesBuilder_.getMessage(index); + return supportedInterfacesBuilder_.getMessage(index); } } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder setAdditionalInterfaces( + public Builder setSupportedInterfaces( int index, io.a2a.grpc.AgentInterface value) { - if (additionalInterfacesBuilder_ == null) { + if (supportedInterfacesBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.set(index, value); + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.set(index, value); onChanged(); } else { - additionalInterfacesBuilder_.setMessage(index, value); + supportedInterfacesBuilder_.setMessage(index, value); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder setAdditionalInterfaces( + public Builder setSupportedInterfaces( int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { - if (additionalInterfacesBuilder_ == null) { - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.set(index, builderForValue.build()); + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.set(index, builderForValue.build()); onChanged(); } else { - additionalInterfacesBuilder_.setMessage(index, builderForValue.build()); + supportedInterfacesBuilder_.setMessage(index, builderForValue.build()); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder addAdditionalInterfaces(io.a2a.grpc.AgentInterface value) { - if (additionalInterfacesBuilder_ == null) { + public Builder addSupportedInterfaces(io.a2a.grpc.AgentInterface value) { + if (supportedInterfacesBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.add(value); + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(value); onChanged(); } else { - additionalInterfacesBuilder_.addMessage(value); + supportedInterfacesBuilder_.addMessage(value); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder addAdditionalInterfaces( + public Builder addSupportedInterfaces( int index, io.a2a.grpc.AgentInterface value) { - if (additionalInterfacesBuilder_ == null) { + if (supportedInterfacesBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.add(index, value); + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(index, value); onChanged(); } else { - additionalInterfacesBuilder_.addMessage(index, value); + supportedInterfacesBuilder_.addMessage(index, value); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder addAdditionalInterfaces( + public Builder addSupportedInterfaces( io.a2a.grpc.AgentInterface.Builder builderForValue) { - if (additionalInterfacesBuilder_ == null) { - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.add(builderForValue.build()); + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(builderForValue.build()); onChanged(); } else { - additionalInterfacesBuilder_.addMessage(builderForValue.build()); + supportedInterfacesBuilder_.addMessage(builderForValue.build()); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder addAdditionalInterfaces( + public Builder addSupportedInterfaces( int index, io.a2a.grpc.AgentInterface.Builder builderForValue) { - if (additionalInterfacesBuilder_ == null) { - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.add(index, builderForValue.build()); + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.add(index, builderForValue.build()); onChanged(); } else { - additionalInterfacesBuilder_.addMessage(index, builderForValue.build()); + supportedInterfacesBuilder_.addMessage(index, builderForValue.build()); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder addAllAdditionalInterfaces( + public Builder addAllSupportedInterfaces( java.lang.Iterable values) { - if (additionalInterfacesBuilder_ == null) { - ensureAdditionalInterfacesIsMutable(); + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, additionalInterfaces_); + values, supportedInterfaces_); onChanged(); } else { - additionalInterfacesBuilder_.addAllMessages(values); + supportedInterfacesBuilder_.addAllMessages(values); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder clearAdditionalInterfaces() { - if (additionalInterfacesBuilder_ == null) { - additionalInterfaces_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000040); + public Builder clearSupportedInterfaces() { + if (supportedInterfacesBuilder_ == null) { + supportedInterfaces_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); onChanged(); } else { - additionalInterfacesBuilder_.clear(); + supportedInterfacesBuilder_.clear(); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public Builder removeAdditionalInterfaces(int index) { - if (additionalInterfacesBuilder_ == null) { - ensureAdditionalInterfacesIsMutable(); - additionalInterfaces_.remove(index); + public Builder removeSupportedInterfaces(int index) { + if (supportedInterfacesBuilder_ == null) { + ensureSupportedInterfacesIsMutable(); + supportedInterfaces_.remove(index); onChanged(); } else { - additionalInterfacesBuilder_.remove(index); + supportedInterfacesBuilder_.remove(index); } return this; } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public io.a2a.grpc.AgentInterface.Builder getAdditionalInterfacesBuilder( + public io.a2a.grpc.AgentInterface.Builder getSupportedInterfacesBuilder( int index) { - return internalGetAdditionalInterfacesFieldBuilder().getBuilder(index); + return internalGetSupportedInterfacesFieldBuilder().getBuilder(index); } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( + public io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( int index) { - if (additionalInterfacesBuilder_ == null) { - return additionalInterfaces_.get(index); } else { - return additionalInterfacesBuilder_.getMessageOrBuilder(index); + if (supportedInterfacesBuilder_ == null) { + return supportedInterfaces_.get(index); } else { + return supportedInterfacesBuilder_.getMessageOrBuilder(index); } } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public java.util.List - getAdditionalInterfacesOrBuilderList() { - if (additionalInterfacesBuilder_ != null) { - return additionalInterfacesBuilder_.getMessageOrBuilderList(); + public java.util.List + getSupportedInterfacesOrBuilderList() { + if (supportedInterfacesBuilder_ != null) { + return supportedInterfacesBuilder_.getMessageOrBuilderList(); } else { - return java.util.Collections.unmodifiableList(additionalInterfaces_); + return java.util.Collections.unmodifiableList(supportedInterfaces_); } } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder() { - return internalGetAdditionalInterfacesFieldBuilder().addBuilder( + public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder() { + return internalGetSupportedInterfacesFieldBuilder().addBuilder( io.a2a.grpc.AgentInterface.getDefaultInstance()); } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public io.a2a.grpc.AgentInterface.Builder addAdditionalInterfacesBuilder( + public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder( int index) { - return internalGetAdditionalInterfacesFieldBuilder().addBuilder( + return internalGetSupportedInterfacesFieldBuilder().addBuilder( index, io.a2a.grpc.AgentInterface.getDefaultInstance()); } /** *
    -     * DEPRECATED: Use 'supported_interfaces' instead.
    +     * Ordered list of supported interfaces. First entry is preferred.
          * 
    * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ - @java.lang.Deprecated public java.util.List - getAdditionalInterfacesBuilderList() { - return internalGetAdditionalInterfacesFieldBuilder().getBuilderList(); + public java.util.List + getSupportedInterfacesBuilderList() { + return internalGetSupportedInterfacesFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder> - internalGetAdditionalInterfacesFieldBuilder() { - if (additionalInterfacesBuilder_ == null) { - additionalInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + internalGetSupportedInterfacesFieldBuilder() { + if (supportedInterfacesBuilder_ == null) { + supportedInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder>( - additionalInterfaces_, - ((bitField0_ & 0x00000040) != 0), + supportedInterfaces_, + ((bitField0_ & 0x00000008) != 0), getParentForChildren(), isClean()); - additionalInterfaces_ = null; + supportedInterfaces_ = null; } - return additionalInterfacesBuilder_; + return supportedInterfacesBuilder_; } private io.a2a.grpc.AgentProvider provider_; @@ -3446,7 +2602,7 @@ private void ensureAdditionalInterfacesIsMutable() { * @return Whether the provider field is set. */ public boolean hasProvider() { - return ((bitField0_ & 0x00000080) != 0); + return ((bitField0_ & 0x00000010) != 0); } /** *
    @@ -3479,7 +2635,7 @@ public Builder setProvider(io.a2a.grpc.AgentProvider value) {
           } else {
             providerBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000080;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return this;
         }
    @@ -3497,7 +2653,7 @@ public Builder setProvider(
           } else {
             providerBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000080;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return this;
         }
    @@ -3510,7 +2666,7 @@ public Builder setProvider(
          */
         public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
           if (providerBuilder_ == null) {
    -        if (((bitField0_ & 0x00000080) != 0) &&
    +        if (((bitField0_ & 0x00000010) != 0) &&
               provider_ != null &&
               provider_ != io.a2a.grpc.AgentProvider.getDefaultInstance()) {
               getProviderBuilder().mergeFrom(value);
    @@ -3521,7 +2677,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
             providerBuilder_.mergeFrom(value);
           }
           if (provider_ != null) {
    -        bitField0_ |= 0x00000080;
    +        bitField0_ |= 0x00000010;
             onChanged();
           }
           return this;
    @@ -3534,7 +2690,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
          * .a2a.v1.AgentProvider provider = 4;
          */
         public Builder clearProvider() {
    -      bitField0_ = (bitField0_ & ~0x00000080);
    +      bitField0_ = (bitField0_ & ~0x00000010);
           provider_ = null;
           if (providerBuilder_ != null) {
             providerBuilder_.dispose();
    @@ -3551,7 +2707,7 @@ public Builder clearProvider() {
          * .a2a.v1.AgentProvider provider = 4;
          */
         public io.a2a.grpc.AgentProvider.Builder getProviderBuilder() {
    -      bitField0_ |= 0x00000080;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return internalGetProviderFieldBuilder().getBuilder();
         }
    @@ -3649,7 +2805,7 @@ public Builder setVersion(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           version_ = value;
    -      bitField0_ |= 0x00000100;
    +      bitField0_ |= 0x00000020;
           onChanged();
           return this;
         }
    @@ -3664,7 +2820,7 @@ public Builder setVersion(
          */
         public Builder clearVersion() {
           version_ = getDefaultInstance().getVersion();
    -      bitField0_ = (bitField0_ & ~0x00000100);
    +      bitField0_ = (bitField0_ & ~0x00000020);
           onChanged();
           return this;
         }
    @@ -3683,7 +2839,7 @@ public Builder setVersionBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           version_ = value;
    -      bitField0_ |= 0x00000100;
    +      bitField0_ |= 0x00000020;
           onChanged();
           return this;
         }
    @@ -3698,7 +2854,7 @@ public Builder setVersionBytes(
          * @return Whether the documentationUrl field is set.
          */
         public boolean hasDocumentationUrl() {
    -      return ((bitField0_ & 0x00000200) != 0);
    +      return ((bitField0_ & 0x00000040) != 0);
         }
         /**
          * 
    @@ -3754,7 +2910,7 @@ public Builder setDocumentationUrl(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           documentationUrl_ = value;
    -      bitField0_ |= 0x00000200;
    +      bitField0_ |= 0x00000040;
           onChanged();
           return this;
         }
    @@ -3768,7 +2924,7 @@ public Builder setDocumentationUrl(
          */
         public Builder clearDocumentationUrl() {
           documentationUrl_ = getDefaultInstance().getDocumentationUrl();
    -      bitField0_ = (bitField0_ & ~0x00000200);
    +      bitField0_ = (bitField0_ & ~0x00000040);
           onChanged();
           return this;
         }
    @@ -3786,7 +2942,7 @@ public Builder setDocumentationUrlBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           documentationUrl_ = value;
    -      bitField0_ |= 0x00000200;
    +      bitField0_ |= 0x00000040;
           onChanged();
           return this;
         }
    @@ -3803,7 +2959,7 @@ public Builder setDocumentationUrlBytes(
          * @return Whether the capabilities field is set.
          */
         public boolean hasCapabilities() {
    -      return ((bitField0_ & 0x00000400) != 0);
    +      return ((bitField0_ & 0x00000080) != 0);
         }
         /**
          * 
    @@ -3836,7 +2992,7 @@ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) {
           } else {
             capabilitiesBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000080;
           onChanged();
           return this;
         }
    @@ -3854,7 +3010,7 @@ public Builder setCapabilities(
           } else {
             capabilitiesBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000080;
           onChanged();
           return this;
         }
    @@ -3867,7 +3023,7 @@ public Builder setCapabilities(
          */
         public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) {
           if (capabilitiesBuilder_ == null) {
    -        if (((bitField0_ & 0x00000400) != 0) &&
    +        if (((bitField0_ & 0x00000080) != 0) &&
               capabilities_ != null &&
               capabilities_ != io.a2a.grpc.AgentCapabilities.getDefaultInstance()) {
               getCapabilitiesBuilder().mergeFrom(value);
    @@ -3878,7 +3034,7 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) {
             capabilitiesBuilder_.mergeFrom(value);
           }
           if (capabilities_ != null) {
    -        bitField0_ |= 0x00000400;
    +        bitField0_ |= 0x00000080;
             onChanged();
           }
           return this;
    @@ -3891,7 +3047,7 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) {
          * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED];
          */
         public Builder clearCapabilities() {
    -      bitField0_ = (bitField0_ & ~0x00000400);
    +      bitField0_ = (bitField0_ & ~0x00000080);
           capabilities_ = null;
           if (capabilitiesBuilder_ != null) {
             capabilitiesBuilder_.dispose();
    @@ -3908,7 +3064,7 @@ public Builder clearCapabilities() {
          * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED];
          */
         public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() {
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000080;
           onChanged();
           return internalGetCapabilitiesFieldBuilder().getBuilder();
         }
    @@ -3976,7 +3132,7 @@ public com.google.protobuf.MapEntry(securitySchemesConverter);
           }
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000100;
           onChanged();
           return securitySchemes_;
         }
    @@ -4050,7 +3206,7 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow(
           return securitySchemesConverter.build(map.get(key));
         }
         public Builder clearSecuritySchemes() {
    -      bitField0_ = (bitField0_ & ~0x00000800);
    +      bitField0_ = (bitField0_ & ~0x00000100);
           internalGetMutableSecuritySchemes().clear();
           return this;
         }
    @@ -4074,7 +3230,7 @@ public Builder removeSecuritySchemes(
         @java.lang.Deprecated
         public java.util.Map
             getMutableSecuritySchemes() {
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000100;
           return internalGetMutableSecuritySchemes().ensureMessageMap();
         }
         /**
    @@ -4091,7 +3247,7 @@ public Builder putSecuritySchemes(
           if (value == null) { throw new NullPointerException("map value"); }
           internalGetMutableSecuritySchemes().ensureBuilderMap()
               .put(key, value);
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000100;
           return this;
         }
         /**
    @@ -4110,7 +3266,7 @@ public Builder putAllSecuritySchemes(
           }
           internalGetMutableSecuritySchemes().ensureBuilderMap()
               .putAll(values);
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000100;
           return this;
         }
         /**
    @@ -4138,9 +3294,9 @@ public io.a2a.grpc.SecurityScheme.Builder putSecuritySchemesBuilderIfAbsent(
         private java.util.List security_ =
           java.util.Collections.emptyList();
         private void ensureSecurityIsMutable() {
    -      if (!((bitField0_ & 0x00001000) != 0)) {
    +      if (!((bitField0_ & 0x00000200) != 0)) {
             security_ = new java.util.ArrayList(security_);
    -        bitField0_ |= 0x00001000;
    +        bitField0_ |= 0x00000200;
            }
         }
     
    @@ -4345,7 +3501,7 @@ public Builder addAllSecurity(
         public Builder clearSecurity() {
           if (securityBuilder_ == null) {
             security_ = java.util.Collections.emptyList();
    -        bitField0_ = (bitField0_ & ~0x00001000);
    +        bitField0_ = (bitField0_ & ~0x00000200);
             onChanged();
           } else {
             securityBuilder_.clear();
    @@ -4457,7 +3613,7 @@ public io.a2a.grpc.Security.Builder addSecurityBuilder(
             securityBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
                 io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder>(
                     security_,
    -                ((bitField0_ & 0x00001000) != 0),
    +                ((bitField0_ & 0x00000200) != 0),
                     getParentForChildren(),
                     isClean());
             security_ = null;
    @@ -4471,7 +3627,7 @@ private void ensureDefaultInputModesIsMutable() {
           if (!defaultInputModes_.isModifiable()) {
             defaultInputModes_ = new com.google.protobuf.LazyStringArrayList(defaultInputModes_);
           }
    -      bitField0_ |= 0x00002000;
    +      bitField0_ |= 0x00000400;
         }
         /**
          * 
    @@ -4547,7 +3703,7 @@ public Builder setDefaultInputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultInputModesIsMutable();
           defaultInputModes_.set(index, value);
    -      bitField0_ |= 0x00002000;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -4567,7 +3723,7 @@ public Builder addDefaultInputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultInputModesIsMutable();
           defaultInputModes_.add(value);
    -      bitField0_ |= 0x00002000;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -4587,7 +3743,7 @@ public Builder addAllDefaultInputModes(
           ensureDefaultInputModesIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
               values, defaultInputModes_);
    -      bitField0_ |= 0x00002000;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -4604,7 +3760,7 @@ public Builder addAllDefaultInputModes(
         public Builder clearDefaultInputModes() {
           defaultInputModes_ =
             com.google.protobuf.LazyStringArrayList.emptyList();
    -      bitField0_ = (bitField0_ & ~0x00002000);;
    +      bitField0_ = (bitField0_ & ~0x00000400);;
           onChanged();
           return this;
         }
    @@ -4625,7 +3781,7 @@ public Builder addDefaultInputModesBytes(
           checkByteStringIsUtf8(value);
           ensureDefaultInputModesIsMutable();
           defaultInputModes_.add(value);
    -      bitField0_ |= 0x00002000;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -4636,7 +3792,7 @@ private void ensureDefaultOutputModesIsMutable() {
           if (!defaultOutputModes_.isModifiable()) {
             defaultOutputModes_ = new com.google.protobuf.LazyStringArrayList(defaultOutputModes_);
           }
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00000800;
         }
         /**
          * 
    @@ -4702,7 +3858,7 @@ public Builder setDefaultOutputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultOutputModesIsMutable();
           defaultOutputModes_.set(index, value);
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00000800;
           onChanged();
           return this;
         }
    @@ -4720,7 +3876,7 @@ public Builder addDefaultOutputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultOutputModesIsMutable();
           defaultOutputModes_.add(value);
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00000800;
           onChanged();
           return this;
         }
    @@ -4738,7 +3894,7 @@ public Builder addAllDefaultOutputModes(
           ensureDefaultOutputModesIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
               values, defaultOutputModes_);
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00000800;
           onChanged();
           return this;
         }
    @@ -4753,7 +3909,7 @@ public Builder addAllDefaultOutputModes(
         public Builder clearDefaultOutputModes() {
           defaultOutputModes_ =
             com.google.protobuf.LazyStringArrayList.emptyList();
    -      bitField0_ = (bitField0_ & ~0x00004000);;
    +      bitField0_ = (bitField0_ & ~0x00000800);;
           onChanged();
           return this;
         }
    @@ -4772,7 +3928,7 @@ public Builder addDefaultOutputModesBytes(
           checkByteStringIsUtf8(value);
           ensureDefaultOutputModesIsMutable();
           defaultOutputModes_.add(value);
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00000800;
           onChanged();
           return this;
         }
    @@ -4780,9 +3936,9 @@ public Builder addDefaultOutputModesBytes(
         private java.util.List skills_ =
           java.util.Collections.emptyList();
         private void ensureSkillsIsMutable() {
    -      if (!((bitField0_ & 0x00008000) != 0)) {
    +      if (!((bitField0_ & 0x00001000) != 0)) {
             skills_ = new java.util.ArrayList(skills_);
    -        bitField0_ |= 0x00008000;
    +        bitField0_ |= 0x00001000;
            }
         }
     
    @@ -4998,7 +4154,7 @@ public Builder addAllSkills(
         public Builder clearSkills() {
           if (skillsBuilder_ == null) {
             skills_ = java.util.Collections.emptyList();
    -        bitField0_ = (bitField0_ & ~0x00008000);
    +        bitField0_ = (bitField0_ & ~0x00001000);
             onChanged();
           } else {
             skillsBuilder_.clear();
    @@ -5117,7 +4273,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder(
             skillsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
                 io.a2a.grpc.AgentSkill, io.a2a.grpc.AgentSkill.Builder, io.a2a.grpc.AgentSkillOrBuilder>(
                     skills_,
    -                ((bitField0_ & 0x00008000) != 0),
    +                ((bitField0_ & 0x00001000) != 0),
                     getParentForChildren(),
                     isClean());
             skills_ = null;
    @@ -5125,68 +4281,12 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder(
           return skillsBuilder_;
         }
     
    -    private boolean supportsExtendedAgentCard_ ;
    -    /**
    -     * 
    -     * Whether the agent supports providing an extended agent card when authenticated.
    -     * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return Whether the supportsExtendedAgentCard field is set. - */ - @java.lang.Override - public boolean hasSupportsExtendedAgentCard() { - return ((bitField0_ & 0x00010000) != 0); - } - /** - *
    -     * Whether the agent supports providing an extended agent card when authenticated.
    -     * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return The supportsExtendedAgentCard. - */ - @java.lang.Override - public boolean getSupportsExtendedAgentCard() { - return supportsExtendedAgentCard_; - } - /** - *
    -     * Whether the agent supports providing an extended agent card when authenticated.
    -     * 
    - * - * optional bool supports_extended_agent_card = 13; - * @param value The supportsExtendedAgentCard to set. - * @return This builder for chaining. - */ - public Builder setSupportsExtendedAgentCard(boolean value) { - - supportsExtendedAgentCard_ = value; - bitField0_ |= 0x00010000; - onChanged(); - return this; - } - /** - *
    -     * Whether the agent supports providing an extended agent card when authenticated.
    -     * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return This builder for chaining. - */ - public Builder clearSupportsExtendedAgentCard() { - bitField0_ = (bitField0_ & ~0x00010000); - supportsExtendedAgentCard_ = false; - onChanged(); - return this; - } - private java.util.List signatures_ = java.util.Collections.emptyList(); private void ensureSignaturesIsMutable() { - if (!((bitField0_ & 0x00020000) != 0)) { + if (!((bitField0_ & 0x00002000) != 0)) { signatures_ = new java.util.ArrayList(signatures_); - bitField0_ |= 0x00020000; + bitField0_ |= 0x00002000; } } @@ -5380,7 +4480,7 @@ public Builder addAllSignatures( public Builder clearSignatures() { if (signaturesBuilder_ == null) { signatures_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00020000); + bitField0_ = (bitField0_ & ~0x00002000); onChanged(); } else { signaturesBuilder_.clear(); @@ -5485,7 +4585,7 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder( signaturesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< io.a2a.grpc.AgentCardSignature, io.a2a.grpc.AgentCardSignature.Builder, io.a2a.grpc.AgentCardSignatureOrBuilder>( signatures_, - ((bitField0_ & 0x00020000) != 0), + ((bitField0_ & 0x00002000) != 0), getParentForChildren(), isClean()); signatures_ = null; @@ -5503,7 +4603,7 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder( * @return Whether the iconUrl field is set. */ public boolean hasIconUrl() { - return ((bitField0_ & 0x00040000) != 0); + return ((bitField0_ & 0x00004000) != 0); } /** *
    @@ -5559,7 +4659,7 @@ public Builder setIconUrl(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           iconUrl_ = value;
    -      bitField0_ |= 0x00040000;
    +      bitField0_ |= 0x00004000;
           onChanged();
           return this;
         }
    @@ -5573,7 +4673,7 @@ public Builder setIconUrl(
          */
         public Builder clearIconUrl() {
           iconUrl_ = getDefaultInstance().getIconUrl();
    -      bitField0_ = (bitField0_ & ~0x00040000);
    +      bitField0_ = (bitField0_ & ~0x00004000);
           onChanged();
           return this;
         }
    @@ -5591,7 +4691,7 @@ public Builder setIconUrlBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           iconUrl_ = value;
    -      bitField0_ |= 0x00040000;
    +      bitField0_ |= 0x00004000;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java
    index 91ad7f3b4..8c31e3053 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java
    @@ -12,35 +12,56 @@ public interface AgentCardOrBuilder extends
     
       /**
        * 
    -   * The version of the A2A protocol this agent supports.
    -   * Default: "1.0"
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
        * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return Whether the protocolVersion field is set. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return A list containing the protocolVersions. */ - boolean hasProtocolVersion(); + java.util.List + getProtocolVersionsList(); + /** + *
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
    +   * 
    + * + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @return The count of protocolVersions. + */ + int getProtocolVersionsCount(); /** *
    -   * The version of the A2A protocol this agent supports.
    -   * Default: "1.0"
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
        * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The protocolVersion. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index of the element to return. + * @return The protocolVersions at the given index. */ - java.lang.String getProtocolVersion(); + java.lang.String getProtocolVersions(int index); /** *
    -   * The version of the A2A protocol this agent supports.
    -   * Default: "1.0"
    +   * The versions of the A2A protocol this agent supports.
    +   * For stable versions (1.x+), list only the latest supported minor version per major version.
    +   * For legacy experimental versions (0.x), explicitly list each supported version.
    +   * Default: ["1.0"]
        * 
    * - * optional string protocol_version = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for protocolVersion. + * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; + * @param index The index of the value to return. + * @return The bytes of the protocolVersions at the given index. */ com.google.protobuf.ByteString - getProtocolVersionBytes(); + getProtocolVersionsBytes(int index); /** *
    @@ -93,7 +114,7 @@ public interface AgentCardOrBuilder extends
        * Ordered list of supported interfaces. First entry is preferred.
        * 
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getSupportedInterfacesList(); @@ -102,7 +123,7 @@ public interface AgentCardOrBuilder extends * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.AgentInterface getSupportedInterfaces(int index); /** @@ -110,7 +131,7 @@ public interface AgentCardOrBuilder extends * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ int getSupportedInterfacesCount(); /** @@ -118,7 +139,7 @@ public interface AgentCardOrBuilder extends * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ java.util.List getSupportedInterfacesOrBuilderList(); @@ -127,125 +148,11 @@ public interface AgentCardOrBuilder extends * Ordered list of supported interfaces. First entry is preferred. *
    * - * repeated .a2a.v1.AgentInterface supported_interfaces = 19; + * repeated .a2a.v1.AgentInterface supported_interfaces = 19 [(.google.api.field_behavior) = REQUIRED]; */ io.a2a.grpc.AgentInterfaceOrBuilder getSupportedInterfacesOrBuilder( int index); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return Whether the url field is set. - */ - @java.lang.Deprecated boolean hasUrl(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return The url. - */ - @java.lang.Deprecated java.lang.String getUrl(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string url = 3 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.url is deprecated. - * See a2a.proto;l=426 - * @return The bytes for url. - */ - @java.lang.Deprecated com.google.protobuf.ByteString - getUrlBytes(); - - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return Whether the preferredTransport field is set. - */ - @java.lang.Deprecated boolean hasPreferredTransport(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return The preferredTransport. - */ - @java.lang.Deprecated java.lang.String getPreferredTransport(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * optional string preferred_transport = 14 [deprecated = true]; - * @deprecated a2a.v1.AgentCard.preferred_transport is deprecated. - * See a2a.proto;l=428 - * @return The bytes for preferredTransport. - */ - @java.lang.Deprecated com.google.protobuf.ByteString - getPreferredTransportBytes(); - - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Deprecated java.util.List - getAdditionalInterfacesList(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Deprecated io.a2a.grpc.AgentInterface getAdditionalInterfaces(int index); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Deprecated int getAdditionalInterfacesCount(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Deprecated java.util.List - getAdditionalInterfacesOrBuilderList(); - /** - *
    -   * DEPRECATED: Use 'supported_interfaces' instead.
    -   * 
    - * - * repeated .a2a.v1.AgentInterface additional_interfaces = 15 [deprecated = true]; - */ - @java.lang.Deprecated io.a2a.grpc.AgentInterfaceOrBuilder getAdditionalInterfacesOrBuilder( - int index); - /** *
        * The service provider of the agent.
    @@ -598,25 +505,6 @@ io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder(
       io.a2a.grpc.AgentSkillOrBuilder getSkillsOrBuilder(
           int index);
     
    -  /**
    -   * 
    -   * Whether the agent supports providing an extended agent card when authenticated.
    -   * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return Whether the supportsExtendedAgentCard field is set. - */ - boolean hasSupportsExtendedAgentCard(); - /** - *
    -   * Whether the agent supports providing an extended agent card when authenticated.
    -   * 
    - * - * optional bool supports_extended_agent_card = 13; - * @return The supportsExtendedAgentCard. - */ - boolean getSupportsExtendedAgentCard(); - /** *
        * JSON Web Signatures computed for this AgentCard.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java
    index 9bbc4a253..06b7b8f41 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlow.java
    @@ -299,6 +299,22 @@ public java.lang.String getScopesOrThrow(
         return map.get(key);
       }
     
    +  public static final int PKCE_REQUIRED_FIELD_NUMBER = 5;
    +  private boolean pkceRequired_ = false;
    +  /**
    +   * 
    +   * Indicates if PKCE (RFC 7636) is required for this flow.
    +   * PKCE should always be used for public clients and is recommended for all clients.
    +   * 
    + * + * bool pkce_required = 5; + * @return The pkceRequired. + */ + @java.lang.Override + public boolean getPkceRequired() { + return pkceRequired_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -328,6 +344,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) internalGetScopes(), ScopesDefaultEntryHolder.defaultEntry, 4); + if (pkceRequired_ != false) { + output.writeBool(5, pkceRequired_); + } getUnknownFields().writeTo(output); } @@ -356,6 +375,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(4, scopes__); } + if (pkceRequired_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, pkceRequired_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -379,6 +402,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getRefreshUrl())) return false; if (!internalGetScopes().equals( other.internalGetScopes())) return false; + if (getPkceRequired() + != other.getPkceRequired()) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -400,6 +425,9 @@ public int hashCode() { hash = (37 * hash) + SCOPES_FIELD_NUMBER; hash = (53 * hash) + internalGetScopes().hashCode(); } + hash = (37 * hash) + PKCE_REQUIRED_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getPkceRequired()); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -562,6 +590,7 @@ public Builder clear() { tokenUrl_ = ""; refreshUrl_ = ""; internalGetMutableScopes().clear(); + pkceRequired_ = false; return this; } @@ -608,6 +637,9 @@ private void buildPartial0(io.a2a.grpc.AuthorizationCodeOAuthFlow result) { result.scopes_ = internalGetScopes(); result.scopes_.makeImmutable(); } + if (((from_bitField0_ & 0x00000010) != 0)) { + result.pkceRequired_ = pkceRequired_; + } } @java.lang.Override @@ -640,6 +672,9 @@ public Builder mergeFrom(io.a2a.grpc.AuthorizationCodeOAuthFlow other) { internalGetMutableScopes().mergeFrom( other.internalGetScopes()); bitField0_ |= 0x00000008; + if (other.getPkceRequired() != false) { + setPkceRequired(other.getPkceRequired()); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -690,6 +725,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000008; break; } // case 34 + case 40: { + pkceRequired_ = input.readBool(); + bitField0_ |= 0x00000010; + break; + } // case 40 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -1138,6 +1178,53 @@ public Builder putAllScopes( return this; } + private boolean pkceRequired_ ; + /** + *
    +     * Indicates if PKCE (RFC 7636) is required for this flow.
    +     * PKCE should always be used for public clients and is recommended for all clients.
    +     * 
    + * + * bool pkce_required = 5; + * @return The pkceRequired. + */ + @java.lang.Override + public boolean getPkceRequired() { + return pkceRequired_; + } + /** + *
    +     * Indicates if PKCE (RFC 7636) is required for this flow.
    +     * PKCE should always be used for public clients and is recommended for all clients.
    +     * 
    + * + * bool pkce_required = 5; + * @param value The pkceRequired to set. + * @return This builder for chaining. + */ + public Builder setPkceRequired(boolean value) { + + pkceRequired_ = value; + bitField0_ |= 0x00000010; + onChanged(); + return this; + } + /** + *
    +     * Indicates if PKCE (RFC 7636) is required for this flow.
    +     * PKCE should always be used for public clients and is recommended for all clients.
    +     * 
    + * + * bool pkce_required = 5; + * @return This builder for chaining. + */ + public Builder clearPkceRequired() { + bitField0_ = (bitField0_ & ~0x00000010); + pkceRequired_ = false; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AuthorizationCodeOAuthFlow) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java index b50acbd01..5dd2d393d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthorizationCodeOAuthFlowOrBuilder.java @@ -123,4 +123,15 @@ java.lang.String getScopesOrDefault( */ java.lang.String getScopesOrThrow( java.lang.String key); + + /** + *
    +   * Indicates if PKCE (RFC 7636) is required for this flow.
    +   * PKCE should always be used for public clients and is recommended for all clients.
    +   * 
    + * + * bool pkce_required = 5; + * @return The pkceRequired. + */ + boolean getPkceRequired(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/DeviceCodeOAuthFlow.java similarity index 69% rename from spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java rename to spec-grpc/src/main/java/io/a2a/grpc/DeviceCodeOAuthFlow.java index 2f21f9789..701371c2b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeviceCodeOAuthFlow.java @@ -7,17 +7,19 @@ /** *
    - * --8<-- [start:PasswordOAuthFlow]
    - * Defines configuration details for the OAuth 2.0 Resource Owner Password flow.
    + * --8<-- [start:DeviceCodeOAuthFlow]
    + * Defines configuration details for the OAuth 2.0 Device Code flow (RFC 8628).
    + * This flow is designed for input-constrained devices such as IoT devices,
    + * and CLI tools where the user authenticates on a separate device.
      * 
    * - * Protobuf type {@code a2a.v1.PasswordOAuthFlow} + * Protobuf type {@code a2a.v1.DeviceCodeOAuthFlow} */ @com.google.protobuf.Generated -public final class PasswordOAuthFlow extends +public final class DeviceCodeOAuthFlow extends com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.PasswordOAuthFlow) - PasswordOAuthFlowOrBuilder { + // @@protoc_insertion_point(message_implements:a2a.v1.DeviceCodeOAuthFlow) + DeviceCodeOAuthFlowOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( @@ -26,20 +28,21 @@ public final class PasswordOAuthFlow extends /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - "PasswordOAuthFlow"); + "DeviceCodeOAuthFlow"); } - // Use PasswordOAuthFlow.newBuilder() to construct. - private PasswordOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + // Use DeviceCodeOAuthFlow.newBuilder() to construct. + private DeviceCodeOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } - private PasswordOAuthFlow() { + private DeviceCodeOAuthFlow() { + deviceAuthorizationUrl_ = ""; tokenUrl_ = ""; refreshUrl_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor; } @SuppressWarnings({"rawtypes"}) @@ -47,7 +50,7 @@ private PasswordOAuthFlow() { protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( int number) { switch (number) { - case 3: + case 4: return internalGetScopes(); default: throw new RuntimeException( @@ -57,12 +60,59 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeviceCodeOAuthFlow_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.PasswordOAuthFlow.class, io.a2a.grpc.PasswordOAuthFlow.Builder.class); + io.a2a.grpc.DeviceCodeOAuthFlow.class, io.a2a.grpc.DeviceCodeOAuthFlow.Builder.class); } - public static final int TOKEN_URL_FIELD_NUMBER = 1; + public static final int DEVICE_AUTHORIZATION_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object deviceAuthorizationUrl_ = ""; + /** + *
    +   * The device authorization endpoint URL.
    +   * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The deviceAuthorizationUrl. + */ + @java.lang.Override + public java.lang.String getDeviceAuthorizationUrl() { + java.lang.Object ref = deviceAuthorizationUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + deviceAuthorizationUrl_ = s; + return s; + } + } + /** + *
    +   * The device authorization endpoint URL.
    +   * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for deviceAuthorizationUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getDeviceAuthorizationUrlBytes() { + java.lang.Object ref = deviceAuthorizationUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + deviceAuthorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TOKEN_URL_FIELD_NUMBER = 2; @SuppressWarnings("serial") private volatile java.lang.Object tokenUrl_ = ""; /** @@ -70,7 +120,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl * The token URL to be used for this flow. *
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ @java.lang.Override @@ -91,7 +141,7 @@ public java.lang.String getTokenUrl() { * The token URL to be used for this flow. *
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ @java.lang.Override @@ -109,7 +159,7 @@ public java.lang.String getTokenUrl() { } } - public static final int REFRESH_URL_FIELD_NUMBER = 2; + public static final int REFRESH_URL_FIELD_NUMBER = 3; @SuppressWarnings("serial") private volatile java.lang.Object refreshUrl_ = ""; /** @@ -117,7 +167,7 @@ public java.lang.String getTokenUrl() { * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @return The refreshUrl. */ @java.lang.Override @@ -138,7 +188,7 @@ public java.lang.String getRefreshUrl() { * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @return The bytes for refreshUrl. */ @java.lang.Override @@ -156,13 +206,13 @@ public java.lang.String getRefreshUrl() { } } - public static final int SCOPES_FIELD_NUMBER = 3; + public static final int SCOPES_FIELD_NUMBER = 4; private static final class ScopesDefaultEntryHolder { static final com.google.protobuf.MapEntry< java.lang.String, java.lang.String> defaultEntry = com.google.protobuf.MapEntry .newDefaultInstance( - io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, + io.a2a.grpc.A2A.internal_static_a2a_v1_DeviceCodeOAuthFlow_ScopesEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.STRING, @@ -187,7 +237,7 @@ public int getScopesCount() { * The available scopes for the OAuth2 security scheme. *
    * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -208,7 +258,7 @@ public java.util.Map getScopes() { * The available scopes for the OAuth2 security scheme. *
    * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -219,7 +269,7 @@ public java.util.Map getScopesMap() { * The available scopes for the OAuth2 security scheme. *
    * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -237,7 +287,7 @@ java.lang.String getScopesOrDefault( * The available scopes for the OAuth2 security scheme. *
    * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -265,18 +315,21 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(deviceAuthorizationUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, deviceAuthorizationUrl_); + } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, tokenUrl_); + com.google.protobuf.GeneratedMessage.writeString(output, 2, tokenUrl_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); + com.google.protobuf.GeneratedMessage.writeString(output, 3, refreshUrl_); } com.google.protobuf.GeneratedMessage .serializeStringMapTo( output, internalGetScopes(), ScopesDefaultEntryHolder.defaultEntry, - 3); + 4); getUnknownFields().writeTo(output); } @@ -286,11 +339,14 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(deviceAuthorizationUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, deviceAuthorizationUrl_); + } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, tokenUrl_); + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tokenUrl_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, refreshUrl_); } for (java.util.Map.Entry entry : internalGetScopes().getMap().entrySet()) { @@ -300,7 +356,7 @@ public int getSerializedSize() { .setValue(entry.getValue()) .build(); size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, scopes__); + .computeMessageSize(4, scopes__); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -312,11 +368,13 @@ public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } - if (!(obj instanceof io.a2a.grpc.PasswordOAuthFlow)) { + if (!(obj instanceof io.a2a.grpc.DeviceCodeOAuthFlow)) { return super.equals(obj); } - io.a2a.grpc.PasswordOAuthFlow other = (io.a2a.grpc.PasswordOAuthFlow) obj; + io.a2a.grpc.DeviceCodeOAuthFlow other = (io.a2a.grpc.DeviceCodeOAuthFlow) obj; + if (!getDeviceAuthorizationUrl() + .equals(other.getDeviceAuthorizationUrl())) return false; if (!getTokenUrl() .equals(other.getTokenUrl())) return false; if (!getRefreshUrl() @@ -334,6 +392,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + DEVICE_AUTHORIZATION_URL_FIELD_NUMBER; + hash = (53 * hash) + getDeviceAuthorizationUrl().hashCode(); hash = (37 * hash) + TOKEN_URL_FIELD_NUMBER; hash = (53 * hash) + getTokenUrl().hashCode(); hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; @@ -347,44 +407,44 @@ public int hashCode() { return hash; } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom(byte[] data) + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom(java.io.InputStream input) + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -392,26 +452,26 @@ public static io.a2a.grpc.PasswordOAuthFlow parseFrom( .parseWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.PasswordOAuthFlow parseDelimitedFrom(java.io.InputStream input) + public static io.a2a.grpc.DeviceCodeOAuthFlow parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } - public static io.a2a.grpc.PasswordOAuthFlow parseDelimitedFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + public static io.a2a.grpc.DeviceCodeOAuthFlow parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -424,7 +484,7 @@ public static io.a2a.grpc.PasswordOAuthFlow parseFrom( public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(io.a2a.grpc.PasswordOAuthFlow prototype) { + public static Builder newBuilder(io.a2a.grpc.DeviceCodeOAuthFlow prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override @@ -441,26 +501,28 @@ protected Builder newBuilderForType( } /** *
    -   * --8<-- [start:PasswordOAuthFlow]
    -   * Defines configuration details for the OAuth 2.0 Resource Owner Password flow.
    +   * --8<-- [start:DeviceCodeOAuthFlow]
    +   * Defines configuration details for the OAuth 2.0 Device Code flow (RFC 8628).
    +   * This flow is designed for input-constrained devices such as IoT devices,
    +   * and CLI tools where the user authenticates on a separate device.
        * 
    * - * Protobuf type {@code a2a.v1.PasswordOAuthFlow} + * Protobuf type {@code a2a.v1.DeviceCodeOAuthFlow} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.PasswordOAuthFlow) - io.a2a.grpc.PasswordOAuthFlowOrBuilder { + // @@protoc_insertion_point(builder_implements:a2a.v1.DeviceCodeOAuthFlow) + io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor; } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( int number) { switch (number) { - case 3: + case 4: return internalGetScopes(); default: throw new RuntimeException( @@ -471,7 +533,7 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( int number) { switch (number) { - case 3: + case 4: return internalGetMutableScopes(); default: throw new RuntimeException( @@ -481,12 +543,12 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFi @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeviceCodeOAuthFlow_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.PasswordOAuthFlow.class, io.a2a.grpc.PasswordOAuthFlow.Builder.class); + io.a2a.grpc.DeviceCodeOAuthFlow.class, io.a2a.grpc.DeviceCodeOAuthFlow.Builder.class); } - // Construct using io.a2a.grpc.PasswordOAuthFlow.newBuilder() + // Construct using io.a2a.grpc.DeviceCodeOAuthFlow.newBuilder() private Builder() { } @@ -500,6 +562,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; + deviceAuthorizationUrl_ = ""; tokenUrl_ = ""; refreshUrl_ = ""; internalGetMutableScopes().clear(); @@ -509,17 +572,17 @@ public Builder clear() { @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor; } @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlow getDefaultInstanceForType() { - return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + public io.a2a.grpc.DeviceCodeOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlow build() { - io.a2a.grpc.PasswordOAuthFlow result = buildPartial(); + public io.a2a.grpc.DeviceCodeOAuthFlow build() { + io.a2a.grpc.DeviceCodeOAuthFlow result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } @@ -527,22 +590,25 @@ public io.a2a.grpc.PasswordOAuthFlow build() { } @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlow buildPartial() { - io.a2a.grpc.PasswordOAuthFlow result = new io.a2a.grpc.PasswordOAuthFlow(this); + public io.a2a.grpc.DeviceCodeOAuthFlow buildPartial() { + io.a2a.grpc.DeviceCodeOAuthFlow result = new io.a2a.grpc.DeviceCodeOAuthFlow(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } - private void buildPartial0(io.a2a.grpc.PasswordOAuthFlow result) { + private void buildPartial0(io.a2a.grpc.DeviceCodeOAuthFlow result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - result.tokenUrl_ = tokenUrl_; + result.deviceAuthorizationUrl_ = deviceAuthorizationUrl_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.refreshUrl_ = refreshUrl_; + result.tokenUrl_ = tokenUrl_; } if (((from_bitField0_ & 0x00000004) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000008) != 0)) { result.scopes_ = internalGetScopes(); result.scopes_.makeImmutable(); } @@ -550,29 +616,34 @@ private void buildPartial0(io.a2a.grpc.PasswordOAuthFlow result) { @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.PasswordOAuthFlow) { - return mergeFrom((io.a2a.grpc.PasswordOAuthFlow)other); + if (other instanceof io.a2a.grpc.DeviceCodeOAuthFlow) { + return mergeFrom((io.a2a.grpc.DeviceCodeOAuthFlow)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(io.a2a.grpc.PasswordOAuthFlow other) { - if (other == io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance()) return this; + public Builder mergeFrom(io.a2a.grpc.DeviceCodeOAuthFlow other) { + if (other == io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance()) return this; + if (!other.getDeviceAuthorizationUrl().isEmpty()) { + deviceAuthorizationUrl_ = other.deviceAuthorizationUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } if (!other.getTokenUrl().isEmpty()) { tokenUrl_ = other.tokenUrl_; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); } if (!other.getRefreshUrl().isEmpty()) { refreshUrl_ = other.refreshUrl_; - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; onChanged(); } internalGetMutableScopes().mergeFrom( other.internalGetScopes()); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -600,24 +671,29 @@ public Builder mergeFrom( done = true; break; case 10: { - tokenUrl_ = input.readStringRequireUtf8(); + deviceAuthorizationUrl_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 10 case 18: { - refreshUrl_ = input.readStringRequireUtf8(); + tokenUrl_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 18 case 26: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 34: { com.google.protobuf.MapEntry scopes__ = input.readMessage( ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); internalGetMutableScopes().getMutableMap().put( scopes__.getKey(), scopes__.getValue()); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; break; - } // case 26 + } // case 34 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -635,13 +711,105 @@ public Builder mergeFrom( } private int bitField0_; + private java.lang.Object deviceAuthorizationUrl_ = ""; + /** + *
    +     * The device authorization endpoint URL.
    +     * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The deviceAuthorizationUrl. + */ + public java.lang.String getDeviceAuthorizationUrl() { + java.lang.Object ref = deviceAuthorizationUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + deviceAuthorizationUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The device authorization endpoint URL.
    +     * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for deviceAuthorizationUrl. + */ + public com.google.protobuf.ByteString + getDeviceAuthorizationUrlBytes() { + java.lang.Object ref = deviceAuthorizationUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + deviceAuthorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The device authorization endpoint URL.
    +     * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The deviceAuthorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setDeviceAuthorizationUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + deviceAuthorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * The device authorization endpoint URL.
    +     * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearDeviceAuthorizationUrl() { + deviceAuthorizationUrl_ = getDefaultInstance().getDeviceAuthorizationUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * The device authorization endpoint URL.
    +     * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for deviceAuthorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setDeviceAuthorizationUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + deviceAuthorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + private java.lang.Object tokenUrl_ = ""; /** *
          * The token URL to be used for this flow.
          * 
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ public java.lang.String getTokenUrl() { @@ -661,7 +829,7 @@ public java.lang.String getTokenUrl() { * The token URL to be used for this flow. *
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ public com.google.protobuf.ByteString @@ -682,7 +850,7 @@ public java.lang.String getTokenUrl() { * The token URL to be used for this flow. *
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The tokenUrl to set. * @return This builder for chaining. */ @@ -690,7 +858,7 @@ public Builder setTokenUrl( java.lang.String value) { if (value == null) { throw new NullPointerException(); } tokenUrl_ = value; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); return this; } @@ -699,12 +867,12 @@ public Builder setTokenUrl( * The token URL to be used for this flow. *
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ public Builder clearTokenUrl() { tokenUrl_ = getDefaultInstance().getTokenUrl(); - bitField0_ = (bitField0_ & ~0x00000001); + bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } @@ -713,7 +881,7 @@ public Builder clearTokenUrl() { * The token URL to be used for this flow. *
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @param value The bytes for tokenUrl to set. * @return This builder for chaining. */ @@ -722,7 +890,7 @@ public Builder setTokenUrlBytes( if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); tokenUrl_ = value; - bitField0_ |= 0x00000001; + bitField0_ |= 0x00000002; onChanged(); return this; } @@ -733,7 +901,7 @@ public Builder setTokenUrlBytes( * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @return The refreshUrl. */ public java.lang.String getRefreshUrl() { @@ -753,7 +921,7 @@ public java.lang.String getRefreshUrl() { * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @return The bytes for refreshUrl. */ public com.google.protobuf.ByteString @@ -774,7 +942,7 @@ public java.lang.String getRefreshUrl() { * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @param value The refreshUrl to set. * @return This builder for chaining. */ @@ -782,7 +950,7 @@ public Builder setRefreshUrl( java.lang.String value) { if (value == null) { throw new NullPointerException(); } refreshUrl_ = value; - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; onChanged(); return this; } @@ -791,12 +959,12 @@ public Builder setRefreshUrl( * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @return This builder for chaining. */ public Builder clearRefreshUrl() { refreshUrl_ = getDefaultInstance().getRefreshUrl(); - bitField0_ = (bitField0_ & ~0x00000002); + bitField0_ = (bitField0_ & ~0x00000004); onChanged(); return this; } @@ -805,7 +973,7 @@ public Builder clearRefreshUrl() { * The URL to be used for obtaining refresh tokens. *
    * - * string refresh_url = 2; + * string refresh_url = 3; * @param value The bytes for refreshUrl to set. * @return This builder for chaining. */ @@ -814,7 +982,7 @@ public Builder setRefreshUrlBytes( if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); refreshUrl_ = value; - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; onChanged(); return this; } @@ -838,7 +1006,7 @@ public Builder setRefreshUrlBytes( if (!scopes_.isMutable()) { scopes_ = scopes_.copy(); } - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; onChanged(); return scopes_; } @@ -850,7 +1018,7 @@ public int getScopesCount() { * The available scopes for the OAuth2 security scheme. *
    * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public boolean containsScopes( @@ -871,7 +1039,7 @@ public java.util.Map getScopes() { * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.util.Map getScopesMap() { @@ -882,7 +1050,7 @@ public java.util.Map getScopesMap() { * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public /* nullable */ @@ -900,7 +1068,7 @@ java.lang.String getScopesOrDefault( * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override public java.lang.String getScopesOrThrow( @@ -914,7 +1082,7 @@ public java.lang.String getScopesOrThrow( return map.get(key); } public Builder clearScopes() { - bitField0_ = (bitField0_ & ~0x00000004); + bitField0_ = (bitField0_ & ~0x00000008); internalGetMutableScopes().getMutableMap() .clear(); return this; @@ -924,7 +1092,7 @@ public Builder clearScopes() { * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ public Builder removeScopes( java.lang.String key) { @@ -939,7 +1107,7 @@ public Builder removeScopes( @java.lang.Deprecated public java.util.Map getMutableScopes() { - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; return internalGetMutableScopes().getMutableMap(); } /** @@ -947,7 +1115,7 @@ public Builder removeScopes( * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putScopes( java.lang.String key, @@ -956,7 +1124,7 @@ public Builder putScopes( if (value == null) { throw new NullPointerException("map value"); } internalGetMutableScopes().getMutableMap() .put(key, value); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; return this; } /** @@ -964,33 +1132,33 @@ public Builder putScopes( * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ public Builder putAllScopes( java.util.Map values) { internalGetMutableScopes().getMutableMap() .putAll(values); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000008; return this; } - // @@protoc_insertion_point(builder_scope:a2a.v1.PasswordOAuthFlow) + // @@protoc_insertion_point(builder_scope:a2a.v1.DeviceCodeOAuthFlow) } - // @@protoc_insertion_point(class_scope:a2a.v1.PasswordOAuthFlow) - private static final io.a2a.grpc.PasswordOAuthFlow DEFAULT_INSTANCE; + // @@protoc_insertion_point(class_scope:a2a.v1.DeviceCodeOAuthFlow) + private static final io.a2a.grpc.DeviceCodeOAuthFlow DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new io.a2a.grpc.PasswordOAuthFlow(); + DEFAULT_INSTANCE = new io.a2a.grpc.DeviceCodeOAuthFlow(); } - public static io.a2a.grpc.PasswordOAuthFlow getDefaultInstance() { + public static io.a2a.grpc.DeviceCodeOAuthFlow getDefaultInstance() { return DEFAULT_INSTANCE; } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { @java.lang.Override - public PasswordOAuthFlow parsePartialFrom( + public DeviceCodeOAuthFlow parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { @@ -1009,17 +1177,17 @@ public PasswordOAuthFlow parsePartialFrom( } }; - public static com.google.protobuf.Parser parser() { + public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override - public com.google.protobuf.Parser getParserForType() { + public com.google.protobuf.Parser getParserForType() { return PARSER; } @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlow getDefaultInstanceForType() { + public io.a2a.grpc.DeviceCodeOAuthFlow getDefaultInstanceForType() { return DEFAULT_INSTANCE; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DeviceCodeOAuthFlowOrBuilder.java similarity index 63% rename from spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/DeviceCodeOAuthFlowOrBuilder.java index 1b0aeeac0..761e2a6db 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeviceCodeOAuthFlowOrBuilder.java @@ -6,16 +6,36 @@ package io.a2a.grpc; @com.google.protobuf.Generated -public interface PasswordOAuthFlowOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.PasswordOAuthFlow) +public interface DeviceCodeOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.DeviceCodeOAuthFlow) com.google.protobuf.MessageOrBuilder { + /** + *
    +   * The device authorization endpoint URL.
    +   * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The deviceAuthorizationUrl. + */ + java.lang.String getDeviceAuthorizationUrl(); + /** + *
    +   * The device authorization endpoint URL.
    +   * 
    + * + * string device_authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for deviceAuthorizationUrl. + */ + com.google.protobuf.ByteString + getDeviceAuthorizationUrlBytes(); + /** *
        * The token URL to be used for this flow.
        * 
    * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The tokenUrl. */ java.lang.String getTokenUrl(); @@ -24,7 +44,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The token URL to be used for this flow. * * - * string token_url = 1 [(.google.api.field_behavior) = REQUIRED]; + * string token_url = 2 [(.google.api.field_behavior) = REQUIRED]; * @return The bytes for tokenUrl. */ com.google.protobuf.ByteString @@ -35,7 +55,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The URL to be used for obtaining refresh tokens. * * - * string refresh_url = 2; + * string refresh_url = 3; * @return The refreshUrl. */ java.lang.String getRefreshUrl(); @@ -44,7 +64,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The URL to be used for obtaining refresh tokens. * * - * string refresh_url = 2; + * string refresh_url = 3; * @return The bytes for refreshUrl. */ com.google.protobuf.ByteString @@ -55,7 +75,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ int getScopesCount(); /** @@ -63,7 +83,7 @@ public interface PasswordOAuthFlowOrBuilder extends * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ boolean containsScopes( java.lang.String key); @@ -78,7 +98,7 @@ boolean containsScopes( * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ java.util.Map getScopesMap(); @@ -87,7 +107,7 @@ boolean containsScopes( * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ /* nullable */ java.lang.String getScopesOrDefault( @@ -99,7 +119,7 @@ java.lang.String getScopesOrDefault( * The available scopes for the OAuth2 security scheme. * * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; + * map<string, string> scopes = 4 [(.google.api.field_behavior) = REQUIRED]; */ java.lang.String getScopesOrThrow( java.lang.String key); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java index 2322a9f8b..33395ab8d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -151,7 +151,10 @@ public java.lang.String getName() { private int historyLength_ = 0; /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve. An
    +   * unset value means the client does not impose any limit. A value of zero is
    +   * a request to not include any messages. The server MUST NOT return more
    +   * messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 2; @@ -163,7 +166,10 @@ public boolean hasHistoryLength() { } /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve. An
    +   * unset value means the client does not impose any limit. A value of zero is
    +   * a request to not include any messages. The server MUST NOT return more
    +   * messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 2; @@ -720,7 +726,10 @@ public Builder setNameBytes( private int historyLength_ ; /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve. An
    +     * unset value means the client does not impose any limit. A value of zero is
    +     * a request to not include any messages. The server MUST NOT return more
    +     * messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 2; @@ -732,7 +741,10 @@ public boolean hasHistoryLength() { } /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve. An
    +     * unset value means the client does not impose any limit. A value of zero is
    +     * a request to not include any messages. The server MUST NOT return more
    +     * messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 2; @@ -744,7 +756,10 @@ public int getHistoryLength() { } /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve. An
    +     * unset value means the client does not impose any limit. A value of zero is
    +     * a request to not include any messages. The server MUST NOT return more
    +     * messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 2; @@ -760,7 +775,10 @@ public Builder setHistoryLength(int value) { } /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve. An
    +     * unset value means the client does not impose any limit. A value of zero is
    +     * a request to not include any messages. The server MUST NOT return more
    +     * messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 2; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java index a17389884..1ef78600d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java @@ -54,7 +54,10 @@ public interface GetTaskRequestOrBuilder extends /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve. An
    +   * unset value means the client does not impose any limit. A value of zero is
    +   * a request to not include any messages. The server MUST NOT return more
    +   * messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 2; @@ -63,7 +66,10 @@ public interface GetTaskRequestOrBuilder extends boolean hasHistoryLength(); /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve. An
    +   * unset value means the client does not impose any limit. A value of zero is
    +   * a request to not include any messages. The server MUST NOT return more
    +   * messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 2; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java deleted file mode 100644 index a7f186016..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java +++ /dev/null @@ -1,1027 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// NO CHECKED-IN PROTOBUF GENCODE -// source: a2a.proto -// Protobuf Java Version: 4.33.1 - -package io.a2a.grpc; - -/** - *
    - * --8<-- [start:ImplicitOAuthFlow]
    - * Defines configuration details for the OAuth 2.0 Implicit flow.
    - * 
    - * - * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} - */ -@com.google.protobuf.Generated -public final class ImplicitOAuthFlow extends - com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.ImplicitOAuthFlow) - ImplicitOAuthFlowOrBuilder { -private static final long serialVersionUID = 0L; - static { - com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( - com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, - /* major= */ 4, - /* minor= */ 33, - /* patch= */ 1, - /* suffix= */ "", - "ImplicitOAuthFlow"); - } - // Use ImplicitOAuthFlow.newBuilder() to construct. - private ImplicitOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - } - private ImplicitOAuthFlow() { - authorizationUrl_ = ""; - refreshUrl_ = ""; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; - } - - @SuppressWarnings({"rawtypes"}) - @java.lang.Override - protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( - int number) { - switch (number) { - case 3: - return internalGetScopes(); - default: - throw new RuntimeException( - "Invalid map field number: " + number); - } - } - @java.lang.Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.a2a.grpc.ImplicitOAuthFlow.class, io.a2a.grpc.ImplicitOAuthFlow.Builder.class); - } - - public static final int AUTHORIZATION_URL_FIELD_NUMBER = 1; - @SuppressWarnings("serial") - private volatile java.lang.Object authorizationUrl_ = ""; - /** - *
    -   * The authorization URL to be used for this flow.
    -   * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The authorizationUrl. - */ - @java.lang.Override - public java.lang.String getAuthorizationUrl() { - java.lang.Object ref = authorizationUrl_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - authorizationUrl_ = s; - return s; - } - } - /** - *
    -   * The authorization URL to be used for this flow.
    -   * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for authorizationUrl. - */ - @java.lang.Override - public com.google.protobuf.ByteString - getAuthorizationUrlBytes() { - java.lang.Object ref = authorizationUrl_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - authorizationUrl_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int REFRESH_URL_FIELD_NUMBER = 2; - @SuppressWarnings("serial") - private volatile java.lang.Object refreshUrl_ = ""; - /** - *
    -   * The URL to be used for obtaining refresh tokens.
    -   * 
    - * - * string refresh_url = 2; - * @return The refreshUrl. - */ - @java.lang.Override - public java.lang.String getRefreshUrl() { - java.lang.Object ref = refreshUrl_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - refreshUrl_ = s; - return s; - } - } - /** - *
    -   * The URL to be used for obtaining refresh tokens.
    -   * 
    - * - * string refresh_url = 2; - * @return The bytes for refreshUrl. - */ - @java.lang.Override - public com.google.protobuf.ByteString - getRefreshUrlBytes() { - java.lang.Object ref = refreshUrl_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - refreshUrl_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int SCOPES_FIELD_NUMBER = 3; - private static final class ScopesDefaultEntryHolder { - static final com.google.protobuf.MapEntry< - java.lang.String, java.lang.String> defaultEntry = - com.google.protobuf.MapEntry - .newDefaultInstance( - io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, - com.google.protobuf.WireFormat.FieldType.STRING, - "", - com.google.protobuf.WireFormat.FieldType.STRING, - ""); - } - @SuppressWarnings("serial") - private com.google.protobuf.MapField< - java.lang.String, java.lang.String> scopes_; - private com.google.protobuf.MapField - internalGetScopes() { - if (scopes_ == null) { - return com.google.protobuf.MapField.emptyMapField( - ScopesDefaultEntryHolder.defaultEntry); - } - return scopes_; - } - public int getScopesCount() { - return internalGetScopes().getMap().size(); - } - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public boolean containsScopes( - java.lang.String key) { - if (key == null) { throw new NullPointerException("map key"); } - return internalGetScopes().getMap().containsKey(key); - } - /** - * Use {@link #getScopesMap()} instead. - */ - @java.lang.Override - @java.lang.Deprecated - public java.util.Map getScopes() { - return getScopesMap(); - } - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public java.util.Map getScopesMap() { - return internalGetScopes().getMap(); - } - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public /* nullable */ -java.lang.String getScopesOrDefault( - java.lang.String key, - /* nullable */ -java.lang.String defaultValue) { - if (key == null) { throw new NullPointerException("map key"); } - java.util.Map map = - internalGetScopes().getMap(); - return map.containsKey(key) ? map.get(key) : defaultValue; - } - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public java.lang.String getScopesOrThrow( - java.lang.String key) { - if (key == null) { throw new NullPointerException("map key"); } - java.util.Map map = - internalGetScopes().getMap(); - if (!map.containsKey(key)) { - throw new java.lang.IllegalArgumentException(); - } - return map.get(key); - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, authorizationUrl_); - } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); - } - com.google.protobuf.GeneratedMessage - .serializeStringMapTo( - output, - internalGetScopes(), - ScopesDefaultEntryHolder.defaultEntry, - 3); - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, authorizationUrl_); - } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); - } - for (java.util.Map.Entry entry - : internalGetScopes().getMap().entrySet()) { - com.google.protobuf.MapEntry - scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() - .setKey(entry.getKey()) - .setValue(entry.getValue()) - .build(); - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, scopes__); - } - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.a2a.grpc.ImplicitOAuthFlow)) { - return super.equals(obj); - } - io.a2a.grpc.ImplicitOAuthFlow other = (io.a2a.grpc.ImplicitOAuthFlow) obj; - - if (!getAuthorizationUrl() - .equals(other.getAuthorizationUrl())) return false; - if (!getRefreshUrl() - .equals(other.getRefreshUrl())) return false; - if (!internalGetScopes().equals( - other.internalGetScopes())) return false; - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + AUTHORIZATION_URL_FIELD_NUMBER; - hash = (53 * hash) + getAuthorizationUrl().hashCode(); - hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; - hash = (53 * hash) + getRefreshUrl().hashCode(); - if (!internalGetScopes().getMap().isEmpty()) { - hash = (37 * hash) + SCOPES_FIELD_NUMBER; - hash = (53 * hash) + internalGetScopes().hashCode(); - } - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.a2a.grpc.ImplicitOAuthFlow parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.a2a.grpc.ImplicitOAuthFlow parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input); - } - public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.a2a.grpc.ImplicitOAuthFlow prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - *
    -   * --8<-- [start:ImplicitOAuthFlow]
    -   * Defines configuration details for the OAuth 2.0 Implicit flow.
    -   * 
    - * - * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.ImplicitOAuthFlow) - io.a2a.grpc.ImplicitOAuthFlowOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; - } - - @SuppressWarnings({"rawtypes"}) - protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( - int number) { - switch (number) { - case 3: - return internalGetScopes(); - default: - throw new RuntimeException( - "Invalid map field number: " + number); - } - } - @SuppressWarnings({"rawtypes"}) - protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( - int number) { - switch (number) { - case 3: - return internalGetMutableScopes(); - default: - throw new RuntimeException( - "Invalid map field number: " + number); - } - } - @java.lang.Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.a2a.grpc.ImplicitOAuthFlow.class, io.a2a.grpc.ImplicitOAuthFlow.Builder.class); - } - - // Construct using io.a2a.grpc.ImplicitOAuthFlow.newBuilder() - private Builder() { - - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - - } - @java.lang.Override - public Builder clear() { - super.clear(); - bitField0_ = 0; - authorizationUrl_ = ""; - refreshUrl_ = ""; - internalGetMutableScopes().clear(); - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; - } - - @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlow getDefaultInstanceForType() { - return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); - } - - @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlow build() { - io.a2a.grpc.ImplicitOAuthFlow result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlow buildPartial() { - io.a2a.grpc.ImplicitOAuthFlow result = new io.a2a.grpc.ImplicitOAuthFlow(this); - if (bitField0_ != 0) { buildPartial0(result); } - onBuilt(); - return result; - } - - private void buildPartial0(io.a2a.grpc.ImplicitOAuthFlow result) { - int from_bitField0_ = bitField0_; - if (((from_bitField0_ & 0x00000001) != 0)) { - result.authorizationUrl_ = authorizationUrl_; - } - if (((from_bitField0_ & 0x00000002) != 0)) { - result.refreshUrl_ = refreshUrl_; - } - if (((from_bitField0_ & 0x00000004) != 0)) { - result.scopes_ = internalGetScopes(); - result.scopes_.makeImmutable(); - } - } - - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.ImplicitOAuthFlow) { - return mergeFrom((io.a2a.grpc.ImplicitOAuthFlow)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.a2a.grpc.ImplicitOAuthFlow other) { - if (other == io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance()) return this; - if (!other.getAuthorizationUrl().isEmpty()) { - authorizationUrl_ = other.authorizationUrl_; - bitField0_ |= 0x00000001; - onChanged(); - } - if (!other.getRefreshUrl().isEmpty()) { - refreshUrl_ = other.refreshUrl_; - bitField0_ |= 0x00000002; - onChanged(); - } - internalGetMutableScopes().mergeFrom( - other.internalGetScopes()); - bitField0_ |= 0x00000004; - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - case 10: { - authorizationUrl_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; - break; - } // case 10 - case 18: { - refreshUrl_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000002; - break; - } // case 18 - case 26: { - com.google.protobuf.MapEntry - scopes__ = input.readMessage( - ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); - internalGetMutableScopes().getMutableMap().put( - scopes__.getKey(), scopes__.getValue()); - bitField0_ |= 0x00000004; - break; - } // case 26 - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - private int bitField0_; - - private java.lang.Object authorizationUrl_ = ""; - /** - *
    -     * The authorization URL to be used for this flow.
    -     * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The authorizationUrl. - */ - public java.lang.String getAuthorizationUrl() { - java.lang.Object ref = authorizationUrl_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - authorizationUrl_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * The authorization URL to be used for this flow.
    -     * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for authorizationUrl. - */ - public com.google.protobuf.ByteString - getAuthorizationUrlBytes() { - java.lang.Object ref = authorizationUrl_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - authorizationUrl_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * The authorization URL to be used for this flow.
    -     * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The authorizationUrl to set. - * @return This builder for chaining. - */ - public Builder setAuthorizationUrl( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - authorizationUrl_ = value; - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * The authorization URL to be used for this flow.
    -     * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return This builder for chaining. - */ - public Builder clearAuthorizationUrl() { - authorizationUrl_ = getDefaultInstance().getAuthorizationUrl(); - bitField0_ = (bitField0_ & ~0x00000001); - onChanged(); - return this; - } - /** - *
    -     * The authorization URL to be used for this flow.
    -     * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes for authorizationUrl to set. - * @return This builder for chaining. - */ - public Builder setAuthorizationUrlBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - authorizationUrl_ = value; - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - - private java.lang.Object refreshUrl_ = ""; - /** - *
    -     * The URL to be used for obtaining refresh tokens.
    -     * 
    - * - * string refresh_url = 2; - * @return The refreshUrl. - */ - public java.lang.String getRefreshUrl() { - java.lang.Object ref = refreshUrl_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - refreshUrl_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * The URL to be used for obtaining refresh tokens.
    -     * 
    - * - * string refresh_url = 2; - * @return The bytes for refreshUrl. - */ - public com.google.protobuf.ByteString - getRefreshUrlBytes() { - java.lang.Object ref = refreshUrl_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - refreshUrl_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * The URL to be used for obtaining refresh tokens.
    -     * 
    - * - * string refresh_url = 2; - * @param value The refreshUrl to set. - * @return This builder for chaining. - */ - public Builder setRefreshUrl( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - refreshUrl_ = value; - bitField0_ |= 0x00000002; - onChanged(); - return this; - } - /** - *
    -     * The URL to be used for obtaining refresh tokens.
    -     * 
    - * - * string refresh_url = 2; - * @return This builder for chaining. - */ - public Builder clearRefreshUrl() { - refreshUrl_ = getDefaultInstance().getRefreshUrl(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - return this; - } - /** - *
    -     * The URL to be used for obtaining refresh tokens.
    -     * 
    - * - * string refresh_url = 2; - * @param value The bytes for refreshUrl to set. - * @return This builder for chaining. - */ - public Builder setRefreshUrlBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - refreshUrl_ = value; - bitField0_ |= 0x00000002; - onChanged(); - return this; - } - - private com.google.protobuf.MapField< - java.lang.String, java.lang.String> scopes_; - private com.google.protobuf.MapField - internalGetScopes() { - if (scopes_ == null) { - return com.google.protobuf.MapField.emptyMapField( - ScopesDefaultEntryHolder.defaultEntry); - } - return scopes_; - } - private com.google.protobuf.MapField - internalGetMutableScopes() { - if (scopes_ == null) { - scopes_ = com.google.protobuf.MapField.newMapField( - ScopesDefaultEntryHolder.defaultEntry); - } - if (!scopes_.isMutable()) { - scopes_ = scopes_.copy(); - } - bitField0_ |= 0x00000004; - onChanged(); - return scopes_; - } - public int getScopesCount() { - return internalGetScopes().getMap().size(); - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public boolean containsScopes( - java.lang.String key) { - if (key == null) { throw new NullPointerException("map key"); } - return internalGetScopes().getMap().containsKey(key); - } - /** - * Use {@link #getScopesMap()} instead. - */ - @java.lang.Override - @java.lang.Deprecated - public java.util.Map getScopes() { - return getScopesMap(); - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public java.util.Map getScopesMap() { - return internalGetScopes().getMap(); - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public /* nullable */ -java.lang.String getScopesOrDefault( - java.lang.String key, - /* nullable */ -java.lang.String defaultValue) { - if (key == null) { throw new NullPointerException("map key"); } - java.util.Map map = - internalGetScopes().getMap(); - return map.containsKey(key) ? map.get(key) : defaultValue; - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public java.lang.String getScopesOrThrow( - java.lang.String key) { - if (key == null) { throw new NullPointerException("map key"); } - java.util.Map map = - internalGetScopes().getMap(); - if (!map.containsKey(key)) { - throw new java.lang.IllegalArgumentException(); - } - return map.get(key); - } - public Builder clearScopes() { - bitField0_ = (bitField0_ & ~0x00000004); - internalGetMutableScopes().getMutableMap() - .clear(); - return this; - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder removeScopes( - java.lang.String key) { - if (key == null) { throw new NullPointerException("map key"); } - internalGetMutableScopes().getMutableMap() - .remove(key); - return this; - } - /** - * Use alternate mutation accessors instead. - */ - @java.lang.Deprecated - public java.util.Map - getMutableScopes() { - bitField0_ |= 0x00000004; - return internalGetMutableScopes().getMutableMap(); - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder putScopes( - java.lang.String key, - java.lang.String value) { - if (key == null) { throw new NullPointerException("map key"); } - if (value == null) { throw new NullPointerException("map value"); } - internalGetMutableScopes().getMutableMap() - .put(key, value); - bitField0_ |= 0x00000004; - return this; - } - /** - *
    -     * The available scopes for the OAuth2 security scheme.
    -     * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder putAllScopes( - java.util.Map values) { - internalGetMutableScopes().getMutableMap() - .putAll(values); - bitField0_ |= 0x00000004; - return this; - } - - // @@protoc_insertion_point(builder_scope:a2a.v1.ImplicitOAuthFlow) - } - - // @@protoc_insertion_point(class_scope:a2a.v1.ImplicitOAuthFlow) - private static final io.a2a.grpc.ImplicitOAuthFlow DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.a2a.grpc.ImplicitOAuthFlow(); - } - - public static io.a2a.grpc.ImplicitOAuthFlow getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public ImplicitOAuthFlow parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlow getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - -} - diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java deleted file mode 100644 index e59040641..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java +++ /dev/null @@ -1,106 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// NO CHECKED-IN PROTOBUF GENCODE -// source: a2a.proto -// Protobuf Java Version: 4.33.1 - -package io.a2a.grpc; - -@com.google.protobuf.Generated -public interface ImplicitOAuthFlowOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.ImplicitOAuthFlow) - com.google.protobuf.MessageOrBuilder { - - /** - *
    -   * The authorization URL to be used for this flow.
    -   * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The authorizationUrl. - */ - java.lang.String getAuthorizationUrl(); - /** - *
    -   * The authorization URL to be used for this flow.
    -   * 
    - * - * string authorization_url = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for authorizationUrl. - */ - com.google.protobuf.ByteString - getAuthorizationUrlBytes(); - - /** - *
    -   * The URL to be used for obtaining refresh tokens.
    -   * 
    - * - * string refresh_url = 2; - * @return The refreshUrl. - */ - java.lang.String getRefreshUrl(); - /** - *
    -   * The URL to be used for obtaining refresh tokens.
    -   * 
    - * - * string refresh_url = 2; - * @return The bytes for refreshUrl. - */ - com.google.protobuf.ByteString - getRefreshUrlBytes(); - - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - int getScopesCount(); - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - boolean containsScopes( - java.lang.String key); - /** - * Use {@link #getScopesMap()} instead. - */ - @java.lang.Deprecated - java.util.Map - getScopes(); - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - java.util.Map - getScopesMap(); - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - /* nullable */ -java.lang.String getScopesOrDefault( - java.lang.String key, - /* nullable */ -java.lang.String defaultValue); - /** - *
    -   * The available scopes for the OAuth2 security scheme.
    -   * 
    - * - * map<string, string> scopes = 3 [(.google.api.field_behavior) = REQUIRED]; - */ - java.lang.String getScopesOrThrow( - java.lang.String key); -} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java index 93d5a8bc9..c2645afcc 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequest.java @@ -276,20 +276,45 @@ public int getHistoryLength() { return historyLength_; } - public static final int LAST_UPDATED_AFTER_FIELD_NUMBER = 6; - private long lastUpdatedAfter_ = 0L; + public static final int STATUS_TIMESTAMP_AFTER_FIELD_NUMBER = 6; + private com.google.protobuf.Timestamp statusTimestampAfter_; /** *
    -   * Filter tasks updated after this timestamp (milliseconds since epoch).
    -   * Only tasks with a last updated time greater than or equal to this value will be returned.
    +   * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +   * Only tasks with a status timestamp time greater than or equal to this value will be returned.
        * 
    * - * int64 last_updated_after = 6; - * @return The lastUpdatedAfter. + * .google.protobuf.Timestamp status_timestamp_after = 6; + * @return Whether the statusTimestampAfter field is set. */ @java.lang.Override - public long getLastUpdatedAfter() { - return lastUpdatedAfter_; + public boolean hasStatusTimestampAfter() { + return ((bitField0_ & 0x00000004) != 0); + } + /** + *
    +   * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +   * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +   * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + * @return The statusTimestampAfter. + */ + @java.lang.Override + public com.google.protobuf.Timestamp getStatusTimestampAfter() { + return statusTimestampAfter_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : statusTimestampAfter_; + } + /** + *
    +   * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +   * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +   * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + @java.lang.Override + public com.google.protobuf.TimestampOrBuilder getStatusTimestampAfterOrBuilder() { + return statusTimestampAfter_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : statusTimestampAfter_; } public static final int INCLUDE_ARTIFACTS_FIELD_NUMBER = 7; @@ -305,7 +330,7 @@ public long getLastUpdatedAfter() { */ @java.lang.Override public boolean hasIncludeArtifacts() { - return ((bitField0_ & 0x00000004) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** *
    @@ -350,10 +375,10 @@ public void writeTo(com.google.protobuf.CodedOutputStream output)
         if (((bitField0_ & 0x00000002) != 0)) {
           output.writeInt32(5, historyLength_);
         }
    -    if (lastUpdatedAfter_ != 0L) {
    -      output.writeInt64(6, lastUpdatedAfter_);
    -    }
         if (((bitField0_ & 0x00000004) != 0)) {
    +      output.writeMessage(6, getStatusTimestampAfter());
    +    }
    +    if (((bitField0_ & 0x00000008) != 0)) {
           output.writeBool(7, includeArtifacts_);
         }
         if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) {
    @@ -386,11 +411,11 @@ public int getSerializedSize() {
           size += com.google.protobuf.CodedOutputStream
             .computeInt32Size(5, historyLength_);
         }
    -    if (lastUpdatedAfter_ != 0L) {
    +    if (((bitField0_ & 0x00000004) != 0)) {
           size += com.google.protobuf.CodedOutputStream
    -        .computeInt64Size(6, lastUpdatedAfter_);
    +        .computeMessageSize(6, getStatusTimestampAfter());
         }
    -    if (((bitField0_ & 0x00000004) != 0)) {
    +    if (((bitField0_ & 0x00000008) != 0)) {
           size += com.google.protobuf.CodedOutputStream
             .computeBoolSize(7, includeArtifacts_);
         }
    @@ -429,8 +454,11 @@ public boolean equals(final java.lang.Object obj) {
           if (getHistoryLength()
               != other.getHistoryLength()) return false;
         }
    -    if (getLastUpdatedAfter()
    -        != other.getLastUpdatedAfter()) return false;
    +    if (hasStatusTimestampAfter() != other.hasStatusTimestampAfter()) return false;
    +    if (hasStatusTimestampAfter()) {
    +      if (!getStatusTimestampAfter()
    +          .equals(other.getStatusTimestampAfter())) return false;
    +    }
         if (hasIncludeArtifacts() != other.hasIncludeArtifacts()) return false;
         if (hasIncludeArtifacts()) {
           if (getIncludeArtifacts()
    @@ -463,9 +491,10 @@ public int hashCode() {
           hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER;
           hash = (53 * hash) + getHistoryLength();
         }
    -    hash = (37 * hash) + LAST_UPDATED_AFTER_FIELD_NUMBER;
    -    hash = (53 * hash) + com.google.protobuf.Internal.hashLong(
    -        getLastUpdatedAfter());
    +    if (hasStatusTimestampAfter()) {
    +      hash = (37 * hash) + STATUS_TIMESTAMP_AFTER_FIELD_NUMBER;
    +      hash = (53 * hash) + getStatusTimestampAfter().hashCode();
    +    }
         if (hasIncludeArtifacts()) {
           hash = (37 * hash) + INCLUDE_ARTIFACTS_FIELD_NUMBER;
           hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(
    @@ -595,13 +624,19 @@ public static final class Builder extends
     
         // Construct using io.a2a.grpc.ListTasksRequest.newBuilder()
         private Builder() {
    -
    +      maybeForceBuilderInitialization();
         }
     
         private Builder(
             com.google.protobuf.GeneratedMessage.BuilderParent parent) {
           super(parent);
    -
    +      maybeForceBuilderInitialization();
    +    }
    +    private void maybeForceBuilderInitialization() {
    +      if (com.google.protobuf.GeneratedMessage
    +              .alwaysUseFieldBuilders) {
    +        internalGetStatusTimestampAfterFieldBuilder();
    +      }
         }
         @java.lang.Override
         public Builder clear() {
    @@ -613,7 +648,11 @@ public Builder clear() {
           pageSize_ = 0;
           pageToken_ = "";
           historyLength_ = 0;
    -      lastUpdatedAfter_ = 0L;
    +      statusTimestampAfter_ = null;
    +      if (statusTimestampAfterBuilder_ != null) {
    +        statusTimestampAfterBuilder_.dispose();
    +        statusTimestampAfterBuilder_ = null;
    +      }
           includeArtifacts_ = false;
           return this;
         }
    @@ -670,11 +709,14 @@ private void buildPartial0(io.a2a.grpc.ListTasksRequest result) {
             to_bitField0_ |= 0x00000002;
           }
           if (((from_bitField0_ & 0x00000040) != 0)) {
    -        result.lastUpdatedAfter_ = lastUpdatedAfter_;
    +        result.statusTimestampAfter_ = statusTimestampAfterBuilder_ == null
    +            ? statusTimestampAfter_
    +            : statusTimestampAfterBuilder_.build();
    +        to_bitField0_ |= 0x00000004;
           }
           if (((from_bitField0_ & 0x00000080) != 0)) {
             result.includeArtifacts_ = includeArtifacts_;
    -        to_bitField0_ |= 0x00000004;
    +        to_bitField0_ |= 0x00000008;
           }
           result.bitField0_ |= to_bitField0_;
         }
    @@ -715,8 +757,8 @@ public Builder mergeFrom(io.a2a.grpc.ListTasksRequest other) {
           if (other.hasHistoryLength()) {
             setHistoryLength(other.getHistoryLength());
           }
    -      if (other.getLastUpdatedAfter() != 0L) {
    -        setLastUpdatedAfter(other.getLastUpdatedAfter());
    +      if (other.hasStatusTimestampAfter()) {
    +        mergeStatusTimestampAfter(other.getStatusTimestampAfter());
           }
           if (other.hasIncludeArtifacts()) {
             setIncludeArtifacts(other.getIncludeArtifacts());
    @@ -772,11 +814,13 @@ public Builder mergeFrom(
                   bitField0_ |= 0x00000020;
                   break;
                 } // case 40
    -            case 48: {
    -              lastUpdatedAfter_ = input.readInt64();
    +            case 50: {
    +              input.readMessage(
    +                  internalGetStatusTimestampAfterFieldBuilder().getBuilder(),
    +                  extensionRegistry);
                   bitField0_ |= 0x00000040;
                   break;
    -            } // case 48
    +            } // case 50
                 case 56: {
                   includeArtifacts_ = input.readBool();
                   bitField0_ |= 0x00000080;
    @@ -1267,52 +1311,171 @@ public Builder clearHistoryLength() {
           return this;
         }
     
    -    private long lastUpdatedAfter_ ;
    +    private com.google.protobuf.Timestamp statusTimestampAfter_;
    +    private com.google.protobuf.SingleFieldBuilder<
    +        com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> statusTimestampAfterBuilder_;
         /**
          * 
    -     * Filter tasks updated after this timestamp (milliseconds since epoch).
    -     * Only tasks with a last updated time greater than or equal to this value will be returned.
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
          * 
    * - * int64 last_updated_after = 6; - * @return The lastUpdatedAfter. + * .google.protobuf.Timestamp status_timestamp_after = 6; + * @return Whether the statusTimestampAfter field is set. */ - @java.lang.Override - public long getLastUpdatedAfter() { - return lastUpdatedAfter_; + public boolean hasStatusTimestampAfter() { + return ((bitField0_ & 0x00000040) != 0); } /** *
    -     * Filter tasks updated after this timestamp (milliseconds since epoch).
    -     * Only tasks with a last updated time greater than or equal to this value will be returned.
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
          * 
    * - * int64 last_updated_after = 6; - * @param value The lastUpdatedAfter to set. - * @return This builder for chaining. + * .google.protobuf.Timestamp status_timestamp_after = 6; + * @return The statusTimestampAfter. */ - public Builder setLastUpdatedAfter(long value) { - - lastUpdatedAfter_ = value; + public com.google.protobuf.Timestamp getStatusTimestampAfter() { + if (statusTimestampAfterBuilder_ == null) { + return statusTimestampAfter_ == null ? com.google.protobuf.Timestamp.getDefaultInstance() : statusTimestampAfter_; + } else { + return statusTimestampAfterBuilder_.getMessage(); + } + } + /** + *
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +     * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + public Builder setStatusTimestampAfter(com.google.protobuf.Timestamp value) { + if (statusTimestampAfterBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + statusTimestampAfter_ = value; + } else { + statusTimestampAfterBuilder_.setMessage(value); + } bitField0_ |= 0x00000040; onChanged(); return this; } /** *
    -     * Filter tasks updated after this timestamp (milliseconds since epoch).
    -     * Only tasks with a last updated time greater than or equal to this value will be returned.
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
          * 
    * - * int64 last_updated_after = 6; - * @return This builder for chaining. + * .google.protobuf.Timestamp status_timestamp_after = 6; */ - public Builder clearLastUpdatedAfter() { + public Builder setStatusTimestampAfter( + com.google.protobuf.Timestamp.Builder builderForValue) { + if (statusTimestampAfterBuilder_ == null) { + statusTimestampAfter_ = builderForValue.build(); + } else { + statusTimestampAfterBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +     * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + public Builder mergeStatusTimestampAfter(com.google.protobuf.Timestamp value) { + if (statusTimestampAfterBuilder_ == null) { + if (((bitField0_ & 0x00000040) != 0) && + statusTimestampAfter_ != null && + statusTimestampAfter_ != com.google.protobuf.Timestamp.getDefaultInstance()) { + getStatusTimestampAfterBuilder().mergeFrom(value); + } else { + statusTimestampAfter_ = value; + } + } else { + statusTimestampAfterBuilder_.mergeFrom(value); + } + if (statusTimestampAfter_ != null) { + bitField0_ |= 0x00000040; + onChanged(); + } + return this; + } + /** + *
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +     * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + public Builder clearStatusTimestampAfter() { bitField0_ = (bitField0_ & ~0x00000040); - lastUpdatedAfter_ = 0L; + statusTimestampAfter_ = null; + if (statusTimestampAfterBuilder_ != null) { + statusTimestampAfterBuilder_.dispose(); + statusTimestampAfterBuilder_ = null; + } onChanged(); return this; } + /** + *
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +     * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + public com.google.protobuf.Timestamp.Builder getStatusTimestampAfterBuilder() { + bitField0_ |= 0x00000040; + onChanged(); + return internalGetStatusTimestampAfterFieldBuilder().getBuilder(); + } + /** + *
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +     * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + public com.google.protobuf.TimestampOrBuilder getStatusTimestampAfterOrBuilder() { + if (statusTimestampAfterBuilder_ != null) { + return statusTimestampAfterBuilder_.getMessageOrBuilder(); + } else { + return statusTimestampAfter_ == null ? + com.google.protobuf.Timestamp.getDefaultInstance() : statusTimestampAfter_; + } + } + /** + *
    +     * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +     * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +     * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + private com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder> + internalGetStatusTimestampAfterFieldBuilder() { + if (statusTimestampAfterBuilder_ == null) { + statusTimestampAfterBuilder_ = new com.google.protobuf.SingleFieldBuilder< + com.google.protobuf.Timestamp, com.google.protobuf.Timestamp.Builder, com.google.protobuf.TimestampOrBuilder>( + getStatusTimestampAfter(), + getParentForChildren(), + isClean()); + statusTimestampAfter_ = null; + } + return statusTimestampAfterBuilder_; + } private boolean includeArtifacts_ ; /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java index 6b832c0d1..410485d50 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTasksRequestOrBuilder.java @@ -131,14 +131,33 @@ public interface ListTasksRequestOrBuilder extends /** *
    -   * Filter tasks updated after this timestamp (milliseconds since epoch).
    -   * Only tasks with a last updated time greater than or equal to this value will be returned.
    +   * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +   * Only tasks with a status timestamp time greater than or equal to this value will be returned.
        * 
    * - * int64 last_updated_after = 6; - * @return The lastUpdatedAfter. + * .google.protobuf.Timestamp status_timestamp_after = 6; + * @return Whether the statusTimestampAfter field is set. */ - long getLastUpdatedAfter(); + boolean hasStatusTimestampAfter(); + /** + *
    +   * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +   * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +   * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + * @return The statusTimestampAfter. + */ + com.google.protobuf.Timestamp getStatusTimestampAfter(); + /** + *
    +   * Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z").
    +   * Only tasks with a status timestamp time greater than or equal to this value will be returned.
    +   * 
    + * + * .google.protobuf.Timestamp status_timestamp_after = 6; + */ + com.google.protobuf.TimestampOrBuilder getStatusTimestampAfterOrBuilder(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Message.java b/spec-grpc/src/main/java/io/a2a/grpc/Message.java
    index 8ff6a60f6..e8f296411 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/Message.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/Message.java
    @@ -8,13 +8,12 @@
     /**
      * 
      * --8<-- [start:Message]
    - * Message is one unit of communication between client and server. It is
    - * associated with a context and optionally a task. Since the server is
    - * responsible for the context definition, it must always provide a context_id
    - * in its messages. The client can optionally provide the context_id if it
    - * knows the context to associate the message to. Similarly for task_id,
    - * except the server decides if a task is created and whether to include the
    - * task_id.
    + * Message is one unit of communication between client and server. It can be
    + * associated with a context and/or a task. For server messages, context_id must
    + * be provided, and task_id only if a task was created. For client messages, both
    + * fields are optional, with the caveat that if both are provided, they have to
    + * match (the context_id has to be the one that is set on the task). If only
    + * task_id is provided, the server will infer context_id from it.
      * 
    * * Protobuf type {@code a2a.v1.Message} @@ -702,13 +701,12 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:Message]
    -   * Message is one unit of communication between client and server. It is
    -   * associated with a context and optionally a task. Since the server is
    -   * responsible for the context definition, it must always provide a context_id
    -   * in its messages. The client can optionally provide the context_id if it
    -   * knows the context to associate the message to. Similarly for task_id,
    -   * except the server decides if a task is created and whether to include the
    -   * task_id.
    +   * Message is one unit of communication between client and server. It can be
    +   * associated with a context and/or a task. For server messages, context_id must
    +   * be provided, and task_id only if a task was created. For client messages, both
    +   * fields are optional, with the caveat that if both are provided, they have to
    +   * match (the context_id has to be the one that is set on the task). If only
    +   * task_id is provided, the server will infer context_id from it.
        * 
    * * Protobuf type {@code a2a.v1.Message} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java index f2da009e6..e4fd7c957 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java @@ -56,8 +56,7 @@ public enum FlowCase com.google.protobuf.AbstractMessage.InternalOneOfEnum { AUTHORIZATION_CODE(1), CLIENT_CREDENTIALS(2), - IMPLICIT(3), - PASSWORD(4), + DEVICE_CODE(5), FLOW_NOT_SET(0); private final int value; private FlowCase(int value) { @@ -77,8 +76,7 @@ public static FlowCase forNumber(int value) { switch (value) { case 1: return AUTHORIZATION_CODE; case 2: return CLIENT_CREDENTIALS; - case 3: return IMPLICIT; - case 4: return PASSWORD; + case 5: return DEVICE_CODE; case 0: return FLOW_NOT_SET; default: return null; } @@ -180,90 +178,47 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance(); } - public static final int IMPLICIT_FIELD_NUMBER = 3; + public static final int DEVICE_CODE_FIELD_NUMBER = 5; /** *
    -   * Configuration for the OAuth Implicit flow.
    +   * Configuration for the OAuth Device Code flow.
        * 
    * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - * @return Whether the implicit field is set. + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; + * @return Whether the deviceCode field is set. */ @java.lang.Override - public boolean hasImplicit() { - return flowCase_ == 3; + public boolean hasDeviceCode() { + return flowCase_ == 5; } /** *
    -   * Configuration for the OAuth Implicit flow.
    +   * Configuration for the OAuth Device Code flow.
        * 
    * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - * @return The implicit. + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; + * @return The deviceCode. */ @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { - if (flowCase_ == 3) { - return (io.a2a.grpc.ImplicitOAuthFlow) flow_; + public io.a2a.grpc.DeviceCodeOAuthFlow getDeviceCode() { + if (flowCase_ == 5) { + return (io.a2a.grpc.DeviceCodeOAuthFlow) flow_; } - return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + return io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } /** *
    -   * Configuration for the OAuth Implicit flow.
    +   * Configuration for the OAuth Device Code flow.
        * 
    * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { - if (flowCase_ == 3) { - return (io.a2a.grpc.ImplicitOAuthFlow) flow_; + public io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder getDeviceCodeOrBuilder() { + if (flowCase_ == 5) { + return (io.a2a.grpc.DeviceCodeOAuthFlow) flow_; } - return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); - } - - public static final int PASSWORD_FIELD_NUMBER = 4; - /** - *
    -   * Configuration for the OAuth Resource Owner Password flow.
    -   * 
    - * - * .a2a.v1.PasswordOAuthFlow password = 4; - * @return Whether the password field is set. - */ - @java.lang.Override - public boolean hasPassword() { - return flowCase_ == 4; - } - /** - *
    -   * Configuration for the OAuth Resource Owner Password flow.
    -   * 
    - * - * .a2a.v1.PasswordOAuthFlow password = 4; - * @return The password. - */ - @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlow getPassword() { - if (flowCase_ == 4) { - return (io.a2a.grpc.PasswordOAuthFlow) flow_; - } - return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); - } - /** - *
    -   * Configuration for the OAuth Resource Owner Password flow.
    -   * 
    - * - * .a2a.v1.PasswordOAuthFlow password = 4; - */ - @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { - if (flowCase_ == 4) { - return (io.a2a.grpc.PasswordOAuthFlow) flow_; - } - return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + return io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } private byte memoizedIsInitialized = -1; @@ -286,11 +241,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (flowCase_ == 2) { output.writeMessage(2, (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_); } - if (flowCase_ == 3) { - output.writeMessage(3, (io.a2a.grpc.ImplicitOAuthFlow) flow_); - } - if (flowCase_ == 4) { - output.writeMessage(4, (io.a2a.grpc.PasswordOAuthFlow) flow_); + if (flowCase_ == 5) { + output.writeMessage(5, (io.a2a.grpc.DeviceCodeOAuthFlow) flow_); } getUnknownFields().writeTo(output); } @@ -309,13 +261,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(2, (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_); } - if (flowCase_ == 3) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, (io.a2a.grpc.ImplicitOAuthFlow) flow_); - } - if (flowCase_ == 4) { + if (flowCase_ == 5) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, (io.a2a.grpc.PasswordOAuthFlow) flow_); + .computeMessageSize(5, (io.a2a.grpc.DeviceCodeOAuthFlow) flow_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -342,13 +290,9 @@ public boolean equals(final java.lang.Object obj) { if (!getClientCredentials() .equals(other.getClientCredentials())) return false; break; - case 3: - if (!getImplicit() - .equals(other.getImplicit())) return false; - break; - case 4: - if (!getPassword() - .equals(other.getPassword())) return false; + case 5: + if (!getDeviceCode() + .equals(other.getDeviceCode())) return false; break; case 0: default: @@ -373,13 +317,9 @@ public int hashCode() { hash = (37 * hash) + CLIENT_CREDENTIALS_FIELD_NUMBER; hash = (53 * hash) + getClientCredentials().hashCode(); break; - case 3: - hash = (37 * hash) + IMPLICIT_FIELD_NUMBER; - hash = (53 * hash) + getImplicit().hashCode(); - break; - case 4: - hash = (37 * hash) + PASSWORD_FIELD_NUMBER; - hash = (53 * hash) + getPassword().hashCode(); + case 5: + hash = (37 * hash) + DEVICE_CODE_FIELD_NUMBER; + hash = (53 * hash) + getDeviceCode().hashCode(); break; case 0: default: @@ -526,11 +466,8 @@ public Builder clear() { if (clientCredentialsBuilder_ != null) { clientCredentialsBuilder_.clear(); } - if (implicitBuilder_ != null) { - implicitBuilder_.clear(); - } - if (passwordBuilder_ != null) { - passwordBuilder_.clear(); + if (deviceCodeBuilder_ != null) { + deviceCodeBuilder_.clear(); } flowCase_ = 0; flow_ = null; @@ -581,13 +518,9 @@ private void buildPartialOneofs(io.a2a.grpc.OAuthFlows result) { clientCredentialsBuilder_ != null) { result.flow_ = clientCredentialsBuilder_.build(); } - if (flowCase_ == 3 && - implicitBuilder_ != null) { - result.flow_ = implicitBuilder_.build(); - } - if (flowCase_ == 4 && - passwordBuilder_ != null) { - result.flow_ = passwordBuilder_.build(); + if (flowCase_ == 5 && + deviceCodeBuilder_ != null) { + result.flow_ = deviceCodeBuilder_.build(); } } @@ -612,12 +545,8 @@ public Builder mergeFrom(io.a2a.grpc.OAuthFlows other) { mergeClientCredentials(other.getClientCredentials()); break; } - case IMPLICIT: { - mergeImplicit(other.getImplicit()); - break; - } - case PASSWORD: { - mergePassword(other.getPassword()); + case DEVICE_CODE: { + mergeDeviceCode(other.getDeviceCode()); break; } case FLOW_NOT_SET: { @@ -664,20 +593,13 @@ public Builder mergeFrom( flowCase_ = 2; break; } // case 18 - case 26: { - input.readMessage( - internalGetImplicitFieldBuilder().getBuilder(), - extensionRegistry); - flowCase_ = 3; - break; - } // case 26 - case 34: { + case 42: { input.readMessage( - internalGetPasswordFieldBuilder().getBuilder(), + internalGetDeviceCodeFieldBuilder().getBuilder(), extensionRegistry); - flowCase_ = 4; + flowCase_ = 5; break; - } // case 34 + } // case 42 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -1067,359 +989,181 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui } private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> implicitBuilder_; - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - * @return Whether the implicit field is set. - */ - @java.lang.Override - public boolean hasImplicit() { - return flowCase_ == 3; - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - * @return The implicit. - */ - @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlow getImplicit() { - if (implicitBuilder_ == null) { - if (flowCase_ == 3) { - return (io.a2a.grpc.ImplicitOAuthFlow) flow_; - } - return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); - } else { - if (flowCase_ == 3) { - return implicitBuilder_.getMessage(); - } - return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); - } - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { - if (implicitBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - flow_ = value; - onChanged(); - } else { - implicitBuilder_.setMessage(value); - } - flowCase_ = 3; - return this; - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - public Builder setImplicit( - io.a2a.grpc.ImplicitOAuthFlow.Builder builderForValue) { - if (implicitBuilder_ == null) { - flow_ = builderForValue.build(); - onChanged(); - } else { - implicitBuilder_.setMessage(builderForValue.build()); - } - flowCase_ = 3; - return this; - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) { - if (implicitBuilder_ == null) { - if (flowCase_ == 3 && - flow_ != io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance()) { - flow_ = io.a2a.grpc.ImplicitOAuthFlow.newBuilder((io.a2a.grpc.ImplicitOAuthFlow) flow_) - .mergeFrom(value).buildPartial(); - } else { - flow_ = value; - } - onChanged(); - } else { - if (flowCase_ == 3) { - implicitBuilder_.mergeFrom(value); - } else { - implicitBuilder_.setMessage(value); - } - } - flowCase_ = 3; - return this; - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - public Builder clearImplicit() { - if (implicitBuilder_ == null) { - if (flowCase_ == 3) { - flowCase_ = 0; - flow_ = null; - onChanged(); - } - } else { - if (flowCase_ == 3) { - flowCase_ = 0; - flow_ = null; - } - implicitBuilder_.clear(); - } - return this; - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - public io.a2a.grpc.ImplicitOAuthFlow.Builder getImplicitBuilder() { - return internalGetImplicitFieldBuilder().getBuilder(); - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - @java.lang.Override - public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() { - if ((flowCase_ == 3) && (implicitBuilder_ != null)) { - return implicitBuilder_.getMessageOrBuilder(); - } else { - if (flowCase_ == 3) { - return (io.a2a.grpc.ImplicitOAuthFlow) flow_; - } - return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); - } - } - /** - *
    -     * Configuration for the OAuth Implicit flow.
    -     * 
    - * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - */ - private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> - internalGetImplicitFieldBuilder() { - if (implicitBuilder_ == null) { - if (!(flowCase_ == 3)) { - flow_ = io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); - } - implicitBuilder_ = new com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder>( - (io.a2a.grpc.ImplicitOAuthFlow) flow_, - getParentForChildren(), - isClean()); - flow_ = null; - } - flowCase_ = 3; - onChanged(); - return implicitBuilder_; - } - - private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> passwordBuilder_; + io.a2a.grpc.DeviceCodeOAuthFlow, io.a2a.grpc.DeviceCodeOAuthFlow.Builder, io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder> deviceCodeBuilder_; /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; - * @return Whether the password field is set. + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; + * @return Whether the deviceCode field is set. */ @java.lang.Override - public boolean hasPassword() { - return flowCase_ == 4; + public boolean hasDeviceCode() { + return flowCase_ == 5; } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; - * @return The password. + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; + * @return The deviceCode. */ @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlow getPassword() { - if (passwordBuilder_ == null) { - if (flowCase_ == 4) { - return (io.a2a.grpc.PasswordOAuthFlow) flow_; + public io.a2a.grpc.DeviceCodeOAuthFlow getDeviceCode() { + if (deviceCodeBuilder_ == null) { + if (flowCase_ == 5) { + return (io.a2a.grpc.DeviceCodeOAuthFlow) flow_; } - return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + return io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } else { - if (flowCase_ == 4) { - return passwordBuilder_.getMessage(); + if (flowCase_ == 5) { + return deviceCodeBuilder_.getMessage(); } - return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + return io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ - public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) { - if (passwordBuilder_ == null) { + public Builder setDeviceCode(io.a2a.grpc.DeviceCodeOAuthFlow value) { + if (deviceCodeBuilder_ == null) { if (value == null) { throw new NullPointerException(); } flow_ = value; onChanged(); } else { - passwordBuilder_.setMessage(value); + deviceCodeBuilder_.setMessage(value); } - flowCase_ = 4; + flowCase_ = 5; return this; } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ - public Builder setPassword( - io.a2a.grpc.PasswordOAuthFlow.Builder builderForValue) { - if (passwordBuilder_ == null) { + public Builder setDeviceCode( + io.a2a.grpc.DeviceCodeOAuthFlow.Builder builderForValue) { + if (deviceCodeBuilder_ == null) { flow_ = builderForValue.build(); onChanged(); } else { - passwordBuilder_.setMessage(builderForValue.build()); + deviceCodeBuilder_.setMessage(builderForValue.build()); } - flowCase_ = 4; + flowCase_ = 5; return this; } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ - public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) { - if (passwordBuilder_ == null) { - if (flowCase_ == 4 && - flow_ != io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance()) { - flow_ = io.a2a.grpc.PasswordOAuthFlow.newBuilder((io.a2a.grpc.PasswordOAuthFlow) flow_) + public Builder mergeDeviceCode(io.a2a.grpc.DeviceCodeOAuthFlow value) { + if (deviceCodeBuilder_ == null) { + if (flowCase_ == 5 && + flow_ != io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance()) { + flow_ = io.a2a.grpc.DeviceCodeOAuthFlow.newBuilder((io.a2a.grpc.DeviceCodeOAuthFlow) flow_) .mergeFrom(value).buildPartial(); } else { flow_ = value; } onChanged(); } else { - if (flowCase_ == 4) { - passwordBuilder_.mergeFrom(value); + if (flowCase_ == 5) { + deviceCodeBuilder_.mergeFrom(value); } else { - passwordBuilder_.setMessage(value); + deviceCodeBuilder_.setMessage(value); } } - flowCase_ = 4; + flowCase_ = 5; return this; } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ - public Builder clearPassword() { - if (passwordBuilder_ == null) { - if (flowCase_ == 4) { + public Builder clearDeviceCode() { + if (deviceCodeBuilder_ == null) { + if (flowCase_ == 5) { flowCase_ = 0; flow_ = null; onChanged(); } } else { - if (flowCase_ == 4) { + if (flowCase_ == 5) { flowCase_ = 0; flow_ = null; } - passwordBuilder_.clear(); + deviceCodeBuilder_.clear(); } return this; } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ - public io.a2a.grpc.PasswordOAuthFlow.Builder getPasswordBuilder() { - return internalGetPasswordFieldBuilder().getBuilder(); + public io.a2a.grpc.DeviceCodeOAuthFlow.Builder getDeviceCodeBuilder() { + return internalGetDeviceCodeFieldBuilder().getBuilder(); } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ @java.lang.Override - public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() { - if ((flowCase_ == 4) && (passwordBuilder_ != null)) { - return passwordBuilder_.getMessageOrBuilder(); + public io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder getDeviceCodeOrBuilder() { + if ((flowCase_ == 5) && (deviceCodeBuilder_ != null)) { + return deviceCodeBuilder_.getMessageOrBuilder(); } else { - if (flowCase_ == 4) { - return (io.a2a.grpc.PasswordOAuthFlow) flow_; + if (flowCase_ == 5) { + return (io.a2a.grpc.DeviceCodeOAuthFlow) flow_; } - return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + return io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } } /** *
    -     * Configuration for the OAuth Resource Owner Password flow.
    +     * Configuration for the OAuth Device Code flow.
          * 
    * - * .a2a.v1.PasswordOAuthFlow password = 4; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> - internalGetPasswordFieldBuilder() { - if (passwordBuilder_ == null) { - if (!(flowCase_ == 4)) { - flow_ = io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + io.a2a.grpc.DeviceCodeOAuthFlow, io.a2a.grpc.DeviceCodeOAuthFlow.Builder, io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder> + internalGetDeviceCodeFieldBuilder() { + if (deviceCodeBuilder_ == null) { + if (!(flowCase_ == 5)) { + flow_ = io.a2a.grpc.DeviceCodeOAuthFlow.getDefaultInstance(); } - passwordBuilder_ = new com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder>( - (io.a2a.grpc.PasswordOAuthFlow) flow_, + deviceCodeBuilder_ = new com.google.protobuf.SingleFieldBuilder< + io.a2a.grpc.DeviceCodeOAuthFlow, io.a2a.grpc.DeviceCodeOAuthFlow.Builder, io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder>( + (io.a2a.grpc.DeviceCodeOAuthFlow) flow_, getParentForChildren(), isClean()); flow_ = null; } - flowCase_ = 4; + flowCase_ = 5; onChanged(); - return passwordBuilder_; + return deviceCodeBuilder_; } // @@protoc_insertion_point(builder_scope:a2a.v1.OAuthFlows) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java index 1bdc0f5e0..cb84fa83e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java @@ -66,57 +66,30 @@ public interface OAuthFlowsOrBuilder extends /** *
    -   * Configuration for the OAuth Implicit flow.
    +   * Configuration for the OAuth Device Code flow.
        * 
    * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - * @return Whether the implicit field is set. + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; + * @return Whether the deviceCode field is set. */ - boolean hasImplicit(); + boolean hasDeviceCode(); /** *
    -   * Configuration for the OAuth Implicit flow.
    +   * Configuration for the OAuth Device Code flow.
        * 
    * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; - * @return The implicit. + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; + * @return The deviceCode. */ - io.a2a.grpc.ImplicitOAuthFlow getImplicit(); + io.a2a.grpc.DeviceCodeOAuthFlow getDeviceCode(); /** *
    -   * Configuration for the OAuth Implicit flow.
    +   * Configuration for the OAuth Device Code flow.
        * 
    * - * .a2a.v1.ImplicitOAuthFlow implicit = 3; + * .a2a.v1.DeviceCodeOAuthFlow device_code = 5; */ - io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder(); - - /** - *
    -   * Configuration for the OAuth Resource Owner Password flow.
    -   * 
    - * - * .a2a.v1.PasswordOAuthFlow password = 4; - * @return Whether the password field is set. - */ - boolean hasPassword(); - /** - *
    -   * Configuration for the OAuth Resource Owner Password flow.
    -   * 
    - * - * .a2a.v1.PasswordOAuthFlow password = 4; - * @return The password. - */ - io.a2a.grpc.PasswordOAuthFlow getPassword(); - /** - *
    -   * Configuration for the OAuth Resource Owner Password flow.
    -   * 
    - * - * .a2a.v1.PasswordOAuthFlow password = 4; - */ - io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder(); + io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder getDeviceCodeOrBuilder(); io.a2a.grpc.OAuthFlows.FlowCase getFlowCase(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index 457c55b1f..6bec1fb51 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -146,7 +146,10 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu private int historyLength_ = 0; /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve in
    +   * the response. An unset value means the client does not impose any limit. A
    +   * value of zero is a request to not include any messages. The server MUST NOT
    +   * return more messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 3; @@ -158,7 +161,10 @@ public boolean hasHistoryLength() { } /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve in
    +   * the response. An unset value means the client does not impose any limit. A
    +   * value of zero is a request to not include any messages. The server MUST NOT
    +   * return more messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 3; @@ -903,7 +909,10 @@ public io.a2a.grpc.PushNotificationConfigOrBuilder getPushNotificationConfigOrBu private int historyLength_ ; /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve in
    +     * the response. An unset value means the client does not impose any limit. A
    +     * value of zero is a request to not include any messages. The server MUST NOT
    +     * return more messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 3; @@ -915,7 +924,10 @@ public boolean hasHistoryLength() { } /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve in
    +     * the response. An unset value means the client does not impose any limit. A
    +     * value of zero is a request to not include any messages. The server MUST NOT
    +     * return more messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 3; @@ -927,7 +939,10 @@ public int getHistoryLength() { } /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve in
    +     * the response. An unset value means the client does not impose any limit. A
    +     * value of zero is a request to not include any messages. The server MUST NOT
    +     * return more messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 3; @@ -943,7 +958,10 @@ public Builder setHistoryLength(int value) { } /** *
    -     * The maximum number of messages to include in the history.
    +     * The maximum number of most recent messages from the task's history to retrieve in
    +     * the response. An unset value means the client does not impose any limit. A
    +     * value of zero is a request to not include any messages. The server MUST NOT
    +     * return more messages than the provided value, but MAY apply a lower limit.
          * 
    * * optional int32 history_length = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java index cfbab940c..2fed6a6af 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -80,7 +80,10 @@ public interface SendMessageConfigurationOrBuilder extends /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve in
    +   * the response. An unset value means the client does not impose any limit. A
    +   * value of zero is a request to not include any messages. The server MUST NOT
    +   * return more messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 3; @@ -89,7 +92,10 @@ public interface SendMessageConfigurationOrBuilder extends boolean hasHistoryLength(); /** *
    -   * The maximum number of messages to include in the history.
    +   * The maximum number of most recent messages from the task's history to retrieve in
    +   * the response. An unset value means the client does not impose any limit. A
    +   * value of zero is a request to not include any messages. The server MUST NOT
    +   * return more messages than the provided value, but MAY apply a lower limit.
        * 
    * * optional int32 history_length = 3; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index 41ba38002..caaf9740e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -98,18 +98,18 @@ public java.lang.String getTenant() { } } - public static final int REQUEST_FIELD_NUMBER = 1; - private io.a2a.grpc.Message request_; + public static final int MESSAGE_FIELD_NUMBER = 1; + private io.a2a.grpc.Message message_; /** *
        * The message to send to the agent.
        * 
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; - * @return Whether the request field is set. + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return Whether the message field is set. */ @java.lang.Override - public boolean hasRequest() { + public boolean hasMessage() { return ((bitField0_ & 0x00000001) != 0); } /** @@ -117,23 +117,23 @@ public boolean hasRequest() { * The message to send to the agent. *
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; - * @return The request. + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The message. */ @java.lang.Override - public io.a2a.grpc.Message getRequest() { - return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; + public io.a2a.grpc.Message getMessage() { + return message_ == null ? io.a2a.grpc.Message.getDefaultInstance() : message_; } /** *
        * The message to send to the agent.
        * 
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override - public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { - return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; + public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() { + return message_ == null ? io.a2a.grpc.Message.getDefaultInstance() : message_; } public static final int CONFIGURATION_FIELD_NUMBER = 2; @@ -227,7 +227,7 @@ public final boolean isInitialized() { public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(1, getRequest()); + output.writeMessage(1, getMessage()); } if (((bitField0_ & 0x00000002) != 0)) { output.writeMessage(2, getConfiguration()); @@ -249,7 +249,7 @@ public int getSerializedSize() { size = 0; if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, getRequest()); + .computeMessageSize(1, getMessage()); } if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream @@ -279,10 +279,10 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (hasRequest() != other.hasRequest()) return false; - if (hasRequest()) { - if (!getRequest() - .equals(other.getRequest())) return false; + if (hasMessage() != other.hasMessage()) return false; + if (hasMessage()) { + if (!getMessage() + .equals(other.getMessage())) return false; } if (hasConfiguration() != other.hasConfiguration()) return false; if (hasConfiguration()) { @@ -307,9 +307,9 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - if (hasRequest()) { - hash = (37 * hash) + REQUEST_FIELD_NUMBER; - hash = (53 * hash) + getRequest().hashCode(); + if (hasMessage()) { + hash = (37 * hash) + MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getMessage().hashCode(); } if (hasConfiguration()) { hash = (37 * hash) + CONFIGURATION_FIELD_NUMBER; @@ -455,7 +455,7 @@ private Builder( private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage .alwaysUseFieldBuilders) { - internalGetRequestFieldBuilder(); + internalGetMessageFieldBuilder(); internalGetConfigurationFieldBuilder(); internalGetMetadataFieldBuilder(); } @@ -465,10 +465,10 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - request_ = null; - if (requestBuilder_ != null) { - requestBuilder_.dispose(); - requestBuilder_ = null; + message_ = null; + if (messageBuilder_ != null) { + messageBuilder_.dispose(); + messageBuilder_ = null; } configuration_ = null; if (configurationBuilder_ != null) { @@ -518,9 +518,9 @@ private void buildPartial0(io.a2a.grpc.SendMessageRequest result) { } int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000002) != 0)) { - result.request_ = requestBuilder_ == null - ? request_ - : requestBuilder_.build(); + result.message_ = messageBuilder_ == null + ? message_ + : messageBuilder_.build(); to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000004) != 0)) { @@ -555,8 +555,8 @@ public Builder mergeFrom(io.a2a.grpc.SendMessageRequest other) { bitField0_ |= 0x00000001; onChanged(); } - if (other.hasRequest()) { - mergeRequest(other.getRequest()); + if (other.hasMessage()) { + mergeMessage(other.getMessage()); } if (other.hasConfiguration()) { mergeConfiguration(other.getConfiguration()); @@ -592,7 +592,7 @@ public Builder mergeFrom( break; case 10: { input.readMessage( - internalGetRequestFieldBuilder().getBuilder(), + internalGetMessageFieldBuilder().getBuilder(), extensionRegistry); bitField0_ |= 0x00000002; break; @@ -725,18 +725,18 @@ public Builder setTenantBytes( return this; } - private io.a2a.grpc.Message request_; + private io.a2a.grpc.Message message_; private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> requestBuilder_; + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> messageBuilder_; /** *
          * The message to send to the agent.
          * 
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; - * @return Whether the request field is set. + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return Whether the message field is set. */ - public boolean hasRequest() { + public boolean hasMessage() { return ((bitField0_ & 0x00000002) != 0); } /** @@ -744,14 +744,14 @@ public boolean hasRequest() { * The message to send to the agent. *
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; - * @return The request. + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The message. */ - public io.a2a.grpc.Message getRequest() { - if (requestBuilder_ == null) { - return request_ == null ? io.a2a.grpc.Message.getDefaultInstance() : request_; + public io.a2a.grpc.Message getMessage() { + if (messageBuilder_ == null) { + return message_ == null ? io.a2a.grpc.Message.getDefaultInstance() : message_; } else { - return requestBuilder_.getMessage(); + return messageBuilder_.getMessage(); } } /** @@ -759,16 +759,16 @@ public io.a2a.grpc.Message getRequest() { * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - public Builder setRequest(io.a2a.grpc.Message value) { - if (requestBuilder_ == null) { + public Builder setMessage(io.a2a.grpc.Message value) { + if (messageBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - request_ = value; + message_ = value; } else { - requestBuilder_.setMessage(value); + messageBuilder_.setMessage(value); } bitField0_ |= 0x00000002; onChanged(); @@ -779,14 +779,14 @@ public Builder setRequest(io.a2a.grpc.Message value) { * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - public Builder setRequest( + public Builder setMessage( io.a2a.grpc.Message.Builder builderForValue) { - if (requestBuilder_ == null) { - request_ = builderForValue.build(); + if (messageBuilder_ == null) { + message_ = builderForValue.build(); } else { - requestBuilder_.setMessage(builderForValue.build()); + messageBuilder_.setMessage(builderForValue.build()); } bitField0_ |= 0x00000002; onChanged(); @@ -797,21 +797,21 @@ public Builder setRequest( * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - public Builder mergeRequest(io.a2a.grpc.Message value) { - if (requestBuilder_ == null) { + public Builder mergeMessage(io.a2a.grpc.Message value) { + if (messageBuilder_ == null) { if (((bitField0_ & 0x00000002) != 0) && - request_ != null && - request_ != io.a2a.grpc.Message.getDefaultInstance()) { - getRequestBuilder().mergeFrom(value); + message_ != null && + message_ != io.a2a.grpc.Message.getDefaultInstance()) { + getMessageBuilder().mergeFrom(value); } else { - request_ = value; + message_ = value; } } else { - requestBuilder_.mergeFrom(value); + messageBuilder_.mergeFrom(value); } - if (request_ != null) { + if (message_ != null) { bitField0_ |= 0x00000002; onChanged(); } @@ -822,14 +822,14 @@ public Builder mergeRequest(io.a2a.grpc.Message value) { * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - public Builder clearRequest() { + public Builder clearMessage() { bitField0_ = (bitField0_ & ~0x00000002); - request_ = null; - if (requestBuilder_ != null) { - requestBuilder_.dispose(); - requestBuilder_ = null; + message_ = null; + if (messageBuilder_ != null) { + messageBuilder_.dispose(); + messageBuilder_ = null; } onChanged(); return this; @@ -839,26 +839,26 @@ public Builder clearRequest() { * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - public io.a2a.grpc.Message.Builder getRequestBuilder() { + public io.a2a.grpc.Message.Builder getMessageBuilder() { bitField0_ |= 0x00000002; onChanged(); - return internalGetRequestFieldBuilder().getBuilder(); + return internalGetMessageFieldBuilder().getBuilder(); } /** *
          * The message to send to the agent.
          * 
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { - if (requestBuilder_ != null) { - return requestBuilder_.getMessageOrBuilder(); + public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() { + if (messageBuilder_ != null) { + return messageBuilder_.getMessageOrBuilder(); } else { - return request_ == null ? - io.a2a.grpc.Message.getDefaultInstance() : request_; + return message_ == null ? + io.a2a.grpc.Message.getDefaultInstance() : message_; } } /** @@ -866,20 +866,20 @@ public io.a2a.grpc.MessageOrBuilder getRequestOrBuilder() { * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> - internalGetRequestFieldBuilder() { - if (requestBuilder_ == null) { - requestBuilder_ = new com.google.protobuf.SingleFieldBuilder< + internalGetMessageFieldBuilder() { + if (messageBuilder_ == null) { + messageBuilder_ = new com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( - getRequest(), + getMessage(), getParentForChildren(), isClean()); - request_ = null; + message_ = null; } - return requestBuilder_; + return messageBuilder_; } private io.a2a.grpc.SendMessageConfiguration configuration_; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java index 5040d6562..de2e6cc12 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequestOrBuilder.java @@ -35,27 +35,27 @@ public interface SendMessageRequestOrBuilder extends * The message to send to the agent. * * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; - * @return Whether the request field is set. + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return Whether the message field is set. */ - boolean hasRequest(); + boolean hasMessage(); /** *
        * The message to send to the agent.
        * 
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; - * @return The request. + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The message. */ - io.a2a.grpc.Message getRequest(); + io.a2a.grpc.Message getMessage(); /** *
        * The message to send to the agent.
        * 
    * - * .a2a.v1.Message request = 1 [json_name = "message", (.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.Message message = 1 [(.google.api.field_behavior) = REQUIRED]; */ - io.a2a.grpc.MessageOrBuilder getRequestOrBuilder(); + io.a2a.grpc.MessageOrBuilder getMessageOrBuilder(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java
    index 0fe457f59..466f2206a 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponse.java
    @@ -55,7 +55,7 @@ public enum PayloadCase
           implements com.google.protobuf.Internal.EnumLite,
               com.google.protobuf.AbstractMessage.InternalOneOfEnum {
         TASK(1),
    -    MSG(2),
    +    MESSAGE(2),
         PAYLOAD_NOT_SET(0);
         private final int value;
         private PayloadCase(int value) {
    @@ -74,7 +74,7 @@ public static PayloadCase valueOf(int value) {
         public static PayloadCase forNumber(int value) {
           switch (value) {
             case 1: return TASK;
    -        case 2: return MSG;
    +        case 2: return MESSAGE;
             case 0: return PAYLOAD_NOT_SET;
             default: return null;
           }
    @@ -121,31 +121,31 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() {
         return io.a2a.grpc.Task.getDefaultInstance();
       }
     
    -  public static final int MSG_FIELD_NUMBER = 2;
    +  public static final int MESSAGE_FIELD_NUMBER = 2;
       /**
    -   * .a2a.v1.Message msg = 2 [json_name = "message"];
    -   * @return Whether the msg field is set.
    +   * .a2a.v1.Message message = 2;
    +   * @return Whether the message field is set.
        */
       @java.lang.Override
    -  public boolean hasMsg() {
    +  public boolean hasMessage() {
         return payloadCase_ == 2;
       }
       /**
    -   * .a2a.v1.Message msg = 2 [json_name = "message"];
    -   * @return The msg.
    +   * .a2a.v1.Message message = 2;
    +   * @return The message.
        */
       @java.lang.Override
    -  public io.a2a.grpc.Message getMsg() {
    +  public io.a2a.grpc.Message getMessage() {
         if (payloadCase_ == 2) {
            return (io.a2a.grpc.Message) payload_;
         }
         return io.a2a.grpc.Message.getDefaultInstance();
       }
       /**
    -   * .a2a.v1.Message msg = 2 [json_name = "message"];
    +   * .a2a.v1.Message message = 2;
        */
       @java.lang.Override
    -  public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() {
    +  public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() {
         if (payloadCase_ == 2) {
            return (io.a2a.grpc.Message) payload_;
         }
    @@ -211,8 +211,8 @@ public boolean equals(final java.lang.Object obj) {
                 .equals(other.getTask())) return false;
             break;
           case 2:
    -        if (!getMsg()
    -            .equals(other.getMsg())) return false;
    +        if (!getMessage()
    +            .equals(other.getMessage())) return false;
             break;
           case 0:
           default:
    @@ -234,8 +234,8 @@ public int hashCode() {
             hash = (53 * hash) + getTask().hashCode();
             break;
           case 2:
    -        hash = (37 * hash) + MSG_FIELD_NUMBER;
    -        hash = (53 * hash) + getMsg().hashCode();
    +        hash = (37 * hash) + MESSAGE_FIELD_NUMBER;
    +        hash = (53 * hash) + getMessage().hashCode();
             break;
           case 0:
           default:
    @@ -379,8 +379,8 @@ public Builder clear() {
           if (taskBuilder_ != null) {
             taskBuilder_.clear();
           }
    -      if (msgBuilder_ != null) {
    -        msgBuilder_.clear();
    +      if (messageBuilder_ != null) {
    +        messageBuilder_.clear();
           }
           payloadCase_ = 0;
           payload_ = null;
    @@ -428,8 +428,8 @@ private void buildPartialOneofs(io.a2a.grpc.SendMessageResponse result) {
             result.payload_ = taskBuilder_.build();
           }
           if (payloadCase_ == 2 &&
    -          msgBuilder_ != null) {
    -        result.payload_ = msgBuilder_.build();
    +          messageBuilder_ != null) {
    +        result.payload_ = messageBuilder_.build();
           }
         }
     
    @@ -450,8 +450,8 @@ public Builder mergeFrom(io.a2a.grpc.SendMessageResponse other) {
               mergeTask(other.getTask());
               break;
             }
    -        case MSG: {
    -          mergeMsg(other.getMsg());
    +        case MESSAGE: {
    +          mergeMessage(other.getMessage());
               break;
             }
             case PAYLOAD_NOT_SET: {
    @@ -493,7 +493,7 @@ public Builder mergeFrom(
                 } // case 10
                 case 18: {
                   input.readMessage(
    -                  internalGetMsgFieldBuilder().getBuilder(),
    +                  internalGetMessageFieldBuilder().getBuilder(),
                       extensionRegistry);
                   payloadCase_ = 2;
                   break;
    @@ -673,68 +673,68 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() {
         }
     
         private com.google.protobuf.SingleFieldBuilder<
    -        io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> msgBuilder_;
    +        io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> messageBuilder_;
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    -     * @return Whether the msg field is set.
    +     * .a2a.v1.Message message = 2;
    +     * @return Whether the message field is set.
          */
         @java.lang.Override
    -    public boolean hasMsg() {
    +    public boolean hasMessage() {
           return payloadCase_ == 2;
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    -     * @return The msg.
    +     * .a2a.v1.Message message = 2;
    +     * @return The message.
          */
         @java.lang.Override
    -    public io.a2a.grpc.Message getMsg() {
    -      if (msgBuilder_ == null) {
    +    public io.a2a.grpc.Message getMessage() {
    +      if (messageBuilder_ == null) {
             if (payloadCase_ == 2) {
               return (io.a2a.grpc.Message) payload_;
             }
             return io.a2a.grpc.Message.getDefaultInstance();
           } else {
             if (payloadCase_ == 2) {
    -          return msgBuilder_.getMessage();
    +          return messageBuilder_.getMessage();
             }
             return io.a2a.grpc.Message.getDefaultInstance();
           }
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
    -    public Builder setMsg(io.a2a.grpc.Message value) {
    -      if (msgBuilder_ == null) {
    +    public Builder setMessage(io.a2a.grpc.Message value) {
    +      if (messageBuilder_ == null) {
             if (value == null) {
               throw new NullPointerException();
             }
             payload_ = value;
             onChanged();
           } else {
    -        msgBuilder_.setMessage(value);
    +        messageBuilder_.setMessage(value);
           }
           payloadCase_ = 2;
           return this;
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
    -    public Builder setMsg(
    +    public Builder setMessage(
             io.a2a.grpc.Message.Builder builderForValue) {
    -      if (msgBuilder_ == null) {
    +      if (messageBuilder_ == null) {
             payload_ = builderForValue.build();
             onChanged();
           } else {
    -        msgBuilder_.setMessage(builderForValue.build());
    +        messageBuilder_.setMessage(builderForValue.build());
           }
           payloadCase_ = 2;
           return this;
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
    -    public Builder mergeMsg(io.a2a.grpc.Message value) {
    -      if (msgBuilder_ == null) {
    +    public Builder mergeMessage(io.a2a.grpc.Message value) {
    +      if (messageBuilder_ == null) {
             if (payloadCase_ == 2 &&
                 payload_ != io.a2a.grpc.Message.getDefaultInstance()) {
               payload_ = io.a2a.grpc.Message.newBuilder((io.a2a.grpc.Message) payload_)
    @@ -745,19 +745,19 @@ public Builder mergeMsg(io.a2a.grpc.Message value) {
             onChanged();
           } else {
             if (payloadCase_ == 2) {
    -          msgBuilder_.mergeFrom(value);
    +          messageBuilder_.mergeFrom(value);
             } else {
    -          msgBuilder_.setMessage(value);
    +          messageBuilder_.setMessage(value);
             }
           }
           payloadCase_ = 2;
           return this;
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
    -    public Builder clearMsg() {
    -      if (msgBuilder_ == null) {
    +    public Builder clearMessage() {
    +      if (messageBuilder_ == null) {
             if (payloadCase_ == 2) {
               payloadCase_ = 0;
               payload_ = null;
    @@ -768,23 +768,23 @@ public Builder clearMsg() {
               payloadCase_ = 0;
               payload_ = null;
             }
    -        msgBuilder_.clear();
    +        messageBuilder_.clear();
           }
           return this;
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
    -    public io.a2a.grpc.Message.Builder getMsgBuilder() {
    -      return internalGetMsgFieldBuilder().getBuilder();
    +    public io.a2a.grpc.Message.Builder getMessageBuilder() {
    +      return internalGetMessageFieldBuilder().getBuilder();
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
         @java.lang.Override
    -    public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() {
    -      if ((payloadCase_ == 2) && (msgBuilder_ != null)) {
    -        return msgBuilder_.getMessageOrBuilder();
    +    public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() {
    +      if ((payloadCase_ == 2) && (messageBuilder_ != null)) {
    +        return messageBuilder_.getMessageOrBuilder();
           } else {
             if (payloadCase_ == 2) {
               return (io.a2a.grpc.Message) payload_;
    @@ -793,16 +793,16 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() {
           }
         }
         /**
    -     * .a2a.v1.Message msg = 2 [json_name = "message"];
    +     * .a2a.v1.Message message = 2;
          */
         private com.google.protobuf.SingleFieldBuilder<
             io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> 
    -        internalGetMsgFieldBuilder() {
    -      if (msgBuilder_ == null) {
    +        internalGetMessageFieldBuilder() {
    +      if (messageBuilder_ == null) {
             if (!(payloadCase_ == 2)) {
               payload_ = io.a2a.grpc.Message.getDefaultInstance();
             }
    -        msgBuilder_ = new com.google.protobuf.SingleFieldBuilder<
    +        messageBuilder_ = new com.google.protobuf.SingleFieldBuilder<
                 io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>(
                     (io.a2a.grpc.Message) payload_,
                     getParentForChildren(),
    @@ -811,7 +811,7 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() {
           }
           payloadCase_ = 2;
           onChanged();
    -      return msgBuilder_;
    +      return messageBuilder_;
         }
     
         // @@protoc_insertion_point(builder_scope:a2a.v1.SendMessageResponse)
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java
    index 40a838920..355131b71 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageResponseOrBuilder.java
    @@ -26,19 +26,19 @@ public interface SendMessageResponseOrBuilder extends
       io.a2a.grpc.TaskOrBuilder getTaskOrBuilder();
     
       /**
    -   * .a2a.v1.Message msg = 2 [json_name = "message"];
    -   * @return Whether the msg field is set.
    +   * .a2a.v1.Message message = 2;
    +   * @return Whether the message field is set.
        */
    -  boolean hasMsg();
    +  boolean hasMessage();
       /**
    -   * .a2a.v1.Message msg = 2 [json_name = "message"];
    -   * @return The msg.
    +   * .a2a.v1.Message message = 2;
    +   * @return The message.
        */
    -  io.a2a.grpc.Message getMsg();
    +  io.a2a.grpc.Message getMessage();
       /**
    -   * .a2a.v1.Message msg = 2 [json_name = "message"];
    +   * .a2a.v1.Message message = 2;
        */
    -  io.a2a.grpc.MessageOrBuilder getMsgOrBuilder();
    +  io.a2a.grpc.MessageOrBuilder getMessageOrBuilder();
     
       io.a2a.grpc.SendMessageResponse.PayloadCase getPayloadCase();
     }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java
    index 139650483..3d27db5ef 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponse.java
    @@ -55,7 +55,7 @@ public enum PayloadCase
           implements com.google.protobuf.Internal.EnumLite,
               com.google.protobuf.AbstractMessage.InternalOneOfEnum {
         TASK(1),
    -    MSG(2),
    +    MESSAGE(2),
         STATUS_UPDATE(3),
         ARTIFACT_UPDATE(4),
         PAYLOAD_NOT_SET(0);
    @@ -76,7 +76,7 @@ public static PayloadCase valueOf(int value) {
         public static PayloadCase forNumber(int value) {
           switch (value) {
             case 1: return TASK;
    -        case 2: return MSG;
    +        case 2: return MESSAGE;
             case 3: return STATUS_UPDATE;
             case 4: return ARTIFACT_UPDATE;
             case 0: return PAYLOAD_NOT_SET;
    @@ -137,17 +137,17 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() {
         return io.a2a.grpc.Task.getDefaultInstance();
       }
     
    -  public static final int MSG_FIELD_NUMBER = 2;
    +  public static final int MESSAGE_FIELD_NUMBER = 2;
       /**
        * 
        * A Message object containing a message from the agent.
        * 
    * - * .a2a.v1.Message msg = 2 [json_name = "message"]; - * @return Whether the msg field is set. + * .a2a.v1.Message message = 2; + * @return Whether the message field is set. */ @java.lang.Override - public boolean hasMsg() { + public boolean hasMessage() { return payloadCase_ == 2; } /** @@ -155,11 +155,11 @@ public boolean hasMsg() { * A Message object containing a message from the agent. *
    * - * .a2a.v1.Message msg = 2 [json_name = "message"]; - * @return The msg. + * .a2a.v1.Message message = 2; + * @return The message. */ @java.lang.Override - public io.a2a.grpc.Message getMsg() { + public io.a2a.grpc.Message getMessage() { if (payloadCase_ == 2) { return (io.a2a.grpc.Message) payload_; } @@ -170,10 +170,10 @@ public io.a2a.grpc.Message getMsg() { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ @java.lang.Override - public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { + public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() { if (payloadCase_ == 2) { return (io.a2a.grpc.Message) payload_; } @@ -339,8 +339,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getTask())) return false; break; case 2: - if (!getMsg() - .equals(other.getMsg())) return false; + if (!getMessage() + .equals(other.getMessage())) return false; break; case 3: if (!getStatusUpdate() @@ -370,8 +370,8 @@ public int hashCode() { hash = (53 * hash) + getTask().hashCode(); break; case 2: - hash = (37 * hash) + MSG_FIELD_NUMBER; - hash = (53 * hash) + getMsg().hashCode(); + hash = (37 * hash) + MESSAGE_FIELD_NUMBER; + hash = (53 * hash) + getMessage().hashCode(); break; case 3: hash = (37 * hash) + STATUS_UPDATE_FIELD_NUMBER; @@ -523,8 +523,8 @@ public Builder clear() { if (taskBuilder_ != null) { taskBuilder_.clear(); } - if (msgBuilder_ != null) { - msgBuilder_.clear(); + if (messageBuilder_ != null) { + messageBuilder_.clear(); } if (statusUpdateBuilder_ != null) { statusUpdateBuilder_.clear(); @@ -578,8 +578,8 @@ private void buildPartialOneofs(io.a2a.grpc.StreamResponse result) { result.payload_ = taskBuilder_.build(); } if (payloadCase_ == 2 && - msgBuilder_ != null) { - result.payload_ = msgBuilder_.build(); + messageBuilder_ != null) { + result.payload_ = messageBuilder_.build(); } if (payloadCase_ == 3 && statusUpdateBuilder_ != null) { @@ -608,8 +608,8 @@ public Builder mergeFrom(io.a2a.grpc.StreamResponse other) { mergeTask(other.getTask()); break; } - case MSG: { - mergeMsg(other.getMsg()); + case MESSAGE: { + mergeMessage(other.getMessage()); break; } case STATUS_UPDATE: { @@ -659,7 +659,7 @@ public Builder mergeFrom( } // case 10 case 18: { input.readMessage( - internalGetMsgFieldBuilder().getBuilder(), + internalGetMessageFieldBuilder().getBuilder(), extensionRegistry); payloadCase_ = 2; break; @@ -889,17 +889,17 @@ public io.a2a.grpc.TaskOrBuilder getTaskOrBuilder() { } private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> msgBuilder_; + io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> messageBuilder_; /** *
          * A Message object containing a message from the agent.
          * 
    * - * .a2a.v1.Message msg = 2 [json_name = "message"]; - * @return Whether the msg field is set. + * .a2a.v1.Message message = 2; + * @return Whether the message field is set. */ @java.lang.Override - public boolean hasMsg() { + public boolean hasMessage() { return payloadCase_ == 2; } /** @@ -907,19 +907,19 @@ public boolean hasMsg() { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; - * @return The msg. + * .a2a.v1.Message message = 2; + * @return The message. */ @java.lang.Override - public io.a2a.grpc.Message getMsg() { - if (msgBuilder_ == null) { + public io.a2a.grpc.Message getMessage() { + if (messageBuilder_ == null) { if (payloadCase_ == 2) { return (io.a2a.grpc.Message) payload_; } return io.a2a.grpc.Message.getDefaultInstance(); } else { if (payloadCase_ == 2) { - return msgBuilder_.getMessage(); + return messageBuilder_.getMessage(); } return io.a2a.grpc.Message.getDefaultInstance(); } @@ -929,17 +929,17 @@ public io.a2a.grpc.Message getMsg() { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder setMsg(io.a2a.grpc.Message value) { - if (msgBuilder_ == null) { + public Builder setMessage(io.a2a.grpc.Message value) { + if (messageBuilder_ == null) { if (value == null) { throw new NullPointerException(); } payload_ = value; onChanged(); } else { - msgBuilder_.setMessage(value); + messageBuilder_.setMessage(value); } payloadCase_ = 2; return this; @@ -949,15 +949,15 @@ public Builder setMsg(io.a2a.grpc.Message value) { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder setMsg( + public Builder setMessage( io.a2a.grpc.Message.Builder builderForValue) { - if (msgBuilder_ == null) { + if (messageBuilder_ == null) { payload_ = builderForValue.build(); onChanged(); } else { - msgBuilder_.setMessage(builderForValue.build()); + messageBuilder_.setMessage(builderForValue.build()); } payloadCase_ = 2; return this; @@ -967,10 +967,10 @@ public Builder setMsg( * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder mergeMsg(io.a2a.grpc.Message value) { - if (msgBuilder_ == null) { + public Builder mergeMessage(io.a2a.grpc.Message value) { + if (messageBuilder_ == null) { if (payloadCase_ == 2 && payload_ != io.a2a.grpc.Message.getDefaultInstance()) { payload_ = io.a2a.grpc.Message.newBuilder((io.a2a.grpc.Message) payload_) @@ -981,9 +981,9 @@ public Builder mergeMsg(io.a2a.grpc.Message value) { onChanged(); } else { if (payloadCase_ == 2) { - msgBuilder_.mergeFrom(value); + messageBuilder_.mergeFrom(value); } else { - msgBuilder_.setMessage(value); + messageBuilder_.setMessage(value); } } payloadCase_ = 2; @@ -994,10 +994,10 @@ public Builder mergeMsg(io.a2a.grpc.Message value) { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public Builder clearMsg() { - if (msgBuilder_ == null) { + public Builder clearMessage() { + if (messageBuilder_ == null) { if (payloadCase_ == 2) { payloadCase_ = 0; payload_ = null; @@ -1008,7 +1008,7 @@ public Builder clearMsg() { payloadCase_ = 0; payload_ = null; } - msgBuilder_.clear(); + messageBuilder_.clear(); } return this; } @@ -1017,22 +1017,22 @@ public Builder clearMsg() { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - public io.a2a.grpc.Message.Builder getMsgBuilder() { - return internalGetMsgFieldBuilder().getBuilder(); + public io.a2a.grpc.Message.Builder getMessageBuilder() { + return internalGetMessageFieldBuilder().getBuilder(); } /** *
          * A Message object containing a message from the agent.
          * 
    * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ @java.lang.Override - public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { - if ((payloadCase_ == 2) && (msgBuilder_ != null)) { - return msgBuilder_.getMessageOrBuilder(); + public io.a2a.grpc.MessageOrBuilder getMessageOrBuilder() { + if ((payloadCase_ == 2) && (messageBuilder_ != null)) { + return messageBuilder_.getMessageOrBuilder(); } else { if (payloadCase_ == 2) { return (io.a2a.grpc.Message) payload_; @@ -1045,16 +1045,16 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder> - internalGetMsgFieldBuilder() { - if (msgBuilder_ == null) { + internalGetMessageFieldBuilder() { + if (messageBuilder_ == null) { if (!(payloadCase_ == 2)) { payload_ = io.a2a.grpc.Message.getDefaultInstance(); } - msgBuilder_ = new com.google.protobuf.SingleFieldBuilder< + messageBuilder_ = new com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.Message, io.a2a.grpc.Message.Builder, io.a2a.grpc.MessageOrBuilder>( (io.a2a.grpc.Message) payload_, getParentForChildren(), @@ -1063,7 +1063,7 @@ public io.a2a.grpc.MessageOrBuilder getMsgOrBuilder() { } payloadCase_ = 2; onChanged(); - return msgBuilder_; + return messageBuilder_; } private com.google.protobuf.SingleFieldBuilder< diff --git a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java index d6596f26d..5688ed06b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/StreamResponseOrBuilder.java @@ -42,27 +42,27 @@ public interface StreamResponseOrBuilder extends * A Message object containing a message from the agent. * * - * .a2a.v1.Message msg = 2 [json_name = "message"]; - * @return Whether the msg field is set. + * .a2a.v1.Message message = 2; + * @return Whether the message field is set. */ - boolean hasMsg(); + boolean hasMessage(); /** *
        * A Message object containing a message from the agent.
        * 
    * - * .a2a.v1.Message msg = 2 [json_name = "message"]; - * @return The msg. + * .a2a.v1.Message message = 2; + * @return The message. */ - io.a2a.grpc.Message getMsg(); + io.a2a.grpc.Message getMessage(); /** *
        * A Message object containing a message from the agent.
        * 
    * - * .a2a.v1.Message msg = 2 [json_name = "message"]; + * .a2a.v1.Message message = 2; */ - io.a2a.grpc.MessageOrBuilder getMsgOrBuilder(); + io.a2a.grpc.MessageOrBuilder getMessageOrBuilder(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
    index 723df2e53..0acdc5d3e 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
    @@ -370,6 +370,44 @@ default Instant millisToInstant(long millis) {
             return millis > 0L ? Instant.ofEpochMilli(millis) : null;
         }
     
    +    // ========================================================================
    +    // Instant ↔ Timestamp Conversions (for Timestamp timestamp fields)
    +    // ========================================================================
    +    /**
    +     * Converts domain Instant to protobuf Timestamp.
    +     * 

    + * Use this with {@code @Mapping(qualifiedByName = "instantToProtoTimestamp")}. + * + * @param instant the domain Instant + * @return protobuf Timestamp, or default instance if input is null + */ + @Named("instantToProtoTimestamp") + default Timestamp instantToProtoTimestamp(Instant instant) { + if (instant == null) { + return Timestamp.getDefaultInstance(); + } + return Timestamp.newBuilder() + .setSeconds(instant.getEpochSecond()) + .setNanos(instant.getNano()) + .build(); + } + + /** + * Converts protobuf Timestamp to domain Instant. + *

    + * Use this with {@code @Mapping(qualifiedByName = "protoTimestampToInstant")}. + * + * @param timestamp the protobuf Timestamp + * @return Instant, or null if input is null/default + */ + @Named("protoTimestampToInstant") + default Instant protoTimestampToInstant(Timestamp timestamp) { + if (timestamp == null || timestamp.equals(Timestamp.getDefaultInstance())) { + return null; + } + return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); + } + // ======================================================================== // Enum Conversions (handling UNSPECIFIED/UNKNOWN) // ======================================================================== diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java index a8216fec8..337ea8de6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; /** * Mapper between {@link io.a2a.spec.AgentCapabilities} and {@link io.a2a.grpc.AgentCapabilities}. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java index 7a27cb57d..97b9ed0a1 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCardMapper.java @@ -22,10 +22,6 @@ public interface AgentCardMapper { AgentCardMapper INSTANCE = A2AMappers.getMapper(AgentCardMapper.class); - // Deprecated proto fields - not present in spec API (removed in 1.0.0) - @Mapping(target = "url", ignore = true) - @Mapping(target = "preferredTransport", ignore = true) - @Mapping(target = "additionalInterfaces", ignore = true) @Mapping(target = "provider", source = "provider", conditionExpression = "java(domain.provider() != null)") @Mapping(target = "documentationUrl", source = "documentationUrl", conditionExpression = "java(domain.documentationUrl() != null)") @Mapping(target = "iconUrl", source = "iconUrl", conditionExpression = "java(domain.iconUrl() != null)") diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java index 09fdc3184..d59a3e10a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AuthorizationCodeOAuthFlowMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; /** * Mapper between {@link io.a2a.spec.AuthorizationCodeOAuthFlow} and {@link io.a2a.grpc.AuthorizationCodeOAuthFlow}. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java deleted file mode 100644 index 99f0b7854..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ImplicitOAuthFlowMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.a2a.grpc.mapper; - -import org.mapstruct.CollectionMappingStrategy; -import org.mapstruct.Mapper; - -/** - * Mapper between {@link io.a2a.spec.ImplicitOAuthFlow} and {@link io.a2a.grpc.ImplicitOAuthFlow}. - */ -@Mapper(config = A2AProtoMapperConfig.class, - collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) -public interface ImplicitOAuthFlowMapper { - - ImplicitOAuthFlowMapper INSTANCE = A2AMappers.getMapper(ImplicitOAuthFlowMapper.class); - - io.a2a.grpc.ImplicitOAuthFlow toProto(io.a2a.spec.ImplicitOAuthFlow domain); - - io.a2a.spec.ImplicitOAuthFlow fromProto(io.a2a.grpc.ImplicitOAuthFlow proto); -} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java index cdb501d9a..06d4c2dd4 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/ListTasksParamsMapper.java @@ -26,7 +26,7 @@ public interface ListTasksParamsMapper { @Mapping(target = "pageSize", source = "pageSize", conditionExpression = "java(params.pageSize() != null)") @Mapping(target = "pageToken", source = "pageToken", conditionExpression = "java(params.pageToken() != null)") @Mapping(target = "historyLength", source = "historyLength", conditionExpression = "java(params.historyLength() != null)") - @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "instantToMillis") + @Mapping(target = "statusTimestampAfter", source = "statusTimestampAfter", qualifiedByName = "instantToProtoTimestamp") @Mapping(target = "includeArtifacts", source = "includeArtifacts", conditionExpression = "java(params.includeArtifacts() != null)") ListTasksRequest toProto(io.a2a.spec.ListTasksParams params); @@ -43,7 +43,7 @@ public interface ListTasksParamsMapper { @Mapping(target = "pageToken", source = "pageToken", qualifiedByName = "emptyToNull") // historyLength: Check if field is set using hasHistoryLength() for consistency with pageSize @Mapping(target = "historyLength", expression = "java(request.hasHistoryLength() ? request.getHistoryLength() : null)") - @Mapping(target = "lastUpdatedAfter", source = "lastUpdatedAfter", qualifiedByName = "millisToInstant") + @Mapping(target = "statusTimestampAfter", source = "statusTimestampAfter", qualifiedByName = "protoTimestampToInstant") @Mapping(target = "includeArtifacts", source = "includeArtifacts", qualifiedByName = "falseToNull") io.a2a.spec.ListTasksParams fromProto(ListTasksRequest request); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java index 759fc0fef..11885ac11 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/MessageSendParamsMapper.java @@ -18,20 +18,18 @@ public interface MessageSendParamsMapper { /** * Converts domain MessageSendParams to proto SendMessageRequest. - * Maps domain "message" field to proto "request" field. + * Maps domain "message" field to proto "message" field. */ - @Mapping(target = "request", source = "message") @Mapping(target = "configuration", source = "configuration", conditionExpression = "java(domain.configuration() != null)") @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataToProto") io.a2a.grpc.SendMessageRequest toProto(MessageSendParams domain); /** * Converts proto SendMessageRequest to domain MessageSendParams. - * Maps proto "request" field to domain "message" field. + * Maps proto "message" field to domain "message" field. * Uses Builder pattern for record construction. */ @BeanMapping(builder = @Builder(buildMethod = "build")) - @Mapping(target = "message", source = "request") @Mapping(target = "metadata", source = "metadata", qualifiedByName = "metadataFromProto") MessageSendParams fromProto(io.a2a.grpc.SendMessageRequest proto); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java index 6af0e28ce..e919b50d6 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/OAuthFlowsMapper.java @@ -2,6 +2,7 @@ import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; /** * Mapper between {@link io.a2a.spec.OAuthFlows} and {@link io.a2a.grpc.OAuthFlows}. @@ -10,9 +11,7 @@ collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, uses = { AuthorizationCodeOAuthFlowMapper.class, - ClientCredentialsOAuthFlowMapper.class, - ImplicitOAuthFlowMapper.class, - PasswordOAuthFlowMapper.class + ClientCredentialsOAuthFlowMapper.class }) public interface OAuthFlowsMapper { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java deleted file mode 100644 index 84b6e1bdf..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/PasswordOAuthFlowMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.a2a.grpc.mapper; - -import org.mapstruct.CollectionMappingStrategy; -import org.mapstruct.Mapper; - -/** - * Mapper between {@link io.a2a.spec.PasswordOAuthFlow} and {@link io.a2a.grpc.PasswordOAuthFlow}. - */ -@Mapper(config = A2AProtoMapperConfig.class, - collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) -public interface PasswordOAuthFlowMapper { - - PasswordOAuthFlowMapper INSTANCE = A2AMappers.getMapper(PasswordOAuthFlowMapper.class); - - io.a2a.grpc.PasswordOAuthFlow toProto(io.a2a.spec.PasswordOAuthFlow domain); - - io.a2a.spec.PasswordOAuthFlow fromProto(io.a2a.grpc.PasswordOAuthFlow proto); -} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java index 2b91beff0..07eacb148 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/StreamResponseMapper.java @@ -45,7 +45,7 @@ default io.a2a.grpc.StreamResponse toProto(StreamingEventKind domain) { .setTask(TaskMapper.INSTANCE.toProto((Task) domain)) .build(); case Message.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() - .setMsg(MessageMapper.INSTANCE.toProto((Message) domain)) + .setMessage(MessageMapper.INSTANCE.toProto((Message) domain)) .build(); case TaskStatusUpdateEvent.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() .setStatusUpdate(TaskStatusUpdateEventMapper.INSTANCE.toProto((TaskStatusUpdateEvent) domain)) @@ -74,8 +74,8 @@ default StreamingEventKind fromProto(io.a2a.grpc.StreamResponse proto) { return switch (proto.getPayloadCase()) { case TASK -> TaskMapper.INSTANCE.fromProto(proto.getTask()); - case MSG -> - MessageMapper.INSTANCE.fromProto(proto.getMsg()); + case MESSAGE -> + MessageMapper.INSTANCE.fromProto(proto.getMessage()); case STATUS_UPDATE -> TaskStatusUpdateEventMapper.INSTANCE.fromProto(proto.getStatusUpdate()); case ARTIFACT_UPDATE -> diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index c7d1df72f..1692ae3a5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -1,7 +1,7 @@ package io.a2a.grpc.utils; import static io.a2a.spec.A2AErrorCodes.CONTENT_TYPE_NOT_SUPPORTED_ERROR_CODE; -import static io.a2a.spec.A2AErrorCodes.EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.EXTENDED_AGENT_CARD_NOT_CONFIGURED_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.EXTENSION_SUPPORT_REQUIRED_ERROR; import static io.a2a.spec.A2AErrorCodes.INTERNAL_ERROR_CODE; import static io.a2a.spec.A2AErrorCodes.INVALID_AGENT_RESPONSE_ERROR_CODE; @@ -48,8 +48,8 @@ import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; 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.GetExtendedAgentCardRequest; +import io.a2a.jsonrpc.common.wrappers.GetExtendedAgentCardResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; @@ -66,7 +66,7 @@ import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.spec.A2AError; 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; @@ -245,7 +245,7 @@ private static A2ARequest parseMethodRequest(String version, Object id, Strin return new DeleteTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.deleteTaskPushNotificationConfigParams(builder)); } case GET_EXTENDED_AGENT_CARD_METHOD -> { - return new GetAuthenticatedExtendedCardRequest(version, id); + return new GetExtendedAgentCardRequest(version, id); } case SEND_STREAMING_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); @@ -314,8 +314,8 @@ public static A2AResponse parseResponseBody(String body, String method) throw case SEND_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageResponse.Builder builder = io.a2a.grpc.SendMessageResponse.newBuilder(); parseRequestBody(paramsNode, builder, id); - if (builder.hasMsg()) { - return new SendMessageResponse(id, ProtoUtils.FromProto.message(builder.getMsg())); + if (builder.hasMessage()) { + return new SendMessageResponse(id, ProtoUtils.FromProto.message(builder.getMessage())); } return new SendMessageResponse(id, ProtoUtils.FromProto.task(builder.getTask())); } @@ -330,7 +330,7 @@ public static A2AResponse parseResponseBody(String body, String method) throw case GET_EXTENDED_AGENT_CARD_METHOD -> { io.a2a.grpc.AgentCard.Builder builder = io.a2a.grpc.AgentCard.newBuilder(); parseRequestBody(paramsNode, builder, id); - return new GetAuthenticatedExtendedCardResponse(id, ProtoUtils.FromProto.agentCard(builder)); + return new GetExtendedAgentCardResponse(id, ProtoUtils.FromProto.agentCard(builder)); } default -> throw new MethodNotFoundJsonMappingException("Unsupported JSON-RPC method: '" + method + "' in response parsing.", getIdIfPossible(jsonRpc)); @@ -393,8 +393,8 @@ private static A2AError processError(JsonObject error) { return new ContentTypeNotSupportedError(code, message, data); case INVALID_AGENT_RESPONSE_ERROR_CODE: return new InvalidAgentResponseError(code, message, data); - case EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE: - return new ExtendedCardNotConfiguredError(code, message, data); + case EXTENDED_AGENT_CARD_NOT_CONFIGURED_ERROR_CODE: + return new ExtendedAgentCardNotConfiguredError(code, message, data); case EXTENSION_SUPPORT_REQUIRED_ERROR: return new ExtensionSupportRequiredError(code, message, data); case VERSION_NOT_SUPPORTED_ERROR_CODE: diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index b4e9a1755..b2941dc98 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -149,7 +149,7 @@ public static io.a2a.grpc.SendMessageResponse taskOrMessage(EventKind eventKind) .setTask(task((Task) eventKind)) .build(); case Message.STREAMING_EVENT_ID -> io.a2a.grpc.SendMessageResponse.newBuilder() - .setMsg(message((Message) eventKind)) + .setMessage(message((Message) eventKind)) .build(); default -> throw new IllegalArgumentException("Unsupported event type: " + eventKind); }; @@ -161,7 +161,7 @@ public static io.a2a.grpc.StreamResponse taskOrMessageStream(StreamingEventKind .setTask(task((Task) eventKind)) .build(); case Message.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() - .setMsg(message((Message) eventKind)) + .setMessage(message((Message) eventKind)) .build(); case TaskStatusUpdateEvent.STREAMING_EVENT_ID -> io.a2a.grpc.StreamResponse.newBuilder() .setStatusUpdate(taskStatusUpdateEvent((TaskStatusUpdateEvent) eventKind)) @@ -181,7 +181,7 @@ public static io.a2a.grpc.TaskPushNotificationConfig getTaskPushNotificationConf return taskPushNotificationConfig(config); } - public static io.a2a.grpc.AgentCard getAuthenticatedExtendedCardResponse(AgentCard card) { + public static io.a2a.grpc.AgentCard getExtendedCardResponse(AgentCard card) { return agentCard(card); } } diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index 888fd18b8..ccf37e6ab 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -9,8 +9,6 @@ import "google/protobuf/empty.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; -//From commit c196824396bb4af4c595f30e2c503a5ab1dbac4b - option csharp_namespace = "A2a.V1"; option go_package = "google.golang.org/a2a/v1"; option java_multiple_files = true; @@ -145,7 +143,10 @@ message SendMessageConfiguration { repeated string accepted_output_modes = 1; // Configuration for the agent to send push notifications for task updates. PushNotificationConfig push_notification_config = 2; - // The maximum number of messages to include in the history. + // The maximum number of most recent messages from the task's history to retrieve in + // the response. An unset value means the client does not impose any limit. A + // value of zero is a request to not include any messages. The server MUST NOT + // return more messages than the provided value, but MAY apply a lower limit. optional int32 history_length = 3; // If true, the operation waits until the task reaches a terminal state before returning. Default is false. bool blocking = 4; @@ -277,13 +278,12 @@ enum Role { // --8<-- [end:Role] // --8<-- [start:Message] -// Message is one unit of communication between client and server. It is -// associated with a context and optionally a task. Since the server is -// responsible for the context definition, it must always provide a context_id -// in its messages. The client can optionally provide the context_id if it -// knows the context to associate the message to. Similarly for task_id, -// except the server decides if a task is created and whether to include the -// task_id. +// Message is one unit of communication between client and server. It can be +// associated with a context and/or a task. For server messages, context_id must +// be provided, and task_id only if a task was created. For client messages, both +// fields are optional, with the caveat that if both are provided, they have to +// match (the context_id has to be the one that is set on the task). If only +// task_id is provided, the server will infer context_id from it. message Message { // The unique identifier (e.g. UUID) of the message. This is required and // created by the message creator. @@ -411,9 +411,14 @@ message AgentInterface { // communication methods, and security requirements. // Next ID: 20 message AgentCard { - // The version of the A2A protocol this agent supports. - // Default: "1.0" - optional string protocol_version = 16 [(google.api.field_behavior) = REQUIRED]; + // The versions of the A2A protocol this agent supports. + // For stable versions (1.x+), list only the latest supported minor version per major version. + // For legacy experimental versions (0.x), explicitly list each supported version. + // Default: ["1.0"] + repeated string protocol_versions = 16 [(google.api.field_behavior) = REQUIRED]; + // Reserve these field numbers as they were previously used by removed + // fields. + reserved 3, 14, 15; // A human readable name for the agent. // Example: "Recipe Agent" string name = 1 [(google.api.field_behavior) = REQUIRED]; @@ -422,13 +427,7 @@ message AgentCard { // Example: "Agent that helps users with recipes and cooking." string description = 2 [(google.api.field_behavior) = REQUIRED]; // Ordered list of supported interfaces. First entry is preferred. - repeated AgentInterface supported_interfaces = 19; - // DEPRECATED: Use 'supported_interfaces' instead. - optional string url = 3 [deprecated = true]; - // DEPRECATED: Use 'supported_interfaces' instead. - optional string preferred_transport = 14 [deprecated = true]; - // DEPRECATED: Use 'supported_interfaces' instead. - repeated AgentInterface additional_interfaces = 15 [deprecated = true]; + repeated AgentInterface supported_interfaces = 19 [(google.api.field_behavior) = REQUIRED]; // The service provider of the agent. AgentProvider provider = 4; // The version of the agent. @@ -453,8 +452,6 @@ message AgentCard { // a descriptive concept but represents a more focused set of behaviors that the // agent is likely to succeed at. repeated AgentSkill skills = 12 [(google.api.field_behavior) = REQUIRED]; - // Whether the agent supports providing an extended agent card when authenticated. - optional bool supports_extended_agent_card = 13; // JSON Web Signatures computed for this AgentCard. repeated AgentCardSignature signatures = 17; // An optional URL to an icon for the agent. @@ -485,6 +482,8 @@ message AgentCapabilities { repeated AgentExtension extensions = 3; // Indicates if the agent provides a history of state transitions for a task. optional bool state_transition_history = 4; + // Indicates if the agent supports providing an extended agent card when authenticated. + optional bool extended_agent_card = 5; } // --8<-- [end:AgentCapabilities] @@ -644,15 +643,15 @@ message MutualTlsSecurityScheme { // --8<-- [start:OAuthFlows] // Defines the configuration for the supported OAuth 2.0 flows. message OAuthFlows { + // Tags 3 and 4 were previously used by deprecated OAuth flows. + reserved 3, 4; oneof flow { // Configuration for the OAuth Authorization Code flow. AuthorizationCodeOAuthFlow authorization_code = 1; // Configuration for the OAuth Client Credentials flow. ClientCredentialsOAuthFlow client_credentials = 2; - // Configuration for the OAuth Implicit flow. - ImplicitOAuthFlow implicit = 3; - // Configuration for the OAuth Resource Owner Password flow. - PasswordOAuthFlow password = 4; + // Configuration for the OAuth Device Code flow. + DeviceCodeOAuthFlow device_code = 5; } } // --8<-- [end:OAuthFlows] @@ -668,6 +667,9 @@ message AuthorizationCodeOAuthFlow { string refresh_url = 3; // The available scopes for the OAuth2 security scheme. map scopes = 4 [(google.api.field_behavior) = REQUIRED]; + // Indicates if PKCE (RFC 7636) is required for this flow. + // PKCE should always be used for public clients and is recommended for all clients. + bool pkce_required = 5; } // --8<-- [end:AuthorizationCodeOAuthFlow] @@ -683,29 +685,21 @@ message ClientCredentialsOAuthFlow { } // --8<-- [end:ClientCredentialsOAuthFlow] -// --8<-- [start:ImplicitOAuthFlow] -// Defines configuration details for the OAuth 2.0 Implicit flow. -message ImplicitOAuthFlow { - // The authorization URL to be used for this flow. - string authorization_url = 1 [(google.api.field_behavior) = REQUIRED]; - // The URL to be used for obtaining refresh tokens. - string refresh_url = 2; - // The available scopes for the OAuth2 security scheme. - map scopes = 3 [(google.api.field_behavior) = REQUIRED]; -} -// --8<-- [end:ImplicitOAuthFlow] - -// --8<-- [start:PasswordOAuthFlow] -// Defines configuration details for the OAuth 2.0 Resource Owner Password flow. -message PasswordOAuthFlow { +// --8<-- [start:DeviceCodeOAuthFlow] +// Defines configuration details for the OAuth 2.0 Device Code flow (RFC 8628). +// This flow is designed for input-constrained devices such as IoT devices, +// and CLI tools where the user authenticates on a separate device. +message DeviceCodeOAuthFlow { + // The device authorization endpoint URL. + string device_authorization_url = 1 [(google.api.field_behavior) = REQUIRED]; // The token URL to be used for this flow. - string token_url = 1 [(google.api.field_behavior) = REQUIRED]; + string token_url = 2 [(google.api.field_behavior) = REQUIRED]; // The URL to be used for obtaining refresh tokens. - string refresh_url = 2; + string refresh_url = 3; // The available scopes for the OAuth2 security scheme. - map scopes = 3 [(google.api.field_behavior) = REQUIRED]; + map scopes = 4 [(google.api.field_behavior) = REQUIRED]; } -// --8<-- [end:PasswordOAuthFlow] +// --8<-- [end:DeviceCodeOAuthFlow] ///////////// Request Messages /////////// // --8<-- [start:SendMessageRequest] @@ -714,10 +708,7 @@ message SendMessageRequest { // Optional tenant, provided as a path parameter. string tenant = 4; // The message to send to the agent. - Message request = 1 [ - (google.api.field_behavior) = REQUIRED, - json_name = "message" - ]; + Message message = 1 [(google.api.field_behavior) = REQUIRED]; // Configuration for the send request. SendMessageConfiguration configuration = 2; // A flexible key-value map for passing additional context or parameters. @@ -733,7 +724,10 @@ message GetTaskRequest { // The resource name of the task. // Format: tasks/{task_id} string name = 1 [(google.api.field_behavior) = REQUIRED]; - // The maximum number of messages to include in the history. + // The maximum number of most recent messages from the task's history to retrieve. An + // unset value means the client does not impose any limit. A value of zero is + // a request to not include any messages. The server MUST NOT return more + // messages than the provided value, but MAY apply a lower limit. optional int32 history_length = 2; } // --8<-- [end:GetTaskRequest] @@ -754,9 +748,9 @@ message ListTasksRequest { string page_token = 4; // The maximum number of messages to include in each task's history. optional int32 history_length = 5; - // Filter tasks updated after this timestamp (milliseconds since epoch). - // Only tasks with a last updated time greater than or equal to this value will be returned. - int64 last_updated_after = 6; + // Filter tasks which have a status updated after the provided timestamp in ISO 8601 format (e.g., "2023-10-27T10:00:00Z"). + // Only tasks with a status timestamp time greater than or equal to this value will be returned. + google.protobuf.Timestamp status_timestamp_after = 6; // Whether to include artifacts in the returned tasks. // Defaults to false to reduce payload size. optional bool include_artifacts = 7; @@ -861,7 +855,7 @@ message GetExtendedAgentCardRequest { message SendMessageResponse { oneof payload { Task task = 1; - Message msg = 2 [json_name = "message"]; + Message message = 2; } } // --8<-- [end:SendMessageResponse] @@ -873,7 +867,7 @@ message StreamResponse { // A Task object containing the current state of the task. Task task = 1; // A Message object containing a message from the agent. - Message msg = 2 [json_name = "message"]; + Message message = 2; // An event indicating a task status update. TaskStatusUpdateEvent status_update = 3; // An event indicating a task artifact update. @@ -892,4 +886,4 @@ message ListTaskPushNotificationConfigResponse { // If this field is omitted, there are no subsequent pages. string next_page_token = 2; } -// --8<-- [end:ListTaskPushNotificationConfigResponse] +// --8<-- [end:ListTaskPushNotificationConfigResponse] \ No newline at end of file diff --git a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java index 0619af352..bd007d61f 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/mapper/StreamResponseMapperTest.java @@ -80,17 +80,17 @@ void testConvertMessage_ToProto() { // Assert assertNotNull(result); - assertEquals(io.a2a.grpc.StreamResponse.PayloadCase.MSG, result.getPayloadCase()); - assertEquals("msg-123", result.getMsg().getMessageId()); - assertEquals("context-456", result.getMsg().getContextId()); - assertEquals(io.a2a.grpc.Role.ROLE_USER, result.getMsg().getRole()); + assertEquals(io.a2a.grpc.StreamResponse.PayloadCase.MESSAGE, result.getPayloadCase()); + assertEquals("msg-123", result.getMessage().getMessageId()); + assertEquals("context-456", result.getMessage().getContextId()); + assertEquals(io.a2a.grpc.Role.ROLE_USER, result.getMessage().getRole()); } @Test void testConvertMessage_FromProto() { // Arrange io.a2a.grpc.StreamResponse proto = io.a2a.grpc.StreamResponse.newBuilder() - .setMsg(io.a2a.grpc.Message.newBuilder() + .setMessage(io.a2a.grpc.Message.newBuilder() .setMessageId("msg-123") .setContextId("context-456") .setRole(io.a2a.grpc.Role.ROLE_USER) diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index ec6d1cd86..e0a67c9c8 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.OffsetDateTime; import java.util.Collections; @@ -65,7 +66,7 @@ public void convertAgentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion("123") // Weird protool version on purpose to make sure non-default takes effect + .protocolVersions("123") // Weird protool version on purpose to make sure non-default takes effect .build(); io.a2a.grpc.AgentCard result = ProtoUtils.ToProto.agentCard(agentCard); assertEquals("Hello World Agent", result.getName()); @@ -79,7 +80,8 @@ public void convertAgentCard() { assertEquals("text", result.getDefaultInputModes(0)); assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); - assertEquals("123", result.getProtocolVersion()); + // protocolVersions is now a repeated field, checking if the list contains the value + assertTrue(result.getProtocolVersionsList().contains("123")); agentCard = AgentCard.builder() .name("Hello World Agent") .description("Just a hello world agent") @@ -103,7 +105,7 @@ public void convertAgentCard() { // .iconUrl("http://example.com/icon.svg") .securitySchemes(Map.of("basic", HTTPAuthSecurityScheme.builder().scheme("basic").description("Basic Auth").build())) .security(List.of(Map.of("oauth", List.of("read")))) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); result = ProtoUtils.ToProto.agentCard(agentCard); assertEquals("Hello World Agent", result.getName()); @@ -117,7 +119,8 @@ public void convertAgentCard() { assertEquals("text", result.getDefaultInputModes(0)); assertEquals(1, result.getDefaultOutputModesCount()); assertEquals("text", result.getDefaultOutputModes(0)); - assertEquals(CURRENT_PROTOCOL_VERSION, result.getProtocolVersion()); + // protocolVersions is now a repeated field, checking if the list contains the value + assertTrue(result.getProtocolVersionsList().contains(CURRENT_PROTOCOL_VERSION)); assertEquals(1, result.getSecurityCount()); assertEquals(1, result.getSecurity(0).getSchemesMap().size()); assertEquals(true, result.getSecurity(0).getSchemesMap().containsKey("oauth")); diff --git a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java index 9c1b4c88a..8eb4c6584 100644 --- a/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java +++ b/spec/src/main/java/io/a2a/spec/A2AErrorCodes.java @@ -23,8 +23,8 @@ public interface A2AErrorCodes { /** Error code indicating the agent returned an invalid response (-32006). */ int INVALID_AGENT_RESPONSE_ERROR_CODE = -32006; - /** Error code indicating extended card is not configured (-32007). */ - int EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE = -32007; + /** Error code indicating extended agent card is not configured (-32007). */ + int EXTENDED_AGENT_CARD_NOT_CONFIGURED_ERROR_CODE = -32007; /** Error code indicating client requested use of an extension marked as required: true in the Agent Card * but the client did not declare support for it in the request (-32008). */ diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java index 2913a2b97..ae8fb6f01 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java @@ -26,12 +26,16 @@ * @param streaming whether the agent supports streaming responses with incremental artifacts * @param pushNotifications whether the agent supports push notifications for state changes * @param stateTransitionHistory whether the agent maintains state transition history + * @param extendedAgentCard whether the agent supports an extended agent card * @param extensions list of custom extensions supported by the agent (optional) * @see AgentCard * @see AgentExtension * @see A2A Protocol Specification */ -public record AgentCapabilities(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory, +public record AgentCapabilities(boolean streaming, + boolean pushNotifications, + boolean stateTransitionHistory, + boolean extendedAgentCard, List extensions) { /** @@ -62,6 +66,7 @@ public static class Builder { private boolean streaming; private boolean pushNotifications; private boolean stateTransitionHistory; + private boolean extendedAgentCard; private List extensions; /** @@ -112,6 +117,18 @@ public Builder stateTransitionHistory(boolean stateTransitionHistory) { return this; } + /** + * Sets whether the agent supports an extended agent card. + * state transition history. + * + * @param extendedAgentCard true if an extended agent card is supported, false otherwise + * @return this builder for method chaining + */ + public Builder extendedAgentCard(boolean extendedAgentCard) { + this.extendedAgentCard = extendedAgentCard; + return this; + } + /** * Sets the list of custom extensions supported by the agent. *

    @@ -133,7 +150,7 @@ public Builder extensions(List extensions) { * @return a new AgentCapabilities instance */ public AgentCapabilities build() { - return new AgentCapabilities(streaming, pushNotifications, stateTransitionHistory, extensions); + return new AgentCapabilities(streaming, pushNotifications, stateTransitionHistory, extendedAgentCard, extensions); } } } diff --git a/spec/src/main/java/io/a2a/spec/AgentCard.java b/spec/src/main/java/io/a2a/spec/AgentCard.java index d8ed1231a..f5a642509 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCard.java +++ b/spec/src/main/java/io/a2a/spec/AgentCard.java @@ -32,12 +32,11 @@ * @param defaultInputModes list of supported input modes, e.g., "text", "audio" (required) * @param defaultOutputModes list of supported output modes, e.g., "text", "audio" (required) * @param skills list of skills that this agent can perform (required) - * @param supportsExtendedAgentCard whether the agent supports authenticated extended card retrieval (optional, defaults to false) * @param securitySchemes map of security scheme names to their definitions (optional) * @param security list of security requirements for accessing the agent (optional) * @param iconUrl URL to an icon representing the agent (optional) * @param supportedInterfaces ordered list of protocol+URL interface combinations; first entry is preferred (required) - * @param protocolVersion the version of the A2A Protocol this agent implements (defaults to {@link #CURRENT_PROTOCOL_VERSION}) + * @param protocolVersions the versions of the A2A Protocol this agent implements (defaults to a singleton list of {@link #CURRENT_PROTOCOL_VERSION}) * @param signatures digital signatures verifying the authenticity of the agent card (optional) * @see AgentInterface * @see A2A Protocol Specification @@ -52,12 +51,11 @@ public record AgentCard( List defaultInputModes, List defaultOutputModes, List skills, - boolean supportsExtendedAgentCard, Map securitySchemes, List>> security, String iconUrl, List supportedInterfaces, - String protocolVersion, + List protocolVersions, List signatures) { /** The default A2A Protocol version used when not explicitly specified. */ @@ -75,12 +73,11 @@ public record AgentCard( * @param defaultInputModes the defaultInputModes parameter (see class-level JavaDoc) * @param defaultOutputModes the defaultOutputModes parameter (see class-level JavaDoc) * @param skills the skills parameter (see class-level JavaDoc) - * @param supportsExtendedAgentCard the supportsExtendedAgentCard parameter (see class-level JavaDoc) * @param securitySchemes the securitySchemes parameter (see class-level JavaDoc) * @param security the security parameter (see class-level JavaDoc) * @param iconUrl the iconUrl parameter (see class-level JavaDoc) * @param supportedInterfaces the supportedInterfaces parameter (see class-level JavaDoc) - * @param protocolVersion the protocolVersion parameter (see class-level JavaDoc) + * @param protocolVersions the protocolVersions parameter (see class-level JavaDoc) * @param signatures the signatures parameter (see class-level JavaDoc) * @throws IllegalArgumentException if any required field is null */ @@ -94,8 +91,8 @@ public record AgentCard( Assert.checkNotNullParam("supportedInterfaces", supportedInterfaces); Assert.checkNotNullParam("version", version); - if (protocolVersion == null) { - protocolVersion = CURRENT_PROTOCOL_VERSION; + if (protocolVersions == null || protocolVersions.isEmpty()) { + protocolVersions = List.of(CURRENT_PROTOCOL_VERSION); } } @@ -162,12 +159,11 @@ public static class Builder { private List defaultInputModes; private List defaultOutputModes; private List skills; - private boolean supportsExtendedAgentCard = false; private Map securitySchemes; private List>> security; private String iconUrl; private List supportedInterfaces; - private String protocolVersion; + private List protocolVersions; private List signatures; /** @@ -195,12 +191,11 @@ private Builder(AgentCard card) { this.defaultInputModes = card.defaultInputModes != null ? new ArrayList<>(card.defaultInputModes) : null; this.defaultOutputModes = card.defaultOutputModes != null ? new ArrayList<>(card.defaultOutputModes) : null; this.skills = card.skills != null ? new ArrayList<>(card.skills) : null; - this.supportsExtendedAgentCard = card.supportsExtendedAgentCard; this.securitySchemes = card.securitySchemes != null ? Map.copyOf(card.securitySchemes) : null; this.security = card.security != null ? new ArrayList<>(card.security) : null; this.iconUrl = card.iconUrl; this.supportedInterfaces = card.supportedInterfaces != null ? new ArrayList<>(card.supportedInterfaces) : null; - this.protocolVersion = card.protocolVersion; + this.protocolVersions = card.protocolVersions; this.signatures = card.signatures != null ? new ArrayList<>(card.signatures) : null; } @@ -316,21 +311,6 @@ public Builder skills(List skills) { return this; } - /** - * Sets whether the agent supports extended card retrieval. - *

    - * When true, the agent can provide additional information through the - * {@code GetAuthenticatedExtendedCard} method, which may include private - * or user-specific details not in the public card. - * - * @param supportsExtendedAgentCard true if supported, false otherwise - * @return this builder for method chaining - */ - public Builder supportsExtendedAgentCard(boolean supportsExtendedAgentCard) { - this.supportsExtendedAgentCard = supportsExtendedAgentCard; - return this; - } - /** * Sets the map of security scheme definitions. *

    @@ -398,13 +378,13 @@ public Builder supportedInterfaces(List supportedInterfaces) { /** * Sets the version of the A2A Protocol this agent implements. *

    - * If not set, defaults to {@link AgentCard#CURRENT_PROTOCOL_VERSION}. + * If not set, defaults to a single list of {@link AgentCard#CURRENT_PROTOCOL_VERSION}. * - * @param protocolVersion the protocol version string + * @param protocolVersions the protocol versions * @return this builder for method chaining */ - public Builder protocolVersion(String protocolVersion) { - this.protocolVersion = protocolVersion; + public Builder protocolVersions(String... protocolVersions) { + this.protocolVersions = List.of(protocolVersions); return this; } @@ -434,8 +414,8 @@ public Builder signatures(List signatures) { public AgentCard build() { return new AgentCard(name, description, provider, version, documentationUrl, capabilities, defaultInputModes, defaultOutputModes, skills, - supportsExtendedAgentCard, securitySchemes, security, iconUrl, - supportedInterfaces, protocolVersion, signatures); + securitySchemes, security, iconUrl, + supportedInterfaces, protocolVersions, signatures); } } } diff --git a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java index c4db756a4..d3d399057 100644 --- a/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java +++ b/spec/src/main/java/io/a2a/spec/AuthorizationCodeOAuthFlow.java @@ -19,13 +19,14 @@ * @param refreshUrl URL for obtaining refresh tokens (optional) * @param scopes map of available OAuth scopes to their descriptions (required) * @param tokenUrl URL for the token endpoint where codes are exchanged for tokens (required) + * @param pkceRequired Indicates if PKCE (RFC 7636) is required for this flow. (required) * @see OAuthFlows for the container of all supported OAuth flows * @see OAuth2SecurityScheme for the security scheme using these flows * @see RFC 6749 - Authorization Code Grant * @see A2A Protocol Specification */ public record AuthorizationCodeOAuthFlow(String authorizationUrl, String refreshUrl, Map scopes, - String tokenUrl) { + String tokenUrl, boolean pkceRequired) { /** * Compact constructor that validates required fields. @@ -34,6 +35,7 @@ public record AuthorizationCodeOAuthFlow(String authorizationUrl, String refresh * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) * @param scopes the scopes parameter (see class-level JavaDoc) * @param tokenUrl the tokenUrl parameter (see class-level JavaDoc) + * @param pkceRequired Indicates if PKCE (RFC 7636) is required for this flow. (required) * @throws IllegalArgumentException if authorizationUrl, scopes, or tokenUrl is null */ public AuthorizationCodeOAuthFlow { diff --git a/spec/src/main/java/io/a2a/spec/DeviceCodeOAuthFlow.java b/spec/src/main/java/io/a2a/spec/DeviceCodeOAuthFlow.java new file mode 100644 index 000000000..d2d25c047 --- /dev/null +++ b/spec/src/main/java/io/a2a/spec/DeviceCodeOAuthFlow.java @@ -0,0 +1,37 @@ +package io.a2a.spec; + +import java.util.Map; + +import io.a2a.util.Assert; + +/** + * Configuration details for the OAuth 2.0 Device Code flow (RFC 8628). + *

    + * This flow is designed for input-constrained devices such as IoT devices, + * and CLI tools where the user authenticates on a separate device. + * + * @param deviceAuthorizationUrl the device authorization endpoint URL (required) + * @param tokenUrl URL for the token endpoint where credentials are exchanged for tokens (required) + * @param refreshUrl URL for obtaining refresh tokens (optional) + * @param scopes map of available OAuth scopes to their descriptions (required) + * @see OAuthFlows for the container of all supported OAuth flows + * @see OAuth2SecurityScheme for the security scheme using these flows + * @see A2A Protocol Specification + */ +public record DeviceCodeOAuthFlow(String deviceAuthorizationUrl, String tokenUrl, String refreshUrl, Map scopes) { + + /** + * Compact constructor that validates required fields. + * + * @param deviceAuthorizationUrl the device authorization endpoint URL (required) + * @param tokenUrl URL for the token endpoint where credentials are exchanged for tokens (required) + * @param refreshUrl URL for obtaining refresh tokens (optional) + * @param scopes map of available OAuth scopes to their descriptions (required) + * @throws IllegalArgumentException if authorizationUrl, scopes, or tokenUrl is null + */ + public DeviceCodeOAuthFlow { + Assert.checkNotNullParam("deviceAuthorizationUrl", deviceAuthorizationUrl); + Assert.checkNotNullParam("tokenUrl", tokenUrl); + Assert.checkNotNullParam("scopes", scopes); + } +} diff --git a/spec/src/main/java/io/a2a/spec/ExtendedCardNotConfiguredError.java b/spec/src/main/java/io/a2a/spec/ExtendedAgentCardNotConfiguredError.java similarity index 78% rename from spec/src/main/java/io/a2a/spec/ExtendedCardNotConfiguredError.java rename to spec/src/main/java/io/a2a/spec/ExtendedAgentCardNotConfiguredError.java index 3403b3ffd..c8998fe04 100644 --- a/spec/src/main/java/io/a2a/spec/ExtendedCardNotConfiguredError.java +++ b/spec/src/main/java/io/a2a/spec/ExtendedAgentCardNotConfiguredError.java @@ -1,6 +1,6 @@ package io.a2a.spec; -import static io.a2a.spec.A2AErrorCodes.EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE; +import static io.a2a.spec.A2AErrorCodes.EXTENDED_AGENT_CARD_NOT_CONFIGURED_ERROR_CODE; import static io.a2a.util.Utils.defaultIfNull; @@ -19,15 +19,15 @@ * Usage example: *

    {@code
      * // In agent implementation
    - * if (authenticatedExtendedCard == null) {
    - *     throw new AuthenticatedExtendedCardNotConfiguredError();
    + * if (extendedAgentCard == null) {
    + *     throw new ExtendedAgentCardNotConfiguredError();
      * }
      * }
    * * @see AgentCard for the base agent card structure * @see A2A Protocol Specification */ -public class ExtendedCardNotConfiguredError extends A2AProtocolError { +public class ExtendedAgentCardNotConfiguredError extends A2AProtocolError { /** * Constructs an error for agents that don't support authenticated extended card retrieval. @@ -36,12 +36,12 @@ public class ExtendedCardNotConfiguredError extends A2AProtocolError { * @param message the error message * @param data additional error data */ - public ExtendedCardNotConfiguredError( + public ExtendedAgentCardNotConfiguredError( Integer code, String message, Object data) { super( - defaultIfNull(code, EXTENDED_CARD_NOT_CONFIGURED_ERROR_CODE), + defaultIfNull(code, EXTENDED_AGENT_CARD_NOT_CONFIGURED_ERROR_CODE), defaultIfNull(message, "Extended Card not configured"), data, "https://a2a-protocol.org/errors/extended-agent-card-not-configured"); diff --git a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java b/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java deleted file mode 100644 index 06341ab2b..000000000 --- a/spec/src/main/java/io/a2a/spec/ImplicitOAuthFlow.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.a2a.spec; - -import java.util.Map; - -import io.a2a.util.Assert; - -/** - * Configuration for the OAuth 2.0 Implicit flow. - *

    - * The implicit flow is designed for browser-based applications where the client - * cannot securely store credentials. The access token is returned directly from - * the authorization endpoint without an intermediate authorization code. - *

    - * Note: The implicit flow is considered less secure than the - * authorization code flow and is deprecated in OAuth 2.1. It should only be used - * for legacy applications or when the authorization code flow with PKCE is not feasible. - * - * @param authorizationUrl URL for the authorization endpoint where users authenticate (required) - * @param refreshUrl URL for obtaining refresh tokens (optional, rarely used in implicit flow) - * @param scopes map of available OAuth scopes to their descriptions (required) - * @see OAuthFlows for the container of all supported OAuth flows - * @see OAuth2SecurityScheme for the security scheme using these flows - * @see RFC 6749 - Implicit Grant - * @see A2A Protocol Specification - */ -public record ImplicitOAuthFlow(String authorizationUrl, String refreshUrl, Map scopes) { - - /** - * Compact constructor that validates required fields. - * - * @param authorizationUrl the authorizationUrl parameter (see class-level JavaDoc) - * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) - * @param scopes the scopes parameter (see class-level JavaDoc) - * @throws IllegalArgumentException if authorizationUrl or scopes is null - */ - public ImplicitOAuthFlow { - Assert.checkNotNullParam("authorizationUrl", authorizationUrl); - Assert.checkNotNullParam("scopes", scopes); - } -} diff --git a/spec/src/main/java/io/a2a/spec/ListTasksParams.java b/spec/src/main/java/io/a2a/spec/ListTasksParams.java index 6fb9ff5d1..d99d25bcf 100644 --- a/spec/src/main/java/io/a2a/spec/ListTasksParams.java +++ b/spec/src/main/java/io/a2a/spec/ListTasksParams.java @@ -12,7 +12,7 @@ * @param pageSize Maximum number of tasks to return (1-100, defaults to 50) * @param pageToken Token for pagination from a previous ListTasksResult * @param historyLength Number of recent messages to include in each task's history (defaults to 0) - * @param lastUpdatedAfter Filter tasks updated after this timestamp + * @param statusTimestampAfter Filter tasks updated after this timestamp * @param includeArtifacts Whether to include artifacts in the returned tasks (defaults to false) * @param tenant optional tenant, provided as a path parameter. */ @@ -22,7 +22,7 @@ public record ListTasksParams( @Nullable Integer pageSize, @Nullable String pageToken, @Nullable Integer historyLength, - @Nullable Instant lastUpdatedAfter, + @Nullable Instant statusTimestampAfter, @Nullable Boolean includeArtifacts, String tenant ) { @@ -38,7 +38,7 @@ public record ListTasksParams( * @param pageSize maximum number of results per page * @param pageToken pagination token * @param historyLength number of history items to include - * @param lastUpdatedAfter filter by last update timestamp + * @param statusTimestampAfter filter by status timestamp * @param includeArtifacts whether to include artifacts * @param tenant the tenant identifier * @throws InvalidParamsError if tenant is null or if pageSize or historyLength are out of valid range @@ -124,7 +124,7 @@ public static class Builder { private Integer pageSize; private String pageToken; private Integer historyLength; - private Instant lastUpdatedAfter; + private Instant statusTimestampAfter; private Boolean includeArtifacts; private String tenant; @@ -190,13 +190,13 @@ public Builder historyLength(Integer historyLength) { } /** - * Sets the lastUpdatedAfter. + * Sets the statusTimestampAfter. * - * @param lastUpdatedAfter the lastUpdatedAfter + * @param statusTimestampAfter the statusTimestampAfter * @return this builder for method chaining */ - public Builder lastUpdatedAfter(Instant lastUpdatedAfter) { - this.lastUpdatedAfter = lastUpdatedAfter; + public Builder statusTimestampAfter(Instant statusTimestampAfter) { + this.statusTimestampAfter = statusTimestampAfter; return this; } @@ -229,7 +229,7 @@ public Builder tenant(String tenant) { */ public ListTasksParams build() { return new ListTasksParams(contextId, status, pageSize, pageToken, historyLength, - lastUpdatedAfter, includeArtifacts, tenant); + statusTimestampAfter, includeArtifacts, tenant); } } } diff --git a/spec/src/main/java/io/a2a/spec/OAuthFlows.java b/spec/src/main/java/io/a2a/spec/OAuthFlows.java index bcddf7f43..5cabfcccd 100644 --- a/spec/src/main/java/io/a2a/spec/OAuthFlows.java +++ b/spec/src/main/java/io/a2a/spec/OAuthFlows.java @@ -10,14 +10,13 @@ * * @param authorizationCode OAuth 2.0 authorization code flow configuration * @param clientCredentials OAuth 2.0 client credentials flow configuration - * @param implicit OAuth 2.0 implicit flow configuration - * @param password OAuth 2.0 resource owner password credentials flow configuration + * @param deviceCode OAuth 2.0 device code flow configuration * @see OAuth2SecurityScheme for the security scheme using these flows * @see OpenAPI OAuth Flows Object * @see A2A Protocol Specification */ public record OAuthFlows(AuthorizationCodeOAuthFlow authorizationCode, ClientCredentialsOAuthFlow clientCredentials, - ImplicitOAuthFlow implicit, PasswordOAuthFlow password) { + DeviceCodeOAuthFlow deviceCode) { /** * Create a new Builder @@ -34,8 +33,7 @@ public static Builder builder() { public static class Builder { private AuthorizationCodeOAuthFlow authorizationCode; private ClientCredentialsOAuthFlow clientCredentials; - private ImplicitOAuthFlow implicit; - private PasswordOAuthFlow password; + private DeviceCodeOAuthFlow deviceCode; /** * Creates a new Builder with all fields unset. @@ -66,26 +64,16 @@ public Builder clientCredentials(ClientCredentialsOAuthFlow clientCredentials) { } /** - * Sets the implicit flow configuration. + * Sets the device code flow configuration. * - * @param implicit the implicit flow (optional) + * @param deviceCode the device code flow (optional) * @return this builder for method chaining */ - public Builder implicit(ImplicitOAuthFlow implicit) { - this.implicit = implicit; + public Builder deviceCode(DeviceCodeOAuthFlow deviceCode) { + this.deviceCode = deviceCode; return this; } - /** - * Sets the password flow configuration. - * - * @param password the password flow (optional) - * @return this builder for method chaining - */ - public Builder password(PasswordOAuthFlow password) { - this.password = password; - return this; - } /** * Builds a new immutable OAuthFlows instance. @@ -93,7 +81,7 @@ public Builder password(PasswordOAuthFlow password) { * @return a new OAuthFlows instance */ public OAuthFlows build() { - return new OAuthFlows(authorizationCode, clientCredentials, implicit, password); + return new OAuthFlows(authorizationCode, clientCredentials, deviceCode); } } } diff --git a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java b/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java deleted file mode 100644 index a6a3a8803..000000000 --- a/spec/src/main/java/io/a2a/spec/PasswordOAuthFlow.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.a2a.spec; - -import java.util.Map; - -import io.a2a.util.Assert; - -/** - * Configuration for the OAuth 2.0 Resource Owner Password Credentials flow. - *

    - * The password flow allows the client to exchange a user's credentials (username and password) - * directly for an access token. This flow should only be used when there is a high degree of - * trust between the user and the client application. - *

    - * Note: This flow is generally discouraged and deprecated in OAuth 2.1 - * because it exposes user credentials to the client. Use the authorization code flow - * with PKCE instead whenever possible. - * - * @param refreshUrl URL for obtaining refresh tokens (optional) - * @param scopes map of available OAuth scopes to their descriptions (required) - * @param tokenUrl URL for the token endpoint where credentials are exchanged for tokens (required) - * @see OAuthFlows for the container of all supported OAuth flows - * @see OAuth2SecurityScheme for the security scheme using these flows - * @see RFC 6749 - Resource Owner Password Credentials Grant - * @see A2A Protocol Specification - */ -public record PasswordOAuthFlow(String refreshUrl, Map scopes, String tokenUrl) { - - /** - * Compact constructor that validates required fields. - * - * @param refreshUrl the refreshUrl parameter (see class-level JavaDoc) - * @param scopes the scopes parameter (see class-level JavaDoc) - * @param tokenUrl the tokenUrl parameter (see class-level JavaDoc) - * @throws IllegalArgumentException if scopes or tokenUrl is null - */ - public PasswordOAuthFlow { - Assert.checkNotNullParam("scopes", scopes); - Assert.checkNotNullParam("tokenUrl", tokenUrl); - } -} diff --git a/spec/src/main/java/io/a2a/spec/VersionNotSupportedError.java b/spec/src/main/java/io/a2a/spec/VersionNotSupportedError.java index 50c8f3a45..955709986 100644 --- a/spec/src/main/java/io/a2a/spec/VersionNotSupportedError.java +++ b/spec/src/main/java/io/a2a/spec/VersionNotSupportedError.java @@ -23,7 +23,7 @@ * } * }

    * - * @see AgentCard#protocolVersion() for supported version declaration + * @see AgentCard#protocolVersions() for supported version declaration * @see A2A Protocol Specification */ public class VersionNotSupportedError extends A2AProtocolError { diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index d5b5d6ffd..526b81ce5 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -50,7 +50,7 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 5fab9d8d7..d8a871e04 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -19,6 +19,7 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -606,21 +607,26 @@ public void testError() throws A2AClientException { } @Test - public void testGetAgentCard() throws A2AClientException { - AgentCard agentCard = getClient().getAgentCard(); + public void testGetExtendedAgentCard() throws A2AClientException { + AgentCard agentCard = getClient().getExtendedAgentCard(); assertNotNull(agentCard); assertEquals("test-card", agentCard.name()); assertEquals("A test agent card", agentCard.description()); assertNotNull(agentCard.supportedInterfaces()); assertFalse(agentCard.supportedInterfaces().isEmpty()); - assertEquals(getTransportUrl(), Utils.getFavoriteInterface(agentCard).url()); + Optional transportInterface = agentCard.supportedInterfaces().stream() + .filter(i -> getTransportProtocol().equals(i.protocolBinding())) + .findFirst(); + assertTrue(transportInterface.isPresent()); + System.out.println("transportInterface = " + transportInterface); + assertEquals(getTransportUrl(),transportInterface.get().url()); assertEquals("1.0", agentCard.version()); assertEquals("http://example.com/docs", agentCard.documentationUrl()); assertTrue(agentCard.capabilities().pushNotifications()); assertTrue(agentCard.capabilities().streaming()); assertTrue(agentCard.capabilities().stateTransitionHistory()); + assertTrue(agentCard.capabilities().extendedAgentCard()); assertTrue(agentCard.skills().isEmpty()); - assertFalse(agentCard.supportsExtendedAgentCard()); } @Test @@ -1994,7 +2000,7 @@ private AgentCard createTestAgentCard() { .defaultOutputModes(List.of("text")) .skills(List.of()) .supportedInterfaces(List.of(new AgentInterface(getTransportProtocol(), getTransportUrl()))) - .protocolVersion(CURRENT_PROTOCOL_VERSION) + .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index b8b9b376f..03ec3481b 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -1,6 +1,7 @@ package io.a2a.server.apps.common; import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; +import static io.a2a.spec.TransportProtocol.GRPC; import java.io.IOException; import java.io.InputStream; @@ -12,10 +13,13 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import io.a2a.server.ExtendedAgentCard; import io.a2a.server.PublicAgentCard; import io.a2a.spec.AgentCapabilities; import io.a2a.spec.AgentCard; import io.a2a.spec.AgentInterface; +import io.a2a.spec.TransportProtocol; + import io.quarkus.arc.profile.IfBuildProfile; import org.junit.jupiter.api.Assertions; @@ -28,9 +32,11 @@ public class AgentCardProducer { @Produces @PublicAgentCard + @ExtendedAgentCard public AgentCard agentCard() { String port = System.getProperty("test.agent.card.port", "8081"); String preferredTransport = loadPreferredTransportFromProperties(); + String transportUrl = GRPC.toString().equals(preferredTransport) ? "localhost:" + port : "http://localhost:" + port; AgentCard.Builder builder = AgentCard.builder() .name("test-card") @@ -41,12 +47,13 @@ public AgentCard agentCard() { .streaming(true) .pushNotifications(true) .stateTransitionHistory(true) + .extendedAgentCard(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) .skills(new ArrayList<>()) - .protocolVersion(CURRENT_PROTOCOL_VERSION) - .supportedInterfaces(Collections.singletonList(new AgentInterface(preferredTransport, "http://localhost:" + port))); + .protocolVersions(CURRENT_PROTOCOL_VERSION) + .supportedInterfaces(Collections.singletonList(new AgentInterface(preferredTransport, transportUrl))); return builder.build(); } diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 2998965ea..408205aa2 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -33,7 +33,7 @@ import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; -import io.a2a.spec.ExtendedCardNotConfiguredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; import io.a2a.spec.ExtensionSupportRequiredError; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; @@ -325,8 +325,11 @@ public void onComplete() { public void getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request, StreamObserver responseObserver) { try { - responseObserver.onNext(ToProto.agentCard(getAgentCardInternal())); - responseObserver.onCompleted(); + AgentCard extendedAgentCard = getExtendedAgentCard(); + if (extendedAgentCard != null) { + responseObserver.onNext(ToProto.agentCard(extendedAgentCard)); + responseObserver.onCompleted(); + } } catch (Throwable t) { handleInternalError(responseObserver, t); } @@ -452,7 +455,7 @@ private void handleError(StreamObserver responseObserver, A2AError error) } else if (error instanceof InvalidAgentResponseError) { status = Status.INTERNAL; description = "InvalidAgentResponseError: " + error.getMessage(); - } else if (error instanceof ExtendedCardNotConfiguredError) { + } else if (error instanceof ExtendedAgentCardNotConfiguredError) { status = Status.FAILED_PRECONDITION; description = "ExtendedCardNotConfiguredError: " + error.getMessage(); } else if (error instanceof ExtensionSupportRequiredError) { @@ -498,6 +501,7 @@ private void handleInternalError(StreamObserver responseObserver, Throwab handleError(responseObserver, new InternalError(t.getMessage())); } + private AgentCard getAgentCardInternal() { AgentCard agentCard = getAgentCard(); if (initialised.compareAndSet(false, true)) { @@ -538,6 +542,8 @@ public static void setStreamingSubscribedRunnable(Runnable runnable) { protected abstract AgentCard getAgentCard(); + protected abstract AgentCard getExtendedAgentCard(); + protected abstract CallContextFactory getCallContextFactory(); protected abstract Executor getExecutor(); diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index a3ce7ca2a..690d69a87 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -12,6 +12,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import jakarta.enterprise.inject.Instance; + import com.google.protobuf.Empty; import com.google.protobuf.Struct; import io.a2a.grpc.AuthenticationInfo; @@ -49,16 +51,13 @@ import io.a2a.spec.AgentInterface; import io.a2a.spec.Artifact; import io.a2a.spec.Event; -import io.a2a.spec.ExtensionSupportRequiredError; import io.a2a.spec.InternalError; -import io.a2a.spec.VersionNotSupportedError; import io.a2a.spec.MessageSendParams; import io.a2a.spec.TaskArtifactUpdateEvent; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.transport.grpc.context.GrpcContextKeys; -import io.grpc.Context; + import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.internal.testing.StreamRecorder; @@ -194,7 +193,7 @@ public void testOnMessageNewMessageSuccess() throws Exception { Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); SendMessageResponse response = result.get(0); - assertEquals(GRPC_MESSAGE, response.getMsg()); + assertEquals(GRPC_MESSAGE, response.getMessage()); } @Test @@ -210,7 +209,7 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); SendMessageResponse response = result.get(0); - assertEquals(GRPC_MESSAGE, response.getMsg()); + assertEquals(GRPC_MESSAGE, response.getMessage()); } @Test @@ -316,8 +315,8 @@ public void testOnMessageStreamNewMessageSuccess() throws Exception { Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); StreamResponse response = result.get(0); - Assertions.assertTrue(response.hasMsg()); - Message message = response.getMsg(); + Assertions.assertTrue(response.hasMessage()); + Message message = response.getMessage(); Assertions.assertEquals(GRPC_MESSAGE, message); } @@ -533,7 +532,7 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { // We need to send some events in order for those to end up in the queue SendMessageRequest sendMessageRequest = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder messageRecorder = StreamRecorder.create(); handler.sendStreamingMessage(sendMessageRequest, messageRecorder); @@ -545,8 +544,8 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { Assertions.assertNotNull(result); Assertions.assertEquals(1, result.size()); StreamResponse response = result.get(0); - Assertions.assertTrue(response.hasMsg()); - assertEquals(GRPC_MESSAGE, response.getMsg()); + Assertions.assertTrue(response.hasMessage()); + assertEquals(GRPC_MESSAGE, response.getMessage()); Assertions.assertNull(streamRecorder.getError()); } @@ -755,7 +754,7 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { } @Disabled - public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { + public void testOnGetExtendedAgentCard() throws Exception { // TODO - getting the authenticated extended agent card isn't supported for gRPC right now } @@ -843,13 +842,13 @@ public void testExtensionSupportRequiredErrorOnSendMessage() throws Exception { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); GrpcHandler handler = new TestGrpcHandler(cardWithExtension, requestHandler, internalExecutor); SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendMessage(request, streamRecorder); @@ -879,13 +878,13 @@ public void testExtensionSupportRequiredErrorOnSendStreamingMessage() throws Exc .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); GrpcHandler handler = new TestGrpcHandler(cardWithExtension, requestHandler, internalExecutor); SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendStreamingMessage(request, streamRecorder); @@ -915,7 +914,7 @@ public void testRequiredExtensionProvidedSuccess() throws Exception { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); // Create a TestGrpcHandler that provides the required extension in the context @@ -942,7 +941,7 @@ public ServerCallContext create(StreamObserver streamObserver) { }; SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendMessage(request, streamRecorder); @@ -989,7 +988,7 @@ public ServerCallContext create(StreamObserver streamObserver) { }; SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendMessage(request, streamRecorder); @@ -1034,7 +1033,7 @@ public ServerCallContext create(StreamObserver streamObserver) { }; SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendStreamingMessage(request, streamRecorder); @@ -1083,7 +1082,7 @@ public ServerCallContext create(StreamObserver streamObserver) { }; SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendMessage(request, streamRecorder); @@ -1134,7 +1133,7 @@ public ServerCallContext create(StreamObserver streamObserver) { }; SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendMessage(request, streamRecorder); @@ -1147,7 +1146,7 @@ public ServerCallContext create(StreamObserver streamObserver) { private StreamRecorder sendMessageRequest(GrpcHandler handler) throws Exception { SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendMessage(request, streamRecorder); @@ -1189,7 +1188,7 @@ private StreamRecorder getTaskPushNotificationConfig private StreamRecorder sendStreamingMessageRequest(GrpcHandler handler) throws Exception { SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); handler.sendStreamingMessage(request, streamRecorder); @@ -1199,7 +1198,7 @@ private StreamRecorder sendStreamingMessageRequest(GrpcHandler h private void sendStreamingMessageRequest(GrpcHandler handler, StreamObserver streamObserver) throws Exception { SendMessageRequest request = SendMessageRequest.newBuilder() - .setRequest(GRPC_MESSAGE) + .setMessage(GRPC_MESSAGE) .build(); handler.sendStreamingMessage(request, streamObserver); } @@ -1218,7 +1217,7 @@ public void testListTasksNegativeTimestampReturnsInvalidArgument() { // Negative timestamp should trigger validation error ListTasksRequest request = ListTasksRequest.newBuilder() - .setLastUpdatedAfter(-1L) + .setStatusTimestampAfter(com.google.protobuf.Timestamp.newBuilder().setSeconds(-1L).build()) .setTenant("") .build(); @@ -1279,6 +1278,11 @@ protected AgentCard getAgentCard() { return card; } + @Override + protected AgentCard getExtendedAgentCard() { + return card; + } + @Override protected CallContextFactory getCallContextFactory() { return null; diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index ca8149099..2e1a1fdf0 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -14,8 +14,8 @@ import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; 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.GetExtendedAgentCardRequest; +import io.a2a.jsonrpc.common.wrappers.GetExtendedAgentCardResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; @@ -42,8 +42,7 @@ import io.a2a.server.version.A2AVersionValidator; import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; -import io.a2a.spec.ExtendedCardNotConfiguredError; -import io.a2a.spec.ExtensionSupportRequiredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; import io.a2a.spec.EventKind; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; @@ -53,7 +52,7 @@ import io.a2a.spec.Task; import io.a2a.spec.TaskNotFoundError; import io.a2a.spec.TaskPushNotificationConfig; -import io.a2a.spec.VersionNotSupportedError; + import mutiny.zero.ZeroPublisher; import org.jspecify.annotations.Nullable; @@ -262,18 +261,18 @@ public DeleteTaskPushNotificationConfigResponse deletePushNotificationConfig( } // TODO: Add authentication (https://github.com/a2aproject/a2a-java/issues/77) - public GetAuthenticatedExtendedCardResponse onGetAuthenticatedExtendedCardRequest( - GetAuthenticatedExtendedCardRequest request, ServerCallContext context) { - if (!agentCard.supportsExtendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { - return new GetAuthenticatedExtendedCardResponse(request.getId(), - new ExtendedCardNotConfiguredError(null, "Extended Card not configured", null)); + public GetExtendedAgentCardResponse onGetExtendedCardRequest( + GetExtendedAgentCardRequest request, ServerCallContext context) { + if (!agentCard.capabilities().extendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { + return new GetExtendedAgentCardResponse(request.getId(), + new ExtendedAgentCardNotConfiguredError(null, "Extended Card not configured", null)); } try { - return new GetAuthenticatedExtendedCardResponse(request.getId(), extendedAgentCard.get()); + return new GetExtendedAgentCardResponse(request.getId(), extendedAgentCard.get()); } catch (A2AError e) { - return new GetAuthenticatedExtendedCardResponse(request.getId(), e); + return new GetExtendedAgentCardResponse(request.getId(), e); } catch (Throwable t) { - return new GetAuthenticatedExtendedCardResponse(request.getId(), new InternalError(t.getMessage())); + return new GetExtendedAgentCardResponse(request.getId(), new InternalError(t.getMessage())); } } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 3436a34b1..b43c28029 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -22,8 +22,8 @@ import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigRequest; 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.GetExtendedAgentCardRequest; +import io.a2a.jsonrpc.common.wrappers.GetExtendedAgentCardResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskRequest; @@ -52,14 +52,13 @@ import io.a2a.spec.AgentExtension; import io.a2a.spec.AgentInterface; import io.a2a.spec.Artifact; -import io.a2a.spec.ExtendedCardNotConfiguredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; import io.a2a.spec.ExtensionSupportRequiredError; import io.a2a.spec.VersionNotSupportedError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.Event; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; -import io.a2a.spec.InvalidParamsError; import io.a2a.spec.InvalidRequestError; import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; @@ -1525,12 +1524,12 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { } @Test - public void testOnGetAuthenticatedExtendedAgentCard() throws Exception { + public void testOnGetExtendedAgentCard() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - GetAuthenticatedExtendedCardRequest request = new GetAuthenticatedExtendedCardRequest("1"); - GetAuthenticatedExtendedCardResponse response = handler.onGetAuthenticatedExtendedCardRequest(request, callContext); + GetExtendedAgentCardRequest request = new GetExtendedAgentCardRequest("1"); + GetExtendedAgentCardResponse response = handler.onGetExtendedCardRequest(request, callContext); assertEquals(request.getId(), response.getId()); - assertInstanceOf(ExtendedCardNotConfiguredError.class, response.getError()); + assertInstanceOf(ExtendedAgentCardNotConfiguredError.class, response.getError()); assertNull(response.getResult()); } @@ -1633,7 +1632,7 @@ public void testExtensionSupportRequiredErrorOnMessageSend() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); JSONRPCHandler handler = new JSONRPCHandler(cardWithExtension, requestHandler, internalExecutor); @@ -1672,7 +1671,7 @@ public void testExtensionSupportRequiredErrorOnMessageSendStream() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); JSONRPCHandler handler = new JSONRPCHandler(cardWithExtension, requestHandler, internalExecutor); @@ -1741,7 +1740,7 @@ public void testRequiredExtensionProvidedSuccess() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); JSONRPCHandler handler = new JSONRPCHandler(cardWithExtension, requestHandler, internalExecutor); diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 4d9a9667d..3ffb56c5f 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -36,7 +36,7 @@ import io.a2a.server.util.async.Internal; import io.a2a.spec.A2AError; import io.a2a.spec.AgentCard; -import io.a2a.spec.ExtendedCardNotConfiguredError; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; import io.a2a.spec.ContentTypeNotSupportedError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.EventKind; @@ -210,7 +210,7 @@ public HTTPRestResponse getTask(String taskId, @Nullable Integer historyLength, public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String status, @Nullable Integer pageSize, @Nullable String pageToken, - @Nullable Integer historyLength, @Nullable String lastUpdatedAfter, + @Nullable Integer historyLength, @Nullable String statusTimestampAfter, @Nullable Boolean includeArtifacts, String tenant, ServerCallContext context) { try { @@ -266,21 +266,21 @@ public HTTPRestResponse listTasks(@Nullable String contextId, @Nullable String s paramsBuilder.historyLength(historyLength); } paramsBuilder.tenant(tenant); - if (lastUpdatedAfter != null) { + if (statusTimestampAfter != null) { try { // Try parsing as Unix milliseconds first (integer) - long millis = Long.parseLong(lastUpdatedAfter); + long millis = Long.parseLong(statusTimestampAfter); if (millis < 0L) { Map errorData = new HashMap<>(); - errorData.put("parameter", "lastUpdatedAfter"); + errorData.put("parameter", "statusTimestampAfter"); errorData.put("reason", "Must be a non-negative timestamp value, got: " + millis); throw new InvalidParamsError(null, "Invalid params", errorData); } - paramsBuilder.lastUpdatedAfter(Instant.ofEpochMilli(millis)); + paramsBuilder.statusTimestampAfter(Instant.ofEpochMilli(millis)); } catch (NumberFormatException nfe) { // Fall back to ISO-8601 format try { - paramsBuilder.lastUpdatedAfter(Instant.parse(lastUpdatedAfter)); + paramsBuilder.statusTimestampAfter(Instant.parse(statusTimestampAfter)); } catch (DateTimeParseException e) { Map errorData = new HashMap<>(); errorData.put("parameter", "lastUpdatedAfter"); @@ -466,7 +466,7 @@ private int mapErrorToHttpStatus(A2AError error) { if (error instanceof InvalidAgentResponseError) { return 502; } - if (error instanceof ExtendedCardNotConfiguredError + if (error instanceof ExtendedAgentCardNotConfiguredError || error instanceof ExtensionSupportRequiredError) { return 400; } @@ -478,8 +478,8 @@ private int mapErrorToHttpStatus(A2AError error) { public HTTPRestResponse getExtendedAgentCard(String tenant) { try { - if (!agentCard.supportsExtendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { - throw new ExtendedCardNotConfiguredError(null, "Extended Card not configured", null); + if (!agentCard.capabilities().extendedAgentCard() || extendedAgentCard == null || !extendedAgentCard.isResolvable()) { + throw new ExtendedAgentCardNotConfiguredError(null, "Extended Card not configured", null); } return new HTTPRestResponse(200, "application/json", JsonUtil.toJson(extendedAgentCard.get())); } catch (A2AError e) { diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index a2e6ef6e9..7d930415b 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -19,8 +19,6 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.AgentExtension; import io.a2a.spec.AgentInterface; -import io.a2a.spec.ExtensionSupportRequiredError; -import io.a2a.spec.VersionNotSupportedError; import io.a2a.spec.Task; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -458,7 +456,7 @@ public void testExtensionSupportRequiredErrorOnSendMessage() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); RestHandler handler = new RestHandler(cardWithExtension, requestHandler, internalExecutor); @@ -508,7 +506,7 @@ public void testExtensionSupportRequiredErrorOnSendStreamingMessage() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); RestHandler handler = new RestHandler(cardWithExtension, requestHandler, internalExecutor); @@ -596,7 +594,7 @@ public void testRequiredExtensionProvidedSuccess() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .protocolVersion(AgentCard.CURRENT_PROTOCOL_VERSION) + .protocolVersions(AgentCard.CURRENT_PROTOCOL_VERSION) .build(); RestHandler handler = new RestHandler(cardWithExtension, requestHandler, internalExecutor); From d3e5b28d7258250013da788af26f71a0e15e57a7 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 23 Jan 2026 16:04:10 +0100 Subject: [PATCH 267/493] test: Add input-required workflow coverage (#609) Add test for input-required task state transitions: create task, receive input-required status, send input, and complete task. Signed-off-by: Emmanuel Hugonnet --- .../apps/common/AbstractA2AServerTest.java | 62 +++++++++++++++++++ .../apps/common/AgentExecutorProducer.java | 42 +++++++++++++ 2 files changed, 104 insertions(+) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index d8a871e04..724b58613 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1424,6 +1424,68 @@ public void testNonBlockingWithMultipleMessages() throws Exception { } } + @Test + @Timeout(value = 1, unit = TimeUnit.MINUTES) + public void testInputRequiredWorkflow() throws Exception { + String inputRequiredTaskId = "input-required-test-" + java.util.UUID.randomUUID(); + try { + // 1. Send initial message - AgentExecutor will transition task to INPUT_REQUIRED + Message initialMessage = Message.builder(MESSAGE) + .taskId(inputRequiredTaskId) + .contextId("test-context") + .parts(new TextPart("Initial request")) + .build(); + + CountDownLatch initialLatch = new CountDownLatch(1); + AtomicReference initialState = new AtomicReference<>(); + AtomicBoolean initialUnexpectedEvent = new AtomicBoolean(false); + + BiConsumer initialConsumer = (event, agentCard) -> { + if (event instanceof TaskEvent te) { + initialState.set(te.getTask().status().state()); + initialLatch.countDown(); + } else { + initialUnexpectedEvent.set(true); + } + }; + + // Send initial message - task will go to INPUT_REQUIRED state + getNonStreamingClient().sendMessage(initialMessage, List.of(initialConsumer), null); + assertTrue(initialLatch.await(10, TimeUnit.SECONDS)); + assertFalse(initialUnexpectedEvent.get()); + assertEquals(TaskState.INPUT_REQUIRED, initialState.get()); + + // 2. Send input message - AgentExecutor will complete the task + Message inputMessage = Message.builder(MESSAGE) + .taskId(inputRequiredTaskId) + .contextId("test-context") + .parts(new TextPart("User input")) + .build(); + + CountDownLatch completionLatch = new CountDownLatch(1); + AtomicReference completedState = new AtomicReference<>(); + AtomicBoolean completionUnexpectedEvent = new AtomicBoolean(false); + + BiConsumer completionConsumer = (event, agentCard) -> { + if (event instanceof TaskEvent te) { + completedState.set(te.getTask().status().state()); + completionLatch.countDown(); + } else { + completionUnexpectedEvent.set(true); + } + }; + + // Send input - task will be completed + getNonStreamingClient().sendMessage(inputMessage, List.of(completionConsumer), null); + assertTrue(completionLatch.await(10, TimeUnit.SECONDS)); + assertFalse(completionUnexpectedEvent.get()); + assertEquals(TaskState.COMPLETED, completedState.get()); + + } finally { + deleteTaskInTaskStore(inputRequiredTaskId); + } + } + @Test public void testMalformedJSONRPCRequest() { // skip this test for non-JSONRPC transports diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java index 8b03a68f4..364d2275f 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentExecutorProducer.java @@ -10,6 +10,9 @@ import io.a2a.server.events.EventQueue; import io.a2a.server.tasks.TaskUpdater; import io.a2a.spec.A2AError; +import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.Message; +import io.a2a.spec.Part; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; import io.quarkus.arc.profile.IfBuildProfile; @@ -43,6 +46,28 @@ public void execute(RequestContext context, EventQueue eventQueue) throws A2AErr } } + // Special handling for input-required test + if (taskId != null && taskId.startsWith("input-required-test")) { + // First call: context.getTask() == null (new task) + if (context.getTask() == null) { + updater.startWork(); + updater.requiresInput(updater.newAgentMessage( + List.of(new TextPart("Please provide additional information")), + context.getMessage().metadata())); + // Return immediately - queue stays open because task is in INPUT_REQUIRED state + return; + } else { + String input = extractTextFromMessage(context.getMessage()); + if(! "User input".equals(input)) { + throw new InvalidParamsError("We didn't get the expected input"); + } + // Second call: context.getTask() != null (input provided) + updater.startWork(); + updater.complete(); + return; + } + } + if (context.getTaskId().equals("task-not-supported-123")) { eventQueue.enqueueEvent(new UnsupportedOperationError()); } @@ -60,4 +85,21 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws A2AErro } }; } + + /** + * Extract the content of TextPart in a message to create a single String. + * @param message the message containing the TextPart. + * @return a String aggreagating all the TextPart contents of the message. + */ + private String extractTextFromMessage(final Message message) { + final StringBuilder textBuilder = new StringBuilder(); + if (message.parts() != null) { + for (final Part part : message.parts()) { + if (part instanceof TextPart textPart) { + textBuilder.append(textPart.text()); + } + } + } + return textBuilder.toString(); + } } From ae3e01b69f2c446919f47e611fa02b421ddd9b98 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Mon, 26 Jan 2026 08:02:08 +0000 Subject: [PATCH 268/493] chore: Centralise PageToken parsing (#601) fixes: #597 --------- Signed-off-by: Emmanuel Hugonnet Co-authored-by: Emmanuel Hugonnet --- ...paDatabasePushNotificationConfigStore.java | 33 ++-- ...otificationConfigStoreIntegrationTest.java | 16 +- .../database/jpa/JpaDatabaseTaskStore.java | 25 +-- .../a2a/server/tasks/InMemoryTaskStore.java | 82 ++++----- spec/src/main/java/io/a2a/util/PageToken.java | 68 ++++++++ .../test/java/io/a2a/util/PageTokenTest.java | 161 ++++++++++++++++++ 6 files changed, 285 insertions(+), 100 deletions(-) create mode 100644 spec/src/main/java/io/a2a/util/PageToken.java create mode 100644 spec/src/test/java/io/a2a/util/PageTokenTest.java diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index 1df8b57f3..36245e277 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -15,6 +15,7 @@ import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.spec.ListTaskPushNotificationConfigParams; import io.a2a.spec.ListTaskPushNotificationConfigResult; +import io.a2a.util.PageToken; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.TaskPushNotificationConfig; import java.util.stream.Collectors; @@ -79,18 +80,17 @@ public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConf String taskId = params.id(); LOGGER.debug("Retrieving PushNotificationConfigs for Task '{}' with params: pageSize={}, pageToken={}", taskId, params.pageSize(), params.pageToken()); + + // Parse pageToken once at the beginning + PageToken pageToken = PageToken.fromString(params.pageToken()); + try { StringBuilder queryBuilder = new StringBuilder("SELECT c FROM JpaPushNotificationConfig c WHERE c.id.taskId = :taskId"); - if (params.pageToken() != null && !params.pageToken().isEmpty()) { - String[] tokenParts = params.pageToken().split(":", 2); - if (tokenParts.length == 2) { + if (pageToken != null) { // Keyset pagination: get tasks where timestamp < tokenTimestamp OR (timestamp = tokenTimestamp AND id > tokenId) // All tasks have timestamps (TaskStatus canonical constructor ensures this) queryBuilder.append(" AND (COALESCE(c.createdAt, :nullSentinel) < :tokenTimestamp OR (COALESCE(c.createdAt, :nullSentinel) = :tokenTimestamp AND c.id.configId > :tokenId))"); - } else { - // Based on the comments in the test case, if the pageToken is invalid start from the beginning. - } } queryBuilder.append(" ORDER BY COALESCE(c.createdAt, :nullSentinel) DESC, c.id.configId ASC"); @@ -99,22 +99,9 @@ public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConf query.setParameter("taskId", taskId); query.setParameter("nullSentinel", NULL_TIMESTAMP_SENTINEL); - if (params.pageToken() != null && !params.pageToken().isEmpty()) { - String[] tokenParts = params.pageToken().split(":", 2); - if (tokenParts.length == 2) { - try { - long timestampMillis = Long.parseLong(tokenParts[0]); - String tokenId = tokenParts[1]; - - Instant tokenTimestamp = Instant.ofEpochMilli(timestampMillis); - query.setParameter("tokenTimestamp", tokenTimestamp); - query.setParameter("tokenId", tokenId); - } catch (NumberFormatException e) { - // Malformed timestamp in pageToken - throw new io.a2a.spec.InvalidParamsError(null, - "Invalid pageToken format: timestamp must be numeric milliseconds", null); - } - } + if (pageToken != null) { + query.setParameter("tokenTimestamp", pageToken.timestamp()); + query.setParameter("tokenId", pageToken.id()); } int pageSize = params.getEffectivePageSize(); @@ -128,7 +115,7 @@ public ListTaskPushNotificationConfigResult getInfo(ListTaskPushNotificationConf jpaConfigsPage = jpaConfigsPage.subList(0, pageSize); JpaPushNotificationConfig lastConfig = jpaConfigsPage.get(jpaConfigsPage.size() - 1); Instant timestamp = lastConfig.getCreatedAt() != null ? lastConfig.getCreatedAt() : NULL_TIMESTAMP_SENTINEL; - nextPageToken = timestamp.toEpochMilli() + ":" + lastConfig.getId().getConfigId(); + nextPageToken = new PageToken(timestamp, lastConfig.getId().getConfigId()).toString(); } List configs = jpaConfigsPage.stream() diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index a31beb631..cca31e536 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -352,15 +352,12 @@ public void testPaginationWithInvalidToken() { // Create 5 configs createSamples(taskId, 5); - // Request with invalid pageToken - JPA implementation behavior is to start from beginning + // Request with invalid pageToken - should throw InvalidParamsError for invalid format ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams( taskId, 2, "invalid_token_that_does_not_exist", ""); - ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); - assertNotNull(result); - // When token is not found, implementation starts from beginning - assertEquals(2, result.configs().size(), "Should return first page when token is not found"); - assertNotNull(result.nextPageToken(), "Should have nextPageToken since more items exist"); + assertThrows(io.a2a.spec.InvalidParamsError.class, () -> pushNotificationConfigStore.getInfo(params), + "Should throw InvalidParamsError for invalid pageToken format (missing colon)"); } @Test @@ -428,12 +425,9 @@ public void testPageTokenWithMissingColon() { ListTaskPushNotificationConfigParams params = new ListTaskPushNotificationConfigParams(taskId, 2, "123456789cfg1", ""); - ListTaskPushNotificationConfigResult result = pushNotificationConfigStore.getInfo(params); - assertNotNull(result); - assertEquals(2, result.configs().size(), - "Should return first page when pageToken format is invalid (missing colon)"); - assertNotNull(result.nextPageToken(), "Should have nextPageToken since more items exist"); + assertThrows(io.a2a.spec.InvalidParamsError.class, () -> pushNotificationConfigStore.getInfo(params), + "Should throw InvalidParamsError for invalid pageToken format (missing colon)"); } @Test diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index 0db0e1548..b65b71650 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -25,6 +25,7 @@ import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; import io.a2a.spec.Message; +import io.a2a.util.PageToken; import io.a2a.spec.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -227,23 +228,9 @@ public ListTasksResult list(ListTasksParams params) { params.contextId(), params.status(), params.pageSize(), params.pageToken()); // Parse pageToken once at the beginning - Instant tokenTimestamp = null; - String tokenId = null; - if (params.pageToken() != null && !params.pageToken().isEmpty()) { - String[] tokenParts = params.pageToken().split(":", 2); - if (tokenParts.length == 2) { - try { - long timestampMillis = Long.parseLong(tokenParts[0]); - tokenId = tokenParts[1]; - tokenTimestamp = Instant.ofEpochMilli(timestampMillis); - } catch (NumberFormatException e) { - throw new io.a2a.spec.InvalidParamsError(null, - "Invalid pageToken format: timestamp must be numeric milliseconds", null); - } - } else { - throw new io.a2a.spec.InvalidParamsError(null, "Invalid pageToken format: expected 'timestamp:id'", null); - } - } + PageToken pageToken = PageToken.fromString(params.pageToken()); + Instant tokenTimestamp = pageToken != null ? pageToken.timestamp() : null; + String tokenId = pageToken != null ? pageToken.id() : null; // Build dynamic JPQL query with WHERE clauses for filtering StringBuilder queryBuilder = new StringBuilder("SELECT t FROM JpaTask t WHERE 1=1"); @@ -337,8 +324,8 @@ public ListTasksResult list(ListTasksParams params) { if (hasMore && !tasks.isEmpty()) { Task lastTask = tasks.get(tasks.size() - 1); // All tasks have timestamps (TaskStatus canonical constructor ensures this) - long timestampMillis = lastTask.status().timestamp().toInstant().toEpochMilli(); - nextPageToken = timestampMillis + ":" + lastTask.id(); + Instant timestamp = lastTask.status().timestamp().toInstant(); + nextPageToken = new PageToken(timestamp, lastTask.id()).toString(); } // Apply post-processing transformations (history limiting, artifact removal) diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 8a3d72133..1e3ae1206 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -11,6 +11,7 @@ import io.a2a.spec.Artifact; import io.a2a.spec.ListTasksParams; import io.a2a.spec.Message; +import io.a2a.util.PageToken; import io.a2a.spec.Task; import org.jspecify.annotations.Nullable; @@ -73,54 +74,41 @@ public ListTasksResult list(ListTasksParams params) { // Handle page token using keyset pagination (format: "timestamp_millis:taskId") // Use binary search to efficiently find the first task after the pageToken position (O(log N)) - if (params.pageToken() != null && !params.pageToken().isEmpty()) { - String[] tokenParts = params.pageToken().split(":", 2); - if (tokenParts.length == 2) { - try { - long tokenTimestampMillis = Long.parseLong(tokenParts[0]); - java.time.Instant tokenTimestamp = java.time.Instant.ofEpochMilli(tokenTimestampMillis); - String tokenId = tokenParts[1]; - - // Binary search for first task where: timestamp < tokenTimestamp OR (timestamp == tokenTimestamp AND id > tokenId) - // Since list is sorted (timestamp DESC, id ASC), we search for the insertion point - int left = 0; - int right = allFilteredTasks.size(); - - while (left < right) { - int mid = left + (right - left) / 2; - Task task = allFilteredTasks.get(mid); - - java.time.Instant taskTimestamp = (task.status() != null && task.status().timestamp() != null) - ? task.status().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) - : null; - - if (taskTimestamp == null) { - // Task with null timestamp is always "before" a token with a timestamp, as they are sorted last. - // So, we search in the right half. - left = mid + 1; - } else { - int timestampCompare = taskTimestamp.compareTo(tokenTimestamp); - - if (timestampCompare < 0 || (timestampCompare == 0 && task.id().compareTo(tokenId) > 0)) { - // This task is after the token, search left half - right = mid; - } else { - // This task is before or equal to token, search right half - left = mid + 1; - } - } + PageToken pageToken = PageToken.fromString(params.pageToken()); + if (pageToken != null) { + java.time.Instant tokenTimestamp = pageToken.timestamp(); + String tokenId = pageToken.id(); + + // Binary search for first task where: timestamp < tokenTimestamp OR (timestamp == tokenTimestamp AND id > tokenId) + // Since list is sorted (timestamp DESC, id ASC), we search for the insertion point + int left = 0; + int right = allFilteredTasks.size(); + + while (left < right) { + int mid = left + (right - left) / 2; + Task task = allFilteredTasks.get(mid); + + java.time.Instant taskTimestamp = (task.status() != null && task.status().timestamp() != null) + ? task.status().timestamp().toInstant().truncatedTo(java.time.temporal.ChronoUnit.MILLIS) + : null; + + if (taskTimestamp == null) { + // Task with null timestamp is always "before" a token with a timestamp, as they are sorted last. + // So, we search in the right half. + left = mid + 1; + } else { + int timestampCompare = taskTimestamp.compareTo(tokenTimestamp); + + if (timestampCompare < 0 || (timestampCompare == 0 && task.id().compareTo(tokenId) > 0)) { + // This task is after the token, search left half + right = mid; + } else { + // This task is before or equal to token, search right half + left = mid + 1; } - startIndex = left; - } catch (NumberFormatException e) { - // Malformed timestamp in pageToken - throw new io.a2a.spec.InvalidParamsError(null, - "Invalid pageToken format: timestamp must be numeric milliseconds", null); } - } else { - // Legacy ID-only pageToken format is not supported with timestamp-based sorting - // Throw error to prevent incorrect pagination results - throw new io.a2a.spec.InvalidParamsError(null, "Invalid pageToken format: expected 'timestamp:id'", null); } + startIndex = left; } // Get the page of tasks @@ -132,8 +120,8 @@ public ListTasksResult list(ListTasksParams params) { if (endIndex < allFilteredTasks.size()) { Task lastTask = allFilteredTasks.get(endIndex - 1); // All tasks have timestamps (TaskStatus canonical constructor ensures this) - long timestampMillis = lastTask.status().timestamp().toInstant().toEpochMilli(); - nextPageToken = timestampMillis + ":" + lastTask.id(); + java.time.Instant timestamp = lastTask.status().timestamp().toInstant(); + nextPageToken = new PageToken(timestamp, lastTask.id()).toString(); } // Transform tasks: limit history and optionally remove artifacts diff --git a/spec/src/main/java/io/a2a/util/PageToken.java b/spec/src/main/java/io/a2a/util/PageToken.java new file mode 100644 index 000000000..c703a903a --- /dev/null +++ b/spec/src/main/java/io/a2a/util/PageToken.java @@ -0,0 +1,68 @@ +package io.a2a.util; + +import java.time.Instant; + +import io.a2a.spec.InvalidParamsError; +import org.jspecify.annotations.Nullable; + +/** + * Represents a pagination token for keyset-based pagination. + *

    + * PageTokens use the format {@code "timestamp_millis:id"} where: + *

      + *
    • {@code timestamp_millis} - Unix timestamp in milliseconds (numeric)
    • + *
    • {@code id} - The entity identifier (String)
    • + *
    + * This format enables efficient keyset pagination by allowing queries to resume + * at a specific point in a timestamp-sorted, ID-secondary-sorted result set. + * + * @param timestamp The timestamp component of the page token + * @param id The identifier component of the page token + */ +public record PageToken(Instant timestamp, String id) { + + /** + * Parses a pageToken string into a PageToken record. + *

    + * Expected format: {@code "timestamp_millis:id"} + * + * @param tokenStr The pageToken string to parse, may be null or empty + * @return A PageToken instance, or null if tokenStr is null or empty + * @throws InvalidParamsError if the token format is invalid or timestamp is not numeric + */ + public static @Nullable PageToken fromString(@Nullable String tokenStr) { + if (tokenStr == null || tokenStr.isEmpty()) { + return null; + } + + String[] tokenParts = tokenStr.split(":", 2); + if (tokenParts.length != 2) { + throw new InvalidParamsError(null, + "Invalid pageToken format: expected 'timestamp:id'", null); + } + + try { + long timestampMillis = Long.parseLong(tokenParts[0]); + String id = tokenParts[1]; + if (id.isEmpty()) { + throw new InvalidParamsError(null, "Invalid pageToken format: id part cannot be empty", null); + } + return new PageToken(Instant.ofEpochMilli(timestampMillis), id); + } catch (NumberFormatException e) { + throw new InvalidParamsError(null, + "Invalid pageToken format: timestamp must be numeric milliseconds", null); + } + } + + /** + * Converts this PageToken to its string representation. + *

    + * Format: {@code "timestamp_millis:id"} + * + * @return The pageToken string + */ + @Override + public String toString() { + return timestamp.toEpochMilli() + ":" + id; + } +} diff --git a/spec/src/test/java/io/a2a/util/PageTokenTest.java b/spec/src/test/java/io/a2a/util/PageTokenTest.java new file mode 100644 index 000000000..aef9c8192 --- /dev/null +++ b/spec/src/test/java/io/a2a/util/PageTokenTest.java @@ -0,0 +1,161 @@ +package io.a2a.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.time.Instant; +import java.util.stream.Stream; + +import io.a2a.spec.InvalidParamsError; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +/** + * Unit tests for {@link PageToken}. + */ +class PageTokenTest { + + // ========== Valid Token Parsing Tests ========== + + /** + * Provides test data for valid token parsing tests. + * Format: tokenString, expectedTimestampMillis, expectedId + */ + static Stream validTokens() { + return Stream.of( + Arguments.of("1640000000000:user123", 1640000000000L, "user123"), + Arguments.of("1640000000000:user:123:extra", 1640000000000L, "user:123:extra"), + Arguments.of("0:user123", 0L, "user123"), + Arguments.of("-1000:user123", -1000L, "user123"), + Arguments.of(Long.MAX_VALUE + ":user123", Long.MAX_VALUE, "user123"), + Arguments.of(Long.MIN_VALUE + ":user123", Long.MIN_VALUE, "user123"), + Arguments.of("1640000000000:" + "a".repeat(1000), 1640000000000L, "a".repeat(1000)), + Arguments.of("1640000000000:user-123_test@example.com", 1640000000000L, "user-123_test@example.com") + ); + } + + /** + * Verifies that various valid pageToken strings are correctly parsed into PageToken objects. + */ + @ParameterizedTest + @MethodSource("validTokens") + void testFromString_validTokens_parseCorrectly(String tokenStr, long expectedTimestampMillis, String expectedId) { + PageToken token = PageToken.fromString(tokenStr); + + assertNotNull(token); + assertEquals(Instant.ofEpochMilli(expectedTimestampMillis), token.timestamp()); + assertEquals(expectedId, token.id()); + } + + // ========== Null and Empty Input Tests ========== + + /** + * Verifies that null or empty strings return null instead of throwing an exception. + */ + @ParameterizedTest + @NullAndEmptySource + void testFromString_nullOrEmpty_returnsNull(String tokenStr) { + PageToken token = PageToken.fromString(tokenStr); + assertNull(token); + } + + // ========== Invalid Format Tests ========== + + /** + * Provides test data for invalid token format tests. + * Format: tokenString, expectedErrorMessage + */ + static Stream invalidTokenFormats() { + return Stream.of( + Arguments.of("1640000000000user123", "Invalid pageToken format: expected 'timestamp:id'"), + Arguments.of("1640000000000", "Invalid pageToken format: expected 'timestamp:id'"), + Arguments.of("1640000000000:", "Invalid pageToken format: id part cannot be empty"), + Arguments.of(":", "Invalid pageToken format: timestamp must be numeric milliseconds"), + Arguments.of("notanumber:user123", "Invalid pageToken format: timestamp must be numeric milliseconds"), + Arguments.of("1640000000.123:user123", "Invalid pageToken format: timestamp must be numeric milliseconds"), + Arguments.of("1640 000 000:user123", "Invalid pageToken format: timestamp must be numeric milliseconds") + ); + } + + /** + * Verifies that various invalid token formats throw InvalidParamsError with appropriate messages. + */ + @ParameterizedTest + @MethodSource("invalidTokenFormats") + void testFromString_invalidFormats_throwsInvalidParamsError(String tokenStr, String expectedErrorMessage) { + InvalidParamsError ex = assertThrows(InvalidParamsError.class, () -> { + PageToken.fromString(tokenStr); + }); + assertNotNull(ex.getMessage()); + assertEquals(expectedErrorMessage, ex.getMessage()); + } + + // ========== toString Tests ========== + + /** + * Provides test data for toString tests. + * Format: timestampMillis, id, expectedString + */ + static Stream toStringTestData() { + return Stream.of( + Arguments.of(1640000000000L, "user123", "1640000000000:user123"), + Arguments.of(1640000000000L, "user:123", "1640000000000:user:123"), + Arguments.of(0L, "user123", "0:user123"), + Arguments.of(-1000L, "user123", "-1000:user123") + ); + } + + /** + * Verifies that toString formats PageTokens correctly as "timestamp_millis:id". + */ + @ParameterizedTest + @MethodSource("toStringTestData") + void testToString_formatsCorrectly(long timestampMillis, String id, String expectedString) { + PageToken token = new PageToken(Instant.ofEpochMilli(timestampMillis), id); + String result = token.toString(); + assertEquals(expectedString, result); + } + + // ========== Round-Trip Conversion Tests ========== + + /** + * Provides test data for round-trip conversion tests. + */ + static Stream roundTripTokens() { + return Stream.of( + "1640000000000:user123", + "1640000000000:user:123:extra", + "0:user123", + "-1000:user123" + ); + } + + /** + * Verifies that converting from string to PageToken and back to string preserves the original value. + */ + @ParameterizedTest + @MethodSource("roundTripTokens") + void testRoundTrip_fromStringToString_preservesValue(String original) { + PageToken token = PageToken.fromString(original); + String result = token.toString(); + assertEquals(original, result); + } + + /** + * Verifies that converting from PageToken to string and back to PageToken preserves the original value. + */ + @Test + void testRoundTrip_toStringFromString_preservesValue() { + PageToken original = new PageToken(Instant.ofEpochMilli(1640000000000L), "user123"); + String tokenStr = original.toString(); + PageToken result = PageToken.fromString(tokenStr); + + assertEquals(original.timestamp(), result.timestamp()); + assertEquals(original.id(), result.id()); + } +} From 659df81b28f49218df55d766c87cc37a4c72bc94 Mon Sep 17 00:00:00 2001 From: Harsha Ramesh Date: Mon, 26 Jan 2026 02:56:28 -0800 Subject: [PATCH 269/493] fix: add missing META-INF/beans.xml to microprofile-config integration (#589) The microprofile-config integration was missing the beans.xml marker file, which prevented the configuration from being loaded as an alternative bean in Quarkus applications. This file is required for CDI bean discovery. Fixes #587 --- .../src/main/resources/META-INF/beans.xml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 integrations/microprofile-config/src/main/resources/META-INF/beans.xml diff --git a/integrations/microprofile-config/src/main/resources/META-INF/beans.xml b/integrations/microprofile-config/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..ffc8abb42 --- /dev/null +++ b/integrations/microprofile-config/src/main/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + From 21c15d647156d14481be538517df21a7a1d78a48 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Wed, 4 Feb 2026 17:30:25 +0000 Subject: [PATCH 270/493] feat: Implement MainEventBus architecture for event queue processing (#611) Introduces centralized event processing with single background thread to guarantee event persistence before client visibility and eliminate race conditions in concurrent task updates. Key Changes: - MainEventBus: Central LinkedBlockingDeque for all events - MainEventBusProcessor: Single background thread ensuring serial processing (TaskStore.save() -> PushNotificationSender.send() -> distributeToChildren()) - Two-level queue cleanup protection via TaskStateProvider.isTaskFinalized() to prevent premature cleanup for fire-and-forget tasks - Deterministic blocking calls: waitForTaskFinalization() ensures TaskStore persistence completes before returning to client - Streaming closure: agentCompleted flag via EnhancedRunnable.DoneCallback for graceful drain when agent completes - SseFormatter utility: Framework-agnostic SSE formatting in server-common - Executor pool improvements: Bounded EventConsumerExecutor pool (size 15) prevents exhaustion during high concurrency Null TaskId Support: - QueueManager.switchKey(): Atomic key switching from temp to real task ID - Non-streaming path now handles null taskId like streaming (generates temp UUID) - Cloud deployment test reverted to use A2A.toUserMessage() (valid null taskId case) - No duplicate queue map entries, clean queue lifecycle management Additional Fixes: - Blocking calls always wait for task finalization (not just final events) - RequestContext uses message.taskId() for new tasks when task is null - PostgreSQL pod deployment: Wait loop for pod creation before readiness check - Javadoc: Remove invalid

    tags around

     blocks
    - ServerCallContext: EventConsumer cancellation via closeHandler for
    graceful disconnect
    - Gemini feedback improvements
    
    Architecture Impact:
    - All events flow through MainEventBus before distribution to
    ChildQueues
    - Clients never see unpersisted events (persistence-before-distribution)
    - Fire-and-forget tasks supported: queues stay open for non-final states
    - Late resubscription enabled: queues persist until task finalization
    - Null taskId messages supported: temp IDs transition to real IDs via
    switchKey
    - Test synchronization: MainEventBusProcessorCallback for deterministic
    testing
    
    ---------
    
    Signed-off-by: Emmanuel Hugonnet 
    Co-authored-by: Emmanuel Hugonnet 
    ---
     .../jsonrpc/sse/SSEEventListener.java         |   20 +-
     .../rest/sse/RestSSEEventListener.java        |   24 +-
     examples/cloud-deployment/scripts/deploy.sh   |   16 +
     .../common/events/TaskFinalizedEvent.java     |   14 +-
     ...paDatabasePushNotificationConfigStore.java |    1 +
     .../core/ReplicatedEventQueueItem.java        |   10 +
     .../core/ReplicatedQueueManager.java          |  103 +-
     .../core/ReplicatedQueueManagerTest.java      |  345 ++++--
     .../io/a2a/server/events/EventQueueUtil.java  |   11 +
     .../src/main/resources/application.properties |    2 +
     .../src/main/resources/application.properties |    2 +
     .../MultiInstanceReplicationTest.java         |   97 +-
     .../KafkaReplicationIntegrationTest.java      |   12 +
     .../database/jpa/JpaDatabaseTaskStore.java    |   16 +-
     .../jpa/JpaDatabaseTaskStoreTest.java         |   64 +-
     .../server/apps/quarkus/A2AServerRoutes.java  |  120 +-
     .../src/test/resources/application.properties |    5 +
     .../server/rest/quarkus/A2AServerRoutes.java  |  144 ++-
     .../java/io/a2a/server/ServerCallContext.java |   61 +
     .../io/a2a/server/events/EventConsumer.java   |   80 ++
     .../java/io/a2a/server/events/EventQueue.java |  427 ++++---
     .../server/events/InMemoryQueueManager.java   |   32 +-
     .../io/a2a/server/events/MainEventBus.java    |   42 +
     .../server/events/MainEventBusContext.java    |   11 +
     .../server/events/MainEventBusProcessor.java  |  386 +++++++
     .../events/MainEventBusProcessorCallback.java |   66 ++
     .../MainEventBusProcessorInitializer.java     |   43 +
     .../io/a2a/server/events/QueueManager.java    |   26 +-
     .../DefaultRequestHandler.java                |  515 +++++----
     .../a2a/server/tasks/InMemoryTaskStore.java   |    3 +-
     .../io/a2a/server/tasks/ResultAggregator.java |  128 ++-
     .../java/io/a2a/server/tasks/TaskManager.java |   38 +-
     .../java/io/a2a/server/tasks/TaskStore.java   |    5 +-
     .../util/async/AsyncExecutorProducer.java     |   57 +-
     .../async/EventConsumerExecutorProducer.java  |   93 ++
     .../io/a2a/server/util/sse/SseFormatter.java  |  136 +++
     .../io/a2a/server/util/sse/package-info.java  |   11 +
     .../META-INF/a2a-defaults.properties          |    4 +
     .../a2a/server/events/EventConsumerTest.java  |  100 +-
     .../io/a2a/server/events/EventQueueTest.java  |  227 ++--
     .../io/a2a/server/events/EventQueueUtil.java  |   37 +-
     .../events/InMemoryQueueManagerTest.java      |   34 +-
     .../AbstractA2ARequestHandlerTest.java        |   23 +-
     .../DefaultRequestHandlerTest.java            | 1001 -----------------
     .../server/tasks/InMemoryTaskStoreTest.java   |   49 -
     .../server/tasks/ResultAggregatorTest.java    |   81 +-
     .../io/a2a/server/tasks/TaskManagerTest.java  |   91 +-
     .../io/a2a/server/tasks/TaskUpdaterTest.java  |   58 +-
     tck/src/main/resources/application.properties |    1 +
     .../apps/common/AbstractA2AServerTest.java    |   63 ++
     .../a2a/server/apps/common/TestUtilsBean.java |    2 +-
     .../transport/grpc/handler/GrpcHandler.java   |   24 +-
     .../grpc/handler/GrpcHandlerTest.java         |   83 +-
     .../jsonrpc/handler/JSONRPCHandlerTest.java   |  416 ++++---
     .../transport/rest/handler/RestHandler.java   |   15 +
     .../rest/handler/RestHandlerTest.java         |   20 +-
     56 files changed, 3284 insertions(+), 2211 deletions(-)
     create mode 100644 extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueUtil.java
     create mode 100644 server-common/src/main/java/io/a2a/server/events/MainEventBus.java
     create mode 100644 server-common/src/main/java/io/a2a/server/events/MainEventBusContext.java
     create mode 100644 server-common/src/main/java/io/a2a/server/events/MainEventBusProcessor.java
     create mode 100644 server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorCallback.java
     create mode 100644 server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorInitializer.java
     create mode 100644 server-common/src/main/java/io/a2a/server/util/async/EventConsumerExecutorProducer.java
     create mode 100644 server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java
     create mode 100644 server-common/src/main/java/io/a2a/server/util/sse/package-info.java
    
    diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java
    index 33025ed5e..3a24f5145 100644
    --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java
    +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java
    @@ -10,6 +10,8 @@
     import io.a2a.jsonrpc.common.json.JsonProcessingException;
     import io.a2a.spec.A2AError;
     import io.a2a.spec.StreamingEventKind;
    +import io.a2a.spec.Task;
    +import io.a2a.spec.TaskState;
     import io.a2a.spec.TaskStatusUpdateEvent;
     import org.jspecify.annotations.Nullable;
     
    @@ -64,11 +66,23 @@ private void handleMessage(String message, @Nullable Future future) {
     
                 StreamingEventKind event = ProtoUtils.FromProto.streamingEventKind(response);
                 eventHandler.accept(event);
    -            if (event instanceof TaskStatusUpdateEvent && ((TaskStatusUpdateEvent) event).isFinal()) {
    -                if (future != null) {
    -                    future.cancel(true); // close SSE channel
    +
    +            // Client-side auto-close on final events to prevent connection leaks
    +            // Handles both TaskStatusUpdateEvent and Task objects with final states
    +            // This covers late subscriptions to completed tasks and ensures no connection leaks
    +            boolean shouldClose = false;
    +            if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) {
    +                shouldClose = true;
    +            } else if (event instanceof Task task) {
    +                TaskState state = task.status().state();
    +                if (state.isFinal()) {
    +                    shouldClose = true;
                     }
                 }
    +
    +            if (shouldClose && future != null) {
    +                future.cancel(true); // close SSE channel
    +            }
             } catch (A2AError error) {
                 if (errorHandler != null) {
                     errorHandler.accept(error);
    diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java
    index ec74d2fbc..85e604da3 100644
    --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java
    +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java
    @@ -10,6 +10,9 @@
     import io.a2a.grpc.StreamResponse;
     import io.a2a.grpc.utils.ProtoUtils;
     import io.a2a.spec.StreamingEventKind;
    +import io.a2a.spec.Task;
    +import io.a2a.spec.TaskState;
    +import io.a2a.spec.TaskStatusUpdateEvent;
     import org.jspecify.annotations.Nullable;
     
     public class RestSSEEventListener {
    @@ -29,7 +32,7 @@ public void onMessage(String message, @Nullable Future completableFuture)
                 log.fine("Streaming message received: " + message);
                 io.a2a.grpc.StreamResponse.Builder builder = io.a2a.grpc.StreamResponse.newBuilder();
                 JsonFormat.parser().merge(message, builder);
    -            handleMessage(builder.build());
    +            handleMessage(builder.build(), completableFuture);
             } catch (InvalidProtocolBufferException e) {
                 errorHandler.accept(RestErrorMapper.mapRestError(message, 500));
             }
    @@ -44,7 +47,7 @@ public void onError(Throwable throwable, @Nullable Future future) {
             }
         }
     
    -    private void handleMessage(StreamResponse response) {
    +    private void handleMessage(StreamResponse response, @Nullable Future future) {
             StreamingEventKind event;
             switch (response.getPayloadCase()) {
                 case MESSAGE ->
    @@ -62,6 +65,23 @@ private void handleMessage(StreamResponse response) {
                 }
             }
             eventHandler.accept(event);
    +
    +        // Client-side auto-close on final events to prevent connection leaks
    +        // Handles both TaskStatusUpdateEvent and Task objects with final states
    +        // This covers late subscriptions to completed tasks and ensures no connection leaks
    +        boolean shouldClose = false;
    +        if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) {
    +            shouldClose = true;
    +        } else if (event instanceof Task task) {
    +            TaskState state = task.status().state();
    +            if (state.isFinal()) {
    +                shouldClose = true;
    +            }
    +        }
    +
    +        if (shouldClose && future != null) {
    +            future.cancel(true); // close SSE channel
    +        }
         }
     
     }
    diff --git a/examples/cloud-deployment/scripts/deploy.sh b/examples/cloud-deployment/scripts/deploy.sh
    index e267f3302..fff2a6061 100755
    --- a/examples/cloud-deployment/scripts/deploy.sh
    +++ b/examples/cloud-deployment/scripts/deploy.sh
    @@ -212,6 +212,22 @@ echo ""
     echo "Deploying PostgreSQL..."
     kubectl apply -f ../k8s/01-postgres.yaml
     echo "Waiting for PostgreSQL to be ready..."
    +
    +# Wait for pod to be created (StatefulSet takes time to create pod)
    +for i in {1..30}; do
    +    if kubectl get pod -l app=postgres -n a2a-demo 2>/dev/null | grep -q postgres; then
    +        echo "PostgreSQL pod found, waiting for ready state..."
    +        break
    +    fi
    +    if [ $i -eq 30 ]; then
    +        echo -e "${RED}ERROR: PostgreSQL pod not created after 30 seconds${NC}"
    +        kubectl get statefulset -n a2a-demo
    +        exit 1
    +    fi
    +    sleep 1
    +done
    +
    +# Now wait for pod to be ready
     kubectl wait --for=condition=Ready pod -l app=postgres -n a2a-demo --timeout=120s
     echo -e "${GREEN}✓ PostgreSQL deployed${NC}"
     
    diff --git a/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java b/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java
    index 8c5f59348..0c35bad7a 100644
    --- a/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java
    +++ b/extras/common/src/main/java/io/a2a/extras/common/events/TaskFinalizedEvent.java
    @@ -5,22 +5,28 @@
      * This event is fired AFTER the database transaction commits, making it safe for downstream
      * components to assume the task is durably stored.
      *
    - * 

    Used by the replicated queue manager to send poison pill events after ensuring - * the final task state is committed to the database, eliminating race conditions. + *

    Used by the replicated queue manager to send the final task state before the poison pill, + * ensuring correct event ordering across instances and eliminating race conditions. */ public class TaskFinalizedEvent { private final String taskId; + private final Object task; // Task type from io.a2a.spec - using Object to avoid dependency - public TaskFinalizedEvent(String taskId) { + public TaskFinalizedEvent(String taskId, Object task) { this.taskId = taskId; + this.task = task; } public String getTaskId() { return taskId; } + public Object getTask() { + return task; + } + @Override public String toString() { - return "TaskFinalizedEvent{taskId='" + taskId + "'}"; + return "TaskFinalizedEvent{taskId='" + taskId + "', task=" + task + "}"; } } diff --git a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java index 36245e277..5049bc9a4 100644 --- a/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java +++ b/extras/push-notification-config-store-database-jpa/src/main/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStore.java @@ -164,4 +164,5 @@ public void deleteInfo(String taskId, String configId) { taskId, configId); } } + } diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java index 87c10fb4e..206e07f03 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedEventQueueItem.java @@ -149,6 +149,16 @@ public void setClosedEvent(boolean closedEvent) { } } + /** + * Check if this event is a Task event. + * Task events should always be processed even for inactive tasks, + * as they carry the final task state. + * @return true if this is a Task event + */ + public boolean isTaskEvent() { + return event instanceof io.a2a.spec.Task; + } + @Override public String toString() { return "ReplicatedEventQueueItem{" + diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index 586ab11a7..44dfbe427 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -13,8 +13,10 @@ import io.a2a.server.events.EventQueueFactory; import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.events.MainEventBus; import io.a2a.server.events.QueueManager; import io.a2a.server.tasks.TaskStateProvider; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,10 +47,12 @@ protected ReplicatedQueueManager() { } @Inject - public ReplicatedQueueManager(ReplicationStrategy replicationStrategy, TaskStateProvider taskStateProvider) { + public ReplicatedQueueManager(ReplicationStrategy replicationStrategy, + TaskStateProvider taskStateProvider, + MainEventBus mainEventBus) { this.replicationStrategy = replicationStrategy; this.taskStateProvider = taskStateProvider; - this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory(), taskStateProvider); + this.delegate = new InMemoryQueueManager(new ReplicatingEventQueueFactory(), taskStateProvider, mainEventBus); } @@ -77,8 +81,7 @@ public void close(String taskId) { @Override public EventQueue createOrTap(String taskId) { - EventQueue queue = delegate.createOrTap(taskId); - return queue; + return delegate.createOrTap(taskId); } @Override @@ -87,9 +90,11 @@ public void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedExcep } public void onReplicatedEvent(@Observes ReplicatedEventQueueItem replicatedEvent) { - // Check if task is still active before processing replicated event (unless it's a QueueClosedEvent) - // QueueClosedEvent should always be processed to terminate streams, even for inactive tasks + // Check if task is still active before processing replicated event + // Always allow QueueClosedEvent and Task events (they carry final state) + // Skip other event types for inactive tasks to prevent queue creation for expired tasks if (!replicatedEvent.isClosedEvent() + && !replicatedEvent.isTaskEvent() && !taskStateProvider.isTaskActive(replicatedEvent.getTaskId())) { // Task is no longer active - skip processing this replicated event // This prevents creating queues for tasks that have been finalized beyond the grace period @@ -97,38 +102,81 @@ public void onReplicatedEvent(@Observes ReplicatedEventQueueItem replicatedEvent return; } - // Get or create a ChildQueue for this task (creates MainQueue if it doesn't exist) - EventQueue childQueue = delegate.createOrTap(replicatedEvent.getTaskId()); - + // Get the MainQueue to enqueue the replicated event item + // We must use enqueueItem (not enqueueEvent) to preserve the isReplicated() flag + // and avoid triggering the replication hook again (which would cause a replication loop) + // + // IMPORTANT: We must NOT create a ChildQueue here! Creating and immediately closing + // a ChildQueue means there are zero children when MainEventBusProcessor distributes + // the event. Existing ChildQueues (from active client subscriptions) will receive + // the event when MainEventBusProcessor distributes it to all children. + // + // If MainQueue doesn't exist, create it. This handles late-arriving replicated events + // for tasks that were created on another instance. + EventQueue childQueue = null; // Track ChildQueue we might create + EventQueue mainQueue = delegate.get(replicatedEvent.getTaskId()); try { - // Get the MainQueue to enqueue the replicated event item - // We must use enqueueItem (not enqueueEvent) to preserve the isReplicated() flag - // and avoid triggering the replication hook again (which would cause a replication loop) - EventQueue mainQueue = delegate.get(replicatedEvent.getTaskId()); + if (mainQueue == null) { + LOGGER.debug("Creating MainQueue for replicated event on task {}", replicatedEvent.getTaskId()); + childQueue = delegate.createOrTap(replicatedEvent.getTaskId()); // Creates MainQueue + returns ChildQueue + mainQueue = delegate.get(replicatedEvent.getTaskId()); // Get MainQueue from map + } + if (mainQueue != null) { mainQueue.enqueueItem(replicatedEvent); } else { - LOGGER.warn("MainQueue not found for task {}, cannot enqueue replicated event. This may happen if the queue was already cleaned up.", - replicatedEvent.getTaskId()); + LOGGER.warn( + "MainQueue not found for task {}, cannot enqueue replicated event. This may happen if the queue was already cleaned up.", + replicatedEvent.getTaskId()); } } finally { - // Close the temporary ChildQueue to prevent leaks - // The MainQueue remains open for other consumers - childQueue.close(); + if (childQueue != null) { + try { + childQueue.close(); // Close the ChildQueue we created (not MainQueue!) + } catch (Exception ignore) { + // The close is safe, but print a stacktrace just in case + if (LOGGER.isDebugEnabled()) { + ignore.printStackTrace(); + } + } + } } } /** * Observes task finalization events fired AFTER database transaction commits. - * This guarantees the task's final state is durably stored before sending the poison pill. + * This guarantees the task's final state is durably stored before replication. * - * @param event the task finalized event containing the task ID + * Sends TaskStatusUpdateEvent (not full Task) FIRST, then the poison pill (QueueClosedEvent), + * ensuring correct event ordering across instances and eliminating race conditions where + * the poison pill arrives before the final task state. + * + * IMPORTANT: We send TaskStatusUpdateEvent instead of full Task to maintain consistency + * with local event distribution. Clients expect TaskStatusUpdateEvent for status changes, + * and sending the full Task causes issues in remote instances where clients don't handle + * bare Task objects the same way they handle TaskStatusUpdateEvent. + * + * @param event the task finalized event containing the task ID and final Task */ public void onTaskFinalized(@Observes(during = TransactionPhase.AFTER_SUCCESS) TaskFinalizedEvent event) { String taskId = event.getTaskId(); - LOGGER.debug("Task {} finalized - sending poison pill (QueueClosedEvent) after transaction commit", taskId); + io.a2a.spec.Task finalTask = (io.a2a.spec.Task) event.getTask(); // Cast from Object + + LOGGER.debug("Task {} finalized - sending TaskStatusUpdateEvent then poison pill (QueueClosedEvent) after transaction commit", taskId); + + // Convert final Task to TaskStatusUpdateEvent to match local event distribution + // This ensures remote instances receive the same event type as local instances + io.a2a.spec.TaskStatusUpdateEvent finalStatusEvent = io.a2a.spec.TaskStatusUpdateEvent.builder() + .taskId(taskId) + .contextId(finalTask.contextId()) + .status(finalTask.status()) + .isFinal(true) + .build(); + + // Send TaskStatusUpdateEvent FIRST to ensure it arrives before poison pill + replicationStrategy.send(taskId, finalStatusEvent); - // Send poison pill directly via replication strategy + // Then send poison pill // The transaction has committed, so the final state is guaranteed to be in the database io.a2a.server.events.QueueClosedEvent closedEvent = new io.a2a.server.events.QueueClosedEvent(taskId); replicationStrategy.send(taskId, closedEvent); @@ -152,12 +200,11 @@ public EventQueue.EventQueueBuilder builder(String taskId) { // which sends the QueueClosedEvent after the database transaction commits. // This ensures proper ordering and transactional guarantees. - // Return the builder with callbacks - return delegate.getEventQueueBuilder(taskId) - .taskId(taskId) - .hook(new ReplicationHook(taskId)) - .addOnCloseCallback(delegate.getCleanupCallback(taskId)) - .taskStateProvider(taskStateProvider); + // Call createBaseEventQueueBuilder() directly to avoid infinite recursion + // (getEventQueueBuilder() would delegate back to this factory, creating a loop) + // The base builder already includes: taskId, cleanup callback, taskStateProvider, mainEventBus + return delegate.createBaseEventQueueBuilder(taskId) + .hook(new ReplicationHook(taskId)); } } diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 43571cd30..14b4c1f51 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -22,12 +22,19 @@ import io.a2a.server.events.EventQueueClosedException; import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.EventQueueTestHelper; +import io.a2a.server.events.EventQueueUtil; +import io.a2a.server.events.MainEventBus; +import io.a2a.server.events.MainEventBusProcessor; import io.a2a.server.events.QueueClosedEvent; +import io.a2a.server.tasks.InMemoryTaskStore; +import io.a2a.server.tasks.PushNotificationSender; import io.a2a.spec.Event; import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -35,10 +42,27 @@ class ReplicatedQueueManagerTest { private ReplicatedQueueManager queueManager; private StreamingEventKind testEvent; + private MainEventBus mainEventBus; + private MainEventBusProcessor mainEventBusProcessor; + private static final PushNotificationSender NOOP_PUSHNOTIFICATION_SENDER = task -> {}; @BeforeEach void setUp() { - queueManager = new ReplicatedQueueManager(new NoOpReplicationStrategy(), new MockTaskStateProvider(true)); + // Create MainEventBus first + InMemoryTaskStore taskStore = new InMemoryTaskStore(); + mainEventBus = new MainEventBus(); + + // Create QueueManager before MainEventBusProcessor (processor needs it as parameter) + queueManager = new ReplicatedQueueManager( + new NoOpReplicationStrategy(), + new MockTaskStateProvider(true), + mainEventBus + ); + + // Create MainEventBusProcessor with QueueManager + mainEventBusProcessor = new MainEventBusProcessor(mainEventBus, taskStore, NOOP_PUSHNOTIFICATION_SENDER, queueManager); + EventQueueUtil.start(mainEventBusProcessor); + testEvent = TaskStatusUpdateEvent.builder() .taskId("test-task") .contextId("test-context") @@ -47,25 +71,82 @@ void setUp() { .build(); } + /** + * Helper to create a test event with the specified taskId. + * This ensures taskId consistency between queue creation and event creation. + */ + private TaskStatusUpdateEvent createEventForTask(String taskId) { + return TaskStatusUpdateEvent.builder() + .taskId(taskId) + .contextId("test-context") + .status(new TaskStatus(TaskState.SUBMITTED)) + .isFinal(false) + .build(); + } + + @AfterEach + void tearDown() { + if (mainEventBusProcessor != null) { + mainEventBusProcessor.setCallback(null); // Clear any test callbacks + EventQueueUtil.stop(mainEventBusProcessor); + } + mainEventBusProcessor = null; + mainEventBus = null; + queueManager = null; + } + + /** + * Helper to wait for MainEventBusProcessor to process an event. + * Replaces polling patterns with deterministic callback-based waiting. + * + * @param action the action that triggers event processing + * @throws InterruptedException if waiting is interrupted + * @throws AssertionError if processing doesn't complete within timeout + */ + private void waitForEventProcessing(Runnable action) throws InterruptedException { + CountDownLatch processingLatch = new CountDownLatch(1); + mainEventBusProcessor.setCallback(new io.a2a.server.events.MainEventBusProcessorCallback() { + @Override + public void onEventProcessed(String taskId, io.a2a.spec.Event event) { + processingLatch.countDown(); + } + + @Override + public void onTaskFinalized(String taskId) { + // Not needed for basic event processing wait + } + }); + + try { + action.run(); + assertTrue(processingLatch.await(5, TimeUnit.SECONDS), + "MainEventBusProcessor should have processed the event within timeout"); + } finally { + mainEventBusProcessor.setCallback(null); + } + } + @Test void testReplicationStrategyTriggeredOnNormalEnqueue() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true), mainEventBus); String taskId = "test-task-1"; EventQueue queue = queueManager.createOrTap(taskId); + TaskStatusUpdateEvent event = createEventForTask(taskId); - queue.enqueueEvent(testEvent); + // Wait for MainEventBusProcessor to process the event and trigger replication + waitForEventProcessing(() -> queue.enqueueEvent(event)); assertEquals(1, strategy.getCallCount()); assertEquals(taskId, strategy.getLastTaskId()); - assertEquals(testEvent, strategy.getLastEvent()); + assertEquals(event, strategy.getLastEvent()); } @Test void testReplicationStrategyNotTriggeredOnReplicatedEvent() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true), mainEventBus); String taskId = "test-task-2"; EventQueue queue = queueManager.createOrTap(taskId); @@ -79,17 +160,19 @@ void testReplicationStrategyNotTriggeredOnReplicatedEvent() throws InterruptedEx @Test void testReplicationStrategyWithCountingImplementation() throws InterruptedException { CountingReplicationStrategy countingStrategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(countingStrategy, new MockTaskStateProvider(true)); + queueManager = new ReplicatedQueueManager(countingStrategy, new MockTaskStateProvider(true), mainEventBus); String taskId = "test-task-3"; EventQueue queue = queueManager.createOrTap(taskId); + TaskStatusUpdateEvent event = createEventForTask(taskId); - queue.enqueueEvent(testEvent); - queue.enqueueEvent(testEvent); + // Wait for MainEventBusProcessor to process each event + waitForEventProcessing(() -> queue.enqueueEvent(event)); + waitForEventProcessing(() -> queue.enqueueEvent(event)); assertEquals(2, countingStrategy.getCallCount()); assertEquals(taskId, countingStrategy.getLastTaskId()); - assertEquals(testEvent, countingStrategy.getLastEvent()); + assertEquals(event, countingStrategy.getLastEvent()); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); queueManager.onReplicatedEvent(replicatedEvent); @@ -100,46 +183,45 @@ void testReplicationStrategyWithCountingImplementation() throws InterruptedExcep @Test void testReplicatedEventDeliveredToCorrectQueue() throws InterruptedException { String taskId = "test-task-4"; + TaskStatusUpdateEvent eventForTask = createEventForTask(taskId); // Use matching taskId EventQueue queue = queueManager.createOrTap(taskId); - ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); - queueManager.onReplicatedEvent(replicatedEvent); + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, eventForTask); - Event dequeuedEvent; - try { - dequeuedEvent = queue.dequeueEventItem(100).getEvent(); - } catch (EventQueueClosedException e) { - fail("Queue should not be closed"); - return; - } - assertEquals(testEvent, dequeuedEvent); + // Use callback to wait for event processing + EventQueueItem item = dequeueEventWithRetry(queue, () -> queueManager.onReplicatedEvent(replicatedEvent)); + assertNotNull(item, "Event should be available in queue"); + Event dequeuedEvent = item.getEvent(); + assertEquals(eventForTask, dequeuedEvent); } @Test void testReplicatedEventCreatesQueueIfNeeded() throws InterruptedException { String taskId = "non-existent-task"; + TaskStatusUpdateEvent eventForTask = createEventForTask(taskId); // Use matching taskId // Verify no queue exists initially assertNull(queueManager.get(taskId)); - ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); - - // Process the replicated event - assertDoesNotThrow(() -> queueManager.onReplicatedEvent(replicatedEvent)); - - // Verify that a queue was created and the event was enqueued - EventQueue queue = queueManager.get(taskId); - assertNotNull(queue, "Queue should be created when processing replicated event for non-existent task"); - - // Verify the event was enqueued by dequeuing it - Event dequeuedEvent; - try { - dequeuedEvent = queue.dequeueEventItem(100).getEvent(); - } catch (EventQueueClosedException e) { - fail("Queue should not be closed"); - return; - } - assertEquals(testEvent, dequeuedEvent, "The replicated event should be enqueued in the newly created queue"); + // Create a ChildQueue BEFORE processing the replicated event + // This ensures the ChildQueue exists when MainEventBusProcessor distributes the event + EventQueue childQueue = queueManager.createOrTap(taskId); + assertNotNull(childQueue, "ChildQueue should be created"); + + // Verify MainQueue was created + EventQueue mainQueue = queueManager.get(taskId); + assertNotNull(mainQueue, "MainQueue should exist after createOrTap"); + + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, eventForTask); + + // Process the replicated event and wait for distribution + // Use callback to wait for event processing + EventQueueItem item = dequeueEventWithRetry(childQueue, () -> { + assertDoesNotThrow(() -> queueManager.onReplicatedEvent(replicatedEvent)); + }); + assertNotNull(item, "Event should be available in queue"); + Event dequeuedEvent = item.getEvent(); + assertEquals(eventForTask, dequeuedEvent, "The replicated event should be enqueued in the newly created queue"); } @Test @@ -170,17 +252,18 @@ void testBasicQueueManagerFunctionality() throws InterruptedException { void testQueueToTaskIdMappingMaintained() throws InterruptedException { String taskId = "test-task-6"; CountingReplicationStrategy countingStrategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(countingStrategy, new MockTaskStateProvider(true)); + queueManager = new ReplicatedQueueManager(countingStrategy, new MockTaskStateProvider(true), mainEventBus); + TaskStatusUpdateEvent event = createEventForTask(taskId); EventQueue queue = queueManager.createOrTap(taskId); - queue.enqueueEvent(testEvent); + waitForEventProcessing(() -> queue.enqueueEvent(event)); assertEquals(taskId, countingStrategy.getLastTaskId()); queueManager.close(taskId); // Task is active, so NO poison pill is sent EventQueue newQueue = queueManager.createOrTap(taskId); - newQueue.enqueueEvent(testEvent); + waitForEventProcessing(() -> newQueue.enqueueEvent(event)); assertEquals(taskId, countingStrategy.getLastTaskId()); // 2 replication calls: 1 testEvent, 1 testEvent (no QueueClosedEvent because task is active) @@ -217,16 +300,43 @@ void testReplicatedEventJsonSerialization() throws Exception { @Test void testParallelReplicationBehavior() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true), mainEventBus); String taskId = "parallel-test-task"; EventQueue queue = queueManager.createOrTap(taskId); int numThreads = 10; int eventsPerThread = 5; + int expectedEventCount = (numThreads / 2) * eventsPerThread; // Only normal enqueues + int totalEventCount = numThreads * eventsPerThread; // All events (normal + replicated) ExecutorService executor = Executors.newFixedThreadPool(numThreads); CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch doneLatch = new CountDownLatch(numThreads); + + // Use CyclicBarrier for better thread synchronization + // This ensures all threads start their work at approximately the same time + java.util.concurrent.CyclicBarrier barrier = new java.util.concurrent.CyclicBarrier(numThreads); + + // Track processed events for better diagnostics on failure + java.util.concurrent.CopyOnWriteArrayList processedEvents = + new java.util.concurrent.CopyOnWriteArrayList<>(); + + // Set up callback to wait for ALL events to be processed by MainEventBusProcessor + // Must wait for all 50 events (25 normal + 25 replicated) to ensure all normal events + // have triggered replication before we check the count + CountDownLatch processingLatch = new CountDownLatch(totalEventCount); + mainEventBusProcessor.setCallback(new io.a2a.server.events.MainEventBusProcessorCallback() { + @Override + public void onEventProcessed(String tid, io.a2a.spec.Event event) { + processedEvents.add(event); + processingLatch.countDown(); + } + + @Override + public void onTaskFinalized(String tid) { + // Not needed for this test + } + }); // Launch threads that will enqueue events normally (should trigger replication) for (int i = 0; i < numThreads / 2; i++) { @@ -234,18 +344,20 @@ void testParallelReplicationBehavior() throws InterruptedException { executor.submit(() -> { try { startLatch.await(); + barrier.await(); // Synchronize thread starts for better interleaving for (int j = 0; j < eventsPerThread; j++) { TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() - .taskId("normal-" + threadId + "-" + j) + .taskId(taskId) // Use same taskId as queue .contextId("test-context") .status(new TaskStatus(TaskState.WORKING)) .isFinal(false) .build(); queue.enqueueEvent(event); - Thread.sleep(1); // Small delay to interleave operations } } catch (InterruptedException e) { Thread.currentThread().interrupt(); + } catch (java.util.concurrent.BrokenBarrierException e) { + throw new RuntimeException("Barrier broken", e); } finally { doneLatch.countDown(); } @@ -258,19 +370,21 @@ void testParallelReplicationBehavior() throws InterruptedException { executor.submit(() -> { try { startLatch.await(); + barrier.await(); // Synchronize thread starts for better interleaving for (int j = 0; j < eventsPerThread; j++) { TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() - .taskId("replicated-" + threadId + "-" + j) + .taskId(taskId) // Use same taskId as queue .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) .isFinal(true) .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); queueManager.onReplicatedEvent(replicatedEvent); - Thread.sleep(1); // Small delay to interleave operations } } catch (InterruptedException e) { Thread.currentThread().interrupt(); + } catch (java.util.concurrent.BrokenBarrierException e) { + throw new RuntimeException("Barrier broken", e); } finally { doneLatch.countDown(); } @@ -280,24 +394,43 @@ void testParallelReplicationBehavior() throws InterruptedException { // Start all threads simultaneously startLatch.countDown(); - // Wait for all threads to complete - assertTrue(doneLatch.await(10, TimeUnit.SECONDS), "All threads should complete within 10 seconds"); + // Wait for all threads to complete with explicit timeout + assertTrue(doneLatch.await(10, TimeUnit.SECONDS), + "All " + numThreads + " threads should complete within 10 seconds"); executor.shutdown(); - assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS), "Executor should shutdown within 5 seconds"); + assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS), + "Executor should shutdown within 5 seconds"); + + // Wait for MainEventBusProcessor to process all events + try { + boolean allProcessed = processingLatch.await(10, TimeUnit.SECONDS); + assertTrue(allProcessed, + String.format("MainEventBusProcessor should have processed all %d events within timeout. " + + "Processed: %d, Remaining: %d", + totalEventCount, processedEvents.size(), processingLatch.getCount())); + } finally { + mainEventBusProcessor.setCallback(null); + queue.close(true, true); + } + + // Verify we processed the expected number of events + assertEquals(totalEventCount, processedEvents.size(), + "Should have processed exactly " + totalEventCount + " events (normal + replicated)"); // Only the normal enqueue operations should have triggered replication // numThreads/2 threads * eventsPerThread events each = total expected replication calls int expectedReplicationCalls = (numThreads / 2) * eventsPerThread; assertEquals(expectedReplicationCalls, strategy.getCallCount(), - "Only normal enqueue operations should trigger replication, not replicated events"); + String.format("Only normal enqueue operations should trigger replication, not replicated events. " + + "Expected: %d, Actual: %d", expectedReplicationCalls, strategy.getCallCount())); } @Test void testReplicatedEventSkippedWhenTaskInactive() throws InterruptedException { // Create a task state provider that returns false (task is inactive) MockTaskStateProvider stateProvider = new MockTaskStateProvider(false); - queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider); + queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider, mainEventBus); String taskId = "inactive-task"; @@ -316,30 +449,32 @@ void testReplicatedEventSkippedWhenTaskInactive() throws InterruptedException { void testReplicatedEventProcessedWhenTaskActive() throws InterruptedException { // Create a task state provider that returns true (task is active) MockTaskStateProvider stateProvider = new MockTaskStateProvider(true); - queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider); + queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider, mainEventBus); String taskId = "active-task"; + TaskStatusUpdateEvent eventForTask = createEventForTask(taskId); // Use matching taskId // Verify no queue exists initially assertNull(queueManager.get(taskId)); - // Process a replicated event for an active task - ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, testEvent); - queueManager.onReplicatedEvent(replicatedEvent); + // Create a ChildQueue BEFORE processing the replicated event + // This ensures the ChildQueue exists when MainEventBusProcessor distributes the event + EventQueue childQueue = queueManager.createOrTap(taskId); + assertNotNull(childQueue, "ChildQueue should be created"); - // Queue should be created and event should be enqueued - EventQueue queue = queueManager.get(taskId); - assertNotNull(queue, "Queue should be created for active task"); + // Verify MainQueue was created + EventQueue mainQueue = queueManager.get(taskId); + assertNotNull(mainQueue, "MainQueue should exist after createOrTap"); - // Verify the event was enqueued - Event dequeuedEvent; - try { - dequeuedEvent = queue.dequeueEventItem(100).getEvent(); - } catch (EventQueueClosedException e) { - fail("Queue should not be closed"); - return; - } - assertEquals(testEvent, dequeuedEvent, "Event should be enqueued for active task"); + // Process a replicated event for an active task + ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, eventForTask); + + // Verify the event was enqueued and distributed to our ChildQueue + // Use callback to wait for event processing + EventQueueItem item = dequeueEventWithRetry(childQueue, () -> queueManager.onReplicatedEvent(replicatedEvent)); + assertNotNull(item, "Event should be available in queue"); + Event dequeuedEvent = item.getEvent(); + assertEquals(eventForTask, dequeuedEvent, "Event should be enqueued for active task"); } @@ -347,7 +482,7 @@ void testReplicatedEventProcessedWhenTaskActive() throws InterruptedException { void testReplicatedEventToExistingQueueWhenTaskBecomesInactive() throws InterruptedException { // Create a task state provider that returns true initially MockTaskStateProvider stateProvider = new MockTaskStateProvider(true); - queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider); + queueManager = new ReplicatedQueueManager(new CountingReplicationStrategy(), stateProvider, mainEventBus); String taskId = "task-becomes-inactive"; @@ -387,30 +522,38 @@ void testReplicatedEventToExistingQueueWhenTaskBecomesInactive() throws Interrup @Test void testPoisonPillSentViaTransactionAwareEvent() throws InterruptedException { CountingReplicationStrategy strategy = new CountingReplicationStrategy(); - queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true)); + queueManager = new ReplicatedQueueManager(strategy, new MockTaskStateProvider(true), mainEventBus); String taskId = "poison-pill-test"; EventQueue queue = queueManager.createOrTap(taskId); + TaskStatusUpdateEvent event = createEventForTask(taskId); - // Enqueue a normal event first - queue.enqueueEvent(testEvent); + // Enqueue a normal event first and wait for processing + waitForEventProcessing(() -> queue.enqueueEvent(event)); // In the new architecture, QueueClosedEvent (poison pill) is sent via CDI events // when JpaDatabaseTaskStore.save() persists a final task and the transaction commits // ReplicatedQueueManager.onTaskFinalized() observes AFTER_SUCCESS and sends the poison pill // Simulate the CDI event observer being called (what happens in real execution) - TaskFinalizedEvent taskFinalizedEvent = new TaskFinalizedEvent(taskId); + // Create a final task for the event + Task finalTask = Task.builder() + .id(taskId) + .contextId("test-context") + .status(new TaskStatus(TaskState.COMPLETED)) + .build(); + TaskFinalizedEvent taskFinalizedEvent = new TaskFinalizedEvent(taskId, finalTask); // Call the observer method directly (simulating CDI event delivery) queueManager.onTaskFinalized(taskFinalizedEvent); - // Verify that QueueClosedEvent was replicated - // strategy.getCallCount() should be 2: one for testEvent, one for QueueClosedEvent - assertEquals(2, strategy.getCallCount(), "Should have replicated both normal event and QueueClosedEvent"); + // Verify that final Task and QueueClosedEvent were replicated + // strategy.getCallCount() should be 3: testEvent, final Task, then QueueClosedEvent (poison pill) + assertEquals(3, strategy.getCallCount(), "Should have replicated testEvent, final Task, and QueueClosedEvent"); + // Verify the last event is QueueClosedEvent (poison pill) Event lastEvent = strategy.getLastEvent(); - assertTrue(lastEvent instanceof QueueClosedEvent, "Last replicated event should be QueueClosedEvent"); + assertTrue(lastEvent instanceof QueueClosedEvent, "Last replicated event should be QueueClosedEvent (poison pill)"); assertEquals(taskId, ((QueueClosedEvent) lastEvent).getTaskId()); } @@ -451,36 +594,21 @@ void testQueueClosedEventJsonSerialization() throws Exception { @Test void testReplicatedQueueClosedEventTerminatesConsumer() throws InterruptedException { String taskId = "remote-close-test"; + TaskStatusUpdateEvent eventForTask = createEventForTask(taskId); // Use matching taskId EventQueue queue = queueManager.createOrTap(taskId); - // Enqueue a normal event - queue.enqueueEvent(testEvent); - // Simulate receiving QueueClosedEvent from remote node QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); ReplicatedEventQueueItem replicatedClosedEvent = new ReplicatedEventQueueItem(taskId, closedEvent); - queueManager.onReplicatedEvent(replicatedClosedEvent); - // Dequeue the normal event first - EventQueueItem item1; - try { - item1 = queue.dequeueEventItem(100); - } catch (EventQueueClosedException e) { - fail("Should not throw on first dequeue"); - return; - } - assertNotNull(item1); - assertEquals(testEvent, item1.getEvent()); + // Dequeue the normal event first (use callback to wait for async processing) + EventQueueItem item1 = dequeueEventWithRetry(queue, () -> queue.enqueueEvent(eventForTask)); + assertNotNull(item1, "First event should be available"); + assertEquals(eventForTask, item1.getEvent()); - // Next dequeue should get the QueueClosedEvent - EventQueueItem item2; - try { - item2 = queue.dequeueEventItem(100); - } catch (EventQueueClosedException e) { - fail("Should not throw on second dequeue, should return the event"); - return; - } - assertNotNull(item2); + // Next dequeue should get the QueueClosedEvent (use callback to wait for async processing) + EventQueueItem item2 = dequeueEventWithRetry(queue, () -> queueManager.onReplicatedEvent(replicatedClosedEvent)); + assertNotNull(item2, "QueueClosedEvent should be available"); assertTrue(item2.getEvent() instanceof QueueClosedEvent, "Second event should be QueueClosedEvent"); } @@ -539,4 +667,25 @@ public void setActive(boolean active) { this.active = active; } } + + /** + * Helper method to dequeue an event after waiting for MainEventBusProcessor distribution. + * Uses callback-based waiting instead of polling for deterministic synchronization. + * + * @param queue the queue to dequeue from + * @param enqueueAction the action that enqueues the event (triggers event processing) + * @return the dequeued EventQueueItem, or null if queue is closed + */ + private EventQueueItem dequeueEventWithRetry(EventQueue queue, Runnable enqueueAction) throws InterruptedException { + // Wait for event to be processed and distributed + waitForEventProcessing(enqueueAction); + + // Event is now available, dequeue directly + try { + return queue.dequeueEventItem(100); + } catch (EventQueueClosedException e) { + // Queue closed, return null + return null; + } + } } \ No newline at end of file diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueUtil.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueUtil.java new file mode 100644 index 000000000..a91575aaa --- /dev/null +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/server/events/EventQueueUtil.java @@ -0,0 +1,11 @@ +package io.a2a.server.events; + +public class EventQueueUtil { + public static void start(MainEventBusProcessor processor) { + processor.start(); + } + + public static void stop(MainEventBusProcessor processor) { + processor.stop(); + } +} diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties index d0692ca53..ea64096cb 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-1/src/main/resources/application.properties @@ -34,5 +34,7 @@ quarkus.messaging.kafka.health.timeout=5s # Enable debug logging quarkus.log.category."io.a2a.server.events".level=DEBUG quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.server.tasks".level=DEBUG quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +quarkus.log.category."io.a2a.extras.taskstore.database.jpa".level=DEBUG quarkus.log.category."io.a2a.client".level=DEBUG diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties index 0b647f3a5..d9a495e8f 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-app-2/src/main/resources/application.properties @@ -34,5 +34,7 @@ quarkus.messaging.kafka.health.timeout=5s # Enable debug logging quarkus.log.category."io.a2a.server.events".level=DEBUG quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.server.tasks".level=DEBUG quarkus.log.category."io.a2a.extras.queuemanager.replicated".level=DEBUG +quarkus.log.category."io.a2a.extras.taskstore.database.jpa".level=DEBUG quarkus.log.category."io.a2a.client".level=DEBUG diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java index 93093388d..b98f0e87d 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java @@ -10,6 +10,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; @@ -254,9 +255,11 @@ public void testMultiInstanceEventReplication() throws Exception { final String taskId = "replication-test-task-" + System.currentTimeMillis(); final String contextId = "replication-test-context"; - // Step 1: Send initial message NON-streaming to create task - Message initialMessage = Message.builder(A2A.toUserMessage("Initial test message")) - .taskId(taskId) + Throwable testFailure = null; + try { + // Step 1: Send initial message NON-streaming to create task + Message initialMessage = Message.builder(A2A.toUserMessage("Initial test message")) + .taskId(taskId) .contextId(contextId) .build(); @@ -308,11 +311,33 @@ public void testMultiInstanceEventReplication() throws Exception { AtomicReference app1Error = new AtomicReference<>(); AtomicReference app2Error = new AtomicReference<>(); + AtomicBoolean app1ReceivedInitialTask = new AtomicBoolean(false); + AtomicBoolean app2ReceivedInitialTask = new AtomicBoolean(false); // App1 subscriber BiConsumer app1Subscriber = (event, card) -> { + String eventDetail = event.getClass().getSimpleName(); + if (event instanceof io.a2a.client.TaskUpdateEvent tue) { + eventDetail += " [" + tue.getUpdateEvent().getClass().getSimpleName(); + if (tue.getUpdateEvent() instanceof io.a2a.spec.TaskStatusUpdateEvent statusEvent) { + eventDetail += ", state=" + (statusEvent.status() != null ? statusEvent.status().state() : "null"); + } + eventDetail += "]"; + } else if (event instanceof io.a2a.client.TaskEvent te) { + eventDetail += " [state=" + (te.getTask().status() != null ? te.getTask().status().state() : "null") + "]"; + } + System.out.println("APP1 received event: " + eventDetail); + + // Per A2A spec 3.1.6: Handle initial TaskEvent on resubscribe + if (!app1ReceivedInitialTask.get() && event instanceof io.a2a.client.TaskEvent) { + app1ReceivedInitialTask.set(true); + System.out.println("APP1 filtered initial TaskEvent"); + // Don't count initial TaskEvent toward expected artifact/status events + return; + } app1Events.add(event); - app1EventCount.incrementAndGet(); + int count = app1EventCount.incrementAndGet(); + System.out.println("APP1 event count now: " + count + ", event: " + eventDetail); }; Consumer app1ErrorHandler = error -> { @@ -323,8 +348,28 @@ public void testMultiInstanceEventReplication() throws Exception { // App2 subscriber BiConsumer app2Subscriber = (event, card) -> { + String eventDetail = event.getClass().getSimpleName(); + if (event instanceof io.a2a.client.TaskUpdateEvent tue) { + eventDetail += " [" + tue.getUpdateEvent().getClass().getSimpleName(); + if (tue.getUpdateEvent() instanceof io.a2a.spec.TaskStatusUpdateEvent statusEvent) { + eventDetail += ", state=" + (statusEvent.status() != null ? statusEvent.status().state() : "null"); + } + eventDetail += "]"; + } else if (event instanceof io.a2a.client.TaskEvent te) { + eventDetail += " [state=" + (te.getTask().status() != null ? te.getTask().status().state() : "null") + "]"; + } + System.out.println("APP2 received event: " + eventDetail); + + // Per A2A spec 3.1.6: Handle initial TaskEvent on resubscribe + if (!app2ReceivedInitialTask.get() && event instanceof io.a2a.client.TaskEvent) { + app2ReceivedInitialTask.set(true); + System.out.println("APP2 filtered initial TaskEvent"); + // Don't count initial TaskEvent toward expected artifact/status events + return; + } app2Events.add(event); - app2EventCount.incrementAndGet(); + int count = app2EventCount.incrementAndGet(); + System.out.println("APP2 event count now: " + count + ", event: " + eventDetail); }; Consumer app2ErrorHandler = error -> { @@ -409,9 +454,45 @@ public void testMultiInstanceEventReplication() throws Exception { throw new AssertionError("App2 subscriber error", app2Error.get()); } - // Verify both received at least 3 events (could be more due to initial state events) - assertTrue(app1Events.size() >= 3, "App1 should receive at least 3 events, got: " + app1Events.size()); - assertTrue(app2Events.size() >= 3, "App2 should receive at least 3 events, got: " + app2Events.size()); + // Verify both received at least 3 events (could be more due to initial state events) + assertTrue(app1Events.size() >= 3, "App1 should receive at least 3 events, got: " + app1Events.size()); + assertTrue(app2Events.size() >= 3, "App2 should receive at least 3 events, got: " + app2Events.size()); + } catch (Throwable t) { + testFailure = t; + throw t; + } finally { + // Output container logs if test failed + if (testFailure != null) { + System.err.println("\n========================================"); + System.err.println("TEST FAILED - Dumping container logs"); + System.err.println("========================================\n"); + + dumpContainerLogs("KAFKA", kafka, 100); + dumpContainerLogs("APP1", app1, 200); + dumpContainerLogs("APP2", app2, 200); + + System.err.println("\n========================================"); + System.err.println("END OF CONTAINER LOGS"); + System.err.println("========================================\n"); + } + } + } + + /** + * Dumps the last N lines of logs from a container to stderr. + */ + private void dumpContainerLogs(String containerName, org.testcontainers.containers.ContainerState container, int lastLines) { + System.err.println("\n--- " + containerName + " LOGS (last " + lastLines + " lines) ---"); + try { + String logs = container.getLogs(); + String[] lines = logs.split("\n"); + int start = Math.max(0, lines.length - lastLines); + for (int i = start; i < lines.length; i++) { + System.err.println(lines[i]); + } + } catch (Exception e) { + System.err.println("Failed to retrieve " + containerName + " logs: " + e.getMessage()); + } } /** diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index 3825f3bad..c4b23fb0d 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -222,9 +222,21 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { AtomicReference receivedCompletedEvent = new AtomicReference<>(); AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); + AtomicBoolean receivedInitialTask = new AtomicBoolean(false); // Create consumer to handle resubscribed events BiConsumer consumer = (event, agentCard) -> { + // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent + if (!receivedInitialTask.get()) { + if (event instanceof TaskEvent) { + receivedInitialTask.set(true); + return; + } else { + throw new AssertionError("First event on resubscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); + } + } + + // Process subsequent events if (event instanceof TaskUpdateEvent taskUpdateEvent) { if (taskUpdateEvent.getUpdateEvent() instanceof TaskStatusUpdateEvent statusEvent) { if (statusEvent.status().state() == TaskState.COMPLETED) { diff --git a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java index b65b71650..a4afa8588 100644 --- a/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java +++ b/extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java @@ -65,19 +65,23 @@ void initConfig() { @Transactional @Override - public void save(Task task) { - LOGGER.debug("Saving task with ID: {}", task.id()); + public void save(Task task, boolean isReplicated) { + LOGGER.debug("Saving task with ID: {} (replicated: {})", task.id(), isReplicated); try { JpaTask jpaTask = JpaTask.createFromTask(task); em.merge(jpaTask); LOGGER.debug("Persisted/updated task with ID: {}", task.id()); - if (task.status() != null && task.status().state() != null && task.status().state().isFinal()) { + // Only fire TaskFinalizedEvent for locally-generated final states, NOT for replicated events + // This prevents feedback loops where receiving a replicated final task triggers another replication + if (!isReplicated && task.status() != null && task.status().state() != null && task.status().state().isFinal()) { // Fire CDI event if task reached final state // IMPORTANT: The event will be delivered AFTER transaction commits (AFTER_SUCCESS observers) - // This ensures the task's final state is durably stored before the QueueClosedEvent poison pill is sent - LOGGER.debug("Task {} is in final state, firing TaskFinalizedEvent", task.id()); - taskFinalizedEvent.fire(new TaskFinalizedEvent(task.id())); + // This ensures the task's final state is durably stored before the final task and poison pill are sent + LOGGER.debug("Task {} is in final state, firing TaskFinalizedEvent with full Task", task.id()); + taskFinalizedEvent.fire(new TaskFinalizedEvent(task.id(), task)); + } else if (isReplicated && task.status() != null && task.status().state() != null && task.status().state().isFinal()) { + LOGGER.debug("Task {} is in final state but from replication - NOT firing TaskFinalizedEvent (prevents feedback loop)", task.id()); } } catch (JsonProcessingException e) { LOGGER.error("Failed to serialize task with ID: {}", task.id(), e); diff --git a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java index ea77f73c7..15c01a626 100644 --- a/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java +++ b/extras/task-store-database-jpa/src/test/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStoreTest.java @@ -54,7 +54,7 @@ public void testSaveAndRetrieveTask() { .build(); // Save the task - taskStore.save(task); + taskStore.save(task, false); // Retrieve the task Task retrieved = taskStore.get("test-task-1"); @@ -84,7 +84,7 @@ public void testSaveAndRetrieveTaskWithHistory() { .build(); // Save the task - taskStore.save(task); + taskStore.save(task, false); // Retrieve the task Task retrieved = taskStore.get("test-task-2"); @@ -108,7 +108,7 @@ public void testUpdateExistingTask() { .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - taskStore.save(initialTask); + taskStore.save(initialTask, false); // Update the task Task updatedTask = Task.builder() @@ -117,7 +117,7 @@ public void testUpdateExistingTask() { .status(new TaskStatus(TaskState.COMPLETED)) .build(); - taskStore.save(updatedTask); + taskStore.save(updatedTask, false); // Retrieve and verify the update Task retrieved = taskStore.get("test-task-3"); @@ -144,7 +144,7 @@ public void testDeleteTask() { .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - taskStore.save(task); + taskStore.save(task, false); // Verify it exists assertNotNull(taskStore.get("test-task-4")); @@ -180,7 +180,7 @@ public void testTaskWithComplexMetadata() { .build(); // Save and retrieve - taskStore.save(task); + taskStore.save(task, false); Task retrieved = taskStore.get("test-task-5"); assertNotNull(retrieved); @@ -201,7 +201,7 @@ public void testIsTaskActiveForNonFinalTask() { .status(new TaskStatus(TaskState.WORKING)) .build(); - taskStore.save(task); + taskStore.save(task, false); // Task should be active (not in final state) JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; @@ -220,7 +220,7 @@ public void testIsTaskActiveForFinalTaskWithinGracePeriod() { .status(new TaskStatus(TaskState.WORKING)) .build(); - taskStore.save(task); + taskStore.save(task, false); // Update to final state Task finalTask = Task.builder() @@ -229,7 +229,7 @@ public void testIsTaskActiveForFinalTaskWithinGracePeriod() { .status(new TaskStatus(TaskState.COMPLETED)) .build(); - taskStore.save(finalTask); + taskStore.save(finalTask, false); // Task should be active (within grace period - default 15 seconds) JpaDatabaseTaskStore jpaDatabaseTaskStore = (JpaDatabaseTaskStore) taskStore; @@ -248,7 +248,7 @@ public void testIsTaskActiveForFinalTaskBeyondGracePeriod() { .status(new TaskStatus(TaskState.COMPLETED)) .build(); - taskStore.save(task); + taskStore.save(task, false); // Directly update the finalizedAt timestamp to 20 seconds in the past // (beyond the default 15-second grace period) @@ -322,9 +322,9 @@ public void testListTasksFilterByContextId() { .status(new TaskStatus(TaskState.COMPLETED)) .build(); - taskStore.save(task1); - taskStore.save(task2); - taskStore.save(task3); + taskStore.save(task1, false); + taskStore.save(task2, false); + taskStore.save(task3, false); // List tasks for context-A ListTasksParams params = ListTasksParams.builder() @@ -361,9 +361,9 @@ public void testListTasksFilterByStatus() { .status(new TaskStatus(TaskState.COMPLETED)) .build(); - taskStore.save(task1); - taskStore.save(task2); - taskStore.save(task3); + taskStore.save(task1, false); + taskStore.save(task2, false); + taskStore.save(task3, false); // List only WORKING tasks in this context ListTasksParams params = ListTasksParams.builder() @@ -401,9 +401,9 @@ public void testListTasksCombinedFilters() { .status(new TaskStatus(TaskState.WORKING)) .build(); - taskStore.save(task1); - taskStore.save(task2); - taskStore.save(task3); + taskStore.save(task1, false); + taskStore.save(task2, false); + taskStore.save(task3, false); // List WORKING tasks in context-X ListTasksParams params = ListTasksParams.builder() @@ -432,7 +432,7 @@ public void testListTasksPagination() { .contextId("context-pagination") .status(new TaskStatus(TaskState.SUBMITTED, null, sameTimestamp)) .build(); - taskStore.save(task); + taskStore.save(task, false); } // First page: pageSize=2 @@ -488,7 +488,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(10))) .build(); - taskStore.save(task1); + taskStore.save(task1, false); // Task 2: 5 minutes ago, ID="task-diff-b" Task task2 = Task.builder() @@ -496,7 +496,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(5))) .build(); - taskStore.save(task2); + taskStore.save(task2, false); // Task 3: 5 minutes ago, ID="task-diff-c" (same timestamp as task2, tests ID tie-breaker) Task task3 = Task.builder() @@ -504,7 +504,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(5))) .build(); - taskStore.save(task3); + taskStore.save(task3, false); // Task 4: Now, ID="task-diff-d" Task task4 = Task.builder() @@ -512,7 +512,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now)) .build(); - taskStore.save(task4); + taskStore.save(task4, false); // Task 5: 1 minute ago, ID="task-diff-e" Task task5 = Task.builder() @@ -520,7 +520,7 @@ public void testListTasksPaginationWithDifferentTimestamps() { .contextId("context-diff-timestamps") .status(new TaskStatus(TaskState.WORKING, null, now.minusMinutes(1))) .build(); - taskStore.save(task5); + taskStore.save(task5, false); // Expected order (timestamp DESC, id ASC): // 1. task-diff-d (now) @@ -616,7 +616,7 @@ public void testListTasksHistoryLimiting() { .history(longHistory) .build(); - taskStore.save(task); + taskStore.save(task, false); // List with historyLength=3 (should keep only last 3 messages) - filter by unique context ListTasksParams params = ListTasksParams.builder() @@ -654,7 +654,7 @@ public void testListTasksArtifactInclusion() { .artifacts(artifacts) .build(); - taskStore.save(task); + taskStore.save(task, false); // List without artifacts (default) - filter by unique context ListTasksParams paramsWithoutArtifacts = ListTasksParams.builder() @@ -691,7 +691,7 @@ public void testListTasksDefaultPageSize() { .contextId("context-default-pagesize") .status(new TaskStatus(TaskState.SUBMITTED)) .build(); - taskStore.save(task); + taskStore.save(task, false); } // List without specifying pageSize (should use default of 50) @@ -715,7 +715,7 @@ public void testListTasksInvalidPageTokenFormat() { .contextId("context-invalid-token") .status(new TaskStatus(TaskState.WORKING)) .build(); - taskStore.save(task); + taskStore.save(task, false); // Test 1: Legacy ID-only pageToken should throw InvalidParamsError ListTasksParams params1 = ListTasksParams.builder() @@ -777,9 +777,9 @@ public void testListTasksOrderingById() { .build(); // Save in reverse order - taskStore.save(task3); - taskStore.save(task1); - taskStore.save(task2); + taskStore.save(task3, false); + taskStore.save(task1, false); + taskStore.save(task2, false); // List should return sorted by timestamp DESC (all same), then by ID ASC ListTasksParams params = ListTasksParams.builder() diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 18e18a2f1..cb5bdb25b 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -13,7 +13,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; @@ -21,6 +20,7 @@ import com.google.gson.JsonSyntaxException; import io.a2a.common.A2AHeaders; +import io.a2a.server.util.sse.SseFormatter; import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.jsonrpc.common.json.IdJsonMappingException; import io.a2a.jsonrpc.common.json.InvalidParamsJsonMappingException; @@ -65,7 +65,6 @@ import io.a2a.transport.jsonrpc.handler.JSONRPCHandler; import io.quarkus.security.Authenticated; import io.quarkus.vertx.web.Body; -import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; import io.smallrye.mutiny.Multi; import io.vertx.core.AsyncResult; @@ -74,6 +73,8 @@ import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Singleton public class A2AServerRoutes { @@ -135,8 +136,12 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { } else if (streaming) { final Multi> finalStreamingResponse = streamingResponse; executor.execute(() -> { - MultiSseSupport.subscribeObject( - finalStreamingResponse.map(i -> (Object) i), rc); + // Convert Multi to Multi with SSE formatting + AtomicLong eventIdCounter = new AtomicLong(0); + Multi sseEvents = finalStreamingResponse + .map(response -> SseFormatter.formatResponseAsSSE(response, eventIdCounter.getAndIncrement())); + // Write SSE-formatted strings to HTTP response + MultiSseSupport.writeSseStrings(sseEvents, rc, context); }); } else { @@ -295,34 +300,30 @@ private static com.google.protobuf.MessageOrBuilder convertToProto(A2AResponse + * This class only handles HTTP-specific concerns (writing to response, backpressure, disconnect). + * SSE formatting and JSON serialization are handled by {@link SseFormatter}. + */ private static class MultiSseSupport { + private static final Logger logger = LoggerFactory.getLogger(MultiSseSupport.class); private MultiSseSupport() { // Avoid direct instantiation. } - private static void initialize(HttpServerResponse response) { - if (response.bytesWritten() == 0) { - MultiMap headers = response.headers(); - if (headers.get(CONTENT_TYPE) == null) { - headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); - } - response.setChunked(true); - } - } - - private static void onWriteDone(Flow.Subscription subscription, AsyncResult ar, RoutingContext rc) { - if (ar.failed()) { - rc.fail(ar.cause()); - } else { - subscription.request(1); - } - } - - public static void write(Multi multi, RoutingContext rc) { + /** + * Write SSE-formatted strings to HTTP response. + * + * @param sseStrings Multi stream of SSE-formatted strings (from SseFormatter) + * @param rc Vert.x routing context + * @param context A2A server call context (for EventConsumer cancellation) + */ + public static void writeSseStrings(Multi sseStrings, RoutingContext rc, ServerCallContext context) { HttpServerResponse response = rc.response(); - multi.subscribe().withSubscriber(new Flow.Subscriber() { + + sseStrings.subscribe().withSubscriber(new Flow.Subscriber() { Flow.Subscription upstream; @Override @@ -330,6 +331,13 @@ public void onSubscribe(Flow.Subscription subscription) { this.upstream = subscription; this.upstream.request(1); + // Detect client disconnect and call EventConsumer.cancel() directly + response.closeHandler(v -> { + logger.info("SSE connection closed by client, calling EventConsumer.cancel() to stop polling loop"); + context.invokeEventConsumerCancelCallback(); + subscription.cancel(); + }); + // Notify tests that we are subscribed Runnable runnable = streamingMultiSseSupportSubscribedRunnable; if (runnable != null) { @@ -338,54 +346,50 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Buffer item) { - initialize(response); - response.write(item, new Handler>() { + public void onNext(String sseEvent) { + // Set SSE headers on first event + if (response.bytesWritten() == 0) { + MultiMap headers = response.headers(); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); + } + response.setChunked(true); + } + + // Write SSE-formatted string to response + response.write(Buffer.buffer(sseEvent), new Handler>() { @Override public void handle(AsyncResult ar) { - onWriteDone(upstream, ar, rc); + if (ar.failed()) { + // Client disconnected or write failed - cancel upstream to stop EventConsumer + upstream.cancel(); + rc.fail(ar.cause()); + } else { + upstream.request(1); + } } }); } @Override public void onError(Throwable throwable) { + // Cancel upstream to stop EventConsumer when error occurs + upstream.cancel(); rc.fail(throwable); } @Override public void onComplete() { - endOfStream(response); - } - }); - } - - public static void subscribeObject(Multi multi, RoutingContext rc) { - AtomicLong count = new AtomicLong(); - write(multi.map(new Function() { - @Override - public Buffer apply(Object o) { - if (o instanceof ReactiveRoutes.ServerSentEvent) { - ReactiveRoutes.ServerSentEvent ev = (ReactiveRoutes.ServerSentEvent) o; - long id = ev.id() != -1 ? ev.id() : count.getAndIncrement(); - String e = ev.event() == null ? "" : "event: " + ev.event() + "\n"; - String data = serializeResponse((A2AResponse) ev.data()); - return Buffer.buffer(e + "data: " + data + "\nid: " + id + "\n\n"); + if (response.bytesWritten() == 0) { + // No events written - still set SSE content type + MultiMap headers = response.headers(); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); + } } - String data = serializeResponse((A2AResponse) o); - return Buffer.buffer("data: " + data + "\nid: " + count.getAndIncrement() + "\n\n"); - } - }), rc); - } - - private static void endOfStream(HttpServerResponse response) { - if (response.bytesWritten() == 0) { // No item - MultiMap headers = response.headers(); - if (headers.get(CONTENT_TYPE) == null) { - headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); + response.end(); } - } - response.end(); + }); } } } diff --git a/reference/jsonrpc/src/test/resources/application.properties b/reference/jsonrpc/src/test/resources/application.properties index 7b9cea9cc..e612925d4 100644 --- a/reference/jsonrpc/src/test/resources/application.properties +++ b/reference/jsonrpc/src/test/resources/application.properties @@ -1 +1,6 @@ quarkus.arc.selected-alternatives=io.a2a.server.apps.common.TestHttpClient + +# Debug logging for event processing and request handling +quarkus.log.category."io.a2a.server.events".level=DEBUG +quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG +quarkus.log.category."io.a2a.server.tasks".level=DEBUG diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 46d0d38e6..7a50f0afb 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -15,7 +15,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; + +import io.a2a.server.util.sse.SseFormatter; import jakarta.annotation.security.PermitAll; import jakarta.enterprise.inject.Instance; @@ -38,7 +39,6 @@ import io.a2a.util.Utils; import io.quarkus.security.Authenticated; import io.quarkus.vertx.web.Body; -import io.quarkus.vertx.web.ReactiveRoutes; import io.quarkus.vertx.web.Route; import io.smallrye.mutiny.Multi; import io.vertx.core.AsyncResult; @@ -110,10 +110,14 @@ public void sendMessageStreaming(@Body String body, RoutingContext rc) { if (error != null) { sendResponse(rc, error); } else if (streamingResponse != null) { - Multi events = Multi.createFrom().publisher(streamingResponse.getPublisher()); + final HTTPRestStreamingResponse finalStreamingResponse = streamingResponse; executor.execute(() -> { - MultiSseSupport.subscribeObject( - events.map(i -> (Object) i), rc); + // Convert Flow.Publisher (JSON) to Multi (SSE-formatted) + AtomicLong eventIdCounter = new AtomicLong(0); + Multi sseEvents = Multi.createFrom().publisher(finalStreamingResponse.getPublisher()) + .map(json -> SseFormatter.formatJsonAsSSE(json, eventIdCounter.getAndIncrement())); + // Write SSE-formatted strings to HTTP response + MultiSseSupport.writeSseStrings(sseEvents, rc, context); }); } } @@ -243,10 +247,14 @@ public void subscribeToTask(RoutingContext rc) { if (error != null) { sendResponse(rc, error); } else if (streamingResponse != null) { - Multi events = Multi.createFrom().publisher(streamingResponse.getPublisher()); + final HTTPRestStreamingResponse finalStreamingResponse = streamingResponse; executor.execute(() -> { - MultiSseSupport.subscribeObject( - events.map(i -> (Object) i), rc); + // Convert Flow.Publisher (JSON) to Multi (SSE-formatted) + AtomicLong eventIdCounter = new AtomicLong(0); + Multi sseEvents = Multi.createFrom().publisher(finalStreamingResponse.getPublisher()) + .map(json -> SseFormatter.formatJsonAsSSE(json, eventIdCounter.getAndIncrement())); + // Write SSE-formatted strings to HTTP response + MultiSseSupport.writeSseStrings(sseEvents, rc, context); }); } } @@ -450,34 +458,30 @@ public String getUsername() { } } - // Port of import io.quarkus.vertx.web.runtime.MultiSseSupport, which is considered internal API + /** + * Simplified SSE support for Vert.x/Quarkus. + *

    + * This class only handles HTTP-specific concerns (writing to response, backpressure, disconnect). + * SSE formatting and JSON serialization are handled by {@link SseFormatter}. + */ private static class MultiSseSupport { + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MultiSseSupport.class); private MultiSseSupport() { // Avoid direct instantiation. } - private static void initialize(HttpServerResponse response) { - if (response.bytesWritten() == 0) { - MultiMap headers = response.headers(); - if (headers.get(CONTENT_TYPE) == null) { - headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); - } - response.setChunked(true); - } - } - - private static void onWriteDone(Flow.@Nullable Subscription subscription, AsyncResult ar, RoutingContext rc) { - if (ar.failed()) { - rc.fail(ar.cause()); - } else if (subscription != null) { - subscription.request(1); - } - } - - private static void write(Multi multi, RoutingContext rc) { + /** + * Write SSE-formatted strings to HTTP response. + * + * @param sseStrings Multi stream of SSE-formatted strings (from SseFormatter) + * @param rc Vert.x routing context + * @param context A2A server call context (for EventConsumer cancellation) + */ + public static void writeSseStrings(Multi sseStrings, RoutingContext rc, ServerCallContext context) { HttpServerResponse response = rc.response(); - multi.subscribe().withSubscriber(new Flow.Subscriber() { + + sseStrings.subscribe().withSubscriber(new Flow.Subscriber() { Flow.@Nullable Subscription upstream; @Override @@ -485,6 +489,13 @@ public void onSubscribe(Flow.Subscription subscription) { this.upstream = subscription; this.upstream.request(1); + // Detect client disconnect and call EventConsumer.cancel() directly + response.closeHandler(v -> { + logger.debug("REST SSE connection closed by client, calling EventConsumer.cancel() to stop polling loop"); + context.invokeEventConsumerCancelCallback(); + subscription.cancel(); + }); + // Notify tests that we are subscribed Runnable runnable = streamingMultiSseSupportSubscribedRunnable; if (runnable != null) { @@ -493,53 +504,64 @@ public void onSubscribe(Flow.Subscription subscription) { } @Override - public void onNext(Buffer item) { - initialize(response); - response.write(item, new Handler>() { + public void onNext(String sseEvent) { + // Set SSE headers on first event + if (response.bytesWritten() == 0) { + MultiMap headers = response.headers(); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); + } + // Additional SSE headers to prevent buffering + headers.set("Cache-Control", "no-cache"); + headers.set("X-Accel-Buffering", "no"); // Disable nginx buffering + response.setChunked(true); + + // CRITICAL: Disable write queue max size to prevent buffering + // Vert.x buffers writes by default - we need immediate flushing for SSE + response.setWriteQueueMaxSize(1); // Force immediate flush + + // Send initial SSE comment to kickstart the stream + // This forces Vert.x to send headers and start the stream immediately + response.write(": SSE stream started\n\n"); + } + + // Write SSE-formatted string to response + response.write(Buffer.buffer(sseEvent), new Handler>() { @Override public void handle(AsyncResult ar) { - onWriteDone(upstream, ar, rc); + if (ar.failed()) { + // Client disconnected or write failed - cancel upstream to stop EventConsumer + // NullAway: upstream is guaranteed non-null after onSubscribe + java.util.Objects.requireNonNull(upstream).cancel(); + rc.fail(ar.cause()); + } else { + // NullAway: upstream is guaranteed non-null after onSubscribe + java.util.Objects.requireNonNull(upstream).request(1); + } } }); } @Override public void onError(Throwable throwable) { + // Cancel upstream to stop EventConsumer when error occurs + // NullAway: upstream is guaranteed non-null after onSubscribe + java.util.Objects.requireNonNull(upstream).cancel(); rc.fail(throwable); } @Override public void onComplete() { - endOfStream(response); - } - }); - } - - private static void subscribeObject(Multi multi, RoutingContext rc) { - AtomicLong count = new AtomicLong(); - write(multi.map(new Function() { - @Override - public Buffer apply(Object o) { - if (o instanceof ReactiveRoutes.ServerSentEvent) { - ReactiveRoutes.ServerSentEvent ev = (ReactiveRoutes.ServerSentEvent) o; - long id = ev.id() != -1 ? ev.id() : count.getAndIncrement(); - String e = ev.event() == null ? "" : "event: " + ev.event() + "\n"; - return Buffer.buffer(e + "data: " + ev.data() + "\nid: " + id + "\n\n"); - } else { - return Buffer.buffer("data: " + o + "\nid: " + count.getAndIncrement() + "\n\n"); + if (response.bytesWritten() == 0) { + // No events written - still set SSE content type + MultiMap headers = response.headers(); + if (headers.get(CONTENT_TYPE) == null) { + headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); + } } + response.end(); } - }), rc); - } - - private static void endOfStream(HttpServerResponse response) { - if (response.bytesWritten() == 0) { // No item - MultiMap headers = response.headers(); - if (headers.get(CONTENT_TYPE) == null) { - headers.set(CONTENT_TYPE, SERVER_SENT_EVENTS); - } - } - response.end(); + }); } } diff --git a/server-common/src/main/java/io/a2a/server/ServerCallContext.java b/server-common/src/main/java/io/a2a/server/ServerCallContext.java index ba5c20b95..c12c60c21 100644 --- a/server-common/src/main/java/io/a2a/server/ServerCallContext.java +++ b/server-common/src/main/java/io/a2a/server/ServerCallContext.java @@ -16,6 +16,7 @@ public class ServerCallContext { private final Set requestedExtensions; private final Set activatedExtensions; private final @Nullable String requestedProtocolVersion; + private volatile @Nullable Runnable eventConsumerCancelCallback; public ServerCallContext(User user, Map state, Set requestedExtensions) { this(user, state, requestedExtensions, null); @@ -64,4 +65,64 @@ public boolean isExtensionRequested(String extensionUri) { public @Nullable String getRequestedProtocolVersion() { return requestedProtocolVersion; } + + /** + * Sets the callback to be invoked when the client disconnects or the call is cancelled. + *

    + * This callback is typically used to stop the EventConsumer polling loop when a client + * disconnects from a streaming endpoint. The callback is invoked by transport layers + * (JSON-RPC over HTTP/SSE, REST over HTTP/SSE, gRPC streaming) when they detect that + * the client has closed the connection. + *

    + *

    + * Thread Safety: The callback may be invoked from any thread, depending + * on the transport implementation. Implementations should be thread-safe. + *

    + * Example Usage: + *
    {@code
    +     * EventConsumer consumer = new EventConsumer(queue);
    +     * context.setEventConsumerCancelCallback(consumer::cancel);
    +     * }
    + * + * @param callback the callback to invoke on client disconnect, or null to clear any existing callback + * @see #invokeEventConsumerCancelCallback() + */ + public void setEventConsumerCancelCallback(@Nullable Runnable callback) { + this.eventConsumerCancelCallback = callback; + } + + /** + * Invokes the EventConsumer cancel callback if one has been set. + *

    + * This method is called by transport layers when a client disconnects or cancels a + * streaming request. It triggers the callback registered via + * {@link #setEventConsumerCancelCallback(Runnable)}, which typically stops the + * EventConsumer polling loop. + *

    + *

    + * Transport-Specific Behavior: + *

    + *
      + *
    • JSON-RPC/REST over HTTP/SSE: Called from Vert.x + * {@code HttpServerResponse.closeHandler()} when the SSE connection is closed
    • + *
    • gRPC streaming: Called from gRPC + * {@code Context.CancellationListener.cancelled()} when the call is cancelled
    • + *
    + *

    + * Thread Safety: This method is thread-safe. The callback is stored + * in a volatile field and null-checked before invocation to prevent race conditions. + *

    + *

    + * If no callback has been set, this method does nothing (no-op). + *

    + * + * @see #setEventConsumerCancelCallback(Runnable) + * @see io.a2a.server.events.EventConsumer#cancel() + */ + public void invokeEventConsumerCancelCallback() { + Runnable callback = this.eventConsumerCancelCallback; + if (callback != null) { + callback.run(); + } + } } diff --git a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java index d4fe5b395..7c7b28452 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventConsumer.java +++ b/server-common/src/main/java/io/a2a/server/events/EventConsumer.java @@ -19,10 +19,17 @@ public class EventConsumer { private static final Logger LOGGER = LoggerFactory.getLogger(EventConsumer.class); private final EventQueue queue; private volatile @Nullable Throwable error; + private volatile boolean cancelled = false; + private volatile boolean agentCompleted = false; + private volatile int pollTimeoutsAfterAgentCompleted = 0; private static final String ERROR_MSG = "Agent did not return any response"; private static final int NO_WAIT = -1; private static final int QUEUE_WAIT_MILLISECONDS = 500; + // In replicated scenarios, events can arrive hundreds of milliseconds after local agent completes + // Grace period allows Kafka replication to deliver late-arriving events + // 3 timeouts * 500ms = 1500ms grace period for replication delays + private static final int MAX_POLL_TIMEOUTS_AFTER_AGENT_COMPLETED = 3; public EventConsumer(EventQueue queue) { this.queue = queue; @@ -45,6 +52,14 @@ public Flow.Publisher consumeAll() { boolean completed = false; try { while (true) { + // Check if cancelled by client disconnect + if (cancelled) { + LOGGER.debug("EventConsumer detected cancellation, exiting polling loop for queue {}", System.identityHashCode(queue)); + completed = true; + tube.complete(); + return; + } + if (error != null) { completed = true; tube.fail(error); @@ -60,13 +75,49 @@ public Flow.Publisher consumeAll() { EventQueueItem item; Event event; try { + LOGGER.debug("EventConsumer polling queue {} (error={}, agentCompleted={})", + System.identityHashCode(queue), error, agentCompleted); item = queue.dequeueEventItem(QUEUE_WAIT_MILLISECONDS); if (item == null) { + int queueSize = queue.size(); + LOGGER.debug("EventConsumer poll timeout (null item), agentCompleted={}, queue.size()={}, timeoutCount={}", + agentCompleted, queueSize, pollTimeoutsAfterAgentCompleted); + // If agent completed, a poll timeout means no more events are coming + // MainEventBusProcessor has 500ms to distribute events from MainEventBus + // If we timeout with agentCompleted=true, all events have been distributed + // + // IMPORTANT: In replicated scenarios, remote events may arrive AFTER local agent completes! + // Use grace period to allow for Kafka replication delays (can be 400-500ms) + if (agentCompleted && queueSize == 0) { + pollTimeoutsAfterAgentCompleted++; + if (pollTimeoutsAfterAgentCompleted >= MAX_POLL_TIMEOUTS_AFTER_AGENT_COMPLETED) { + LOGGER.debug("Agent completed with {} consecutive poll timeouts and empty queue, closing for graceful completion (queue={})", + pollTimeoutsAfterAgentCompleted, System.identityHashCode(queue)); + queue.close(); + completed = true; + tube.complete(); + return; + } else { + LOGGER.debug("Agent completed but grace period active ({}/{} timeouts), continuing to poll (queue={})", + pollTimeoutsAfterAgentCompleted, MAX_POLL_TIMEOUTS_AFTER_AGENT_COMPLETED, System.identityHashCode(queue)); + } + } else if (agentCompleted && queueSize > 0) { + LOGGER.debug("Agent completed but queue has {} pending events, resetting timeout counter and continuing to poll (queue={})", + queueSize, System.identityHashCode(queue)); + pollTimeoutsAfterAgentCompleted = 0; // Reset counter when events arrive + } continue; } + // Event received - reset timeout counter + pollTimeoutsAfterAgentCompleted = 0; event = item.getEvent(); + LOGGER.debug("EventConsumer received event: {} (queue={})", + event.getClass().getSimpleName(), System.identityHashCode(queue)); + // Defensive logging for error handling if (event instanceof Throwable thr) { + LOGGER.debug("EventConsumer detected Throwable event: {} - triggering tube.fail()", + thr.getClass().getSimpleName()); tube.fail(thr); return; } @@ -90,14 +141,30 @@ public Flow.Publisher consumeAll() { // Only send event if it's not a QueueClosedEvent // QueueClosedEvent is an internal coordination event used for replication // and should not be exposed to API consumers + boolean isFinalSent = false; if (!(event instanceof QueueClosedEvent)) { tube.send(item); + isFinalSent = isFinalEvent; } if (isFinalEvent) { LOGGER.debug("Final or interrupted event detected, closing queue and breaking loop for queue {}", System.identityHashCode(queue)); queue.close(); LOGGER.debug("Queue closed, breaking loop for queue {}", System.identityHashCode(queue)); + + // CRITICAL: Allow tube buffer to flush before calling tube.complete() + // tube.send() buffers events asynchronously. If we call tube.complete() immediately, + // the stream-end signal can reach the client BEFORE the buffered final event, + // causing the client to close the connection and never receive the final event. + // This is especially important in replicated scenarios where events arrive via Kafka + // and timing is less deterministic. A small delay ensures the buffer flushes. + if (isFinalSent) { + try { + Thread.sleep(50); // 50ms to allow SSE buffer flush + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } break; } } catch (EventQueueClosedException e) { @@ -138,12 +205,25 @@ private boolean isStreamTerminatingTask(Task task) { public EnhancedRunnable.DoneCallback createAgentRunnableDoneCallback() { return agentRunnable -> { + LOGGER.debug("EventConsumer: Agent done callback invoked (hasError={}, queue={})", + agentRunnable.getError() != null, System.identityHashCode(queue)); if (agentRunnable.getError() != null) { error = agentRunnable.getError(); + LOGGER.debug("EventConsumer: Set error field from agent callback"); + } else { + agentCompleted = true; + LOGGER.debug("EventConsumer: Agent completed successfully, set agentCompleted=true, will close queue after draining"); } }; } + public void cancel() { + // Set cancellation flag to stop polling loop + // Called when client disconnects without completing stream + LOGGER.debug("EventConsumer cancelled (client disconnect), stopping polling for queue {}", System.identityHashCode(queue)); + cancelled = true; + } + public void close() { // Close the queue to stop the polling loop in consumeAll() // This will cause EventQueueClosedException and exit the while(true) loop diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index a08f63084..99f8bc2dc 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -1,6 +1,7 @@ package io.a2a.server.events; import java.util.List; +import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -11,6 +12,8 @@ import io.a2a.server.tasks.TaskStateProvider; import io.a2a.spec.Event; +import io.a2a.spec.Task; +import io.a2a.spec.TaskStatusUpdateEvent; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +26,7 @@ * and hierarchical queue structures via MainQueue and ChildQueue implementations. *

    *

    - * Use {@link #builder()} to create configured instances or extend MainQueue/ChildQueue directly. + * Use {@link #builder(MainEventBus)} to create configured instances or extend MainQueue/ChildQueue directly. *

    */ public abstract class EventQueue implements AutoCloseable { @@ -36,14 +39,6 @@ public abstract class EventQueue implements AutoCloseable { public static final int DEFAULT_QUEUE_SIZE = 1000; private final int queueSize; - /** - * Internal blocking queue for storing event queue items. - */ - protected final BlockingQueue queue = new LinkedBlockingDeque<>(); - /** - * Semaphore for backpressure control, limiting the number of pending events. - */ - protected final Semaphore semaphore; private volatile boolean closed = false; /** @@ -64,7 +59,6 @@ protected EventQueue(int queueSize) { throw new IllegalArgumentException("Queue size must be greater than 0"); } this.queueSize = queueSize; - this.semaphore = new Semaphore(queueSize, true); LOGGER.trace("Creating {} with queue size: {}", this, queueSize); } @@ -78,8 +72,8 @@ protected EventQueue(EventQueue parent) { LOGGER.trace("Creating {}, parent: {}", this, parent); } - static EventQueueBuilder builder() { - return new EventQueueBuilder(); + static EventQueueBuilder builder(MainEventBus mainEventBus) { + return new EventQueueBuilder().mainEventBus(mainEventBus); } /** @@ -95,6 +89,7 @@ public static class EventQueueBuilder { private @Nullable String taskId; private List onCloseCallbacks = new java.util.ArrayList<>(); private @Nullable TaskStateProvider taskStateProvider; + private @Nullable MainEventBus mainEventBus; /** * Sets the maximum queue size. @@ -153,17 +148,31 @@ public EventQueueBuilder taskStateProvider(TaskStateProvider taskStateProvider) return this; } + /** + * Sets the main event bus + * + * @param mainEventBus the main event bus + * @return this builder + */ + public EventQueueBuilder mainEventBus(MainEventBus mainEventBus) { + this.mainEventBus = mainEventBus; + return this; + } + /** * Builds and returns the configured EventQueue. * * @return a new MainQueue instance */ public EventQueue build() { - if (hook != null || !onCloseCallbacks.isEmpty() || taskStateProvider != null) { - return new MainQueue(queueSize, hook, taskId, onCloseCallbacks, taskStateProvider); - } else { - return new MainQueue(queueSize); + // MainEventBus is REQUIRED - enforce single architectural path + if (mainEventBus == null) { + throw new IllegalStateException("MainEventBus is required for EventQueue creation"); } + if (taskId == null) { + throw new IllegalStateException("taskId is required for EventQueue creation"); + } + return new MainQueue(queueSize, hook, taskId, onCloseCallbacks, taskStateProvider, mainEventBus); } } @@ -209,21 +218,39 @@ public void enqueueEvent(Event event) { * @param item the event queue item to enqueue * @throws RuntimeException if interrupted while waiting to acquire the semaphore */ - public void enqueueItem(EventQueueItem item) { - Event event = item.getEvent(); - if (closed) { - LOGGER.warn("Queue is closed. Event will not be enqueued. {} {}", this, event); - return; - } - // Call toString() since for errors we don't really want the full stacktrace - try { - semaphore.acquire(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("Unable to acquire the semaphore to enqueue the event", e); - } - queue.add(item); - LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); + public abstract void enqueueItem(EventQueueItem item); + + /** + * Enqueues an event directly to this specific queue only, bypassing the MainEventBus. + *

    + * This method is used for enqueuing already-persisted events (e.g., current task state + * on resubscribe) that should only be sent to this specific subscriber, not distributed + * to all children or sent through MainEventBusProcessor. + *

    + *

    + * Default implementation throws UnsupportedOperationException. Only ChildQueue supports this. + *

    + * + * @param item the event queue item to enqueue directly + * @throws UnsupportedOperationException if called on MainQueue or other queue types + */ + public void enqueueLocalOnly(EventQueueItem item) { + throw new UnsupportedOperationException( + "enqueueLocalOnly is only supported on ChildQueue for resubscribe scenarios"); + } + + /** + * Enqueues an event directly to this specific queue only, bypassing the MainEventBus. + *

    + * Convenience method that wraps the event in a LocalEventQueueItem before calling + * {@link #enqueueLocalOnly(EventQueueItem)}. + *

    + * + * @param event the event to enqueue directly + * @throws UnsupportedOperationException if called on MainQueue or other queue types + */ + public void enqueueEventLocalOnly(Event event) { + enqueueLocalOnly(new LocalEventQueueItem(event)); } /** @@ -244,48 +271,17 @@ public void enqueueItem(EventQueueItem item) { * This method returns the full EventQueueItem wrapper, allowing callers to check * metadata like whether the event is replicated via {@link EventQueueItem#isReplicated()}. *

    + *

    + * Note: MainQueue does not support dequeue operations - only ChildQueues can be consumed. + *

    * * @param waitMilliSeconds the maximum time to wait in milliseconds * @return the EventQueueItem, or null if timeout occurs * @throws EventQueueClosedException if the queue is closed and empty + * @throws UnsupportedOperationException if called on MainQueue */ - public @Nullable EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { - if (closed && queue.isEmpty()) { - LOGGER.debug("Queue is closed, and empty. Sending termination message. {}", this); - throw new EventQueueClosedException(); - } - try { - if (waitMilliSeconds <= 0) { - EventQueueItem item = queue.poll(); - if (item != null) { - Event event = item.getEvent(); - // Call toString() since for errors we don't really want the full stacktrace - LOGGER.debug("Dequeued event item (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); - semaphore.release(); - } - return item; - } - try { - LOGGER.trace("Polling queue {} (wait={}ms)", System.identityHashCode(this), waitMilliSeconds); - EventQueueItem item = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); - if (item != null) { - Event event = item.getEvent(); - // Call toString() since for errors we don't really want the full stacktrace - LOGGER.debug("Dequeued event item (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); - semaphore.release(); - } else { - LOGGER.trace("Dequeue timeout (null) from queue {}", System.identityHashCode(this)); - } - return item; - } catch (InterruptedException e) { - LOGGER.debug("Interrupted dequeue (waiting) {}", this); - Thread.currentThread().interrupt(); - return null; - } - } finally { - signalQueuePollerStarted(); - } - } + @Nullable + public abstract EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException; /** * Placeholder method for task completion notification. @@ -295,6 +291,18 @@ public void taskDone() { // TODO Not sure if needed yet. BlockingQueue.poll()/.take() remove the events. } + /** + * Returns the current size of the queue. + *

    + * For MainQueue: returns the number of events in-flight (in MainEventBus queue + currently being processed). + * This reflects actual capacity usage tracked by the semaphore. + * For ChildQueue: returns the size of the local consumption queue. + *

    + * + * @return the number of events currently in the queue + */ + public abstract int size(); + /** * Closes this event queue gracefully, allowing pending events to be consumed. */ @@ -348,72 +356,71 @@ protected void doClose(boolean immediate) { LOGGER.debug("Closing {} (immediate={})", this, immediate); closed = true; } - - if (immediate) { - // Immediate close: clear pending events - queue.clear(); - LOGGER.debug("Cleared queue for immediate close: {}", this); - } - // For graceful close, let the queue drain naturally through normal consumption + // Subclasses handle immediate close logic (e.g., ChildQueue clears its local queue) } static class MainQueue extends EventQueue { private final List children = new CopyOnWriteArrayList<>(); + protected final Semaphore semaphore; private final CountDownLatch pollingStartedLatch = new CountDownLatch(1); private final AtomicBoolean pollingStarted = new AtomicBoolean(false); private final @Nullable EventEnqueueHook enqueueHook; - private final @Nullable String taskId; + private final String taskId; private final List onCloseCallbacks; private final @Nullable TaskStateProvider taskStateProvider; - - MainQueue() { - super(); - this.enqueueHook = null; - this.taskId = null; - this.onCloseCallbacks = List.of(); - this.taskStateProvider = null; - } - - MainQueue(int queueSize) { - super(queueSize); - this.enqueueHook = null; - this.taskId = null; - this.onCloseCallbacks = List.of(); - this.taskStateProvider = null; - } - - MainQueue(EventEnqueueHook hook) { - super(); - this.enqueueHook = hook; - this.taskId = null; - this.onCloseCallbacks = List.of(); - this.taskStateProvider = null; - } - - MainQueue(int queueSize, EventEnqueueHook hook) { - super(queueSize); - this.enqueueHook = hook; - this.taskId = null; - this.onCloseCallbacks = List.of(); - this.taskStateProvider = null; - } - - MainQueue(int queueSize, @Nullable EventEnqueueHook hook, @Nullable String taskId, List onCloseCallbacks, @Nullable TaskStateProvider taskStateProvider) { + private final MainEventBus mainEventBus; + + MainQueue(int queueSize, + @Nullable EventEnqueueHook hook, + String taskId, + List onCloseCallbacks, + @Nullable TaskStateProvider taskStateProvider, + @Nullable MainEventBus mainEventBus) { super(queueSize); + this.semaphore = new Semaphore(queueSize, true); this.enqueueHook = hook; this.taskId = taskId; this.onCloseCallbacks = List.copyOf(onCloseCallbacks); // Defensive copy this.taskStateProvider = taskStateProvider; - LOGGER.debug("Created MainQueue for task {} with {} onClose callbacks and TaskStateProvider: {}", + this.mainEventBus = Objects.requireNonNull(mainEventBus, "MainEventBus is required"); + LOGGER.debug("Created MainQueue for task {} with {} onClose callbacks, TaskStateProvider: {}, MainEventBus configured", taskId, onCloseCallbacks.size(), taskStateProvider != null); } + public EventQueue tap() { ChildQueue child = new ChildQueue(this); children.add(child); return child; } + /** + * Returns the current number of child queues. + * Useful for debugging and logging event distribution. + */ + public int getChildCount() { + return children.size(); + } + + /** + * Returns the enqueue hook for replication (package-protected for MainEventBusProcessor). + */ + @Nullable EventEnqueueHook getEnqueueHook() { + return enqueueHook; + } + + @Override + public EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { + throw new UnsupportedOperationException("MainQueue cannot be consumed directly - use tap() to create a ChildQueue for consumption"); + } + + @Override + public int size() { + // Return total in-flight events (in MainEventBus + being processed) + // This aligns with semaphore's capacity tracking + return getQueueSize() - semaphore.availablePermits(); + } + @Override public void enqueueItem(EventQueueItem item) { // MainQueue must accept events even when closed to support: @@ -424,6 +431,15 @@ public void enqueueItem(EventQueueItem item) { // We bypass the parent's closed check and enqueue directly Event event = item.getEvent(); + // Check if this is a final event BEFORE submitting to MainEventBus + // If it is, notify all children to expect it (so they wait for MainEventBusProcessor) + if (isFinalEvent(event)) { + LOGGER.debug("Final event detected, notifying {} children to expect it", children.size()); + for (ChildQueue child : children) { + child.expectFinalEvent(); + } + } + // Acquire semaphore for backpressure try { semaphore.acquire(); @@ -432,17 +448,27 @@ public void enqueueItem(EventQueueItem item) { throw new RuntimeException("Unable to acquire the semaphore to enqueue the event", e); } - // Add to this MainQueue's internal queue - queue.add(item); LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); - // Distribute to all ChildQueues (they will receive the event even if MainQueue is closed) - children.forEach(eq -> eq.internalEnqueueItem(item)); + // Submit to MainEventBus for centralized persistence + distribution + // MainEventBus is guaranteed non-null by constructor requirement + // Note: Replication now happens in MainEventBusProcessor AFTER persistence - // Trigger replication hook if configured - if (enqueueHook != null) { - enqueueHook.onEnqueue(item); + // Submit event to MainEventBus with our taskId + mainEventBus.submit(taskId, this, item); + } + + /** + * Checks if an event represents a final task state. + */ + private boolean isFinalEvent(Event event) { + if (event instanceof Task task) { + return task.status() != null && task.status().state() != null + && task.status().state().isFinal(); + } else if (event instanceof TaskStatusUpdateEvent statusUpdate) { + return statusUpdate.isFinal(); } + return false; } @Override @@ -465,20 +491,15 @@ public void signalQueuePollerStarted() { void childClosing(ChildQueue child, boolean immediate) { children.remove(child); // Remove the closing child - // Close immediately if requested - if (immediate) { - LOGGER.debug("MainQueue closing immediately (immediate=true)"); - this.doClose(immediate); - return; - } - // If there are still children, keep queue open if (!children.isEmpty()) { LOGGER.debug("MainQueue staying open: {} children remaining", children.size()); return; } - // No children left - check if task is finalized before auto-closing + // No children left - check if task is finalized before closing + // IMPORTANT: This check must happen BEFORE the immediate flag check + // to prevent closing queues for non-final tasks (fire-and-forget, resubscription support) if (taskStateProvider != null && taskId != null) { boolean isFinalized = taskStateProvider.isTaskFinalized(taskId); if (!isFinalized) { @@ -493,6 +514,36 @@ void childClosing(ChildQueue child, boolean immediate) { this.doClose(immediate); } + /** + * Distribute event to all ChildQueues. + * Called by MainEventBusProcessor after TaskStore persistence. + */ + void distributeToChildren(EventQueueItem item) { + int childCount = children.size(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("MainQueue[{}]: Distributing event {} to {} children", + taskId, item.getEvent().getClass().getSimpleName(), childCount); + } + children.forEach(child -> { + LOGGER.debug("MainQueue[{}]: Enqueueing event {} to child queue", + taskId, item.getEvent().getClass().getSimpleName()); + child.internalEnqueueItem(item); + }); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("MainQueue[{}]: Completed distribution of {} to {} children", + taskId, item.getEvent().getClass().getSimpleName(), childCount); + } + } + + /** + * Release the semaphore after event processing is complete. + * Called by MainEventBusProcessor in finally block to ensure release even on exceptions. + * Balances the acquire() in enqueueEvent() - protects MainEventBus throughput. + */ + void releaseSemaphore() { + semaphore.release(); + } + /** * Get the count of active child queues. * Used for testing to verify reference counting mechanism. @@ -539,10 +590,17 @@ public void close(boolean immediate) { public void close(boolean immediate, boolean notifyParent) { throw new UnsupportedOperationException("MainQueue does not support notifyParent parameter - use close(boolean) instead"); } + + String getTaskId() { + return taskId; + } } static class ChildQueue extends EventQueue { private final MainQueue parent; + private final BlockingQueue queue = new LinkedBlockingDeque<>(); + private volatile boolean immediateClose = false; + private volatile boolean awaitingFinalEvent = false; public ChildQueue(MainQueue parent) { this.parent = parent; @@ -553,8 +611,94 @@ public void enqueueEvent(Event event) { parent.enqueueEvent(event); } + @Override + public void enqueueItem(EventQueueItem item) { + // ChildQueue delegates writes to parent MainQueue + parent.enqueueItem(item); + } + private void internalEnqueueItem(EventQueueItem item) { - super.enqueueItem(item); + // Internal method called by MainEventBusProcessor to add to local queue + // Note: Semaphore is managed by parent MainQueue (acquire/release), not ChildQueue + Event event = item.getEvent(); + // For graceful close: still accept events so they can be drained by EventConsumer + // For immediate close: reject events to stop distribution quickly + if (isClosed() && immediateClose) { + LOGGER.warn("ChildQueue is immediately closed. Event will not be enqueued. {} {}", this, event); + return; + } + if (!queue.offer(item)) { + LOGGER.warn("ChildQueue {} is full. Closing immediately.", this); + close(true); // immediate close + } else { + LOGGER.debug("Enqueued event {} {}", event instanceof Throwable ? event.toString() : event, this); + + // If we were awaiting a final event and this is it, clear the flag + if (awaitingFinalEvent && isFinalEvent(event)) { + awaitingFinalEvent = false; + LOGGER.debug("ChildQueue {} received awaited final event", System.identityHashCode(this)); + } + } + } + + /** + * Checks if an event represents a final task state. + */ + private boolean isFinalEvent(Event event) { + if (event instanceof Task task) { + return task.status() != null && task.status().state() != null + && task.status().state().isFinal(); + } else if (event instanceof TaskStatusUpdateEvent statusUpdate) { + return statusUpdate.isFinal(); + } + return false; + } + + @Override + public void enqueueLocalOnly(EventQueueItem item) { + internalEnqueueItem(item); + } + + @Override + @Nullable + public EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { + // For immediate close: exit immediately even if queue is not empty (race with MainEventBusProcessor) + // For graceful close: only exit when queue is empty (wait for all events to be consumed) + // BUT: if awaiting final event, keep polling even if closed and empty + if (isClosed() && (queue.isEmpty() || immediateClose) && !awaitingFinalEvent) { + LOGGER.debug("ChildQueue is closed{}, sending termination message. {} (queueSize={})", + immediateClose ? " (immediate)" : " and empty", + this, + queue.size()); + throw new EventQueueClosedException(); + } + try { + if (waitMilliSeconds <= 0) { + EventQueueItem item = queue.poll(); + if (item != null) { + Event event = item.getEvent(); + LOGGER.debug("Dequeued event item (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); + } + return item; + } + try { + LOGGER.trace("Polling ChildQueue {} (wait={}ms)", System.identityHashCode(this), waitMilliSeconds); + EventQueueItem item = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); + if (item != null) { + Event event = item.getEvent(); + LOGGER.debug("Dequeued event item (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); + } else { + LOGGER.trace("Dequeue timeout (null) from ChildQueue {}", System.identityHashCode(this)); + } + return item; + } catch (InterruptedException e) { + LOGGER.debug("Interrupted dequeue (waiting) {}", this); + Thread.currentThread().interrupt(); + return null; + } + } finally { + signalQueuePollerStarted(); + } } @Override @@ -562,6 +706,12 @@ public EventQueue tap() { throw new IllegalStateException("Can only tap the main queue"); } + @Override + public int size() { + // Return size of local consumption queue + return queue.size(); + } + @Override public void awaitQueuePollerStart() throws InterruptedException { parent.awaitQueuePollerStart(); @@ -572,6 +722,29 @@ public void signalQueuePollerStarted() { parent.signalQueuePollerStarted(); } + @Override + protected void doClose(boolean immediate) { + super.doClose(immediate); // Sets closed flag + if (immediate) { + // Immediate close: clear pending events from local queue + this.immediateClose = true; + int clearedCount = queue.size(); + queue.clear(); + LOGGER.debug("Cleared {} events from ChildQueue for immediate close: {}", clearedCount, this); + } + // For graceful close, let the queue drain naturally through normal consumption + } + + /** + * Notifies this ChildQueue to expect a final event. + * Called by MainQueue when it enqueues a final event, BEFORE submitting to MainEventBus. + * This ensures the ChildQueue keeps polling until the final event arrives (after MainEventBusProcessor). + */ + void expectFinalEvent() { + awaitingFinalEvent = true; + LOGGER.debug("ChildQueue {} now awaiting final event", System.identityHashCode(this)); + } + @Override public void close() { close(false); diff --git a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java index e5a17e0e7..53a089e4c 100644 --- a/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/InMemoryQueueManager.java @@ -34,16 +34,20 @@ protected InMemoryQueueManager() { this.taskStateProvider = null; } + MainEventBus mainEventBus; + @Inject - public InMemoryQueueManager(TaskStateProvider taskStateProvider) { + public InMemoryQueueManager(TaskStateProvider taskStateProvider, MainEventBus mainEventBus) { + this.mainEventBus = mainEventBus; this.factory = new DefaultEventQueueFactory(); this.taskStateProvider = taskStateProvider; } - // For testing with custom factory - public InMemoryQueueManager(EventQueueFactory factory, TaskStateProvider taskStateProvider) { + // For testing/extensions with custom factory and MainEventBus + public InMemoryQueueManager(EventQueueFactory factory, TaskStateProvider taskStateProvider, MainEventBus mainEventBus) { this.factory = factory; this.taskStateProvider = taskStateProvider; + this.mainEventBus = mainEventBus; } @Override @@ -101,7 +105,6 @@ public EventQueue createOrTap(String taskId) { EventQueue newQueue = null; if (existing == null) { // Use builder pattern for cleaner queue creation - // Use the new taskId-aware builder method if available newQueue = factory.builder(taskId).build(); // Make sure an existing queue has not been added in the meantime existing = queues.putIfAbsent(taskId, newQueue); @@ -128,6 +131,12 @@ public void awaitQueuePollerStart(EventQueue eventQueue) throws InterruptedExcep eventQueue.awaitQueuePollerStart(); } + @Override + public EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { + // Use the factory to ensure proper configuration (MainEventBus, callbacks, etc.) + return factory.builder(taskId); + } + @Override public int getActiveChildQueueCount(String taskId) { EventQueue queue = queues.get(taskId); @@ -142,6 +151,14 @@ public int getActiveChildQueueCount(String taskId) { return -1; } + @Override + public EventQueue.EventQueueBuilder createBaseEventQueueBuilder(String taskId) { + return EventQueue.builder(mainEventBus) + .taskId(taskId) + .addOnCloseCallback(getCleanupCallback(taskId)) + .taskStateProvider(taskStateProvider); + } + /** * Get the cleanup callback that removes a queue from the map when it closes. * This is exposed so that subclasses (like ReplicatedQueueManager) can reuse @@ -181,11 +198,8 @@ public Runnable getCleanupCallback(String taskId) { private class DefaultEventQueueFactory implements EventQueueFactory { @Override public EventQueue.EventQueueBuilder builder(String taskId) { - // Return builder with callback that removes queue from map when closed - return EventQueue.builder() - .taskId(taskId) - .addOnCloseCallback(getCleanupCallback(taskId)) - .taskStateProvider(taskStateProvider); + // Delegate to the base builder creation method + return createBaseEventQueueBuilder(taskId); } } } diff --git a/server-common/src/main/java/io/a2a/server/events/MainEventBus.java b/server-common/src/main/java/io/a2a/server/events/MainEventBus.java new file mode 100644 index 000000000..90080b1e2 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/MainEventBus.java @@ -0,0 +1,42 @@ +package io.a2a.server.events; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class MainEventBus { + private static final Logger LOGGER = LoggerFactory.getLogger(MainEventBus.class); + private final BlockingQueue queue; + + public MainEventBus() { + this.queue = new LinkedBlockingDeque<>(); + } + + void submit(String taskId, EventQueue.MainQueue mainQueue, EventQueueItem item) { + try { + queue.put(new MainEventBusContext(taskId, mainQueue, item)); + LOGGER.debug("Submitted event for task {} to MainEventBus (queue size: {})", + taskId, queue.size()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted submitting to MainEventBus", e); + } + } + + MainEventBusContext take() throws InterruptedException { + LOGGER.debug("MainEventBus: Waiting to take event (current queue size: {})...", queue.size()); + MainEventBusContext context = queue.take(); + LOGGER.debug("MainEventBus: Took event for task {} (remaining queue size: {})", + context.taskId(), queue.size()); + return context; + } + + public int size() { + return queue.size(); + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/MainEventBusContext.java b/server-common/src/main/java/io/a2a/server/events/MainEventBusContext.java new file mode 100644 index 000000000..292a60f21 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/MainEventBusContext.java @@ -0,0 +1,11 @@ +package io.a2a.server.events; + +import java.util.Objects; + +record MainEventBusContext(String taskId, EventQueue.MainQueue eventQueue, EventQueueItem eventQueueItem) { + MainEventBusContext { + Objects.requireNonNull(taskId, "taskId cannot be null"); + Objects.requireNonNull(eventQueue, "eventQueue cannot be null"); + Objects.requireNonNull(eventQueueItem, "eventQueueItem cannot be null"); + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessor.java b/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessor.java new file mode 100644 index 000000000..8b3dc6fa3 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessor.java @@ -0,0 +1,386 @@ +package io.a2a.server.events; + +import java.util.concurrent.CompletableFuture; + +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import io.a2a.server.tasks.PushNotificationSender; +import io.a2a.server.tasks.TaskManager; +import io.a2a.server.tasks.TaskStore; +import io.a2a.spec.A2AServerException; +import io.a2a.spec.Event; +import io.a2a.spec.InternalError; +import io.a2a.spec.Message; +import io.a2a.spec.Task; +import io.a2a.spec.TaskArtifactUpdateEvent; +import io.a2a.spec.TaskStatusUpdateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Background processor for the MainEventBus. + *

    + * This processor runs in a dedicated background thread, consuming events from the MainEventBus + * and performing two critical operations in order: + *

    + *
      + *
    1. Update TaskStore with event data (persistence FIRST)
    2. + *
    3. Distribute event to ChildQueues (clients see it AFTER persistence)
    4. + *
    + *

    + * This architecture ensures clients never receive events before they're persisted, + * eliminating race conditions and enabling reliable event replay. + *

    + *

    + * Note: This bean is eagerly initialized by {@link MainEventBusProcessorInitializer} + * to ensure the background thread starts automatically when the application starts. + *

    + */ +@ApplicationScoped +public class MainEventBusProcessor implements Runnable { + private static final Logger LOGGER = LoggerFactory.getLogger(MainEventBusProcessor.class); + + /** + * Callback for testing synchronization with async event processing. + * Default is NOOP to avoid null checks in production code. + * Tests can inject their own callback via setCallback(). + */ + private volatile MainEventBusProcessorCallback callback = MainEventBusProcessorCallback.NOOP; + + /** + * Optional executor for push notifications. + * If null, uses default ForkJoinPool (async). + * Tests can inject a synchronous executor to ensure deterministic ordering. + */ + private volatile @Nullable java.util.concurrent.Executor pushNotificationExecutor = null; + + private final MainEventBus eventBus; + + private final TaskStore taskStore; + + private final PushNotificationSender pushSender; + + private final QueueManager queueManager; + + private volatile boolean running = true; + private @Nullable Thread processorThread; + + @Inject + public MainEventBusProcessor(MainEventBus eventBus, TaskStore taskStore, PushNotificationSender pushSender, QueueManager queueManager) { + this.eventBus = eventBus; + this.taskStore = taskStore; + this.pushSender = pushSender; + this.queueManager = queueManager; + } + + /** + * Set a callback for testing synchronization with async event processing. + *

    + * This is primarily intended for tests that need to wait for event processing to complete. + * Pass null to reset to the default NOOP callback. + *

    + * + * @param callback the callback to invoke during event processing, or null for NOOP + */ + public void setCallback(MainEventBusProcessorCallback callback) { + this.callback = callback != null ? callback : MainEventBusProcessorCallback.NOOP; + } + + /** + * Set a custom executor for push notifications (primarily for testing). + *

    + * By default, push notifications are sent asynchronously using CompletableFuture.runAsync() + * with the default ForkJoinPool. For tests that need deterministic ordering of push + * notifications, inject a synchronous executor that runs tasks immediately on the calling thread. + *

    + * Example synchronous executor for tests: + *
    {@code
    +     * Executor syncExecutor = Runnable::run;
    +     * mainEventBusProcessor.setPushNotificationExecutor(syncExecutor);
    +     * }
    + * + * @param executor the executor to use for push notifications, or null to use default ForkJoinPool + */ + public void setPushNotificationExecutor(java.util.concurrent.Executor executor) { + this.pushNotificationExecutor = executor; + } + + @PostConstruct + void start() { + processorThread = new Thread(this, "MainEventBusProcessor"); + processorThread.setDaemon(true); // Allow JVM to exit even if this thread is running + processorThread.start(); + LOGGER.info("MainEventBusProcessor started"); + } + + /** + * No-op method to force CDI proxy resolution and ensure @PostConstruct has been called. + * Called by MainEventBusProcessorInitializer during application startup. + */ + public void ensureStarted() { + // Method intentionally empty - just forces proxy resolution + } + + @PreDestroy + void stop() { + LOGGER.info("MainEventBusProcessor stopping..."); + running = false; + if (processorThread != null) { + processorThread.interrupt(); + try { + long start = System.currentTimeMillis(); + processorThread.join(5000); // Wait up to 5 seconds + long elapsed = System.currentTimeMillis() - start; + LOGGER.info("MainEventBusProcessor thread stopped in {}ms", elapsed); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.warn("Interrupted while waiting for MainEventBusProcessor thread to stop"); + } + } + LOGGER.info("MainEventBusProcessor stopped"); + } + + @Override + public void run() { + LOGGER.info("MainEventBusProcessor processing loop started"); + while (running) { + try { + LOGGER.debug("MainEventBusProcessor: Waiting for event from MainEventBus..."); + MainEventBusContext context = eventBus.take(); + LOGGER.debug("MainEventBusProcessor: Retrieved event for task {} from MainEventBus", + context.taskId()); + processEvent(context); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.info("MainEventBusProcessor interrupted, shutting down"); + break; + } catch (Exception e) { + LOGGER.error("Error processing event from MainEventBus", e); + // Continue processing despite errors + } + } + LOGGER.info("MainEventBusProcessor processing loop ended"); + } + + private void processEvent(MainEventBusContext context) { + String taskId = context.taskId(); + Event event = context.eventQueueItem().getEvent(); + // MainEventBus.submit() guarantees this is always a MainQueue + EventQueue.MainQueue mainQueue = (EventQueue.MainQueue) context.eventQueue(); + + LOGGER.debug("MainEventBusProcessor: Processing event for task {}: {}", + taskId, event.getClass().getSimpleName()); + + Event eventToDistribute = null; + boolean isReplicated = context.eventQueueItem().isReplicated(); + try { + // Step 1: Update TaskStore FIRST (persistence before clients see it) + // If this throws, we distribute an error to ensure "persist before client visibility" + + try { + boolean isFinal = updateTaskStore(taskId, event, isReplicated); + + eventToDistribute = event; // Success - distribute original event + + // Trigger replication AFTER successful persistence + // SKIP replication if task is final - ReplicatedQueueManager handles this via TaskFinalizedEvent + // to ensure final Task is sent before poison pill (QueueClosedEvent) + if (!isFinal) { + EventEnqueueHook hook = mainQueue.getEnqueueHook(); + if (hook != null) { + LOGGER.debug("Triggering replication hook for task {} after successful persistence", taskId); + hook.onEnqueue(context.eventQueueItem()); + } + } else { + LOGGER.debug("Task {} is final - skipping replication hook (handled by ReplicatedQueueManager)", taskId); + } + } catch (InternalError e) { + // Persistence failed - create error event to distribute instead + LOGGER.error("Failed to persist event for task {}, distributing error to clients", taskId, e); + String errorMessage = "Failed to persist event: " + e.getMessage(); + eventToDistribute = e; + } catch (Exception e) { + LOGGER.error("Failed to persist event for task {}, distributing error to clients", taskId, e); + String errorMessage = "Failed to persist event: " + e.getMessage(); + eventToDistribute = new InternalError(errorMessage); + } + + // Step 2: Send push notification AFTER successful persistence (only from active node) + // Skip push notifications for replicated events to avoid duplicate notifications in multi-instance deployments + if (eventToDistribute == event && !isReplicated) { + // Capture task state immediately after persistence, before going async + // This ensures we send the task as it existed when THIS event was processed, + // not whatever state might exist later when the async callback executes + Task taskSnapshot = taskStore.get(taskId); + if (taskSnapshot != null) { + sendPushNotification(taskId, taskSnapshot); + } else { + LOGGER.warn("Task {} not found in TaskStore after successful persistence, skipping push notification", taskId); + } + } + + // Step 3: Then distribute to ChildQueues (clients see either event or error AFTER persistence attempt) + if (eventToDistribute == null) { + LOGGER.error("MainEventBusProcessor: eventToDistribute is NULL for task {} - this should never happen!", taskId); + eventToDistribute = new InternalError("Internal error: event processing failed"); + } + + int childCount = mainQueue.getChildCount(); + LOGGER.debug("MainEventBusProcessor: Distributing {} to {} children for task {}", + eventToDistribute.getClass().getSimpleName(), childCount, taskId); + // Create new EventQueueItem with the event to distribute (original or error) + EventQueueItem itemToDistribute = new LocalEventQueueItem(eventToDistribute); + mainQueue.distributeToChildren(itemToDistribute); + LOGGER.debug("MainEventBusProcessor: Distributed {} to {} children for task {}", + eventToDistribute.getClass().getSimpleName(), childCount, taskId); + + LOGGER.debug("MainEventBusProcessor: Completed processing event for task {}", taskId); + + } finally { + try { + // Step 4: Notify callback after all processing is complete + // Call callback with the distributed event (original or error) + if (eventToDistribute != null) { + callback.onEventProcessed(taskId, eventToDistribute); + + // Step 5: If this is a final event, notify task finalization + // Only for successful persistence (not for errors) + if (eventToDistribute == event && isFinalEvent(event)) { + callback.onTaskFinalized(taskId); + } + } + } finally { + // ALWAYS release semaphore, even if processing fails + // Balances the acquire() in MainQueue.enqueueEvent() + mainQueue.releaseSemaphore(); + } + } + } + + /** + * Updates TaskStore using TaskManager.process(). + *

    + * Creates a temporary TaskManager instance for this event and delegates to its process() method, + * which handles all event types (Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent). + * This leverages existing TaskManager logic for status updates, artifact appending, message history, etc. + *

    + *

    + * If persistence fails, the exception is propagated to processEvent() which distributes an + * InternalError to clients instead of the original event, ensuring "persist before visibility". + * See Gemini's comment: https://github.com/a2aproject/a2a-java/pull/515#discussion_r2604621833 + *

    + * + * @param taskId the task ID + * @param event the event to persist + * @return true if the task reached a final state, false otherwise + * @throws InternalError if persistence fails + */ + private boolean updateTaskStore(String taskId, Event event, boolean isReplicated) throws InternalError { + try { + // Extract contextId from event (all relevant events have it) + String contextId = extractContextId(event); + + // Create temporary TaskManager instance for this event + TaskManager taskManager = new TaskManager(taskId, contextId, taskStore, null); + + // Use TaskManager.process() - handles all event types with existing logic + boolean isFinal = taskManager.process(event, isReplicated); + LOGGER.debug("TaskStore updated via TaskManager.process() for task {}: {} (final: {}, replicated: {})", + taskId, event.getClass().getSimpleName(), isFinal, isReplicated); + return isFinal; + } catch (InternalError e) { + LOGGER.error("Error updating TaskStore via TaskManager for task {}", taskId, e); + // Rethrow to prevent distributing unpersisted event to clients + throw e; + } catch (Exception e) { + LOGGER.error("Unexpected error updating TaskStore for task {}", taskId, e); + // Rethrow to prevent distributing unpersisted event to clients + throw new InternalError("TaskStore persistence failed: " + e.getMessage()); + } + } + + /** + * Sends push notification for the task AFTER persistence. + *

    + * This is called after updateTaskStore() to ensure the notification contains + * the latest persisted state, avoiding race conditions. + *

    + *

    + * CRITICAL: Push notifications are sent asynchronously in the background + * to avoid blocking event distribution to ChildQueues. The 83ms overhead from + * PushNotificationSender.sendNotification() was causing streaming delays. + *

    + *

    + * IMPORTANT: The task parameter is a snapshot captured immediately after + * persistence. This ensures we send the task state as it existed when THIS event + * was processed, not whatever state might exist in TaskStore when the async + * callback executes (subsequent events may have already updated the store). + *

    + *

    + * NOTE: Tests can inject a synchronous executor via setPushNotificationExecutor() + * to ensure deterministic ordering of push notifications in the test environment. + *

    + * + * @param taskId the task ID + * @param task the task snapshot to send (captured immediately after persistence) + */ + private void sendPushNotification(String taskId, Task task) { + Runnable pushTask = () -> { + try { + if (task != null) { + LOGGER.debug("Sending push notification for task {}", taskId); + pushSender.sendNotification(task); + } else { + LOGGER.debug("Skipping push notification - task snapshot is null for task {}", taskId); + } + } catch (Exception e) { + LOGGER.error("Error sending push notification for task {}", taskId, e); + // Don't rethrow - push notifications are best-effort + } + }; + + // Use custom executor if set (for tests), otherwise use default ForkJoinPool (async) + if (pushNotificationExecutor != null) { + pushNotificationExecutor.execute(pushTask); + } else { + CompletableFuture.runAsync(pushTask); + } + } + + /** + * Extracts contextId from an event. + * Returns null if the event type doesn't have a contextId (e.g., Message). + */ + @Nullable + private String extractContextId(Event event) { + if (event instanceof Task task) { + return task.contextId(); + } else if (event instanceof TaskStatusUpdateEvent statusUpdate) { + return statusUpdate.contextId(); + } else if (event instanceof TaskArtifactUpdateEvent artifactUpdate) { + return artifactUpdate.contextId(); + } + // Message and other events don't have contextId + return null; + } + + /** + * Checks if an event represents a final task state. + * + * @param event the event to check + * @return true if the event represents a final state (COMPLETED, FAILED, CANCELED, REJECTED, UNKNOWN) + */ + private boolean isFinalEvent(Event event) { + if (event instanceof Task task) { + return task.status() != null && task.status().state() != null + && task.status().state().isFinal(); + } else if (event instanceof TaskStatusUpdateEvent statusUpdate) { + return statusUpdate.isFinal(); + } + return false; + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorCallback.java b/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorCallback.java new file mode 100644 index 000000000..b0a9adbce --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorCallback.java @@ -0,0 +1,66 @@ +package io.a2a.server.events; + +import io.a2a.spec.Event; + +/** + * Callback interface for MainEventBusProcessor events. + *

    + * This interface is primarily intended for testing, allowing tests to synchronize + * with the asynchronous MainEventBusProcessor. Production code should not rely on this. + *

    + * Usage in tests: + *
    + * {@code
    + * @Inject
    + * MainEventBusProcessor processor;
    + *
    + * @BeforeEach
    + * void setUp() {
    + *     CountDownLatch latch = new CountDownLatch(3);
    + *     processor.setCallback(new MainEventBusProcessorCallback() {
    + *         public void onEventProcessed(String taskId, Event event) {
    + *             latch.countDown();
    + *         }
    + *     });
    + * }
    + *
    + * @AfterEach
    + * void tearDown() {
    + *     processor.setCallback(null); // Reset to NOOP
    + * }
    + * }
    + * 
    + */ +public interface MainEventBusProcessorCallback { + + /** + * Called after an event has been fully processed (persisted, notification sent, distributed to children). + * + * @param taskId the task ID + * @param event the event that was processed + */ + void onEventProcessed(String taskId, Event event); + + /** + * Called when a task reaches a final state (COMPLETED, FAILED, CANCELED, REJECTED). + * + * @param taskId the task ID that was finalized + */ + void onTaskFinalized(String taskId); + + /** + * No-op implementation that does nothing. + * Used as the default callback to avoid null checks. + */ + MainEventBusProcessorCallback NOOP = new MainEventBusProcessorCallback() { + @Override + public void onEventProcessed(String taskId, Event event) { + // No-op + } + + @Override + public void onTaskFinalized(String taskId) { + // No-op + } + }; +} diff --git a/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorInitializer.java b/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorInitializer.java new file mode 100644 index 000000000..ba4b300be --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/events/MainEventBusProcessorInitializer.java @@ -0,0 +1,43 @@ +package io.a2a.server.events; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Portable CDI initializer for MainEventBusProcessor. + *

    + * This bean observes the ApplicationScoped initialization event and injects + * MainEventBusProcessor, which triggers its eager creation and starts the background thread. + *

    + *

    + * This approach is portable across all Jakarta CDI implementations (Weld, OpenWebBeans, Quarkus, etc.) + * and ensures MainEventBusProcessor starts automatically when the application starts. + *

    + */ +@ApplicationScoped +public class MainEventBusProcessorInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(MainEventBusProcessorInitializer.class); + + @Inject + MainEventBusProcessor processor; + + /** + * Observes ApplicationScoped initialization to force eager creation of MainEventBusProcessor. + * The injection of MainEventBusProcessor in this bean triggers its creation, and calling + * ensureStarted() forces the CDI proxy to be resolved, which ensures @PostConstruct has been + * called and the background thread is running. + */ + void onStart(@Observes @Initialized(ApplicationScoped.class) Object event) { + if (processor != null) { + // Force proxy resolution to ensure @PostConstruct has been called + processor.ensureStarted(); + LOGGER.info("MainEventBusProcessor initialized and started"); + } else { + LOGGER.error("MainEventBusProcessor is null - initialization failed!"); + } + } +} diff --git a/server-common/src/main/java/io/a2a/server/events/QueueManager.java b/server-common/src/main/java/io/a2a/server/events/QueueManager.java index 01e754fcb..4ad30f0cb 100644 --- a/server-common/src/main/java/io/a2a/server/events/QueueManager.java +++ b/server-common/src/main/java/io/a2a/server/events/QueueManager.java @@ -177,7 +177,31 @@ public interface QueueManager { * @return a builder for creating event queues */ default EventQueue.EventQueueBuilder getEventQueueBuilder(String taskId) { - return EventQueue.builder(); + throw new UnsupportedOperationException( + "QueueManager implementations must override getEventQueueBuilder() to provide MainEventBus" + ); + } + + /** + * Creates a base EventQueueBuilder with standard configuration for this QueueManager. + * This method provides the foundation for creating event queues with proper configuration + * (MainEventBus, TaskStateProvider, cleanup callbacks, etc.). + *

    + * QueueManager implementations that use custom factories can call this method directly + * to get the base builder without going through the factory (which could cause infinite + * recursion if the factory delegates back to getEventQueueBuilder()). + *

    + *

    + * Callers can then add additional configuration (hooks, callbacks) before building the queue. + *

    + * + * @param taskId the task ID for the queue + * @return a builder with base configuration specific to this QueueManager implementation + */ + default EventQueue.EventQueueBuilder createBaseEventQueueBuilder(String taskId) { + throw new UnsupportedOperationException( + "QueueManager implementations must override createBaseEventQueueBuilder() to provide MainEventBus" + ); } /** diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index 002acbafd..c476c8741 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -11,13 +11,13 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Flow; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -35,13 +35,15 @@ import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueItem; +import io.a2a.server.events.MainEventBusProcessor; +import io.a2a.server.events.MainEventBusProcessorCallback; import io.a2a.server.events.QueueManager; -import io.a2a.server.events.TaskQueueExistsException; import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.server.tasks.PushNotificationSender; import io.a2a.server.tasks.ResultAggregator; import io.a2a.server.tasks.TaskManager; import io.a2a.server.tasks.TaskStore; +import io.a2a.server.util.async.EventConsumerExecutorProducer.EventConsumerExecutor; import io.a2a.server.util.async.Internal; import io.a2a.spec.A2AError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; @@ -64,6 +66,7 @@ import io.a2a.spec.TaskPushNotificationConfig; import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.UnsupportedOperationError; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -122,7 +125,6 @@ *
  • {@link EventConsumer} polls and processes events on Vert.x worker thread
  • *
  • Queue closes automatically on final event (COMPLETED/FAILED/CANCELED)
  • *
  • Cleanup waits for both agent execution AND event consumption to complete
  • - *
  • Background tasks tracked via {@link #trackBackgroundTask(CompletableFuture)}
  • * * *

    Threading Model

    @@ -179,6 +181,13 @@ public class DefaultRequestHandler implements RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRequestHandler.class); + /** + * Separate logger for thread statistics diagnostic logging. + * This allows independent control of verbose thread pool monitoring without affecting + * general request handler logging. Enable with: logging.level.io.a2a.server.diagnostics.ThreadStats=DEBUG + */ + private static final Logger THREAD_STATS_LOGGER = LoggerFactory.getLogger("io.a2a.server.diagnostics.ThreadStats"); + private static final String A2A_BLOCKING_AGENT_TIMEOUT_SECONDS = "a2a.blocking.agent.timeout.seconds"; private static final String A2A_BLOCKING_CONSUMPTION_TIMEOUT_SECONDS = "a2a.blocking.consumption.timeout.seconds"; @@ -214,13 +223,14 @@ public class DefaultRequestHandler implements RequestHandler { private TaskStore taskStore; private QueueManager queueManager; private PushNotificationConfigStore pushConfigStore; - private PushNotificationSender pushSender; + private MainEventBusProcessor mainEventBusProcessor; private Supplier requestContextBuilder; private final ConcurrentMap> runningAgents = new ConcurrentHashMap<>(); - private final Set> backgroundTasks = ConcurrentHashMap.newKeySet(); + private Executor executor; + private Executor eventConsumerExecutor; /** * No-args constructor for CDI proxy creation. @@ -234,21 +244,25 @@ protected DefaultRequestHandler() { this.taskStore = null; this.queueManager = null; this.pushConfigStore = null; - this.pushSender = null; + this.mainEventBusProcessor = null; this.requestContextBuilder = null; this.executor = null; + this.eventConsumerExecutor = null; } @Inject public DefaultRequestHandler(AgentExecutor agentExecutor, TaskStore taskStore, QueueManager queueManager, PushNotificationConfigStore pushConfigStore, - PushNotificationSender pushSender, @Internal Executor executor) { + MainEventBusProcessor mainEventBusProcessor, + @Internal Executor executor, + @EventConsumerExecutor Executor eventConsumerExecutor) { this.agentExecutor = agentExecutor; this.taskStore = taskStore; this.queueManager = queueManager; this.pushConfigStore = pushConfigStore; - this.pushSender = pushSender; + this.mainEventBusProcessor = mainEventBusProcessor; this.executor = executor; + this.eventConsumerExecutor = eventConsumerExecutor; // TODO In Python this is also a constructor parameter defaulting to this SimpleRequestContextBuilder // implementation if the parameter is null. Skip that for now, since otherwise I get CDI errors, and // I am unsure about the correct scope. @@ -264,16 +278,20 @@ void initConfig() { configProvider.getValue(A2A_BLOCKING_CONSUMPTION_TIMEOUT_SECONDS)); } + /** * For testing */ public static DefaultRequestHandler create(AgentExecutor agentExecutor, TaskStore taskStore, QueueManager queueManager, PushNotificationConfigStore pushConfigStore, - PushNotificationSender pushSender, Executor executor) { + MainEventBusProcessor mainEventBusProcessor, + Executor executor, Executor eventConsumerExecutor) { DefaultRequestHandler handler = - new DefaultRequestHandler(agentExecutor, taskStore, queueManager, pushConfigStore, pushSender, executor); + new DefaultRequestHandler(agentExecutor, taskStore, queueManager, pushConfigStore, + mainEventBusProcessor, executor, eventConsumerExecutor); handler.agentCompletionTimeoutSeconds = 5; handler.consumptionCompletionTimeoutSeconds = 2; + return handler; } @@ -359,12 +377,9 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws taskStore, null); - ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); + ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor, eventConsumerExecutor); - EventQueue queue = queueManager.tap(task.id()); - if (queue == null) { - queue = queueManager.getEventQueueBuilder(task.id()).build(); - } + EventQueue queue = queueManager.createOrTap(task.id()); agentExecutor.cancel( requestContextBuilder.get() .setTaskId(task.id()) @@ -395,28 +410,41 @@ public Task onCancelTask(TaskIdParams params, ServerCallContext context) throws @Override public EventKind onMessageSend(MessageSendParams params, ServerCallContext context) throws A2AError { LOGGER.debug("onMessageSend - task: {}; context {}", params.message().taskId(), params.message().contextId()); + + // Build MessageSendSetup which creates RequestContext with real taskId (auto-generated if needed) MessageSendSetup mss = initMessageSend(params, context); - String taskId = mss.requestContext.getTaskId(); - LOGGER.debug("Request context taskId: {}", taskId); + // Use the taskId from RequestContext for queue management (no temp ID needed!) + // RequestContext.build() guarantees taskId is non-null via checkOrGenerateTaskId() + String queueTaskId = java.util.Objects.requireNonNull( + mss.requestContext.getTaskId(), "TaskId must be non-null after RequestContext.build()"); + LOGGER.debug("Queue taskId: {}", queueTaskId); - if (taskId == null) { - throw new io.a2a.spec.InternalError("Task ID is null in onMessageSend"); - } - EventQueue queue = queueManager.createOrTap(taskId); - ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor); + // Create queue with real taskId (no tempId parameter needed) + EventQueue queue = queueManager.createOrTap(queueTaskId); + final java.util.concurrent.atomic.AtomicReference<@NonNull String> taskId = new java.util.concurrent.atomic.AtomicReference<>(queueTaskId); + ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor, eventConsumerExecutor); + // Default to blocking=false per A2A spec (return after task creation) boolean blocking = params.configuration() != null && Boolean.TRUE.equals(params.configuration().blocking()); + // Log blocking behavior from client request + if (params.configuration() != null && params.configuration().blocking() != null) { + LOGGER.debug("DefaultRequestHandler: Client requested blocking={} for task {}", + params.configuration().blocking(), taskId.get()); + } else if (params.configuration() != null) { + LOGGER.debug("DefaultRequestHandler: Client sent configuration but blocking=null, using default blocking={} for task {}", blocking, taskId.get()); + } else { + LOGGER.debug("DefaultRequestHandler: Client sent no configuration, using default blocking={} for task {}", blocking, taskId.get()); + } + LOGGER.debug("DefaultRequestHandler: Final blocking decision: {} for task {}", blocking, taskId.get()); + boolean interruptedOrNonBlocking = false; - EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(taskId, mss.requestContext, queue); + EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(queueTaskId, mss.requestContext, queue); ResultAggregator.EventTypeAndInterrupt etai = null; EventKind kind = null; // Declare outside try block so it's in scope for return try { - // Create callback for push notifications during background event processing - Runnable pushNotificationCallback = () -> sendPushNotification(taskId, resultAggregator); - EventConsumer consumer = new EventConsumer(queue); // This callback must be added before we start consuming. Otherwise, @@ -424,7 +452,7 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); // Get agent future before consuming (for blocking calls to wait for agent completion) - CompletableFuture agentFuture = runningAgents.get(taskId); + CompletableFuture agentFuture = runningAgents.get(queueTaskId); etai = resultAggregator.consumeAndBreakOnInterrupt(consumer, blocking); if (etai == null) { @@ -432,7 +460,8 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte throw new InternalError("No result"); } interruptedOrNonBlocking = etai.interrupted(); - LOGGER.debug("Was interrupted or non-blocking: {}", interruptedOrNonBlocking); + LOGGER.debug("DefaultRequestHandler: interruptedOrNonBlocking={} (blocking={}, eventType={})", + interruptedOrNonBlocking, blocking, kind != null ? kind.getClass().getSimpleName() : null); // For blocking calls that were interrupted (returned on first event), // wait for agent execution and event processing BEFORE returning to client. @@ -441,30 +470,36 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte // during the consumption loop itself. kind = etai.eventType(); + // No ID switching needed - agent uses context.getTaskId() which is the same as queue key + // Store push notification config for newly created tasks (mirrors streaming logic) // Only for NEW tasks - existing tasks are handled by initMessageSend() if (mss.task() == null && kind instanceof Task createdTask && shouldAddPushInfo(params)) { - LOGGER.debug("Storing push notification config for new task {}", createdTask.id()); + LOGGER.debug("Storing push notification config for new task {} (original taskId from params: {})", + createdTask.id(), params.message().taskId()); pushConfigStore.setInfo(createdTask.id(), params.configuration().pushNotificationConfig()); } if (blocking && interruptedOrNonBlocking) { - // For blocking calls: ensure all events are processed before returning - // Order of operations is critical to avoid circular dependency: - // 1. Wait for agent to finish enqueueing events + // For blocking calls: ensure all consumed events are persisted to TaskStore before returning + // Order of operations is critical to avoid circular dependency and race conditions: + // 1. Wait for agent to finish enqueueing events (or timeout) // 2. Close the queue to signal consumption can complete // 3. Wait for consumption to finish processing events - // 4. Fetch final task state from TaskStore + // 4. (Implicit) MainEventBusProcessor persistence guarantee via consumption completion + // 5. Fetch current task state from TaskStore (includes all consumed & persisted events) + LOGGER.debug("DefaultRequestHandler: Entering blocking fire-and-forget handling for task {}", taskId.get()); try { // Step 1: Wait for agent to finish (with configurable timeout) if (agentFuture != null) { try { agentFuture.get(agentCompletionTimeoutSeconds, SECONDS); - LOGGER.debug("Agent completed for task {}", taskId); + LOGGER.debug("DefaultRequestHandler: Step 1 - Agent completed for task {}", taskId.get()); } catch (java.util.concurrent.TimeoutException e) { // Agent still running after timeout - that's fine, events already being processed - LOGGER.debug("Agent still running for task {} after {}s", taskId, agentCompletionTimeoutSeconds); + LOGGER.debug("DefaultRequestHandler: Step 1 - Agent still running for task {} after {}s timeout", + taskId.get(), agentCompletionTimeoutSeconds); } } @@ -472,55 +507,84 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte // For fire-and-forget tasks, there's no final event, so we need to close the queue // This allows EventConsumer.consumeAll() to exit queue.close(false, false); // graceful close, don't notify parent yet - LOGGER.debug("Closed queue for task {} to allow consumption completion", taskId); + LOGGER.debug("DefaultRequestHandler: Step 2 - Closed queue for task {} to allow consumption completion", taskId.get()); // Step 3: Wait for consumption to complete (now that queue is closed) if (etai.consumptionFuture() != null) { etai.consumptionFuture().get(consumptionCompletionTimeoutSeconds, SECONDS); - LOGGER.debug("Consumption completed for task {}", taskId); + LOGGER.debug("DefaultRequestHandler: Step 3 - Consumption completed for task {}", taskId.get()); } + + // Step 4: Implicit guarantee of persistence via consumption completion + // We do NOT add an explicit wait for MainEventBusProcessor here because: + // 1. MainEventBusProcessor persists BEFORE distributing to ChildQueues + // 2. Step 3 (consumption completion) already guarantees all consumed events are persisted + // 3. Adding another explicit synchronization point would require exposing + // MainEventBusProcessor internals and blocking event loop threads + // + // Note: For fire-and-forget tasks, if the agent is still running after Step 1 timeout, + // it may enqueue additional events. These will be persisted asynchronously but won't + // be included in the task state returned to the client (already consumed in Step 3). + } catch (InterruptedException e) { Thread.currentThread().interrupt(); - String msg = String.format("Error waiting for task %s completion", taskId); + String msg = String.format("Error waiting for task %s completion", taskId.get()); LOGGER.warn(msg, e); throw new InternalError(msg); } catch (java.util.concurrent.ExecutionException e) { - String msg = String.format("Error during task %s execution", taskId); + String msg = String.format("Error during task %s execution", taskId.get()); LOGGER.warn(msg, e.getCause()); throw new InternalError(msg); - } catch (java.util.concurrent.TimeoutException e) { - String msg = String.format("Timeout waiting for consumption to complete for task %s", taskId); - LOGGER.warn(msg, taskId); + } catch (TimeoutException e) { + // Timeout from consumption future.get() - different from finalization timeout + String msg = String.format("Timeout waiting for task %s consumption", taskId.get()); + LOGGER.warn(msg, e); throw new InternalError(msg); } - // Step 4: Fetch the final task state from TaskStore (all events have been processed) - // taskId is guaranteed non-null here (checked earlier) - String nonNullTaskId = taskId; + // Step 5: Fetch the current task state from TaskStore + // All events consumed in Step 3 are guaranteed persisted (MainEventBusProcessor + // ordering: persist → distribute → consume). This returns the persisted state + // including all consumed events and artifacts. + String nonNullTaskId = Objects.requireNonNull(taskId.get(), "taskId cannot be null"); Task updatedTask = taskStore.get(nonNullTaskId); if (updatedTask != null) { kind = updatedTask; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Fetched final task for {} with state {} and {} artifacts", - nonNullTaskId, updatedTask.status().state(), - updatedTask.artifacts().size()); - } + LOGGER.debug("DefaultRequestHandler: Step 5 - Fetched current task for {} with state {} and {} artifacts", + taskId.get(), updatedTask.status().state(), + updatedTask.artifacts().size()); + } else { + LOGGER.warn("DefaultRequestHandler: Step 5 - Task {} not found in TaskStore!", taskId.get()); } } - if (kind instanceof Task taskResult && !taskId.equals(taskResult.id())) { + String finalTaskId = Objects.requireNonNull(taskId.get(), "taskId cannot be null"); + if (kind instanceof Task taskResult && !finalTaskId.equals(taskResult.id())) { throw new InternalError("Task ID mismatch in agent response"); } - - // Send push notification after initial return (for both blocking and non-blocking) - pushNotificationCallback.run(); } finally { + // For non-blocking calls: close ChildQueue IMMEDIATELY to free EventConsumer thread + // CRITICAL: Must use immediate=true to clear the local queue, otherwise EventConsumer + // continues polling until queue drains naturally, holding executor thread. + // Immediate close clears pending events and triggers EventQueueClosedException on next poll. + // Events continue flowing through MainQueue → MainEventBus → TaskStore. + if (!blocking && etai != null && etai.interrupted()) { + LOGGER.debug("DefaultRequestHandler: Non-blocking call in finally - closing ChildQueue IMMEDIATELY for task {} to free EventConsumer", taskId.get()); + queue.close(true); // immediate=true: clear queue and free EventConsumer + } + // Remove agent from map immediately to prevent accumulation - CompletableFuture agentFuture = runningAgents.remove(taskId); - LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId, runningAgents.size()); + CompletableFuture agentFuture = runningAgents.remove(queueTaskId); + String cleanupTaskId = Objects.requireNonNull(taskId.get(), "taskId cannot be null"); + LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", cleanupTaskId, runningAgents.size()); - // Track cleanup as background task to avoid blocking Vert.x threads + // Cleanup as background task to avoid blocking Vert.x threads // Pass the consumption future to ensure cleanup waits for background consumption to complete - trackBackgroundTask(cleanupProducer(agentFuture, etai != null ? etai.consumptionFuture() : null, taskId, queue, false)); + cleanupProducer(agentFuture, etai != null ? etai.consumptionFuture() : null, cleanupTaskId, queue, false) + .whenComplete((res, err) -> { + if (err != null) { + LOGGER.error("Error during async cleanup for task {}", taskId.get(), err); + } + }); } LOGGER.debug("Returning: {}", kind); @@ -530,29 +594,44 @@ public EventKind onMessageSend(MessageSendParams params, ServerCallContext conte @Override public Flow.Publisher onMessageSendStream( MessageSendParams params, ServerCallContext context) throws A2AError { - LOGGER.debug("onMessageSendStream START - task: {}; context: {}; runningAgents: {}; backgroundTasks: {}", - params.message().taskId(), params.message().contextId(), runningAgents.size(), backgroundTasks.size()); + LOGGER.debug("onMessageSendStream START - task: {}; context: {}; runningAgents: {}", + params.message().taskId(), params.message().contextId(), runningAgents.size()); + + // Build MessageSendSetup which creates RequestContext with real taskId (auto-generated if needed) MessageSendSetup mss = initMessageSend(params, context); - @Nullable String initialTaskId = mss.requestContext.getTaskId(); - // For streaming, taskId can be null initially (will be set when Task event arrives) - // Use a temporary ID for queue creation if needed - String queueTaskId = initialTaskId != null ? initialTaskId : "temp-" + java.util.UUID.randomUUID(); + // Use the taskId from RequestContext for queue management (no temp ID needed!) + // RequestContext.build() guarantees taskId is non-null via checkOrGenerateTaskId() + String queueTaskId = java.util.Objects.requireNonNull( + mss.requestContext.getTaskId(), "TaskId must be non-null after RequestContext.build()"); + final AtomicReference<@NonNull String> taskId = new AtomicReference<>(queueTaskId); - AtomicReference<@NonNull String> taskId = new AtomicReference<>(queueTaskId); - @SuppressWarnings("NullAway") - EventQueue queue = queueManager.createOrTap(taskId.get()); + // Create queue with real taskId (no tempId parameter needed) + EventQueue queue = queueManager.createOrTap(queueTaskId); LOGGER.debug("Created/tapped queue for task {}: {}", taskId.get(), queue); - ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor); + + // Store push notification config SYNCHRONOUSLY for new tasks before agent starts + // This ensures config is available when MainEventBusProcessor sends push notifications + // For existing tasks, config is stored in initMessageSend() + if (mss.task() == null && shouldAddPushInfo(params)) { + // Satisfy Nullaway + Objects.requireNonNull(taskId.get(), "taskId was null"); + LOGGER.debug("Storing push notification config for new streaming task {} EARLY (original taskId from params: {})", + taskId.get(), params.message().taskId()); + pushConfigStore.setInfo(taskId.get(), params.configuration().pushNotificationConfig()); + } + + ResultAggregator resultAggregator = new ResultAggregator(mss.taskManager, null, executor, eventConsumerExecutor); EnhancedRunnable producerRunnable = registerAndExecuteAgentAsync(queueTaskId, mss.requestContext, queue); // Move consumer creation and callback registration outside try block - // so consumer is available for background consumption on client disconnect EventConsumer consumer = new EventConsumer(queue); producerRunnable.addDoneCallback(consumer.createAgentRunnableDoneCallback()); - AtomicBoolean backgroundConsumeStarted = new AtomicBoolean(false); + // Store cancel callback in context for closeHandler to access + // When client disconnects, closeHandler can call this to stop EventConsumer polling loop + context.setEventConsumerCancelCallback(consumer::cancel); try { Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); @@ -562,36 +641,13 @@ public Flow.Publisher onMessageSendStream( processor(createTubeConfig(), results, ((errorConsumer, item) -> { Event event = item.getEvent(); if (event instanceof Task createdTask) { - if (!Objects.equals(taskId.get(), createdTask.id())) { - errorConsumer.accept(new InternalError("Task ID mismatch in agent response")); - } - - // TODO the Python implementation no longer has the following block but removing it causes - // failures here - try { - queueManager.add(createdTask.id(), queue); - taskId.set(createdTask.id()); - } catch (TaskQueueExistsException e) { - // TODO Log - } - if (pushConfigStore != null && - params.configuration() != null && - params.configuration().pushNotificationConfig() != null) { - - pushConfigStore.setInfo( - createdTask.id(), - params.configuration().pushNotificationConfig()); - } - - } - String currentTaskId = taskId.get(); - if (pushSender != null && currentTaskId != null) { - EventKind latest = resultAggregator.getCurrentResult(); - if (latest instanceof Task latestTask) { - pushSender.sendNotification(latestTask); + // Verify task ID matches (should always match now - agent uses context.getTaskId()) + String currentId = Objects.requireNonNull(taskId.get(), "taskId cannot be null"); + if (!currentId.equals(createdTask.id())) { + errorConsumer.accept(new InternalError("Task ID mismatch: expected " + currentId + + " but got " + createdTask.id())); } } - return true; })); @@ -600,7 +656,8 @@ public Flow.Publisher onMessageSendStream( Flow.Publisher finalPublisher = convertingProcessor(eventPublisher, event -> (StreamingEventKind) event); - // Wrap publisher to detect client disconnect and continue background consumption + // Wrap publisher to detect client disconnect and immediately close ChildQueue + // This prevents ChildQueue backpressure from blocking MainEventBusProcessor return subscriber -> { String currentTaskId = taskId.get(); LOGGER.debug("Creating subscription wrapper for task {}", currentTaskId); @@ -621,8 +678,10 @@ public void request(long n) { @Override public void cancel() { - LOGGER.debug("Client cancelled subscription for task {}, starting background consumption", taskId.get()); - startBackgroundConsumption(); + LOGGER.debug("Client cancelled subscription for task {}, closing ChildQueue immediately", taskId.get()); + // Close ChildQueue immediately to prevent backpressure + // (clears queue and releases semaphore permits) + queue.close(true); // immediate=true subscription.cancel(); } }); @@ -647,8 +706,8 @@ public void onComplete() { subscriber.onComplete(); } catch (IllegalStateException e) { // Client already disconnected and response closed - this is expected - // for streaming responses where client disconnect triggers background - // consumption. Log and ignore. + // for streaming responses where client disconnect closes ChildQueue. + // Log and ignore. if (e.getMessage() != null && e.getMessage().contains("Response has already been written")) { LOGGER.debug("Client disconnected before onComplete, response already closed for task {}", taskId.get()); } else { @@ -656,36 +715,26 @@ public void onComplete() { } } } - - private void startBackgroundConsumption() { - if (backgroundConsumeStarted.compareAndSet(false, true)) { - LOGGER.debug("Starting background consumption for task {}", taskId.get()); - // Client disconnected: continue consuming and persisting events in background - CompletableFuture bgTask = CompletableFuture.runAsync(() -> { - try { - LOGGER.debug("Background consumption thread started for task {}", taskId.get()); - resultAggregator.consumeAll(consumer); - LOGGER.debug("Background consumption completed for task {}", taskId.get()); - } catch (Exception e) { - LOGGER.error("Error during background consumption for task {}", taskId.get(), e); - } - }, executor); - trackBackgroundTask(bgTask); - } else { - LOGGER.debug("Background consumption already started for task {}", taskId.get()); - } - } }); }; } finally { - LOGGER.debug("onMessageSendStream FINALLY - task: {}; runningAgents: {}; backgroundTasks: {}", - taskId.get(), runningAgents.size(), backgroundTasks.size()); - - // Remove agent from map immediately to prevent accumulation - CompletableFuture agentFuture = runningAgents.remove(taskId.get()); - LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId.get(), runningAgents.size()); - - trackBackgroundTask(cleanupProducer(agentFuture, null, Objects.requireNonNull(taskId.get()), queue, true)); + // Needed to satisfy Nullaway + String idOfTask = taskId.get(); + if (idOfTask != null) { + LOGGER.debug("onMessageSendStream FINALLY - task: {}; runningAgents: {}", + idOfTask, runningAgents.size()); + + // Remove agent from map immediately to prevent accumulation + CompletableFuture agentFuture = runningAgents.remove(idOfTask); + LOGGER.debug("Removed agent for task {} from runningAgents in finally block, size after: {}", taskId.get(), runningAgents.size()); + + cleanupProducer(agentFuture, null, idOfTask, queue, true) + .whenComplete((res, err) -> { + if (err != null) { + LOGGER.error("Error during async cleanup for streaming task {}", taskId.get(), err); + } + }); + } } } @@ -746,7 +795,7 @@ public Flow.Publisher onResubscribeToTask( } TaskManager taskManager = new TaskManager(task.id(), task.contextId(), taskStore, null); - ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor); + ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor, eventConsumerExecutor); EventQueue queue = queueManager.tap(task.id()); LOGGER.debug("onResubscribeToTask - tapped queue: {}", queue != null ? System.identityHashCode(queue) : "null"); @@ -761,6 +810,13 @@ public Flow.Publisher onResubscribeToTask( queue = queueManager.createOrTap(task.id()); } + // Per A2A Protocol Spec 3.1.6 (Subscribe to Task): + // "The operation MUST return a Task object as the first event in the stream, + // representing the current state of the task at the time of subscription." + // Enqueue the current task state directly to this ChildQueue only (already persisted, no need for MainEventBus) + queue.enqueueEventLocalOnly(task); + LOGGER.debug("onResubscribeToTask - enqueued current task state as first event for taskId: {}", params.id()); + EventConsumer consumer = new EventConsumer(queue); Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); LOGGER.debug("onResubscribeToTask - returning publisher for taskId: {}", params.id()); @@ -819,8 +875,7 @@ public void run() { LOGGER.debug("Agent execution starting for task {}", taskId); agentExecutor.execute(requestContext, queue); LOGGER.debug("Agent execution completed for task {}", taskId); - // No longer wait for queue poller to start - the consumer (which is guaranteed - // to be running on the Vert.x worker thread) will handle queue lifecycle. + // The consumer (running on the Vert.x worker thread) handles queue lifecycle. // This avoids blocking agent-executor threads waiting for worker threads. } }; @@ -833,8 +888,8 @@ public void run() { // Don't close queue here - let the consumer handle it via error callback // This ensures the consumer (which may not have started polling yet) gets the error } - // Queue lifecycle is now managed entirely by EventConsumer.consumeAll() - // which closes the queue on final events. No need to close here. + // Queue lifecycle is managed by EventConsumer.consumeAll() + // which closes the queue on final events. logThreadStats("AGENT COMPLETE END"); runnable.invokeDoneCallbacks(); }); @@ -843,47 +898,6 @@ public void run() { return runnable; } - private void trackBackgroundTask(CompletableFuture task) { - backgroundTasks.add(task); - LOGGER.debug("Tracking background task (total: {}): {}", backgroundTasks.size(), task); - - task.whenComplete((result, throwable) -> { - try { - if (throwable != null) { - // Unwrap CompletionException to check for CancellationException - Throwable cause = throwable; - if (throwable instanceof java.util.concurrent.CompletionException && throwable.getCause() != null) { - cause = throwable.getCause(); - } - - if (cause instanceof java.util.concurrent.CancellationException) { - LOGGER.debug("Background task cancelled: {}", task); - } else { - LOGGER.error("Background task failed", throwable); - } - } - } finally { - backgroundTasks.remove(task); - LOGGER.debug("Removed background task (remaining: {}): {}", backgroundTasks.size(), task); - } - }); - } - - /** - * Wait for all background tasks to complete. - * Useful for testing to ensure cleanup completes before assertions. - * - * @return CompletableFuture that completes when all background tasks finish - */ - public CompletableFuture waitForBackgroundTasks() { - CompletableFuture[] tasks = backgroundTasks.toArray(new CompletableFuture[0]); - if (tasks.length == 0) { - return CompletableFuture.completedFuture(null); - } - LOGGER.debug("Waiting for {} background tasks to complete", tasks.length); - return CompletableFuture.allOf(tasks); - } - private CompletableFuture cleanupProducer(@Nullable CompletableFuture agentFuture, @Nullable CompletableFuture consumptionFuture, String taskId, EventQueue queue, boolean isStreaming) { LOGGER.debug("Starting cleanup for task {} (streaming={})", taskId, isStreaming); logThreadStats("CLEANUP START"); @@ -908,14 +922,20 @@ private CompletableFuture cleanupProducer(@Nullable CompletableFuture cleanupProducer(@Nullable CompletableFuture + * Enable independently with: {@code logging.level.io.a2a.server.diagnostics.ThreadStats=DEBUG} + *

    */ @SuppressWarnings("unused") // Used for debugging private void logThreadStats(String label) { // Early return if debug logging is not enabled to avoid overhead - if (!LOGGER.isDebugEnabled()) { + if (!THREAD_STATS_LOGGER.isDebugEnabled()) { return; } @@ -982,28 +1036,57 @@ private void logThreadStats(String label) { } int activeThreads = rootGroup.activeCount(); - LOGGER.debug("=== THREAD STATS: {} ===", label); - LOGGER.debug("Active threads: {}", activeThreads); - LOGGER.debug("Running agents: {}", runningAgents.size()); - LOGGER.debug("Background tasks: {}", backgroundTasks.size()); - LOGGER.debug("Queue manager active queues: {}", queueManager.getClass().getSimpleName()); + // Count specific thread types + Thread[] threads = new Thread[activeThreads * 2]; + int count = rootGroup.enumerate(threads); + int eventConsumerThreads = 0; + int agentExecutorThreads = 0; + for (int i = 0; i < count; i++) { + if (threads[i] != null) { + String name = threads[i].getName(); + if (name.startsWith("a2a-event-consumer-")) { + eventConsumerThreads++; + } else if (name.startsWith("a2a-agent-executor-")) { + agentExecutorThreads++; + } + } + } + + THREAD_STATS_LOGGER.debug("=== THREAD STATS: {} ===", label); + THREAD_STATS_LOGGER.debug("Total active threads: {}", activeThreads); + THREAD_STATS_LOGGER.debug("EventConsumer threads: {}", eventConsumerThreads); + THREAD_STATS_LOGGER.debug("AgentExecutor threads: {}", agentExecutorThreads); + THREAD_STATS_LOGGER.debug("Running agents: {}", runningAgents.size()); + THREAD_STATS_LOGGER.debug("Queue manager active queues: {}", queueManager.getClass().getSimpleName()); // List running agents if (!runningAgents.isEmpty()) { - LOGGER.debug("Running agent tasks:"); + THREAD_STATS_LOGGER.debug("Running agent tasks:"); runningAgents.forEach((taskId, future) -> - LOGGER.debug(" - Task {}: {}", taskId, future.isDone() ? "DONE" : "RUNNING") + THREAD_STATS_LOGGER.debug(" - Task {}: {}", taskId, future.isDone() ? "DONE" : "RUNNING") ); } - // List background tasks - if (!backgroundTasks.isEmpty()) { - LOGGER.debug("Background tasks:"); - backgroundTasks.forEach(task -> - LOGGER.debug(" - {}: {}", task, task.isDone() ? "DONE" : "RUNNING") - ); + THREAD_STATS_LOGGER.debug("=== END THREAD STATS ==="); + } + + /** + * Check if an event represents a final task state. + * + * @param eventKind the event to check + * @return true if the event represents a final state (COMPLETED, FAILED, CANCELED, REJECTED, UNKNOWN) + */ + private boolean isFinalEvent(EventKind eventKind) { + if (!(eventKind instanceof Event event)) { + return false; + } + if (event instanceof Task task) { + return task.status() != null && task.status().state() != null + && task.status().state().isFinal(); + } else if (event instanceof TaskStatusUpdateEvent statusUpdate) { + return statusUpdate.isFinal(); } - LOGGER.debug("=== END THREAD STATS ==="); + return false; } private record MessageSendSetup(TaskManager taskManager, @Nullable Task task, RequestContext requestContext) {} diff --git a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java index 1e3ae1206..15f94d7e8 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/InMemoryTaskStore.java @@ -32,8 +32,9 @@ public class InMemoryTaskStore implements TaskStore, TaskStateProvider { private final ConcurrentMap tasks = new ConcurrentHashMap<>(); @Override - public void save(Task task) { + public void save(Task task, boolean isReplicated) { tasks.put(task.id(), task); + // InMemoryTaskStore doesn't fire TaskFinalizedEvent, so isReplicated is unused here } @Override diff --git a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java index 95684e199..506b3f3b6 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java +++ b/server-common/src/main/java/io/a2a/server/tasks/ResultAggregator.java @@ -14,9 +14,9 @@ import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueueItem; import io.a2a.spec.A2AError; -import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; import io.a2a.spec.EventKind; +import io.a2a.spec.InternalError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; @@ -31,12 +31,14 @@ public class ResultAggregator { private final TaskManager taskManager; private final Executor executor; + private final Executor eventConsumerExecutor; private volatile @Nullable Message message; - public ResultAggregator(TaskManager taskManager, @Nullable Message message, Executor executor) { + public ResultAggregator(TaskManager taskManager, @Nullable Message message, Executor executor, Executor eventConsumerExecutor) { this.taskManager = taskManager; this.message = message; this.executor = executor; + this.eventConsumerExecutor = eventConsumerExecutor; } public @Nullable EventKind getCurrentResult() { @@ -49,20 +51,23 @@ public ResultAggregator(TaskManager taskManager, @Nullable Message message, Exec public Flow.Publisher consumeAndEmit(EventConsumer consumer) { Flow.Publisher allItems = consumer.consumeAll(); - // Process items conditionally - only save non-replicated events to database - return processor(createTubeConfig(), allItems, (errorConsumer, item) -> { - // Only process non-replicated events to avoid duplicate database writes - if (!item.isReplicated()) { - try { - callTaskManagerProcess(item.getEvent()); - } catch (A2AServerException e) { - errorConsumer.accept(e); - return false; - } - } - // Continue processing and emit (both replicated and non-replicated) + // Just stream events - no persistence needed + // TaskStore update moved to MainEventBusProcessor + Flow.Publisher processed = processor(createTubeConfig(), allItems, (errorConsumer, item) -> { + // Continue processing and emit all events return true; }); + + // Wrap the publisher to ensure subscription happens on eventConsumerExecutor + // This prevents EventConsumer polling loop from running on AgentExecutor threads + // which caused thread accumulation when those threads didn't timeout + return new Flow.Publisher() { + @Override + public void subscribe(Flow.Subscriber subscriber) { + // Submit subscription to eventConsumerExecutor to isolate polling work + eventConsumerExecutor.execute(() -> processed.subscribe(subscriber)); + } + }; } public EventKind consumeAll(EventConsumer consumer) throws A2AError { @@ -81,15 +86,7 @@ public EventKind consumeAll(EventConsumer consumer) throws A2AError { return false; } } - // Only process non-replicated events to avoid duplicate database writes - if (!item.isReplicated()) { - try { - callTaskManagerProcess(event); - } catch (A2AServerException e) { - error.set(e); - return false; - } - } + // TaskStore update moved to MainEventBusProcessor return true; }, error::set); @@ -113,18 +110,24 @@ public EventKind consumeAll(EventConsumer consumer) throws A2AError { public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, boolean blocking) throws A2AError { Flow.Publisher allItems = consumer.consumeAll(); AtomicReference message = new AtomicReference<>(); + AtomicReference capturedTask = new AtomicReference<>(); // Capture Task events AtomicBoolean interrupted = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); CompletableFuture completionFuture = new CompletableFuture<>(); // Separate future for tracking background consumption completion CompletableFuture consumptionCompletionFuture = new CompletableFuture<>(); + // Latch to ensure EventConsumer starts polling before we wait on completionFuture + java.util.concurrent.CountDownLatch pollingStarted = new java.util.concurrent.CountDownLatch(1); // CRITICAL: The subscription itself must run on a background thread to avoid blocking // the Vert.x worker thread. EventConsumer.consumeAll() starts a polling loop that // blocks in dequeueEventItem(), so we must subscribe from a background thread. - // Use the @Internal executor (not ForkJoinPool.commonPool) to avoid saturation - // during concurrent request bursts. + // Use the dedicated @EventConsumerExecutor (cached thread pool) which creates threads + // on demand for I/O-bound polling. Using the @Internal executor caused deadlock when + // pool exhausted (100+ concurrent queues but maxPoolSize=50). CompletableFuture.runAsync(() -> { + // Signal that polling is about to start + pollingStarted.countDown(); consumer( createTubeConfig(), allItems, @@ -146,25 +149,30 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, return false; } - // Process event through TaskManager - only for non-replicated events - if (!item.isReplicated()) { - try { - callTaskManagerProcess(event); - } catch (A2AServerException e) { - errorRef.set(e); - completionFuture.completeExceptionally(e); - return false; + // Capture Task events (especially for new tasks where taskManager.getTask() would return null) + // We capture the LATEST task to ensure we get the most up-to-date state + if (event instanceof Task t) { + Task previousTask = capturedTask.get(); + capturedTask.set(t); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Captured Task event: id={}, state={} (previous: {})", + t.id(), t.status().state(), + previousTask != null ? previousTask.id() + "/" + previousTask.status().state() : "none"); } } + // TaskStore update moved to MainEventBusProcessor + // Determine interrupt behavior boolean shouldInterrupt = false; - boolean continueInBackground = false; boolean isFinalEvent = (event instanceof Task task && task.status().state().isFinal()) || (event instanceof TaskStatusUpdateEvent tsue && tsue.isFinal()); boolean isAuthRequired = (event instanceof Task task && task.status().state() == TaskState.AUTH_REQUIRED) || (event instanceof TaskStatusUpdateEvent tsue && tsue.status().state() == TaskState.AUTH_REQUIRED); + LOGGER.debug("ResultAggregator: Evaluating interrupt (blocking={}, isFinal={}, isAuth={}, eventType={})", + blocking, isFinalEvent, isAuthRequired, event.getClass().getSimpleName()); + // Always interrupt on auth_required, as it needs external action. if (isAuthRequired) { // auth-required is a special state: the message should be @@ -174,20 +182,19 @@ public EventTypeAndInterrupt consumeAndBreakOnInterrupt(EventConsumer consumer, // new request is expected in order for the agent to make progress, // so the agent should exit. shouldInterrupt = true; - continueInBackground = true; + LOGGER.debug("ResultAggregator: Setting shouldInterrupt=true (AUTH_REQUIRED)"); } else if (!blocking) { // For non-blocking calls, interrupt as soon as a task is available. shouldInterrupt = true; - continueInBackground = true; + LOGGER.debug("ResultAggregator: Setting shouldInterrupt=true (non-blocking)"); } else if (blocking) { // For blocking calls: Interrupt to free Vert.x thread, but continue in background // Python's async consumption doesn't block threads, but Java's does // So we interrupt to return quickly, then rely on background consumption - // DefaultRequestHandler will fetch the final state from TaskStore shouldInterrupt = true; - continueInBackground = true; + LOGGER.debug("ResultAggregator: Setting shouldInterrupt=true (blocking, isFinal={})", isFinalEvent); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Blocking call for task {}: {} event, returning with background consumption", taskIdForLogging(), isFinalEvent ? "final" : "non-final"); @@ -195,14 +202,14 @@ else if (blocking) { } if (shouldInterrupt) { + LOGGER.debug("ResultAggregator: Interrupting consumption (setting interrupted=true)"); // Complete the future to unblock the main thread interrupted.set(true); completionFuture.complete(null); // For blocking calls, DON'T complete consumptionCompletionFuture here. // Let it complete naturally when subscription finishes (onComplete callback below). - // This ensures all events are processed and persisted to TaskStore before - // DefaultRequestHandler.cleanupProducer() proceeds with cleanup. + // This ensures all events are fully processed before cleanup. // // For non-blocking and auth-required calls, complete immediately to allow // cleanup to proceed while consumption continues in background. @@ -237,7 +244,16 @@ else if (blocking) { } } ); - }, executor); + }, eventConsumerExecutor); + + // Wait for EventConsumer to start polling before we wait for events + // This prevents race where agent enqueues events before EventConsumer starts + try { + pollingStarted.await(5, java.util.concurrent.TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new io.a2a.spec.InternalError("Interrupted waiting for EventConsumer to start"); + } // Wait for completion or interruption try { @@ -261,28 +277,30 @@ else if (blocking) { Utils.rethrow(error); } - EventKind eventType; - Message msg = message.get(); - if (msg != null) { - eventType = msg; - } else { - Task task = taskManager.getTask(); - if (task == null) { - throw new io.a2a.spec.InternalError("No task or message available after consuming events"); + // Return Message if captured, otherwise Task if captured, otherwise fetch from TaskStore + EventKind eventKind = message.get(); + if (eventKind == null) { + eventKind = capturedTask.get(); + if (LOGGER.isDebugEnabled() && eventKind instanceof Task t) { + LOGGER.debug("Returning capturedTask: id={}, state={}", t.id(), t.status().state()); } - eventType = task; + } + if (eventKind == null) { + eventKind = taskManager.getTask(); + if (LOGGER.isDebugEnabled() && eventKind instanceof Task t) { + LOGGER.debug("Returning task from TaskStore: id={}, state={}", t.id(), t.status().state()); + } + } + if (eventKind == null) { + throw new InternalError("Could not find a Task/Message for " + taskManager.getTaskId()); } return new EventTypeAndInterrupt( - eventType, + eventKind, interrupted.get(), consumptionCompletionFuture); } - private void callTaskManagerProcess(Event event) throws A2AServerException { - taskManager.process(event); - } - private String taskIdForLogging() { Task task = taskManager.getTask(); return task != null ? task.id() : "unknown"; diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java index fd3696a60..948ec596c 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskManager.java @@ -12,7 +12,7 @@ import io.a2a.spec.A2AServerException; import io.a2a.spec.Event; -import io.a2a.spec.InvalidParamsError; +import io.a2a.spec.InternalError; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskArtifactUpdateEvent; @@ -59,12 +59,13 @@ public TaskManager(@Nullable String taskId, @Nullable String contextId, TaskStor return currentTask; } - Task saveTaskEvent(Task task) throws A2AServerException { + boolean saveTaskEvent(Task task, boolean isReplicated) throws A2AServerException { checkIdsAndUpdateIfNecessary(task.id(), task.contextId()); - return saveTask(task); + Task savedTask = saveTask(task, isReplicated); + return savedTask.status() != null && savedTask.status().state() != null && savedTask.status().state().isFinal(); } - Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { + boolean saveTaskEvent(TaskStatusUpdateEvent event, boolean isReplicated) throws A2AServerException { checkIdsAndUpdateIfNecessary(event.taskId(), event.contextId()); Task task = ensureTask(event.taskId(), event.contextId()); @@ -86,10 +87,11 @@ Task saveTaskEvent(TaskStatusUpdateEvent event) throws A2AServerException { } task = builder.build(); - return saveTask(task); + Task savedTask = saveTask(task, isReplicated); + return savedTask.status() != null && savedTask.status().state() != null && savedTask.status().state().isFinal(); } - Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { + boolean saveTaskEvent(TaskArtifactUpdateEvent event, boolean isReplicated) throws A2AServerException { checkIdsAndUpdateIfNecessary(event.taskId(), event.contextId()); Task task = ensureTask(event.taskId(), event.contextId()); // taskId is guaranteed to be non-null after checkIdsAndUpdateIfNecessary @@ -98,18 +100,20 @@ Task saveTaskEvent(TaskArtifactUpdateEvent event) throws A2AServerException { throw new IllegalStateException("taskId should not be null after checkIdsAndUpdateIfNecessary"); } task = appendArtifactToTask(task, event, nonNullTaskId); - return saveTask(task); + Task savedTask = saveTask(task, isReplicated); + return savedTask.status() != null && savedTask.status().state() != null && savedTask.status().state().isFinal(); } - public Event process(Event event) throws A2AServerException { + public boolean process(Event event, boolean isReplicated) throws A2AServerException { + boolean isFinal = false; if (event instanceof Task task) { - saveTaskEvent(task); + isFinal = saveTaskEvent(task, isReplicated); } else if (event instanceof TaskStatusUpdateEvent taskStatusUpdateEvent) { - saveTaskEvent(taskStatusUpdateEvent); + isFinal = saveTaskEvent(taskStatusUpdateEvent, isReplicated); } else if (event instanceof TaskArtifactUpdateEvent taskArtifactUpdateEvent) { - saveTaskEvent(taskArtifactUpdateEvent); + isFinal = saveTaskEvent(taskArtifactUpdateEvent, isReplicated); } - return event; + return isFinal; } public Task updateWithMessage(Message message, Task task) { @@ -125,7 +129,7 @@ public Task updateWithMessage(Message message, Task task) { .status(status) .history(history) .build(); - saveTask(task); + saveTask(task, false); // Local operation, not replicated return task; } @@ -133,7 +137,7 @@ private void checkIdsAndUpdateIfNecessary(String eventTaskId, String eventContex if (taskId != null && !eventTaskId.equals(taskId)) { throw new A2AServerException( "Invalid task id", - new InvalidParamsError(String.format("Task in event doesn't match TaskManager "))); + new InternalError(String.format("Task event has taskId %s but TaskManager has %s", eventTaskId, taskId))); } if (taskId == null) { taskId = eventTaskId; @@ -155,7 +159,7 @@ private Task ensureTask(String eventTaskId, String eventContextId) { } if (task == null) { task = createTask(eventTaskId, eventContextId); - saveTask(task); + saveTask(task, false); // Local operation, not replicated } return task; } @@ -170,8 +174,8 @@ private Task createTask(String taskId, String contextId) { .build(); } - private Task saveTask(Task task) { - taskStore.save(task); + private Task saveTask(Task task, boolean isReplicated) { + taskStore.save(task, isReplicated); if (taskId == null) { taskId = task.id(); contextId = task.contextId(); diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java index 18707fba2..3df903f77 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskStore.java @@ -100,8 +100,11 @@ public interface TaskStore { * Saves or updates a task. * * @param task the task to save + * @param isReplicated true if this task update came from a replicated event, + * false if it originated locally. Used to prevent feedback loops + * in replicated scenarios (e.g., don't fire TaskFinalizedEvent for replicated updates) */ - void save(Task task); + void save(Task task, boolean isReplicated); /** * Retrieves a task by its ID. diff --git a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java index e26dd55fb..eee254ba3 100644 --- a/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java +++ b/server-common/src/main/java/io/a2a/server/util/async/AsyncExecutorProducer.java @@ -1,8 +1,8 @@ package io.a2a.server.util.async; +import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -26,6 +26,7 @@ public class AsyncExecutorProducer { private static final String A2A_EXECUTOR_CORE_POOL_SIZE = "a2a.executor.core-pool-size"; private static final String A2A_EXECUTOR_MAX_POOL_SIZE = "a2a.executor.max-pool-size"; private static final String A2A_EXECUTOR_KEEP_ALIVE_SECONDS = "a2a.executor.keep-alive-seconds"; + private static final String A2A_EXECUTOR_QUEUE_CAPACITY = "a2a.executor.queue-capacity"; @Inject A2AConfigProvider configProvider; @@ -57,6 +58,16 @@ public class AsyncExecutorProducer { */ long keepAliveSeconds; + /** + * Queue capacity for pending tasks. + *

    + * Property: {@code a2a.executor.queue-capacity}
    + * Default: 100
    + * Note: Must be bounded to allow pool growth to maxPoolSize. + * When queue is full, new threads are created up to maxPoolSize. + */ + int queueCapacity; + private @Nullable ExecutorService executor; @PostConstruct @@ -64,18 +75,34 @@ public void init() { corePoolSize = Integer.parseInt(configProvider.getValue(A2A_EXECUTOR_CORE_POOL_SIZE)); maxPoolSize = Integer.parseInt(configProvider.getValue(A2A_EXECUTOR_MAX_POOL_SIZE)); keepAliveSeconds = Long.parseLong(configProvider.getValue(A2A_EXECUTOR_KEEP_ALIVE_SECONDS)); - - LOGGER.info("Initializing async executor: corePoolSize={}, maxPoolSize={}, keepAliveSeconds={}", - corePoolSize, maxPoolSize, keepAliveSeconds); - - executor = new ThreadPoolExecutor( + queueCapacity = Integer.parseInt(configProvider.getValue(A2A_EXECUTOR_QUEUE_CAPACITY)); + + LOGGER.info("Initializing async executor: corePoolSize={}, maxPoolSize={}, keepAliveSeconds={}, queueCapacity={}", + corePoolSize, maxPoolSize, keepAliveSeconds, queueCapacity); + + // CRITICAL: Use ArrayBlockingQueue (bounded) instead of LinkedBlockingQueue (unbounded). + // With unbounded queue, ThreadPoolExecutor NEVER grows beyond corePoolSize because the + // queue never fills. This causes executor pool exhaustion during concurrent requests when + // EventConsumer polling threads hold all core threads and agent tasks queue indefinitely. + // Bounded queue enables pool growth: when queue is full, new threads are created up to + // maxPoolSize, preventing agent execution starvation. + ThreadPoolExecutor tpe = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveSeconds, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), + new ArrayBlockingQueue<>(queueCapacity), new A2AThreadFactory() ); + + // CRITICAL: Allow core threads to timeout after keepAliveSeconds when idle. + // By default, ThreadPoolExecutor only times out threads above corePoolSize. + // Without this, core threads accumulate during testing and never clean up. + // This is essential for streaming scenarios where many short-lived tasks create threads + // for agent execution and cleanup callbacks, but those threads remain idle afterward. + tpe.allowCoreThreadTimeOut(true); + + executor = tpe; } @PreDestroy @@ -106,6 +133,22 @@ public Executor produce() { return executor; } + /** + * Log current executor pool statistics for diagnostics. + * Useful for debugging pool exhaustion or sizing issues. + */ + public void logPoolStats() { + if (executor instanceof ThreadPoolExecutor tpe) { + LOGGER.info("Executor pool stats: active={}/{}, queued={}/{}, completed={}, total={}", + tpe.getActiveCount(), + tpe.getPoolSize(), + tpe.getQueue().size(), + queueCapacity, + tpe.getCompletedTaskCount(), + tpe.getTaskCount()); + } + } + private static class A2AThreadFactory implements ThreadFactory { private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix = "a2a-agent-executor-"; diff --git a/server-common/src/main/java/io/a2a/server/util/async/EventConsumerExecutorProducer.java b/server-common/src/main/java/io/a2a/server/util/async/EventConsumerExecutorProducer.java new file mode 100644 index 000000000..24ff7f5d1 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/util/async/EventConsumerExecutorProducer.java @@ -0,0 +1,93 @@ +package io.a2a.server.util.async; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Qualifier; + +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Produces a dedicated executor for EventConsumer polling threads. + *

    + * CRITICAL: EventConsumer polling must use a separate executor from AgentExecutor because: + *

      + *
    • EventConsumer threads are I/O-bound (blocking on queue.poll()), not CPU-bound
    • + *
    • One EventConsumer thread needed per active queue (can be 100+ concurrent)
    • + *
    • Threads are mostly idle, waiting for events
    • + *
    • Using the same bounded pool as AgentExecutor causes deadlock when pool exhausted
    • + *
    + *

    + * Uses a cached thread pool (unbounded) with automatic thread reclamation: + *

      + *
    • Creates threads on demand as EventConsumers start
    • + *
    • Idle threads automatically terminated after 10 seconds
    • + *
    • No queue saturation since threads are created as needed
    • + *
    + */ +@ApplicationScoped +public class EventConsumerExecutorProducer { + private static final Logger LOGGER = LoggerFactory.getLogger(EventConsumerExecutorProducer.class); + + /** + * Qualifier annotation for EventConsumer executor injection. + */ + @Retention(RUNTIME) + @Target({METHOD, FIELD, PARAMETER, TYPE}) + @Qualifier + public @interface EventConsumerExecutor { + } + + /** + * Thread factory for EventConsumer threads. + */ + private static class EventConsumerThreadFactory implements ThreadFactory { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "a2a-event-consumer-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + } + + private @Nullable ExecutorService executor; + + @Produces + @EventConsumerExecutor + @ApplicationScoped + public Executor eventConsumerExecutor() { + // Cached thread pool with 10s idle timeout (reduced from default 60s): + // - Creates threads on demand as EventConsumers start + // - Reclaims idle threads after 10s to prevent accumulation during fast test execution + // - Perfect for I/O-bound EventConsumer polling which blocks on queue.poll() + // - 10s timeout balances thread reuse (production) vs cleanup (testing) + executor = new ThreadPoolExecutor( + 0, // corePoolSize - no core threads + Integer.MAX_VALUE, // maxPoolSize - unbounded + 10, TimeUnit.SECONDS, // keepAliveTime - 10s idle timeout + new SynchronousQueue<>(), // queue - same as cached pool + new EventConsumerThreadFactory() + ); + + LOGGER.info("Initialized EventConsumer executor: cached thread pool (unbounded, 10s idle timeout)"); + + return executor; + } +} diff --git a/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java b/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java new file mode 100644 index 000000000..737fbac23 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java @@ -0,0 +1,136 @@ +package io.a2a.server.util.sse; + +import io.a2a.grpc.utils.JSONRPCUtils; +import io.a2a.jsonrpc.common.wrappers.A2AErrorResponse; +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.GetExtendedAgentCardResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; +import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; +import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; +import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; + +/** + * Framework-agnostic utility for formatting A2A responses as Server-Sent Events (SSE). + *

    + * Provides static methods to serialize A2A responses to JSON and format them as SSE events. + * This allows HTTP server frameworks (Vert.x, Jakarta/WildFly, etc.) to use their own + * reactive libraries for publisher mapping while sharing the serialization logic. + *

    + * Example usage (Quarkus/Vert.x with Mutiny): + *

    {@code
    + * Flow.Publisher> responses = handler.onMessageSendStream(request, context);
    + * AtomicLong eventId = new AtomicLong(0);
    + *
    + * Multi sseEvents = Multi.createFrom().publisher(responses)
    + *     .map(response -> SseFormatter.formatResponseAsSSE(response, eventId.getAndIncrement()));
    + *
    + * sseEvents.subscribe().with(sseEvent -> httpResponse.write(Buffer.buffer(sseEvent)));
    + * }
    + *

    + * Example usage (Jakarta/WildFly with custom reactive library): + *

    {@code
    + * Flow.Publisher jsonStrings = restHandler.getJsonPublisher();
    + * AtomicLong eventId = new AtomicLong(0);
    + *
    + * Flow.Publisher sseEvents = mapPublisher(jsonStrings,
    + *     json -> SseFormatter.formatJsonAsSSE(json, eventId.getAndIncrement()));
    + * }
    + */ +public class SseFormatter { + + private SseFormatter() { + // Utility class - prevent instantiation + } + + /** + * Format an A2A response as an SSE event. + *

    + * Serializes the response to JSON and formats as: + *

    +     * data: {"jsonrpc":"2.0","result":{...},"id":123}
    +     * id: 0
    +     *
    +     * 
    + * + * @param response the A2A response to format + * @param eventId the SSE event ID + * @return SSE-formatted string (ready to write to HTTP response) + */ + public static String formatResponseAsSSE(A2AResponse response, long eventId) { + String jsonData = serializeResponse(response); + return "data: " + jsonData + "\nid: " + eventId + "\n\n"; + } + + /** + * Format a pre-serialized JSON string as an SSE event. + *

    + * Wraps the JSON in SSE format as: + *

    +     * data: {"jsonrpc":"2.0","result":{...},"id":123}
    +     * id: 0
    +     *
    +     * 
    + *

    + * Use this when you already have JSON strings (e.g., from REST transport) + * and just need to add SSE formatting. + * + * @param jsonString the JSON string to wrap + * @param eventId the SSE event ID + * @return SSE-formatted string (ready to write to HTTP response) + */ + public static String formatJsonAsSSE(String jsonString, long eventId) { + return "data: " + jsonString + "\nid: " + eventId + "\n\n"; + } + + /** + * Serialize an A2AResponse to JSON string. + */ + private static String serializeResponse(A2AResponse response) { + // For error responses, use standard JSON-RPC error format + if (response instanceof A2AErrorResponse error) { + return JSONRPCUtils.toJsonRPCErrorResponse(error.getId(), error.getError()); + } + if (response.getError() != null) { + return JSONRPCUtils.toJsonRPCErrorResponse(response.getId(), response.getError()); + } + + // Convert domain response to protobuf message and serialize + com.google.protobuf.MessageOrBuilder protoMessage = convertToProto(response); + return JSONRPCUtils.toJsonRPCResultResponse(response.getId(), protoMessage); + } + + /** + * Convert A2AResponse to protobuf message for serialization. + */ + private static com.google.protobuf.MessageOrBuilder convertToProto(A2AResponse response) { + if (response instanceof GetTaskResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.task(r.getResult()); + } else if (response instanceof CancelTaskResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.task(r.getResult()); + } else if (response instanceof SendMessageResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.taskOrMessage(r.getResult()); + } else if (response instanceof ListTasksResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.listTasksResult(r.getResult()); + } else if (response instanceof SetTaskPushNotificationConfigResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.setTaskPushNotificationConfigResponse(r.getResult()); + } else if (response instanceof GetTaskPushNotificationConfigResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.getTaskPushNotificationConfigResponse(r.getResult()); + } else if (response instanceof ListTaskPushNotificationConfigResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.listTaskPushNotificationConfigResponse(r.getResult()); + } else if (response instanceof DeleteTaskPushNotificationConfigResponse) { + // DeleteTaskPushNotificationConfig has no result body, just return empty message + return com.google.protobuf.Empty.getDefaultInstance(); + } else if (response instanceof GetExtendedAgentCardResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.getExtendedCardResponse(r.getResult()); + } else if (response instanceof SendStreamingMessageResponse r) { + return io.a2a.grpc.utils.ProtoUtils.ToProto.taskOrMessageStream(r.getResult()); + } else { + throw new IllegalArgumentException("Unknown response type: " + response.getClass().getName()); + } + } +} diff --git a/server-common/src/main/java/io/a2a/server/util/sse/package-info.java b/server-common/src/main/java/io/a2a/server/util/sse/package-info.java new file mode 100644 index 000000000..7e668b632 --- /dev/null +++ b/server-common/src/main/java/io/a2a/server/util/sse/package-info.java @@ -0,0 +1,11 @@ +/** + * Server-Sent Events (SSE) formatting utilities for A2A streaming responses. + *

    + * Provides framework-agnostic conversion of {@code Flow.Publisher>} to + * {@code Flow.Publisher} with SSE formatting, enabling easy integration with + * any HTTP server framework (Vert.x, Jakarta Servlet, etc.). + */ +@NullMarked +package io.a2a.server.util.sse; + +import org.jspecify.annotations.NullMarked; diff --git a/server-common/src/main/resources/META-INF/a2a-defaults.properties b/server-common/src/main/resources/META-INF/a2a-defaults.properties index 280fd943b..719be9e7a 100644 --- a/server-common/src/main/resources/META-INF/a2a-defaults.properties +++ b/server-common/src/main/resources/META-INF/a2a-defaults.properties @@ -19,3 +19,7 @@ a2a.executor.max-pool-size=50 # Keep-alive time for idle threads (seconds) a2a.executor.keep-alive-seconds=60 + +# Queue capacity for pending tasks (must be bounded to enable pool growth) +# When queue is full, new threads are created up to max-pool-size +a2a.executor.queue-capacity=100 diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 4354f1639..146bfb10a 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -16,6 +16,8 @@ import java.util.concurrent.atomic.AtomicReference; import io.a2a.jsonrpc.common.json.JsonProcessingException; +import io.a2a.server.tasks.InMemoryTaskStore; +import io.a2a.server.tasks.PushNotificationSender; import io.a2a.spec.A2AError; import io.a2a.spec.A2AServerException; import io.a2a.spec.Artifact; @@ -27,14 +29,19 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class EventConsumerTest { + private static final PushNotificationSender NOOP_PUSHNOTIFICATION_SENDER = task -> {}; + private static final String TASK_ID = "123"; // Must match MINIMAL_TASK id + private EventQueue eventQueue; private EventConsumer eventConsumer; - + private MainEventBus mainEventBus; + private MainEventBusProcessor mainEventBusProcessor; private static final String MINIMAL_TASK = """ { @@ -54,10 +61,59 @@ public class EventConsumerTest { @BeforeEach public void init() { - eventQueue = EventQueue.builder().build(); + // Set up MainEventBus and processor for production-like test environment + InMemoryTaskStore taskStore = new InMemoryTaskStore(); + mainEventBus = new MainEventBus(); + InMemoryQueueManager queueManager = new InMemoryQueueManager(taskStore, mainEventBus); + mainEventBusProcessor = new MainEventBusProcessor(mainEventBus, taskStore, NOOP_PUSHNOTIFICATION_SENDER, queueManager); + EventQueueUtil.start(mainEventBusProcessor); + + eventQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .taskId(TASK_ID) + .mainEventBus(mainEventBus) + .build().tap(); eventConsumer = new EventConsumer(eventQueue); } + @AfterEach + public void cleanup() { + if (mainEventBusProcessor != null) { + mainEventBusProcessor.setCallback(null); // Clear any test callbacks + EventQueueUtil.stop(mainEventBusProcessor); + } + } + + /** + * Helper to wait for MainEventBusProcessor to process an event. + * Replaces polling patterns with deterministic callback-based waiting. + * + * @param action the action that triggers event processing + * @throws InterruptedException if waiting is interrupted + * @throws AssertionError if processing doesn't complete within timeout + */ + private void waitForEventProcessing(Runnable action) throws InterruptedException { + CountDownLatch processingLatch = new CountDownLatch(1); + mainEventBusProcessor.setCallback(new MainEventBusProcessorCallback() { + @Override + public void onEventProcessed(String taskId, Event event) { + processingLatch.countDown(); + } + + @Override + public void onTaskFinalized(String taskId) { + // Not needed for basic event processing wait + } + }); + + try { + action.run(); + assertTrue(processingLatch.await(5, TimeUnit.SECONDS), + "MainEventBusProcessor should have processed the event within timeout"); + } finally { + mainEventBusProcessor.setCallback(null); + } + } + @Test public void testConsumeOneTaskEvent() throws Exception { Task event = fromJson(MINIMAL_TASK, Task.class); @@ -92,7 +148,7 @@ public void testConsumeAllMultipleEvents() throws JsonProcessingException { List events = List.of( fromJson(MINIMAL_TASK, Task.class), TaskArtifactUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .artifact(Artifact.builder() .artifactId("11") @@ -100,7 +156,7 @@ public void testConsumeAllMultipleEvents() throws JsonProcessingException { .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) .isFinal(true) @@ -128,7 +184,7 @@ public void testConsumeUntilMessage() throws Exception { List events = List.of( fromJson(MINIMAL_TASK, Task.class), TaskArtifactUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .artifact(Artifact.builder() .artifactId("11") @@ -136,7 +192,7 @@ public void testConsumeUntilMessage() throws Exception { .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) .isFinal(true) @@ -185,14 +241,14 @@ public void testConsumeMessageEvents() throws Exception { @Test public void testConsumeTaskInputRequired() { Task task = Task.builder() - .id("task-id") - .contextId("task-context") + .id(TASK_ID) + .contextId("session-xyz") .status(new TaskStatus(TaskState.INPUT_REQUIRED)) .build(); List events = List.of( task, TaskArtifactUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .artifact(Artifact.builder() .artifactId("11") @@ -200,7 +256,7 @@ public void testConsumeTaskInputRequired() { .build()) .build(), TaskStatusUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) .isFinal(true) @@ -332,7 +388,9 @@ public void onComplete() { @Test public void testConsumeAllStopsOnQueueClosed() throws Exception { - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .mainEventBus(mainEventBus) + .build().tap(); EventConsumer consumer = new EventConsumer(queue); // Close the queue immediately @@ -378,12 +436,16 @@ public void onComplete() { @Test public void testConsumeAllHandlesQueueClosedException() throws Exception { - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .mainEventBus(mainEventBus) + .build().tap(); EventConsumer consumer = new EventConsumer(queue); // Add a message event (which will complete the stream) Event message = fromJson(MESSAGE_PAYLOAD, Message.class); - queue.enqueueEvent(message); + + // Use callback to wait for event processing + waitForEventProcessing(() -> queue.enqueueEvent(message)); // Close the queue before consuming queue.close(); @@ -428,11 +490,13 @@ public void onComplete() { @Test public void testConsumeAllTerminatesOnQueueClosedEvent() throws Exception { - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .mainEventBus(mainEventBus) + .build().tap(); EventConsumer consumer = new EventConsumer(queue); // Enqueue a QueueClosedEvent (poison pill) - QueueClosedEvent queueClosedEvent = new QueueClosedEvent("task-123"); + QueueClosedEvent queueClosedEvent = new QueueClosedEvent(TASK_ID); queue.enqueueEvent(queueClosedEvent); Flow.Publisher publisher = consumer.consumeAll(); @@ -477,8 +541,12 @@ public void onComplete() { } private void enqueueAndConsumeOneEvent(Event event) throws Exception { - eventQueue.enqueueEvent(event); + // Use callback to wait for event processing + waitForEventProcessing(() -> eventQueue.enqueueEvent(event)); + + // Event is now available, consume it directly Event result = eventConsumer.consumeOne(); + assertNotNull(result, "Event should be available"); assertSame(event, result); } diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index a3dc7d916..2499a8173 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -11,7 +11,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import io.a2a.server.tasks.InMemoryTaskStore; +import io.a2a.server.tasks.PushNotificationSender; import io.a2a.spec.A2AError; import io.a2a.spec.Artifact; import io.a2a.spec.Event; @@ -23,12 +27,17 @@ import io.a2a.spec.TaskStatus; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class EventQueueTest { private EventQueue eventQueue; + private MainEventBus mainEventBus; + private MainEventBusProcessor mainEventBusProcessor; + + private static final String TASK_ID = "123"; // Must match MINIMAL_TASK id private static final String MINIMAL_TASK = """ { @@ -46,38 +55,96 @@ public class EventQueueTest { } """; + private static final PushNotificationSender NOOP_PUSHNOTIFICATION_SENDER = task -> {}; @BeforeEach public void init() { - eventQueue = EventQueue.builder().build(); + // Set up MainEventBus and processor for production-like test environment + InMemoryTaskStore taskStore = new InMemoryTaskStore(); + mainEventBus = new MainEventBus(); + InMemoryQueueManager queueManager = new InMemoryQueueManager(taskStore, mainEventBus); + mainEventBusProcessor = new MainEventBusProcessor(mainEventBus, taskStore, NOOP_PUSHNOTIFICATION_SENDER, queueManager); + EventQueueUtil.start(mainEventBusProcessor); + + eventQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .taskId(TASK_ID) + .mainEventBus(mainEventBus) + .build().tap(); + } + + @AfterEach + public void cleanup() { + if (mainEventBusProcessor != null) { + mainEventBusProcessor.setCallback(null); // Clear any test callbacks + EventQueueUtil.stop(mainEventBusProcessor); + } + } + /** + * Helper to create a queue with MainEventBus configured (for tests that need event distribution). + */ + private EventQueue createQueueWithEventBus(String taskId) { + return EventQueueUtil.getEventQueueBuilder(mainEventBus) + .taskId(taskId) + .build(); + } + + /** + * Helper to wait for MainEventBusProcessor to process an event. + * Replaces polling patterns with deterministic callback-based waiting. + * + * @param action the action that triggers event processing + * @throws InterruptedException if waiting is interrupted + * @throws AssertionError if processing doesn't complete within timeout + */ + private void waitForEventProcessing(Runnable action) throws InterruptedException { + CountDownLatch processingLatch = new CountDownLatch(1); + mainEventBusProcessor.setCallback(new io.a2a.server.events.MainEventBusProcessorCallback() { + @Override + public void onEventProcessed(String taskId, io.a2a.spec.Event event) { + processingLatch.countDown(); + } + + @Override + public void onTaskFinalized(String taskId) { + // Not needed for basic event processing wait + } + }); + + try { + action.run(); + assertTrue(processingLatch.await(5, TimeUnit.SECONDS), + "MainEventBusProcessor should have processed the event within timeout"); + } finally { + mainEventBusProcessor.setCallback(null); + } } @Test public void testConstructorDefaultQueueSize() { - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); assertEquals(EventQueue.DEFAULT_QUEUE_SIZE, queue.getQueueSize()); } @Test public void testConstructorCustomQueueSize() { int customSize = 500; - EventQueue queue = EventQueue.builder().queueSize(customSize).build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).queueSize(customSize).build(); assertEquals(customSize, queue.getQueueSize()); } @Test public void testConstructorInvalidQueueSize() { // Test zero queue size - assertThrows(IllegalArgumentException.class, () -> EventQueue.builder().queueSize(0).build()); + assertThrows(IllegalArgumentException.class, () -> EventQueueUtil.getEventQueueBuilder(mainEventBus).queueSize(0).build()); // Test negative queue size - assertThrows(IllegalArgumentException.class, () -> EventQueue.builder().queueSize(-10).build()); + assertThrows(IllegalArgumentException.class, () -> EventQueueUtil.getEventQueueBuilder(mainEventBus).queueSize(-10).build()); } @Test public void testTapCreatesChildQueue() { - EventQueue parentQueue = EventQueue.builder().build(); + EventQueue parentQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); EventQueue childQueue = parentQueue.tap(); assertNotNull(childQueue); @@ -87,7 +154,7 @@ public void testTapCreatesChildQueue() { @Test public void testTapOnChildQueueThrowsException() { - EventQueue parentQueue = EventQueue.builder().build(); + EventQueue parentQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); EventQueue childQueue = parentQueue.tap(); assertThrows(IllegalStateException.class, () -> childQueue.tap()); @@ -95,69 +162,74 @@ public void testTapOnChildQueueThrowsException() { @Test public void testEnqueueEventPropagagesToChildren() throws Exception { - EventQueue parentQueue = EventQueue.builder().build(); - EventQueue childQueue = parentQueue.tap(); + EventQueue mainQueue = createQueueWithEventBus(TASK_ID); + EventQueue childQueue1 = mainQueue.tap(); + EventQueue childQueue2 = mainQueue.tap(); Event event = fromJson(MINIMAL_TASK, Task.class); - parentQueue.enqueueEvent(event); + mainQueue.enqueueEvent(event); - // Event should be available in both parent and child queues - Event parentEvent = parentQueue.dequeueEventItem(-1).getEvent(); - Event childEvent = childQueue.dequeueEventItem(-1).getEvent(); + // Event should be available in all child queues + // Note: MainEventBusProcessor runs async, so we use dequeueEventItem with timeout + Event child1Event = childQueue1.dequeueEventItem(5000).getEvent(); + Event child2Event = childQueue2.dequeueEventItem(5000).getEvent(); - assertSame(event, parentEvent); - assertSame(event, childEvent); + assertSame(event, child1Event); + assertSame(event, child2Event); } @Test public void testMultipleChildQueuesReceiveEvents() throws Exception { - EventQueue parentQueue = EventQueue.builder().build(); - EventQueue childQueue1 = parentQueue.tap(); - EventQueue childQueue2 = parentQueue.tap(); + EventQueue mainQueue = createQueueWithEventBus(TASK_ID); + EventQueue childQueue1 = mainQueue.tap(); + EventQueue childQueue2 = mainQueue.tap(); + EventQueue childQueue3 = mainQueue.tap(); Event event1 = fromJson(MINIMAL_TASK, Task.class); Event event2 = fromJson(MESSAGE_PAYLOAD, Message.class); - parentQueue.enqueueEvent(event1); - parentQueue.enqueueEvent(event2); + mainQueue.enqueueEvent(event1); + mainQueue.enqueueEvent(event2); - // All queues should receive both events - assertSame(event1, parentQueue.dequeueEventItem(-1).getEvent()); - assertSame(event2, parentQueue.dequeueEventItem(-1).getEvent()); + // All child queues should receive both events + // Note: Use timeout for async processing + assertSame(event1, childQueue1.dequeueEventItem(5000).getEvent()); + assertSame(event2, childQueue1.dequeueEventItem(5000).getEvent()); - assertSame(event1, childQueue1.dequeueEventItem(-1).getEvent()); - assertSame(event2, childQueue1.dequeueEventItem(-1).getEvent()); + assertSame(event1, childQueue2.dequeueEventItem(5000).getEvent()); + assertSame(event2, childQueue2.dequeueEventItem(5000).getEvent()); - assertSame(event1, childQueue2.dequeueEventItem(-1).getEvent()); - assertSame(event2, childQueue2.dequeueEventItem(-1).getEvent()); + assertSame(event1, childQueue3.dequeueEventItem(5000).getEvent()); + assertSame(event2, childQueue3.dequeueEventItem(5000).getEvent()); } @Test public void testChildQueueDequeueIndependently() throws Exception { - EventQueue parentQueue = EventQueue.builder().build(); - EventQueue childQueue1 = parentQueue.tap(); - EventQueue childQueue2 = parentQueue.tap(); + EventQueue mainQueue = createQueueWithEventBus(TASK_ID); + EventQueue childQueue1 = mainQueue.tap(); + EventQueue childQueue2 = mainQueue.tap(); + EventQueue childQueue3 = mainQueue.tap(); Event event = fromJson(MINIMAL_TASK, Task.class); - parentQueue.enqueueEvent(event); + mainQueue.enqueueEvent(event); - // Dequeue from child1 first - Event child1Event = childQueue1.dequeueEventItem(-1).getEvent(); + // Dequeue from child1 first (use timeout for async processing) + Event child1Event = childQueue1.dequeueEventItem(5000).getEvent(); assertSame(event, child1Event); // child2 should still have the event available - Event child2Event = childQueue2.dequeueEventItem(-1).getEvent(); + Event child2Event = childQueue2.dequeueEventItem(5000).getEvent(); assertSame(event, child2Event); - // Parent should still have the event available - Event parentEvent = parentQueue.dequeueEventItem(-1).getEvent(); - assertSame(event, parentEvent); + // child3 should still have the event available + Event child3Event = childQueue3.dequeueEventItem(5000).getEvent(); + assertSame(event, child3Event); } @Test public void testCloseImmediatePropagationToChildren() throws Exception { - EventQueue parentQueue = EventQueue.builder().build(); + EventQueue parentQueue = createQueueWithEventBus(TASK_ID); EventQueue childQueue = parentQueue.tap(); // Add events to both parent and child @@ -166,7 +238,7 @@ public void testCloseImmediatePropagationToChildren() throws Exception { assertFalse(childQueue.isClosed()); try { - assertNotNull(childQueue.dequeueEventItem(-1)); // Child has the event + assertNotNull(childQueue.dequeueEventItem(5000)); // Child has the event (use timeout) } catch (EventQueueClosedException e) { // This is fine if queue closed before dequeue } @@ -187,27 +259,37 @@ public void testCloseImmediatePropagationToChildren() throws Exception { @Test public void testEnqueueEventWhenClosed() throws Exception { - EventQueue queue = EventQueue.builder().build(); + EventQueue mainQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .taskId(TASK_ID) + .build(); + EventQueue childQueue = mainQueue.tap(); Event event = fromJson(MINIMAL_TASK, Task.class); - queue.close(); // Close the queue first - assertTrue(queue.isClosed()); + childQueue.close(); // Close the child queue first (removes from children list) + assertTrue(childQueue.isClosed()); + + // Create a new child queue BEFORE enqueuing (ensures it's in children list for distribution) + EventQueue newChildQueue = mainQueue.tap(); // MainQueue accepts events even when closed (for replication support) // This ensures late-arriving replicated events can be enqueued to closed queues - queue.enqueueEvent(event); + // Note: MainEventBusProcessor runs asynchronously, so we use dequeueEventItem with timeout + mainQueue.enqueueEvent(event); - // Event should be available for dequeuing - Event dequeuedEvent = queue.dequeueEventItem(-1).getEvent(); + // New child queue should receive the event (old closed child was removed from children list) + EventQueueItem item = newChildQueue.dequeueEventItem(5000); + assertNotNull(item); + Event dequeuedEvent = item.getEvent(); assertSame(event, dequeuedEvent); - // Now queue is closed and empty, should throw exception - assertThrows(EventQueueClosedException.class, () -> queue.dequeueEventItem(-1)); + // Now new child queue is closed and empty, should throw exception + newChildQueue.close(); + assertThrows(EventQueueClosedException.class, () -> newChildQueue.dequeueEventItem(-1)); } @Test public void testDequeueEventWhenClosedAndEmpty() throws Exception { - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build().tap(); queue.close(); assertTrue(queue.isClosed()); @@ -217,19 +299,27 @@ public void testDequeueEventWhenClosedAndEmpty() throws Exception { @Test public void testDequeueEventWhenClosedButHasEvents() throws Exception { - EventQueue queue = EventQueue.builder().build(); + EventQueue mainQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .taskId(TASK_ID) + .build(); + EventQueue childQueue = mainQueue.tap(); Event event = fromJson(MINIMAL_TASK, Task.class); - queue.enqueueEvent(event); - queue.close(); // Graceful close - events should remain - assertTrue(queue.isClosed()); + // Use callback to wait for event processing instead of polling + waitForEventProcessing(() -> mainQueue.enqueueEvent(event)); - // Should still be able to dequeue existing events - Event dequeuedEvent = queue.dequeueEventItem(-1).getEvent(); + // At this point, event has been processed and distributed to childQueue + childQueue.close(); // Graceful close - events should remain + assertTrue(childQueue.isClosed()); + + // Should still be able to dequeue existing events from closed queue + EventQueueItem item = childQueue.dequeueEventItem(5000); + assertNotNull(item); + Event dequeuedEvent = item.getEvent(); assertSame(event, dequeuedEvent); // Now queue is closed and empty, should throw exception - assertThrows(EventQueueClosedException.class, () -> queue.dequeueEventItem(-1)); + assertThrows(EventQueueClosedException.class, () -> childQueue.dequeueEventItem(-1)); } @Test @@ -244,7 +334,9 @@ public void testEnqueueAndDequeueEvent() throws Exception { public void testDequeueEventNoWait() throws Exception { Event event = fromJson(MINIMAL_TASK, Task.class); eventQueue.enqueueEvent(event); - Event dequeuedEvent = eventQueue.dequeueEventItem(-1).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event dequeuedEvent = item.getEvent(); assertSame(event, dequeuedEvent); } @@ -257,7 +349,7 @@ public void testDequeueEventEmptyQueueNoWait() throws Exception { @Test public void testDequeueEventWait() throws Exception { Event event = TaskStatusUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) .isFinal(true) @@ -271,7 +363,7 @@ public void testDequeueEventWait() throws Exception { @Test public void testTaskDone() throws Exception { Event event = TaskArtifactUpdateEvent.builder() - .taskId("task-123") + .taskId(TASK_ID) .contextId("session-xyz") .artifact(Artifact.builder() .artifactId("11") @@ -347,7 +439,7 @@ public void testCloseIdempotent() throws Exception { assertTrue(eventQueue.isClosed()); // Test with immediate close as well - EventQueue eventQueue2 = EventQueue.builder().build(); + EventQueue eventQueue2 = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); eventQueue2.close(true); assertTrue(eventQueue2.isClosed()); @@ -361,19 +453,20 @@ public void testCloseIdempotent() throws Exception { */ @Test public void testCloseChildQueues() throws Exception { - EventQueue childQueue = eventQueue.tap(); + EventQueue mainQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); + EventQueue childQueue = mainQueue.tap(); assertTrue(childQueue != null); // Graceful close - parent closes but children remain open - eventQueue.close(); - assertTrue(eventQueue.isClosed()); + mainQueue.close(); + assertTrue(mainQueue.isClosed()); assertFalse(childQueue.isClosed()); // Child NOT closed on graceful parent close // Immediate close - parent force-closes all children - EventQueue parentQueue2 = EventQueue.builder().build(); - EventQueue childQueue2 = parentQueue2.tap(); - parentQueue2.close(true); // immediate=true - assertTrue(parentQueue2.isClosed()); + EventQueue mainQueue2 = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); + EventQueue childQueue2 = mainQueue2.tap(); + mainQueue2.close(true); // immediate=true + assertTrue(mainQueue2.isClosed()); assertTrue(childQueue2.isClosed()); // Child IS closed on immediate parent close } @@ -383,7 +476,7 @@ public void testCloseChildQueues() throws Exception { */ @Test public void testMainQueueReferenceCountingStaysOpenWithActiveChildren() throws Exception { - EventQueue mainQueue = EventQueue.builder().build(); + EventQueue mainQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); EventQueue child1 = mainQueue.tap(); EventQueue child2 = mainQueue.tap(); diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java b/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java index 39201c1f6..6c9ed4a17 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueUtil.java @@ -1,8 +1,39 @@ package io.a2a.server.events; +import java.util.concurrent.atomic.AtomicInteger; + public class EventQueueUtil { - // Since EventQueue.builder() is package protected, add a method to expose it - public static EventQueue.EventQueueBuilder getEventQueueBuilder() { - return EventQueue.builder(); + // Counter for generating unique test taskIds + private static final AtomicInteger TASK_ID_COUNTER = new AtomicInteger(0); + + /** + * Get an EventQueue builder pre-configured with the shared test MainEventBus and a unique taskId. + *

    + * Note: Returns MainQueue - tests should call .tap() if they need to consume events. + *

    + * + * @return builder with TEST_EVENT_BUS and unique taskId already set + */ + public static EventQueue.EventQueueBuilder getEventQueueBuilder(MainEventBus eventBus) { + return EventQueue.builder(eventBus) + .taskId("test-task-" + TASK_ID_COUNTER.incrementAndGet()); + } + + /** + * Start a MainEventBusProcessor instance. + * + * @param processor the processor to start + */ + public static void start(MainEventBusProcessor processor) { + processor.start(); + } + + /** + * Stop a MainEventBusProcessor instance. + * + * @param processor the processor to stop + */ + public static void stop(MainEventBusProcessor processor) { + processor.stop(); } } diff --git a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java index 1eca1b739..3e09ff2af 100644 --- a/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/InMemoryQueueManagerTest.java @@ -14,7 +14,10 @@ import java.util.concurrent.ExecutionException; import java.util.stream.IntStream; +import io.a2a.server.tasks.InMemoryTaskStore; import io.a2a.server.tasks.MockTaskStateProvider; +import io.a2a.server.tasks.PushNotificationSender; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,17 +25,30 @@ public class InMemoryQueueManagerTest { private InMemoryQueueManager queueManager; private MockTaskStateProvider taskStateProvider; + private InMemoryTaskStore taskStore; + private MainEventBus mainEventBus; + private MainEventBusProcessor mainEventBusProcessor; + private static final PushNotificationSender NOOP_PUSHNOTIFICATION_SENDER = task -> {}; @BeforeEach public void setUp() { taskStateProvider = new MockTaskStateProvider(); - queueManager = new InMemoryQueueManager(taskStateProvider); + taskStore = new InMemoryTaskStore(); + mainEventBus = new MainEventBus(); + queueManager = new InMemoryQueueManager(taskStateProvider, mainEventBus); + mainEventBusProcessor = new MainEventBusProcessor(mainEventBus, taskStore, NOOP_PUSHNOTIFICATION_SENDER, queueManager); + EventQueueUtil.start(mainEventBusProcessor); + } + + @AfterEach + public void tearDown() { + EventQueueUtil.stop(mainEventBusProcessor); } @Test public void testAddNewQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, queue); @@ -43,8 +59,8 @@ public void testAddNewQueue() { @Test public void testAddExistingQueueThrowsException() { String taskId = "test_task_id"; - EventQueue queue1 = EventQueue.builder().build(); - EventQueue queue2 = EventQueue.builder().build(); + EventQueue queue1 = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); + EventQueue queue2 = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, queue1); @@ -56,7 +72,7 @@ public void testAddExistingQueueThrowsException() { @Test public void testGetExistingQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, queue); EventQueue result = queueManager.get(taskId); @@ -73,7 +89,7 @@ public void testGetNonexistentQueue() { @Test public void testTapExistingQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, queue); EventQueue tappedQueue = queueManager.tap(taskId); @@ -94,7 +110,7 @@ public void testTapNonexistentQueue() { @Test public void testCloseExistingQueue() { String taskId = "test_task_id"; - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, queue); queueManager.close(taskId); @@ -129,7 +145,7 @@ public void testCreateOrTapNewQueue() { @Test public void testCreateOrTapExistingQueue() { String taskId = "test_task_id"; - EventQueue originalQueue = EventQueue.builder().build(); + EventQueue originalQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, originalQueue); EventQueue result = queueManager.createOrTap(taskId); @@ -151,7 +167,7 @@ public void testConcurrentOperations() throws InterruptedException, ExecutionExc // Add tasks concurrently List> addFutures = taskIds.stream() .map(taskId -> CompletableFuture.supplyAsync(() -> { - EventQueue queue = EventQueue.builder().build(); + EventQueue queue = EventQueueUtil.getEventQueueBuilder(mainEventBus).build(); queueManager.add(taskId, queue); return taskId; })) diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index ea5bbe797..9c64f03f9 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -26,7 +26,10 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueItem; +import io.a2a.server.events.EventQueueUtil; import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.events.MainEventBus; +import io.a2a.server.events.MainEventBusProcessor; import io.a2a.server.tasks.BasePushNotificationSender; import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; import io.a2a.server.tasks.InMemoryTaskStore; @@ -66,6 +69,8 @@ public class AbstractA2ARequestHandlerTest { private static final String PREFERRED_TRANSPORT = "preferred-transport"; private static final String A2A_REQUESTHANDLER_TEST_PROPERTIES = "/a2a-requesthandler-test.properties"; + private static final PushNotificationSender NOOP_PUSHNOTIFICATION_SENDER = task -> {}; + protected AgentExecutor executor; protected TaskStore taskStore; protected RequestHandler requestHandler; @@ -73,6 +78,8 @@ public class AbstractA2ARequestHandlerTest { protected AgentExecutorMethod agentExecutorCancel; protected InMemoryQueueManager queueManager; protected TestHttpClient httpClient; + protected MainEventBus mainEventBus; + protected MainEventBusProcessor mainEventBusProcessor; protected final Executor internalExecutor = Executors.newCachedThreadPool(); @@ -96,19 +103,31 @@ public void cancel(RequestContext context, EventQueue eventQueue) throws A2AErro InMemoryTaskStore inMemoryTaskStore = new InMemoryTaskStore(); taskStore = inMemoryTaskStore; - queueManager = new InMemoryQueueManager(inMemoryTaskStore); + + // Create push notification components BEFORE MainEventBusProcessor httpClient = new TestHttpClient(); PushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); PushNotificationSender pushSender = new BasePushNotificationSender(pushConfigStore, httpClient); + // Create MainEventBus and MainEventBusProcessor (production code path) + mainEventBus = new MainEventBus(); + queueManager = new InMemoryQueueManager(inMemoryTaskStore, mainEventBus); + mainEventBusProcessor = new MainEventBusProcessor(mainEventBus, taskStore, pushSender, queueManager); + EventQueueUtil.start(mainEventBusProcessor); + requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, pushConfigStore, pushSender, internalExecutor); + executor, taskStore, queueManager, pushConfigStore, mainEventBusProcessor, internalExecutor, internalExecutor); } @AfterEach public void cleanup() { agentExecutorExecute = null; agentExecutorCancel = null; + + // Stop MainEventBusProcessor background thread + if (mainEventBusProcessor != null) { + EventQueueUtil.stop(mainEventBusProcessor); + } } protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifications, boolean stateTransitionHistory) { diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java index 293babe4e..e69de29bb 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/DefaultRequestHandlerTest.java @@ -1,1001 +0,0 @@ -package io.a2a.server.requesthandlers; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import io.a2a.server.ServerCallContext; -import io.a2a.server.agentexecution.AgentExecutor; -import io.a2a.server.agentexecution.RequestContext; -import io.a2a.server.auth.UnauthenticatedUser; -import io.a2a.server.events.EventQueue; -import io.a2a.server.events.InMemoryQueueManager; -import io.a2a.server.tasks.InMemoryPushNotificationConfigStore; -import io.a2a.server.tasks.InMemoryTaskStore; -import io.a2a.server.tasks.TaskUpdater; -import io.a2a.spec.A2AError; -import io.a2a.spec.ListTaskPushNotificationConfigParams; -import io.a2a.spec.ListTaskPushNotificationConfigResult; -import io.a2a.spec.Message; -import io.a2a.spec.MessageSendConfiguration; -import io.a2a.spec.MessageSendParams; -import io.a2a.spec.PushNotificationConfig; -import io.a2a.spec.Task; -import io.a2a.spec.TaskState; -import io.a2a.spec.TaskStatus; -import io.a2a.spec.TextPart; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -/** - * Comprehensive tests for DefaultRequestHandler, backported from Python's - * test_default_request_handler.py. These tests cover core functionality that - * is transport-agnostic and should work across JSON-RPC, gRPC, and REST. - * - * Background cleanup and task tracking tests are from Python PR #440 and #472. - */ -public class DefaultRequestHandlerTest { - - private DefaultRequestHandler requestHandler; - private InMemoryTaskStore taskStore; - private InMemoryQueueManager queueManager; - private TestAgentExecutor agentExecutor; - private ServerCallContext serverCallContext; - - @BeforeEach - void setUp() { - taskStore = new InMemoryTaskStore(); - // Pass taskStore as TaskStateProvider to queueManager for task-aware queue management - queueManager = new InMemoryQueueManager(taskStore); - agentExecutor = new TestAgentExecutor(); - - requestHandler = DefaultRequestHandler.create( - agentExecutor, - taskStore, - queueManager, - null, // pushConfigStore - null, // pushSender - Executors.newCachedThreadPool() - ); - - serverCallContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of(), Set.of()); - } - - /** - * Test that multiple blocking messages to the same task work correctly - * when agent doesn't emit final events (fire-and-forget pattern). - * This replicates TCK test: test_message_send_continue_task - */ - @Test - @Timeout(10) - void testBlockingMessageContinueTask() throws Exception { - String taskId = "continue-task-1"; - String contextId = "continue-ctx-1"; - - // Configure agent to NOT complete tasks (like TCK fire-and-forget agent) - agentExecutor.setExecuteCallback((context, queue) -> { - Task task = context.getTask(); - if (task == null) { - // First message: create SUBMITTED task - task = Task.builder() - .id(context.getTaskId()) - .contextId(context.getContextId()) - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - } else { - // Subsequent messages: emit WORKING task (non-final) - task = Task.builder() - .id(context.getTaskId()) - .contextId(context.getContextId()) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - } - queue.enqueueEvent(task); - // Don't complete - just return (fire-and-forget) - }); - - // First blocking message - should return SUBMITTED task - Message message1 = Message.builder() - .messageId("msg-1") - .role(Message.Role.USER) - .parts(new TextPart("first message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params1 = new MessageSendParams(message1, null, null, ""); - Object result1 = requestHandler.onMessageSend(params1, serverCallContext); - - assertTrue(result1 instanceof Task); - Task task1 = (Task) result1; - assertTrue(task1.id().equals(taskId)); - assertTrue(task1.status().state() == TaskState.SUBMITTED); - - // Second blocking message to SAME taskId - should not hang - Message message2 = Message.builder() - .messageId("msg-2") - .role(Message.Role.USER) - .parts(new TextPart("second message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params2 = new MessageSendParams(message2, null, null, ""); - Object result2 = requestHandler.onMessageSend(params2, serverCallContext); - - // Should complete successfully (not timeout) - assertTrue(result2 instanceof Task); - } - - /** - * Test that background cleanup tasks are properly tracked and cleared. - * Backported from Python test: test_background_cleanup_task_is_tracked_and_cleared - */ - @Test - @Timeout(10) - void testBackgroundCleanupTaskIsTrackedAndCleared() throws Exception { - String taskId = "track-task-1"; - String contextId = "track-ctx-1"; - - // Create a task that will trigger background cleanup - Task task = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.SUBMITTED)) - .build(); - - taskStore.save(task); - - Message message = Message.builder() - .messageId("msg-track") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params = new MessageSendParams(message, null, null, ""); - - // Set up agent to finish quickly so cleanup runs - CountDownLatch agentStarted = new CountDownLatch(1); - CountDownLatch allowAgentFinish = new CountDownLatch(1); - - agentExecutor.setExecuteCallback((context, queue) -> { - agentStarted.countDown(); - try { - allowAgentFinish.await(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - }); - - // Start streaming (this will create background tasks) - var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); - - // Wait for agent to start - assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); - - // Allow agent to finish, which should trigger cleanup - allowAgentFinish.countDown(); - - // Give some time for background tasks to be tracked and cleaned up - Thread.sleep(1000); - - // Background tasks should eventually be cleared - // Note: We can't directly access the backgroundTasks field without reflection, - // but the test verifies the mechanism doesn't hang or leak tasks - assertTrue(true, "Background cleanup completed without hanging"); - } - - /** - * Test that client disconnect triggers background cleanup and producer continues. - * Backported from Python test: test_on_message_send_stream_client_disconnect_triggers_background_cleanup_and_producer_continues - */ - @Test - @Timeout(10) - void testStreamingClientDisconnectTriggersBackgroundCleanup() throws Exception { - String taskId = "disc-task-1"; - String contextId = "disc-ctx-1"; - - Message message = Message.builder() - .messageId("mid") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params = new MessageSendParams(message, null, null, ""); - - // Agent should start and then wait - CountDownLatch agentStarted = new CountDownLatch(1); - CountDownLatch allowAgentFinish = new CountDownLatch(1); - AtomicBoolean agentCompleted = new AtomicBoolean(false); - - agentExecutor.setExecuteCallback((context, queue) -> { - agentStarted.countDown(); - try { - allowAgentFinish.await(10, TimeUnit.SECONDS); - agentCompleted.set(true); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - }); - - // Start streaming - var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); - - // Wait for agent to start - assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start executing"); - - // Simulate client disconnect by not consuming the stream - // In real scenarios, the client would close the connection - - // Agent should still be running (not finished immediately on "disconnect") - Thread.sleep(500); - assertTrue(agentExecutor.isExecuting(), "Producer should still be running after simulated disconnect"); - - // Allow agent to finish - allowAgentFinish.countDown(); - - // Wait a bit for completion - Thread.sleep(1000); - - assertTrue(agentCompleted.get(), "Agent should have completed execution"); - } - - /** - * Test that resubscription works after client disconnect. - * Backported from Python test: test_stream_disconnect_then_resubscribe_receives_future_events - */ - @Test - @Timeout(15) - void testStreamDisconnectThenResubscribeReceivesFutureEvents() throws Exception { - String taskId = "reconn-task-1"; - String contextId = "reconn-ctx-1"; - - // Create initial task - Task initialTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - - taskStore.save(initialTask); - - Message message = Message.builder() - .messageId("msg-reconn") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params = new MessageSendParams(message, null, null, ""); - - // Set up agent to emit events with controlled timing - CountDownLatch agentStarted = new CountDownLatch(1); - CountDownLatch allowSecondEvent = new CountDownLatch(1); - CountDownLatch allowFinish = new CountDownLatch(1); - - agentExecutor.setExecuteCallback((context, queue) -> { - agentStarted.countDown(); - - // Emit first event - Task firstEvent = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - queue.enqueueEvent(firstEvent); - - // Wait for permission to emit second event - try { - allowSecondEvent.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - - // Emit second event - Task secondEvent = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.COMPLETED)) - .build(); - queue.enqueueEvent(secondEvent); - - try { - allowFinish.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - }); - - // Start streaming and simulate getting first event then disconnecting - var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); - - // Wait for agent to start and emit first event - assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); - - // Simulate client disconnect (in real scenario, client would close connection) - // The background cleanup should keep the producer running - - // Now try to resubscribe to the task - io.a2a.spec.TaskIdParams resubParams = new io.a2a.spec.TaskIdParams(taskId); - - // Allow agent to emit second event - allowSecondEvent.countDown(); - - // Try resubscription - this should work because queue is still alive - var resubResult = requestHandler.onResubscribeToTask(resubParams, serverCallContext); - // If we get here without exception, resubscription worked - assertTrue(true, "Resubscription succeeded"); - - // Clean up - allowFinish.countDown(); - } - - /** - * Test that task state is persisted to task store after client disconnect. - * Backported from Python test: test_disconnect_persists_final_task_to_store - */ - @Test - @Timeout(15) - void testDisconnectPersistsFinalTaskToStore() throws Exception { - String taskId = "persist-task-1"; - String contextId = "persist-ctx-1"; - - Message message = Message.builder() - .messageId("msg-persist") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params = new MessageSendParams(message, null, null, ""); - - // Agent that completes after some delay - CountDownLatch agentStarted = new CountDownLatch(1); - CountDownLatch allowCompletion = new CountDownLatch(1); - - agentExecutor.setExecuteCallback((context, queue) -> { - agentStarted.countDown(); - - // Emit working status - Task workingTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - queue.enqueueEvent(workingTask); - - try { - allowCompletion.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - - // Emit final completed status - Task completedTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.COMPLETED)) - .build(); - queue.enqueueEvent(completedTask); - }); - - // Start streaming and simulate client disconnect - var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); - - // Wait for agent to start - assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); - - // Simulate client disconnect by not consuming the stream further - // In real scenarios, the reactive stream would be cancelled - - // Allow agent to complete in background - allowCompletion.countDown(); - - // Give time for background processing to persist the final state - Thread.sleep(2000); - - // Verify the final task state was persisted despite client disconnect - Task persistedTask = taskStore.get(taskId); - if (persistedTask != null) { - // If task was persisted, it should have the final state - assertTrue( - persistedTask.status().state() == TaskState.COMPLETED || - persistedTask.status().state() == TaskState.WORKING, - "Task should be persisted with working or completed state, got: " + persistedTask.status().state() - ); - } - // Note: In some architectures, the task might not be persisted if the - // background consumption isn't implemented. This test documents the expected behavior. - } - - /** - * Test that blocking message call waits for agent to finish and returns complete Task - * even when agent does fire-and-forget (emits non-final state and returns). - * - * Expected behavior: - * 1. Agent emits WORKING state with artifacts - * 2. Agent's execute() method returns WITHOUT emitting final state - * 3. Blocking onMessageSend() should wait for agent execution to complete - * 4. Blocking onMessageSend() should wait for all queued events to be processed - * 5. Returned Task should have WORKING state with all artifacts included - * - * This tests fire-and-forget pattern with blocking calls. - */ - @Test - @Timeout(15) - void testBlockingFireAndForgetReturnsNonFinalTask() throws Exception { - String taskId = "blocking-fire-forget-task"; - String contextId = "blocking-fire-forget-ctx"; - - Message message = Message.builder() - .messageId("msg-blocking-fire-forget") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendConfiguration config = MessageSendConfiguration.builder() - .blocking(true) - .build(); - - MessageSendParams params = new MessageSendParams(message, config, null, ""); - - // Agent that does fire-and-forget: emits WORKING with artifact but never completes - agentExecutor.setExecuteCallback((context, queue) -> { - TaskUpdater updater = new TaskUpdater(context, queue); - - // Start work (WORKING state) - updater.startWork(); - - // Add artifact - updater.addArtifact( - List.of(new TextPart("Fire and forget artifact")), - "artifact-1", "FireForget", null); - - // Agent returns WITHOUT calling updater.complete() - // Task stays in WORKING state (non-final) - }); - - // Call blocking onMessageSend - should wait for agent to finish - Object result = requestHandler.onMessageSend(params, serverCallContext); - - // The returned result should be a Task in WORKING state with artifact - assertTrue(result instanceof Task, "Result should be a Task"); - Task returnedTask = (Task) result; - - // Verify task is in WORKING state (non-final, fire-and-forget) - assertEquals(TaskState.WORKING, returnedTask.status().state(), - "Returned task should be WORKING (fire-and-forget), got: " + returnedTask.status().state()); - - // Verify artifacts are included in the returned task - assertNotNull(returnedTask.artifacts(), - "Returned task should have artifacts"); - assertTrue(returnedTask.artifacts().size() >= 1, - "Returned task should have at least 1 artifact, got: " + - returnedTask.artifacts().size()); - } - - /** - * Test that non-blocking message call returns immediately and persists all events in background. - * - * Expected behavior: - * 1. Non-blocking call returns immediately with first event (WORKING state) - * 2. Agent continues running in background and produces more events - * 3. Background consumption continues and persists all events to TaskStore - * 4. Final task state (COMPLETED) is persisted in background - */ - @Test - @Timeout(15) - void testNonBlockingMessagePersistsAllEventsInBackground() throws Exception { - String taskId = "blocking-persist-task"; - String contextId = "blocking-persist-ctx"; - - Message message = Message.builder() - .messageId("msg-nonblocking-persist") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - // Use default non-blocking behavior - MessageSendConfiguration config = MessageSendConfiguration.builder() - .build(); - - MessageSendParams params = new MessageSendParams(message, config, null, ""); - - // Agent that produces multiple events with delays - CountDownLatch agentStarted = new CountDownLatch(1); - CountDownLatch firstEventEmitted = new CountDownLatch(1); - CountDownLatch allowCompletion = new CountDownLatch(1); - - agentExecutor.setExecuteCallback((context, queue) -> { - agentStarted.countDown(); - - // Emit first event (WORKING state) - Task workingTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - queue.enqueueEvent(workingTask); - firstEventEmitted.countDown(); - - // Sleep to ensure the non-blocking call has returned before we emit more events - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - - // Wait for permission to complete - try { - allowCompletion.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - - // Emit final event (COMPLETED state) - // This event should be persisted to TaskStore in background - Task completedTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.COMPLETED)) - .build(); - queue.enqueueEvent(completedTask); - }); - - // Call non-blocking onMessageSend - Object result = requestHandler.onMessageSend(params, serverCallContext); - - // Assertion 1: The immediate result should be the first event (WORKING) - assertTrue(result instanceof Task, "Result should be a Task"); - Task immediateTask = (Task) result; - assertEquals(TaskState.WORKING, immediateTask.status().state(), - "Non-blocking should return immediately with WORKING state, got: " + immediateTask.status().state()); - - // At this point, the non-blocking call has returned, but the agent is still running - - // Allow the agent to emit the final COMPLETED event - allowCompletion.countDown(); - - // Assertion 2: Poll for the final task state to be persisted in background - // Use polling loop instead of fixed sleep for faster and more reliable test - long timeoutMs = 5000; - long startTime = System.currentTimeMillis(); - Task persistedTask = null; - boolean completedStateFound = false; - - while (System.currentTimeMillis() - startTime < timeoutMs) { - persistedTask = taskStore.get(taskId); - if (persistedTask != null && persistedTask.status().state() == TaskState.COMPLETED) { - completedStateFound = true; - break; - } - Thread.sleep(100); // Poll every 100ms - } - - assertTrue(persistedTask != null, "Task should be persisted to store"); - assertTrue( - completedStateFound, - "Final task state should be COMPLETED (background consumption should have processed it), got: " + - (persistedTask != null ? persistedTask.status().state() : "null") + - " after " + (System.currentTimeMillis() - startTime) + "ms" - ); - } - - /** - * Test the BIG idea: MainQueue stays open for non-final tasks even when all children close. - * This enables fire-and-forget tasks and late resubscriptions. - */ - @Test - @Timeout(15) - void testMainQueueStaysOpenForNonFinalTasks() throws Exception { - String taskId = "fire-and-forget-task"; - String contextId = "fire-ctx"; - - // Create initial task in WORKING state (non-final) - Task initialTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - taskStore.save(initialTask); - - Message message = Message.builder() - .messageId("msg-fire") - .role(Message.Role.USER) - .parts(new TextPart("fire and forget")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendParams params = new MessageSendParams(message, null, null, ""); - - // Agent that emits WORKING status but never completes (fire-and-forget pattern) - CountDownLatch agentStarted = new CountDownLatch(1); - CountDownLatch allowAgentFinish = new CountDownLatch(1); - - agentExecutor.setExecuteCallback((context, queue) -> { - agentStarted.countDown(); - - // Emit WORKING status (non-final) - Task workingTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - queue.enqueueEvent(workingTask); - - // Don't emit final state - just wait and finish - try { - allowAgentFinish.await(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - // Agent finishes WITHOUT emitting final task state - }); - - // Start streaming - var streamingResult = requestHandler.onMessageSendStream(params, serverCallContext); - - // Wait for agent to start and emit WORKING event - assertTrue(agentStarted.await(5, TimeUnit.SECONDS), "Agent should start"); - - // Give time for WORKING event to be processed - Thread.sleep(500); - - // Simulate client disconnect - this closes the ChildQueue - // but MainQueue should stay open because task is non-final - - // Allow agent to finish - allowAgentFinish.countDown(); - - // Give time for agent to finish and cleanup to run - Thread.sleep(2000); - - // THE BIG IDEA TEST: Resubscription should work because MainQueue is still open - // Even though: - // 1. The original ChildQueue closed (client disconnected) - // 2. The agent finished executing - // 3. Task is still in non-final WORKING state - // Therefore: MainQueue should still be open for resubscriptions - - io.a2a.spec.TaskIdParams resubParams = new io.a2a.spec.TaskIdParams(taskId); - var resubResult = requestHandler.onResubscribeToTask(resubParams, serverCallContext); - - // If we get here without exception, the BIG idea works! - assertTrue(true, "Resubscription succeeded - MainQueue stayed open for non-final task"); - } - - /** - * Test that MainQueue DOES close when task is finalized. - * This ensures Level 2 protection doesn't prevent cleanup of completed tasks. - */ - @Test - @Timeout(15) - void testMainQueueClosesForFinalizedTasks() throws Exception { - String taskId = "completed-task"; - String contextId = "completed-ctx"; - - // Create initial task in COMPLETED state (already finalized) - Task completedTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.COMPLETED)) - .build(); - taskStore.save(completedTask); - - // Create a queue for this task - EventQueue mainQueue = queueManager.createOrTap(taskId); - assertTrue(mainQueue != null, "Queue should be created"); - - // Close the child queue (simulating client disconnect) - mainQueue.close(); - - // Give time for cleanup callback to run - Thread.sleep(1000); - - // Since the task is finalized (COMPLETED), the MainQueue should be removed from the map - // This tests that Level 2 protection (childClosing check) allows cleanup for finalized tasks - EventQueue queue = queueManager.get(taskId); - assertTrue(queue == null || queue.isClosed(), - "Queue for finalized task should be null or closed"); - } - - /** - * Test that blocking message call returns a Task with ALL artifacts included. - * This reproduces the reported bug: blocking call returns before artifacts are processed. - * - * Expected behavior: - * 1. Agent emits multiple artifacts via TaskUpdater - * 2. Blocking onMessageSend() should wait for ALL events to be processed - * 3. Returned Task should have all artifacts included in COMPLETED state - * - * Bug manifestation: - * - onMessageSend() returns after first event - * - Artifacts are still being processed in background - * - Returned Task is incomplete - */ - @Test - @Timeout(15) - void testBlockingCallReturnsCompleteTaskWithArtifacts() throws Exception { - String taskId = "blocking-artifacts-task"; - String contextId = "blocking-artifacts-ctx"; - - Message message = Message.builder() - .messageId("msg-blocking-artifacts") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendConfiguration config = MessageSendConfiguration.builder() - .blocking(true) - .build(); - - MessageSendParams params = new MessageSendParams(message, config, null, ""); - - // Agent that uses TaskUpdater to emit multiple artifacts (like real agents do) - agentExecutor.setExecuteCallback((context, queue) -> { - TaskUpdater updater = new TaskUpdater(context, queue); - - // Start work (WORKING state) - updater.startWork(); - - // Add first artifact - updater.addArtifact( - List.of(new TextPart("First artifact")), - "artifact-1", "First", null); - - // Add second artifact - updater.addArtifact( - List.of(new TextPart("Second artifact")), - "artifact-2", "Second", null); - - // Complete the task - updater.complete(); - }); - - // Call blocking onMessageSend - should wait for ALL events - Object result = requestHandler.onMessageSend(params, serverCallContext); - - // The returned result should be a Task with ALL artifacts - assertTrue(result instanceof Task, "Result should be a Task"); - Task returnedTask = (Task) result; - - // Verify task is completed - assertEquals(TaskState.COMPLETED, returnedTask.status().state(), - "Returned task should be COMPLETED"); - - // Verify artifacts are included in the returned task - assertNotNull(returnedTask.artifacts(), - "Returned task should have artifacts"); - assertTrue(returnedTask.artifacts().size() >= 2, - "Returned task should have at least 2 artifacts, got: " + - returnedTask.artifacts().size()); - } - - /** - * Test that pushNotificationConfig from SendMessageConfiguration is stored for NEW tasks - * in non-streaming (blocking) mode. This reproduces the bug from issue #84. - * - * Expected behavior: - * 1. Client sends message with pushNotificationConfig in SendMessageConfiguration - * 2. Agent creates a new task - * 3. pushNotificationConfig should be stored in PushNotificationConfigStore - * 4. Config should be retrievable via getInfo() - */ - @Test - @Timeout(10) - void testBlockingMessageStoresPushNotificationConfigForNewTask() throws Exception { - String taskId = "push-config-blocking-new-task"; - String contextId = "push-config-ctx"; - - // Create test config store - InMemoryPushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); - - // Re-create request handler with pushConfigStore - requestHandler = DefaultRequestHandler.create( - agentExecutor, - taskStore, - queueManager, - pushConfigStore, // Add push config store - null, // pushSender - Executors.newCachedThreadPool() - ); - - // Create push notification config - PushNotificationConfig pushConfig = PushNotificationConfig.builder() - .id("config-1") - .url("https://example.com/webhook") - .token("test-token-123") - .build(); - - // Create message with pushNotificationConfig - Message message = Message.builder() - .messageId("msg-push-config") - .role(Message.Role.USER) - .parts(new TextPart("test message")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendConfiguration config = MessageSendConfiguration.builder() - .blocking(true) - .pushNotificationConfig(pushConfig) - .build(); - - MessageSendParams params = new MessageSendParams(message, config, null, ""); - - // Agent creates a new task - agentExecutor.setExecuteCallback((context, queue) -> { - TaskUpdater updater = new TaskUpdater(context, queue); - updater.submit(); // Creates new task in SUBMITTED state - updater.complete(); - }); - - // Call blocking onMessageSend - Object result = requestHandler.onMessageSend(params, serverCallContext); - - // Verify result is a task - assertTrue(result instanceof Task, "Result should be a Task"); - Task returnedTask = (Task) result; - assertEquals(taskId, returnedTask.id()); - - // THE KEY ASSERTION: Verify pushNotificationConfig was stored - ListTaskPushNotificationConfigResult storedConfigs = pushConfigStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); - assertNotNull(storedConfigs, "Push notification config should be stored for new task"); - assertEquals(1, storedConfigs.size(), - "Should have exactly 1 push config stored"); - PushNotificationConfig storedConfig = storedConfigs.configs().get(0).pushNotificationConfig(); - assertEquals("config-1", storedConfig.id()); - assertEquals("https://example.com/webhook", storedConfig.url()); - } - - /** - * Test that pushNotificationConfig is stored for EXISTING tasks. - * This verifies the initMessageSend logic works correctly. - */ - @Test - @Timeout(10) - void testBlockingMessageStoresPushNotificationConfigForExistingTask() throws Exception { - String taskId = "push-config-existing-task"; - String contextId = "push-config-existing-ctx"; - - // Create test config store - InMemoryPushNotificationConfigStore pushConfigStore = new InMemoryPushNotificationConfigStore(); - - // Re-create request handler with pushConfigStore - requestHandler = DefaultRequestHandler.create( - agentExecutor, - taskStore, - queueManager, - pushConfigStore, // Add push config store - null, // pushSender - Executors.newCachedThreadPool() - ); - - // Create EXISTING task in store - Task existingTask = Task.builder() - .id(taskId) - .contextId(contextId) - .status(new TaskStatus(TaskState.WORKING)) - .build(); - taskStore.save(existingTask); - - // Create push notification config - PushNotificationConfig pushConfig = PushNotificationConfig.builder() - .id("config-existing-1") - .url("https://example.com/existing-webhook") - .token("existing-token-789") - .build(); - - Message message = Message.builder() - .messageId("msg-push-existing") - .role(Message.Role.USER) - .parts(new TextPart("update existing task")) - .taskId(taskId) - .contextId(contextId) - .build(); - - MessageSendConfiguration config = MessageSendConfiguration.builder() - .blocking(true) - .pushNotificationConfig(pushConfig) - .build(); - - MessageSendParams params = new MessageSendParams(message, config, null, ""); - - // Agent updates the existing task - agentExecutor.setExecuteCallback((context, queue) -> { - TaskUpdater updater = new TaskUpdater(context, queue); - updater.addArtifact( - List.of(new TextPart("update artifact")), - "artifact-1", "Update", null); - updater.complete(); - }); - - // Call blocking onMessageSend - Object result = requestHandler.onMessageSend(params, serverCallContext); - - // Verify result - assertTrue(result instanceof Task, "Result should be a Task"); - - // Verify pushNotificationConfig was stored (initMessageSend path) - ListTaskPushNotificationConfigResult storedConfigs = pushConfigStore.getInfo(new ListTaskPushNotificationConfigParams(taskId)); - assertNotNull(storedConfigs,"Push notification config should be stored for existing task"); - assertEquals(1, storedConfigs.size(),"Should have exactly 1 push config stored"); - PushNotificationConfig storedConfig = storedConfigs.configs().get(0).pushNotificationConfig(); - assertEquals("config-existing-1", storedConfig.id()); - assertEquals("https://example.com/existing-webhook", storedConfig.url()); - } - - /** - * Simple test agent executor that allows controlling execution timing - */ - private static class TestAgentExecutor implements AgentExecutor { - private ExecuteCallback executeCallback; - private volatile boolean executing = false; - - interface ExecuteCallback { - void call(RequestContext context, EventQueue queue) throws A2AError; - } - - void setExecuteCallback(ExecuteCallback callback) { - this.executeCallback = callback; - } - - boolean isExecuting() { - return executing; - } - - @Override - public void execute(RequestContext context, EventQueue eventQueue) throws A2AError { - executing = true; - try { - if (executeCallback != null) { - // Custom callback is responsible for emitting events - executeCallback.call(context, eventQueue); - } else { - // No custom callback - emit default completion event - Task completedTask = Task.builder() - .id(context.getTaskId()) - .contextId(context.getContextId()) - .status(new TaskStatus(TaskState.COMPLETED)) - .build(); - eventQueue.enqueueEvent(completedTask); - } - - } finally { - executing = false; - } - } - - @Override - public void cancel(RequestContext context, EventQueue eventQueue) throws A2AError { - // Simple cancel implementation - executing = false; - } - } -} \ No newline at end of file diff --git a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java index e814c1c15..e69de29bb 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/InMemoryTaskStoreTest.java @@ -1,49 +0,0 @@ -package io.a2a.server.tasks; - -import static io.a2a.jsonrpc.common.json.JsonUtil.fromJson; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -import io.a2a.spec.Task; -import org.junit.jupiter.api.Test; - -public class InMemoryTaskStoreTest { - private static final String TASK_JSON = """ - { - "id": "task-abc", - "contextId" : "session-xyz", - "status": {"state": "submitted"} - }"""; - - @Test - public void testSaveAndGet() throws Exception { - InMemoryTaskStore store = new InMemoryTaskStore(); - Task task = fromJson(TASK_JSON, Task.class); - store.save(task); - Task retrieved = store.get(task.id()); - assertSame(task, retrieved); - } - - @Test - public void testGetNonExistent() throws Exception { - InMemoryTaskStore store = new InMemoryTaskStore(); - Task retrieved = store.get("nonexistent"); - assertNull(retrieved); - } - - @Test - public void testDelete() throws Exception { - InMemoryTaskStore store = new InMemoryTaskStore(); - Task task = fromJson(TASK_JSON, Task.class); - store.save(task); - store.delete(task.id()); - Task retrieved = store.get(task.id()); - assertNull(retrieved); - } - - @Test - public void testDeleteNonExistent() throws Exception { - InMemoryTaskStore store = new InMemoryTaskStore(); - store.delete("non-existent"); - } -} diff --git a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java index d64729077..0e25e9aad 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/ResultAggregatorTest.java @@ -11,18 +11,25 @@ import static org.mockito.Mockito.when; import java.util.Collections; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import io.a2a.server.events.EventConsumer; import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueUtil; import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.events.MainEventBus; +import io.a2a.server.events.MainEventBusProcessor; +import io.a2a.spec.Event; import io.a2a.spec.EventKind; import io.a2a.spec.Message; import io.a2a.spec.Task; import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatus; import io.a2a.spec.TextPart; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -49,7 +56,7 @@ public class ResultAggregatorTest { @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); - aggregator = new ResultAggregator(mockTaskManager, null, testExecutor); + aggregator = new ResultAggregator(mockTaskManager, null, testExecutor, testExecutor); } // Helper methods for creating sample data @@ -69,13 +76,45 @@ private Task createSampleTask(String taskId, TaskState statusState, String conte .build(); } + /** + * Helper to wait for MainEventBusProcessor to process an event. + * Replaces polling patterns with deterministic callback-based waiting. + * + * @param processor the processor to set callback on + * @param action the action that triggers event processing + * @throws InterruptedException if waiting is interrupted + * @throws AssertionError if processing doesn't complete within timeout + */ + private void waitForEventProcessing(MainEventBusProcessor processor, Runnable action) throws InterruptedException { + CountDownLatch processingLatch = new CountDownLatch(1); + processor.setCallback(new io.a2a.server.events.MainEventBusProcessorCallback() { + @Override + public void onEventProcessed(String taskId, Event event) { + processingLatch.countDown(); + } + + @Override + public void onTaskFinalized(String taskId) { + // Not needed for basic event processing wait + } + }); + + try { + action.run(); + assertTrue(processingLatch.await(5, TimeUnit.SECONDS), + "MainEventBusProcessor should have processed the event within timeout"); + } finally { + processor.setCallback(null); + } + } + // Basic functionality tests @Test void testConstructorWithMessage() { Message initialMessage = createSampleMessage("initial", "msg1", Message.Role.USER); - ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, initialMessage, testExecutor); + ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, initialMessage, testExecutor, testExecutor); // Test that the message is properly stored by checking getCurrentResult assertEquals(initialMessage, aggregatorWithMessage.getCurrentResult()); @@ -86,7 +125,7 @@ void testConstructorWithMessage() { @Test void testGetCurrentResultWithMessageSet() { Message sampleMessage = createSampleMessage("hola", "msg1", Message.Role.USER); - ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, sampleMessage, testExecutor); + ResultAggregator aggregatorWithMessage = new ResultAggregator(mockTaskManager, sampleMessage, testExecutor, testExecutor); EventKind result = aggregatorWithMessage.getCurrentResult(); @@ -121,7 +160,7 @@ void testConstructorStoresTaskManagerCorrectly() { @Test void testConstructorWithNullMessage() { - ResultAggregator aggregatorWithNullMessage = new ResultAggregator(mockTaskManager, null, testExecutor); + ResultAggregator aggregatorWithNullMessage = new ResultAggregator(mockTaskManager, null, testExecutor, testExecutor); Task expectedTask = createSampleTask("null_msg_task", TaskState.WORKING, "ctx1"); when(mockTaskManager.getTask()).thenReturn(expectedTask); @@ -181,7 +220,7 @@ void testMultipleGetCurrentResultCalls() { void testGetCurrentResultWithMessageTakesPrecedence() { // Test that when both message and task are available, message takes precedence Message message = createSampleMessage("priority message", "pri1", Message.Role.USER); - ResultAggregator messageAggregator = new ResultAggregator(mockTaskManager, message, testExecutor); + ResultAggregator messageAggregator = new ResultAggregator(mockTaskManager, message, testExecutor, testExecutor); // Even if we set up the task manager to return something, message should take precedence Task task = createSampleTask("should_not_be_returned", TaskState.WORKING, "ctx1"); @@ -197,17 +236,24 @@ void testGetCurrentResultWithMessageTakesPrecedence() { @Test void testConsumeAndBreakNonBlocking() throws Exception { // Test that with blocking=false, the method returns after the first event - Task firstEvent = createSampleTask("non_blocking_task", TaskState.WORKING, "ctx1"); + String taskId = "test-task"; + Task firstEvent = createSampleTask(taskId, TaskState.WORKING, "ctx1"); // After processing firstEvent, the current result will be that task when(mockTaskManager.getTask()).thenReturn(firstEvent); // Create an event queue using QueueManager (which has access to builder) + MainEventBus mainEventBus = new MainEventBus(); + InMemoryTaskStore taskStore = new InMemoryTaskStore(); InMemoryQueueManager queueManager = - new InMemoryQueueManager(new MockTaskStateProvider()); + new InMemoryQueueManager(new MockTaskStateProvider(), mainEventBus); + MainEventBusProcessor processor = new MainEventBusProcessor(mainEventBus, taskStore, task -> {}, queueManager); + EventQueueUtil.start(processor); + + EventQueue queue = queueManager.getEventQueueBuilder(taskId).build().tap(); - EventQueue queue = queueManager.getEventQueueBuilder("test-task").build(); - queue.enqueueEvent(firstEvent); + // Use callback to wait for event processing (replaces polling) + waitForEventProcessing(processor, () -> queue.enqueueEvent(firstEvent)); // Create real EventConsumer with the queue EventConsumer eventConsumer = @@ -221,11 +267,16 @@ void testConsumeAndBreakNonBlocking() throws Exception { assertEquals(firstEvent, result.eventType()); assertTrue(result.interrupted()); - verify(mockTaskManager).process(firstEvent); - // getTask() is called at least once for the return value (line 255) - // May be called once more if debug logging executes in time (line 209) - // The async consumer may or may not execute before verification, so we accept 1-2 calls - verify(mockTaskManager, atLeast(1)).getTask(); - verify(mockTaskManager, atMost(2)).getTask(); + // NOTE: ResultAggregator no longer calls taskManager.process() + // That responsibility has moved to MainEventBusProcessor for centralized persistence + // + // NOTE: Since firstEvent is a Task, ResultAggregator captures it directly from the queue + // (capturedTask.get() at line 283 in ResultAggregator). Therefore, taskManager.getTask() + // is only called for debug logging in taskIdForLogging() (line 305), which may or may not + // execute depending on timing and log level. We expect 0-1 calls, not 1-2. + verify(mockTaskManager, atMost(1)).getTask(); + + // Cleanup: stop the processor + EventQueueUtil.stop(processor); } } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index f14ebc0fe..91010e52e 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -48,7 +48,7 @@ public void init() throws Exception { @Test public void testGetTaskExisting() { Task expectedTask = minimalTask; - taskStore.save(expectedTask); + taskStore.save(expectedTask, false); Task retrieved = taskManager.getTask(); assertSame(expectedTask, retrieved); } @@ -61,16 +61,16 @@ public void testGetTaskNonExistent() { @Test public void testSaveTaskEventNewTask() throws A2AServerException { - Task saved = taskManager.saveTaskEvent(minimalTask); + taskManager.saveTaskEvent(minimalTask, false); + Task saved = taskManager.getTask(); Task retrieved = taskManager.getTask(); assertSame(minimalTask, retrieved); - assertSame(retrieved, saved); } @Test public void testSaveTaskEventStatusUpdate() throws A2AServerException { Task initialTask = minimalTask; - taskStore.save(initialTask); + taskStore.save(initialTask, false); TaskStatus newStatus = new TaskStatus( TaskState.WORKING, @@ -88,11 +88,11 @@ public void testSaveTaskEventStatusUpdate() throws A2AServerException { new HashMap<>()); - Task saved = taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task updated = taskManager.getTask(); assertNotSame(initialTask, updated); - assertSame(updated, saved); assertEquals(initialTask.id(), updated.id()); assertEquals(initialTask.contextId(), updated.contextId()); @@ -114,10 +114,10 @@ public void testSaveTaskEventArtifactUpdate() throws A2AServerException { .contextId(minimalTask.contextId()) .artifact(newArtifact) .build(); - Task saved = taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task updatedTask = taskManager.getTask(); - assertSame(updatedTask, saved); assertNotSame(initialTask, updatedTask); assertEquals(initialTask.id(), updatedTask.id()); @@ -144,7 +144,8 @@ public void testEnsureTaskNonExistentForStatusUpdate() throws A2AServerException .isFinal(false) .build(); - Task task = taskManagerWithoutId.saveTaskEvent(event); + taskManagerWithoutId.saveTaskEvent(event, false); + Task task = taskManagerWithoutId.getTask(); assertEquals(event.taskId(), taskManagerWithoutId.getTaskId()); assertEquals(event.contextId(), taskManagerWithoutId.getContextId()); @@ -164,13 +165,13 @@ public void testSaveTaskEventNewTaskNoTaskId() throws A2AServerException { .status(new TaskStatus(TaskState.WORKING)) .build(); - Task saved = taskManagerWithoutId.saveTaskEvent(task); + taskManagerWithoutId.saveTaskEvent(task, false); + Task saved = taskManager.getTask(); assertEquals(task.id(), taskManagerWithoutId.getTaskId()); assertEquals(task.contextId(), taskManagerWithoutId.getContextId()); Task retrieved = taskManagerWithoutId.getTask(); assertSame(task, retrieved); - assertSame(retrieved, saved); } @Test @@ -194,7 +195,7 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) .build(); - taskStore.save(taskWithArtifact); + taskStore.save(taskWithArtifact, false); // Test: Append new parts to existing artifact Artifact newArtifact = Artifact.builder() @@ -209,7 +210,8 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A .append(true) .build(); - Task updatedTask = taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); + Task updatedTask = taskManager.getTask(); assertEquals(1, updatedTask.artifacts().size()); Artifact updatedArtifact = updatedTask.artifacts().get(0); @@ -223,7 +225,7 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throws A2AServerException { // Setup: Create a task without artifacts Task initialTask = minimalTask; - taskStore.save(initialTask); + taskStore.save(initialTask, false); // Test: Try to append to non-existent artifact (should be ignored) Artifact newArtifact = Artifact.builder() @@ -238,7 +240,8 @@ public void testTaskArtifactUpdateEventAppendTrueWithoutExistingArtifact() throw .append(true) .build(); - Task saved = taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task updatedTask = taskManager.getTask(); // Should have no artifacts since append was ignored @@ -257,7 +260,7 @@ public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) .build(); - taskStore.save(taskWithArtifact); + taskStore.save(taskWithArtifact, false); // Test: Replace existing artifact (append=false) Artifact newArtifact = Artifact.builder() @@ -272,7 +275,8 @@ public void testTaskArtifactUpdateEventAppendFalseWithExistingArtifact() throws .append(false) .build(); - Task saved = taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task updatedTask = taskManager.getTask(); assertEquals(1, updatedTask.artifacts().size()); @@ -294,7 +298,7 @@ public void testTaskArtifactUpdateEventAppendNullWithExistingArtifact() throws A Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) .build(); - taskStore.save(taskWithArtifact); + taskStore.save(taskWithArtifact, false); // Test: Replace existing artifact (append=null, defaults to false) Artifact newArtifact = Artifact.builder() @@ -308,7 +312,8 @@ public void testTaskArtifactUpdateEventAppendNullWithExistingArtifact() throws A .artifact(newArtifact) .build(); // append is null - Task saved = taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task updatedTask = taskManager.getTask(); assertEquals(1, updatedTask.artifacts().size()); @@ -330,7 +335,7 @@ public void testAddingTaskWithDifferentIdFails() { .build(); assertThrows(A2AServerException.class, () -> { - taskManagerWithId.saveTaskEvent(differentTask); + taskManagerWithId.saveTaskEvent(differentTask, false); }); } @@ -347,7 +352,7 @@ public void testAddingTaskWithDifferentIdViaStatusUpdateFails() { .build(); assertThrows(A2AServerException.class, () -> { - taskManagerWithId.saveTaskEvent(event); + taskManagerWithId.saveTaskEvent(event, false); }); } @@ -368,7 +373,7 @@ public void testAddingTaskWithDifferentIdViaArtifactUpdateFails() { .build(); assertThrows(A2AServerException.class, () -> { - taskManagerWithId.saveTaskEvent(event); + taskManagerWithId.saveTaskEvent(event, false); }); } @@ -392,7 +397,8 @@ public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException .isFinal(false) .build(); - Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); + taskManagerWithInitialMessage.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task retrieved = taskManagerWithInitialMessage.getTask(); // Check that the task has the initial message in its history @@ -429,7 +435,8 @@ public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerExcept .isFinal(false) .build(); - Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); + taskManagerWithInitialMessage.saveTaskEvent(event, false); + Task saved = taskManager.getTask(); Task retrieved = taskManagerWithInitialMessage.getTask(); // There should now be a history containing the initialMessage @@ -447,7 +454,7 @@ public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerExcept public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException { // Test handling of multiple artifacts with the same artifactId Task initialTask = minimalTask; - taskStore.save(initialTask); + taskStore.save(initialTask, false); // Add first artifact Artifact artifact1 = Artifact.builder() @@ -460,7 +467,7 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException .contextId(minimalTask.contextId()) .artifact(artifact1) .build(); - taskManager.saveTaskEvent(event1); + taskManager.saveTaskEvent(event1, false); // Add second artifact with same artifactId (should replace the first) Artifact artifact2 = Artifact.builder() @@ -473,7 +480,7 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException .contextId(minimalTask.contextId()) .artifact(artifact2) .build(); - taskManager.saveTaskEvent(event2); + taskManager.saveTaskEvent(event2, false); Task updatedTask = taskManager.getTask(); assertEquals(1, updatedTask.artifacts().size()); @@ -487,7 +494,7 @@ public void testMultipleArtifactsWithSameArtifactId() throws A2AServerException public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerException { // Test handling of multiple artifacts with different artifactIds Task initialTask = minimalTask; - taskStore.save(initialTask); + taskStore.save(initialTask, false); // Add first artifact Artifact artifact1 = Artifact.builder() @@ -500,7 +507,7 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce .contextId(minimalTask.contextId()) .artifact(artifact1) .build(); - taskManager.saveTaskEvent(event1); + taskManager.saveTaskEvent(event1, false); // Add second artifact with different artifactId (should be added) Artifact artifact2 = Artifact.builder() @@ -513,7 +520,7 @@ public void testMultipleArtifactsWithDifferentArtifactIds() throws A2AServerExce .contextId(minimalTask.contextId()) .artifact(artifact2) .build(); - taskManager.saveTaskEvent(event2); + taskManager.saveTaskEvent(event2, false); Task updatedTask = taskManager.getTask(); assertEquals(2, updatedTask.artifacts().size()); @@ -545,7 +552,7 @@ public void testInvalidTaskIdValidation() { public void testSaveTaskEventMetadataUpdate() throws A2AServerException { // Test that metadata from TaskStatusUpdateEvent gets saved to the task Task initialTask = minimalTask; - taskStore.save(initialTask); + taskStore.save(initialTask, false); Map newMetadata = new HashMap<>(); newMetadata.put("meta_key_test", "meta_value_test"); @@ -558,7 +565,7 @@ public void testSaveTaskEventMetadataUpdate() throws A2AServerException { .metadata(newMetadata) .build(); - taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); Task updatedTask = taskManager.getTask(); assertEquals(newMetadata, updatedTask.metadata()); @@ -568,7 +575,7 @@ public void testSaveTaskEventMetadataUpdate() throws A2AServerException { public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { // Test that null metadata in TaskStatusUpdateEvent doesn't affect task Task initialTask = minimalTask; - taskStore.save(initialTask); + taskStore.save(initialTask, false); TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(minimalTask.id()) @@ -578,7 +585,7 @@ public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { .metadata(null) .build(); - taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); Task updatedTask = taskManager.getTask(); // Should preserve original task's metadata (which is likely null for minimal task) @@ -594,7 +601,7 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { Task taskWithMetadata = Task.builder(minimalTask) .metadata(originalMetadata) .build(); - taskStore.save(taskWithMetadata); + taskStore.save(taskWithMetadata, false); Map newMetadata = new HashMap<>(); newMetadata.put("new_key", "new_value"); @@ -607,7 +614,7 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { .metadata(newMetadata) .build(); - taskManager.saveTaskEvent(event); + taskManager.saveTaskEvent(event, false); Task updatedTask = taskManager.getTask(); @@ -634,7 +641,8 @@ public void testCreateTaskWithInitialMessage() throws A2AServerException { .isFinal(false) .build(); - Task savedTask = taskManagerWithMessage.saveTaskEvent(event); + taskManagerWithMessage.saveTaskEvent(event, false); + Task savedTask = taskManagerWithMessage.getTask(); // Verify task was created properly assertNotNull(savedTask); @@ -662,7 +670,8 @@ public void testCreateTaskWithoutInitialMessage() throws A2AServerException { .isFinal(false) .build(); - Task savedTask = taskManagerWithoutMessage.saveTaskEvent(event); + taskManagerWithoutMessage.saveTaskEvent(event, false); + Task savedTask = taskManagerWithoutMessage.getTask(); // Verify task was created properly assertNotNull(savedTask); @@ -685,7 +694,8 @@ public void testSaveTaskInternal() throws A2AServerException { .status(new TaskStatus(TaskState.WORKING)) .build(); - Task savedTask = taskManagerWithoutId.saveTaskEvent(newTask); + taskManagerWithoutId.saveTaskEvent(newTask, false); + Task savedTask = taskManagerWithoutId.getTask(); // Verify internal state was updated assertEquals("test-task-id", taskManagerWithoutId.getTaskId()); @@ -716,7 +726,8 @@ public void testUpdateWithMessage() throws A2AServerException { .isFinal(false) .build(); - Task saved = taskManagerWithInitialMessage.saveTaskEvent(event); + taskManagerWithInitialMessage.saveTaskEvent(event, false); + Task saved = taskManagerWithInitialMessage.getTask(); Message updateMessage = Message.builder() .role(Message.Role.USER) diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 40f763569..73da17824 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -14,7 +14,11 @@ import io.a2a.server.agentexecution.RequestContext; import io.a2a.server.events.EventQueue; +import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.EventQueueUtil; +import io.a2a.server.events.InMemoryQueueManager; +import io.a2a.server.events.MainEventBus; +import io.a2a.server.events.MainEventBusProcessor; import io.a2a.spec.Event; import io.a2a.spec.Message; import io.a2a.spec.Part; @@ -22,6 +26,7 @@ import io.a2a.spec.TaskState; import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,14 +43,28 @@ public class TaskUpdaterTest { private static final List> SAMPLE_PARTS = List.of(new TextPart("Test message")); + private static final PushNotificationSender NOOP_PUSHNOTIFICATION_SENDER = task -> {}; + EventQueue eventQueue; + private MainEventBus mainEventBus; + private MainEventBusProcessor mainEventBusProcessor; private TaskUpdater taskUpdater; @BeforeEach public void init() { - eventQueue = EventQueueUtil.getEventQueueBuilder().build(); + // Set up MainEventBus and processor for production-like test environment + InMemoryTaskStore taskStore = new InMemoryTaskStore(); + mainEventBus = new MainEventBus(); + InMemoryQueueManager queueManager = new InMemoryQueueManager(taskStore, mainEventBus); + mainEventBusProcessor = new MainEventBusProcessor(mainEventBus, taskStore, NOOP_PUSHNOTIFICATION_SENDER, queueManager); + EventQueueUtil.start(mainEventBusProcessor); + + eventQueue = EventQueueUtil.getEventQueueBuilder(mainEventBus) + .taskId(TEST_TASK_ID) + .mainEventBus(mainEventBus) + .build().tap(); RequestContext context = new RequestContext.Builder() .setTaskId(TEST_TASK_ID) .setContextId(TEST_TASK_CONTEXT_ID) @@ -53,10 +72,19 @@ public void init() { taskUpdater = new TaskUpdater(context, eventQueue); } + @AfterEach + public void cleanup() { + if (mainEventBusProcessor != null) { + EventQueueUtil.stop(mainEventBusProcessor); + } + } + @Test public void testAddArtifactWithCustomIdAndName() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "custom-artifact-id", "Custom Artifact", null); - Event event = eventQueue.dequeueEventItem(0).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -239,7 +267,9 @@ public void testNewAgentMessageWithMetadata() throws Exception { @Test public void testAddArtifactWithAppendTrue() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, true, null); - Event event = eventQueue.dequeueEventItem(0).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -258,7 +288,9 @@ public void testAddArtifactWithAppendTrue() throws Exception { @Test public void testAddArtifactWithLastChunkTrue() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, null, true); - Event event = eventQueue.dequeueEventItem(0).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -273,7 +305,9 @@ public void testAddArtifactWithLastChunkTrue() throws Exception { @Test public void testAddArtifactWithAppendAndLastChunk() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, "artifact-id", "Test Artifact", null, true, false); - Event event = eventQueue.dequeueEventItem(0).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -287,7 +321,9 @@ public void testAddArtifactWithAppendAndLastChunk() throws Exception { @Test public void testAddArtifactGeneratesIdWhenNull() throws Exception { taskUpdater.addArtifact(SAMPLE_PARTS, null, "Test Artifact", null); - Event event = eventQueue.dequeueEventItem(0).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskArtifactUpdateEvent.class, event); @@ -383,7 +419,9 @@ public void testConcurrentCompletionAttempts() throws Exception { thread2.join(); // Exactly one event should have been queued - Event event = eventQueue.dequeueEventItem(0).getEvent(); + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskStatusUpdateEvent.class, event); @@ -396,7 +434,10 @@ public void testConcurrentCompletionAttempts() throws Exception { } private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, TaskState state, Message statusMessage) throws Exception { - Event event = eventQueue.dequeueEventItem(0).getEvent(); + // Wait up to 5 seconds for event (async MainEventBusProcessor needs time to distribute) + EventQueueItem item = eventQueue.dequeueEventItem(5000); + assertNotNull(item); + Event event = item.getEvent(); assertNotNull(event); assertInstanceOf(TaskStatusUpdateEvent.class, event); @@ -408,6 +449,7 @@ private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, assertEquals(state, tsue.status().state()); assertEquals(statusMessage, tsue.status().message()); + // Check no additional events (still use 0 timeout for this check) assertNull(eventQueue.dequeueEventItem(0)); return tsue; diff --git a/tck/src/main/resources/application.properties b/tck/src/main/resources/application.properties index c68793be4..b23747b00 100644 --- a/tck/src/main/resources/application.properties +++ b/tck/src/main/resources/application.properties @@ -12,6 +12,7 @@ a2a.executor.keep-alive-seconds=60 quarkus.log.category."io.a2a.server.requesthandlers".level=DEBUG quarkus.log.category."io.a2a.server.events".level=DEBUG quarkus.log.category."io.a2a.server.tasks".level=DEBUG +io.a2a.server.diagnostics.ThreadStats.level=DEBUG # Log to file for analysis quarkus.log.file.enable=true diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 724b58613..62acc506e 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -656,7 +656,20 @@ public void testResubscribeExistingTaskSuccess() throws Exception { AtomicReference errorRef = new AtomicReference<>(); // Create consumer to handle resubscribed events + AtomicBoolean receivedInitialTask = new AtomicBoolean(false); BiConsumer consumer = (event, agentCard) -> { + // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent + if (!receivedInitialTask.get()) { + if (event instanceof TaskEvent) { + receivedInitialTask.set(true); + // Don't count down latch for initial Task + return; + } else { + fail("First event on resubscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); + } + } + + // Process subsequent events if (event instanceof TaskUpdateEvent taskUpdateEvent) { if (taskUpdateEvent.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifactEvent) { artifactUpdateEvent.set(artifactEvent); @@ -755,12 +768,25 @@ public void testResubscribeExistingTaskSuccessWithClientConsumers() throws Excep AtomicReference errorRef = new AtomicReference<>(); // Create consumer to handle resubscribed events + AtomicBoolean receivedInitialTask = new AtomicBoolean(false); AgentCard agentCard = createTestAgentCard(); ClientConfig clientConfig = createClientConfig(true); ClientBuilder clientBuilder = Client .builder(agentCard) .addConsumer((evt, agentCard1) -> { + // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent + if (!receivedInitialTask.get()) { + if (evt instanceof TaskEvent) { + receivedInitialTask.set(true); + // Don't count down latch for initial Task + return; + } else { + fail("First event on resubscribe MUST be TaskEvent, but was: " + evt.getClass().getSimpleName()); + } + } + + // Process subsequent events if (evt instanceof TaskUpdateEvent taskUpdateEvent) { if (taskUpdateEvent.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifactEvent) { artifactUpdateEvent.set(artifactEvent); @@ -918,8 +944,20 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti AtomicReference firstConsumerEvent = new AtomicReference<>(); AtomicBoolean firstUnexpectedEvent = new AtomicBoolean(false); AtomicReference firstErrorRef = new AtomicReference<>(); + AtomicBoolean firstReceivedInitialTask = new AtomicBoolean(false); BiConsumer firstConsumer = (event, agentCard) -> { + // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent + if (!firstReceivedInitialTask.get()) { + if (event instanceof TaskEvent) { + firstReceivedInitialTask.set(true); + return; + } else { + fail("First event on resubscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); + } + } + + // Process subsequent events if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifact) { firstConsumerEvent.set(artifact); firstConsumerLatch.countDown(); @@ -975,8 +1013,20 @@ public void testMainQueueReferenceCountingWithMultipleConsumers() throws Excepti AtomicReference secondConsumerEvent = new AtomicReference<>(); AtomicBoolean secondUnexpectedEvent = new AtomicBoolean(false); AtomicReference secondErrorRef = new AtomicReference<>(); + AtomicBoolean secondReceivedInitialTask = new AtomicBoolean(false); BiConsumer secondConsumer = (event, agentCard) -> { + // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent + if (!secondReceivedInitialTask.get()) { + if (event instanceof TaskEvent) { + secondReceivedInitialTask.set(true); + return; + } else { + fail("First event on resubscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); + } + } + + // Process subsequent events if (event instanceof TaskUpdateEvent tue && tue.getUpdateEvent() instanceof TaskArtifactUpdateEvent artifact) { secondConsumerEvent.set(artifact); secondConsumerLatch.countDown(); @@ -1316,8 +1366,20 @@ public void testNonBlockingWithMultipleMessages() throws Exception { List resubReceivedEvents = new CopyOnWriteArrayList<>(); AtomicBoolean resubUnexpectedEvent = new AtomicBoolean(false); AtomicReference resubErrorRef = new AtomicReference<>(); + AtomicBoolean resubReceivedInitialTask = new AtomicBoolean(false); BiConsumer resubConsumer = (event, agentCard) -> { + // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent + if (!resubReceivedInitialTask.get()) { + if (event instanceof TaskEvent) { + resubReceivedInitialTask.set(true); + return; + } else { + fail("First event on resubscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); + } + } + + // Process subsequent events if (event instanceof TaskUpdateEvent tue) { resubReceivedEvents.add(tue.getUpdateEvent()); resubEventLatch.countDown(); @@ -1355,6 +1417,7 @@ public void testNonBlockingWithMultipleMessages() throws Exception { AtomicBoolean streamUnexpectedEvent = new AtomicBoolean(false); BiConsumer streamConsumer = (event, agentCard) -> { + // This consumer is for sendMessage() (not resubscribe), so it doesn't get initial TaskEvent if (event instanceof TaskUpdateEvent tue) { streamReceivedEvents.add(tue.getUpdateEvent()); streamEventLatch.countDown(); diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java index 9df23c565..45483f214 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/TestUtilsBean.java @@ -31,7 +31,7 @@ public class TestUtilsBean { PushNotificationConfigStore pushNotificationConfigStore; public void saveTask(Task task) { - taskStore.save(task); + taskStore.save(task, false); } public Task getTask(String taskId) { diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 408205aa2..439d97497 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -242,7 +242,7 @@ public void sendStreamingMessage(io.a2a.grpc.SendMessageRequest request, A2AExtensions.validateRequiredExtensions(getAgentCardInternal(), context); MessageSendParams params = FromProto.messageSendParams(request); Flow.Publisher publisher = getRequestHandler().onMessageSendStream(params, context); - convertToStreamResponse(publisher, responseObserver); + convertToStreamResponse(publisher, responseObserver, context); } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { @@ -264,7 +264,7 @@ public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, ServerCallContext context = createCallContext(responseObserver); TaskIdParams params = FromProto.taskIdParams(request); Flow.Publisher publisher = getRequestHandler().onResubscribeToTask(params, context); - convertToStreamResponse(publisher, responseObserver); + convertToStreamResponse(publisher, responseObserver, context); } catch (A2AError e) { handleError(responseObserver, e); } catch (SecurityException e) { @@ -275,7 +275,8 @@ public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, } private void convertToStreamResponse(Flow.Publisher publisher, - StreamObserver responseObserver) { + StreamObserver responseObserver, + ServerCallContext context) { CompletableFuture.runAsync(() -> { publisher.subscribe(new Flow.Subscriber() { private Flow.Subscription subscription; @@ -285,6 +286,18 @@ public void onSubscribe(Flow.Subscription subscription) { this.subscription = subscription; subscription.request(1); + // Detect gRPC client disconnect and call EventConsumer.cancel() directly + // This stops the polling loop without relying on subscription cancellation propagation + Context grpcContext = Context.current(); + grpcContext.addListener(new Context.CancellationListener() { + @Override + public void cancelled(Context ctx) { + LOGGER.fine(() -> "gRPC call cancelled by client, calling EventConsumer.cancel() to stop polling loop"); + context.invokeEventConsumerCancelCallback(); + subscription.cancel(); + } + }, getExecutor()); + // Notify tests that we are subscribed Runnable runnable = streamingSubscribedRunnable; if (runnable != null) { @@ -305,6 +318,8 @@ public void onNext(StreamingEventKind event) { @Override public void onError(Throwable throwable) { + // Cancel upstream to stop EventConsumer when error occurs + subscription.cancel(); if (throwable instanceof A2AError jsonrpcError) { handleError(responseObserver, jsonrpcError); } else { @@ -329,6 +344,9 @@ public void getExtendedAgentCard(io.a2a.grpc.GetExtendedAgentCardRequest request if (extendedAgentCard != null) { responseObserver.onNext(ToProto.agentCard(extendedAgentCard)); responseObserver.onCompleted(); + } else { + // Extended agent card not configured - return error instead of hanging + handleError(responseObserver, new ExtendedAgentCardNotConfiguredError(null, "Extended agent card not configured", null)); } } catch (Throwable t) { handleInternalError(responseObserver, t); diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 690d69a87..afed1329f 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -84,7 +84,7 @@ public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testOnGetTaskSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); GetTaskRequest request = GetTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .build(); @@ -120,7 +120,7 @@ public void testOnGetTaskNotFound() throws Exception { @Test public void testOnCancelTaskSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorCancel = (context, eventQueue) -> { // We need to cancel the task or the EventConsumer never finds a 'final' event. @@ -151,7 +151,7 @@ public void testOnCancelTaskSuccess() throws Exception { @Test public void testOnCancelTaskNotSupported() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorCancel = (context, eventQueue) -> { throw new UnsupportedOperationError(); @@ -199,7 +199,7 @@ public void testOnMessageNewMessageSuccess() throws Exception { @Test public void testOnMessageNewMessageWithExistingTaskSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -281,8 +281,7 @@ public void testPushNotificationsNotSupportedError() throws Exception { @Test public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = - new DefaultRequestHandler(executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); @@ -293,8 +292,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { @Test public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); @@ -330,7 +328,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception io.a2a.spec.Task task = io.a2a.spec.Task.builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK) .history(new ArrayList<>()) .build(); - taskStore.save(task); + taskStore.save(task, false); List results = new ArrayList<>(); List errors = new ArrayList<>(); @@ -379,7 +377,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep io.a2a.spec.Task task = io.a2a.spec.Task.builder(AbstractA2ARequestHandlerTest.MINIMAL_TASK) .history(new ArrayList<>()) .build(); - taskStore.save(task); + taskStore.save(task, false); // This is used to send events from a mock List events = List.of( @@ -424,9 +422,14 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() throws Excep @Test public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { - GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - List events = List.of( - AbstractA2ARequestHandlerTest.MINIMAL_TASK, + // Use synchronous executor for push notifications to ensure deterministic ordering + // Without this, async push notifications can execute out of order, causing test flakiness + mainEventBusProcessor.setPushNotificationExecutor(Runnable::run); + + try { + GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); + List events = List.of( + AbstractA2ARequestHandlerTest.MINIMAL_TASK, TaskArtifactUpdateEvent.builder() .taskId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) .contextId(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId()) @@ -493,13 +496,16 @@ public void onCompleted() { Assertions.assertEquals(1, curr.artifacts().get(0).parts().size()); Assertions.assertEquals("text", ((TextPart)curr.artifacts().get(0).parts().get(0)).text()); - curr = httpClient.tasks.get(2); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), curr.id()); - Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), curr.contextId()); - Assertions.assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.status().state()); - Assertions.assertEquals(1, curr.artifacts().size()); - Assertions.assertEquals(1, curr.artifacts().get(0).parts().size()); - Assertions.assertEquals("text", ((TextPart)curr.artifacts().get(0).parts().get(0)).text()); + curr = httpClient.tasks.get(2); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(), curr.id()); + Assertions.assertEquals(AbstractA2ARequestHandlerTest.MINIMAL_TASK.contextId(), curr.contextId()); + Assertions.assertEquals(io.a2a.spec.TaskState.COMPLETED, curr.status().state()); + Assertions.assertEquals(1, curr.artifacts().size()); + Assertions.assertEquals(1, curr.artifacts().get(0).parts().size()); + Assertions.assertEquals("text", ((TextPart)curr.artifacts().get(0).parts().get(0)).text()); + } finally { + mainEventBusProcessor.setPushNotificationExecutor(null); + } } @Test @@ -517,7 +523,7 @@ public void testOnResubscribeNoExistingTaskError() throws Exception { @Test public void testOnResubscribeExistingTaskSuccess() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()); agentExecutorExecute = (context, eventQueue) -> { @@ -542,9 +548,18 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); List result = streamRecorder.getValues(); Assertions.assertNotNull(result); - Assertions.assertEquals(1, result.size()); - StreamResponse response = result.get(0); - Assertions.assertTrue(response.hasMessage()); + // Per A2A Protocol Spec 3.1.6, resubscribe sends current Task as first event, + // followed by the Message from the agent executor + Assertions.assertEquals(2, result.size()); + + // ENFORCE that first event is Task + Assertions.assertTrue(result.get(0).hasTask(), + "First event on resubscribe MUST be Task (current state)"); + + // Second event should be Message from agent executor + StreamResponse response = result.get(1); + Assertions.assertTrue(response.hasMessage(), + "Expected Message after initial Task"); assertEquals(GRPC_MESSAGE, response.getMessage()); Assertions.assertNull(streamRecorder.getError()); } @@ -552,7 +567,7 @@ public void testOnResubscribeExistingTaskSuccess() throws Exception { @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); queueManager.createOrTap(AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()); List events = List.of( @@ -627,7 +642,7 @@ public void testOnMessageStreamInternalError() throws Exception { @Test public void testListPushNotificationConfig() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -653,7 +668,7 @@ public void testListPushNotificationConfig() throws Exception { public void testListPushNotificationConfigNotSupported() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -668,10 +683,9 @@ public void testListPushNotificationConfigNotSupported() throws Exception { @Test public void testListPushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -702,7 +716,7 @@ public void testListPushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfig() throws Exception { GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -725,7 +739,7 @@ public void testDeletePushNotificationConfig() throws Exception { public void testDeletePushNotificationConfigNotSupported() throws Exception { AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -741,8 +755,7 @@ public void testDeletePushNotificationConfigNotSupported() throws Exception { @Test public void testDeletePushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); GrpcHandler handler = new TestGrpcHandler(AbstractA2ARequestHandlerTest.CARD, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); DeleteTaskPushNotificationConfigRequest request = DeleteTaskPushNotificationConfigRequest.newBuilder() @@ -1155,7 +1168,7 @@ private StreamRecorder sendMessageRequest(GrpcHandler handl } private StreamRecorder createTaskPushNotificationConfigRequest(GrpcHandler handler, String name) throws Exception { - taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK); + taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); PushNotificationConfig config = PushNotificationConfig.newBuilder() .setUrl("http://example.com") .setId("config456") diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index b43c28029..4dc151626 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.Collections; @@ -30,6 +32,8 @@ import io.a2a.jsonrpc.common.wrappers.GetTaskResponse; import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.ListTasksRequest; +import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; @@ -37,12 +41,11 @@ import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; -import io.a2a.jsonrpc.common.wrappers.ListTasksRequest; -import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; +import io.a2a.server.events.MainEventBusProcessorCallback; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; import io.a2a.server.requesthandlers.DefaultRequestHandler; import io.a2a.server.tasks.ResultAggregator; @@ -52,16 +55,15 @@ import io.a2a.spec.AgentExtension; import io.a2a.spec.AgentInterface; import io.a2a.spec.Artifact; -import io.a2a.spec.ExtendedAgentCardNotConfiguredError; -import io.a2a.spec.ExtensionSupportRequiredError; -import io.a2a.spec.VersionNotSupportedError; import io.a2a.spec.DeleteTaskPushNotificationConfigParams; import io.a2a.spec.Event; +import io.a2a.spec.ExtendedAgentCardNotConfiguredError; +import io.a2a.spec.ExtensionSupportRequiredError; import io.a2a.spec.GetTaskPushNotificationConfigParams; import io.a2a.spec.InternalError; import io.a2a.spec.InvalidRequestError; -import io.a2a.spec.ListTasksParams; import io.a2a.spec.ListTaskPushNotificationConfigParams; +import io.a2a.spec.ListTasksParams; import io.a2a.spec.Message; import io.a2a.spec.MessageSendParams; import io.a2a.spec.PushNotificationConfig; @@ -78,6 +80,7 @@ import io.a2a.spec.TaskStatusUpdateEvent; import io.a2a.spec.TextPart; import io.a2a.spec.UnsupportedOperationError; +import io.a2a.spec.VersionNotSupportedError; import mutiny.zero.ZeroPublisher; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; @@ -92,7 +95,7 @@ public class JSONRPCHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testOnGetTaskSuccess() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); GetTaskRequest request = new GetTaskRequest("1", new TaskQueryParams(MINIMAL_TASK.id())); GetTaskResponse response = handler.onGetTask(request, callContext); assertEquals(request.getId(), response.getId()); @@ -113,7 +116,7 @@ public void testOnGetTaskNotFound() throws Exception { @Test public void testOnCancelTaskSuccess() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorCancel = (context, eventQueue) -> { // We need to cancel the task or the EventConsumer never finds a 'final' event. @@ -138,7 +141,7 @@ public void testOnCancelTaskSuccess() throws Exception { @Test public void testOnCancelTaskNotSupported() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorCancel = (context, eventQueue) -> { throw new UnsupportedOperationError(); @@ -174,42 +177,13 @@ public void testOnMessageNewMessageSuccess() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); assertNull(response.getError()); - // The Python implementation returns a Task here, but then again they are using hardcoded mocks and - // bypassing the whole EventQueue. - // If we were to send a Task in agentExecutorExecute EventConsumer.consumeAll() would not exit due to - // the Task not having a 'final' state - // - // See testOnMessageNewMessageSuccessMocks() for a test more similar to the Python implementation Assertions.assertSame(message, response.getResult()); } - @Test - public void testOnMessageNewMessageSuccessMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - - Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.id()) - .contextId(MINIMAL_TASK.contextId()) - .build(); - - SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response; - try (MockedConstruction mocked = Mockito.mockConstruction( - EventConsumer.class, - (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll(); - Mockito.doCallRealMethod().when(mock).createAgentRunnableDoneCallback(); - })) { - response = handler.onMessageSend(request, callContext); - } - assertNull(response.getError()); - Assertions.assertSame(MINIMAL_TASK, response.getResult()); - } - @Test public void testOnMessageNewMessageWithExistingTaskSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getMessage()); }; @@ -220,38 +194,9 @@ public void testOnMessageNewMessageWithExistingTaskSuccess() { SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); SendMessageResponse response = handler.onMessageSend(request, callContext); assertNull(response.getError()); - // The Python implementation returns a Task here, but then again they are using hardcoded mocks and - // bypassing the whole EventQueue. - // If we were to send a Task in agentExecutorExecute EventConsumer.consumeAll() would not exit due to - // the Task not having a 'final' state - // - // See testOnMessageNewMessageWithExistingTaskSuccessMocks() for a test more similar to the Python implementation Assertions.assertSame(message, response.getResult()); } - @Test - public void testOnMessageNewMessageWithExistingTaskSuccessMocks() { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); - - Message message = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.id()) - .contextId(MINIMAL_TASK.contextId()) - .build(); - SendMessageRequest request = new SendMessageRequest("1", new MessageSendParams(message, null, null)); - SendMessageResponse response; - try (MockedConstruction mocked = Mockito.mockConstruction( - EventConsumer.class, - (mock, context) -> { - Mockito.doReturn(ZeroPublisher.fromItems(wrapEvent(MINIMAL_TASK))).when(mock).consumeAll(); - })) { - response = handler.onMessageSend(request, callContext); - } - assertNull(response.getError()); - Assertions.assertSame(MINIMAL_TASK, response.getResult()); - - } - @Test public void testOnMessageError() { // See testMessageOnErrorMocks() for a test more similar to the Python implementation, using mocks for @@ -352,9 +297,11 @@ public void onComplete() { @Test public void testOnMessageStreamNewMessageMultipleEventsSuccess() throws InterruptedException { + // Note: Do NOT set callback - DefaultRequestHandler has a permanent callback + // We'll verify persistence by checking TaskStore after streaming completes JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - // Create multiple events to be sent during streaming + // Create multiple events to be sent during streaming Task taskEvent = Task.builder(MINIMAL_TASK) .status(new TaskStatus(TaskState.WORKING)) .build(); @@ -429,8 +376,8 @@ public void onComplete() { } }); - // Wait for all events to be received - Assertions.assertTrue(latch.await(2, TimeUnit.SECONDS), + // Wait for all events to be received (increased timeout for async processing) + assertTrue(latch.await(10, TimeUnit.SECONDS), "Expected to receive 3 events within timeout"); // Assert no error occurred during streaming @@ -456,6 +403,17 @@ public void onComplete() { "Third event should be a TaskStatusUpdateEvent"); assertEquals(MINIMAL_TASK.id(), receivedStatus.taskId()); assertEquals(TaskState.COMPLETED, receivedStatus.status().state()); + + // Verify events were persisted to TaskStore (poll for final state) + for (int i = 0; i < 50; i++) { + Task storedTask = taskStore.get(MINIMAL_TASK.id()); + if (storedTask != null && storedTask.status() != null + && TaskState.COMPLETED.equals(storedTask.status().state())) { + return; // Success - task finalized in TaskStore + } + Thread.sleep(100); + } + fail("Task should have been finalized in TaskStore within timeout"); } @Test @@ -538,7 +496,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccess() throws Exception Task task = Task.builder(MINIMAL_TASK) .history(new ArrayList<>()) .build(); - taskStore.save(task); + taskStore.save(task, false); Message message = Message.builder(MESSAGE) .taskId(task.id()) @@ -583,7 +541,7 @@ public void onComplete() { }); }); - Assertions.assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(latch.await(1, TimeUnit.SECONDS)); subscriptionRef.get().cancel(); // The Python implementation has several events emitted since it uses mocks. // @@ -608,7 +566,7 @@ public void testOnMessageStreamNewMessageExistingTaskSuccessMocks() { Task task = Task.builder(MINIMAL_TASK) .history(new ArrayList<>()) .build(); - taskStore.save(task); + taskStore.save(task, false); // This is used to send events from a mock List events = List.of( @@ -682,7 +640,7 @@ public void onComplete() { @Test public void testSetPushNotificationConfigSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( @@ -704,7 +662,7 @@ public void testSetPushNotificationConfigSuccess() { @Test public void testGetPushNotificationConfigSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -729,112 +687,124 @@ public void testGetPushNotificationConfigSuccess() { @Test public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Exception { - JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); - - List events = List.of( - MINIMAL_TASK, - TaskArtifactUpdateEvent.builder() - .taskId(MINIMAL_TASK.id()) - .contextId(MINIMAL_TASK.contextId()) - .artifact(Artifact.builder() - .artifactId("11") - .parts(new TextPart("text")) - .build()) - .build(), - TaskStatusUpdateEvent.builder() - .taskId(MINIMAL_TASK.id()) - .contextId(MINIMAL_TASK.contextId()) - .status(new TaskStatus(TaskState.COMPLETED)) - .build()); - - agentExecutorExecute = (context, eventQueue) -> { - // Hardcode the events to send here - for (Event event : events) { - eventQueue.enqueueEvent(event); - } - }; - - TaskPushNotificationConfig config = new TaskPushNotificationConfig( - MINIMAL_TASK.id(), - PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); - - SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); - SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); - assertNull(stpnResponse.getError()); - - Message msg = Message.builder(MESSAGE) - .taskId(MINIMAL_TASK.id()) - .build(); - SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(msg, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request, callContext); - - final List results = Collections.synchronizedList(new ArrayList<>()); - final AtomicReference subscriptionRef = new AtomicReference<>(); - final CountDownLatch latch = new CountDownLatch(6); - httpClient.latch = latch; - - Executors.newSingleThreadExecutor().execute(() -> { - response.subscribe(new Flow.Subscriber<>() { - @Override - public void onSubscribe(Flow.Subscription subscription) { - subscriptionRef.set(subscription); - subscription.request(1); - } - - @Override - public void onNext(SendStreamingMessageResponse item) { - System.out.println("-> " + item.getResult()); - results.add(item.getResult()); - System.out.println(results); - subscriptionRef.get().request(1); - latch.countDown(); - } - - @Override - public void onError(Throwable throwable) { - subscriptionRef.get().cancel(); - } - - @Override - public void onComplete() { - subscriptionRef.get().cancel(); + // Note: Do NOT set callback - DefaultRequestHandler has a permanent callback + + // Use synchronous executor for push notifications to ensure deterministic ordering + // Without this, async push notifications can execute out of order, causing test flakiness + mainEventBusProcessor.setPushNotificationExecutor(Runnable::run); + + try { + JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); + taskStore.save(MINIMAL_TASK, false); + + List events = List.of( + MINIMAL_TASK, + TaskArtifactUpdateEvent.builder() + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) + .artifact(Artifact.builder() + .artifactId("11") + .parts(new TextPart("text")) + .build()) + .build(), + TaskStatusUpdateEvent.builder() + .taskId(MINIMAL_TASK.id()) + .contextId(MINIMAL_TASK.contextId()) + .status(new TaskStatus(TaskState.COMPLETED)) + .build()); + + + agentExecutorExecute = (context, eventQueue) -> { + // Hardcode the events to send here + for (Event event : events) { + eventQueue.enqueueEvent(event); } + }; + + TaskPushNotificationConfig config = new TaskPushNotificationConfig( + MINIMAL_TASK.id(), + PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); + + SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); + SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); + assertNull(stpnResponse.getError()); + + Message msg = Message.builder(MESSAGE) + .taskId(MINIMAL_TASK.id()) + .build(); + SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(msg, null, null)); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); + + final List results = Collections.synchronizedList(new ArrayList<>()); + final AtomicReference subscriptionRef = new AtomicReference<>(); + final CountDownLatch latch = new CountDownLatch(6); + httpClient.latch = latch; + + Executors.newSingleThreadExecutor().execute(() -> { + response.subscribe(new Flow.Subscriber<>() { + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscriptionRef.set(subscription); + subscription.request(1); + } + + @Override + public void onNext(SendStreamingMessageResponse item) { + System.out.println("-> " + item.getResult()); + results.add(item.getResult()); + System.out.println(results); + subscriptionRef.get().request(1); + latch.countDown(); + } + + @Override + public void onError(Throwable throwable) { + subscriptionRef.get().cancel(); + } + + @Override + public void onComplete() { + subscriptionRef.get().cancel(); + } + }); }); - }); - Assertions.assertTrue(latch.await(5, TimeUnit.SECONDS)); - subscriptionRef.get().cancel(); - assertEquals(3, results.size()); - assertEquals(3, httpClient.tasks.size()); - - Task curr = httpClient.tasks.get(0); - assertEquals(MINIMAL_TASK.id(), curr.id()); - assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); - assertEquals(MINIMAL_TASK.status().state(), curr.status().state()); - assertEquals(0, curr.artifacts() == null ? 0 : curr.artifacts().size()); - - curr = httpClient.tasks.get(1); - assertEquals(MINIMAL_TASK.id(), curr.id()); - assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); - assertEquals(MINIMAL_TASK.status().state(), curr.status().state()); - assertEquals(1, curr.artifacts().size()); - assertEquals(1, curr.artifacts().get(0).parts().size()); - assertEquals("text", ((TextPart) curr.artifacts().get(0).parts().get(0)).text()); - - curr = httpClient.tasks.get(2); - assertEquals(MINIMAL_TASK.id(), curr.id()); - assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); - assertEquals(TaskState.COMPLETED, curr.status().state()); - assertEquals(1, curr.artifacts().size()); - assertEquals(1, curr.artifacts().get(0).parts().size()); - assertEquals("text", ((TextPart) curr.artifacts().get(0).parts().get(0)).text()); + assertTrue(latch.await(5, TimeUnit.SECONDS)); + + subscriptionRef.get().cancel(); + assertEquals(3, results.size()); + assertEquals(3, httpClient.tasks.size()); + + Task curr = httpClient.tasks.get(0); + assertEquals(MINIMAL_TASK.id(), curr.id()); + assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); + assertEquals(MINIMAL_TASK.status().state(), curr.status().state()); + assertEquals(0, curr.artifacts() == null ? 0 : curr.artifacts().size()); + + curr = httpClient.tasks.get(1); + assertEquals(MINIMAL_TASK.id(), curr.id()); + assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); + assertEquals(MINIMAL_TASK.status().state(), curr.status().state()); + assertEquals(1, curr.artifacts().size()); + assertEquals(1, curr.artifacts().get(0).parts().size()); + assertEquals("text", ((TextPart) curr.artifacts().get(0).parts().get(0)).text()); + + curr = httpClient.tasks.get(2); + assertEquals(MINIMAL_TASK.id(), curr.id()); + assertEquals(MINIMAL_TASK.contextId(), curr.contextId()); + assertEquals(TaskState.COMPLETED, curr.status().state()); + assertEquals(1, curr.artifacts().size()); + assertEquals(1, curr.artifacts().get(0).parts().size()); + assertEquals("text", ((TextPart) curr.artifacts().get(0).parts().get(0)).text()); + } finally { + mainEventBusProcessor.setPushNotificationExecutor(null); + } } @Test public void testOnResubscribeExistingTaskSuccess() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); queueManager.createOrTap(MINIMAL_TASK.id()); agentExecutorExecute = (context, eventQueue) -> { @@ -861,6 +831,7 @@ public void testOnResubscribeExistingTaskSuccess() { CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); + AtomicBoolean receivedInitialTask = new AtomicBoolean(false); response.subscribe(new Flow.Subscriber<>() { private Flow.Subscription subscription; @@ -873,7 +844,20 @@ public void onSubscribe(Flow.Subscription subscription) { @Override public void onNext(SendStreamingMessageResponse item) { - results.add(item.getResult()); + StreamingEventKind event = item.getResult(); + results.add(event); + + // Per A2A Protocol Spec 3.1.6: ENFORCE that first event is Task + if (!receivedInitialTask.get()) { + assertTrue(event instanceof Task, + "First event on resubscribe MUST be Task (current state), but was: " + event.getClass().getSimpleName()); + receivedInitialTask.set(true); + } else { + // Subsequent events should be the expected type (Message in this case) + assertTrue(event instanceof Message, + "Expected Message after initial Task, but was: " + event.getClass().getSimpleName()); + } + subscription.request(1); } @@ -892,16 +876,15 @@ public void onComplete() { future.join(); - // The Python implementation has several events emitted since it uses mocks. - // - // See testOnMessageStreamNewMessageExistingTaskSuccessMocks() for a test more similar to the Python implementation - assertEquals(1, results.size()); + // Verify we received exactly 2 events and the initial Task was received + assertEquals(2, results.size()); + assertTrue(receivedInitialTask.get(), "Should have received initial Task event"); } @Test public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); queueManager.createOrTap(MINIMAL_TASK.id()); List events = List.of( @@ -1060,7 +1043,7 @@ public void onComplete() { if (results.get(0).getError() != null && results.get(0).getError() instanceof InvalidRequestError ire) { assertEquals("Streaming is not supported by the agent", ire.getMessage()); } else { - Assertions.fail("Expected a response containing an error"); + fail("Expected a response containing an error"); } } @@ -1107,7 +1090,7 @@ public void onComplete() { if (results.get(0).getError() != null && results.get(0).getError() instanceof InvalidRequestError ire) { assertEquals("Streaming is not supported by the agent", ire.getMessage()); } else { - Assertions.fail("Expected a response containing an error"); + fail("Expected a response containing an error"); } } @@ -1115,7 +1098,7 @@ public void onComplete() { public void testPushNotificationsNotSupportedError() { AgentCard card = createAgentCard(true, false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); TaskPushNotificationConfig config = new TaskPushNotificationConfig( @@ -1135,12 +1118,11 @@ public void testPushNotificationsNotSupportedError() { @Test public void testOnGetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); GetTaskPushNotificationConfigRequest request = new GetTaskPushNotificationConfigRequest("id", new GetTaskPushNotificationConfigParams(MINIMAL_TASK.id())); @@ -1154,12 +1136,11 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { @Test public void testOnSetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); TaskPushNotificationConfig config = new TaskPushNotificationConfig( @@ -1246,12 +1227,11 @@ public void testDefaultRequestHandlerWithCustomComponents() { @Test public void testOnMessageSendErrorHandling() { - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); AgentCard card = createAgentCard(false, true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); Message message = Message.builder(MESSAGE) .taskId(MINIMAL_TASK.id()) @@ -1280,7 +1260,7 @@ public void testOnMessageSendErrorHandling() { @Test public void testOnMessageSendTaskIdMismatch() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = ((context, eventQueue) -> { eventQueue.enqueueEvent(MINIMAL_TASK); @@ -1293,16 +1273,17 @@ public void testOnMessageSendTaskIdMismatch() { } @Test - public void testOnMessageStreamTaskIdMismatch() { + public void testOnMessageStreamTaskIdMismatch() throws InterruptedException { + // Note: Do NOT set callback - DefaultRequestHandler has a permanent callback JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); - agentExecutorExecute = ((context, eventQueue) -> { - eventQueue.enqueueEvent(MINIMAL_TASK); - }); + agentExecutorExecute = ((context, eventQueue) -> { + eventQueue.enqueueEvent(MINIMAL_TASK); + }); - SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); - Flow.Publisher response = handler.onMessageSendStream(request, callContext); + SendStreamingMessageRequest request = new SendStreamingMessageRequest("1", new MessageSendParams(MESSAGE, null, null)); + Flow.Publisher response = handler.onMessageSendStream(request, callContext); CompletableFuture future = new CompletableFuture<>(); List results = new ArrayList<>(); @@ -1347,7 +1328,7 @@ public void onComplete() { @Test public void testListPushNotificationConfig() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1378,7 +1359,7 @@ public void testListPushNotificationConfig() { public void testListPushNotificationConfigNotSupported() { AgentCard card = createAgentCard(true, false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1404,10 +1385,9 @@ public void testListPushNotificationConfigNotSupported() { @Test public void testListPushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1442,7 +1422,7 @@ public void testListPushNotificationConfigTaskNotFound() { @Test public void testDeletePushNotificationConfig() { JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1470,7 +1450,7 @@ public void testDeletePushNotificationConfig() { public void testDeletePushNotificationConfigNotSupported() { AgentCard card = createAgentCard(true, false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1496,10 +1476,10 @@ public void testDeletePushNotificationConfigNotSupported() { @Test public void testDeletePushNotificationConfigNoPushConfigStore() { - DefaultRequestHandler requestHandler = DefaultRequestHandler.create( - executor, taskStore, queueManager, null, null, internalExecutor); + DefaultRequestHandler requestHandler = + DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); JSONRPCHandler handler = new JSONRPCHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { eventQueue.enqueueEvent(context.getTask() != null ? context.getTask() : context.getMessage()); }; @@ -1593,7 +1573,7 @@ public void onComplete() { }); // The main thread should not be blocked - we should be able to continue immediately - Assertions.assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), + assertTrue(streamStarted.await(100, TimeUnit.MILLISECONDS), "Streaming subscription should start quickly without blocking main thread"); // This proves the main thread is not blocked - we can do other work @@ -1603,11 +1583,11 @@ public void onComplete() { mainThreadBlocked.set(false); // If we get here, main thread was not blocked // Wait for the actual event processing to complete - Assertions.assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), + assertTrue(eventProcessed.await(2, TimeUnit.SECONDS), "Event should be processed within reasonable time"); // Verify we received the event and main thread was not blocked - Assertions.assertTrue(eventReceived.get(), "Should have received streaming event"); + assertTrue(eventReceived.get(), "Should have received streaming event"); Assertions.assertFalse(mainThreadBlocked.get(), "Main thread should not have been blocked"); } @@ -1646,7 +1626,7 @@ public void testExtensionSupportRequiredErrorOnMessageSend() { SendMessageResponse response = handler.onMessageSend(request, callContext); assertInstanceOf(ExtensionSupportRequiredError.class, response.getError()); - Assertions.assertTrue(response.getError().getMessage().contains("https://example.com/test-extension")); + assertTrue(response.getError().getMessage().contains("https://example.com/test-extension")); assertNull(response.getResult()); } @@ -1715,7 +1695,7 @@ public void onComplete() { assertEquals(1, results.size()); assertInstanceOf(ExtensionSupportRequiredError.class, results.get(0).getError()); - Assertions.assertTrue(results.get(0).getError().getMessage().contains("https://example.com/streaming-extension")); + assertTrue(results.get(0).getError().getMessage().contains("https://example.com/streaming-extension")); assertNull(results.get(0).getResult()); } @@ -1805,7 +1785,7 @@ public void testVersionNotSupportedErrorOnMessageSend() { SendMessageResponse response = handler.onMessageSend(request, contextWithVersion); assertInstanceOf(VersionNotSupportedError.class, response.getError()); - Assertions.assertTrue(response.getError().getMessage().contains("2.0")); + assertTrue(response.getError().getMessage().contains("2.0")); assertNull(response.getResult()); } @@ -1876,12 +1856,12 @@ public void onComplete() { }); // Wait for async processing - Assertions.assertTrue(latch.await(2, TimeUnit.SECONDS), "Expected to receive error event within timeout"); + assertTrue(latch.await(2, TimeUnit.SECONDS), "Expected to receive error event within timeout"); assertEquals(1, results.size()); SendStreamingMessageResponse result = results.get(0); assertInstanceOf(VersionNotSupportedError.class, result.getError()); - Assertions.assertTrue(result.getError().getMessage().contains("2.0")); + assertTrue(result.getError().getMessage().contains("2.0")); assertNull(result.getResult()); } diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 3ffb56c5f..3273d6119 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -399,32 +399,46 @@ private Flow.Publisher convertToSendStreamingMessageResponse( Flow.Publisher publisher) { // We can't use the normal convertingProcessor since that propagates any errors as an error handled // via Subscriber.onError() rather than as part of the SendStreamingResponse payload + log.log(Level.FINE, "REST: convertToSendStreamingMessageResponse called, creating ZeroPublisher"); return ZeroPublisher.create(createTubeConfig(), tube -> { + log.log(Level.FINE, "REST: ZeroPublisher tube created, starting CompletableFuture.runAsync"); CompletableFuture.runAsync(() -> { + log.log(Level.FINE, "REST: Inside CompletableFuture, subscribing to EventKind publisher"); publisher.subscribe(new Flow.Subscriber() { Flow.@Nullable Subscription subscription; @Override public void onSubscribe(Flow.Subscription subscription) { + log.log(Level.FINE, "REST: onSubscribe called, storing subscription and requesting first event"); this.subscription = subscription; subscription.request(1); } @Override public void onNext(StreamingEventKind item) { + log.log(Level.FINE, "REST: onNext called with event: {0}", item.getClass().getSimpleName()); try { String payload = JsonFormat.printer().omittingInsignificantWhitespace().print(ProtoUtils.ToProto.taskOrMessageStream(item)); + log.log(Level.FINE, "REST: Converted to JSON, sending via tube: {0}", payload.substring(0, Math.min(100, payload.length()))); tube.send(payload); + log.log(Level.FINE, "REST: tube.send() completed, requesting next event from EventConsumer"); + // Request next event from EventConsumer (Chain 1: EventConsumer → RestHandler) + // This is safe because ZeroPublisher buffers items + // Chain 2 (ZeroPublisher → MultiSseSupport) controls actual delivery via request(1) in onWriteDone() if (subscription != null) { subscription.request(1); + } else { + log.log(Level.WARNING, "REST: subscription is null in onNext!"); } } catch (InvalidProtocolBufferException ex) { + log.log(Level.SEVERE, "REST: JSON conversion failed", ex); onError(ex); } } @Override public void onError(Throwable throwable) { + log.log(Level.SEVERE, "REST: onError called", throwable); if (throwable instanceof A2AError jsonrpcError) { tube.send(new HTTPRestErrorResponse(jsonrpcError).toJson()); } else { @@ -435,6 +449,7 @@ public void onError(Throwable throwable) { @Override public void onComplete() { + log.log(Level.FINE, "REST: onComplete called, calling tube.complete()"); tube.complete(); } }); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 7d930415b..db3ff97aa 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -30,7 +30,7 @@ public class RestHandlerTest extends AbstractA2ARequestHandlerTest { @Test public void testGetTaskSuccess() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); RestHandler.HTTPRestResponse response = handler.getTask(MINIMAL_TASK.id(), 0, "", callContext); @@ -59,7 +59,7 @@ public void testGetTaskNotFound() { @Test public void testListTasksStatusWireString() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); RestHandler.HTTPRestResponse response = handler.listTasks(null, "submitted", null, null, null, null, null, "", callContext); @@ -162,7 +162,7 @@ public void testSendMessageEmptyBody() { @Test public void testCancelTaskSuccess() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); agentExecutorCancel = (context, eventQueue) -> { // We need to cancel the task or the EventConsumer never finds a 'final' event. @@ -246,7 +246,7 @@ public void testSendStreamingMessageNotSupported() { @Test public void testPushNotificationConfigSuccess() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); String requestBody = """ { @@ -293,7 +293,7 @@ public void testPushNotificationConfigNotSupported() { @Test public void testGetPushNotificationConfig() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); // First, create a push notification config String createRequestBody = """ @@ -322,7 +322,7 @@ public void testGetPushNotificationConfig() { @Test public void testDeletePushNotificationConfig() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); RestHandler.HTTPRestResponse response = handler.deleteTaskPushNotificationConfiguration(MINIMAL_TASK.id(), "default-config-id", "", callContext); Assertions.assertEquals(204, response.getStatusCode()); } @@ -330,7 +330,7 @@ public void testDeletePushNotificationConfig() { @Test public void testListPushNotificationConfigs() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); RestHandler.HTTPRestResponse response = handler.listTaskPushNotificationConfigurations(MINIMAL_TASK.id(), 0, "", "", callContext); @@ -894,7 +894,7 @@ public void testListTasksNegativeTimestampReturns422() { @Test public void testListTasksUnixMillisecondsTimestamp() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); // Unix milliseconds timestamp should be accepted String timestampMillis = String.valueOf(System.currentTimeMillis() - 10000); @@ -909,7 +909,7 @@ public void testListTasksUnixMillisecondsTimestamp() { @Test public void testListTasksProtobufEnumStatus() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); // Protobuf enum format (TASK_STATE_SUBMITTED) should be accepted RestHandler.HTTPRestResponse response = handler.listTasks(null, "TASK_STATE_SUBMITTED", null, null, @@ -923,7 +923,7 @@ public void testListTasksProtobufEnumStatus() { @Test public void testListTasksEnumConstantStatus() { RestHandler handler = new RestHandler(CARD, requestHandler, internalExecutor); - taskStore.save(MINIMAL_TASK); + taskStore.save(MINIMAL_TASK, false); // Enum constant format (SUBMITTED) should be accepted RestHandler.HTTPRestResponse response = handler.listTasks(null, "SUBMITTED", null, null, From 1c2af659792d85026768132449b9559292beb243 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 5 Feb 2026 11:21:25 +0000 Subject: [PATCH 271/493] feat: Replace quarkus-rest(-client)-jackson with quarkus-rest(-client) (#581) We no longer use Jackson --- boms/reference/src/it/reference-usage-test/pom.xml | 4 ++-- examples/helloworld/server/pom.xml | 2 +- extras/push-notification-config-store-database-jpa/pom.xml | 2 +- extras/task-store-database-jpa/pom.xml | 2 +- reference/common/pom.xml | 2 +- reference/jsonrpc/pom.xml | 2 +- reference/rest/pom.xml | 2 +- tck/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boms/reference/src/it/reference-usage-test/pom.xml b/boms/reference/src/it/reference-usage-test/pom.xml index 2e000a8a8..5e7a500f3 100644 --- a/boms/reference/src/it/reference-usage-test/pom.xml +++ b/boms/reference/src/it/reference-usage-test/pom.xml @@ -103,11 +103,11 @@ io.quarkus - quarkus-resteasy-jackson + quarkus-grpc io.quarkus - quarkus-grpc + quarkus-rest diff --git a/examples/helloworld/server/pom.xml b/examples/helloworld/server/pom.xml index 4b0ea1564..699adf4d4 100644 --- a/examples/helloworld/server/pom.xml +++ b/examples/helloworld/server/pom.xml @@ -26,7 +26,7 @@ io.quarkus - quarkus-resteasy-jackson + quarkus-resteasy provided diff --git a/extras/push-notification-config-store-database-jpa/pom.xml b/extras/push-notification-config-store-database-jpa/pom.xml index b944295b3..4973fd8e8 100644 --- a/extras/push-notification-config-store-database-jpa/pom.xml +++ b/extras/push-notification-config-store-database-jpa/pom.xml @@ -55,7 +55,7 @@ io.quarkus - quarkus-rest-client-jackson + quarkus-rest-client test diff --git a/extras/task-store-database-jpa/pom.xml b/extras/task-store-database-jpa/pom.xml index b98aa7bf9..ff99cf514 100644 --- a/extras/task-store-database-jpa/pom.xml +++ b/extras/task-store-database-jpa/pom.xml @@ -59,7 +59,7 @@ io.quarkus - quarkus-rest-client-jackson + quarkus-rest-client test diff --git a/reference/common/pom.xml b/reference/common/pom.xml index e05266922..b9e8e57a7 100644 --- a/reference/common/pom.xml +++ b/reference/common/pom.xml @@ -60,7 +60,7 @@ io.quarkus - quarkus-rest-client-jackson + quarkus-rest-client test diff --git a/reference/jsonrpc/pom.xml b/reference/jsonrpc/pom.xml index 37252458e..c23c817fd 100644 --- a/reference/jsonrpc/pom.xml +++ b/reference/jsonrpc/pom.xml @@ -74,7 +74,7 @@ io.quarkus - quarkus-rest-client-jackson + quarkus-rest-client test diff --git a/reference/rest/pom.xml b/reference/rest/pom.xml index 318c85e6c..796ff88fe 100644 --- a/reference/rest/pom.xml +++ b/reference/rest/pom.xml @@ -84,7 +84,7 @@ io.quarkus - quarkus-rest-client-jackson + quarkus-rest-client test diff --git a/tck/pom.xml b/tck/pom.xml index 39c289c72..7b2db1b81 100644 --- a/tck/pom.xml +++ b/tck/pom.xml @@ -30,7 +30,7 @@ io.quarkus - quarkus-rest-jackson + quarkus-rest provided From 168ff2dac4209c177e6f026f47b06c549c9d18d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 5 Feb 2026 12:21:45 +0100 Subject: [PATCH 272/493] refactor: extract shared SSE event handling into SPI module (#624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create client-transport-spi module with AbstractSSEEventListener to eliminate code duplication between jsonrpc and rest transport implementations. - Add AbstractSSEEventListener with common event/error handling logic - Refactor jsonrpc and rest SSEEventListener to extend base class - Add comprehensive test coverage for shared functionality Fixes #623 🦕 Signed-off-by: Emmanuel Hugonnet --- .gitignore | 1 + .../jsonrpc/sse/SSEEventListener.java | 66 ++-- .../jsonrpc/sse/SSEEventListenerTest.java | 15 + .../client/transport/rest/RestTransport.java | 6 +- .../rest/sse/RestSSEEventListener.java | 87 ----- .../transport/rest/sse/SSEEventListener.java | 73 ++++ .../rest/sse/SSEEventListenerTest.java | 344 ++++++++++++++++++ .../spi/sse/AbstractSSEEventListener.java | 120 ++++++ .../transport/spi/sse/package-info.java | 5 + .../spi/sse/SSEEventListenerTest.java | 295 +++++++++++++++ 10 files changed, 879 insertions(+), 133 deletions(-) delete mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java create mode 100644 client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/SSEEventListener.java create mode 100644 client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/AbstractSSEEventListener.java create mode 100644 client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/package-info.java create mode 100644 client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java diff --git a/.gitignore b/.gitignore index c95331d77..143f602c6 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,5 @@ nbactions.xml # Private Claude config .claude/ .serena/ +.bob/ claudedocs diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java index 3a24f5145..2c61d082e 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListener.java @@ -4,42 +4,32 @@ import java.util.function.Consumer; import java.util.logging.Logger; +import io.a2a.client.transport.spi.sse.AbstractSSEEventListener; import io.a2a.grpc.StreamResponse; import io.a2a.grpc.utils.JSONRPCUtils; import io.a2a.grpc.utils.ProtoUtils; import io.a2a.jsonrpc.common.json.JsonProcessingException; import io.a2a.spec.A2AError; import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.Task; -import io.a2a.spec.TaskState; -import io.a2a.spec.TaskStatusUpdateEvent; import org.jspecify.annotations.Nullable; -public class SSEEventListener { +/** + * JSON-RPC transport implementation of SSE event listener. + * Handles parsing of JSON-RPC formatted messages from SSE streams. + */ +public class SSEEventListener extends AbstractSSEEventListener { private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); - private final Consumer eventHandler; - private final @Nullable - Consumer errorHandler; private volatile boolean completed = false; public SSEEventListener(Consumer eventHandler, @Nullable Consumer errorHandler) { - this.eventHandler = eventHandler; - this.errorHandler = errorHandler; + super(eventHandler, errorHandler); } + @Override public void onMessage(String message, @Nullable Future completableFuture) { - handleMessage(message, completableFuture); - } - - public void onError(Throwable throwable, @Nullable Future future) { - if (errorHandler != null) { - errorHandler.accept(throwable); - } - if (future != null) { - future.cancel(true); // close SSE channel - } + parseAndHandleMessage(message, completableFuture); } public void onComplete() { @@ -52,40 +42,30 @@ public void onComplete() { // Signal normal stream completion (null error means successful completion) log.fine("SSEEventListener.onComplete() called - signaling successful stream completion"); - if (errorHandler != null) { + if (getErrorHandler() != null) { log.fine("Calling errorHandler.accept(null) to signal successful completion"); - errorHandler.accept(null); + getErrorHandler().accept(null); } else { log.warning("errorHandler is null, cannot signal completion"); } } - private void handleMessage(String message, @Nullable Future future) { + /** + * Parses a JSON-RPC message and delegates to the base class for event handling. + * + * @param message The raw JSON-RPC message string + * @param future Optional future for controlling the SSE connection + */ + private void parseAndHandleMessage(String message, @Nullable Future future) { try { StreamResponse response = JSONRPCUtils.parseResponseEvent(message); - StreamingEventKind event = ProtoUtils.FromProto.streamingEventKind(response); - eventHandler.accept(event); - - // Client-side auto-close on final events to prevent connection leaks - // Handles both TaskStatusUpdateEvent and Task objects with final states - // This covers late subscriptions to completed tasks and ensures no connection leaks - boolean shouldClose = false; - if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) { - shouldClose = true; - } else if (event instanceof Task task) { - TaskState state = task.status().state(); - if (state.isFinal()) { - shouldClose = true; - } - } - - if (shouldClose && future != null) { - future.cancel(true); // close SSE channel - } + + // Delegate to base class for common event handling and auto-close logic + handleEvent(event, future); } catch (A2AError error) { - if (errorHandler != null) { - errorHandler.accept(error); + if (getErrorHandler() != null) { + getErrorHandler().accept(error); } } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 17f805312..0817b753f 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -198,7 +198,22 @@ public void testFinalTaskStatusUpdateEventCancels() { error -> {} ); + // Parse the message event JSON + String eventData = JsonStreamingMessages.STREAMING_STATUS_UPDATE_EVENT_FINAL.substring( + JsonStreamingMessages.STREAMING_STATUS_UPDATE_EVENT_FINAL.indexOf("{")); + + // Call onMessage with a cancellable future + CancelCapturingFuture future = new CancelCapturingFuture(); + listener.onMessage(eventData, future); + // Verify the event was received and processed + assertNotNull(receivedEvent.get()); + assertTrue(receivedEvent.get() instanceof TaskStatusUpdateEvent); + TaskStatusUpdateEvent received = (TaskStatusUpdateEvent) receivedEvent.get(); + assertTrue(received.isFinal()); + + // Verify the future was cancelled (auto-close on final event) + assertTrue(future.cancelHandlerCalled); } @Test diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 6513086da..ae01e0f3f 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -31,7 +31,7 @@ import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.http.A2AHttpResponse; -import io.a2a.client.transport.rest.sse.RestSSEEventListener; +import io.a2a.client.transport.rest.sse.SSEEventListener; import io.a2a.client.transport.spi.ClientTransport; import io.a2a.client.transport.spi.interceptors.ClientCallContext; import io.a2a.client.transport.spi.interceptors.ClientCallInterceptor; @@ -110,7 +110,7 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer> ref = new AtomicReference<>(); - RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); + SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { A2AHttpClient.PostBuilder postBuilder = createPostBuilder(Utils.buildBaseUrl(agentInterface, messageSendParams.tenant()) + "/message:stream", payloadAndHeaders); ref.set(postBuilder.postAsyncSSE( @@ -395,7 +395,7 @@ public void resubscribe(TaskIdParams request, Consumer event PayloadAndHeaders payloadAndHeaders = applyInterceptors(SUBSCRIBE_TO_TASK_METHOD, builder, agentCard, context); AtomicReference> ref = new AtomicReference<>(); - RestSSEEventListener sseEventListener = new RestSSEEventListener(eventConsumer, errorConsumer); + SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer); try { String url = Utils.buildBaseUrl(agentInterface, request.tenant()) + String.format("/tasks/%1s:subscribe", request.id()); A2AHttpClient.PostBuilder postBuilder = createPostBuilder(url, payloadAndHeaders); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java deleted file mode 100644 index 85e604da3..000000000 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/RestSSEEventListener.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.a2a.client.transport.rest.sse; - -import java.util.concurrent.Future; -import java.util.function.Consumer; -import java.util.logging.Logger; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.util.JsonFormat; -import io.a2a.client.transport.rest.RestErrorMapper; -import io.a2a.grpc.StreamResponse; -import io.a2a.grpc.utils.ProtoUtils; -import io.a2a.spec.StreamingEventKind; -import io.a2a.spec.Task; -import io.a2a.spec.TaskState; -import io.a2a.spec.TaskStatusUpdateEvent; -import org.jspecify.annotations.Nullable; - -public class RestSSEEventListener { - - private static final Logger log = Logger.getLogger(RestSSEEventListener.class.getName()); - private final Consumer eventHandler; - private final Consumer errorHandler; - - public RestSSEEventListener(Consumer eventHandler, - Consumer errorHandler) { - this.eventHandler = eventHandler; - this.errorHandler = errorHandler; - } - - public void onMessage(String message, @Nullable Future completableFuture) { - try { - log.fine("Streaming message received: " + message); - io.a2a.grpc.StreamResponse.Builder builder = io.a2a.grpc.StreamResponse.newBuilder(); - JsonFormat.parser().merge(message, builder); - handleMessage(builder.build(), completableFuture); - } catch (InvalidProtocolBufferException e) { - errorHandler.accept(RestErrorMapper.mapRestError(message, 500)); - } - } - - public void onError(Throwable throwable, @Nullable Future future) { - if (errorHandler != null) { - errorHandler.accept(throwable); - } - if (future != null) { - future.cancel(true); // close SSE channel - } - } - - private void handleMessage(StreamResponse response, @Nullable Future future) { - StreamingEventKind event; - switch (response.getPayloadCase()) { - case MESSAGE -> - event = ProtoUtils.FromProto.message(response.getMessage()); - case TASK -> - event = ProtoUtils.FromProto.task(response.getTask()); - case STATUS_UPDATE -> - event = ProtoUtils.FromProto.taskStatusUpdateEvent(response.getStatusUpdate()); - case ARTIFACT_UPDATE -> - event = ProtoUtils.FromProto.taskArtifactUpdateEvent(response.getArtifactUpdate()); - default -> { - log.warning("Invalid stream response " + response.getPayloadCase()); - errorHandler.accept(new IllegalStateException("Invalid stream response from server: " + response.getPayloadCase())); - return; - } - } - eventHandler.accept(event); - - // Client-side auto-close on final events to prevent connection leaks - // Handles both TaskStatusUpdateEvent and Task objects with final states - // This covers late subscriptions to completed tasks and ensures no connection leaks - boolean shouldClose = false; - if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) { - shouldClose = true; - } else if (event instanceof Task task) { - TaskState state = task.status().state(); - if (state.isFinal()) { - shouldClose = true; - } - } - - if (shouldClose && future != null) { - future.cancel(true); // close SSE channel - } - } - -} diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/SSEEventListener.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/SSEEventListener.java new file mode 100644 index 000000000..2c9167b63 --- /dev/null +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/sse/SSEEventListener.java @@ -0,0 +1,73 @@ +package io.a2a.client.transport.rest.sse; + +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.a2a.client.transport.spi.sse.AbstractSSEEventListener; +import io.a2a.client.transport.rest.RestErrorMapper; +import io.a2a.grpc.StreamResponse; +import io.a2a.grpc.utils.ProtoUtils; +import io.a2a.spec.StreamingEventKind; +import org.jspecify.annotations.Nullable; + +/** + * REST transport implementation of SSE event listener. + * Handles parsing of JSON-formatted protobuf messages from REST SSE streams. + */ +public class SSEEventListener extends AbstractSSEEventListener { + + private static final Logger log = Logger.getLogger(SSEEventListener.class.getName()); + + public SSEEventListener(Consumer eventHandler, + @Nullable Consumer errorHandler) { + super(eventHandler, errorHandler); + } + + @Override + public void onMessage(String message, @Nullable Future completableFuture) { + try { + log.fine("Streaming message received: " + message); + io.a2a.grpc.StreamResponse.Builder builder = io.a2a.grpc.StreamResponse.newBuilder(); + JsonFormat.parser().merge(message, builder); + parseAndHandleMessage(builder.build(), completableFuture); + } catch (InvalidProtocolBufferException e) { + if (getErrorHandler() != null) { + getErrorHandler().accept(RestErrorMapper.mapRestError(message, 500)); + } + } + } + + /** + * Parses a StreamResponse protobuf message and delegates to the base class for event handling. + * + * @param response The parsed StreamResponse + * @param future Optional future for controlling the SSE connection + */ + private void parseAndHandleMessage(StreamResponse response, @Nullable Future future) { + StreamingEventKind event; + switch (response.getPayloadCase()) { + case MESSAGE -> + event = ProtoUtils.FromProto.message(response.getMessage()); + case TASK -> + event = ProtoUtils.FromProto.task(response.getTask()); + case STATUS_UPDATE -> + event = ProtoUtils.FromProto.taskStatusUpdateEvent(response.getStatusUpdate()); + case ARTIFACT_UPDATE -> + event = ProtoUtils.FromProto.taskArtifactUpdateEvent(response.getArtifactUpdate()); + default -> { + log.warning("Invalid stream response " + response.getPayloadCase()); + if (getErrorHandler() != null) { + getErrorHandler().accept(new IllegalStateException("Invalid stream response from server: " + response.getPayloadCase())); + } + return; + } + } + + // Delegate to base class for common event handling and auto-close logic + handleEvent(event, future); + } + +} diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java new file mode 100644 index 000000000..1a2bd7f26 --- /dev/null +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java @@ -0,0 +1,344 @@ +package io.a2a.client.transport.rest.sse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import io.a2a.spec.Message; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import org.junit.jupiter.api.Test; + +/** + * Tests for REST transport SSEEventListener. + * Tests JSON parsing of protobuf messages and event handling. + */ +public class SSEEventListenerTest { + + /** + * Mock Future implementation that captures cancel calls. + */ + private static class CancelCapturingFuture implements Future { + private boolean cancelHandlerCalled = false; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + cancelHandlerCalled = true; + return true; + } + + @Override + public boolean isCancelled() { + return cancelHandlerCalled; + } + + @Override + public boolean isDone() { + return false; + } + + @Override + public Void get() { + return null; + } + + @Override + public Void get(long timeout, TimeUnit unit) { + return null; + } + + public boolean wasCancelled() { + return cancelHandlerCalled; + } + } + + @Test + public void testOnMessageWithTaskResult() { + // Set up event handler + AtomicReference receivedEvent = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> receivedEvent.set(event), + error -> {} + ); + + // JSON format from REST SSE stream (protobuf as JSON) + String jsonMessage = """ + { + "task": { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "TASK_STATE_WORKING" + } + } + } + """; + + // Call the onMessage method + listener.onMessage(jsonMessage, null); + + // Verify the event was processed correctly + assertNotNull(receivedEvent.get()); + assertTrue(receivedEvent.get() instanceof Task); + Task task = (Task) receivedEvent.get(); + assertEquals("task-123", task.id()); + assertEquals("context-456", task.contextId()); + assertEquals(TaskState.WORKING, task.status().state()); + } + + @Test + public void testOnMessageWithMessageResult() { + // Set up event handler + AtomicReference receivedEvent = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> receivedEvent.set(event), + error -> {} + ); + + // JSON format from REST SSE stream + String jsonMessage = """ + { + "message": { + "role": "ROLE_AGENT", + "messageId": "msg-123", + "contextId": "context-456", + "parts": [ + { + "text": "Hello, world!" + } + ] + } + } + """; + + // Call onMessage method + listener.onMessage(jsonMessage, null); + + // Verify the event was processed correctly + assertNotNull(receivedEvent.get()); + assertTrue(receivedEvent.get() instanceof Message); + Message message = (Message) receivedEvent.get(); + assertEquals(Message.Role.AGENT, message.role()); + assertEquals("msg-123", message.messageId()); + assertEquals("context-456", message.contextId()); + assertEquals(1, message.parts().size()); + assertTrue(message.parts().get(0) instanceof TextPart); + assertEquals("Hello, world!", ((TextPart) message.parts().get(0)).text()); + } + + @Test + public void testOnMessageWithStatusUpdateEvent() { + // Set up event handler + AtomicReference receivedEvent = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> receivedEvent.set(event), + error -> {} + ); + + // JSON format from REST SSE stream + String jsonMessage = """ + { + "statusUpdate": { + "taskId": "1", + "contextId": "2", + "status": { + "state": "TASK_STATE_SUBMITTED" + }, + "final": false + } + } + """; + + // Call onMessage method + listener.onMessage(jsonMessage, null); + + // Verify the event was processed correctly + assertNotNull(receivedEvent.get()); + assertTrue(receivedEvent.get() instanceof TaskStatusUpdateEvent); + TaskStatusUpdateEvent taskStatusUpdateEvent = (TaskStatusUpdateEvent) receivedEvent.get(); + assertEquals("1", taskStatusUpdateEvent.taskId()); + assertEquals("2", taskStatusUpdateEvent.contextId()); + assertEquals(TaskState.SUBMITTED, taskStatusUpdateEvent.status().state()); + assertEquals(false, taskStatusUpdateEvent.isFinal()); + } + + @Test + public void testOnMessageWithFinalStatusUpdateEventCancels() { + // Set up event handler + AtomicReference receivedEvent = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> receivedEvent.set(event), + error -> {} + ); + + // JSON format from REST SSE stream with final status + String jsonMessage = """ + { + "statusUpdate": { + "taskId": "1", + "contextId": "2", + "status": { + "state": "TASK_STATE_COMPLETED" + }, + "final": true + } + } + """; + + // Call onMessage with a cancellable future + CancelCapturingFuture future = new CancelCapturingFuture(); + listener.onMessage(jsonMessage, future); + + // Verify the event was received and processed + assertNotNull(receivedEvent.get()); + assertTrue(receivedEvent.get() instanceof TaskStatusUpdateEvent); + TaskStatusUpdateEvent received = (TaskStatusUpdateEvent) receivedEvent.get(); + assertTrue(received.isFinal()); + assertEquals(TaskState.COMPLETED, received.status().state()); + + // Verify the future was cancelled (auto-close on final event) + assertTrue(future.wasCancelled()); + } + + @Test + public void testOnMessageWithCompletedTaskCancels() { + // Set up event handler + AtomicReference receivedEvent = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> receivedEvent.set(event), + error -> {} + ); + + // JSON format from REST SSE stream with completed task + String jsonMessage = """ + { + "task": { + "id": "task-123", + "contextId": "context-456", + "status": { + "state": "TASK_STATE_COMPLETED" + } + } + } + """; + + // Call onMessage with a cancellable future + CancelCapturingFuture future = new CancelCapturingFuture(); + listener.onMessage(jsonMessage, future); + + // Verify the event was received + assertNotNull(receivedEvent.get()); + assertTrue(receivedEvent.get() instanceof Task); + Task task = (Task) receivedEvent.get(); + assertEquals(TaskState.COMPLETED, task.status().state()); + + // Verify the future was cancelled (auto-close on final task state) + assertTrue(future.wasCancelled()); + } + + @Test + public void testOnMessageWithInvalidJsonCallsErrorHandler() { + // Set up error handler + AtomicReference receivedError = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> {}, + error -> receivedError.set(error) + ); + + // Invalid JSON message + String invalidJson = "{ invalid json }"; + + // Call onMessage + listener.onMessage(invalidJson, null); + + // Verify error handler was called + assertNotNull(receivedError.get()); + } + + @Test + public void testOnMessageWithInvalidPayloadCaseCallsErrorHandler() { + // Set up error handler + AtomicReference receivedError = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> {}, + error -> receivedError.set(error) + ); + + // JSON with no recognized payload type (empty object) + String jsonMessage = "{}"; + + // Call onMessage + listener.onMessage(jsonMessage, null); + + // Verify error handler was called + assertNotNull(receivedError.get()); + assertTrue(receivedError.get() instanceof IllegalStateException); + assertTrue(receivedError.get().getMessage().contains("Invalid stream response")); + } + + @Test + public void testOnErrorCallsErrorHandler() { + // Set up error handler + AtomicReference receivedError = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> {}, + error -> receivedError.set(error) + ); + + // Create a test error + IllegalStateException testError = new IllegalStateException("Test error"); + + // Call onError + listener.onError(testError, null); + + // Verify error handler was called + assertNotNull(receivedError.get()); + assertEquals(testError, receivedError.get()); + } + + @Test + public void testOnErrorCancelsFuture() { + // Set up error handler + AtomicBoolean errorHandlerCalled = new AtomicBoolean(false); + SSEEventListener listener = new SSEEventListener( + event -> {}, + error -> errorHandlerCalled.set(true) + ); + + // Create a cancel-capturing future + CancelCapturingFuture future = new CancelCapturingFuture(); + + // Call onError with a future + listener.onError(new RuntimeException("Test error"), future); + + // Verify both error handler was called and future was cancelled + assertTrue(errorHandlerCalled.get()); + assertTrue(future.wasCancelled()); + } + + @Test + public void testOnMessageWithNullErrorHandler() { + // Set up with null error handler + AtomicReference receivedEvent = new AtomicReference<>(); + SSEEventListener listener = new SSEEventListener( + event -> receivedEvent.set(event), + null // Null error handler + ); + + // Invalid JSON message + String invalidJson = "{ invalid json }"; + + // Call onMessage - should not throw even with null error handler + listener.onMessage(invalidJson, null); + + // No exception thrown means test passes + } +} diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/AbstractSSEEventListener.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/AbstractSSEEventListener.java new file mode 100644 index 000000000..b6051bf41 --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/AbstractSSEEventListener.java @@ -0,0 +1,120 @@ +package io.a2a.client.transport.spi.sse; + +import java.util.concurrent.Future; +import java.util.function.Consumer; +import java.util.logging.Logger; + +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatusUpdateEvent; +import org.jspecify.annotations.Nullable; + +/** + * Abstract base class for SSE event listeners that provides common functionality + * for handling Server-Sent Events across different transport implementations. + *

    + * This class implements the Template Method pattern, where subclasses provide + * the specific message parsing logic while the base class handles common concerns + * like error handling and connection lifecycle management. + */ +public abstract class AbstractSSEEventListener { + + private static final Logger log = Logger.getLogger(AbstractSSEEventListener.class.getName()); + + private final Consumer eventHandler; + private final @Nullable Consumer errorHandler; + + /** + * Creates a new SSE event listener with the specified handlers. + * + * @param eventHandler Handler for processing streaming events + * @param errorHandler Optional handler for processing errors + */ + protected AbstractSSEEventListener(Consumer eventHandler, + @Nullable Consumer errorHandler) { + this.eventHandler = eventHandler; + this.errorHandler = errorHandler; + } + + /** + * Gets the event handler for processing streaming events. + * + * @return The event handler + */ + protected Consumer getEventHandler() { + return eventHandler; + } + + /** + * Gets the error handler for processing errors. + * + * @return The error handler, or null if not set + */ + protected @Nullable Consumer getErrorHandler() { + return errorHandler; + } + + /** + * Handles incoming SSE messages. Subclasses must implement the specific + * parsing logic for their transport protocol. + * + * @param message The raw message string from the SSE stream + * @param completableFuture Optional future for controlling the SSE connection + */ + public abstract void onMessage(String message, @Nullable Future completableFuture); + + /** + * Handles errors that occur during SSE streaming. + * This method is identical across all implementations. + * + * @param throwable The error that occurred + * @param future Optional future for closing the SSE connection + */ + public void onError(Throwable throwable, @Nullable Future future) { + if (errorHandler != null) { + errorHandler.accept(throwable); + } + if (future != null) { + future.cancel(true); // close SSE channel + } + } + + /** + * Processes a parsed streaming event and handles auto-close logic for final events. + * This method encapsulates the common logic for handling events and determining + * when to close the SSE connection. + * + * @param event The parsed streaming event + * @param future Optional future for closing the SSE connection + */ + protected void handleEvent(StreamingEventKind event, @Nullable Future future) { + eventHandler.accept(event); + + // Client-side auto-close on final events to prevent connection leaks + // Handles both TaskStatusUpdateEvent and Task objects with final states + // This covers late subscriptions to completed tasks and ensures no connection leaks + if (shouldAutoClose(event) && future != null) { + log.fine("Auto-closing SSE connection for final event: " + event.getClass().getSimpleName()); + future.cancel(true); // close SSE channel + } + } + + /** + * Determines if the SSE connection should be automatically closed based on the event type. + * The connection is closed when receiving final task states to prevent connection leaks. + * + * @param event The streaming event to check + * @return true if the connection should be closed, false otherwise + */ + protected boolean shouldAutoClose(StreamingEventKind event) { + if (event instanceof TaskStatusUpdateEvent tue && tue.isFinal()) { + return true; + } + if (event instanceof Task task) { + TaskState state = task.status().state(); + return state.isFinal(); + } + return false; + } +} diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/package-info.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/package-info.java new file mode 100644 index 000000000..1083bc32e --- /dev/null +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/sse/package-info.java @@ -0,0 +1,5 @@ +@NullMarked +package io.a2a.client.transport.spi.sse; + +import org.jspecify.annotations.NullMarked; + diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java new file mode 100644 index 000000000..3b0f37b2e --- /dev/null +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java @@ -0,0 +1,295 @@ +package io.a2a.client.transport.spi.sse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import io.a2a.spec.Message; +import io.a2a.spec.StreamingEventKind; +import io.a2a.spec.Task; +import io.a2a.spec.TaskState; +import io.a2a.spec.TaskStatus; +import io.a2a.spec.TaskStatusUpdateEvent; +import io.a2a.spec.TextPart; +import org.jspecify.annotations.Nullable; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Tests for BaseSSEEventListener abstract class. + * Tests the common functionality provided by the base class for handling events and errors. + */ +public class SSEEventListenerTest { + + private static final String TEST_TASK_ID = "task-123"; + private static final String TEST_CONTEXT_ID = "ctx-456"; + private static final String TEST_MESSAGE_ID = "msg-123"; + private static final String TEST_TEXT = "test"; + + /** + * Concrete implementation of BaseSSEEventListener for testing. + * Simulates parsing by directly calling handleEvent with provided test data. + */ + private static class TestSSEEventListener extends AbstractSSEEventListener { + private StreamingEventKind eventToHandle; + + public TestSSEEventListener(Consumer eventHandler, + @Nullable Consumer errorHandler) { + super(eventHandler, errorHandler); + } + + @Override + public void onMessage(String message, @Nullable Future completableFuture) { + if (eventToHandle != null) { + handleEvent(eventToHandle, completableFuture); + } + } + + public void setEventToHandle(StreamingEventKind event) { + this.eventToHandle = event; + } + } + + /** + * Mock Future implementation that captures cancel calls. + */ + private static class CancelCapturingFuture implements Future { + private boolean cancelHandlerCalled = false; + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + cancelHandlerCalled = true; + return true; + } + + @Override + public boolean isCancelled() { + return cancelHandlerCalled; + } + + @Override + public boolean isDone() { + return false; + } + + @Override + public Void get() { + return null; + } + + @Override + public Void get(long timeout, TimeUnit unit) { + return null; + } + + public boolean wasCancelled() { + return cancelHandlerCalled; + } + } + + // Helper methods for creating test objects + + private static Message createMessage(Message.Role role) { + return Message.builder() + .role(role) + .messageId(TEST_MESSAGE_ID) + .contextId(TEST_CONTEXT_ID) + .parts(java.util.List.of(new TextPart(TEST_TEXT))) + .build(); + } + + private static Task createTask(TaskState state) { + return Task.builder() + .id(TEST_TASK_ID) + .contextId(TEST_CONTEXT_ID) + .status(new TaskStatus(state)) + .build(); + } + + private static TaskStatusUpdateEvent createTaskStatusUpdateEvent(TaskState state, boolean isFinal) { + return TaskStatusUpdateEvent.builder() + .taskId(TEST_TASK_ID) + .contextId(TEST_CONTEXT_ID) + .status(new TaskStatus(state)) + .isFinal(isFinal) + .build(); + } + + private static TestSSEEventListener createListenerWithEventCapture(AtomicReference eventCapture) { + return new TestSSEEventListener(eventCapture::set, null); + } + + private static TestSSEEventListener createListenerWithErrorCapture(AtomicReference errorCapture) { + return new TestSSEEventListener(event -> {}, errorCapture::set); + } + + private static TestSSEEventListener createBasicListener() { + return new TestSSEEventListener(event -> {}, null); + } + + // Tests + + @Test + public void testHandleEventCallsEventHandler() { + AtomicReference receivedEvent = new AtomicReference<>(); + TestSSEEventListener listener = createListenerWithEventCapture(receivedEvent); + + Message message = createMessage(Message.Role.USER); + + listener.setEventToHandle(message); + listener.onMessage(TEST_TEXT, null); + + assertNotNull(receivedEvent.get()); + assertEquals(message, receivedEvent.get()); + } + + @Test + public void testHandleEventWithNullErrorHandler() { + AtomicReference receivedEvent = new AtomicReference<>(); + TestSSEEventListener listener = createListenerWithEventCapture(receivedEvent); + + Task task = createTask(TaskState.WORKING); + + listener.setEventToHandle(task); + listener.onMessage(TEST_TEXT, null); + + assertNotNull(receivedEvent.get()); + } + + @Test + public void testOnErrorCallsErrorHandler() { + AtomicReference receivedError = new AtomicReference<>(); + TestSSEEventListener listener = createListenerWithErrorCapture(receivedError); + + IllegalStateException testError = new IllegalStateException("Test error"); + + listener.onError(testError, null); + + assertNotNull(receivedError.get()); + assertEquals(testError, receivedError.get()); + } + + @Test + public void testOnErrorWithNullErrorHandler() { + TestSSEEventListener listener = createBasicListener(); + + // Should not throw even with null error handler + listener.onError(new RuntimeException("Test error"), null); + } + + @Test + public void testOnErrorCancelsFuture() { + AtomicBoolean errorHandlerCalled = new AtomicBoolean(false); + TestSSEEventListener listener = new TestSSEEventListener( + event -> {}, + error -> errorHandlerCalled.set(true) + ); + + CancelCapturingFuture future = new CancelCapturingFuture(); + + listener.onError(new RuntimeException("Test error"), future); + + assertTrue(errorHandlerCalled.get()); + assertTrue(future.wasCancelled()); + } + + @Test + public void testShouldAutoCloseWithFinalTaskStatusUpdateEvent() { + TestSSEEventListener listener = createBasicListener(); + TaskStatusUpdateEvent finalEvent = createTaskStatusUpdateEvent(TaskState.COMPLETED, true); + + assertTrue(listener.shouldAutoClose(finalEvent)); + } + + @Test + public void testShouldAutoCloseWithNonFinalTaskStatusUpdateEvent() { + TestSSEEventListener listener = createBasicListener(); + TaskStatusUpdateEvent nonFinalEvent = createTaskStatusUpdateEvent(TaskState.WORKING, false); + + assertFalse(listener.shouldAutoClose(nonFinalEvent)); + } + + @ParameterizedTest + @EnumSource(value = TaskState.class, names = {"COMPLETED", "FAILED", "CANCELED"}) + public void testShouldAutoCloseWithFinalTaskStates(TaskState finalState) { + TestSSEEventListener listener = createBasicListener(); + Task task = createTask(finalState); + + assertTrue(listener.shouldAutoClose(task), + "Task with state " + finalState + " should trigger auto-close"); + } + + @ParameterizedTest + @EnumSource(value = TaskState.class, names = {"WORKING", "SUBMITTED"}) + public void testShouldAutoCloseWithNonFinalTaskStates(TaskState nonFinalState) { + TestSSEEventListener listener = createBasicListener(); + Task task = createTask(nonFinalState); + + assertFalse(listener.shouldAutoClose(task), + "Task with state " + nonFinalState + " should not trigger auto-close"); + } + + @Test + public void testShouldAutoCloseWithMessage() { + TestSSEEventListener listener = createBasicListener(); + Message message = createMessage(Message.Role.AGENT); + + assertFalse(listener.shouldAutoClose(message)); + } + + @Test + public void testAutoCloseCancelsFutureForFinalEvent() { + AtomicReference receivedEvent = new AtomicReference<>(); + TestSSEEventListener listener = createListenerWithEventCapture(receivedEvent); + + TaskStatusUpdateEvent finalEvent = createTaskStatusUpdateEvent(TaskState.COMPLETED, true); + CancelCapturingFuture future = new CancelCapturingFuture(); + + listener.setEventToHandle(finalEvent); + listener.onMessage(TEST_TEXT, future); + + assertNotNull(receivedEvent.get()); + assertEquals(finalEvent, receivedEvent.get()); + assertTrue(future.wasCancelled()); + } + + @Test + public void testAutoCloseDoesNotCancelFutureForNonFinalEvent() { + AtomicReference receivedEvent = new AtomicReference<>(); + TestSSEEventListener listener = createListenerWithEventCapture(receivedEvent); + + Message message = createMessage(Message.Role.AGENT); + CancelCapturingFuture future = new CancelCapturingFuture(); + + listener.setEventToHandle(message); + listener.onMessage(TEST_TEXT, future); + + assertNotNull(receivedEvent.get()); + assertFalse(future.wasCancelled()); + } + + @Test + public void testAutoCloseWithNullFuture() { + AtomicReference receivedEvent = new AtomicReference<>(); + TestSSEEventListener listener = createListenerWithEventCapture(receivedEvent); + + TaskStatusUpdateEvent finalEvent = createTaskStatusUpdateEvent(TaskState.COMPLETED, true); + + // Should not throw with null future + listener.setEventToHandle(finalEvent); + listener.onMessage(TEST_TEXT, null); + + assertNotNull(receivedEvent.get()); + } +} \ No newline at end of file From 153a1de11fada3e94ca1557e334d00f99dfcf757 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 5 Feb 2026 12:25:38 +0100 Subject: [PATCH 273/493] chore: Managing tenant in JSONRPC. (#613) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the tenant is not defined in the JSONRPC payload, we use the one from the request path. Fixes #612 🦕 Signed-off-by: Emmanuel Hugonnet --- .../server/apps/quarkus/A2AServerRoutes.java | 19 +- .../apps/quarkus/A2AServerRoutesTest.java | 198 ++++++++++++++++++ .../server/rest/quarkus/A2AServerRoutes.java | 3 + .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 36 +++- .../io/a2a/grpc/utils/JSONRPCUtilsTest.java | 14 +- .../jsonrpc/context/JSONRPCContextKeys.java | 5 + .../rest/context/RestContextKeys.java | 4 + 7 files changed, 267 insertions(+), 12 deletions(-) diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index cb5bdb25b..672940db8 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -2,6 +2,7 @@ import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.HEADERS_KEY; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; +import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.TENANT_KEY; import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import static jakarta.ws.rs.core.MediaType.SERVER_SENT_EVENTS; @@ -102,7 +103,7 @@ public void invokeJSONRPCHandler(@Body String body, RoutingContext rc) { Multi> streamingResponse = null; A2AErrorResponse error = null; try { - A2ARequest request = JSONRPCUtils.parseRequestBody(body); + A2ARequest request = JSONRPCUtils.parseRequestBody(body, extractTenant(rc)); context.getState().put(METHOD_NAME_KEY, request.getMethod()); if (request instanceof NonStreamingJSONRPCRequest nonStreamingRequest) { nonStreamingResponse = processNonStreamingRequest(nonStreamingRequest, context); @@ -218,7 +219,6 @@ static void setStreamingMultiSseSupportSubscribedRunnable(Runnable runnable) { } private ServerCallContext createCallContext(RoutingContext rc) { - if (callContextFactory.isUnsatisfied()) { User user; if (rc.user() == null) { @@ -245,6 +245,7 @@ public String getUsername() { Set headerNames = rc.request().headers().names(); headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); state.put(HEADERS_KEY, headers); + state.put(TENANT_KEY, extractTenant(rc)); // Extract requested protocol version from X-A2A-Version header String requestedVersion = rc.request().getHeader(A2AHeaders.X_A2A_VERSION); @@ -260,6 +261,20 @@ public String getUsername() { } } + private String extractTenant(RoutingContext rc) { + String tenantPath = rc.normalizedPath(); + if (tenantPath == null || tenantPath.isBlank()) { + return ""; + } + if (tenantPath.startsWith("/")) { + tenantPath = tenantPath.substring(1); + } + if(tenantPath.endsWith("/")) { + tenantPath = tenantPath.substring(0, tenantPath.length() -1); + } + return tenantPath; + } + private static String serializeResponse(A2AResponse response) { // For error responses, use Jackson serialization (errors are standardized) if (response instanceof A2AErrorResponse error) { diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java index 3f28d564d..429eff9e1 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -5,6 +5,7 @@ import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; +import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.TENANT_KEY; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -108,6 +109,7 @@ public void setUp() { when(mockRoutingContext.user()).thenReturn(null); when(mockRequest.headers()).thenReturn(mockHeaders); when(mockRoutingContext.body()).thenReturn(mockRequestBody); + when(mockRoutingContext.normalizedPath()).thenReturn("/"); // Chain the response methods properly when(mockHttpResponse.setStatusCode(any(Integer.class))).thenReturn(mockHttpResponse); @@ -520,6 +522,202 @@ public void testGetExtendedCard_MethodNameSetInContext() { assertEquals(GET_EXTENDED_AGENT_CARD_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); } + @Test + public void testTenantExtraction_MultiSegmentPath() { + // Arrange - simulate request to /test/titi + when(mockRoutingContext.normalizedPath()).thenReturn("/test/titi"); + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "method": "GetTask", + "params": { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength": 10 + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + Task responseTask = Task.builder() + .id("de38c76d-d54c-436c-8b9f-4c2703648d64") + .contextId("context-1234") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + GetTaskResponse realResponse = new GetTaskResponse("1", responseTask); + when(mockJsonRpcHandler.onGetTask(any(GetTaskRequest.class), any(ServerCallContext.class))) + .thenReturn(realResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onGetTask(any(GetTaskRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals("test/titi", capturedContext.getState().get(TENANT_KEY)); + } + + @Test + public void testTenantExtraction_RootPath() { + // Arrange - simulate request to / + when(mockRoutingContext.normalizedPath()).thenReturn("/"); + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "method": "GetTask", + "params": { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength": 10 + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + Task responseTask = Task.builder() + .id("de38c76d-d54c-436c-8b9f-4c2703648d64") + .contextId("context-1234") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + GetTaskResponse realResponse = new GetTaskResponse("1", responseTask); + when(mockJsonRpcHandler.onGetTask(any(GetTaskRequest.class), any(ServerCallContext.class))) + .thenReturn(realResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onGetTask(any(GetTaskRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals("", capturedContext.getState().get(TENANT_KEY)); + } + + @Test + public void testTenantExtraction_SingleSegmentPath() { + // Arrange - simulate request to /tenant1 + when(mockRoutingContext.normalizedPath()).thenReturn("/tenant1"); + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "method": "GetTask", + "params": { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength": 10 + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + Task responseTask = Task.builder() + .id("de38c76d-d54c-436c-8b9f-4c2703648d64") + .contextId("context-1234") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + GetTaskResponse realResponse = new GetTaskResponse("1", responseTask); + when(mockJsonRpcHandler.onGetTask(any(GetTaskRequest.class), any(ServerCallContext.class))) + .thenReturn(realResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onGetTask(any(GetTaskRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals("tenant1", capturedContext.getState().get(TENANT_KEY)); + } + + @Test + public void testTenantExtraction_ThreeSegmentPath() { + // Arrange - simulate request to /tenant1/api/v1 + when(mockRoutingContext.normalizedPath()).thenReturn("/tenant1/api/v1"); + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "method": "GetTask", + "params": { + "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "historyLength": 10 + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + Task responseTask = Task.builder() + .id("de38c76d-d54c-436c-8b9f-4c2703648d64") + .contextId("context-1234") + .status(new TaskStatus(TaskState.SUBMITTED)) + .build(); + GetTaskResponse realResponse = new GetTaskResponse("1", responseTask); + when(mockJsonRpcHandler.onGetTask(any(GetTaskRequest.class), any(ServerCallContext.class))) + .thenReturn(realResponse); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onGetTask(any(GetTaskRequest.class), contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals("tenant1/api/v1", capturedContext.getState().get(TENANT_KEY)); + } + + @Test + public void testTenantExtraction_StreamingRequest() { + // Arrange - simulate streaming request to /myTenant/api + when(mockRoutingContext.normalizedPath()).thenReturn("/myTenant/api"); + String jsonRpcRequest = """ + { + "jsonrpc": "2.0", + "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", + "method": "SendStreamingMessage", + "params": { + "message": { + "messageId": "message-1234", + "contextId": "context-1234", + "role": "ROLE_USER", + "parts": [ + { + "text": "tell me a joke" + } + ], + "metadata": {} + }, + "configuration": { + "acceptedOutputModes": ["text"], + "blocking": true + }, + "metadata": {} + } + }"""; + when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); + + @SuppressWarnings("unchecked") + Flow.Publisher mockPublisher = mock(Flow.Publisher.class); + when(mockJsonRpcHandler.onMessageSendStream(any(SendStreamingMessageRequest.class), + any(ServerCallContext.class))).thenReturn(mockPublisher); + + ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); + + // Act + routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); + + // Assert + verify(mockJsonRpcHandler).onMessageSendStream(any(SendStreamingMessageRequest.class), + contextCaptor.capture()); + ServerCallContext capturedContext = contextCaptor.getValue(); + assertNotNull(capturedContext); + assertEquals("myTenant/api", capturedContext.getState().get(TENANT_KEY)); + } + /** * Helper method to set a field via reflection for testing purposes. */ diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 7a50f0afb..635202062 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -58,6 +58,8 @@ import static io.a2a.spec.A2AMethods.SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD; import static io.a2a.spec.A2AMethods.SUBSCRIBE_TO_TASK_METHOD; +import static io.a2a.transport.rest.context.RestContextKeys.TENANT_KEY; + @Singleton @Authenticated public class A2AServerRoutes { @@ -443,6 +445,7 @@ public String getUsername() { headerNames.forEach(name -> headers.put(name, rc.request().getHeader(name))); state.put(HEADERS_KEY, headers); state.put(METHOD_NAME_KEY, jsonRpcMethodName); + state.put(TENANT_KEY, extractTenant(rc)); // Extract requested protocol version from X-A2A-Version header String requestedVersion = rc.request().getHeader(A2AHeaders.X_A2A_VERSION); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index 1692ae3a5..e49bcbeec 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -183,7 +183,7 @@ public class JSONRPCUtils { private static final Pattern EXTRACT_WRONG_TYPE = Pattern.compile("Expected (.*) but found \".*\""); static final String ERROR_MESSAGE = "Invalid request content: %s. Please verify the request matches the expected schema for this method."; - public static A2ARequest parseRequestBody(String body) throws JsonMappingException, JsonProcessingException { + public static A2ARequest parseRequestBody(String body, @Nullable String tenant) throws JsonMappingException, JsonProcessingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); if (!jsonRpc.has("method")) { @@ -196,52 +196,76 @@ public static A2ARequest parseRequestBody(String body) throws JsonMappingExce String method = jsonRpc.get("method").getAsString(); JsonElement paramsNode = jsonRpc.get("params"); try { - return parseMethodRequest(version, id, method, paramsNode); + return parseMethodRequest(version, id, method, paramsNode, tenant); } catch (InvalidParamsError e) { throw new InvalidParamsJsonMappingException(Utils.defaultIfNull(e.getMessage(), "Invalid parameters"), id); } } - private static A2ARequest parseMethodRequest(String version, Object id, String method, JsonElement paramsNode) throws InvalidParamsError, MethodNotFoundJsonMappingException, JsonProcessingException { + private static A2ARequest parseMethodRequest(String version, Object id, String method, JsonElement paramsNode, @Nullable String tenant) throws InvalidParamsError, MethodNotFoundJsonMappingException, JsonProcessingException { switch (method) { case GET_TASK_METHOD -> { io.a2a.grpc.GetTaskRequest.Builder builder = io.a2a.grpc.GetTaskRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new GetTaskRequest(version, id, ProtoUtils.FromProto.taskQueryParams(builder)); } case CANCEL_TASK_METHOD -> { io.a2a.grpc.CancelTaskRequest.Builder builder = io.a2a.grpc.CancelTaskRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new CancelTaskRequest(version, id, ProtoUtils.FromProto.taskIdParams(builder)); } case LIST_TASK_METHOD -> { io.a2a.grpc.ListTasksRequest.Builder builder = io.a2a.grpc.ListTasksRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new ListTasksRequest(version, id, ProtoUtils.FromProto.listTasksParams(builder)); } case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new SetTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.setTaskPushNotificationConfig(builder)); } case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new GetTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.getTaskPushNotificationConfigParams(builder)); } case SEND_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new SendMessageRequest(version, id, ProtoUtils.FromProto.messageSendParams(builder)); } case LIST_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.ListTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.ListTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new ListTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.listTaskPushNotificationConfigParams(builder)); } case DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.DeleteTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new DeleteTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.deleteTaskPushNotificationConfigParams(builder)); } case GET_EXTENDED_AGENT_CARD_METHOD -> { @@ -250,11 +274,17 @@ private static A2ARequest parseMethodRequest(String version, Object id, Strin case SEND_STREAMING_MESSAGE_METHOD -> { io.a2a.grpc.SendMessageRequest.Builder builder = io.a2a.grpc.SendMessageRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new SendStreamingMessageRequest(version, id, ProtoUtils.FromProto.messageSendParams(builder)); } case SUBSCRIBE_TO_TASK_METHOD -> { io.a2a.grpc.SubscribeToTaskRequest.Builder builder = io.a2a.grpc.SubscribeToTaskRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); + if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { + builder.setTenant(tenant); + } return new SubscribeToTaskRequest(version, id, ProtoUtils.FromProto.taskIdParams(builder)); } default -> diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java index 61506c88e..380353dea 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -49,7 +49,7 @@ public void testParseSetTaskPushNotificationConfigRequest_ValidProtoFormat() thr } """; - A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest); + A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest, null); assertNotNull(request); assertInstanceOf(SetTaskPushNotificationConfigRequest.class, request); @@ -78,7 +78,7 @@ public void testParseGetTaskPushNotificationConfigRequest_ValidProtoFormat() thr } """; - A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest); + A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest, null); assertNotNull(request); assertInstanceOf(GetTaskPushNotificationConfigRequest.class, request); @@ -101,7 +101,7 @@ public void testParseMalformedJSON_ThrowsJsonSyntaxException() { """; // Missing closing braces JsonSyntaxException exception = assertThrows(JsonSyntaxException.class, () -> { - JSONRPCUtils.parseRequestBody(malformedRequest); + JSONRPCUtils.parseRequestBody(malformedRequest, null); }); assertEquals("java.io.EOFException: End of input at line 6 column 1 path $.params.parent", exception.getMessage()); } @@ -119,7 +119,7 @@ public void testParseInvalidParams_ThrowsInvalidParamsJsonMappingException() { InvalidParamsJsonMappingException exception = assertThrows( InvalidParamsJsonMappingException.class, - () -> JSONRPCUtils.parseRequestBody(invalidParamsRequest) + () -> JSONRPCUtils.parseRequestBody(invalidParamsRequest, null) ); assertEquals(3, exception.getId()); } @@ -139,7 +139,7 @@ public void testParseInvalidProtoStructure_ThrowsInvalidParamsJsonMappingExcepti InvalidParamsJsonMappingException exception = assertThrows( InvalidParamsJsonMappingException.class, - () -> JSONRPCUtils.parseRequestBody(invalidStructure) + () -> JSONRPCUtils.parseRequestBody(invalidStructure, null) ); assertEquals(4, exception.getId()); assertEquals(ERROR_MESSAGE.formatted("invalid_field in message a2a.v1.SetTaskPushNotificationConfigRequest"), exception.getMessage()); @@ -169,7 +169,7 @@ public void testParseMissingField_ThrowsInvalidParamsError() throws JsonMappingE }"""; InvalidParamsJsonMappingException exception = assertThrows( InvalidParamsJsonMappingException.class, - () -> JSONRPCUtils.parseRequestBody(missingRoleMessage) + () -> JSONRPCUtils.parseRequestBody(missingRoleMessage, null) ); assertEquals(18, exception.getId()); } @@ -199,7 +199,7 @@ public void testParseUnknownField_ThrowsJsonMappingException() throws JsonMappin }"""; JsonMappingException exception = assertThrows( JsonMappingException.class, - () -> JSONRPCUtils.parseRequestBody(unkownFieldMessage) + () -> JSONRPCUtils.parseRequestBody(unkownFieldMessage, null) ); assertEquals(ERROR_MESSAGE.formatted("unknown in message a2a.v1.Message"), exception.getMessage()); } diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java index 015e3860a..fbed22192 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/context/JSONRPCContextKeys.java @@ -18,6 +18,11 @@ public final class JSONRPCContextKeys { */ public static final String METHOD_NAME_KEY = "method"; + /** + * Context key for storing the tenant identifier extracted from the normalized path. + */ + public static final String TENANT_KEY = "tenant"; + private JSONRPCContextKeys() { // Utility class } diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java b/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java index de35ca0cf..f822607ef 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/context/RestContextKeys.java @@ -17,6 +17,10 @@ public final class RestContextKeys { * Context key for storing the method name being called. */ public static final String METHOD_NAME_KEY = "method"; + /** + * Context key for storing the tenant identifier extracted from the request path. + */ + public static final String TENANT_KEY = "tenant"; private RestContextKeys() { // Utility class From 2a797f444f2b963544bd4804d6b0b0357c6f0e2f Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 5 Feb 2026 12:55:21 +0000 Subject: [PATCH 274/493] chore: Add timouts to requesthandler tests (#626) This should make it easier to diagnose occasional hangs in the tests --- .../java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java | 2 ++ .../io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java | 2 ++ .../java/io/a2a/transport/rest/handler/RestHandlerTest.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index afed1329f..187fd6e99 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -66,9 +66,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.mockito.MockedConstruction; import org.mockito.Mockito; +@Timeout(value = 1, unit = TimeUnit.MINUTES) public class GrpcHandlerTest extends AbstractA2ARequestHandlerTest { private static final Message GRPC_MESSAGE = Message.newBuilder() diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 4dc151626..2053ada41 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -85,9 +85,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.mockito.MockedConstruction; import org.mockito.Mockito; +@Timeout(value = 1, unit = TimeUnit.MINUTES) public class JSONRPCHandlerTest extends AbstractA2ARequestHandlerTest { private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar"), new HashSet<>()); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index db3ff97aa..21005c22f 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -22,7 +22,9 @@ import io.a2a.spec.Task; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +@Timeout(value = 1, unit = TimeUnit.MINUTES) public class RestHandlerTest extends AbstractA2ARequestHandlerTest { private final ServerCallContext callContext = new ServerCallContext(UnauthenticatedUser.INSTANCE, Map.of("foo", "bar"), new HashSet<>()); From 147f9c1c74ad34f8d01685dc860943e2c8e55520 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Thu, 5 Feb 2026 14:43:49 +0100 Subject: [PATCH 275/493] feat!: Update a2a.proto d14f410 (#614) based on https://github.com/a2aproject/A2A/commit/d14f4107119bf0fe0dff9e2c577eb4ab70b51793 Signed-off-by: Jeff Mesnil --- README.md | 5 +- .../java/io/a2a/client/AbstractClient.java | 10 +- .../src/main/java/io/a2a/client/Client.java | 6 +- .../java/io/a2a/client/ClientBuilderTest.java | 1 - .../client/transport/grpc/GrpcTransport.java | 8 +- .../transport/jsonrpc/JSONRPCTransport.java | 10 +- .../jsonrpc/JSONRPCTransportTest.java | 9 +- .../transport/jsonrpc/JsonMessages.java | 5 +- .../client/transport/rest/RestTransport.java | 6 +- .../transport/rest/JsonRestMessages.java | 9 +- .../transport/rest/RestTransportTest.java | 9 +- .../client/transport/spi/ClientTransport.java | 8 +- .../cloud/CloudAgentCardProducer.java | 1 - .../helloworld/AgentCardProducer.java | 1 - ...otificationConfigStoreIntegrationTest.java | 2 +- .../MultiInstanceReplicationAgentCards.java | 1 - .../ReplicationTestAgentCardProducer.java | 1 - .../java/io/a2a/client/http/JsonMessages.java | 3 +- .../jsonrpc/common/wrappers/A2AResponse.java | 2 +- ...ateTaskPushNotificationConfigRequest.java} | 18 +- ...teTaskPushNotificationConfigResponse.java} | 10 +- .../wrappers/NonStreamingJSONRPCRequest.java | 2 +- .../server/apps/quarkus/A2AServerRoutes.java | 10 +- .../apps/quarkus/A2AServerRoutesTest.java | 14 +- .../server/rest/quarkus/A2AServerRoutes.java | 4 +- .../rest/quarkus/A2AServerRoutesTest.java | 8 +- .../DefaultRequestHandler.java | 2 +- .../requesthandlers/RequestHandler.java | 2 +- .../io/a2a/server/util/sse/SseFormatter.java | 6 +- .../AbstractA2ARequestHandlerTest.java | 5 +- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 339 +++++++++--------- .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 76 ++-- .../java/io/a2a/grpc/AgentCapabilities.java | 125 +------ .../a2a/grpc/AgentCapabilitiesOrBuilder.java | 19 - .../java/io/a2a/grpc/CancelTaskRequest.java | 4 +- ...ateTaskPushNotificationConfigRequest.java} | 120 +++---- ...shNotificationConfigRequestOrBuilder.java} | 4 +- ...leteTaskPushNotificationConfigRequest.java | 4 +- .../main/java/io/a2a/grpc/GetTaskRequest.java | 4 +- ...istTaskPushNotificationConfigResponse.java | 4 +- .../java/io/a2a/grpc/SendMessageRequest.java | 4 +- .../grpc/mapper/AgentCapabilitiesMapper.java | 1 - ...eateTaskPushNotificationConfigMapper.java} | 30 +- .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 12 +- .../java/io/a2a/grpc/utils/ProtoUtils.java | 18 +- spec-grpc/src/main/proto/a2a.proto | 26 +- .../io/a2a/grpc/utils/JSONRPCUtilsTest.java | 36 +- .../java/io/a2a/grpc/utils/ToProtoTest.java | 2 - .../src/main/java/io/a2a/spec/A2AMethods.java | 2 +- .../java/io/a2a/spec/AgentCapabilities.java | 26 +- .../io/a2a/tck/server/AgentCardProducer.java | 1 - .../apps/common/AbstractA2AServerTest.java | 8 +- .../server/apps/common/AgentCardProducer.java | 1 - .../transport/grpc/handler/GrpcHandler.java | 6 +- .../grpc/handler/GrpcHandlerTest.java | 44 +-- .../jsonrpc/handler/JSONRPCHandler.java | 18 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 50 ++- .../transport/rest/handler/RestHandler.java | 6 +- .../rest/handler/RestHandlerTest.java | 10 +- 59 files changed, 487 insertions(+), 691 deletions(-) rename jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/{SetTaskPushNotificationConfigRequest.java => CreateTaskPushNotificationConfigRequest.java} (76%) rename jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/{SetTaskPushNotificationConfigResponse.java => CreateTaskPushNotificationConfigResponse.java} (75%) rename spec-grpc/src/main/java/io/a2a/grpc/{SetTaskPushNotificationConfigRequest.java => CreateTaskPushNotificationConfigRequest.java} (85%) rename spec-grpc/src/main/java/io/a2a/grpc/{SetTaskPushNotificationConfigRequestOrBuilder.java => CreateTaskPushNotificationConfigRequestOrBuilder.java} (93%) rename spec-grpc/src/main/java/io/a2a/grpc/mapper/{SetTaskPushNotificationConfigMapper.java => CreateTaskPushNotificationConfigMapper.java} (79%) diff --git a/README.md b/README.md index 50a760b10..941419f45 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,6 @@ public class WeatherAgentCardProducer { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(false) - .stateTransitionHistory(false) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) @@ -575,10 +574,10 @@ TaskPushNotificationConfig taskConfig = TaskPushNotificationConfig.builder() .pushNotificationConfig(pushNotificationConfig) .build(); -TaskPushNotificationConfig result = client.setTaskPushNotificationConfiguration(taskConfig); +TaskPushNotificationConfig result = client.createTaskPushNotificationConfiguration(taskConfig); // You can also optionally specify a ClientCallContext with call-specific config to use -TaskPushNotificationConfig result = client.setTaskPushNotificationConfiguration(taskConfig, clientCallContext); +TaskPushNotificationConfig result = client.createTaskPushNotificationConfiguration(taskConfig, clientCallContext); ``` #### List the push notification configurations for a task diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index 5d2785386..c47d8238b 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -224,26 +224,26 @@ public Task cancelTask(TaskIdParams request) throws A2AClientException { public abstract Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException; /** - * Set or update the push notification configuration for a specific task. + * Create or update the push notification configuration for a specific task. * * @param request the push notification configuration to set for the task * @return the configured TaskPushNotificationConfig * @throws A2AClientException if setting the task push notification configuration fails for any reason */ - public TaskPushNotificationConfig setTaskPushNotificationConfiguration( + public TaskPushNotificationConfig createTaskPushNotificationConfiguration( TaskPushNotificationConfig request) throws A2AClientException { - return setTaskPushNotificationConfiguration(request, null); + return createTaskPushNotificationConfiguration(request, null); } /** - * Set or update the push notification configuration for a specific task. + * Create or update the push notification configuration for a specific task. * * @param request the push notification configuration to set for the task * @param context optional client call context for the request (may be {@code null}) * @return the configured TaskPushNotificationConfig * @throws A2AClientException if setting the task push notification configuration fails for any reason */ - public abstract TaskPushNotificationConfig setTaskPushNotificationConfiguration( + public abstract TaskPushNotificationConfig createTaskPushNotificationConfiguration( TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException; diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index 117351098..405155ee2 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -431,7 +431,7 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context * ) * ) * ); - * client.setTaskPushNotificationConfiguration(config); + * client.createTaskPushNotificationConfiguration(config); * } * * @param request the push notification configuration for the task @@ -442,9 +442,9 @@ public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context * @see PushNotificationConfig */ @Override - public TaskPushNotificationConfig setTaskPushNotificationConfiguration( + public TaskPushNotificationConfig createTaskPushNotificationConfiguration( TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { - return clientTransport.setTaskPushNotificationConfiguration(request, context); + return clientTransport.createTaskPushNotificationConfiguration(request, context); } /** diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index 56eb7560d..dd38c937d 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -31,7 +31,6 @@ public class ClientBuilderTest { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 5cb431419..8cc67d7b0 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -229,12 +229,12 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo } @Override - public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, - @Nullable ClientCallContext context) throws A2AClientException { + public TaskPushNotificationConfig createTaskPushNotificationConfiguration(TaskPushNotificationConfig request, + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); String configId = request.pushNotificationConfig().id(); - io.a2a.grpc.SetTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder() + io.a2a.grpc.CreateTaskPushNotificationConfigRequest grpcRequest = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder() .setParent("tasks/" + request.taskId()) .setConfig(ToProto.taskPushNotificationConfig(request)) .setConfigId(configId != null ? configId : request.taskId()) @@ -244,7 +244,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushN try { A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders); - return FromProto.taskPushNotificationConfig(stubWithMetadata.setTaskPushNotificationConfig(grpcRequest)); + return FromProto.taskPushNotificationConfig(stubWithMetadata.createTaskPushNotificationConfig(grpcRequest)); } catch (StatusRuntimeException | StatusException e) { throw GrpcErrorMapper.mapGrpcError(e, "Failed to create task push notification config: "); } diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index e991eecb3..21ea398c1 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -43,7 +43,7 @@ import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; import io.a2a.jsonrpc.common.wrappers.ListTasksResult; import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.spec.A2AClientError; import io.a2a.spec.A2AClientException; import io.a2a.spec.A2AError; @@ -182,15 +182,15 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo } @Override - public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, - @Nullable ClientCallContext context) throws A2AClientException { + public TaskPushNotificationConfig createTaskPushNotificationConfiguration(TaskPushNotificationConfig request, + @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, - ProtoUtils.ToProto.setTaskPushNotificationConfigRequest(request), agentCard, context); + ProtoUtils.ToProto.createTaskPushNotificationConfigRequest(request), agentCard, context); try { String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, request.tenant()), payloadAndHeaders, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); - SetTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); + CreateTaskPushNotificationConfigResponse response = unmarshalResponse(httpResponseBody, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); return response.getResult(); } catch (A2AClientException e) { throw e; diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 9b6f7f017..74fe9545a 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -1,7 +1,5 @@ package io.a2a.client.transport.jsonrpc; -import static io.a2a.client.transport.jsonrpc.JsonMessages.AGENT_CARD; -import static io.a2a.client.transport.jsonrpc.JsonMessages.AGENT_CARD_SUPPORTS_EXTENDED; import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonMessages.CANCEL_TASK_TEST_RESPONSE; import static io.a2a.client.transport.jsonrpc.JsonMessages.GET_AUTHENTICATED_EXTENDED_AGENT_CARD_REQUEST; @@ -43,7 +41,6 @@ import io.a2a.spec.AgentCard; import io.a2a.spec.ExtensionSupportRequiredError; import io.a2a.spec.VersionNotSupportedError; -import io.a2a.spec.AgentInterface; import io.a2a.spec.AgentSkill; import io.a2a.spec.Artifact; import io.a2a.spec.AuthenticationInfo; @@ -67,7 +64,6 @@ import io.a2a.spec.TaskQueryParams; import io.a2a.spec.TaskState; import io.a2a.spec.TextPart; -import io.a2a.spec.TransportProtocol; import io.a2a.util.Utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -322,7 +318,7 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception { } @Test - public void testA2AClientSetTaskPushNotificationConfig() throws Exception { + public void testA2AClientCreateTaskPushNotificationConfig() throws Exception { this.server.when( request() .withMethod("POST") @@ -337,7 +333,7 @@ public void testA2AClientSetTaskPushNotificationConfig() throws Exception { ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); - TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration( + TaskPushNotificationConfig taskPushNotificationConfig = client.createTaskPushNotificationConfiguration( new TaskPushNotificationConfig("de38c76d-d54c-436c-8b9f-4c2703648d64", PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") @@ -378,7 +374,6 @@ public void testA2AClientGetExtendedAgentCard() throws Exception { assertEquals("https://docs.examplegeoservices.com/georoute-agent/api", agentCard.documentationUrl()); assertTrue(agentCard.capabilities().streaming()); assertTrue(agentCard.capabilities().pushNotifications()); - assertFalse(agentCard.capabilities().stateTransitionHistory()); assertTrue(agentCard.capabilities().extendedAgentCard()); Map securitySchemes = agentCard.securitySchemes(); assertNotNull(securitySchemes); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index dfdbb9741..211f5e4f6 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -28,7 +28,6 @@ public class JsonMessages { "capabilities": { "streaming": true, "pushNotifications": true, - "stateTransitionHistory": false, "extendedAgentCard": false }, "securitySchemes": { @@ -340,7 +339,7 @@ public class JsonMessages { static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ { "jsonrpc":"2.0", - "method":"SetTaskPushNotificationConfig", + "method":"CreateTaskPushNotificationConfig", "params":{ "parent":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", "configId":"c295ea44-7543-4f78-b524-7a38915ad6e4", @@ -610,7 +609,6 @@ public class JsonMessages { "capabilities": { "streaming": true, "pushNotifications": true, - "stateTransitionHistory": false, "extendedAgentCard": true }, "securitySchemes": { @@ -691,7 +689,6 @@ public class JsonMessages { "capabilities": { "streaming": true, "pushNotifications": true, - "stateTransitionHistory": false, "extendedAgentCard": true }, "securitySchemes": { diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index ae01e0f3f..88c86846d 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -269,10 +269,10 @@ private String buildListTasksQueryString(ListTasksParams request) { } @Override - public TaskPushNotificationConfig setTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { + public TaskPushNotificationConfig createTaskPushNotificationConfiguration(TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); - io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder - = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder builder + = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder(); builder.setConfig(ProtoUtils.ToProto.taskPushNotificationConfig(request)) .setParent("tasks/" + request.taskId()); if (request.pushNotificationConfig().id() != null) { diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java index c0c2360b0..f324f004e 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java @@ -121,8 +121,7 @@ public class JsonRestMessages { "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", "capabilities": { "streaming": true, - "pushNotifications": true, - "stateTransitionHistory": false + "pushNotifications": true }, "securitySchemes": { "google": { @@ -188,8 +187,7 @@ public class JsonRestMessages { "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", "capabilities": { "streaming": true, - "pushNotifications": true, - "stateTransitionHistory": false + "pushNotifications": true }, "securitySchemes": { "google": { @@ -255,8 +253,7 @@ public class JsonRestMessages { "documentationUrl": "https://docs.examplegeoservices.com/georoute-agent/api", "capabilities": { "streaming": true, - "pushNotifications": true, - "stateTransitionHistory": false + "pushNotifications": true }, "securitySchemes": { "google": { diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 879363d9c..04efb4eca 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -80,7 +80,6 @@ public class RestTransportTest { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) @@ -287,11 +286,11 @@ public void testSendMessageStreaming() throws Exception { } /** - * Test of setTaskPushNotificationConfiguration method, of class JSONRestTransport. + * Test of CreateTaskPushNotificationConfiguration method, of class JSONRestTransport. */ @Test - public void testSetTaskPushNotificationConfiguration() throws Exception { - log.info("Testing setTaskPushNotificationConfiguration"); + public void testCreateTaskPushNotificationConfiguration() throws Exception { + log.info("Testing CreateTaskPushNotificationConfiguration"); this.server.when( request() .withMethod("POST") @@ -312,7 +311,7 @@ public void testSetTaskPushNotificationConfiguration() throws Exception { .authentication( new AuthenticationInfo(Collections.singletonList("jwt"), null)) .build(), "tenant"); - TaskPushNotificationConfig taskPushNotificationConfig = client.setTaskPushNotificationConfiguration(pushedConfig, null); + TaskPushNotificationConfig taskPushNotificationConfig = client.createTaskPushNotificationConfiguration(pushedConfig, null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index df95e9ffd..504e4c601 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -79,15 +79,15 @@ void sendMessageStreaming(MessageSendParams request, Consumer the type of the result value returned in successful responses */ -public abstract sealed class A2AResponse implements A2AMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetExtendedAgentCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, A2AErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse { +public abstract sealed class A2AResponse implements A2AMessage permits CancelTaskResponse, DeleteTaskPushNotificationConfigResponse, GetExtendedAgentCardResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, A2AErrorResponse, ListTaskPushNotificationConfigResponse, ListTasksResponse, SendMessageResponse, SendStreamingMessageResponse, CreateTaskPushNotificationConfigResponse { /** The JSON-RPC protocol version. */ protected String jsonrpc; diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CreateTaskPushNotificationConfigRequest.java similarity index 76% rename from jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigRequest.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CreateTaskPushNotificationConfigRequest.java index c01e7c2f4..b9b8716be 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CreateTaskPushNotificationConfigRequest.java @@ -16,12 +16,12 @@ *

    * This class implements the JSON-RPC {@code tasks/pushNotificationConfig/set} method. * - * @see SetTaskPushNotificationConfigResponse for the response + * @see CreateTaskPushNotificationConfigResponse for the response * @see TaskPushNotificationConfig for the parameter structure * @see PushNotificationConfig for notification endpoint details * @see A2A Protocol Specification */ -public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { +public final class CreateTaskPushNotificationConfigRequest extends NonStreamingJSONRPCRequest { /** * Constructs request with all parameters. @@ -30,7 +30,7 @@ public final class SetTaskPushNotificationConfigRequest extends NonStreamingJSON * @param id the request ID * @param params the request parameters */ - public SetTaskPushNotificationConfigRequest(String jsonrpc, Object id, TaskPushNotificationConfig params) { + public CreateTaskPushNotificationConfigRequest(String jsonrpc, Object id, TaskPushNotificationConfig params) { super(jsonrpc, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, id, params); } @@ -40,7 +40,7 @@ public SetTaskPushNotificationConfigRequest(String jsonrpc, Object id, TaskPushN * @param id the request ID * @param taskPushConfig the task push notification configuration */ - public SetTaskPushNotificationConfigRequest(String id, TaskPushNotificationConfig taskPushConfig) { + public CreateTaskPushNotificationConfigRequest(String id, TaskPushNotificationConfig taskPushConfig) { this(null, id, taskPushConfig); } @@ -73,7 +73,7 @@ private Builder() { * @param jsonrpc the JSON-RPC version * @return this builder for method chaining */ - public SetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { + public CreateTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { this.jsonrpc = jsonrpc; return this; } @@ -84,7 +84,7 @@ public SetTaskPushNotificationConfigRequest.Builder jsonrpc(String jsonrpc) { * @param id the request ID * @return this builder for method chaining */ - public SetTaskPushNotificationConfigRequest.Builder id(Object id) { + public CreateTaskPushNotificationConfigRequest.Builder id(Object id) { this.id = id; return this; } @@ -95,7 +95,7 @@ public SetTaskPushNotificationConfigRequest.Builder id(Object id) { * @param params the request parameters * @return this builder for method chaining */ - public SetTaskPushNotificationConfigRequest.Builder params(TaskPushNotificationConfig params) { + public CreateTaskPushNotificationConfigRequest.Builder params(TaskPushNotificationConfig params) { this.params = params; return this; } @@ -105,11 +105,11 @@ public SetTaskPushNotificationConfigRequest.Builder params(TaskPushNotificationC * * @return a new instance */ - public SetTaskPushNotificationConfigRequest build() { + public CreateTaskPushNotificationConfigRequest build() { if (id == null) { id = UUID.randomUUID().toString(); } - return new SetTaskPushNotificationConfigRequest(jsonrpc, id, params); + return new CreateTaskPushNotificationConfigRequest(jsonrpc, id, params); } } } diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigResponse.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CreateTaskPushNotificationConfigResponse.java similarity index 75% rename from jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigResponse.java rename to jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CreateTaskPushNotificationConfigResponse.java index 0c39b9713..e46aa7d2b 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SetTaskPushNotificationConfigResponse.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/CreateTaskPushNotificationConfigResponse.java @@ -14,12 +14,12 @@ * If push notifications are not supported or an error occurs, the error field will contain * a {@link A2AError} (e.g., {@link PushNotificationNotSupportedError}). * - * @see SetTaskPushNotificationConfigRequest for the corresponding request + * @see CreateTaskPushNotificationConfigRequest for the corresponding request * @see TaskPushNotificationConfig for the configuration structure * @see PushNotificationNotSupportedError for the error when unsupported * @see A2A Protocol Specification */ -public final class SetTaskPushNotificationConfigResponse extends A2AResponse { +public final class CreateTaskPushNotificationConfigResponse extends A2AResponse { /** * Constructs response with all parameters. @@ -29,7 +29,7 @@ public final class SetTaskPushNotificationConfigResponse extends A2AResponse the type of the request parameters */ public abstract sealed class NonStreamingJSONRPCRequest extends A2ARequest permits GetTaskRequest, - CancelTaskRequest, SetTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, + CancelTaskRequest, CreateTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest, SendMessageRequest, DeleteTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigRequest, GetExtendedAgentCardRequest, ListTasksRequest { diff --git a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java index 672940db8..548f88da3 100644 --- a/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java +++ b/reference/jsonrpc/src/main/java/io/a2a/server/apps/quarkus/A2AServerRoutes.java @@ -51,8 +51,8 @@ import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; @@ -176,7 +176,7 @@ private A2AResponse processNonStreamingRequest(NonStreamingJSONRPCRequest if (request instanceof ListTasksRequest req) { return jsonRpcHandler.onListTasks(req, context); } - if (request instanceof SetTaskPushNotificationConfigRequest req) { + if (request instanceof CreateTaskPushNotificationConfigRequest req) { return jsonRpcHandler.setPushNotificationConfig(req, context); } if (request instanceof GetTaskPushNotificationConfigRequest req) { @@ -297,8 +297,8 @@ private static com.google.protobuf.MessageOrBuilder convertToProto(A2AResponse contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); @@ -366,7 +366,7 @@ public void testSetTaskPushNotificationConfig_MethodNameSetInContext() { routes.invokeJSONRPCHandler(jsonRpcRequest, mockRoutingContext); // Assert - verify(mockJsonRpcHandler).setPushNotificationConfig(any(SetTaskPushNotificationConfigRequest.class), + verify(mockJsonRpcHandler).setPushNotificationConfig(any(CreateTaskPushNotificationConfigRequest.class), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); diff --git a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java index 635202062..a41f2d361 100644 --- a/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java +++ b/reference/rest/src/main/java/io/a2a/server/rest/quarkus/A2AServerRoutes.java @@ -263,7 +263,7 @@ public void subscribeToTask(RoutingContext rc) { } @Route(regex = "^\\/(?[^\\/]*\\/?)tasks\\/(?[^/]+)\\/pushNotificationConfigs$", order = 1, methods = {Route.HttpMethod.POST}, consumes = {APPLICATION_JSON}, type = Route.HandlerType.BLOCKING) - public void setTaskPushNotificationConfiguration(@Body String body, RoutingContext rc) { + public void CreateTaskPushNotificationConfiguration(@Body String body, RoutingContext rc) { String taskId = rc.pathParam("taskId"); ServerCallContext context = createCallContext(rc, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); HTTPRestResponse response = null; @@ -271,7 +271,7 @@ public void setTaskPushNotificationConfiguration(@Body String body, RoutingConte if (taskId == null || taskId.isEmpty()) { response = jsonRestHandler.createErrorResponse(new InvalidParamsError("bad task id")); } else { - response = jsonRestHandler.setTaskPushNotificationConfiguration(taskId, body, extractTenant(rc), context); + response = jsonRestHandler.CreateTaskPushNotificationConfiguration(taskId, body, extractTenant(rc), context); } } catch (Throwable t) { response = jsonRestHandler.createErrorResponse(new InternalError(t.getMessage())); diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java index 0f07b421a..9ef8f7c47 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -198,23 +198,23 @@ public void testResubscribeTask_MethodNameSetInContext() { } @Test - public void testSetTaskPushNotificationConfiguration_MethodNameSetInContext() { + public void testCreateTaskPushNotificationConfiguration_MethodNameSetInContext() { // Arrange when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); when(mockHttpResponse.getStatusCode()).thenReturn(200); when(mockHttpResponse.getContentType()).thenReturn("application/json"); when(mockHttpResponse.getBody()).thenReturn("{}"); - when(mockRestHandler.setTaskPushNotificationConfiguration(anyString(), anyString(), anyString(), + when(mockRestHandler.CreateTaskPushNotificationConfiguration(anyString(), anyString(), anyString(), any(ServerCallContext.class))).thenReturn(mockHttpResponse); ArgumentCaptor contextCaptor = ArgumentCaptor.forClass(ServerCallContext.class); // Act - routes.setTaskPushNotificationConfiguration("{}", mockRoutingContext); + routes.CreateTaskPushNotificationConfiguration("{}", mockRoutingContext); // Assert - verify(mockRestHandler).setTaskPushNotificationConfiguration(eq("task123"), eq("{}"), anyString(), contextCaptor.capture()); + verify(mockRestHandler).CreateTaskPushNotificationConfiguration(eq("task123"), eq("{}"), anyString(), contextCaptor.capture()); ServerCallContext capturedContext = contextCaptor.getValue(); assertNotNull(capturedContext); assertEquals(SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD, capturedContext.getState().get(METHOD_NAME_KEY)); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index c476c8741..e1a16eb3c 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -739,7 +739,7 @@ public void onComplete() { } @Override - public TaskPushNotificationConfig onSetTaskPushNotificationConfig( + public TaskPushNotificationConfig onCreateTaskPushNotificationConfig( TaskPushNotificationConfig params, ServerCallContext context) throws A2AError { if (pushConfigStore == null) { throw new UnsupportedOperationError(); diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index 6d99fe399..639098967 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -39,7 +39,7 @@ Flow.Publisher onMessageSendStream( MessageSendParams params, ServerCallContext context) throws A2AError; - TaskPushNotificationConfig onSetTaskPushNotificationConfig( + TaskPushNotificationConfig onCreateTaskPushNotificationConfig( TaskPushNotificationConfig params, ServerCallContext context) throws A2AError; diff --git a/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java b/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java index 737fbac23..ba02222e6 100644 --- a/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java +++ b/server-common/src/main/java/io/a2a/server/util/sse/SseFormatter.java @@ -4,6 +4,7 @@ import io.a2a.jsonrpc.common.wrappers.A2AErrorResponse; import io.a2a.jsonrpc.common.wrappers.A2AResponse; import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.GetExtendedAgentCardResponse; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; @@ -12,7 +13,6 @@ import io.a2a.jsonrpc.common.wrappers.ListTasksResponse; import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; /** * Framework-agnostic utility for formatting A2A responses as Server-Sent Events (SSE). @@ -116,8 +116,8 @@ private static com.google.protobuf.MessageOrBuilder convertToProto(A2AResponse()) .defaultOutputModes(new ArrayList<>()) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index 0fa06787b..6dc06f861 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -247,10 +247,10 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable; + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_SubscribeToTaskRequest_descriptor; static final @@ -361,172 +361,171 @@ public static void registerAllExtensions( ".a2a.v1.SecurityScheme:\0028\001B\024\n\022_documenta" + "tion_urlB\013\n\t_icon_urlJ\004\010\003\020\004J\004\010\016\020\017J\004\010\017\020\020\"" + "<\n\rAgentProvider\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\031\n\014or" + - "ganization\030\002 \001(\tB\003\340A\002\"\233\002\n\021AgentCapabilit" + + "ganization\030\002 \001(\tB\003\340A\002\"\335\001\n\021AgentCapabilit" + "ies\022\026\n\tstreaming\030\001 \001(\010H\000\210\001\001\022\037\n\022push_noti" + "fications\030\002 \001(\010H\001\210\001\001\022*\n\nextensions\030\003 \003(\013" + - "2\026.a2a.v1.AgentExtension\022%\n\030state_transi" + - "tion_history\030\004 \001(\010H\002\210\001\001\022 \n\023extended_agen" + - "t_card\030\005 \001(\010H\003\210\001\001B\014\n\n_streamingB\025\n\023_push" + - "_notificationsB\033\n\031_state_transition_hist" + - "oryB\026\n\024_extended_agent_card\"m\n\016AgentExte" + - "nsion\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030\002 \001(\t\022" + - "\020\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132\027.goog" + - "le.protobuf.Struct\"\276\001\n\nAgentSkill\022\017\n\002id\030" + - "\001 \001(\tB\003\340A\002\022\021\n\004name\030\002 \001(\tB\003\340A\002\022\030\n\013descrip" + - "tion\030\003 \001(\tB\003\340A\002\022\021\n\004tags\030\004 \003(\tB\003\340A\002\022\020\n\010ex" + - "amples\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014out" + - "put_modes\030\007 \003(\t\022\"\n\010security\030\010 \003(\0132\020.a2a." + - "v1.Security\"m\n\022AgentCardSignature\022\026\n\tpro" + - "tected\030\001 \001(\tB\003\340A\002\022\026\n\tsignature\030\002 \001(\tB\003\340A" + - "\002\022\'\n\006header\030\003 \001(\0132\027.google.protobuf.Stru" + - "ct\"v\n\032TaskPushNotificationConfig\022\021\n\004name" + - "\030\001 \001(\tB\003\340A\002\022E\n\030push_notification_config\030" + - "\002 \001(\0132\036.a2a.v1.PushNotificationConfigB\003\340" + - "A\002\"\032\n\nStringList\022\014\n\004list\030\001 \003(\t\"~\n\010Securi" + - "ty\022.\n\007schemes\030\001 \003(\0132\035.a2a.v1.Security.Sc" + - "hemesEntry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001(\t\022" + - "!\n\005value\030\002 \001(\0132\022.a2a.v1.StringList:\0028\001\"\361" + - "\002\n\016SecurityScheme\022?\n\027api_key_security_sc" + - "heme\030\001 \001(\0132\034.a2a.v1.APIKeySecurityScheme" + - "H\000\022C\n\031http_auth_security_scheme\030\002 \001(\0132\036." + - "a2a.v1.HTTPAuthSecuritySchemeH\000\022>\n\026oauth" + - "2_security_scheme\030\003 \001(\0132\034.a2a.v1.OAuth2S" + - "ecuritySchemeH\000\022N\n\037open_id_connect_secur" + - "ity_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConnectS" + - "ecuritySchemeH\000\022?\n\024mtls_security_scheme\030" + - "\005 \001(\0132\037.a2a.v1.MutualTlsSecuritySchemeH\000" + - "B\010\n\006scheme\"U\n\024APIKeySecurityScheme\022\023\n\013de" + - "scription\030\001 \001(\t\022\025\n\010location\030\002 \001(\tB\003\340A\002\022\021" + - "\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026HTTPAuthSecuritySch" + - "eme\022\023\n\013description\030\001 \001(\t\022\023\n\006scheme\030\002 \001(\t" + - "B\003\340A\002\022\025\n\rbearer_format\030\003 \001(\t\"p\n\024OAuth2Se" + - "curityScheme\022\023\n\013description\030\001 \001(\t\022&\n\005flo" + - "ws\030\002 \001(\0132\022.a2a.v1.OAuthFlowsB\003\340A\002\022\033\n\023oau" + - "th2_metadata_url\030\003 \001(\t\"T\n\033OpenIdConnectS" + - "ecurityScheme\022\023\n\013description\030\001 \001(\t\022 \n\023op" + - "en_id_connect_url\030\002 \001(\tB\003\340A\002\".\n\027MutualTl" + - "sSecurityScheme\022\023\n\013description\030\001 \001(\t\"\330\001\n" + - "\nOAuthFlows\022@\n\022authorization_code\030\001 \001(\0132" + - "\".a2a.v1.AuthorizationCodeOAuthFlowH\000\022@\n" + - "\022client_credentials\030\002 \001(\0132\".a2a.v1.Clien" + - "tCredentialsOAuthFlowH\000\0222\n\013device_code\030\005" + - " \001(\0132\033.a2a.v1.DeviceCodeOAuthFlowH\000B\006\n\004f" + - "lowJ\004\010\003\020\004J\004\010\004\020\005\"\364\001\n\032AuthorizationCodeOAu" + - "thFlow\022\036\n\021authorization_url\030\001 \001(\tB\003\340A\002\022\026" + - "\n\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003 " + - "\001(\t\022C\n\006scopes\030\004 \003(\0132..a2a.v1.Authorizati" + - "onCodeOAuthFlow.ScopesEntryB\003\340A\002\022\025\n\rpkce" + - "_required\030\005 \001(\010\032-\n\013ScopesEntry\022\013\n\003key\030\001 " + - "\001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\275\001\n\032ClientCredent" + - "ialsOAuthFlow\022\026\n\ttoken_url\030\001 \001(\tB\003\340A\002\022\023\n" + - "\013refresh_url\030\002 \001(\t\022C\n\006scopes\030\003 \003(\0132..a2a" + - ".v1.ClientCredentialsOAuthFlow.ScopesEnt" + - "ryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005v" + - "alue\030\002 \001(\t:\0028\001\"\326\001\n\023DeviceCodeOAuthFlow\022%" + - "\n\030device_authorization_url\030\001 \001(\tB\003\340A\002\022\026\n" + - "\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003 \001" + - "(\t\022<\n\006scopes\030\004 \003(\0132\'.a2a.v1.DeviceCodeOA" + - "uthFlow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry\022" + - "\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\257\001\n\022Send" + - "MessageRequest\022\016\n\006tenant\030\004 \001(\t\022%\n\007messag" + - "e\030\001 \001(\0132\017.a2a.v1.MessageB\003\340A\002\0227\n\rconfigu" + - "ration\030\002 \001(\0132 .a2a.v1.SendMessageConfigu" + - "ration\022)\n\010metadata\030\003 \001(\0132\027.google.protob" + - "uf.Struct\"c\n\016GetTaskRequest\022\016\n\006tenant\030\003 " + - "\001(\t\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\033\n\016history_length" + - "\030\002 \001(\005H\000\210\001\001B\021\n\017_history_length\"\265\002\n\020ListT" + - "asksRequest\022\016\n\006tenant\030\t \001(\t\022\022\n\ncontext_i" + - "d\030\001 \001(\t\022!\n\006status\030\002 \001(\0162\021.a2a.v1.TaskSta" + - "te\022\026\n\tpage_size\030\003 \001(\005H\000\210\001\001\022\022\n\npage_token" + - "\030\004 \001(\t\022\033\n\016history_length\030\005 \001(\005H\001\210\001\001\022:\n\026s" + - "tatus_timestamp_after\030\006 \001(\0132\032.google.pro" + - "tobuf.Timestamp\022\036\n\021include_artifacts\030\007 \001" + - "(\010H\002\210\001\001B\014\n\n_page_sizeB\021\n\017_history_length" + - "B\024\n\022_include_artifacts\"\204\001\n\021ListTasksResp" + - "onse\022 \n\005tasks\030\001 \003(\0132\014.a2a.v1.TaskB\003\340A\002\022\034" + - "\n\017next_page_token\030\002 \001(\tB\003\340A\002\022\026\n\tpage_siz" + - "e\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_size\030\004 \001(\005B\003\340A\002\"1\n" + - "\021CancelTaskRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004na" + - "me\030\001 \001(\t\"D\n$GetTaskPushNotificationConfi" + - "gRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"G" + - "\n\'DeleteTaskPushNotificationConfigReques" + - "t\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"\234\001\n$SetT" + - "askPushNotificationConfigRequest\022\016\n\006tena" + - "nt\030\004 \001(\t\022\023\n\006parent\030\001 \001(\tB\003\340A\002\022\026\n\tconfig_" + - "id\030\002 \001(\tB\003\340A\002\0227\n\006config\030\003 \001(\0132\".a2a.v1.T" + - "askPushNotificationConfigB\003\340A\002\"6\n\026Subscr" + - "ibeToTaskRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004name" + - "\030\001 \001(\t\"n\n%ListTaskPushNotificationConfig" + - "Request\022\016\n\006tenant\030\004 \001(\t\022\016\n\006parent\030\001 \001(\t\022" + - "\021\n\tpage_size\030\002 \001(\005\022\022\n\npage_token\030\003 \001(\t\"-" + - "\n\033GetExtendedAgentCardRequest\022\016\n\006tenant\030" + - "\001 \001(\t\"b\n\023SendMessageResponse\022\034\n\004task\030\001 \001" + - "(\0132\014.a2a.v1.TaskH\000\022\"\n\007message\030\002 \001(\0132\017.a2" + - "a.v1.MessageH\000B\t\n\007payload\"\321\001\n\016StreamResp" + - "onse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\"\n\007me" + - "ssage\030\002 \001(\0132\017.a2a.v1.MessageH\000\0226\n\rstatus" + - "_update\030\003 \001(\0132\035.a2a.v1.TaskStatusUpdateE" + - "ventH\000\022:\n\017artifact_update\030\004 \001(\0132\037.a2a.v1" + - ".TaskArtifactUpdateEventH\000B\t\n\007payload\"v\n" + - "&ListTaskPushNotificationConfigResponse\022" + - "3\n\007configs\030\001 \003(\0132\".a2a.v1.TaskPushNotifi" + - "cationConfig\022\027\n\017next_page_token\030\002 \001(\t*\372\001" + - "\n\tTaskState\022\032\n\026TASK_STATE_UNSPECIFIED\020\000\022" + - "\030\n\024TASK_STATE_SUBMITTED\020\001\022\026\n\022TASK_STATE_" + - "WORKING\020\002\022\030\n\024TASK_STATE_COMPLETED\020\003\022\025\n\021T" + - "ASK_STATE_FAILED\020\004\022\030\n\024TASK_STATE_CANCELL" + - "ED\020\005\022\035\n\031TASK_STATE_INPUT_REQUIRED\020\006\022\027\n\023T" + - "ASK_STATE_REJECTED\020\007\022\034\n\030TASK_STATE_AUTH_" + - "REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE_UNSPECIFIED\020\000" + - "\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AGENT\020\0022\276\016\n\nA2AS" + - "ervice\022}\n\013SendMessage\022\032.a2a.v1.SendMessa" + - "geRequest\032\033.a2a.v1.SendMessageResponse\"5" + - "\202\323\344\223\002/\"\r/message:send:\001*Z\033\"\026/{tenant}/me" + - "ssage:send:\001*\022\207\001\n\024SendStreamingMessage\022\032" + - ".a2a.v1.SendMessageRequest\032\026.a2a.v1.Stre" + - "amResponse\"9\202\323\344\223\0023\"\017/message:stream:\001*Z\035" + - "\"\030/{tenant}/message:stream:\001*0\001\022k\n\007GetTa" + - "sk\022\026.a2a.v1.GetTaskRequest\032\014.a2a.v1.Task" + - "\":\332A\004name\202\323\344\223\002-\022\017/{name=tasks/*}Z\032\022\030/{te" + - "nant}/{name=tasks/*}\022c\n\tListTasks\022\030.a2a." + - "v1.ListTasksRequest\032\031.a2a.v1.ListTasksRe" + - "sponse\"!\202\323\344\223\002\033\022\006/tasksZ\021\022\017/{tenant}/task" + - "s\022~\n\nCancelTask\022\031.a2a.v1.CancelTaskReque" + - "st\032\014.a2a.v1.Task\"G\202\323\344\223\002A\"\026/{name=tasks/*" + - "}:cancel:\001*Z$\"\037/{tenant}/{name=tasks/*}:" + - "cancel:\001*\022\224\001\n\017SubscribeToTask\022\036.a2a.v1.S" + - "ubscribeToTaskRequest\032\026.a2a.v1.StreamRes" + - "ponse\"G\202\323\344\223\002A\022\031/{name=tasks/*}:subscribe" + - "Z$\022\"/{tenant}/{name=tasks/*}:subscribe0\001" + - "\022\373\001\n\035SetTaskPushNotificationConfig\022,.a2a" + - ".v1.SetTaskPushNotificationConfigRequest" + - "\032\".a2a.v1.TaskPushNotificationConfig\"\207\001\332" + - "A\rparent,config\202\323\344\223\002q\")/{parent=tasks/*/" + - "pushNotificationConfigs}:\006configZ<\"2/{te" + - "nant}/{parent=tasks/*/pushNotificationCo" + - "nfigs}:\006config\022\341\001\n\035GetTaskPushNotificati" + - "onConfig\022,.a2a.v1.GetTaskPushNotificatio" + - "nConfigRequest\032\".a2a.v1.TaskPushNotifica" + - "tionConfig\"n\332A\004name\202\323\344\223\002a\022)/{name=tasks/" + - "*/pushNotificationConfigs/*}Z4\0222/{tenant" + - "}/{name=tasks/*/pushNotificationConfigs/" + - "*}\022\361\001\n\036ListTaskPushNotificationConfig\022-." + - "a2a.v1.ListTaskPushNotificationConfigReq" + - "uest\032..a2a.v1.ListTaskPushNotificationCo" + - "nfigResponse\"p\332A\006parent\202\323\344\223\002a\022)/{parent=" + - "tasks/*}/pushNotificationConfigsZ4\0222/{te" + - "nant}/{parent=tasks/*}/pushNotificationC" + - "onfigs\022\211\001\n\024GetExtendedAgentCard\022#.a2a.v1" + - ".GetExtendedAgentCardRequest\032\021.a2a.v1.Ag" + - "entCard\"9\202\323\344\223\0023\022\022/extendedAgentCardZ\035\022\033/" + - "{tenant}/extendedAgentCard\022\333\001\n DeleteTas" + - "kPushNotificationConfig\022/.a2a.v1.DeleteT" + - "askPushNotificationConfigRequest\032\026.googl" + - "e.protobuf.Empty\"n\332A\004name\202\323\344\223\002a*)/{name=" + - "tasks/*/pushNotificationConfigs/*}Z4*2/{" + - "tenant}/{name=tasks/*/pushNotificationCo" + - "nfigs/*}B7\n\013io.a2a.grpcB\003A2AP\001Z\030google.g" + - "olang.org/a2a/v1\252\002\006A2a.V1b\006proto3" + "2\026.a2a.v1.AgentExtension\022 \n\023extended_age" + + "nt_card\030\005 \001(\010H\002\210\001\001B\014\n\n_streamingB\025\n\023_pus" + + "h_notificationsB\026\n\024_extended_agent_cardJ" + + "\004\010\004\020\005\"m\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013" + + "description\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006p" + + "arams\030\004 \001(\0132\027.google.protobuf.Struct\"\276\001\n" + + "\nAgentSkill\022\017\n\002id\030\001 \001(\tB\003\340A\002\022\021\n\004name\030\002 \001" + + "(\tB\003\340A\002\022\030\n\013description\030\003 \001(\tB\003\340A\002\022\021\n\004tag" + + "s\030\004 \003(\tB\003\340A\002\022\020\n\010examples\030\005 \003(\t\022\023\n\013input_" + + "modes\030\006 \003(\t\022\024\n\014output_modes\030\007 \003(\t\022\"\n\010sec" + + "urity\030\010 \003(\0132\020.a2a.v1.Security\"m\n\022AgentCa" + + "rdSignature\022\026\n\tprotected\030\001 \001(\tB\003\340A\002\022\026\n\ts" + + "ignature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\0132\027.go" + + "ogle.protobuf.Struct\"v\n\032TaskPushNotifica" + + "tionConfig\022\021\n\004name\030\001 \001(\tB\003\340A\002\022E\n\030push_no" + + "tification_config\030\002 \001(\0132\036.a2a.v1.PushNot" + + "ificationConfigB\003\340A\002\"\032\n\nStringList\022\014\n\004li" + + "st\030\001 \003(\t\"~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035." + + "a2a.v1.Security.SchemesEntry\032B\n\014SchemesE" + + "ntry\022\013\n\003key\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v" + + "1.StringList:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027a" + + "pi_key_security_scheme\030\001 \001(\0132\034.a2a.v1.AP" + + "IKeySecuritySchemeH\000\022C\n\031http_auth_securi" + + "ty_scheme\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecurit" + + "ySchemeH\000\022>\n\026oauth2_security_scheme\030\003 \001(" + + "\0132\034.a2a.v1.OAuth2SecuritySchemeH\000\022N\n\037ope" + + "n_id_connect_security_scheme\030\004 \001(\0132#.a2a" + + ".v1.OpenIdConnectSecuritySchemeH\000\022?\n\024mtl" + + "s_security_scheme\030\005 \001(\0132\037.a2a.v1.MutualT" + + "lsSecuritySchemeH\000B\010\n\006scheme\"U\n\024APIKeySe" + + "curityScheme\022\023\n\013description\030\001 \001(\t\022\025\n\010loc" + + "ation\030\002 \001(\tB\003\340A\002\022\021\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026H" + + "TTPAuthSecurityScheme\022\023\n\013description\030\001 \001" + + "(\t\022\023\n\006scheme\030\002 \001(\tB\003\340A\002\022\025\n\rbearer_format" + + "\030\003 \001(\t\"p\n\024OAuth2SecurityScheme\022\023\n\013descri" + + "ption\030\001 \001(\t\022&\n\005flows\030\002 \001(\0132\022.a2a.v1.OAut" + + "hFlowsB\003\340A\002\022\033\n\023oauth2_metadata_url\030\003 \001(\t" + + "\"T\n\033OpenIdConnectSecurityScheme\022\023\n\013descr" + + "iption\030\001 \001(\t\022 \n\023open_id_connect_url\030\002 \001(" + + "\tB\003\340A\002\".\n\027MutualTlsSecurityScheme\022\023\n\013des" + + "cription\030\001 \001(\t\"\330\001\n\nOAuthFlows\022@\n\022authori" + + "zation_code\030\001 \001(\0132\".a2a.v1.Authorization" + + "CodeOAuthFlowH\000\022@\n\022client_credentials\030\002 " + + "\001(\0132\".a2a.v1.ClientCredentialsOAuthFlowH" + + "\000\0222\n\013device_code\030\005 \001(\0132\033.a2a.v1.DeviceCo" + + "deOAuthFlowH\000B\006\n\004flowJ\004\010\003\020\004J\004\010\004\020\005\"\364\001\n\032Au" + + "thorizationCodeOAuthFlow\022\036\n\021authorizatio" + + "n_url\030\001 \001(\tB\003\340A\002\022\026\n\ttoken_url\030\002 \001(\tB\003\340A\002" + + "\022\023\n\013refresh_url\030\003 \001(\t\022C\n\006scopes\030\004 \003(\0132.." + + "a2a.v1.AuthorizationCodeOAuthFlow.Scopes" + + "EntryB\003\340A\002\022\025\n\rpkce_required\030\005 \001(\010\032-\n\013Sco" + + "pesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001" + + "\"\275\001\n\032ClientCredentialsOAuthFlow\022\026\n\ttoken" + + "_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022C\n\006" + + "scopes\030\003 \003(\0132..a2a.v1.ClientCredentialsO" + + "AuthFlow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry" + + "\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\326\001\n\023Dev" + + "iceCodeOAuthFlow\022%\n\030device_authorization" + + "_url\030\001 \001(\tB\003\340A\002\022\026\n\ttoken_url\030\002 \001(\tB\003\340A\002\022" + + "\023\n\013refresh_url\030\003 \001(\t\022<\n\006scopes\030\004 \003(\0132\'.a" + + "2a.v1.DeviceCodeOAuthFlow.ScopesEntryB\003\340" + + "A\002\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030" + + "\002 \001(\t:\0028\001\"\257\001\n\022SendMessageRequest\022\016\n\006tena" + + "nt\030\004 \001(\t\022%\n\007message\030\001 \001(\0132\017.a2a.v1.Messa" + + "geB\003\340A\002\0227\n\rconfiguration\030\002 \001(\0132 .a2a.v1." + + "SendMessageConfiguration\022)\n\010metadata\030\003 \001" + + "(\0132\027.google.protobuf.Struct\"c\n\016GetTaskRe" + + "quest\022\016\n\006tenant\030\003 \001(\t\022\021\n\004name\030\001 \001(\tB\003\340A\002" + + "\022\033\n\016history_length\030\002 \001(\005H\000\210\001\001B\021\n\017_histor" + + "y_length\"\265\002\n\020ListTasksRequest\022\016\n\006tenant\030" + + "\t \001(\t\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006status\030\002 \001(" + + "\0162\021.a2a.v1.TaskState\022\026\n\tpage_size\030\003 \001(\005H" + + "\000\210\001\001\022\022\n\npage_token\030\004 \001(\t\022\033\n\016history_leng" + + "th\030\005 \001(\005H\001\210\001\001\022:\n\026status_timestamp_after\030" + + "\006 \001(\0132\032.google.protobuf.Timestamp\022\036\n\021inc" + + "lude_artifacts\030\007 \001(\010H\002\210\001\001B\014\n\n_page_sizeB" + + "\021\n\017_history_lengthB\024\n\022_include_artifacts" + + "\"\204\001\n\021ListTasksResponse\022 \n\005tasks\030\001 \003(\0132\014." + + "a2a.v1.TaskB\003\340A\002\022\034\n\017next_page_token\030\002 \001(" + + "\tB\003\340A\002\022\026\n\tpage_size\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_" + + "size\030\004 \001(\005B\003\340A\002\"1\n\021CancelTaskRequest\022\016\n\006" + + "tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"D\n$GetTaskPus" + + "hNotificationConfigRequest\022\016\n\006tenant\030\002 \001" + + "(\t\022\014\n\004name\030\001 \001(\t\"G\n\'DeleteTaskPushNotifi" + + "cationConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004n" + + "ame\030\001 \001(\t\"\237\001\n\'CreateTaskPushNotification" + + "ConfigRequest\022\016\n\006tenant\030\004 \001(\t\022\023\n\006parent\030" + + "\001 \001(\tB\003\340A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227\n\006co" + + "nfig\030\003 \001(\0132\".a2a.v1.TaskPushNotification" + + "ConfigB\003\340A\002\"6\n\026SubscribeToTaskRequest\022\016\n" + + "\006tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"n\n%ListTaskP" + + "ushNotificationConfigRequest\022\016\n\006tenant\030\004" + + " \001(\t\022\016\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022" + + "\022\n\npage_token\030\003 \001(\t\"-\n\033GetExtendedAgentC" + + "ardRequest\022\016\n\006tenant\030\001 \001(\t\"b\n\023SendMessag" + + "eResponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022" + + "\"\n\007message\030\002 \001(\0132\017.a2a.v1.MessageH\000B\t\n\007p" + + "ayload\"\321\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\0132" + + "\014.a2a.v1.TaskH\000\022\"\n\007message\030\002 \001(\0132\017.a2a.v" + + "1.MessageH\000\0226\n\rstatus_update\030\003 \001(\0132\035.a2a" + + ".v1.TaskStatusUpdateEventH\000\022:\n\017artifact_" + + "update\030\004 \001(\0132\037.a2a.v1.TaskArtifactUpdate" + + "EventH\000B\t\n\007payload\"v\n&ListTaskPushNotifi" + + "cationConfigResponse\0223\n\007configs\030\001 \003(\0132\"." + + "a2a.v1.TaskPushNotificationConfig\022\027\n\017nex" + + "t_page_token\030\002 \001(\t*\372\001\n\tTaskState\022\032\n\026TASK" + + "_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STATE_SUBMI" + + "TTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024TASK_S" + + "TATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FAILED\020\004\022" + + "\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK_STATE_" + + "INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_REJECTED\020" + + "\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;\n\004Role\022" + + "\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USER\020\001\022\016\n\n" + + "ROLE_AGENT\020\0022\304\016\n\nA2AService\022}\n\013SendMessa" + + "ge\022\032.a2a.v1.SendMessageRequest\032\033.a2a.v1." + + "SendMessageResponse\"5\202\323\344\223\002/\"\r/message:se" + + "nd:\001*Z\033\"\026/{tenant}/message:send:\001*\022\207\001\n\024S" + + "endStreamingMessage\022\032.a2a.v1.SendMessage" + + "Request\032\026.a2a.v1.StreamResponse\"9\202\323\344\223\0023\"" + + "\017/message:stream:\001*Z\035\"\030/{tenant}/message" + + ":stream:\001*0\001\022k\n\007GetTask\022\026.a2a.v1.GetTask" + + "Request\032\014.a2a.v1.Task\":\332A\004name\202\323\344\223\002-\022\017/{" + + "name=tasks/*}Z\032\022\030/{tenant}/{name=tasks/*" + + "}\022c\n\tListTasks\022\030.a2a.v1.ListTasksRequest" + + "\032\031.a2a.v1.ListTasksResponse\"!\202\323\344\223\002\033\022\006/ta" + + "sksZ\021\022\017/{tenant}/tasks\022~\n\nCancelTask\022\031.a" + + "2a.v1.CancelTaskRequest\032\014.a2a.v1.Task\"G\202" + + "\323\344\223\002A\"\026/{name=tasks/*}:cancel:\001*Z$\"\037/{te" + + "nant}/{name=tasks/*}:cancel:\001*\022\224\001\n\017Subsc" + + "ribeToTask\022\036.a2a.v1.SubscribeToTaskReque" + + "st\032\026.a2a.v1.StreamResponse\"G\202\323\344\223\002A\022\031/{na" + + "me=tasks/*}:subscribeZ$\022\"/{tenant}/{name" + + "=tasks/*}:subscribe0\001\022\201\002\n CreateTaskPush" + + "NotificationConfig\022/.a2a.v1.CreateTaskPu" + + "shNotificationConfigRequest\032\".a2a.v1.Tas" + + "kPushNotificationConfig\"\207\001\332A\rparent,conf" + + "ig\202\323\344\223\002q\")/{parent=tasks/*/pushNotificat" + + "ionConfigs}:\006configZ<\"2/{tenant}/{parent" + + "=tasks/*/pushNotificationConfigs}:\006confi" + + "g\022\341\001\n\035GetTaskPushNotificationConfig\022,.a2" + + "a.v1.GetTaskPushNotificationConfigReques" + + "t\032\".a2a.v1.TaskPushNotificationConfig\"n\332" + + "A\004name\202\323\344\223\002a\022)/{name=tasks/*/pushNotific" + + "ationConfigs/*}Z4\0222/{tenant}/{name=tasks" + + "/*/pushNotificationConfigs/*}\022\361\001\n\036ListTa" + + "skPushNotificationConfig\022-.a2a.v1.ListTa" + + "skPushNotificationConfigRequest\032..a2a.v1" + + ".ListTaskPushNotificationConfigResponse\"" + + "p\332A\006parent\202\323\344\223\002a\022)/{parent=tasks/*}/push" + + "NotificationConfigsZ4\0222/{tenant}/{parent" + + "=tasks/*}/pushNotificationConfigs\022\211\001\n\024Ge" + + "tExtendedAgentCard\022#.a2a.v1.GetExtendedA" + + "gentCardRequest\032\021.a2a.v1.AgentCard\"9\202\323\344\223" + + "\0023\022\022/extendedAgentCardZ\035\022\033/{tenant}/exte" + + "ndedAgentCard\022\333\001\n DeleteTaskPushNotifica" + + "tionConfig\022/.a2a.v1.DeleteTaskPushNotifi" + + "cationConfigRequest\032\026.google.protobuf.Em" + + "pty\"n\332A\004name\202\323\344\223\002a*)/{name=tasks/*/pushN" + + "otificationConfigs/*}Z4*2/{tenant}/{name" + + "=tasks/*/pushNotificationConfigs/*}B7\n\013i" + + "o.a2a.grpcB\003A2AP\001Z\030google.golang.org/a2a" + + "/v1\252\002\006A2a.V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -639,7 +638,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCapabilities_descriptor, - new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "StateTransitionHistory", "ExtendedAgentCard", }); + new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "ExtendedAgentCard", }); internal_static_a2a_v1_AgentExtension_descriptor = getDescriptor().getMessageType(16); internal_static_a2a_v1_AgentExtension_fieldAccessorTable = new @@ -802,11 +801,11 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Tenant", "Name", }); - internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor = + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageType(39); - internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable = new + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor, + internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor, new java.lang.String[] { "Tenant", "Parent", "ConfigId", "Config", }); internal_static_a2a_v1_SubscribeToTaskRequest_descriptor = getDescriptor().getMessageType(40); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java index 0faa8f73d..522c9083b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -201,35 +201,35 @@ io.a2a.grpc.StreamResponse> getSubscribeToTaskMethod() { return getSubscribeToTaskMethod; } - private static volatile io.grpc.MethodDescriptor getSetTaskPushNotificationConfigMethod; + private static volatile io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod; @io.grpc.stub.annotations.RpcMethod( - fullMethodName = SERVICE_NAME + '/' + "SetTaskPushNotificationConfig", - requestType = io.a2a.grpc.SetTaskPushNotificationConfigRequest.class, + fullMethodName = SERVICE_NAME + '/' + "CreateTaskPushNotificationConfig", + requestType = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, responseType = io.a2a.grpc.TaskPushNotificationConfig.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) - public static io.grpc.MethodDescriptor getSetTaskPushNotificationConfigMethod() { - io.grpc.MethodDescriptor getSetTaskPushNotificationConfigMethod; - if ((getSetTaskPushNotificationConfigMethod = A2AServiceGrpc.getSetTaskPushNotificationConfigMethod) == null) { + public static io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod() { + io.grpc.MethodDescriptor getCreateTaskPushNotificationConfigMethod; + if ((getCreateTaskPushNotificationConfigMethod = A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod) == null) { synchronized (A2AServiceGrpc.class) { - if ((getSetTaskPushNotificationConfigMethod = A2AServiceGrpc.getSetTaskPushNotificationConfigMethod) == null) { - A2AServiceGrpc.getSetTaskPushNotificationConfigMethod = getSetTaskPushNotificationConfigMethod = - io.grpc.MethodDescriptor.newBuilder() + if ((getCreateTaskPushNotificationConfigMethod = A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod) == null) { + A2AServiceGrpc.getCreateTaskPushNotificationConfigMethod = getCreateTaskPushNotificationConfigMethod = + io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.UNARY) - .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SetTaskPushNotificationConfig")) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "CreateTaskPushNotificationConfig")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( - io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance())) + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance())) - .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("SetTaskPushNotificationConfig")) + .setSchemaDescriptor(new A2AServiceMethodDescriptorSupplier("CreateTaskPushNotificationConfig")) .build(); } } } - return getSetTaskPushNotificationConfigMethod; + return getCreateTaskPushNotificationConfigMethod; } private static volatile io.grpc.MethodDescriptor - * Set a push notification config for a task. + * Create a push notification config for a task. * */ - default void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request, + default void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, io.grpc.stub.StreamObserver responseObserver) { - io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getSetTaskPushNotificationConfigMethod(), responseObserver); + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCreateTaskPushNotificationConfigMethod(), responseObserver); } /** @@ -636,13 +636,13 @@ public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, /** *

    -     * Set a push notification config for a task.
    +     * Create a push notification config for a task.
          * 
    */ - public void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request, + public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, io.grpc.stub.StreamObserver responseObserver) { io.grpc.stub.ClientCalls.asyncUnaryCall( - getChannel().newCall(getSetTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); + getChannel().newCall(getCreateTaskPushNotificationConfigMethod(), getCallOptions()), request, responseObserver); } /** @@ -776,12 +776,12 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) throws /** *
    -     * Set a push notification config for a task.
    +     * Create a push notification config for a task.
          * 
    */ - public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request) throws io.grpc.StatusException { + public io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) throws io.grpc.StatusException { return io.grpc.stub.ClientCalls.blockingV2UnaryCall( - getChannel(), getSetTaskPushNotificationConfigMethod(), getCallOptions(), request); + getChannel(), getCreateTaskPushNotificationConfigMethod(), getCallOptions(), request); } /** @@ -909,12 +909,12 @@ public java.util.Iterator subscribeToTask( /** *
    -     * Set a push notification config for a task.
    +     * Create a push notification config for a task.
          * 
    */ - public io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request) { + public io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { return io.grpc.stub.ClientCalls.blockingUnaryCall( - getChannel(), getSetTaskPushNotificationConfigMethod(), getCallOptions(), request); + getChannel(), getCreateTaskPushNotificationConfigMethod(), getCallOptions(), request); } /** @@ -1023,13 +1023,13 @@ public com.google.common.util.concurrent.ListenableFuture canc /** *
    -     * Set a push notification config for a task.
    +     * Create a push notification config for a task.
          * 
    */ - public com.google.common.util.concurrent.ListenableFuture setTaskPushNotificationConfig( - io.a2a.grpc.SetTaskPushNotificationConfigRequest request) { + public com.google.common.util.concurrent.ListenableFuture createTaskPushNotificationConfig( + io.a2a.grpc.CreateTaskPushNotificationConfigRequest request) { return io.grpc.stub.ClientCalls.futureUnaryCall( - getChannel().newCall(getSetTaskPushNotificationConfigMethod(), getCallOptions()), request); + getChannel().newCall(getCreateTaskPushNotificationConfigMethod(), getCallOptions()), request); } /** @@ -1083,7 +1083,7 @@ public com.google.common.util.concurrent.ListenableFuture responseObserv serviceImpl.subscribeToTask((io.a2a.grpc.SubscribeToTaskRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; - case METHODID_SET_TASK_PUSH_NOTIFICATION_CONFIG: - serviceImpl.setTaskPushNotificationConfig((io.a2a.grpc.SetTaskPushNotificationConfigRequest) request, + case METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG: + serviceImpl.createTaskPushNotificationConfig((io.a2a.grpc.CreateTaskPushNotificationConfigRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; case METHODID_GET_TASK_PUSH_NOTIFICATION_CONFIG: @@ -1211,12 +1211,12 @@ public static final io.grpc.ServerServiceDefinition bindService(AsyncService ser io.a2a.grpc.StreamResponse>( service, METHODID_SUBSCRIBE_TO_TASK))) .addMethod( - getSetTaskPushNotificationConfigMethod(), + getCreateTaskPushNotificationConfigMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( new MethodHandlers< - io.a2a.grpc.SetTaskPushNotificationConfigRequest, + io.a2a.grpc.CreateTaskPushNotificationConfigRequest, io.a2a.grpc.TaskPushNotificationConfig>( - service, METHODID_SET_TASK_PUSH_NOTIFICATION_CONFIG))) + service, METHODID_CREATE_TASK_PUSH_NOTIFICATION_CONFIG))) .addMethod( getGetTaskPushNotificationConfigMethod(), io.grpc.stub.ServerCalls.asyncUnaryCall( @@ -1299,7 +1299,7 @@ public static io.grpc.ServiceDescriptor getServiceDescriptor() { .addMethod(getListTasksMethod()) .addMethod(getCancelTaskMethod()) .addMethod(getSubscribeToTaskMethod()) - .addMethod(getSetTaskPushNotificationConfigMethod()) + .addMethod(getCreateTaskPushNotificationConfigMethod()) .addMethod(getGetTaskPushNotificationConfigMethod()) .addMethod(getListTaskPushNotificationConfigMethod()) .addMethod(getGetExtendedAgentCardMethod()) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java index 2e6c44ab8..84796af69 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilities.java @@ -165,33 +165,6 @@ public io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder( return extensions_.get(index); } - public static final int STATE_TRANSITION_HISTORY_FIELD_NUMBER = 4; - private boolean stateTransitionHistory_ = false; - /** - *
    -   * Indicates if the agent provides a history of state transitions for a task.
    -   * 
    - * - * optional bool state_transition_history = 4; - * @return Whether the stateTransitionHistory field is set. - */ - @java.lang.Override - public boolean hasStateTransitionHistory() { - return ((bitField0_ & 0x00000004) != 0); - } - /** - *
    -   * Indicates if the agent provides a history of state transitions for a task.
    -   * 
    - * - * optional bool state_transition_history = 4; - * @return The stateTransitionHistory. - */ - @java.lang.Override - public boolean getStateTransitionHistory() { - return stateTransitionHistory_; - } - public static final int EXTENDED_AGENT_CARD_FIELD_NUMBER = 5; private boolean extendedAgentCard_ = false; /** @@ -204,7 +177,7 @@ public boolean getStateTransitionHistory() { */ @java.lang.Override public boolean hasExtendedAgentCard() { - return ((bitField0_ & 0x00000008) != 0); + return ((bitField0_ & 0x00000004) != 0); } /** *
    @@ -243,9 +216,6 @@ public void writeTo(com.google.protobuf.CodedOutputStream output)
           output.writeMessage(3, extensions_.get(i));
         }
         if (((bitField0_ & 0x00000004) != 0)) {
    -      output.writeBool(4, stateTransitionHistory_);
    -    }
    -    if (((bitField0_ & 0x00000008) != 0)) {
           output.writeBool(5, extendedAgentCard_);
         }
         getUnknownFields().writeTo(output);
    @@ -270,10 +240,6 @@ public int getSerializedSize() {
             .computeMessageSize(3, extensions_.get(i));
         }
         if (((bitField0_ & 0x00000004) != 0)) {
    -      size += com.google.protobuf.CodedOutputStream
    -        .computeBoolSize(4, stateTransitionHistory_);
    -    }
    -    if (((bitField0_ & 0x00000008) != 0)) {
           size += com.google.protobuf.CodedOutputStream
             .computeBoolSize(5, extendedAgentCard_);
         }
    @@ -304,11 +270,6 @@ public boolean equals(final java.lang.Object obj) {
         }
         if (!getExtensionsList()
             .equals(other.getExtensionsList())) return false;
    -    if (hasStateTransitionHistory() != other.hasStateTransitionHistory()) return false;
    -    if (hasStateTransitionHistory()) {
    -      if (getStateTransitionHistory()
    -          != other.getStateTransitionHistory()) return false;
    -    }
         if (hasExtendedAgentCard() != other.hasExtendedAgentCard()) return false;
         if (hasExtendedAgentCard()) {
           if (getExtendedAgentCard()
    @@ -339,11 +300,6 @@ public int hashCode() {
           hash = (37 * hash) + EXTENSIONS_FIELD_NUMBER;
           hash = (53 * hash) + getExtensionsList().hashCode();
         }
    -    if (hasStateTransitionHistory()) {
    -      hash = (37 * hash) + STATE_TRANSITION_HISTORY_FIELD_NUMBER;
    -      hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(
    -          getStateTransitionHistory());
    -    }
         if (hasExtendedAgentCard()) {
           hash = (37 * hash) + EXTENDED_AGENT_CARD_FIELD_NUMBER;
           hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(
    @@ -494,7 +450,6 @@ public Builder clear() {
             extensionsBuilder_.clear();
           }
           bitField0_ = (bitField0_ & ~0x00000004);
    -      stateTransitionHistory_ = false;
           extendedAgentCard_ = false;
           return this;
         }
    @@ -552,12 +507,8 @@ private void buildPartial0(io.a2a.grpc.AgentCapabilities result) {
             to_bitField0_ |= 0x00000002;
           }
           if (((from_bitField0_ & 0x00000008) != 0)) {
    -        result.stateTransitionHistory_ = stateTransitionHistory_;
    -        to_bitField0_ |= 0x00000004;
    -      }
    -      if (((from_bitField0_ & 0x00000010) != 0)) {
             result.extendedAgentCard_ = extendedAgentCard_;
    -        to_bitField0_ |= 0x00000008;
    +        to_bitField0_ |= 0x00000004;
           }
           result.bitField0_ |= to_bitField0_;
         }
    @@ -606,9 +557,6 @@ public Builder mergeFrom(io.a2a.grpc.AgentCapabilities other) {
               }
             }
           }
    -      if (other.hasStateTransitionHistory()) {
    -        setStateTransitionHistory(other.getStateTransitionHistory());
    -      }
           if (other.hasExtendedAgentCard()) {
             setExtendedAgentCard(other.getExtendedAgentCard());
           }
    @@ -661,14 +609,9 @@ public Builder mergeFrom(
                   }
                   break;
                 } // case 26
    -            case 32: {
    -              stateTransitionHistory_ = input.readBool();
    -              bitField0_ |= 0x00000008;
    -              break;
    -            } // case 32
                 case 40: {
                   extendedAgentCard_ = input.readBool();
    -              bitField0_ |= 0x00000010;
    +              bitField0_ |= 0x00000008;
                   break;
                 } // case 40
                 default: {
    @@ -1112,62 +1055,6 @@ public io.a2a.grpc.AgentExtension.Builder addExtensionsBuilder(
           return extensionsBuilder_;
         }
     
    -    private boolean stateTransitionHistory_ ;
    -    /**
    -     * 
    -     * Indicates if the agent provides a history of state transitions for a task.
    -     * 
    - * - * optional bool state_transition_history = 4; - * @return Whether the stateTransitionHistory field is set. - */ - @java.lang.Override - public boolean hasStateTransitionHistory() { - return ((bitField0_ & 0x00000008) != 0); - } - /** - *
    -     * Indicates if the agent provides a history of state transitions for a task.
    -     * 
    - * - * optional bool state_transition_history = 4; - * @return The stateTransitionHistory. - */ - @java.lang.Override - public boolean getStateTransitionHistory() { - return stateTransitionHistory_; - } - /** - *
    -     * Indicates if the agent provides a history of state transitions for a task.
    -     * 
    - * - * optional bool state_transition_history = 4; - * @param value The stateTransitionHistory to set. - * @return This builder for chaining. - */ - public Builder setStateTransitionHistory(boolean value) { - - stateTransitionHistory_ = value; - bitField0_ |= 0x00000008; - onChanged(); - return this; - } - /** - *
    -     * Indicates if the agent provides a history of state transitions for a task.
    -     * 
    - * - * optional bool state_transition_history = 4; - * @return This builder for chaining. - */ - public Builder clearStateTransitionHistory() { - bitField0_ = (bitField0_ & ~0x00000008); - stateTransitionHistory_ = false; - onChanged(); - return this; - } - private boolean extendedAgentCard_ ; /** *
    @@ -1179,7 +1066,7 @@ public Builder clearStateTransitionHistory() {
          */
         @java.lang.Override
         public boolean hasExtendedAgentCard() {
    -      return ((bitField0_ & 0x00000010) != 0);
    +      return ((bitField0_ & 0x00000008) != 0);
         }
         /**
          * 
    @@ -1205,7 +1092,7 @@ public boolean getExtendedAgentCard() {
         public Builder setExtendedAgentCard(boolean value) {
     
           extendedAgentCard_ = value;
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -1218,7 +1105,7 @@ public Builder setExtendedAgentCard(boolean value) {
          * @return This builder for chaining.
          */
         public Builder clearExtendedAgentCard() {
    -      bitField0_ = (bitField0_ & ~0x00000010);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           extendedAgentCard_ = false;
           onChanged();
           return this;
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java
    index b084db7ee..05c90e4d2 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCapabilitiesOrBuilder.java
    @@ -92,25 +92,6 @@ public interface AgentCapabilitiesOrBuilder extends
       io.a2a.grpc.AgentExtensionOrBuilder getExtensionsOrBuilder(
           int index);
     
    -  /**
    -   * 
    -   * Indicates if the agent provides a history of state transitions for a task.
    -   * 
    - * - * optional bool state_transition_history = 4; - * @return Whether the stateTransitionHistory field is set. - */ - boolean hasStateTransitionHistory(); - /** - *
    -   * Indicates if the agent provides a history of state transitions for a task.
    -   * 
    - * - * optional bool state_transition_history = 4; - * @return The stateTransitionHistory. - */ - boolean getStateTransitionHistory(); - /** *
        * Indicates if the agent supports providing an extended agent card when authenticated.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java
    index 9ba0ba831..cb1e12880 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java
    @@ -8,7 +8,7 @@
     /**
      * 
      * --8<-- [start:CancelTaskRequest]
    - * Represents a request for the `tasks/cancel` method.
    + * Represents a request for the `CancelTask` method.
      * 
    * * Protobuf type {@code a2a.v1.CancelTaskRequest} @@ -315,7 +315,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:CancelTaskRequest]
    -   * Represents a request for the `tasks/cancel` method.
    +   * Represents a request for the `CancelTask` method.
        * 
    * * Protobuf type {@code a2a.v1.CancelTaskRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java similarity index 85% rename from spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java rename to spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java index 161a1b257..5f8143cb3 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java @@ -7,17 +7,17 @@ /** *
    - * --8<-- [start:SetTaskPushNotificationConfigRequest]
    - * Represents a request for the `tasks/pushNotificationConfig/set` method.
    + * --8<-- [start:CreateTaskPushNotificationConfigRequest]
    + * Represents a request for the `CreateTaskPushNotificationConfig` method.
      * 
    * - * Protobuf type {@code a2a.v1.SetTaskPushNotificationConfigRequest} + * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} */ @com.google.protobuf.Generated -public final class SetTaskPushNotificationConfigRequest extends +public final class CreateTaskPushNotificationConfigRequest extends com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.SetTaskPushNotificationConfigRequest) - SetTaskPushNotificationConfigRequestOrBuilder { + // @@protoc_insertion_point(message_implements:a2a.v1.CreateTaskPushNotificationConfigRequest) + CreateTaskPushNotificationConfigRequestOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( @@ -26,13 +26,13 @@ public final class SetTaskPushNotificationConfigRequest extends /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - "SetTaskPushNotificationConfigRequest"); + "CreateTaskPushNotificationConfigRequest"); } - // Use SetTaskPushNotificationConfigRequest.newBuilder() to construct. - private SetTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { + // Use CreateTaskPushNotificationConfigRequest.newBuilder() to construct. + private CreateTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } - private SetTaskPushNotificationConfigRequest() { + private CreateTaskPushNotificationConfigRequest() { tenant_ = ""; parent_ = ""; configId_ = ""; @@ -40,15 +40,15 @@ private SetTaskPushNotificationConfigRequest() { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.SetTaskPushNotificationConfigRequest.class, io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder.class); + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder.class); } private int bitField0_; @@ -291,10 +291,10 @@ public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } - if (!(obj instanceof io.a2a.grpc.SetTaskPushNotificationConfigRequest)) { + if (!(obj instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest)) { return super.equals(obj); } - io.a2a.grpc.SetTaskPushNotificationConfigRequest other = (io.a2a.grpc.SetTaskPushNotificationConfigRequest) obj; + io.a2a.grpc.CreateTaskPushNotificationConfigRequest other = (io.a2a.grpc.CreateTaskPushNotificationConfigRequest) obj; if (!getTenant() .equals(other.getTenant())) return false; @@ -333,44 +333,44 @@ public int hashCode() { return hash; } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom(byte[] data) + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -378,26 +378,26 @@ public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( .parseWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseDelimitedFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -410,7 +410,7 @@ public static io.a2a.grpc.SetTaskPushNotificationConfigRequest parseFrom( public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(io.a2a.grpc.SetTaskPushNotificationConfigRequest prototype) { + public static Builder newBuilder(io.a2a.grpc.CreateTaskPushNotificationConfigRequest prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override @@ -427,30 +427,30 @@ protected Builder newBuilderForType( } /** *
    -   * --8<-- [start:SetTaskPushNotificationConfigRequest]
    -   * Represents a request for the `tasks/pushNotificationConfig/set` method.
    +   * --8<-- [start:CreateTaskPushNotificationConfigRequest]
    +   * Represents a request for the `CreateTaskPushNotificationConfig` method.
        * 
    * - * Protobuf type {@code a2a.v1.SetTaskPushNotificationConfigRequest} + * Protobuf type {@code a2a.v1.CreateTaskPushNotificationConfigRequest} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.SetTaskPushNotificationConfigRequest) - io.a2a.grpc.SetTaskPushNotificationConfigRequestOrBuilder { + // @@protoc_insertion_point(builder_implements:a2a.v1.CreateTaskPushNotificationConfigRequest) + io.a2a.grpc.CreateTaskPushNotificationConfigRequestOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; } @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.SetTaskPushNotificationConfigRequest.class, io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder.class); + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.class, io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder.class); } - // Construct using io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder() + // Construct using io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder() private Builder() { maybeForceBuilderInitialization(); } @@ -484,17 +484,17 @@ public Builder clear() { @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_SetTaskPushNotificationConfigRequest_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor; } @java.lang.Override - public io.a2a.grpc.SetTaskPushNotificationConfigRequest getDefaultInstanceForType() { - return io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance(); + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstanceForType() { + return io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance(); } @java.lang.Override - public io.a2a.grpc.SetTaskPushNotificationConfigRequest build() { - io.a2a.grpc.SetTaskPushNotificationConfigRequest result = buildPartial(); + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest build() { + io.a2a.grpc.CreateTaskPushNotificationConfigRequest result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } @@ -502,14 +502,14 @@ public io.a2a.grpc.SetTaskPushNotificationConfigRequest build() { } @java.lang.Override - public io.a2a.grpc.SetTaskPushNotificationConfigRequest buildPartial() { - io.a2a.grpc.SetTaskPushNotificationConfigRequest result = new io.a2a.grpc.SetTaskPushNotificationConfigRequest(this); + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest buildPartial() { + io.a2a.grpc.CreateTaskPushNotificationConfigRequest result = new io.a2a.grpc.CreateTaskPushNotificationConfigRequest(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } - private void buildPartial0(io.a2a.grpc.SetTaskPushNotificationConfigRequest result) { + private void buildPartial0(io.a2a.grpc.CreateTaskPushNotificationConfigRequest result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.tenant_ = tenant_; @@ -532,16 +532,16 @@ private void buildPartial0(io.a2a.grpc.SetTaskPushNotificationConfigRequest resu @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.SetTaskPushNotificationConfigRequest) { - return mergeFrom((io.a2a.grpc.SetTaskPushNotificationConfigRequest)other); + if (other instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest) { + return mergeFrom((io.a2a.grpc.CreateTaskPushNotificationConfigRequest)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(io.a2a.grpc.SetTaskPushNotificationConfigRequest other) { - if (other == io.a2a.grpc.SetTaskPushNotificationConfigRequest.getDefaultInstance()) return this; + public Builder mergeFrom(io.a2a.grpc.CreateTaskPushNotificationConfigRequest other) { + if (other == io.a2a.grpc.CreateTaskPushNotificationConfigRequest.getDefaultInstance()) return this; if (!other.getTenant().isEmpty()) { tenant_ = other.tenant_; bitField0_ |= 0x00000001; @@ -1063,23 +1063,23 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { return configBuilder_; } - // @@protoc_insertion_point(builder_scope:a2a.v1.SetTaskPushNotificationConfigRequest) + // @@protoc_insertion_point(builder_scope:a2a.v1.CreateTaskPushNotificationConfigRequest) } - // @@protoc_insertion_point(class_scope:a2a.v1.SetTaskPushNotificationConfigRequest) - private static final io.a2a.grpc.SetTaskPushNotificationConfigRequest DEFAULT_INSTANCE; + // @@protoc_insertion_point(class_scope:a2a.v1.CreateTaskPushNotificationConfigRequest) + private static final io.a2a.grpc.CreateTaskPushNotificationConfigRequest DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new io.a2a.grpc.SetTaskPushNotificationConfigRequest(); + DEFAULT_INSTANCE = new io.a2a.grpc.CreateTaskPushNotificationConfigRequest(); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest getDefaultInstance() { + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstance() { return DEFAULT_INSTANCE; } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { @java.lang.Override - public SetTaskPushNotificationConfigRequest parsePartialFrom( + public CreateTaskPushNotificationConfigRequest parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { @@ -1098,17 +1098,17 @@ public SetTaskPushNotificationConfigRequest parsePartialFrom( } }; - public static com.google.protobuf.Parser parser() { + public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override - public com.google.protobuf.Parser getParserForType() { + public com.google.protobuf.Parser getParserForType() { return PARSER; } @java.lang.Override - public io.a2a.grpc.SetTaskPushNotificationConfigRequest getDefaultInstanceForType() { + public io.a2a.grpc.CreateTaskPushNotificationConfigRequest getDefaultInstanceForType() { return DEFAULT_INSTANCE; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java similarity index 93% rename from spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java index c5a01418a..536c31416 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SetTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java @@ -6,8 +6,8 @@ package io.a2a.grpc; @com.google.protobuf.Generated -public interface SetTaskPushNotificationConfigRequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.SetTaskPushNotificationConfigRequest) +public interface CreateTaskPushNotificationConfigRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.CreateTaskPushNotificationConfigRequest) com.google.protobuf.MessageOrBuilder { /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java index 8bfc24676..cb6914e0b 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -8,7 +8,7 @@ /** *
      * --8<-- [start:DeleteTaskPushNotificationConfigRequest]
    - * Represents a request for the `tasks/pushNotificationConfig/delete` method.
    + * Represents a request for the `DeleteTaskPushNotificationConfig` method.
      * 
    * * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} @@ -315,7 +315,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:DeleteTaskPushNotificationConfigRequest]
    -   * Represents a request for the `tasks/pushNotificationConfig/delete` method.
    +   * Represents a request for the `DeleteTaskPushNotificationConfig` method.
        * 
    * * Protobuf type {@code a2a.v1.DeleteTaskPushNotificationConfigRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java index 33395ab8d..76d9264a0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -8,7 +8,7 @@ /** *
      * --8<-- [start:GetTaskRequest]
    - * Represents a request for the `tasks/get` method.
    + * Represents a request for the `GetTask` method.
      * 
    * * Protobuf type {@code a2a.v1.GetTaskRequest} @@ -365,7 +365,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:GetTaskRequest]
    -   * Represents a request for the `tasks/get` method.
    +   * Represents a request for the `GetTask` method.
        * 
    * * Protobuf type {@code a2a.v1.GetTaskRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java index 452ea2312..e057230c9 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigResponse.java @@ -8,7 +8,7 @@ /** *
      * --8<-- [start:ListTaskPushNotificationConfigResponse]
    - * Represents a successful response for the `tasks/pushNotificationConfig/list`
    + * Represents a successful response for the `ListTaskPushNotificationConfig`
      * method.
      * 
    * @@ -333,7 +333,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:ListTaskPushNotificationConfigResponse]
    -   * Represents a successful response for the `tasks/pushNotificationConfig/list`
    +   * Represents a successful response for the `ListTaskPushNotificationConfig`
        * method.
        * 
    * diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java index caaf9740e..943fc6067 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageRequest.java @@ -9,7 +9,7 @@ *
      * /////////// Request Messages ///////////
      * --8<-- [start:SendMessageRequest]
    - * Represents a request for the `message/send` method.
    + * Represents a request for the `SendMessage` method.
      * 
    * * Protobuf type {@code a2a.v1.SendMessageRequest} @@ -420,7 +420,7 @@ protected Builder newBuilderForType( *
        * /////////// Request Messages ///////////
        * --8<-- [start:SendMessageRequest]
    -   * Represents a request for the `message/send` method.
    +   * Represents a request for the `SendMessage` method.
        * 
    * * Protobuf type {@code a2a.v1.SendMessageRequest} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java index 337ea8de6..a8216fec8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/AgentCapabilitiesMapper.java @@ -2,7 +2,6 @@ import org.mapstruct.CollectionMappingStrategy; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; /** * Mapper between {@link io.a2a.spec.AgentCapabilities} and {@link io.a2a.grpc.AgentCapabilities}. diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/CreateTaskPushNotificationConfigMapper.java similarity index 79% rename from spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java rename to spec-grpc/src/main/java/io/a2a/grpc/mapper/CreateTaskPushNotificationConfigMapper.java index 2e0215bc1..428ea2846 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/SetTaskPushNotificationConfigMapper.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/CreateTaskPushNotificationConfigMapper.java @@ -1,13 +1,13 @@ package io.a2a.grpc.mapper; -import io.a2a.grpc.SetTaskPushNotificationConfigRequest; +import io.a2a.grpc.CreateTaskPushNotificationConfigRequest; import io.a2a.spec.PushNotificationConfig; import io.a2a.spec.TaskPushNotificationConfig; import org.mapstruct.Mapper; import org.mapstruct.Mapping; /** - * MapStruct mapper for SetTaskPushNotificationConfigRequest → TaskPushNotificationConfig. + * MapStruct mapper for CreateTaskPushNotificationConfigRequest → TaskPushNotificationConfig. *

    * Handles resource name parsing and ID override logic for creating push notification configs. *

    @@ -21,37 +21,37 @@ * Compile-Time Safety: If the proto changes fields, MapStruct will fail to compile. */ @Mapper(config = A2AProtoMapperConfig.class, uses = {PushNotificationConfigMapper.class}) -public interface SetTaskPushNotificationConfigMapper { +public interface CreateTaskPushNotificationConfigMapper { - SetTaskPushNotificationConfigMapper INSTANCE = A2AMappers.getMapper(SetTaskPushNotificationConfigMapper.class); + CreateTaskPushNotificationConfigMapper INSTANCE = A2AMappers.getMapper(CreateTaskPushNotificationConfigMapper.class); /** - * Converts SetTaskPushNotificationConfigRequest to domain TaskPushNotificationConfig. + * Converts CreateTaskPushNotificationConfigRequest to domain TaskPushNotificationConfig. *

    * Extracts taskId from parent resource name and maps PushNotificationConfig with * ID override from config_id field. * The tenant field is set to null as it's not present in the protobuf definition. * - * @param request the protobuf SetTaskPushNotificationConfigRequest + * @param request the protobuf CreateTaskPushNotificationConfigRequest * @return domain TaskPushNotificationConfig */ @Mapping(target = "taskId", expression = "java(extractTaskId(request))") @Mapping(target = "pushNotificationConfig", expression = "java(mapPushNotificationConfigWithId(request))") - TaskPushNotificationConfig fromProto(SetTaskPushNotificationConfigRequest request); + TaskPushNotificationConfig fromProto(CreateTaskPushNotificationConfigRequest request); /** - * Converts SetTaskPushNotificationConfigRequest to domain TaskPushNotificationConfig. + * Converts CreateTaskPushNotificationConfigRequest to domain TaskPushNotificationConfig. *

    * Extracts taskId from parent resource name and maps PushNotificationConfig with * ID override from config_id field. * * @param config the domainTaskPushNotificationConfig - * @return proto SetTaskPushNotificationConfigRequest + * @return proto CreateTaskPushNotificationConfigRequest */ @Mapping(target = "parent", expression = "java(ResourceNameParser.defineTaskName(config.taskId()))") @Mapping(target = "configId", expression = "java(extractConfigId(config))") @Mapping(target = "config", expression = "java(mapPushNotificationConfig(config))") - SetTaskPushNotificationConfigRequest toProto(TaskPushNotificationConfig config); + CreateTaskPushNotificationConfigRequest toProto(TaskPushNotificationConfig config); /** * Extracts the task ID from the parent resource name. @@ -59,10 +59,10 @@ public interface SetTaskPushNotificationConfigMapper { * Format: "tasks/{task_id}" * Fallback: If parent is blank, extracts from config.name instead. * - * @param request the protobuf SetTaskPushNotificationConfigRequest + * @param request the protobuf CreateTaskPushNotificationConfigRequest * @return the extracted task ID */ - default String extractTaskId(SetTaskPushNotificationConfigRequest request) { + default String extractTaskId(CreateTaskPushNotificationConfigRequest request) { String parent = request.getParent(); if (parent == null || parent.isBlank()) { @@ -92,10 +92,10 @@ default String extractConfigId(TaskPushNotificationConfig config) { *

    * The config_id from the request overrides the ID in the proto's PushNotificationConfig. * - * @param request the protobuf SetTaskPushNotificationConfigRequest + * @param request the protobuf CreateTaskPushNotificationConfigRequest * @return domain PushNotificationConfig with config_id injected */ - default PushNotificationConfig mapPushNotificationConfigWithId(SetTaskPushNotificationConfigRequest request) { + default PushNotificationConfig mapPushNotificationConfigWithId(CreateTaskPushNotificationConfigRequest request) { // Check if config and push_notification_config exist if (!request.hasConfig() || !request.getConfig().hasPushNotificationConfig() @@ -128,7 +128,7 @@ default PushNotificationConfig mapPushNotificationConfigWithId(SetTaskPushNotifi *

    * The config_id from the request overrides the ID in the proto's PushNotificationConfig. * - * @param domain the protobuf SetTaskPushNotificationConfigRequest + * @param domain the protobuf CreateTaskPushNotificationConfigRequest * @return domain PushNotificationConfig with config_id injected */ default io.a2a.grpc.TaskPushNotificationConfig mapPushNotificationConfig(TaskPushNotificationConfig domain) { diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index e49bcbeec..3ac66bfa8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -61,8 +61,8 @@ import io.a2a.jsonrpc.common.wrappers.SendMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.spec.A2AError; import io.a2a.spec.ContentTypeNotSupportedError; @@ -229,12 +229,12 @@ private static A2ARequest parseMethodRequest(String version, Object id, Strin return new ListTasksRequest(version, id, ProtoUtils.FromProto.listTasksParams(builder)); } case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { - io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(paramsNode, builder, id); if (tenant != null && !tenant.isBlank() && (builder.getTenant() == null || builder.getTenant().isBlank())) { builder.setTenant(tenant); } - return new SetTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.setTaskPushNotificationConfig(builder)); + return new CreateTaskPushNotificationConfigRequest(version, id, ProtoUtils.FromProto.CreateTaskPushNotificationConfig(builder)); } case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.GetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.GetTaskPushNotificationConfigRequest.newBuilder(); @@ -334,7 +334,7 @@ public static A2AResponse parseResponseBody(String body, String method) throw case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); parseRequestBody(paramsNode, builder, id); - return new SetTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.taskPushNotificationConfig(builder)); + return new CreateTaskPushNotificationConfigResponse(id, ProtoUtils.FromProto.taskPushNotificationConfig(builder)); } case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { io.a2a.grpc.TaskPushNotificationConfig.Builder builder = io.a2a.grpc.TaskPushNotificationConfig.newBuilder(); @@ -380,7 +380,7 @@ public static A2AResponse parseError(JsonObject error, Object id, String meth return new ListTasksResponse(id, rpcError); } case SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { - return new SetTaskPushNotificationConfigResponse(id, rpcError); + return new CreateTaskPushNotificationConfigResponse(id, rpcError); } case GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD -> { return new GetTaskPushNotificationConfigResponse(id, rpcError); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java index b2941dc98..d4b1ca55f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java @@ -14,7 +14,7 @@ import io.a2a.grpc.mapper.MessageMapper; import io.a2a.grpc.mapper.MessageSendConfigurationMapper; import io.a2a.grpc.mapper.MessageSendParamsMapper; -import io.a2a.grpc.mapper.SetTaskPushNotificationConfigMapper; +import io.a2a.grpc.mapper.CreateTaskPushNotificationConfigMapper; import io.a2a.grpc.mapper.StreamResponseMapper; import io.a2a.grpc.mapper.TaskArtifactUpdateEventMapper; import io.a2a.grpc.mapper.TaskIdParamsMapper; @@ -71,8 +71,8 @@ public static io.a2a.grpc.SubscribeToTaskRequest subscribeToTaskRequest(TaskIdPa return TaskIdParamsMapper.INSTANCE.toProtoSubscribeToTaskRequest(params); } - public static io.a2a.grpc.SetTaskPushNotificationConfigRequest setTaskPushNotificationConfigRequest(TaskPushNotificationConfig config) { - return SetTaskPushNotificationConfigMapper.INSTANCE.toProto(config); + public static io.a2a.grpc.CreateTaskPushNotificationConfigRequest createTaskPushNotificationConfigRequest(TaskPushNotificationConfig config) { + return CreateTaskPushNotificationConfigMapper.INSTANCE.toProto(config); } public static io.a2a.grpc.GetTaskPushNotificationConfigRequest getTaskPushNotificationConfigRequest(GetTaskPushNotificationConfigParams params) { @@ -173,7 +173,7 @@ public static io.a2a.grpc.StreamResponse taskOrMessageStream(StreamingEventKind }; } - public static io.a2a.grpc.TaskPushNotificationConfig setTaskPushNotificationConfigResponse(TaskPushNotificationConfig config) { + public static io.a2a.grpc.TaskPushNotificationConfig createTaskPushNotificationConfigResponse(TaskPushNotificationConfig config) { return taskPushNotificationConfig(config); } @@ -231,11 +231,11 @@ public static MessageSendParams messageSendParams(io.a2a.grpc.SendMessageRequest return convert(() -> MessageSendParamsMapper.INSTANCE.fromProto(requestProto)); } - public static TaskPushNotificationConfig setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequestOrBuilder config) { - io.a2a.grpc.SetTaskPushNotificationConfigRequest reqProto = config instanceof io.a2a.grpc.SetTaskPushNotificationConfigRequest - ? (io.a2a.grpc.SetTaskPushNotificationConfigRequest) config - : ((io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder) config).build(); - return convert(() -> SetTaskPushNotificationConfigMapper.INSTANCE.fromProto(reqProto)); + public static TaskPushNotificationConfig CreateTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequestOrBuilder config) { + io.a2a.grpc.CreateTaskPushNotificationConfigRequest reqProto = config instanceof io.a2a.grpc.CreateTaskPushNotificationConfigRequest + ? (io.a2a.grpc.CreateTaskPushNotificationConfigRequest) config + : ((io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder) config).build(); + return convert(() -> CreateTaskPushNotificationConfigMapper.INSTANCE.fromProto(reqProto)); } public static TaskPushNotificationConfig taskPushNotificationConfig(io.a2a.grpc.TaskPushNotificationConfigOrBuilder config) { diff --git a/spec-grpc/src/main/proto/a2a.proto b/spec-grpc/src/main/proto/a2a.proto index ccf37e6ab..0f701b6e8 100644 --- a/spec-grpc/src/main/proto/a2a.proto +++ b/spec-grpc/src/main/proto/a2a.proto @@ -81,8 +81,8 @@ service A2AService { }; } - // Set a push notification config for a task. - rpc SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig) { + // Create a push notification config for a task. + rpc CreateTaskPushNotificationConfig(CreateTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig) { option (google.api.http) = { post: "/{parent=tasks/*/pushNotificationConfigs}" body: "config" @@ -480,8 +480,8 @@ message AgentCapabilities { optional bool push_notifications = 2; // A list of protocol extensions supported by the agent. repeated AgentExtension extensions = 3; - // Indicates if the agent provides a history of state transitions for a task. - optional bool state_transition_history = 4; + // Field 4 was previously used by state_transition_history capability. + reserved 4; // Indicates if the agent supports providing an extended agent card when authenticated. optional bool extended_agent_card = 5; } @@ -703,7 +703,7 @@ message DeviceCodeOAuthFlow { ///////////// Request Messages /////////// // --8<-- [start:SendMessageRequest] -// Represents a request for the `message/send` method. +// Represents a request for the `SendMessage` method. message SendMessageRequest { // Optional tenant, provided as a path parameter. string tenant = 4; @@ -717,7 +717,7 @@ message SendMessageRequest { // --8<-- [end:SendMessageRequest] // --8<-- [start:GetTaskRequest] -// Represents a request for the `tasks/get` method. +// Represents a request for the `GetTask` method. message GetTaskRequest { // Optional tenant, provided as a path parameter. string tenant = 3; @@ -772,7 +772,7 @@ message ListTasksResponse { // --8<-- [end:ListTasksResponse] // --8<-- [start:CancelTaskRequest] -// Represents a request for the `tasks/cancel` method. +// Represents a request for the `CancelTask` method. message CancelTaskRequest { // Optional tenant, provided as a path parameter. string tenant = 2; @@ -793,7 +793,7 @@ message GetTaskPushNotificationConfigRequest { // --8<-- [end:GetTaskPushNotificationConfigRequest] // --8<-- [start:DeleteTaskPushNotificationConfigRequest] -// Represents a request for the `tasks/pushNotificationConfig/delete` method. +// Represents a request for the `DeleteTaskPushNotificationConfig` method. message DeleteTaskPushNotificationConfigRequest { // Optional tenant, provided as a path parameter. string tenant = 2; @@ -803,9 +803,9 @@ message DeleteTaskPushNotificationConfigRequest { } // --8<-- [end:DeleteTaskPushNotificationConfigRequest] -// --8<-- [start:SetTaskPushNotificationConfigRequest] -// Represents a request for the `tasks/pushNotificationConfig/set` method. -message SetTaskPushNotificationConfigRequest { +// --8<-- [start:CreateTaskPushNotificationConfigRequest] +// Represents a request for the `CreateTaskPushNotificationConfig` method. +message CreateTaskPushNotificationConfigRequest { // Optional tenant, provided as a path parameter. string tenant = 4; // The parent task resource for this config. @@ -816,7 +816,7 @@ message SetTaskPushNotificationConfigRequest { // The configuration to create. TaskPushNotificationConfig config = 3 [(google.api.field_behavior) = REQUIRED]; } -// --8<-- [end:SetTaskPushNotificationConfigRequest] +// --8<-- [end:CreateTaskPushNotificationConfigRequest] // --8<-- [start:SubscribeToTaskRequest] message SubscribeToTaskRequest { @@ -877,7 +877,7 @@ message StreamResponse { // --8<-- [end:StreamResponse] // --8<-- [start:ListTaskPushNotificationConfigResponse] -// Represents a successful response for the `tasks/pushNotificationConfig/list` +// Represents a successful response for the `ListTaskPushNotificationConfig` // method. message ListTaskPushNotificationConfigResponse { // The list of push notification configurations. diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java index 380353dea..60f137893 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/JSONRPCUtilsTest.java @@ -15,8 +15,8 @@ import io.a2a.jsonrpc.common.wrappers.A2ARequest; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigRequest; import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.spec.InvalidParamsError; import io.a2a.spec.JSONParseError; import io.a2a.spec.PushNotificationConfig; @@ -26,11 +26,11 @@ public class JSONRPCUtilsTest { @Test - public void testParseSetTaskPushNotificationConfigRequest_ValidProtoFormat() throws JsonProcessingException { + public void testParseCreateTaskPushNotificationConfigRequest_ValidProtoFormat() throws JsonProcessingException { String validRequest = """ { "jsonrpc": "2.0", - "method": "SetTaskPushNotificationConfig", + "method": "CreateTaskPushNotificationConfig", "id": "1", "params": { "parent": "tasks/task-123", @@ -52,11 +52,11 @@ public void testParseSetTaskPushNotificationConfigRequest_ValidProtoFormat() thr A2ARequest request = JSONRPCUtils.parseRequestBody(validRequest, null); assertNotNull(request); - assertInstanceOf(SetTaskPushNotificationConfigRequest.class, request); - SetTaskPushNotificationConfigRequest setRequest = (SetTaskPushNotificationConfigRequest) request; + assertInstanceOf(CreateTaskPushNotificationConfigRequest.class, request); + CreateTaskPushNotificationConfigRequest setRequest = (CreateTaskPushNotificationConfigRequest) request; assertEquals("2.0", setRequest.getJsonrpc()); assertEquals(1, setRequest.getId()); - assertEquals("SetTaskPushNotificationConfig", setRequest.getMethod()); + assertEquals("CreateTaskPushNotificationConfig", setRequest.getMethod()); TaskPushNotificationConfig config = setRequest.getParams(); assertNotNull(config); @@ -95,7 +95,7 @@ public void testParseMalformedJSON_ThrowsJsonSyntaxException() { String malformedRequest = """ { "jsonrpc": "2.0", - "method": "SetTaskPushNotificationConfig", + "method": "CreateTaskPushNotificationConfig", "params": { "parent": "tasks/task-123" """; // Missing closing braces @@ -111,7 +111,7 @@ public void testParseInvalidParams_ThrowsInvalidParamsJsonMappingException() { String invalidParamsRequest = """ { "jsonrpc": "2.0", - "method": "SetTaskPushNotificationConfig", + "method": "CreateTaskPushNotificationConfig", "id": "3", "params": "not_a_dict" } @@ -129,7 +129,7 @@ public void testParseInvalidProtoStructure_ThrowsInvalidParamsJsonMappingExcepti String invalidStructure = """ { "jsonrpc": "2.0", - "method": "SetTaskPushNotificationConfig", + "method": "CreateTaskPushNotificationConfig", "id": "4", "params": { "invalid_field": "value" @@ -142,7 +142,7 @@ public void testParseInvalidProtoStructure_ThrowsInvalidParamsJsonMappingExcepti () -> JSONRPCUtils.parseRequestBody(invalidStructure, null) ); assertEquals(4, exception.getId()); - assertEquals(ERROR_MESSAGE.formatted("invalid_field in message a2a.v1.SetTaskPushNotificationConfigRequest"), exception.getMessage()); + assertEquals(ERROR_MESSAGE.formatted("invalid_field in message a2a.v1.CreateTaskPushNotificationConfigRequest"), exception.getMessage()); } @Test @@ -262,7 +262,7 @@ public void testParseInvalidTypeWithValidId_PreservesId() throws Exception { } @Test - public void testGenerateSetTaskPushNotificationConfigResponse_Success() throws Exception { + public void testGenerateCreateTaskPushNotificationConfigResponse_Success() throws Exception { TaskPushNotificationConfig config = new TaskPushNotificationConfig( "task-123", PushNotificationConfig.builder() @@ -286,8 +286,8 @@ public void testGenerateSetTaskPushNotificationConfigResponse_Success() throws E } """; - SetTaskPushNotificationConfigResponse response = - (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); + CreateTaskPushNotificationConfigResponse response = + (CreateTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(responseJson, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(1, response.getId()); @@ -335,8 +335,8 @@ public void testParseErrorResponse_InvalidParams() throws Exception { } """; - SetTaskPushNotificationConfigResponse response = - (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); + CreateTaskPushNotificationConfigResponse response = + (CreateTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(5, response.getId()); @@ -359,8 +359,8 @@ public void testParseErrorResponse_ParseError() throws Exception { } """; - SetTaskPushNotificationConfigResponse response = - (SetTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); + CreateTaskPushNotificationConfigResponse response = + (CreateTaskPushNotificationConfigResponse) JSONRPCUtils.parseResponseBody(errorResponse, SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD); assertNotNull(response); assertEquals(6, response.getId()); diff --git a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java index e0a67c9c8..2e3b412e0 100644 --- a/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java +++ b/spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java @@ -55,7 +55,6 @@ public void convertAgentCard() { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) @@ -91,7 +90,6 @@ public void convertAgentCard() { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) diff --git a/spec/src/main/java/io/a2a/spec/A2AMethods.java b/spec/src/main/java/io/a2a/spec/A2AMethods.java index 636b0d81b..e270a7d91 100644 --- a/spec/src/main/java/io/a2a/spec/A2AMethods.java +++ b/spec/src/main/java/io/a2a/spec/A2AMethods.java @@ -24,7 +24,7 @@ public interface A2AMethods { /** Method name for sending a streaming message. */ String SEND_STREAMING_MESSAGE_METHOD = "SendStreamingMessage"; /** Method name for setting task push notification configuration. */ - String SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "SetTaskPushNotificationConfig"; + String SET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD = "CreateTaskPushNotificationConfig"; /** Method name for subscribing to task events. */ String SUBSCRIBE_TO_TASK_METHOD = "SubscribeToTask"; diff --git a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java index ae8fb6f01..da166a4e1 100644 --- a/spec/src/main/java/io/a2a/spec/AgentCapabilities.java +++ b/spec/src/main/java/io/a2a/spec/AgentCapabilities.java @@ -16,25 +16,21 @@ * waiting for task completion *

  • pushNotifications: Agent can send proactive notifications to clients * when task state changes, eliminating the need for polling
  • - *
  • stateTransitionHistory: Agent maintains and provides a complete history - * of all state transitions for tasks, useful for debugging and auditing
  • * *

    * Capabilities are declared in the {@link AgentCard} and are immutable for the lifetime * of the agent instance. This class uses the Builder pattern for construction. * - * @param streaming whether the agent supports streaming responses with incremental artifacts + * @param streaming whether the agent supports streaming responses with incremental artifacts * @param pushNotifications whether the agent supports push notifications for state changes - * @param stateTransitionHistory whether the agent maintains state transition history * @param extendedAgentCard whether the agent supports an extended agent card - * @param extensions list of custom extensions supported by the agent (optional) + * @param extensions list of custom extensions supported by the agent (optional) * @see AgentCard * @see AgentExtension * @see A2A Protocol Specification */ public record AgentCapabilities(boolean streaming, boolean pushNotifications, - boolean stateTransitionHistory, boolean extendedAgentCard, List extensions) { @@ -57,7 +53,6 @@ public static Builder builder() { * AgentCapabilities capabilities = AgentCapabilities.builder() * .streaming(true) * .pushNotifications(false) - * .stateTransitionHistory(false) * .build(); * }

    */ @@ -65,7 +60,6 @@ public static class Builder { private boolean streaming; private boolean pushNotifications; - private boolean stateTransitionHistory; private boolean extendedAgentCard; private List extensions; @@ -103,20 +97,6 @@ public Builder pushNotifications(boolean pushNotifications) { return this; } - /** - * Sets whether the agent maintains state transition history. - *

    - * When enabled, the agent tracks and provides a complete history - * of all state transitions for each task. - * - * @param stateTransitionHistory true if state history is maintained, false otherwise - * @return this builder for method chaining - */ - public Builder stateTransitionHistory(boolean stateTransitionHistory) { - this.stateTransitionHistory = stateTransitionHistory; - return this; - } - /** * Sets whether the agent supports an extended agent card. * state transition history. @@ -150,7 +130,7 @@ public Builder extensions(List extensions) { * @return a new AgentCapabilities instance */ public AgentCapabilities build() { - return new AgentCapabilities(streaming, pushNotifications, stateTransitionHistory, extendedAgentCard, extensions); + return new AgentCapabilities(streaming, pushNotifications, extendedAgentCard, extensions); } } } diff --git a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java index 526b81ce5..599a0e12c 100644 --- a/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java +++ b/tck/src/main/java/io/a2a/tck/server/AgentCardProducer.java @@ -39,7 +39,6 @@ public AgentCard agentCard() { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 62acc506e..f8031eb5f 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -80,7 +80,7 @@ import io.a2a.spec.TextPart; import io.a2a.spec.TransportProtocol; import io.a2a.spec.UnsupportedOperationError; -import io.a2a.util.Utils; + import io.restassured.RestAssured; import io.restassured.config.ObjectMapperConfig; import io.restassured.specification.RequestSpecification; @@ -553,7 +553,7 @@ public void testSetPushNotificationSuccess() throws Exception { TaskPushNotificationConfig taskPushConfig = new TaskPushNotificationConfig( MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), ""); - TaskPushNotificationConfig config = getClient().setTaskPushNotificationConfiguration(taskPushConfig); + TaskPushNotificationConfig config = getClient().createTaskPushNotificationConfiguration(taskPushConfig); assertEquals(MINIMAL_TASK.id(), config.taskId()); assertEquals("http://example.com", config.pushNotificationConfig().url()); assertEquals("c295ea44-7543-4f78-b524-7a38915ad6e4", config.pushNotificationConfig().id()); @@ -573,7 +573,7 @@ public void testGetPushNotificationSuccess() throws Exception { = new TaskPushNotificationConfig( MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), ""); - TaskPushNotificationConfig setResult = getClient().setTaskPushNotificationConfiguration(taskPushConfig); + TaskPushNotificationConfig setResult = getClient().createTaskPushNotificationConfiguration(taskPushConfig); assertNotNull(setResult); TaskPushNotificationConfig config = getClient().getTaskPushNotificationConfiguration( @@ -624,7 +624,6 @@ public void testGetExtendedAgentCard() throws A2AClientException { assertEquals("http://example.com/docs", agentCard.documentationUrl()); assertTrue(agentCard.capabilities().pushNotifications()); assertTrue(agentCard.capabilities().streaming()); - assertTrue(agentCard.capabilities().stateTransitionHistory()); assertTrue(agentCard.capabilities().extendedAgentCard()); assertTrue(agentCard.skills().isEmpty()); } @@ -2119,7 +2118,6 @@ private AgentCard createTestAgentCard() { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .build()) .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java index 03ec3481b..f9dae8d19 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AgentCardProducer.java @@ -46,7 +46,6 @@ public AgentCard agentCard() { .capabilities(AgentCapabilities.builder() .streaming(true) .pushNotifications(true) - .stateTransitionHistory(true) .extendedAgentCard(true) .build()) .defaultInputModes(Collections.singletonList("text")) diff --git a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java index 439d97497..376d9b11d 100644 --- a/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java +++ b/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java @@ -159,7 +159,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, } @Override - public void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationConfigRequest request, + public void createTaskPushNotificationConfig(io.a2a.grpc.CreateTaskPushNotificationConfigRequest request, StreamObserver responseObserver) { if (!getAgentCardInternal().capabilities().pushNotifications()) { handleError(responseObserver, new PushNotificationNotSupportedError()); @@ -168,8 +168,8 @@ public void setTaskPushNotificationConfig(io.a2a.grpc.SetTaskPushNotificationCon try { ServerCallContext context = createCallContext(responseObserver); - TaskPushNotificationConfig config = FromProto.setTaskPushNotificationConfig(request); - TaskPushNotificationConfig responseConfig = getRequestHandler().onSetTaskPushNotificationConfig(config, context); + TaskPushNotificationConfig config = FromProto.CreateTaskPushNotificationConfig(request); + TaskPushNotificationConfig responseConfig = getRequestHandler().onCreateTaskPushNotificationConfig(config, context); responseObserver.onNext(ToProto.taskPushNotificationConfig(responseConfig)); responseObserver.onCompleted(); } catch (A2AError e) { diff --git a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java index 187fd6e99..c7cf79524 100644 --- a/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java +++ b/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java @@ -12,32 +12,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import jakarta.enterprise.inject.Instance; - import com.google.protobuf.Empty; import com.google.protobuf.Struct; -import io.a2a.grpc.AuthenticationInfo; -import io.a2a.grpc.CancelTaskRequest; -import io.a2a.grpc.DeleteTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskPushNotificationConfigRequest; -import io.a2a.grpc.GetTaskRequest; -import io.a2a.grpc.ListTaskPushNotificationConfigRequest; -import io.a2a.grpc.ListTaskPushNotificationConfigResponse; -import io.a2a.grpc.ListTasksRequest; -import io.a2a.grpc.ListTasksResponse; -import io.a2a.grpc.Message; -import io.a2a.grpc.Part; -import io.a2a.grpc.PushNotificationConfig; -import io.a2a.grpc.Role; -import io.a2a.grpc.SendMessageRequest; -import io.a2a.grpc.SendMessageResponse; -import io.a2a.grpc.SetTaskPushNotificationConfigRequest; -import io.a2a.grpc.StreamResponse; -import io.a2a.grpc.SubscribeToTaskRequest; -import io.a2a.grpc.Task; -import io.a2a.grpc.TaskPushNotificationConfig; -import io.a2a.grpc.TaskState; -import io.a2a.grpc.TaskStatus; + +import io.a2a.grpc.*; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; @@ -273,7 +251,7 @@ public void testGetPushNotificationConfigSuccess() throws Exception { @Test public void testPushNotificationsNotSupportedError() throws Exception { - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); @@ -284,7 +262,7 @@ public void testPushNotificationsNotSupportedError() throws Exception { public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder streamRecorder = getTaskPushNotificationConfigRequest(handler, NAME); @@ -295,7 +273,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() throws Exception { public void testOnSetPushNotificationNoPushNotifierConfig() throws Exception { // Create request handler without a push notifier DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, false); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); String NAME = "tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id() + "/pushNotificationConfigs/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id(); StreamRecorder streamRecorder = createTaskPushNotificationConfigRequest(handler, NAME); @@ -612,7 +590,7 @@ public void testOnResubscribeExistingTaskSuccessMocks() throws Exception { @Test public void testStreamingNotSupportedError() throws Exception { - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); StreamRecorder streamRecorder = sendStreamingMessageRequest(handler); assertGrpcError(streamRecorder, Status.Code.INVALID_ARGUMENT); @@ -621,7 +599,7 @@ public void testStreamingNotSupportedError() throws Exception { @Test public void testStreamingNotSupportedErrorOnResubscribeToTask() throws Exception { // This test does not exist in the Python implementation - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(false, true); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); SubscribeToTaskRequest request = SubscribeToTaskRequest.newBuilder() .setName("tasks/" + AbstractA2ARequestHandlerTest.MINIMAL_TASK.id()) @@ -668,7 +646,7 @@ public void testListPushNotificationConfig() throws Exception { @Test public void testListPushNotificationConfigNotSupported() throws Exception { - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { @@ -739,7 +717,7 @@ public void testDeletePushNotificationConfig() throws Exception { @Test public void testDeletePushNotificationConfigNotSupported() throws Exception { - AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false, true); + AgentCard card = AbstractA2ARequestHandlerTest.createAgentCard(true, false); GrpcHandler handler = new TestGrpcHandler(card, requestHandler, internalExecutor); taskStore.save(AbstractA2ARequestHandlerTest.MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { @@ -1179,14 +1157,14 @@ private StreamRecorder createTaskPushNotificationCon .setName(name) .setPushNotificationConfig(config) .build(); - SetTaskPushNotificationConfigRequest setRequest = SetTaskPushNotificationConfigRequest.newBuilder() + CreateTaskPushNotificationConfigRequest setRequest = CreateTaskPushNotificationConfigRequest.newBuilder() .setConfig(taskPushNotificationConfig) .setConfigId("config456") .setParent("tasks/" + MINIMAL_TASK.id()) .build(); StreamRecorder streamRecorder = StreamRecorder.create(); - handler.setTaskPushNotificationConfig(setRequest, streamRecorder); + handler.createTaskPushNotificationConfig(setRequest, streamRecorder); streamRecorder.awaitCompletion(5, TimeUnit.SECONDS); return streamRecorder; } diff --git a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java index 2e1a1fdf0..a592bd55d 100644 --- a/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java +++ b/transport/jsonrpc/src/main/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandler.java @@ -29,8 +29,8 @@ import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.AgentCardValidator; import io.a2a.server.ExtendedAgentCard; @@ -188,20 +188,20 @@ public GetTaskPushNotificationConfigResponse getPushNotificationConfig( } } - public SetTaskPushNotificationConfigResponse setPushNotificationConfig( - SetTaskPushNotificationConfigRequest request, ServerCallContext context) { + public CreateTaskPushNotificationConfigResponse setPushNotificationConfig( + CreateTaskPushNotificationConfigRequest request, ServerCallContext context) { if (!agentCard.capabilities().pushNotifications()) { - return new SetTaskPushNotificationConfigResponse(request.getId(), + return new CreateTaskPushNotificationConfigResponse(request.getId(), new PushNotificationNotSupportedError()); } try { TaskPushNotificationConfig config = - requestHandler.onSetTaskPushNotificationConfig(request.getParams(), context); - return new SetTaskPushNotificationConfigResponse(request.getId().toString(), config); + requestHandler.onCreateTaskPushNotificationConfig(request.getParams(), context); + return new CreateTaskPushNotificationConfigResponse(request.getId().toString(), config); } catch (A2AError e) { - return new SetTaskPushNotificationConfigResponse(request.getId(), e); + return new CreateTaskPushNotificationConfigResponse(request.getId(), e); } catch (Throwable t) { - return new SetTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); + return new CreateTaskPushNotificationConfigResponse(request.getId(), new InternalError(t.getMessage())); } } diff --git a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java index 2053ada41..8d2751627 100644 --- a/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java +++ b/transport/jsonrpc/src/test/java/io/a2a/transport/jsonrpc/handler/JSONRPCHandlerTest.java @@ -39,13 +39,12 @@ import io.a2a.jsonrpc.common.wrappers.SendMessageResponse; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageRequest; import io.a2a.jsonrpc.common.wrappers.SendStreamingMessageResponse; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigRequest; -import io.a2a.jsonrpc.common.wrappers.SetTaskPushNotificationConfigResponse; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigRequest; +import io.a2a.jsonrpc.common.wrappers.CreateTaskPushNotificationConfigResponse; import io.a2a.jsonrpc.common.wrappers.SubscribeToTaskRequest; import io.a2a.server.ServerCallContext; import io.a2a.server.auth.UnauthenticatedUser; import io.a2a.server.events.EventConsumer; -import io.a2a.server.events.MainEventBusProcessorCallback; import io.a2a.server.requesthandlers.AbstractA2ARequestHandlerTest; import io.a2a.server.requesthandlers.DefaultRequestHandler; import io.a2a.server.tasks.ResultAggregator; @@ -650,8 +649,8 @@ public void testSetPushNotificationConfigSuccess() { PushNotificationConfig.builder().url("http://example.com") .id("c295ea44-7543-4f78-b524-7a38915ad6e4").build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); - SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); TaskPushNotificationConfig taskPushConfigResult = new TaskPushNotificationConfig( MINIMAL_TASK.id(), PushNotificationConfig.builder().url("http://example.com") @@ -674,7 +673,7 @@ public void testGetPushNotificationConfigSuccess() { MINIMAL_TASK.id(), PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); GetTaskPushNotificationConfigRequest getRequest @@ -715,7 +714,6 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex .status(new TaskStatus(TaskState.COMPLETED)) .build()); - agentExecutorExecute = (context, eventQueue) -> { // Hardcode the events to send here for (Event event : events) { @@ -727,8 +725,8 @@ public void testOnMessageStreamNewMessageSendPushNotificationSuccess() throws Ex MINIMAL_TASK.id(), PushNotificationConfig.builder().id("c295ea44-7543-4f78-b524-7a38915ad6e4").url("http://example.com").build(), "tenant"); - SetTaskPushNotificationConfigRequest stpnRequest = new SetTaskPushNotificationConfigRequest("1", config); - SetTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); + CreateTaskPushNotificationConfigRequest stpnRequest = new CreateTaskPushNotificationConfigRequest("1", config); + CreateTaskPushNotificationConfigResponse stpnResponse = handler.setPushNotificationConfig(stpnRequest, callContext); assertNull(stpnResponse.getError()); Message msg = Message.builder(MESSAGE) @@ -1000,7 +998,7 @@ public void onComplete() { @Test public void testStreamingNotSupportedError() { - AgentCard card = createAgentCard(false, true, true); + AgentCard card = createAgentCard(false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); SendStreamingMessageRequest request = SendStreamingMessageRequest.builder() @@ -1052,7 +1050,7 @@ public void onComplete() { @Test public void testStreamingNotSupportedErrorOnResubscribeToTask() { // This test does not exist in the Python implementation - AgentCard card = createAgentCard(false, true, true); + AgentCard card = createAgentCard(false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); SubscribeToTaskRequest request = new SubscribeToTaskRequest("1", new TaskIdParams(MINIMAL_TASK.id())); @@ -1098,7 +1096,7 @@ public void onComplete() { @Test public void testPushNotificationsNotSupportedError() { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = createAgentCard(true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK, false); @@ -1110,10 +1108,10 @@ public void testPushNotificationsNotSupportedError() { .url("http://example.com") .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = SetTaskPushNotificationConfigRequest.builder() + CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.builder() .params(config) .build(); - SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); + CreateTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); assertInstanceOf(PushNotificationNotSupportedError.class, response.getError()); } @@ -1121,7 +1119,7 @@ public void testPushNotificationsNotSupportedError() { public void testOnGetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); - AgentCard card = createAgentCard(false, true, false); + AgentCard card = createAgentCard(false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK, false); @@ -1139,7 +1137,7 @@ public void testOnGetPushNotificationNoPushNotifierConfig() { public void testOnSetPushNotificationNoPushNotifierConfig() { // Create request handler without a push notifier DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); - AgentCard card = createAgentCard(false, true, false); + AgentCard card = createAgentCard(false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK, false); @@ -1152,10 +1150,10 @@ public void testOnSetPushNotificationNoPushNotifierConfig() { .url("http://example.com") .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = SetTaskPushNotificationConfigRequest.builder() + CreateTaskPushNotificationConfigRequest request = CreateTaskPushNotificationConfigRequest.builder() .params(config) .build(); - SetTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); + CreateTaskPushNotificationConfigResponse response = handler.setPushNotificationConfig(request, callContext); assertInstanceOf(UnsupportedOperationError.class, response.getError()); assertEquals("This operation is not supported", response.getError().getMessage()); @@ -1230,7 +1228,7 @@ public void testDefaultRequestHandlerWithCustomComponents() { @Test public void testOnMessageSendErrorHandling() { DefaultRequestHandler requestHandler = DefaultRequestHandler.create(executor, taskStore, queueManager, null, mainEventBusProcessor, internalExecutor, internalExecutor); - AgentCard card = createAgentCard(false, true, false); + AgentCard card = createAgentCard(false, true); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK, false); @@ -1341,7 +1339,7 @@ public void testListPushNotificationConfig() { .url("http://example.com") .id(MINIMAL_TASK.id()) .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); TaskPushNotificationConfig result = new TaskPushNotificationConfig( MINIMAL_TASK.id(), PushNotificationConfig.builder() @@ -1359,7 +1357,7 @@ public void testListPushNotificationConfig() { @Test public void testListPushNotificationConfigNotSupported() { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = createAgentCard(true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { @@ -1372,7 +1370,7 @@ public void testListPushNotificationConfigNotSupported() { .url("http://example.com") .id(MINIMAL_TASK.id()) .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); ListTaskPushNotificationConfigRequest listRequest @@ -1435,7 +1433,7 @@ public void testDeletePushNotificationConfig() { .url("http://example.com") .id(MINIMAL_TASK.id()) .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest @@ -1450,7 +1448,7 @@ public void testDeletePushNotificationConfig() { @Test public void testDeletePushNotificationConfigNotSupported() { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = createAgentCard(true, false); JSONRPCHandler handler = new JSONRPCHandler(card, requestHandler, internalExecutor); taskStore.save(MINIMAL_TASK, false); agentExecutorExecute = (context, eventQueue) -> { @@ -1463,7 +1461,7 @@ public void testDeletePushNotificationConfigNotSupported() { .url("http://example.com") .id(MINIMAL_TASK.id()) .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest @@ -1492,7 +1490,7 @@ public void testDeletePushNotificationConfigNoPushConfigStore() { .url("http://example.com") .id(MINIMAL_TASK.id()) .build(), "tenant"); - SetTaskPushNotificationConfigRequest request = new SetTaskPushNotificationConfigRequest("1", taskPushConfig); + CreateTaskPushNotificationConfigRequest request = new CreateTaskPushNotificationConfigRequest("1", taskPushConfig); handler.setPushNotificationConfig(request, callContext); DeleteTaskPushNotificationConfigRequest deleteRequest diff --git a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java index 3273d6119..f4b594f6b 100644 --- a/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java +++ b/transport/rest/src/main/java/io/a2a/transport/rest/handler/RestHandler.java @@ -161,15 +161,15 @@ public HTTPRestResponse cancelTask(String taskId, String tenant, ServerCallConte } } - public HTTPRestResponse setTaskPushNotificationConfiguration(String taskId, String body, String tenant, ServerCallContext context) { + public HTTPRestResponse CreateTaskPushNotificationConfiguration(String taskId, String body, String tenant, ServerCallContext context) { try { if (!agentCard.capabilities().pushNotifications()) { throw new PushNotificationNotSupportedError(); } - io.a2a.grpc.SetTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.SetTaskPushNotificationConfigRequest.newBuilder(); + io.a2a.grpc.CreateTaskPushNotificationConfigRequest.Builder builder = io.a2a.grpc.CreateTaskPushNotificationConfigRequest.newBuilder(); parseRequestBody(body, builder); builder.setTenant(tenant); - TaskPushNotificationConfig result = requestHandler.onSetTaskPushNotificationConfig(ProtoUtils.FromProto.setTaskPushNotificationConfig(builder), context); + TaskPushNotificationConfig result = requestHandler.onCreateTaskPushNotificationConfig(ProtoUtils.FromProto.CreateTaskPushNotificationConfig(builder), context); return createSuccessResponse(201, io.a2a.grpc.TaskPushNotificationConfig.newBuilder(ProtoUtils.ToProto.taskPushNotificationConfig(result))); } catch (A2AError e) { return createErrorResponse(e); diff --git a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java index 21005c22f..48eaae912 100644 --- a/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java +++ b/transport/rest/src/test/java/io/a2a/transport/rest/handler/RestHandlerTest.java @@ -226,7 +226,7 @@ public void testSendStreamingMessageSuccess() { @Test public void testSendStreamingMessageNotSupported() { - AgentCard card = createAgentCard(false, true, true); + AgentCard card = createAgentCard(false, true); RestHandler handler = new RestHandler(card, requestHandler, internalExecutor); String requestBody = """ @@ -265,7 +265,7 @@ public void testPushNotificationConfigSuccess() { } }""".formatted(MINIMAL_TASK.id(), MINIMAL_TASK.id()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration( MINIMAL_TASK.id(), requestBody, "", callContext); + RestHandler.HTTPRestResponse response = handler.CreateTaskPushNotificationConfiguration( MINIMAL_TASK.id(), requestBody, "", callContext); Assertions.assertEquals(201, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); @@ -274,7 +274,7 @@ public void testPushNotificationConfigSuccess() { @Test public void testPushNotificationConfigNotSupported() { - AgentCard card = createAgentCard(true, false, true); + AgentCard card = createAgentCard(true, false); RestHandler handler = new RestHandler(card, requestHandler, internalExecutor); String requestBody = """ @@ -286,7 +286,7 @@ public void testPushNotificationConfigNotSupported() { } """.formatted(MINIMAL_TASK.id()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.id(), requestBody, "", callContext); + RestHandler.HTTPRestResponse response = handler.CreateTaskPushNotificationConfiguration(MINIMAL_TASK.id(), requestBody, "", callContext); Assertions.assertEquals(501, response.getStatusCode()); Assertions.assertTrue(response.getBody().contains("PushNotificationNotSupportedError")); @@ -312,7 +312,7 @@ public void testGetPushNotificationConfig() { } } }""".formatted(MINIMAL_TASK.id(), MINIMAL_TASK.id()); - RestHandler.HTTPRestResponse response = handler.setTaskPushNotificationConfiguration(MINIMAL_TASK.id(), createRequestBody, "", callContext); + RestHandler.HTTPRestResponse response = handler.CreateTaskPushNotificationConfiguration(MINIMAL_TASK.id(), createRequestBody, "", callContext); Assertions.assertEquals(201, response.getStatusCode(), response.toString()); Assertions.assertEquals("application/json", response.getContentType()); // Now get it From 06b9fe553489b092e818cec532a61083aa5a0f37 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 5 Feb 2026 14:45:08 +0100 Subject: [PATCH 276/493] chore: fixing flaky test (#628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Problem The second sendMessage call doesn't wait for its streaming subscription to be established. This creates a race condition: - The agent receives message2 and immediately sends events (artifact-2 + completion) - But the streamConsumer subscription might not be fully wired up yet - The events get delivered to the resubConsumer (which is already established) but missed by the streamConsumer The Fix Add the awaitStreamingSubscription() pattern before the second sendMessage call: Fixes #627 🦕 Signed-off-by: Emmanuel Hugonnet --- .../io/a2a/server/apps/common/AbstractA2AServerTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index f8031eb5f..6b99776c7 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1425,9 +1425,18 @@ public void testNonBlockingWithMultipleMessages() throws Exception { } }; + // Wait for streaming subscription to be established before sending message + CountDownLatch streamSubscriptionLatch = new CountDownLatch(1); + awaitStreamingSubscription() + .whenComplete((unused, throwable) -> streamSubscriptionLatch.countDown()); + // Streaming message adds artifact-2 and completes task getClient().sendMessage(message2, List.of(streamConsumer), null); + // Ensure subscription is established before agent sends events + assertTrue(streamSubscriptionLatch.await(15, TimeUnit.SECONDS), + "Stream subscription should be established"); + // 4. Verify both consumers received artifact-2 and completion assertTrue(resubEventLatch.await(10, TimeUnit.SECONDS)); assertTrue(streamEventLatch.await(10, TimeUnit.SECONDS)); From 29bd6bb15680ba01ddc7496bc9453b83a22d4677 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Thu, 5 Feb 2026 14:45:27 +0100 Subject: [PATCH 277/493] chore: Upgrade Nullaway and error prone (#629) Upgrading Nullaway fro 0.12.14 to 0.13.1: https://github.com/uber/NullAway/compare/v0.12.14...v0.13.1 Signed-off-by: Emmanuel Hugonnet --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index adf31c903..8f3545dba 100644 --- a/pom.xml +++ b/pom.xml @@ -69,8 +69,8 @@ 5.5.1 2.0.17 1.5.18 - 2.45.0 - 0.12.14 + 2.47.0 + 0.13.1 -XDaddTypeAnnotationsToSymbol=true -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -Xep:RequireExplicitNullMarking:WARN -XepOpt:NullAway:ExhaustiveOverride=true -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:JSpecifyMode=true -XepExcludedPaths:.*/src/test/.* -XepDisableWarningsInGeneratedCode From 3dfa2bfe079e29efc6e00d0b3266d3a11f10dd86 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Thu, 5 Feb 2026 17:05:03 +0000 Subject: [PATCH 278/493] fix: Eliminate race condition in testInputRequiredWorkflow (#630) The test consumers were counting down their latches on the FIRST TaskEvent received, which could be the intermediate WORKING state instead of the expected terminal state (INPUT_REQUIRED or COMPLETED). This caused intermittent test failures with: expected: but was: Fix: Both consumers now only count down when receiving the expected terminal state: - initialConsumer waits for INPUT_REQUIRED (not WORKING) - completionConsumer waits for COMPLETED (not WORKING) This matches the agent's event emission pattern: agentEmitter.startWork() // WORKING state agentEmitter.requiresInput(...) // INPUT_REQUIRED state ...later... agentEmitter.startWork() // WORKING state agentEmitter.complete() // COMPLETED state --- .../apps/common/AbstractA2AServerTest.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java index 6b99776c7..a15f2b087 100644 --- a/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java +++ b/tests/server-common/src/test/java/io/a2a/server/apps/common/AbstractA2AServerTest.java @@ -1512,9 +1512,17 @@ public void testInputRequiredWorkflow() throws Exception { AtomicBoolean initialUnexpectedEvent = new AtomicBoolean(false); BiConsumer initialConsumer = (event, agentCard) -> { + // Idempotency guard: prevent late events from modifying state after latch countdown + if (initialLatch.getCount() == 0) { + return; + } if (event instanceof TaskEvent te) { - initialState.set(te.getTask().status().state()); - initialLatch.countDown(); + TaskState state = te.getTask().status().state(); + initialState.set(state); + // Only count down when we receive INPUT_REQUIRED, not intermediate states like WORKING + if (state == TaskState.INPUT_REQUIRED) { + initialLatch.countDown(); + } } else { initialUnexpectedEvent.set(true); } @@ -1538,9 +1546,17 @@ public void testInputRequiredWorkflow() throws Exception { AtomicBoolean completionUnexpectedEvent = new AtomicBoolean(false); BiConsumer completionConsumer = (event, agentCard) -> { + // Idempotency guard: prevent late events from modifying state after latch countdown + if (completionLatch.getCount() == 0) { + return; + } if (event instanceof TaskEvent te) { - completedState.set(te.getTask().status().state()); - completionLatch.countDown(); + TaskState state = te.getTask().status().state(); + completedState.set(state); + // Only count down when we receive COMPLETED, not intermediate states like WORKING + if (state == TaskState.COMPLETED) { + completionLatch.countDown(); + } } else { completionUnexpectedEvent.set(true); } From 767ca4348d6c4b2062700ad4cf32de295bf1a3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EB=AA=85=ED=98=84?= <117622409+flex-myeonghyeon@users.noreply.github.com> Date: Fri, 6 Feb 2026 19:38:52 +0900 Subject: [PATCH 279/493] fix: merge metadata appendArtifactToTask metadata #615 (#616) Co-authored-by: mhyeon-lee --- .../java/io/a2a/server/tasks/TaskManagerTest.java | 5 +++++ spec/src/main/java/io/a2a/util/Utils.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index 91010e52e..ca4aa07b1 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -191,6 +191,7 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("existing content"))) + .metadata(Map.of("key1", "value1")) .build(); Task taskWithArtifact = Task.builder(initialTask) .artifacts(Collections.singletonList(existingArtifact)) @@ -202,6 +203,7 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A .artifactId("artifact-id") .name("artifact-1") .parts(Collections.singletonList(new TextPart("new content"))) + .metadata(Map.of("key2", "value2")) .build(); TaskArtifactUpdateEvent event = TaskArtifactUpdateEvent.builder() .taskId(minimalTask.id()) @@ -219,6 +221,9 @@ public void testTaskArtifactUpdateEventAppendTrueWithExistingArtifact() throws A assertEquals(2, updatedArtifact.parts().size()); assertEquals("existing content", ((TextPart) updatedArtifact.parts().get(0)).text()); assertEquals("new content", ((TextPart) updatedArtifact.parts().get(1)).text()); + + assertEquals("value1", updatedArtifact.metadata().get("key1")); + assertEquals("value2", updatedArtifact.metadata().get("key2")); } @Test diff --git a/spec/src/main/java/io/a2a/util/Utils.java b/spec/src/main/java/io/a2a/util/Utils.java index 792365579..44d713f75 100644 --- a/spec/src/main/java/io/a2a/util/Utils.java +++ b/spec/src/main/java/io/a2a/util/Utils.java @@ -3,7 +3,9 @@ import static io.a2a.util.Assert.checkNotNullParam; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Logger; import com.google.gson.Gson; @@ -129,8 +131,21 @@ public static Task appendArtifactToTask(Task task, TaskArtifactUpdateEvent event log.fine(String.format("Appending parts to artifact id %s for task %s", artifactId, taskId)); List> parts = new ArrayList<>(existingArtifact.parts()); parts.addAll(newArtifact.parts()); + + Map mergedMetadata = null; + if (existingArtifact.metadata() != null || newArtifact.metadata() != null) { + mergedMetadata = new HashMap<>(); + if (existingArtifact.metadata() != null) { + mergedMetadata.putAll(existingArtifact.metadata()); + } + if (newArtifact.metadata() != null) { + mergedMetadata.putAll(newArtifact.metadata()); + } + } + Artifact updated = Artifact.builder(existingArtifact) .parts(parts) + .metadata(mergedMetadata) .build(); artifacts.set(existingArtifactIndex, updated); } else { From 18d2abff5c35e0079b63fdf9e1b40d0d463520e2 Mon Sep 17 00:00:00 2001 From: Kabir Khan Date: Fri, 6 Feb 2026 12:06:08 +0000 Subject: [PATCH 280/493] =?UTF-8?q?fix:=20Eliminate=20intermittent=20failu?= =?UTF-8?q?re=20in=20testQueueClosedEventTerminates=E2=80=A6=20(#631)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …RemoteSubscribers Two-part fix for race condition in Kafka replication test: 1. EventQueue: Signal polling started BEFORE any early returns - Moved signalQueuePollerStarted() to beginning of dequeueEventItem() - Previously in finally block, which didn't execute if queue was closed - Race: If queue closed before first dequeue, EventQueueClosedException thrown BEFORE try block, so finally never ran and signal never sent - Now always signals even if queue immediately throws on closure 2. Test: Replace unreliable Thread.sleep with proper synchronization - Inject QueueManager to access the queue for the task - Replace Thread.sleep(2000) with queueManager.awaitQueuePollerStart() - Ensures EventConsumer is actually polling before sending QueueClosedEvent - Add debug logging to diagnose future failures Root cause: signalQueuePollerStarted() was only called in finally block, which was skipped when queue was already closed at first dequeue attempt. This caused awaitQueuePollerStart() to hang or timeout intermittently. --- .../KafkaReplicationIntegrationTest.java | 20 +++++++- .../java/io/a2a/server/events/EventQueue.java | 48 +++++++++---------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index c4b23fb0d..d38f8244c 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -30,7 +30,9 @@ import io.a2a.extras.queuemanager.replicated.core.ReplicatedEventQueueItem; import io.a2a.jsonrpc.common.json.JsonUtil; import io.a2a.server.PublicAgentCard; +import io.a2a.server.events.EventQueue; import io.a2a.server.events.QueueClosedEvent; +import io.a2a.server.events.QueueManager; import io.a2a.spec.A2AClientException; import io.a2a.spec.AgentCard; import io.a2a.spec.Message; @@ -46,6 +48,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Integration test for Kafka replication functionality. @@ -54,6 +58,8 @@ @QuarkusTest public class KafkaReplicationIntegrationTest { + private static final Logger LOGGER = LoggerFactory.getLogger(KafkaReplicationIntegrationTest.class); + @Inject @PublicAgentCard AgentCard agentCard; @@ -65,6 +71,9 @@ public class KafkaReplicationIntegrationTest { @Channel("replicated-events-out") Emitter testEmitter; + @Inject + QueueManager queueManager; + private Client streamingClient; private Client nonStreamingClient; private Client pollingClient; @@ -367,8 +376,11 @@ public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { // Resubscribe to the task - this creates a streaming subscription streamingClient.resubscribe(new TaskIdParams(taskId), List.of(consumer), errorHandler); - // Wait a moment to ensure the streaming subscription is fully established - Thread.sleep(2000); + // Wait for the EventConsumer to start polling (replaces unreliable Thread.sleep) + // This ensures the consumer is ready to receive the QueueClosedEvent + EventQueue queue = queueManager.get(taskId); + assertNotNull(queue, "Queue should exist for task " + taskId); + queueManager.awaitQueuePollerStart(queue); // Now manually send a QueueClosedEvent to Kafka to simulate queue closure on another node QueueClosedEvent closedEvent = new QueueClosedEvent(taskId); @@ -384,6 +396,10 @@ public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { "Streaming subscription should complete when QueueClosedEvent is received"); // Verify the stream completed normally (not with an error) + if (!streamCompleted.get()) { + LOGGER.error("Stream did not complete normally! streamErrored={}, errorRef={}", + streamErrored.get(), errorRef.get(), errorRef.get()); + } assertTrue(streamCompleted.get(), "Stream should complete normally when QueueClosedEvent is received"); assertFalse(streamErrored.get(), "Stream should not error on QueueClosedEvent"); assertNull(errorRef.get(), "Should not receive error when stream completes gracefully"); diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 99f8bc2dc..122d4b0f8 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -662,6 +662,10 @@ public void enqueueLocalOnly(EventQueueItem item) { @Override @Nullable public EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueClosedException { + // CRITICAL: Signal polling started BEFORE any early returns + // This ensures awaitQueuePollerStart() unblocks even if queue is already closed + signalQueuePollerStarted(); + // For immediate close: exit immediately even if queue is not empty (race with MainEventBusProcessor) // For graceful close: only exit when queue is empty (wait for all events to be consumed) // BUT: if awaiting final event, keep polling even if closed and empty @@ -672,32 +676,28 @@ public EventQueueItem dequeueEventItem(int waitMilliSeconds) throws EventQueueCl queue.size()); throw new EventQueueClosedException(); } - try { - if (waitMilliSeconds <= 0) { - EventQueueItem item = queue.poll(); - if (item != null) { - Event event = item.getEvent(); - LOGGER.debug("Dequeued event item (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); - } - return item; + if (waitMilliSeconds <= 0) { + EventQueueItem item = queue.poll(); + if (item != null) { + Event event = item.getEvent(); + LOGGER.debug("Dequeued event item (no wait) {} {}", this, event instanceof Throwable ? event.toString() : event); } - try { - LOGGER.trace("Polling ChildQueue {} (wait={}ms)", System.identityHashCode(this), waitMilliSeconds); - EventQueueItem item = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); - if (item != null) { - Event event = item.getEvent(); - LOGGER.debug("Dequeued event item (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); - } else { - LOGGER.trace("Dequeue timeout (null) from ChildQueue {}", System.identityHashCode(this)); - } - return item; - } catch (InterruptedException e) { - LOGGER.debug("Interrupted dequeue (waiting) {}", this); - Thread.currentThread().interrupt(); - return null; + return item; + } + try { + LOGGER.trace("Polling ChildQueue {} (wait={}ms)", System.identityHashCode(this), waitMilliSeconds); + EventQueueItem item = queue.poll(waitMilliSeconds, TimeUnit.MILLISECONDS); + if (item != null) { + Event event = item.getEvent(); + LOGGER.debug("Dequeued event item (waiting) {} {}", this, event instanceof Throwable ? event.toString() : event); + } else { + LOGGER.trace("Dequeue timeout (null) from ChildQueue {}", System.identityHashCode(this)); } - } finally { - signalQueuePollerStarted(); + return item; + } catch (InterruptedException e) { + LOGGER.debug("Interrupted dequeue (waiting) {}", this); + Thread.currentThread().interrupt(); + return null; } } From 5d2cd75ab7f3f93c299c5f1d1fa03d0ec3ad5575 Mon Sep 17 00:00:00 2001 From: Emmanuel Hugonnet Date: Fri, 6 Feb 2026 18:30:28 +0100 Subject: [PATCH 281/493] refactor: Update A2A protocol to align with spec revision (#633) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move protocol version from AgentCard to AgentInterface level - Simplify resource identifiers from hierarchical names to flat IDs (e.g., "tasks/123" -> "123") - Restructure Part types: remove FilePart/DataPart in favor of unified Part with url/raw/data variants - Add OAuth flow support (ImplicitOAuthFlow, PasswordOAuthFlow) - Rename Security to SecurityRequirement for clarity - Standardize spelling: CANCELLED -> CANCELED - Update all client transports (gRPC, JSON-RPC, REST) and tests - Rename resubscribe to subscribeToTask Breaking changes in proto API affecting: - Task operations (get, cancel, subscribe) - Push notification config management - Message part structure Fixes #632 🦕 Signed-off-by: Emmanuel Hugonnet --- README.md | 16 +- .../java/io/a2a/client/AbstractClient.java | 22 +- .../src/main/java/io/a2a/client/Client.java | 16 +- .../AuthenticationAuthorizationTest.java | 2 - .../java/io/a2a/client/ClientBuilderTest.java | 2 - .../client/transport/grpc/GrpcTransport.java | 50 +- .../transport/jsonrpc/JSONRPCTransport.java | 2 +- .../JSONRPCTransportStreamingTest.java | 18 +- .../jsonrpc/JSONRPCTransportTest.java | 15 +- .../transport/jsonrpc/JsonMessages.java | 99 +- .../jsonrpc/JsonStreamingMessages.java | 12 +- .../jsonrpc/sse/SSEEventListenerTest.java | 15 +- .../client/transport/rest/RestTransport.java | 23 +- .../transport/rest/JsonRestMessages.java | 51 +- .../transport/rest/RestTransportTest.java | 21 +- .../rest/sse/SSEEventListenerTest.java | 6 +- .../client/transport/spi/ClientTransport.java | 6 +- .../interceptors/auth/AuthInterceptor.java | 4 +- .../auth/AuthInterceptorTest.java | 6 +- .../spi/sse/SSEEventListenerTest.java | 14 +- .../cloud/CloudAgentCardProducer.java | 2 - .../examples/cloud/A2ACloudExampleClient.java | 2 +- .../helloworld/AgentCardProducer.java | 2 - ...otificationConfigStoreIntegrationTest.java | 4 +- .../core/ReplicatedQueueManager.java | 2 - .../core/EventSerializationTest.java | 3 - .../core/ReplicatedQueueManagerTest.java | 6 - ...ctiveMessagingReplicationStrategyTest.java | 2 - .../MultiInstanceReplicationAgentCards.java | 2 - .../MultiInstanceReplicationTest.java | 12 +- .../KafkaReplicationIntegrationTest.java | 21 +- .../ReplicationTestAgentCardProducer.java | 2 - .../java/io/a2a/client/http/JsonMessages.java | 3 +- .../io/a2a/jsonrpc/common/json/JsonUtil.java | 12 +- .../wrappers/StreamingJSONRPCRequest.java | 2 +- .../wrappers/SubscribeToTaskRequest.java | 2 +- .../StreamingEventKindSerializationTest.java | 1 - .../apps/quarkus/A2AServerRoutesTest.java | 35 +- .../rest/quarkus/A2AServerRoutesTest.java | 2 +- .../java/io/a2a/server/events/EventQueue.java | 6 +- .../DefaultRequestHandler.java | 12 +- .../requesthandlers/RequestHandler.java | 2 +- .../java/io/a2a/server/tasks/TaskUpdater.java | 1 - .../io/a2a/server/util/ArtifactUtils.java | 12 +- .../server/version/A2AVersionValidator.java | 16 +- .../a2a/server/events/EventConsumerTest.java | 9 +- .../io/a2a/server/events/EventQueueTest.java | 1 - .../AbstractA2ARequestHandlerTest.java | 4 +- .../io/a2a/server/tasks/TaskManagerTest.java | 10 - .../io/a2a/server/tasks/TaskUpdaterTest.java | 8 +- .../version/A2AVersionValidatorTest.java | 3 +- spec-grpc/pom.xml | 2 +- spec-grpc/src/main/java/io/a2a/grpc/A2A.java | 613 ++++----- .../main/java/io/a2a/grpc/A2AServiceGrpc.java | 8 +- .../src/main/java/io/a2a/grpc/AgentCard.java | 873 +++++-------- .../java/io/a2a/grpc/AgentCardOrBuilder.java | 82 +- .../main/java/io/a2a/grpc/AgentInterface.java | 182 ++- .../io/a2a/grpc/AgentInterfaceOrBuilder.java | 24 + .../src/main/java/io/a2a/grpc/AgentSkill.java | 359 +++--- .../java/io/a2a/grpc/AgentSkillOrBuilder.java | 29 +- .../java/io/a2a/grpc/AuthenticationInfo.java | 282 ++--- .../a2a/grpc/AuthenticationInfoOrBuilder.java | 43 +- .../java/io/a2a/grpc/CancelTaskRequest.java | 117 +- .../a2a/grpc/CancelTaskRequestOrBuilder.java | 18 +- ...eateTaskPushNotificationConfigRequest.java | 207 ++- ...ushNotificationConfigRequestOrBuilder.java | 28 +- .../src/main/java/io/a2a/grpc/DataPart.java | 617 --------- .../java/io/a2a/grpc/DataPartOrBuilder.java | 39 - ...leteTaskPushNotificationConfigRequest.java | 283 ++++- ...ushNotificationConfigRequestOrBuilder.java | 38 +- .../src/main/java/io/a2a/grpc/FilePart.java | 1109 ----------------- .../java/io/a2a/grpc/FilePartOrBuilder.java | 102 -- .../GetTaskPushNotificationConfigRequest.java | 283 ++++- ...ushNotificationConfigRequestOrBuilder.java | 38 +- .../main/java/io/a2a/grpc/GetTaskRequest.java | 117 +- .../io/a2a/grpc/GetTaskRequestOrBuilder.java | 18 +- .../java/io/a2a/grpc/ImplicitOAuthFlow.java | 1050 ++++++++++++++++ .../a2a/grpc/ImplicitOAuthFlowOrBuilder.java | 115 ++ ...ListTaskPushNotificationConfigRequest.java | 117 +- ...ushNotificationConfigRequestOrBuilder.java | 18 +- .../src/main/java/io/a2a/grpc/OAuthFlows.java | 432 +++++++ .../java/io/a2a/grpc/OAuthFlowsOrBuilder.java | 38 + spec-grpc/src/main/java/io/a2a/grpc/Part.java | 1055 +++++++++++----- .../main/java/io/a2a/grpc/PartOrBuilder.java | 109 +- .../java/io/a2a/grpc/PasswordOAuthFlow.java | 1050 ++++++++++++++++ .../a2a/grpc/PasswordOAuthFlowOrBuilder.java | 115 ++ .../io/a2a/grpc/PushNotificationConfig.java | 24 +- .../grpc/PushNotificationConfigOrBuilder.java | 6 +- ...Security.java => SecurityRequirement.java} | 114 +- ...java => SecurityRequirementOrBuilder.java} | 4 +- .../io/a2a/grpc/SendMessageConfiguration.java | 8 +- .../SendMessageConfigurationOrBuilder.java | 2 +- .../io/a2a/grpc/SubscribeToTaskRequest.java | 117 +- .../grpc/SubscribeToTaskRequestOrBuilder.java | 18 +- .../a2a/grpc/TaskPushNotificationConfig.java | 465 +++++-- .../TaskPushNotificationConfigOrBuilder.java | 58 +- .../src/main/java/io/a2a/grpc/TaskState.java | 20 +- .../io/a2a/grpc/TaskStatusUpdateEvent.java | 99 +- .../grpc/TaskStatusUpdateEventOrBuilder.java | 10 - .../a2a/grpc/mapper/A2ACommonFieldMapper.java | 25 +- .../a2a/grpc/mapper/AgentInterfaceMapper.java | 1 + .../grpc/mapper/AuthenticationInfoMapper.java | 14 + ...reateTaskPushNotificationConfigMapper.java | 82 +- .../io/a2a/grpc/mapper/DataPartMapper.java | 35 - ...askPushNotificationConfigParamsMapper.java | 12 +- .../io/a2a/grpc/mapper/FilePartMapper.java | 81 -- ...askPushNotificationConfigParamsMapper.java | 18 +- ...askPushNotificationConfigParamsMapper.java | 10 +- .../io/a2a/grpc/mapper/OAuthFlowsMapper.java | 2 + .../java/io/a2a/grpc/mapper/PartMapper.java | 71 +- .../io/a2a/grpc/mapper/SecurityMapper.java | 38 +- .../mapper/SubscribeToTaskRequestMapper.java | 6 +- .../a2a/grpc/mapper/TaskIdParamsMapper.java | 8 +- .../TaskPushNotificationConfigMapper.java | 76 +- .../grpc/mapper/TaskQueryParamsMapper.java | 4 +- .../io/a2a/grpc/mapper/TaskStateMapper.java | 4 +- .../mapper/TaskStatusUpdateEventMapper.java | 26 +- spec-grpc/src/main/proto/a2a.proto | 218 ++-- .../grpc/mapper/StreamResponseMapperTest.java | 3 - .../io/a2a/grpc/utils/JSONRPCUtilsTest.java | 20 +- .../java/io/a2a/grpc/utils/ToProtoTest.java | 81 +- .../java/io/a2a/spec/A2AProtocolError.java | 29 + spec/src/main/java/io/a2a/spec/AgentCard.java | 47 +- .../main/java/io/a2a/spec/AgentInterface.java | 28 +- .../src/main/java/io/a2a/spec/AgentSkill.java | 16 +- .../java/io/a2a/spec/AuthenticationInfo.java | 11 +- spec/src/main/java/io/a2a/spec/DataPart.java | 49 +- .../GetTaskPushNotificationConfigParams.java | 13 +- .../ListTaskPushNotificationConfigParams.java | 2 + .../main/java/io/a2a/spec/SecurityScheme.java | 2 +- .../io/a2a/spec/TaskStatusUpdateEvent.java | 39 +- .../io/a2a/spec/VersionNotSupportedError.java | 2 +- .../io/a2a/tck/server/AgentCardProducer.java | 2 - .../a2a/tck/server/AgentExecutorProducer.java | 18 +- .../apps/common/AbstractA2AServerTest.java | 58 +- .../server/apps/common/AgentCardProducer.java | 3 - .../transport/grpc/handler/GrpcHandler.java | 15 +- .../grpc/handler/GrpcHandlerTest.java | 276 ++-- .../jsonrpc/handler/JSONRPCHandler.java | 2 +- .../jsonrpc/handler/JSONRPCHandlerTest.java | 22 +- .../transport/rest/handler/RestHandler.java | 2 +- .../rest/handler/RestHandlerTest.java | 34 +- 142 files changed, 7092 insertions(+), 5569 deletions(-) delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/DataPart.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/FilePart.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java create mode 100644 spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java rename spec-grpc/src/main/java/io/a2a/grpc/{Security.java => SecurityRequirement.java} (84%) rename spec-grpc/src/main/java/io/a2a/grpc/{SecurityOrBuilder.java => SecurityRequirementOrBuilder.java} (94%) delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/DataPartMapper.java delete mode 100644 spec-grpc/src/main/java/io/a2a/grpc/mapper/FilePartMapper.java diff --git a/README.md b/README.md index 941419f45..bd86952da 100644 --- a/README.md +++ b/README.md @@ -612,24 +612,24 @@ client.deleteTaskPushNotificationConfigurations( new DeleteTaskPushNotificationConfigParams("task-1234", "config-4567", clientCallContext); ``` -#### Resubscribe to a task +#### Subscribe to a task ```java -// Resubscribe to an ongoing task with id "task-1234" using configured consumers +// Subscribe to an ongoing task with id "task-1234" using configured consumers TaskIdParams taskIdParams = new TaskIdParams("task-1234"); -client.resubscribe(taskIdParams); +client.subscribeToTask(taskIdParams); -// Or resubscribe with custom consumers and error handler +// Or subscribe with custom consumers and error handler List> customConsumers = List.of( - (event, card) -> System.out.println("Resubscribe event: " + event) + (event, card) -> System.out.println("Subscribe event: " + event) ); Consumer customErrorHandler = error -> - System.err.println("Resubscribe error: " + error.getMessage()); + System.err.println("Subscribe error: " + error.getMessage()); -client.resubscribe(taskIdParams, customConsumers, customErrorHandler); +client.subscribeToTask(taskIdParams, customConsumers, customErrorHandler); // You can also optionally specify a ClientCallContext with call-specific config to use -client.resubscribe(taskIdParams, clientCallContext); +client.subscribeToTask(taskIdParams, clientCallContext); ``` #### Retrieve details about the server agent that this client agent is communicating with diff --git a/client/base/src/main/java/io/a2a/client/AbstractClient.java b/client/base/src/main/java/io/a2a/client/AbstractClient.java index c47d8238b..c3c8c648f 100644 --- a/client/base/src/main/java/io/a2a/client/AbstractClient.java +++ b/client/base/src/main/java/io/a2a/client/AbstractClient.java @@ -318,7 +318,7 @@ public abstract void deleteTaskPushNotificationConfigurations( @Nullable ClientCallContext context) throws A2AClientException; /** - * Resubscribe to a task's event stream. + * Subscribe to a task's event stream. * This is only available if both the client and server support streaming. * The configured client consumers will be used to handle messages, tasks, * and update events received from the remote agent. The configured streaming @@ -327,12 +327,12 @@ public abstract void deleteTaskPushNotificationConfigurations( * @param request the parameters specifying which task's notification configs to delete * @throws A2AClientException if resubscribing fails for any reason */ - public void resubscribe(@NonNull TaskIdParams request) throws A2AClientException { - resubscribe(request, consumers, streamingErrorHandler, null); + public void subscribeToTask(@NonNull TaskIdParams request) throws A2AClientException { + subscribeToTask(request, consumers, streamingErrorHandler, null); } /** - * Resubscribe to a task's event stream. + * Subscribe to a task's event stream. * This is only available if both the client and server support streaming. * The configured client consumers will be used to handle messages, tasks, * and update events received from the remote agent. The configured streaming @@ -342,13 +342,13 @@ public void resubscribe(@NonNull TaskIdParams request) throws A2AClientException * @param context optional client call context for the request * @throws A2AClientException if resubscribing fails for any reason */ - public void resubscribe(@NonNull TaskIdParams request, + public void subscribeToTask(@NonNull TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException { - resubscribe(request, consumers, streamingErrorHandler, context); + subscribeToTask(request, consumers, streamingErrorHandler, context); } /** - * Resubscribe to a task's event stream. + * Subscribe to a task's event stream. * This is only available if both the client and server support streaming. * The specified client consumers will be used to handle messages, tasks, and * update events received from the remote agent. The specified streaming error @@ -359,14 +359,14 @@ public void resubscribe(@NonNull TaskIdParams request, * @param streamingErrorHandler an error handler that should be used for the streaming case if an error occurs * @throws A2AClientException if resubscribing fails for any reason */ - public void resubscribe(@NonNull TaskIdParams request, + public void subscribeToTask(@NonNull TaskIdParams request, @NonNull List> consumers, @Nullable Consumer streamingErrorHandler) throws A2AClientException { - resubscribe(request, consumers, streamingErrorHandler, null); + subscribeToTask(request, consumers, streamingErrorHandler, null); } /** - * Resubscribe to a task's event stream. + * Subscribe to a task's event stream. * This is only available if both the client and server support streaming. * The specified client consumers will be used to handle messages, tasks, and * update events received from the remote agent. The specified streaming error @@ -378,7 +378,7 @@ public void resubscribe(@NonNull TaskIdParams request, * @param context optional client call context for the request * @throws A2AClientException if resubscribing fails for any reason */ - public abstract void resubscribe(@NonNull TaskIdParams request, + public abstract void subscribeToTask(@NonNull TaskIdParams request, @NonNull List> consumers, @Nullable Consumer streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException; diff --git a/client/base/src/main/java/io/a2a/client/Client.java b/client/base/src/main/java/io/a2a/client/Client.java index 405155ee2..e60cac4e5 100644 --- a/client/base/src/main/java/io/a2a/client/Client.java +++ b/client/base/src/main/java/io/a2a/client/Client.java @@ -152,7 +152,7 @@ * * // Later, reconnect and resume receiving events * String taskId = "task-123"; // From original request - * client.resubscribe( + * client.subscribeToTask( * new TaskIdParams(taskId), * List.of((event, card) -> { * // Process events from where we left off @@ -529,7 +529,7 @@ public void deleteTaskPushNotificationConfigurations( } /** - * Resubscribe to an existing task to receive remaining events. + * Subscribe to an existing task to receive remaining events. *

    * This method is useful when a client disconnects during a long-running task and wants to * resume receiving events without starting a new task. The agent will deliver any events @@ -551,7 +551,7 @@ public void deleteTaskPushNotificationConfigurations( * // ... client1 disconnects ... * * // Later, reconnect (client2) - * client2.resubscribe( + * client2.subscribeToTask( * new TaskIdParams(taskId), * List.of((event, card) -> { * if (event instanceof TaskUpdateEvent tue) { @@ -559,19 +559,19 @@ public void deleteTaskPushNotificationConfigurations( * tue.getTask().status().state()); * } * }), - * throwable -> System.err.println("Resubscribe error: " + throwable), + * throwable -> System.err.println("Subscribe error: " + throwable), * null * ); * }

    * - * @param request the task ID to resubscribe to + * @param request the task ID to subscribe to * @param consumers the event consumers for processing events (required) * @param streamingErrorHandler error handler for streaming errors (optional) * @param context custom call context for request interceptors (optional) - * @throws A2AClientException if resubscription is not supported or if the task cannot be found + * @throws A2AClientException if subscription is not supported or if the task cannot be found */ @Override - public void resubscribe(@NonNull TaskIdParams request, + public void subscribeToTask(@NonNull TaskIdParams request, @NonNull List> consumers, @Nullable Consumer streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException { @@ -588,7 +588,7 @@ public void resubscribe(@NonNull TaskIdParams request, overriddenErrorHandler.accept(e); } }; - clientTransport.resubscribe(request, eventHandler, overriddenErrorHandler, context); + clientTransport.subscribeToTask(request, eventHandler, overriddenErrorHandler, context); } /** diff --git a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java index 58b528881..5b8d9c94a 100644 --- a/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java +++ b/client/base/src/test/java/io/a2a/client/AuthenticationAuthorizationTest.java @@ -1,6 +1,5 @@ package io.a2a.client; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockserver.model.HttpRequest.request; @@ -90,7 +89,6 @@ public void setUp() { .description("Test skill") .tags(Collections.singletonList("test")) .build())) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .supportedInterfaces(java.util.Arrays.asList( new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL), new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL), diff --git a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java index dd38c937d..3be9d4918 100644 --- a/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java +++ b/client/base/src/test/java/io/a2a/client/ClientBuilderTest.java @@ -1,6 +1,5 @@ package io.a2a.client; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import java.util.Collections; import java.util.List; @@ -41,7 +40,6 @@ public class ClientBuilderTest { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .supportedInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999"))) .build(); diff --git a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java index 8cc67d7b0..7824e83c0 100644 --- a/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java +++ b/client/transport/grpc/src/main/java/io/a2a/client/transport/grpc/GrpcTransport.java @@ -142,7 +142,7 @@ public void sendMessageStreaming(MessageSendParams request, Consumer eventConsumer, + public void subscribeToTask(TaskIdParams request, Consumer eventConsumer, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); io.a2a.grpc.SubscribeToTaskRequest grpcRequest = io.a2a.grpc.SubscribeToTaskRequest.newBuilder() .setTenant(resolveTenant(request.tenant())) - .setName("tasks/" + request.id()) + .setId(request.id()) .build(); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SUBSCRIBE_TO_TASK_METHOD, grpcRequest, agentCard, context); @@ -331,12 +336,13 @@ public void resubscribe(TaskIdParams request, Consumer event A2AServiceStub stubWithMetadata = createAsyncStubWithMetadata(context, payloadAndHeaders); stubWithMetadata.subscribeToTask(grpcRequest, streamObserver); } catch (StatusRuntimeException e) { - throw GrpcErrorMapper.mapGrpcError(e, "Failed to resubscribe task push notification config: "); + throw GrpcErrorMapper.mapGrpcError(e, "Failed to subscribe task push notification config: "); } } /** * Ensure tenant is set, using agent default if not provided in request + * * @param request the initial request. * @return the updated request with the tenant set. */ @@ -447,24 +453,6 @@ private A2AServiceStub createAsyncStubWithMetadata(@Nullable ClientCallContext c return asyncStub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); } - private String getTaskPushNotificationConfigName(GetTaskPushNotificationConfigParams params) { - return getTaskPushNotificationConfigName(params.id(), params.pushNotificationConfigId()); - } - - private String getTaskPushNotificationConfigName(String taskId, @Nullable String pushNotificationConfigId) { - StringBuilder name = new StringBuilder(); - name.append("tasks/"); - name.append(taskId); - if (pushNotificationConfigId != null) { - name.append("/pushNotificationConfigs/"); - name.append(pushNotificationConfigId); - } - //name.append("/pushNotificationConfigs/"); - // Use taskId as default config ID if none provided - //name.append(pushNotificationConfigId != null ? pushNotificationConfigId : taskId); - return name.toString(); - } - private PayloadAndHeaders applyInterceptors(String methodName, Object payload, AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { PayloadAndHeaders payloadAndHeaders = new PayloadAndHeaders(payload, diff --git a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java index 21ea398c1..9b8af4ea2 100644 --- a/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java +++ b/client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java @@ -255,7 +255,7 @@ public void deleteTaskPushNotificationConfigurations(DeleteTaskPushNotificationC } @Override - public void resubscribe(TaskIdParams request, Consumer eventConsumer, + public void subscribeToTask(TaskIdParams request, Consumer eventConsumer, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); checkNotNullParam("eventConsumer", eventConsumer); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java index c994ff89d..b06d07648 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java @@ -2,8 +2,8 @@ import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.SEND_MESSAGE_STREAMING_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.SEND_MESSAGE_STREAMING_TEST_RESPONSE; -import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE; -import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.TASK_RESUBSCRIPTION_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.TASK_SUBSCRIPTION_TEST_REQUEST; +import static io.a2a.client.transport.jsonrpc.JsonStreamingMessages.TASK_SUBSCRIPTION_REQUEST_TEST_RESPONSE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -124,19 +124,17 @@ public void testA2AClientSendStreamingMessage() throws Exception { } @Test - public void testA2AClientResubscribeToTask() throws Exception { - this.server.when( - request() + public void testA2AClientSubscribeToTask() throws Exception { + this.server.when(request() .withMethod("POST") .withPath("/") - .withBody(JsonBody.json(TASK_RESUBSCRIPTION_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) + .withBody(JsonBody.json(TASK_SUBSCRIPTION_TEST_REQUEST, MatchType.ONLY_MATCHING_FIELDS)) ) - .respond( - response() + .respond(response() .withStatusCode(200) .withHeader("Content-Type", "text/event-stream") - .withBody(TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE) + .withBody(TASK_SUBSCRIPTION_REQUEST_TEST_RESPONSE) ); JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001"); @@ -149,7 +147,7 @@ public void testA2AClientResubscribeToTask() throws Exception { latch.countDown(); }; Consumer errorHandler = error -> {}; - client.resubscribe(taskIdParams, eventHandler, errorHandler, null); + client.subscribeToTask(taskIdParams, eventHandler, errorHandler, null); boolean eventReceived = latch.await(10, TimeUnit.SECONDS); assertTrue(eventReceived); diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java index 74fe9545a..f6677ff13 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java @@ -22,7 +22,7 @@ import static io.a2a.client.transport.jsonrpc.JsonMessages.SEND_MESSAGE_WITH_MIXED_PARTS_TEST_RESPONSE; import static io.a2a.client.transport.jsonrpc.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.transport.jsonrpc.JsonMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; +import static io.a2a.spec.AgentInterface.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -313,8 +313,7 @@ public void testA2AClientGetTaskPushNotificationConfig() throws Exception { assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); - assertTrue(authenticationInfo.schemes().size() == 1); - assertEquals("jwt", authenticationInfo.schemes().get(0)); + assertEquals("jwt", authenticationInfo.scheme()); } @Test @@ -338,15 +337,13 @@ public void testA2AClientCreateTaskPushNotificationConfig() throws Exception { PushNotificationConfig.builder() .id("c295ea44-7543-4f78-b524-7a38915ad6e4") .url("https://example.com/callback") - .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), - null)) + .authentication(new AuthenticationInfo("jwt", null)) .build(), ""), null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); - assertEquals(1, authenticationInfo.schemes().size()); - assertEquals("jwt", authenticationInfo.schemes().get(0)); + assertEquals("jwt", authenticationInfo.scheme()); } @Test @@ -379,7 +376,7 @@ public void testA2AClientGetExtendedAgentCard() throws Exception { assertNotNull(securitySchemes); OpenIdConnectSecurityScheme google = (OpenIdConnectSecurityScheme) securitySchemes.get("google"); assertEquals("https://accounts.google.com/.well-known/openid-configuration", google.openIdConnectUrl()); - List>> security = agentCard.security(); + List>> security = agentCard.securityRequirements(); assertEquals(1, security.size()); Map> securityMap = security.get(0); List scopes = securityMap.get("google"); @@ -418,7 +415,7 @@ public void testA2AClientGetExtendedAgentCard() throws Exception { assertEquals("This is an extended skill.", skills.get(2).description()); assertEquals(List.of("extended"), skills.get(2).tags()); assertEquals("https://georoute-agent.example.com/icon.png", agentCard.iconUrl()); - assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.protocolVersions().get(0)); + assertEquals(CURRENT_PROTOCOL_VERSION, agentCard.supportedInterfaces().get(0).protocolVersion()); } @Test diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index 211f5e4f6..b55c0f9d1 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -1,6 +1,6 @@ package io.a2a.client.transport.jsonrpc; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; +import static io.a2a.spec.AgentInterface.CURRENT_PROTOCOL_VERSION; /** * Request and response messages used by the tests. These have been created following examples from @@ -8,9 +8,8 @@ */ public class JsonMessages { - static final String AGENT_CARD = String.format(""" + static final String AGENT_CARD = """ { - "protocolVersions": ["%s"], "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", "supportedInterfaces" : [ @@ -37,7 +36,7 @@ public class JsonMessages { } } }, - "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], + "securityRequirements": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -81,7 +80,7 @@ public class JsonMessages { "signature": "QFdkNLNszlGj3z3u0YQGt_T9LixY3qtdQpZmsTdDHDe3fXV9y9-B3m2-XgCpzuhiLt8E0tV6HXoZKHv4GtHgKQ" } ] - }""", CURRENT_PROTOCOL_VERSION); + }"""; static final String SEND_MESSAGE_TEST_REQUEST = """ { @@ -228,7 +227,7 @@ public class JsonMessages { "jsonrpc":"2.0", "method":"GetTask", "params":{ - "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength":10 } } @@ -262,16 +261,12 @@ public class JsonMessages { "text":"tell me a joke" }, { - "file":{ - "file_with_uri":"file:///path/to/file.txt", - "mediaType":"text/plain" - } + "url":"file:///path/to/file.txt", + "mediaType":"text/plain" }, { - "file":{ - "file_with_bytes":"aGVsbG8=", - "name":"hello.txt" - } + "raw":"aGVsbG8=", + "filename":"hello.txt" } ], "messageId":"message-123" @@ -289,7 +284,7 @@ public class JsonMessages { "jsonrpc":"2.0", "method":"CancelTask", "params":{ - "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + "id":"de38c76d-d54c-436c-8b9f-4c2703648d64" } } """; @@ -302,7 +297,7 @@ public class JsonMessages { "id":"de38c76d-d54c-436c-8b9f-4c2703648d64", "contextId":"c295ea44-7543-4f78-b524-7a38915ad6e4", "status":{ - "state":"TASK_STATE_CANCELLED" + "state":"TASK_STATE_CANCELED" }, "metadata":{ @@ -316,7 +311,8 @@ public class JsonMessages { "jsonrpc":"2.0", "method":"GetTaskPushNotificationConfig", "params":{ - "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4" + "taskId":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "id":"c295ea44-7543-4f78-b524-7a38915ad6e4" } }"""; @@ -325,11 +321,12 @@ public class JsonMessages { "jsonrpc": "2.0", "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "result": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "c295ea44-7543-4f78-b524-7a38915ad6e4", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { - "schemes": ["jwt"] + "scheme": "jwt" } } } @@ -341,17 +338,12 @@ public class JsonMessages { "jsonrpc":"2.0", "method":"CreateTaskPushNotificationConfig", "params":{ - "parent":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "taskId":"de38c76d-d54c-436c-8b9f-4c2703648d64", "configId":"c295ea44-7543-4f78-b524-7a38915ad6e4", "config":{ - "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4", - "pushNotificationConfig":{ - "url":"https://example.com/callback", - "authentication":{ - "schemes":[ - "jwt" - ] - } + "url":"https://example.com/callback", + "authentication":{ + "scheme":"jwt" } } } @@ -362,11 +354,12 @@ public class JsonMessages { "jsonrpc": "2.0", "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "result": { - "name":"tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/c295ea44-7543-4f78-b524-7a38915ad6e4", + "taskId":"de38c76d-d54c-436c-8b9f-4c2703648d64", + "id":"c295ea44-7543-4f78-b524-7a38915ad6e4", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { - "schemes": ["jwt"] + "scheme": "jwt" } } } @@ -387,10 +380,8 @@ public class JsonMessages { "text":"analyze this image" }, { - "file":{ - "fileWithUri":"file:///path/to/image.jpg", - "mediaType":"image/jpeg" - } + "url":"file:///path/to/image.jpg", + "mediaType":"image/jpeg" } ], "metadata":{ @@ -453,12 +444,10 @@ public class JsonMessages { }, { "data":{ - "data":{ - "temperature":25.5, - "humidity":60.2, - "location":"San Francisco", - "timestamp":"2024-01-15T10:30:00Z" - } + "temperature":25.5, + "humidity":60.2, + "location":"San Francisco", + "timestamp":"2024-01-15T10:30:00Z" } } ], @@ -521,19 +510,15 @@ public class JsonMessages { "text":"analyze this data and image" }, { - "file":{ - "fileWithBytes":"aGVsbG8=", - "mediaType":"image/png", - "name":"chart.png" - } + "raw":"aGVsbG8=", + "mediaType":"image/png", + "filename":"chart.png" }, { "data":{ - "data":{ - "chartType":"bar", - "dataPoints":[10.0, 20.0, 30.0, 40.0], - "labels":["Q1", "Q2", "Q3", "Q4"] - } + "chartType":"bar", + "dataPoints":[10.0, 20.0, 30.0, 40.0], + "labels":["Q1", "Q2", "Q3", "Q4"] } } ], @@ -589,7 +574,7 @@ public class JsonMessages { } """; - static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = String.format(""" + static final String GET_AUTHENTICATED_EXTENDED_AGENT_CARD_RESPONSE = """ { "jsonrpc": "2.0", "id": "1", @@ -618,7 +603,7 @@ public class JsonMessages { } } }, - "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], + "securityRequirements": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -662,7 +647,6 @@ public class JsonMessages { "tags": ["extended"] } ], - "protocolVersions": ["%s"], "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpPU0UiLCJraWQiOiJrZXktMSIsImprdUI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWdlbnQvandrcy5qc29uIn0", @@ -670,9 +654,9 @@ public class JsonMessages { } ] } - }""", CURRENT_PROTOCOL_VERSION); + }"""; - static final String AGENT_CARD_SUPPORTS_EXTENDED = String.format(""" + static final String AGENT_CARD_SUPPORTS_EXTENDED = """ { "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", @@ -735,7 +719,6 @@ public class JsonMessages { "text/html" ] } - ], - "protocolVersions": ["%s"] - }""", CURRENT_PROTOCOL_VERSION); + ] + }"""; } diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java index 021c6c7f6..39f2d7859 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonStreamingMessages.java @@ -50,8 +50,7 @@ public class JsonStreamingMessages { "contextId": "2", "status": { "state": "TASK_STATE_SUBMITTED" - }, - "final": false + } } } }"""; @@ -66,8 +65,7 @@ public class JsonStreamingMessages { "contextId": "2", "status": { "state": "TASK_STATE_COMPLETED" - }, - "final": true + } } } }"""; @@ -141,19 +139,19 @@ public class JsonStreamingMessages { """; - static final String TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE = + static final String TASK_SUBSCRIPTION_REQUEST_TEST_RESPONSE = """ event: message data: {"jsonrpc":"2.0","id":1,"result":{"task":{"id":"2","contextId":"context-1234","status":{"state":"TASK_STATE_COMPLETED"},"artifacts":[{"artifactId":"artifact-1","name":"joke","parts":[{"text":"Why did the chicken cross the road? To get to the other side!"}]}],"metadata":{}}}} """; - public static final String TASK_RESUBSCRIPTION_TEST_REQUEST = """ + public static final String TASK_SUBSCRIPTION_TEST_REQUEST = """ { "jsonrpc":"2.0", "method":"SubscribeToTask", "params":{ - "name":"tasks/task-1234" + "id":"task-1234" } }"""; } \ No newline at end of file diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java index 0817b753f..23e162e7c 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java @@ -184,12 +184,15 @@ public void testOnFailure() { @Test public void testFinalTaskStatusUpdateEventCancels() { - TaskStatusUpdateEvent tsue = TaskStatusUpdateEvent.builder() - .taskId("1234") - .contextId("xyz") - .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) - .build(); + TaskStatus completedStatus = new TaskStatus(TaskState.COMPLETED); + // Use constructor since Builder doesn't have isFinal method + TaskStatusUpdateEvent tsue = new TaskStatusUpdateEvent( + "1234", + completedStatus, + "xyz", + completedStatus.state().isFinal(), // Derive from state + null + ); // Set up event handler AtomicReference receivedEvent = new AtomicReference<>(); diff --git a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java index 88c86846d..61fb29428 100644 --- a/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java +++ b/client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java @@ -27,7 +27,6 @@ import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageOrBuilder; import com.google.protobuf.util.JsonFormat; -import io.a2a.client.http.A2ACardResolver; import io.a2a.client.http.A2AHttpClient; import io.a2a.client.http.A2AHttpClientFactory; import io.a2a.client.http.A2AHttpResponse; @@ -132,7 +131,7 @@ public void sendMessageStreaming(MessageSendParams messageSendParams, Consumer eventConsumer, + public void subscribeToTask(TaskIdParams request, Consumer eventConsumer, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException { checkNotNullParam("request", request); io.a2a.grpc.SubscribeToTaskRequest.Builder builder = io.a2a.grpc.SubscribeToTaskRequest.newBuilder(); - builder.setName("tasks/" + request.id()); + builder.setId(request.id()); PayloadAndHeaders payloadAndHeaders = applyInterceptors(SUBSCRIBE_TO_TASK_METHOD, builder, agentCard, context); AtomicReference> ref = new AtomicReference<>(); diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java index f324f004e..35ac4a9c5 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/JsonRestMessages.java @@ -48,7 +48,7 @@ public class JsonRestMessages { static final String CANCEL_TASK_TEST_REQUEST = """ { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64" }"""; static final String CANCEL_TASK_TEST_RESPONSE = """ @@ -56,7 +56,7 @@ public class JsonRestMessages { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { - "state": "TASK_STATE_CANCELLED" + "state": "TASK_STATE_CANCELED" }, "metadata": {} }"""; @@ -86,16 +86,12 @@ public class JsonRestMessages { "text": "tell me a joke" }, { - "file": { - "file_with_uri": "file:///path/to/file.txt", - "mediaType": "text/plain" - } + "url": "file:///path/to/file.txt", + "mediaType": "text/plain" }, { - "file": { - "file_with_bytes": "aGVsbG8=", - "mediaType": "text/plain" - } + "raw": "aGVsbG8=", + "mediaType": "text/plain" } ], "messageId": "message-123" @@ -129,7 +125,7 @@ public class JsonRestMessages { "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "securityRequirements": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -195,7 +191,7 @@ public class JsonRestMessages { "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "securityRequirements": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -261,7 +257,7 @@ public class JsonRestMessages { "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration" } }, - "security": [{ "google": ["openid", "profile", "email"] }], + "securityRequirements": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ @@ -311,11 +307,12 @@ public class JsonRestMessages { static final String GET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "10", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { - "schemes": ["jwt"] + "scheme": "jwt" } } }"""; @@ -323,16 +320,18 @@ public class JsonRestMessages { { "configs":[ { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "10", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { - "schemes": ["jwt"] + "scheme": "jwt" } } }, { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/5", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "5", "pushNotificationConfig": { "url": "https://test.com/callback" } @@ -343,26 +342,24 @@ public class JsonRestMessages { static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST = """ { - "parent": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", "configId": "default-config-id", "config": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/default-config-id", - "pushNotificationConfig": { - "url": "https://example.com/callback", - "authentication": { - "schemes": [ "jwt" ] - } + "url": "https://example.com/callback", + "authentication": { + "scheme": "jwt" } } }"""; static final String SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE = """ { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/10", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "10", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { - "schemes": ["jwt"] + "scheme": "jwt" } } }"""; diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java index 04efb4eca..476a93ba2 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java @@ -13,7 +13,6 @@ import static io.a2a.client.transport.rest.JsonRestMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_REQUEST; import static io.a2a.client.transport.rest.JsonRestMessages.SET_TASK_PUSH_NOTIFICATION_CONFIG_TEST_RESPONSE; import static io.a2a.client.transport.rest.JsonRestMessages.TASK_RESUBSCRIPTION_REQUEST_TEST_RESPONSE; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -90,7 +89,6 @@ public class RestTransportTest { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); @BeforeEach @@ -309,15 +307,14 @@ public void testCreateTaskPushNotificationConfiguration() throws Exception { .id("default-config-id") .url("https://example.com/callback") .authentication( - new AuthenticationInfo(Collections.singletonList("jwt"), null)) + new AuthenticationInfo("jwt", null)) .build(), "tenant"); TaskPushNotificationConfig taskPushNotificationConfig = client.createTaskPushNotificationConfiguration(pushedConfig, null); PushNotificationConfig pushNotificationConfig = taskPushNotificationConfig.pushNotificationConfig(); assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); - assertEquals(1, authenticationInfo.schemes().size()); - assertEquals("jwt", authenticationInfo.schemes().get(0)); + assertEquals("jwt", authenticationInfo.scheme()); } /** @@ -343,8 +340,7 @@ public void testGetTaskPushNotificationConfiguration() throws Exception { assertNotNull(pushNotificationConfig); assertEquals("https://example.com/callback", pushNotificationConfig.url()); AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); - assertTrue(authenticationInfo.schemes().size() == 1); - assertEquals("jwt", authenticationInfo.schemes().get(0)); + assertEquals("jwt", authenticationInfo.scheme()); } /** @@ -372,8 +368,7 @@ public void testListTaskPushNotificationConfigurations() throws Exception { assertEquals("https://example.com/callback", pushNotificationConfig.url()); assertEquals("10", pushNotificationConfig.id()); AuthenticationInfo authenticationInfo = pushNotificationConfig.authentication(); - assertTrue(authenticationInfo.schemes().size() == 1); - assertEquals("jwt", authenticationInfo.schemes().get(0)); + assertEquals("jwt", authenticationInfo.scheme()); assertEquals("", authenticationInfo.credentials()); pushNotificationConfig = result.configs().get(1).pushNotificationConfig(); assertNotNull(pushNotificationConfig); @@ -404,11 +399,11 @@ public void testDeleteTaskPushNotificationConfigurations() throws Exception { } /** - * Test of resubscribe method, of class JSONRestTransport. + * Test of subscribe method, of class JSONRestTransport. */ @Test - public void testResubscribe() throws Exception { - log.info("Testing resubscribe"); + public void testSubscribe() throws Exception { + log.info("Testing subscribeToTask"); this.server.when( request() @@ -432,7 +427,7 @@ public void testResubscribe() throws Exception { latch.countDown(); }; Consumer errorHandler = error -> {}; - client.resubscribe(taskIdParams, eventHandler, errorHandler, null); + client.subscribeToTask(taskIdParams, eventHandler, errorHandler, null); boolean eventReceived = latch.await(10, TimeUnit.SECONDS); assertTrue(eventReceived); diff --git a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java index 1a2bd7f26..eed199ab3 100644 --- a/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java +++ b/client/transport/rest/src/test/java/io/a2a/client/transport/rest/sse/SSEEventListenerTest.java @@ -151,8 +151,7 @@ public void testOnMessageWithStatusUpdateEvent() { "contextId": "2", "status": { "state": "TASK_STATE_SUBMITTED" - }, - "final": false + } } } """; @@ -187,8 +186,7 @@ public void testOnMessageWithFinalStatusUpdateEventCancels() { "contextId": "2", "status": { "state": "TASK_STATE_COMPLETED" - }, - "final": true + } } } """; diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java index 504e4c601..82fe40996 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/ClientTransport.java @@ -127,13 +127,13 @@ void deleteTaskPushNotificationConfigurations( /** * Reconnect to get task updates for an existing task. * - * @param request the task ID parameters specifying which task to resubscribe to + * @param request the task ID parameters specifying which task to subscribe to * @param eventConsumer consumer that will receive streaming events as they arrive * @param errorConsumer consumer that will be called if an error occurs during streaming * @param context optional client call context for the request (may be {@code null}) - * @throws A2AClientException if resubscribing to the task fails for any reason + * @throws A2AClientException if subscribing to the task fails for any reason */ - void resubscribe(TaskIdParams request, Consumer eventConsumer, + void subscribeToTask(TaskIdParams request, Consumer eventConsumer, Consumer errorConsumer, @Nullable ClientCallContext context) throws A2AClientException; /** diff --git a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java index 3573d6784..6cfc322da 100644 --- a/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java +++ b/client/transport/spi/src/main/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptor.java @@ -35,10 +35,10 @@ public AuthInterceptor(final CredentialService credentialService) { public PayloadAndHeaders intercept(String methodName, @Nullable Object payload, Map headers, @Nullable AgentCard agentCard, @Nullable ClientCallContext clientCallContext) { Map updatedHeaders = new HashMap<>(headers == null ? new HashMap<>() : headers); - if (agentCard == null || agentCard.security() == null || agentCard.securitySchemes() == null) { + if (agentCard == null || agentCard.securityRequirements()== null || agentCard.securitySchemes() == null) { return new PayloadAndHeaders(payload, updatedHeaders); } - for (Map> requirement : agentCard.security()) { + for (Map> requirement : agentCard.securityRequirements()) { for (String securitySchemeName : requirement.keySet()) { String credential = credentialService.getCredential(securitySchemeName, clientCallContext); if (credential != null && agentCard.securitySchemes().containsKey(securitySchemeName)) { diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java index a9f07c83e..9ea69f354 100644 --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/interceptors/auth/AuthInterceptorTest.java @@ -235,7 +235,7 @@ void testAvailableSecuritySchemeNotInAgentCardSecuritySchemes() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .security(List.of(Map.of(schemeName, List.of()))) + .securityRequirements(List.of(Map.of(schemeName, List.of()))) .securitySchemes(Map.of()) // no security schemes .build(); @@ -289,7 +289,7 @@ void testNoAgentCardSecuritySpecified() { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .security(null) // no security info + .securityRequirements(null) // no security info .build(); Map requestPayload = Map.of("test", "payload"); @@ -321,7 +321,7 @@ private AgentCard createAgentCard(String schemeName, SecurityScheme securitySche .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(List.of()) - .security(List.of(Map.of(schemeName, List.of()))) + .securityRequirements(List.of(Map.of(schemeName, List.of()))) .securitySchemes(Map.of(schemeName, securityScheme)) .build(); } diff --git a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java index 3b0f37b2e..ac1392c5e 100644 --- a/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java +++ b/client/transport/spi/src/test/java/io/a2a/client/transport/spi/sse/SSEEventListenerTest.java @@ -118,12 +118,14 @@ private static Task createTask(TaskState state) { } private static TaskStatusUpdateEvent createTaskStatusUpdateEvent(TaskState state, boolean isFinal) { - return TaskStatusUpdateEvent.builder() - .taskId(TEST_TASK_ID) - .contextId(TEST_CONTEXT_ID) - .status(new TaskStatus(state)) - .isFinal(isFinal) - .build(); + // Use constructor since Builder doesn't have isFinal method + return new TaskStatusUpdateEvent( + TEST_TASK_ID, + new TaskStatus(state), + TEST_CONTEXT_ID, + isFinal, + null + ); } private static TestSSEEventListener createListenerWithEventCapture(AtomicReference eventCapture) { diff --git a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java index e96c6f1c1..b3adbcdd8 100644 --- a/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java +++ b/examples/cloud-deployment/server/src/main/java/io/a2a/examples/cloud/CloudAgentCardProducer.java @@ -1,6 +1,5 @@ package io.a2a.examples.cloud; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import java.util.Collections; import java.util.List; @@ -57,7 +56,6 @@ public AgentCard agentCard() { )) .build() )) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java index b29b0b55f..0dce363df 100644 --- a/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java +++ b/examples/cloud-deployment/server/src/test/java/io/a2a/examples/cloud/A2ACloudExampleClient.java @@ -199,7 +199,7 @@ private void subscribeToTaskUpdates() { Thread.sleep(1000); // Wait for Kafka events to propagate } - streamingClient.resubscribe( + streamingClient.subscribeToTask( new TaskIdParams(serverTaskId), List.of(this::handleSubscriptionEvent), this::handleSubscriptionError diff --git a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java index 165b1574e..3725e1bd7 100644 --- a/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java +++ b/examples/helloworld/server/src/main/java/io/a2a/examples/helloworld/AgentCardProducer.java @@ -1,6 +1,5 @@ package io.a2a.examples.helloworld; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import java.util.Collections; import java.util.List; @@ -42,7 +41,6 @@ public AgentCard agentCard() { .tags(Collections.singletonList("hello world")) .examples(List.of("hi", "hello world")) .build())) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java index ab06747de..0366e236a 100644 --- a/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java +++ b/extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaDatabasePushNotificationConfigStoreIntegrationTest.java @@ -129,7 +129,7 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep // Step 3: Verify the configuration was stored using client API TaskPushNotificationConfig storedConfig = client.getTaskPushNotificationConfiguration( - new GetTaskPushNotificationConfigParams(taskId)); + new GetTaskPushNotificationConfigParams(taskId, "test-config-1")); assertNotNull(storedConfig); assertEquals(taskId, storedConfig.taskId()); @@ -183,7 +183,7 @@ public void testJpaDatabasePushNotificationConfigStoreIntegration() throws Excep // Verify deletion by asserting that getting the config now throws an exception assertThrows(A2AClientException.class, () -> { - client.getTaskPushNotificationConfiguration(new GetTaskPushNotificationConfigParams(taskId)); + client.getTaskPushNotificationConfiguration(new GetTaskPushNotificationConfigParams(taskId, "test-config-1")); }, "Getting a deleted config should throw an A2AClientException"); } diff --git a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java index 44dfbe427..a5e3c629c 100644 --- a/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java +++ b/extras/queue-manager-replicated/core/src/main/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManager.java @@ -16,7 +16,6 @@ import io.a2a.server.events.MainEventBus; import io.a2a.server.events.QueueManager; import io.a2a.server.tasks.TaskStateProvider; -import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -170,7 +169,6 @@ public void onTaskFinalized(@Observes(during = TransactionPhase.AFTER_SUCCESS) T .taskId(taskId) .contextId(finalTask.contextId()) .status(finalTask.status()) - .isFinal(true) .build(); // Send TaskStatusUpdateEvent FIRST to ensure it arrives before poison pill diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java index ca781b5b0..33eaa908f 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/EventSerializationTest.java @@ -110,7 +110,6 @@ public void testTaskStatusUpdateEventSerialization() throws JsonProcessingExcept .taskId("test-task-abc") .contextId("test-context-def") .status(status) - .isFinal(true) .build(); // Test serialization as Event @@ -206,7 +205,6 @@ public void testReplicatedEventWithStreamingEventSerialization() throws JsonProc .taskId("replicated-test-task") .contextId("replicated-test-context") .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .build(); // Create ReplicatedEventQueueItem with StreamingEventKind @@ -280,7 +278,6 @@ public void testReplicatedEventBackwardCompatibility() throws JsonProcessingExce .taskId("backward-compat-task") .contextId("backward-compat-context") .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) .build(); // Use the backward compatibility constructor diff --git a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java index 14b4c1f51..b766863aa 100644 --- a/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java +++ b/extras/queue-manager-replicated/core/src/test/java/io/a2a/extras/queuemanager/replicated/core/ReplicatedQueueManagerTest.java @@ -67,7 +67,6 @@ void setUp() { .taskId("test-task") .contextId("test-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); } @@ -80,7 +79,6 @@ private TaskStatusUpdateEvent createEventForTask(String taskId) { .taskId(taskId) .contextId("test-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); } @@ -277,7 +275,6 @@ void testReplicatedEventJsonSerialization() throws Exception { .taskId("json-test-task") .contextId("json-test-context") .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) .build(); ReplicatedEventQueueItem original = new ReplicatedEventQueueItem("json-test-task", originalEvent); @@ -350,7 +347,6 @@ public void onTaskFinalized(String tid) { .taskId(taskId) // Use same taskId as queue .contextId("test-context") .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .build(); queue.enqueueEvent(event); } @@ -376,7 +372,6 @@ public void onTaskFinalized(String tid) { .taskId(taskId) // Use same taskId as queue .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); queueManager.onReplicatedEvent(replicatedEvent); @@ -505,7 +500,6 @@ void testReplicatedEventToExistingQueueWhenTaskBecomesInactive() throws Interrup .taskId(taskId) .contextId("test-context") .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, newEvent); queueManager.onReplicatedEvent(replicatedEvent); diff --git a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java index 4638f7385..81bb9ffe0 100644 --- a/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java +++ b/extras/queue-manager-replicated/replication-mp-reactive/src/test/java/io/a2a/extras/queuemanager/replicated/mp_reactive/ReactiveMessagingReplicationStrategyTest.java @@ -44,7 +44,6 @@ public void setUp() { .taskId("test-task") .contextId("test-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); } @@ -54,7 +53,6 @@ private String createValidJsonMessage(String taskId, String contextId) throws Ex .taskId(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, event); return JsonUtil.toJson(replicatedEvent); diff --git a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java index a8020f5ae..4d1133ad9 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java +++ b/extras/queue-manager-replicated/tests-multi-instance/quarkus-common/src/main/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/common/MultiInstanceReplicationAgentCards.java @@ -1,6 +1,5 @@ package io.a2a.extras.queuemanager.replicated.tests.multiinstance.common; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import java.util.Collections; import java.util.List; @@ -47,7 +46,6 @@ public static AgentCard createAgentCard(int instanceNumber, int port) { .description("Fire-and-forget agent for testing replication") .tags(Collections.singletonList("test")) .build())) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } diff --git a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java index b98f0e87d..81bc23af3 100644 --- a/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java +++ b/extras/queue-manager-replicated/tests-multi-instance/tests/src/test/java/io/a2a/extras/queuemanager/replicated/tests/multiinstance/MultiInstanceReplicationTest.java @@ -244,7 +244,7 @@ public void testInfrastructureStartup() { /** * Main multi-instance replication test following architect's guidance: * 1. Send initial message on app1 (creates task in non-final state) - * 2. Resubscribe to that task from both app1 and app2 + * 2. Subscribe to that task from both app1 and app2 * 3. Send message on app1, verify both subscribers receive it * 4. Send message on app2, verify both subscribers receive it * 5. Send final message to transition task to COMPLETED @@ -328,7 +328,7 @@ public void testMultiInstanceEventReplication() throws Exception { } System.out.println("APP1 received event: " + eventDetail); - // Per A2A spec 3.1.6: Handle initial TaskEvent on resubscribe + // Per A2A spec 3.1.6: Handle initial TaskEvent on subscribe if (!app1ReceivedInitialTask.get() && event instanceof io.a2a.client.TaskEvent) { app1ReceivedInitialTask.set(true); System.out.println("APP1 filtered initial TaskEvent"); @@ -360,7 +360,7 @@ public void testMultiInstanceEventReplication() throws Exception { } System.out.println("APP2 received event: " + eventDetail); - // Per A2A spec 3.1.6: Handle initial TaskEvent on resubscribe + // Per A2A spec 3.1.6: Handle initial TaskEvent on subscribe if (!app2ReceivedInitialTask.get() && event instanceof io.a2a.client.TaskEvent) { app2ReceivedInitialTask.set(true); System.out.println("APP2 filtered initial TaskEvent"); @@ -378,9 +378,9 @@ public void testMultiInstanceEventReplication() throws Exception { } }; - // Start subscriptions (resubscribe returns void) - getClient1().resubscribe(new TaskIdParams(taskId), List.of(app1Subscriber), app1ErrorHandler); - getClient2().resubscribe(new TaskIdParams(taskId), List.of(app2Subscriber), app2ErrorHandler); + // Start subscriptions (subscribe returns void) + getClient1().subscribeToTask(new TaskIdParams(taskId), List.of(app1Subscriber), app1ErrorHandler); + getClient2().subscribeToTask(new TaskIdParams(taskId), List.of(app2Subscriber), app2ErrorHandler); // Wait for subscriptions to be established - at least one event should arrive on each await() diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java index d38f8244c..26fd1bb2d 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/KafkaReplicationIntegrationTest.java @@ -227,13 +227,13 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { Thread.sleep(1000); // Set up resubscription to listen for task updates using streaming client - CountDownLatch resubscribeLatch = new CountDownLatch(1); + CountDownLatch subscribeLatch = new CountDownLatch(1); AtomicReference receivedCompletedEvent = new AtomicReference<>(); AtomicBoolean wasUnexpectedEvent = new AtomicBoolean(false); AtomicReference errorRef = new AtomicReference<>(); AtomicBoolean receivedInitialTask = new AtomicBoolean(false); - // Create consumer to handle resubscribed events + // Create consumer to handle subscribed events BiConsumer consumer = (event, agentCard) -> { // Per A2A spec 3.1.6: ENFORCE that first event is TaskEvent if (!receivedInitialTask.get()) { @@ -241,7 +241,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { receivedInitialTask.set(true); return; } else { - throw new AssertionError("First event on resubscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); + throw new AssertionError("First event on subscribe MUST be TaskEvent, but was: " + event.getClass().getSimpleName()); } } @@ -250,7 +250,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { if (taskUpdateEvent.getUpdateEvent() instanceof TaskStatusUpdateEvent statusEvent) { if (statusEvent.status().state() == TaskState.COMPLETED) { receivedCompletedEvent.set(statusEvent); - resubscribeLatch.countDown(); + subscribeLatch.countDown(); } } else { wasUnexpectedEvent.set(true); @@ -267,18 +267,17 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { if (!isStreamClosedError(error)) { errorRef.set(error); } - resubscribeLatch.countDown(); + subscribeLatch.countDown(); }; - // Resubscribe to the task to listen for updates - streamingClient.resubscribe(new TaskIdParams(taskId), List.of(consumer), errorHandler); + // Subscribe to the task to listen for updates + streamingClient.subscribeToTask(new TaskIdParams(taskId), List.of(consumer), errorHandler); // Now manually send a TaskStatusUpdateEvent to Kafka using reactive messaging TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder() .taskId(taskId) .contextId(contextId) .status(new TaskStatus(TaskState.COMPLETED)) - .isFinal(true) .build(); ReplicatedEventQueueItem replicatedEvent = new ReplicatedEventQueueItem(taskId, statusEvent); @@ -289,7 +288,7 @@ public void testKafkaEventReceivedByA2AServer() throws Exception { // Wait for the replicated event to be received via streaming resubscription // This tests the full round-trip: Manual Kafka Event -> A2A System -> Streaming Client - assertTrue(resubscribeLatch.await(15, TimeUnit.SECONDS), "Should receive COMPLETED event via resubscription"); + assertTrue(subscribeLatch.await(15, TimeUnit.SECONDS), "Should receive COMPLETED event via resubscription"); // Verify no unexpected events or errors assertFalse(wasUnexpectedEvent.get(), "Should not receive unexpected events"); @@ -373,8 +372,8 @@ public void testQueueClosedEventTerminatesRemoteSubscribers() throws Exception { streamCompletedLatch.countDown(); }; - // Resubscribe to the task - this creates a streaming subscription - streamingClient.resubscribe(new TaskIdParams(taskId), List.of(consumer), errorHandler); + // Subscribe to the task - this creates a streaming subscription + streamingClient.subscribeToTask(new TaskIdParams(taskId), List.of(consumer), errorHandler); // Wait for the EventConsumer to start polling (replaces unreliable Thread.sleep) // This ensures the consumer is ready to receive the QueueClosedEvent diff --git a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java index 9b5bea30f..3461ab7d4 100644 --- a/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java +++ b/extras/queue-manager-replicated/tests-single-instance/src/test/java/io/a2a/extras/queuemanager/replicated/tests/ReplicationTestAgentCardProducer.java @@ -1,6 +1,5 @@ package io.a2a.extras.queuemanager.replicated.tests; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import java.util.List; @@ -38,7 +37,6 @@ public AgentCard agentCard() { .skills(List.of()) .supportedInterfaces(List.of( new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:8081"))) - .protocolVersions(CURRENT_PROTOCOL_VERSION) .build(); } } \ No newline at end of file diff --git a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java index eff6d55e5..89d146787 100644 --- a/http-client/src/test/java/io/a2a/client/http/JsonMessages.java +++ b/http-client/src/test/java/io/a2a/client/http/JsonMessages.java @@ -8,7 +8,6 @@ public class JsonMessages { static final String AGENT_CARD = """ { - "protocolVersions": ["0.2.9"], "name": "GeoSpatial Route Planner Agent", "description": "Provides advanced route planning, traffic analysis, and custom map generation services. This agent can calculate optimal routes, estimate travel times considering real-time traffic, and create personalized maps with points of interest.", "supportedInterfaces" : [ @@ -34,7 +33,7 @@ public class JsonMessages { } } }, - "security": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], + "securityRequirements": [{ "schemes": { "google": { "list": ["openid", "profile", "email"] } } }], "defaultInputModes": ["application/json", "text/plain"], "defaultOutputModes": ["application/json", "image/png"], "skills": [ diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java index c0aea8ad2..77452f93e 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/json/JsonUtil.java @@ -567,9 +567,9 @@ public void write(JsonWriter out, Part value) throws java.io.IOException { out.name(FILE); delegateGson.toJson(filePart.file(), FileContent.class, out); } else if (value instanceof DataPart dataPart) { - // DataPart: { "data": {...} } + // DataPart: { "data": } out.name(DATA); - delegateGson.toJson(dataPart.data(), Map.class, out); + delegateGson.toJson(dataPart.data(), Object.class, out); } else { throw new JsonSyntaxException("Unknown Part subclass: " + value.getClass().getName()); } @@ -605,12 +605,12 @@ Part read(JsonReader in) throws java.io.IOException { case TEXT -> new TextPart(jsonObject.get(TEXT).getAsString()); case FILE -> new FilePart(delegateGson.fromJson(jsonObject.get(FILE), FileContent.class)); case DATA -> { - @SuppressWarnings("unchecked") - Map dataMap = delegateGson.fromJson( + // DataPart supports any JSON value: object, array, primitive, or null + Object data = delegateGson.fromJson( jsonObject.get(DATA), - new TypeToken>(){}.getType() + Object.class ); - yield new DataPart(dataMap); + yield new DataPart(data); } default -> throw new JsonSyntaxException(format("Part must have one of: %s (found: %s)", VALID_KEYS, discriminator)); diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java index cbf33a72d..c55200847 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/StreamingJSONRPCRequest.java @@ -13,7 +13,7 @@ * The A2A Protocol defines two primary streaming operations: *
      *
    • {@link SendStreamingMessageRequest} - Stream task execution events in real-time
    • - *
    • {@link SubscribeToTaskRequest} - Resubscribe to events from an existing task
    • + *
    • {@link SubscribeToTaskRequest} - Subscribe to events from an existing task
    • *
    *

    * Streaming requests follow the JSON-RPC 2.0 specification structure but the response diff --git a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java index 27acb3a14..1c965d5b1 100644 --- a/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java +++ b/jsonrpc-common/src/main/java/io/a2a/jsonrpc/common/wrappers/SubscribeToTaskRequest.java @@ -8,7 +8,7 @@ import io.a2a.spec.TaskIdParams; /** - * JSON-RPC request to resubscribe to an ongoing or completed task's event stream. + * JSON-RPC request to subscribe to an ongoing or completed task's event stream. *

    * This request allows clients to reconnect to a task and receive its events, enabling * recovery from disconnections or retrieval of missed updates. The agent will stream diff --git a/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java index 08eea2835..e7dcdc670 100644 --- a/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java +++ b/jsonrpc-common/src/test/java/io/a2a/jsonrpc/common/json/StreamingEventKindSerializationTest.java @@ -97,7 +97,6 @@ void testTaskStatusUpdateEventSerialization() throws JsonProcessingException { .taskId("task-abc") .contextId("context-def") .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .build(); // Serialize as StreamingEventKind diff --git a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java index b2da1d413..25b7cc708 100644 --- a/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java +++ b/reference/jsonrpc/src/test/java/io/a2a/server/apps/quarkus/A2AServerRoutesTest.java @@ -3,7 +3,6 @@ import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD; import static io.a2a.spec.A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD; import static io.a2a.spec.A2AMethods.SEND_STREAMING_MESSAGE_METHOD; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.METHOD_NAME_KEY; import static io.a2a.transport.jsonrpc.context.JSONRPCContextKeys.TENANT_KEY; import static java.util.Collections.singletonList; @@ -226,7 +225,7 @@ public void testGetTask_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "GetTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength": 10 } }"""; @@ -263,7 +262,7 @@ public void testCancelTask_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "CancelTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64" } }"""; when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); @@ -299,7 +298,7 @@ public void testTaskResubscription_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "SubscribeToTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64" } }"""; when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); @@ -331,16 +330,13 @@ public void testCreateTaskPushNotificationConfig_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "CreateTaskPushNotificationConfig", "params": { - "parent": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", "configId": "config-123", "config": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/config-123", - "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { - "schemes": ["jwt"] + "scheme": "jwt" } - } } } }"""; @@ -352,7 +348,7 @@ public void testCreateTaskPushNotificationConfig_MethodNameSetInContext() { PushNotificationConfig.builder() .id("config-123") .url("https://example.com/callback") - .authentication(new AuthenticationInfo(Collections.singletonList("jwt"), null)) + .authentication(new AuthenticationInfo("jwt", null)) .build(), "tenant"); @@ -382,7 +378,8 @@ public void testGetTaskPushNotificationConfig_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "GetTaskPushNotificationConfig", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/config-456" + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "config-456" } }"""; when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); @@ -422,7 +419,9 @@ public void testListTaskPushNotificationConfig_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "ListTaskPushNotificationConfig", "params": { - "parent": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64" + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "pageSize": 0, + "pageToken": "" } }"""; when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); @@ -462,7 +461,8 @@ public void testDeleteTaskPushNotificationConfig_MethodNameSetInContext() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "DeleteTaskPushNotificationConfig", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64/pushNotificationConfigs/config-456" + "taskId": "de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "config-456" } }"""; when(mockRequestBody.asString()).thenReturn(jsonRpcRequest); @@ -497,7 +497,6 @@ public void testGetExtendedCard_MethodNameSetInContext() { .name("Test Agent") .description("Test agent description") .version("1.0.0") - .protocolVersions(CURRENT_PROTOCOL_VERSION) .capabilities(AgentCapabilities.builder().build()) .defaultInputModes(Collections.singletonList("text")) .defaultOutputModes(Collections.singletonList("text")) @@ -532,7 +531,7 @@ public void testTenantExtraction_MultiSegmentPath() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "GetTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength": 10 } }"""; @@ -569,7 +568,7 @@ public void testTenantExtraction_RootPath() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "GetTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength": 10 } }"""; @@ -606,7 +605,7 @@ public void testTenantExtraction_SingleSegmentPath() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "GetTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength": 10 } }"""; @@ -643,7 +642,7 @@ public void testTenantExtraction_ThreeSegmentPath() { "id": "cd4c76de-d54c-436c-8b9f-4c2703648d64", "method": "GetTask", "params": { - "name": "tasks/de38c76d-d54c-436c-8b9f-4c2703648d64", + "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength": 10 } }"""; diff --git a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java index 9ef8f7c47..a8ce70f7f 100644 --- a/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java +++ b/reference/rest/src/test/java/io/a2a/server/rest/quarkus/A2AServerRoutesTest.java @@ -175,7 +175,7 @@ public void testCancelTask_MethodNameSetInContext() { } @Test - public void testResubscribeTask_MethodNameSetInContext() { + public void testSubscribeTask_MethodNameSetInContext() { // Arrange when(mockRoutingContext.pathParam("taskId")).thenReturn("task123"); HTTPRestResponse mockHttpResponse = mock(HTTPRestResponse.class); diff --git a/server-common/src/main/java/io/a2a/server/events/EventQueue.java b/server-common/src/main/java/io/a2a/server/events/EventQueue.java index 122d4b0f8..4899a926d 100644 --- a/server-common/src/main/java/io/a2a/server/events/EventQueue.java +++ b/server-common/src/main/java/io/a2a/server/events/EventQueue.java @@ -224,7 +224,7 @@ public void enqueueEvent(Event event) { * Enqueues an event directly to this specific queue only, bypassing the MainEventBus. *

    * This method is used for enqueuing already-persisted events (e.g., current task state - * on resubscribe) that should only be sent to this specific subscriber, not distributed + * on subscribe) that should only be sent to this specific subscriber, not distributed * to all children or sent through MainEventBusProcessor. *

    *

    @@ -236,7 +236,7 @@ public void enqueueEvent(Event event) { */ public void enqueueLocalOnly(EventQueueItem item) { throw new UnsupportedOperationException( - "enqueueLocalOnly is only supported on ChildQueue for resubscribe scenarios"); + "enqueueLocalOnly is only supported on ChildQueue for subscribe scenarios"); } /** @@ -504,7 +504,7 @@ void childClosing(ChildQueue child, boolean immediate) { boolean isFinalized = taskStateProvider.isTaskFinalized(taskId); if (!isFinalized) { LOGGER.debug("MainQueue for task {} has no children, but task is not finalized - keeping queue open for potential resubscriptions", taskId); - return; // Don't close - keep queue open for fire-and-forget or late resubscribes + return; // Don't close - keep queue open for fire-and-forget or late subscribes } LOGGER.debug("MainQueue for task {} has no children and task is finalized - closing queue", taskId); } else { diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java index e1a16eb3c..31a1a6670 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/DefaultRequestHandler.java @@ -14,7 +14,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.TimeoutException; @@ -36,7 +35,6 @@ import io.a2a.server.events.EventQueue; import io.a2a.server.events.EventQueueItem; import io.a2a.server.events.MainEventBusProcessor; -import io.a2a.server.events.MainEventBusProcessorCallback; import io.a2a.server.events.QueueManager; import io.a2a.server.tasks.PushNotificationConfigStore; import io.a2a.server.tasks.PushNotificationSender; @@ -786,9 +784,9 @@ private PushNotificationConfig getPushNotificationConfig(ListTaskPushNotificatio } @Override - public Flow.Publisher onResubscribeToTask( + public Flow.Publisher onSubscribeToTask( TaskIdParams params, ServerCallContext context) throws A2AError { - LOGGER.debug("onResubscribeToTask - taskId: {}", params.id()); + LOGGER.debug("onSubscribeToTask - taskId: {}", params.id()); Task task = taskStore.get(params.id()); if (task == null) { throw new TaskNotFoundError(); @@ -797,7 +795,7 @@ public Flow.Publisher onResubscribeToTask( TaskManager taskManager = new TaskManager(task.id(), task.contextId(), taskStore, null); ResultAggregator resultAggregator = new ResultAggregator(taskManager, null, executor, eventConsumerExecutor); EventQueue queue = queueManager.tap(task.id()); - LOGGER.debug("onResubscribeToTask - tapped queue: {}", queue != null ? System.identityHashCode(queue) : "null"); + LOGGER.debug("onSubscribeToTask - tapped queue: {}", queue != null ? System.identityHashCode(queue) : "null"); if (queue == null) { // If task is in final state, queue legitimately doesn't exist anymore @@ -815,11 +813,11 @@ public Flow.Publisher onResubscribeToTask( // representing the current state of the task at the time of subscription." // Enqueue the current task state directly to this ChildQueue only (already persisted, no need for MainEventBus) queue.enqueueEventLocalOnly(task); - LOGGER.debug("onResubscribeToTask - enqueued current task state as first event for taskId: {}", params.id()); + LOGGER.debug("onSubscribeToTask - enqueued current task state as first event for taskId: {}", params.id()); EventConsumer consumer = new EventConsumer(queue); Flow.Publisher results = resultAggregator.consumeAndEmit(consumer); - LOGGER.debug("onResubscribeToTask - returning publisher for taskId: {}", params.id()); + LOGGER.debug("onSubscribeToTask - returning publisher for taskId: {}", params.id()); return convertingProcessor(results, item -> (StreamingEventKind) item.getEvent()); } diff --git a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java index 639098967..ff6139654 100644 --- a/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java +++ b/server-common/src/main/java/io/a2a/server/requesthandlers/RequestHandler.java @@ -47,7 +47,7 @@ TaskPushNotificationConfig onGetTaskPushNotificationConfig( GetTaskPushNotificationConfigParams params, ServerCallContext context) throws A2AError; - Flow.Publisher onResubscribeToTask( + Flow.Publisher onSubscribeToTask( TaskIdParams params, ServerCallContext context) throws A2AError; diff --git a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java index 022cde56b..c28e55c5c 100644 --- a/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java +++ b/server-common/src/main/java/io/a2a/server/tasks/TaskUpdater.java @@ -52,7 +52,6 @@ public void updateStatus(TaskState state, @Nullable Message message, boolean isF TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder() .taskId(taskId) .contextId(contextId) - .isFinal(isFinal) .status(new TaskStatus(state, message, null)) .build(); eventQueue.enqueueEvent(event); diff --git a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java index 633b38ced..df49a7c8a 100644 --- a/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java +++ b/server-common/src/main/java/io/a2a/server/util/ArtifactUtils.java @@ -78,13 +78,15 @@ public static Artifact newTextArtifact(String name, String text) { /** * Creates a new Artifact object containing only a single DataPart. + *

    + * Supports any JSON value type: objects (Map), arrays (List), primitives (String, Number, Boolean), or null. * * @param name The human-readable name of the artifact. - * @param data The structured data content of the artifact. + * @param data The structured data content of the artifact (JSON object, array, primitive, or null). * @param description An optional description of the artifact. * @return A new {@code Artifact} object with a generated artifact_id. */ - public static Artifact newDataArtifact(String name, Map data, @Nullable String description) { + public static Artifact newDataArtifact(String name, Object data, @Nullable String description) { return newArtifact( name, List.of(new DataPart(data)), @@ -94,12 +96,14 @@ public static Artifact newDataArtifact(String name, Map data, @N /** * Creates a new Artifact object containing only a single DataPart with empty description. + *

    + * Supports any JSON value type: objects (Map), arrays (List), primitives (String, Number, Boolean), or null. * * @param name The human-readable name of the artifact. - * @param data The structured data content of the artifact. + * @param data The structured data content of the artifact (JSON object, array, primitive, or null). * @return A new {@code Artifact} object with a generated artifact_id. */ - public static Artifact newDataArtifact(String name, Map data) { + public static Artifact newDataArtifact(String name, Object data) { return newDataArtifact(name, data, null); } } diff --git a/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java b/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java index 91159428a..da667e538 100644 --- a/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java +++ b/server-common/src/main/java/io/a2a/server/version/A2AVersionValidator.java @@ -1,9 +1,11 @@ package io.a2a.server.version; import java.util.List; +import java.util.stream.Collectors; import io.a2a.server.ServerCallContext; import io.a2a.spec.AgentCard; +import io.a2a.spec.AgentInterface; import io.a2a.spec.VersionNotSupportedError; /** @@ -16,15 +18,15 @@ * * *

    If the client does not specify a version, the current protocol version - * ({@link AgentCard#CURRENT_PROTOCOL_VERSION}) is assumed as the default. + * ({@link AgentInterface#CURRENT_PROTOCOL_VERSION}) is assumed as the default. */ public class A2AVersionValidator { /** * Validates that the client's requested protocol version is compatible with the agent's - * supported version. + * supported versions across all interfaces. * - * @param agentCard the agent card containing the supported protocol version + * @param agentCard the agent card containing the supported interfaces with their protocol versions * @param context the server call context containing the requested protocol version * @throws VersionNotSupportedError if the versions are incompatible */ @@ -34,10 +36,14 @@ public static void validateProtocolVersion(AgentCard agentCard, ServerCallContex // If client didn't specify a version, default to current version if (requestedVersion == null || requestedVersion.trim().isEmpty()) { - requestedVersion = AgentCard.CURRENT_PROTOCOL_VERSION; + requestedVersion = AgentInterface.CURRENT_PROTOCOL_VERSION; } - List supportedVersions = agentCard.protocolVersions(); + // Collect all unique protocol versions from all supported interfaces + List supportedVersions = agentCard.supportedInterfaces().stream() + .map(AgentInterface::protocolVersion) + .distinct() + .collect(Collectors.toList()); if (!isVersionCompatible(supportedVersions, requestedVersion)) { throw new VersionNotSupportedError( diff --git a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java index 146bfb10a..2dcf7c4b1 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventConsumerTest.java @@ -158,8 +158,7 @@ public void testConsumeAllMultipleEvents() throws JsonProcessingException { TaskStatusUpdateEvent.builder() .taskId(TASK_ID) .contextId("session-xyz") - .status(new TaskStatus(TaskState.WORKING)) - .isFinal(true) + .status(new TaskStatus(TaskState.COMPLETED)) .build()); for (Event event : events) { @@ -194,8 +193,7 @@ public void testConsumeUntilMessage() throws Exception { TaskStatusUpdateEvent.builder() .taskId(TASK_ID) .contextId("session-xyz") - .status(new TaskStatus(TaskState.WORKING)) - .isFinal(true) + .status(new TaskStatus(TaskState.COMPLETED)) .build()); for (Event event : events) { @@ -258,8 +256,7 @@ public void testConsumeTaskInputRequired() { TaskStatusUpdateEvent.builder() .taskId(TASK_ID) .contextId("session-xyz") - .status(new TaskStatus(TaskState.WORKING)) - .isFinal(true) + .status(new TaskStatus(TaskState.COMPLETED)) .build()); for (Event event : events) { eventQueue.enqueueEvent(event); diff --git a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java index 2499a8173..1adcc57ca 100644 --- a/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java +++ b/server-common/src/test/java/io/a2a/server/events/EventQueueTest.java @@ -352,7 +352,6 @@ public void testDequeueEventWait() throws Exception { .taskId(TASK_ID) .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) - .isFinal(true) .build(); eventQueue.enqueueEvent(event); diff --git a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java index 274453fd1..968937b89 100644 --- a/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java +++ b/server-common/src/test/java/io/a2a/server/requesthandlers/AbstractA2ARequestHandlerTest.java @@ -1,6 +1,5 @@ package io.a2a.server.requesthandlers; -import static io.a2a.spec.AgentCard.CURRENT_PROTOCOL_VERSION; import java.io.IOException; import java.io.InputStream; @@ -144,8 +143,7 @@ protected static AgentCard createAgentCard(boolean streaming, boolean pushNotifi .build()) .defaultInputModes(new ArrayList<>()) .defaultOutputModes(new ArrayList<>()) - .skills(new ArrayList<>()) - .protocolVersions(CURRENT_PROTOCOL_VERSION); + .skills(new ArrayList<>()); return builder.build(); } diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java index ca4aa07b1..c058ee22e 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskManagerTest.java @@ -141,7 +141,6 @@ public void testEnsureTaskNonExistentForStatusUpdate() throws A2AServerException .taskId("new-task") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); taskManagerWithoutId.saveTaskEvent(event, false); @@ -353,7 +352,6 @@ public void testAddingTaskWithDifferentIdViaStatusUpdateFails() { .taskId("different-task-id") .contextId("session-xyz") .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .build(); assertThrows(A2AServerException.class, () -> { @@ -399,7 +397,6 @@ public void testTaskWithNoMessageUsesInitialMessage() throws A2AServerException .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); taskManagerWithInitialMessage.saveTaskEvent(event, false); @@ -437,7 +434,6 @@ public void testTaskWithMessageDoesNotUseInitialMessage() throws A2AServerExcept .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED, taskMessage, null)) - .isFinal(false) .build(); taskManagerWithInitialMessage.saveTaskEvent(event, false); @@ -566,7 +562,6 @@ public void testSaveTaskEventMetadataUpdate() throws A2AServerException { .taskId(minimalTask.id()) .contextId(minimalTask.contextId()) .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .metadata(newMetadata) .build(); @@ -586,7 +581,6 @@ public void testSaveTaskEventMetadataUpdateNull() throws A2AServerException { .taskId(minimalTask.id()) .contextId(minimalTask.contextId()) .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .metadata(null) .build(); @@ -615,7 +609,6 @@ public void testSaveTaskEventMetadataMergeExisting() throws A2AServerException { .taskId(minimalTask.id()) .contextId(minimalTask.contextId()) .status(new TaskStatus(TaskState.WORKING)) - .isFinal(false) .metadata(newMetadata) .build(); @@ -643,7 +636,6 @@ public void testCreateTaskWithInitialMessage() throws A2AServerException { .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); taskManagerWithMessage.saveTaskEvent(event, false); @@ -672,7 +664,6 @@ public void testCreateTaskWithoutInitialMessage() throws A2AServerException { .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED)) - .isFinal(false) .build(); taskManagerWithoutMessage.saveTaskEvent(event, false); @@ -728,7 +719,6 @@ public void testUpdateWithMessage() throws A2AServerException { .taskId("new-task-id") .contextId("some-context") .status(new TaskStatus(TaskState.SUBMITTED, taskMessage, null)) - .isFinal(false) .build(); taskManagerWithInitialMessage.saveTaskEvent(event, false); diff --git a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java index 73da17824..a5ec77f01 100644 --- a/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java +++ b/server-common/src/test/java/io/a2a/server/tasks/TaskUpdaterTest.java @@ -186,13 +186,13 @@ public void testRequiresInputWithMessage() throws Exception { @Test public void testRequiresInputWithFinalTrue() throws Exception { taskUpdater.requiresInput(true); - checkTaskStatusUpdateEventOnQueue(true, TaskState.INPUT_REQUIRED, null); + checkTaskStatusUpdateEventOnQueue(false, TaskState.INPUT_REQUIRED, null); } @Test public void testRequiresInputWithMessageAndFinalTrue() throws Exception { taskUpdater.requiresInput(SAMPLE_MESSAGE, true); - checkTaskStatusUpdateEventOnQueue(true, TaskState.INPUT_REQUIRED, SAMPLE_MESSAGE); + checkTaskStatusUpdateEventOnQueue(false, TaskState.INPUT_REQUIRED, SAMPLE_MESSAGE); } @Test @@ -210,13 +210,13 @@ public void testRequiresAuthWithMessage() throws Exception { @Test public void testRequiresAuthWithFinalTrue() throws Exception { taskUpdater.requiresAuth(true); - checkTaskStatusUpdateEventOnQueue(true, TaskState.AUTH_REQUIRED, null); + checkTaskStatusUpdateEventOnQueue(false, TaskState.AUTH_REQUIRED, null); } @Test public void testRequiresAuthWithMessageAndFinalTrue() throws Exception { taskUpdater.requiresAuth(SAMPLE_MESSAGE, true); - checkTaskStatusUpdateEventOnQueue(true, TaskState.AUTH_REQUIRED, SAMPLE_MESSAGE); + checkTaskStatusUpdateEventOnQueue(false, TaskState.AUTH_REQUIRED, SAMPLE_MESSAGE); } @Test diff --git a/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java b/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java index 43e0f55dd..8156fad4c 100644 --- a/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java +++ b/server-common/src/test/java/io/a2a/server/version/A2AVersionValidatorTest.java @@ -144,7 +144,7 @@ private AgentCard createAgentCard(String protocolVersion) { return AgentCard.builder() .name("test-card") .description("Test card") - .supportedInterfaces(List.of(new AgentInterface("GRPC", "http://localhost:9999"))) + .supportedInterfaces(List.of(new AgentInterface("GRPC", "http://localhost:9999", "", protocolVersion))) .version("1.0.0") .capabilities(AgentCapabilities.builder() .streaming(false) @@ -153,7 +153,6 @@ private AgentCard createAgentCard(String protocolVersion) { .defaultInputModes(List.of("text")) .defaultOutputModes(List.of("text")) .skills(Collections.emptyList()) - .protocolVersions(protocolVersion) .build(); } diff --git a/spec-grpc/pom.xml b/spec-grpc/pom.xml index b602f7613..3b2f28c0d 100644 --- a/spec-grpc/pom.xml +++ b/spec-grpc/pom.xml @@ -96,7 +96,7 @@ io.github.ascopes protobuf-maven-plugin - 4.0.3 + 4.1.2 ${skip.protobuf.generate} true diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java index 6dc06f861..7b1286314 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2A.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2A.java @@ -46,16 +46,6 @@ public static void registerAllExtensions( static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_Part_fieldAccessorTable; - static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_FilePart_descriptor; - static final - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_FilePart_fieldAccessorTable; - static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_DataPart_descriptor; - static final - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_DataPart_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_Message_descriptor; static final @@ -137,15 +127,15 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_StringList_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_Security_descriptor; + internal_static_a2a_v1_SecurityRequirement_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_Security_fieldAccessorTable; + internal_static_a2a_v1_SecurityRequirement_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor - internal_static_a2a_v1_Security_SchemesEntry_descriptor; + internal_static_a2a_v1_SecurityRequirement_SchemesEntry_descriptor; static final com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_a2a_v1_Security_SchemesEntry_fieldAccessorTable; + internal_static_a2a_v1_SecurityRequirement_SchemesEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_SecurityScheme_descriptor; static final @@ -201,6 +191,26 @@ public static void registerAllExtensions( static final com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor; + static final + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor; static final @@ -308,224 +318,237 @@ public static void registerAllExtensions( "\n\nTaskStatus\022%\n\005state\030\001 \001(\0162\021.a2a.v1.Tas" + "kStateB\003\340A\002\022 \n\007message\030\002 \001(\0132\017.a2a.v1.Me" + "ssage\022-\n\ttimestamp\030\003 \001(\0132\032.google.protob" + - "uf.Timestamp\"\215\001\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022 \n" + - "\004file\030\002 \001(\0132\020.a2a.v1.FilePartH\000\022 \n\004data\030" + - "\003 \001(\0132\020.a2a.v1.DataPartH\000\022)\n\010metadata\030\004 " + - "\001(\0132\027.google.protobuf.StructB\006\n\004part\"h\n\010" + - "FilePart\022\027\n\rfile_with_uri\030\001 \001(\tH\000\022\031\n\017fil" + - "e_with_bytes\030\002 \001(\014H\000\022\022\n\nmedia_type\030\003 \001(\t" + - "\022\014\n\004name\030\004 \001(\tB\006\n\004file\"6\n\010DataPart\022*\n\004da" + - "ta\030\001 \001(\0132\027.google.protobuf.StructB\003\340A\002\"\345" + - "\001\n\007Message\022\027\n\nmessage_id\030\001 \001(\tB\003\340A\002\022\022\n\nc" + - "ontext_id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t\022\037\n\004role" + - "\030\004 \001(\0162\014.a2a.v1.RoleB\003\340A\002\022 \n\005parts\030\005 \003(\013" + + "uf.Timestamp\"\270\001\n\004Part\022\016\n\004text\030\001 \001(\tH\000\022\r\n" + + "\003raw\030\002 \001(\014H\000\022\r\n\003url\030\003 \001(\tH\000\022&\n\004data\030\004 \001(" + + "\0132\026.google.protobuf.ValueH\000\022)\n\010metadata\030" + + "\005 \001(\0132\027.google.protobuf.Struct\022\020\n\010filena" + + "me\030\006 \001(\t\022\022\n\nmedia_type\030\007 \001(\tB\t\n\007content\"" + + "\345\001\n\007Message\022\027\n\nmessage_id\030\001 \001(\tB\003\340A\002\022\022\n\n" + + "context_id\030\002 \001(\t\022\017\n\007task_id\030\003 \001(\t\022\037\n\004rol" + + "e\030\004 \001(\0162\014.a2a.v1.RoleB\003\340A\002\022 \n\005parts\030\005 \003(" + + "\0132\014.a2a.v1.PartB\003\340A\002\022)\n\010metadata\030\006 \001(\0132\027" + + ".google.protobuf.Struct\022\022\n\nextensions\030\007 " + + "\003(\t\022\032\n\022reference_task_ids\030\010 \003(\t\"\250\001\n\010Arti" + + "fact\022\030\n\013artifact_id\030\001 \001(\tB\003\340A\002\022\014\n\004name\030\003" + + " \001(\t\022\023\n\013description\030\004 \001(\t\022 \n\005parts\030\005 \003(\013" + "2\014.a2a.v1.PartB\003\340A\002\022)\n\010metadata\030\006 \001(\0132\027." + "google.protobuf.Struct\022\022\n\nextensions\030\007 \003" + - "(\t\022\032\n\022reference_task_ids\030\010 \003(\t\"\250\001\n\010Artif" + - "act\022\030\n\013artifact_id\030\001 \001(\tB\003\340A\002\022\014\n\004name\030\003 " + - "\001(\t\022\023\n\013description\030\004 \001(\t\022 \n\005parts\030\005 \003(\0132" + - "\014.a2a.v1.PartB\003\340A\002\022)\n\010metadata\030\006 \001(\0132\027.g" + - "oogle.protobuf.Struct\022\022\n\nextensions\030\007 \003(" + - "\t\"\256\001\n\025TaskStatusUpdateEvent\022\024\n\007task_id\030\001" + - " \001(\tB\003\340A\002\022\027\n\ncontext_id\030\002 \001(\tB\003\340A\002\022\'\n\006st" + - "atus\030\003 \001(\0132\022.a2a.v1.TaskStatusB\003\340A\002\022\022\n\005f" + - "inal\030\004 \001(\010B\003\340A\002\022)\n\010metadata\030\005 \001(\0132\027.goog" + - "le.protobuf.Struct\"\300\001\n\027TaskArtifactUpdat" + - "eEvent\022\024\n\007task_id\030\001 \001(\tB\003\340A\002\022\027\n\ncontext_" + - "id\030\002 \001(\tB\003\340A\002\022\'\n\010artifact\030\003 \001(\0132\020.a2a.v1" + - ".ArtifactB\003\340A\002\022\016\n\006append\030\004 \001(\010\022\022\n\nlast_c" + - "hunk\030\005 \001(\010\022)\n\010metadata\030\006 \001(\0132\027.google.pr" + - "otobuf.Struct\"y\n\026PushNotificationConfig\022" + - "\n\n\002id\030\001 \001(\t\022\020\n\003url\030\002 \001(\tB\003\340A\002\022\r\n\005token\030\003" + - " \001(\t\0222\n\016authentication\030\004 \001(\0132\032.a2a.v1.Au" + - "thenticationInfo\"?\n\022AuthenticationInfo\022\024" + - "\n\007schemes\030\001 \003(\tB\003\340A\002\022\023\n\013credentials\030\002 \001(" + - "\t\"Q\n\016AgentInterface\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\035\n" + - "\020protocol_binding\030\002 \001(\tB\003\340A\002\022\016\n\006tenant\030\003" + - " \001(\t\"\310\005\n\tAgentCard\022\036\n\021protocol_versions\030" + - "\020 \003(\tB\003\340A\002\022\021\n\004name\030\001 \001(\tB\003\340A\002\022\030\n\013descrip" + - "tion\030\002 \001(\tB\003\340A\002\0229\n\024supported_interfaces\030" + - "\023 \003(\0132\026.a2a.v1.AgentInterfaceB\003\340A\002\022\'\n\010pr" + - "ovider\030\004 \001(\0132\025.a2a.v1.AgentProvider\022\024\n\007v" + - "ersion\030\005 \001(\tB\003\340A\002\022\036\n\021documentation_url\030\006" + - " \001(\tH\000\210\001\001\0224\n\014capabilities\030\007 \001(\0132\031.a2a.v1" + - ".AgentCapabilitiesB\003\340A\002\022@\n\020security_sche" + - "mes\030\010 \003(\0132&.a2a.v1.AgentCard.SecuritySch" + - "emesEntry\022\"\n\010security\030\t \003(\0132\020.a2a.v1.Sec" + - "urity\022 \n\023default_input_modes\030\n \003(\tB\003\340A\002\022" + - "!\n\024default_output_modes\030\013 \003(\tB\003\340A\002\022\'\n\006sk" + - "ills\030\014 \003(\0132\022.a2a.v1.AgentSkillB\003\340A\002\022.\n\ns" + - "ignatures\030\021 \003(\0132\032.a2a.v1.AgentCardSignat" + - "ure\022\025\n\010icon_url\030\022 \001(\tH\001\210\001\001\032N\n\024SecuritySc" + - "hemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005value\030\002 \001(\0132\026" + - ".a2a.v1.SecurityScheme:\0028\001B\024\n\022_documenta" + - "tion_urlB\013\n\t_icon_urlJ\004\010\003\020\004J\004\010\016\020\017J\004\010\017\020\020\"" + - "<\n\rAgentProvider\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\031\n\014or" + - "ganization\030\002 \001(\tB\003\340A\002\"\335\001\n\021AgentCapabilit" + - "ies\022\026\n\tstreaming\030\001 \001(\010H\000\210\001\001\022\037\n\022push_noti" + - "fications\030\002 \001(\010H\001\210\001\001\022*\n\nextensions\030\003 \003(\013" + - "2\026.a2a.v1.AgentExtension\022 \n\023extended_age" + - "nt_card\030\005 \001(\010H\002\210\001\001B\014\n\n_streamingB\025\n\023_pus" + - "h_notificationsB\026\n\024_extended_agent_cardJ" + - "\004\010\004\020\005\"m\n\016AgentExtension\022\013\n\003uri\030\001 \001(\t\022\023\n\013" + - "description\030\002 \001(\t\022\020\n\010required\030\003 \001(\010\022\'\n\006p" + - "arams\030\004 \001(\0132\027.google.protobuf.Struct\"\276\001\n" + - "\nAgentSkill\022\017\n\002id\030\001 \001(\tB\003\340A\002\022\021\n\004name\030\002 \001" + - "(\tB\003\340A\002\022\030\n\013description\030\003 \001(\tB\003\340A\002\022\021\n\004tag" + - "s\030\004 \003(\tB\003\340A\002\022\020\n\010examples\030\005 \003(\t\022\023\n\013input_" + - "modes\030\006 \003(\t\022\024\n\014output_modes\030\007 \003(\t\022\"\n\010sec" + - "urity\030\010 \003(\0132\020.a2a.v1.Security\"m\n\022AgentCa" + - "rdSignature\022\026\n\tprotected\030\001 \001(\tB\003\340A\002\022\026\n\ts" + - "ignature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\0132\027.go" + - "ogle.protobuf.Struct\"v\n\032TaskPushNotifica" + - "tionConfig\022\021\n\004name\030\001 \001(\tB\003\340A\002\022E\n\030push_no" + - "tification_config\030\002 \001(\0132\036.a2a.v1.PushNot" + - "ificationConfigB\003\340A\002\"\032\n\nStringList\022\014\n\004li" + - "st\030\001 \003(\t\"~\n\010Security\022.\n\007schemes\030\001 \003(\0132\035." + - "a2a.v1.Security.SchemesEntry\032B\n\014SchemesE" + - "ntry\022\013\n\003key\030\001 \001(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v" + - "1.StringList:\0028\001\"\361\002\n\016SecurityScheme\022?\n\027a" + - "pi_key_security_scheme\030\001 \001(\0132\034.a2a.v1.AP" + - "IKeySecuritySchemeH\000\022C\n\031http_auth_securi" + - "ty_scheme\030\002 \001(\0132\036.a2a.v1.HTTPAuthSecurit" + - "ySchemeH\000\022>\n\026oauth2_security_scheme\030\003 \001(" + - "\0132\034.a2a.v1.OAuth2SecuritySchemeH\000\022N\n\037ope" + - "n_id_connect_security_scheme\030\004 \001(\0132#.a2a" + - ".v1.OpenIdConnectSecuritySchemeH\000\022?\n\024mtl" + - "s_security_scheme\030\005 \001(\0132\037.a2a.v1.MutualT" + - "lsSecuritySchemeH\000B\010\n\006scheme\"U\n\024APIKeySe" + - "curityScheme\022\023\n\013description\030\001 \001(\t\022\025\n\010loc" + - "ation\030\002 \001(\tB\003\340A\002\022\021\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026H" + - "TTPAuthSecurityScheme\022\023\n\013description\030\001 \001" + - "(\t\022\023\n\006scheme\030\002 \001(\tB\003\340A\002\022\025\n\rbearer_format" + - "\030\003 \001(\t\"p\n\024OAuth2SecurityScheme\022\023\n\013descri" + - "ption\030\001 \001(\t\022&\n\005flows\030\002 \001(\0132\022.a2a.v1.OAut" + - "hFlowsB\003\340A\002\022\033\n\023oauth2_metadata_url\030\003 \001(\t" + - "\"T\n\033OpenIdConnectSecurityScheme\022\023\n\013descr" + - "iption\030\001 \001(\t\022 \n\023open_id_connect_url\030\002 \001(" + - "\tB\003\340A\002\".\n\027MutualTlsSecurityScheme\022\023\n\013des" + - "cription\030\001 \001(\t\"\330\001\n\nOAuthFlows\022@\n\022authori" + - "zation_code\030\001 \001(\0132\".a2a.v1.Authorization" + - "CodeOAuthFlowH\000\022@\n\022client_credentials\030\002 " + - "\001(\0132\".a2a.v1.ClientCredentialsOAuthFlowH" + - "\000\0222\n\013device_code\030\005 \001(\0132\033.a2a.v1.DeviceCo" + - "deOAuthFlowH\000B\006\n\004flowJ\004\010\003\020\004J\004\010\004\020\005\"\364\001\n\032Au" + - "thorizationCodeOAuthFlow\022\036\n\021authorizatio" + - "n_url\030\001 \001(\tB\003\340A\002\022\026\n\ttoken_url\030\002 \001(\tB\003\340A\002" + - "\022\023\n\013refresh_url\030\003 \001(\t\022C\n\006scopes\030\004 \003(\0132.." + - "a2a.v1.AuthorizationCodeOAuthFlow.Scopes" + - "EntryB\003\340A\002\022\025\n\rpkce_required\030\005 \001(\010\032-\n\013Sco" + - "pesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001" + - "\"\275\001\n\032ClientCredentialsOAuthFlow\022\026\n\ttoken" + - "_url\030\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022C\n\006" + - "scopes\030\003 \003(\0132..a2a.v1.ClientCredentialsO" + - "AuthFlow.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry" + - "\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\326\001\n\023Dev" + - "iceCodeOAuthFlow\022%\n\030device_authorization" + - "_url\030\001 \001(\tB\003\340A\002\022\026\n\ttoken_url\030\002 \001(\tB\003\340A\002\022" + - "\023\n\013refresh_url\030\003 \001(\t\022<\n\006scopes\030\004 \003(\0132\'.a" + - "2a.v1.DeviceCodeOAuthFlow.ScopesEntryB\003\340" + - "A\002\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030" + - "\002 \001(\t:\0028\001\"\257\001\n\022SendMessageRequest\022\016\n\006tena" + - "nt\030\004 \001(\t\022%\n\007message\030\001 \001(\0132\017.a2a.v1.Messa" + - "geB\003\340A\002\0227\n\rconfiguration\030\002 \001(\0132 .a2a.v1." + - "SendMessageConfiguration\022)\n\010metadata\030\003 \001" + - "(\0132\027.google.protobuf.Struct\"c\n\016GetTaskRe" + - "quest\022\016\n\006tenant\030\003 \001(\t\022\021\n\004name\030\001 \001(\tB\003\340A\002" + - "\022\033\n\016history_length\030\002 \001(\005H\000\210\001\001B\021\n\017_histor" + - "y_length\"\265\002\n\020ListTasksRequest\022\016\n\006tenant\030" + - "\t \001(\t\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006status\030\002 \001(" + - "\0162\021.a2a.v1.TaskState\022\026\n\tpage_size\030\003 \001(\005H" + - "\000\210\001\001\022\022\n\npage_token\030\004 \001(\t\022\033\n\016history_leng" + - "th\030\005 \001(\005H\001\210\001\001\022:\n\026status_timestamp_after\030" + - "\006 \001(\0132\032.google.protobuf.Timestamp\022\036\n\021inc" + - "lude_artifacts\030\007 \001(\010H\002\210\001\001B\014\n\n_page_sizeB" + - "\021\n\017_history_lengthB\024\n\022_include_artifacts" + - "\"\204\001\n\021ListTasksResponse\022 \n\005tasks\030\001 \003(\0132\014." + - "a2a.v1.TaskB\003\340A\002\022\034\n\017next_page_token\030\002 \001(" + - "\tB\003\340A\002\022\026\n\tpage_size\030\003 \001(\005B\003\340A\002\022\027\n\ntotal_" + - "size\030\004 \001(\005B\003\340A\002\"1\n\021CancelTaskRequest\022\016\n\006" + - "tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"D\n$GetTaskPus" + - "hNotificationConfigRequest\022\016\n\006tenant\030\002 \001" + - "(\t\022\014\n\004name\030\001 \001(\t\"G\n\'DeleteTaskPushNotifi" + - "cationConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\014\n\004n" + - "ame\030\001 \001(\t\"\237\001\n\'CreateTaskPushNotification" + - "ConfigRequest\022\016\n\006tenant\030\004 \001(\t\022\023\n\006parent\030" + - "\001 \001(\tB\003\340A\002\022\026\n\tconfig_id\030\002 \001(\tB\003\340A\002\0227\n\006co" + - "nfig\030\003 \001(\0132\".a2a.v1.TaskPushNotification" + - "ConfigB\003\340A\002\"6\n\026SubscribeToTaskRequest\022\016\n" + - "\006tenant\030\002 \001(\t\022\014\n\004name\030\001 \001(\t\"n\n%ListTaskP" + - "ushNotificationConfigRequest\022\016\n\006tenant\030\004" + - " \001(\t\022\016\n\006parent\030\001 \001(\t\022\021\n\tpage_size\030\002 \001(\005\022" + - "\022\n\npage_token\030\003 \001(\t\"-\n\033GetExtendedAgentC" + - "ardRequest\022\016\n\006tenant\030\001 \001(\t\"b\n\023SendMessag" + - "eResponse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022" + - "\"\n\007message\030\002 \001(\0132\017.a2a.v1.MessageH\000B\t\n\007p" + - "ayload\"\321\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\0132" + - "\014.a2a.v1.TaskH\000\022\"\n\007message\030\002 \001(\0132\017.a2a.v" + - "1.MessageH\000\0226\n\rstatus_update\030\003 \001(\0132\035.a2a" + - ".v1.TaskStatusUpdateEventH\000\022:\n\017artifact_" + - "update\030\004 \001(\0132\037.a2a.v1.TaskArtifactUpdate" + - "EventH\000B\t\n\007payload\"v\n&ListTaskPushNotifi" + - "cationConfigResponse\0223\n\007configs\030\001 \003(\0132\"." + - "a2a.v1.TaskPushNotificationConfig\022\027\n\017nex" + - "t_page_token\030\002 \001(\t*\372\001\n\tTaskState\022\032\n\026TASK" + - "_STATE_UNSPECIFIED\020\000\022\030\n\024TASK_STATE_SUBMI" + - "TTED\020\001\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024TASK_S" + - "TATE_COMPLETED\020\003\022\025\n\021TASK_STATE_FAILED\020\004\022" + - "\030\n\024TASK_STATE_CANCELLED\020\005\022\035\n\031TASK_STATE_" + - "INPUT_REQUIRED\020\006\022\027\n\023TASK_STATE_REJECTED\020" + - "\007\022\034\n\030TASK_STATE_AUTH_REQUIRED\020\010*;\n\004Role\022" + - "\024\n\020ROLE_UNSPECIFIED\020\000\022\r\n\tROLE_USER\020\001\022\016\n\n" + - "ROLE_AGENT\020\0022\304\016\n\nA2AService\022}\n\013SendMessa" + - "ge\022\032.a2a.v1.SendMessageRequest\032\033.a2a.v1." + - "SendMessageResponse\"5\202\323\344\223\002/\"\r/message:se" + - "nd:\001*Z\033\"\026/{tenant}/message:send:\001*\022\207\001\n\024S" + - "endStreamingMessage\022\032.a2a.v1.SendMessage" + - "Request\032\026.a2a.v1.StreamResponse\"9\202\323\344\223\0023\"" + - "\017/message:stream:\001*Z\035\"\030/{tenant}/message" + - ":stream:\001*0\001\022k\n\007GetTask\022\026.a2a.v1.GetTask" + - "Request\032\014.a2a.v1.Task\":\332A\004name\202\323\344\223\002-\022\017/{" + - "name=tasks/*}Z\032\022\030/{tenant}/{name=tasks/*" + - "}\022c\n\tListTasks\022\030.a2a.v1.ListTasksRequest" + - "\032\031.a2a.v1.ListTasksResponse\"!\202\323\344\223\002\033\022\006/ta" + - "sksZ\021\022\017/{tenant}/tasks\022~\n\nCancelTask\022\031.a" + - "2a.v1.CancelTaskRequest\032\014.a2a.v1.Task\"G\202" + - "\323\344\223\002A\"\026/{name=tasks/*}:cancel:\001*Z$\"\037/{te" + - "nant}/{name=tasks/*}:cancel:\001*\022\224\001\n\017Subsc" + - "ribeToTask\022\036.a2a.v1.SubscribeToTaskReque" + - "st\032\026.a2a.v1.StreamResponse\"G\202\323\344\223\002A\022\031/{na" + - "me=tasks/*}:subscribeZ$\022\"/{tenant}/{name" + - "=tasks/*}:subscribe0\001\022\201\002\n CreateTaskPush" + - "NotificationConfig\022/.a2a.v1.CreateTaskPu" + - "shNotificationConfigRequest\032\".a2a.v1.Tas" + - "kPushNotificationConfig\"\207\001\332A\rparent,conf" + - "ig\202\323\344\223\002q\")/{parent=tasks/*/pushNotificat" + - "ionConfigs}:\006configZ<\"2/{tenant}/{parent" + - "=tasks/*/pushNotificationConfigs}:\006confi" + - "g\022\341\001\n\035GetTaskPushNotificationConfig\022,.a2" + - "a.v1.GetTaskPushNotificationConfigReques" + - "t\032\".a2a.v1.TaskPushNotificationConfig\"n\332" + - "A\004name\202\323\344\223\002a\022)/{name=tasks/*/pushNotific" + - "ationConfigs/*}Z4\0222/{tenant}/{name=tasks" + - "/*/pushNotificationConfigs/*}\022\361\001\n\036ListTa" + - "skPushNotificationConfig\022-.a2a.v1.ListTa" + - "skPushNotificationConfigRequest\032..a2a.v1" + - ".ListTaskPushNotificationConfigResponse\"" + - "p\332A\006parent\202\323\344\223\002a\022)/{parent=tasks/*}/push" + - "NotificationConfigsZ4\0222/{tenant}/{parent" + - "=tasks/*}/pushNotificationConfigs\022\211\001\n\024Ge" + - "tExtendedAgentCard\022#.a2a.v1.GetExtendedA" + - "gentCardRequest\032\021.a2a.v1.AgentCard\"9\202\323\344\223" + - "\0023\022\022/extendedAgentCardZ\035\022\033/{tenant}/exte" + - "ndedAgentCard\022\333\001\n DeleteTaskPushNotifica" + - "tionConfig\022/.a2a.v1.DeleteTaskPushNotifi" + - "cationConfigRequest\032\026.google.protobuf.Em" + - "pty\"n\332A\004name\202\323\344\223\002a*)/{name=tasks/*/pushN" + - "otificationConfigs/*}Z4*2/{tenant}/{name" + - "=tasks/*/pushNotificationConfigs/*}B7\n\013i" + - "o.a2a.grpcB\003A2AP\001Z\030google.golang.org/a2a" + - "/v1\252\002\006A2a.V1b\006proto3" + "(\t\"\240\001\n\025TaskStatusUpdateEvent\022\024\n\007task_id\030" + + "\001 \001(\tB\003\340A\002\022\027\n\ncontext_id\030\002 \001(\tB\003\340A\002\022\'\n\006s" + + "tatus\030\003 \001(\0132\022.a2a.v1.TaskStatusB\003\340A\002\022)\n\010" + + "metadata\030\005 \001(\0132\027.google.protobuf.StructJ" + + "\004\010\004\020\005\"\300\001\n\027TaskArtifactUpdateEvent\022\024\n\007tas" + + "k_id\030\001 \001(\tB\003\340A\002\022\027\n\ncontext_id\030\002 \001(\tB\003\340A\002" + + "\022\'\n\010artifact\030\003 \001(\0132\020.a2a.v1.ArtifactB\003\340A" + + "\002\022\016\n\006append\030\004 \001(\010\022\022\n\nlast_chunk\030\005 \001(\010\022)\n" + + "\010metadata\030\006 \001(\0132\027.google.protobuf.Struct" + + "\"y\n\026PushNotificationConfig\022\n\n\002id\030\001 \001(\t\022\020" + + "\n\003url\030\002 \001(\tB\003\340A\002\022\r\n\005token\030\003 \001(\t\0222\n\016authe" + + "ntication\030\004 \001(\0132\032.a2a.v1.AuthenticationI" + + "nfo\">\n\022AuthenticationInfo\022\023\n\006scheme\030\001 \001(" + + "\tB\003\340A\002\022\023\n\013credentials\030\002 \001(\t\"p\n\016AgentInte" + + "rface\022\020\n\003url\030\001 \001(\tB\003\340A\002\022\035\n\020protocol_bind" + + "ing\030\002 \001(\tB\003\340A\002\022\016\n\006tenant\030\003 \001(\t\022\035\n\020protoc" + + "ol_version\030\004 \001(\tB\003\340A\002\"\314\005\n\tAgentCard\022\021\n\004n" + + "ame\030\001 \001(\tB\003\340A\002\022\030\n\013description\030\002 \001(\tB\003\340A\002" + + "\0229\n\024supported_interfaces\030\023 \003(\0132\026.a2a.v1." + + "AgentInterfaceB\003\340A\002\022\'\n\010provider\030\004 \001(\0132\025." + + "a2a.v1.AgentProvider\022\024\n\007version\030\005 \001(\tB\003\340" + + "A\002\022\036\n\021documentation_url\030\006 \001(\tH\000\210\001\001\0224\n\014ca" + + "pabilities\030\007 \001(\0132\031.a2a.v1.AgentCapabilit" + + "iesB\003\340A\002\022@\n\020security_schemes\030\010 \003(\0132&.a2a" + + ".v1.AgentCard.SecuritySchemesEntry\022:\n\025se" + + "curity_requirements\030\r \003(\0132\033.a2a.v1.Secur" + + "ityRequirement\022 \n\023default_input_modes\030\n " + + "\003(\tB\003\340A\002\022!\n\024default_output_modes\030\013 \003(\tB\003" + + "\340A\002\022\'\n\006skills\030\014 \003(\0132\022.a2a.v1.AgentSkillB" + + "\003\340A\002\022.\n\nsignatures\030\021 \003(\0132\032.a2a.v1.AgentC" + + "ardSignature\022\025\n\010icon_url\030\022 \001(\tH\001\210\001\001\032N\n\024S" + + "ecuritySchemesEntry\022\013\n\003key\030\001 \001(\t\022%\n\005valu" + + "e\030\002 \001(\0132\026.a2a.v1.SecurityScheme:\0028\001B\024\n\022_" + + "documentation_urlB\013\n\t_icon_urlJ\004\010\003\020\004J\004\010\t" + + "\020\nJ\004\010\016\020\017J\004\010\017\020\020J\004\010\020\020\021\"<\n\rAgentProvider\022\020\n" + + "\003url\030\001 \001(\tB\003\340A\002\022\031\n\014organization\030\002 \001(\tB\003\340" + + "A\002\"\335\001\n\021AgentCapabilities\022\026\n\tstreaming\030\001 " + + "\001(\010H\000\210\001\001\022\037\n\022push_notifications\030\002 \001(\010H\001\210\001" + + "\001\022*\n\nextensions\030\003 \003(\0132\026.a2a.v1.AgentExte" + + "nsion\022 \n\023extended_agent_card\030\005 \001(\010H\002\210\001\001B" + + "\014\n\n_streamingB\025\n\023_push_notificationsB\026\n\024" + + "_extended_agent_cardJ\004\010\004\020\005\"m\n\016AgentExten" + + "sion\022\013\n\003uri\030\001 \001(\t\022\023\n\013description\030\002 \001(\t\022\020" + + "\n\010required\030\003 \001(\010\022\'\n\006params\030\004 \001(\0132\027.googl" + + "e.protobuf.Struct\"\326\001\n\nAgentSkill\022\017\n\002id\030\001" + + " \001(\tB\003\340A\002\022\021\n\004name\030\002 \001(\tB\003\340A\002\022\030\n\013descript" + + "ion\030\003 \001(\tB\003\340A\002\022\021\n\004tags\030\004 \003(\tB\003\340A\002\022\020\n\010exa" + + "mples\030\005 \003(\t\022\023\n\013input_modes\030\006 \003(\t\022\024\n\014outp" + + "ut_modes\030\007 \003(\t\022:\n\025security_requirements\030" + + "\010 \003(\0132\033.a2a.v1.SecurityRequirement\"m\n\022Ag" + + "entCardSignature\022\026\n\tprotected\030\001 \001(\tB\003\340A\002" + + "\022\026\n\tsignature\030\002 \001(\tB\003\340A\002\022\'\n\006header\030\003 \001(\013" + + "2\027.google.protobuf.Struct\"\232\001\n\032TaskPushNo" + + "tificationConfig\022\016\n\006tenant\030\004 \001(\t\022\017\n\002id\030\001" + + " \001(\tB\003\340A\002\022\024\n\007task_id\030\003 \001(\tB\003\340A\002\022E\n\030push_" + + "notification_config\030\002 \001(\0132\036.a2a.v1.PushN" + + "otificationConfigB\003\340A\002\"\032\n\nStringList\022\014\n\004" + + "list\030\001 \003(\t\"\224\001\n\023SecurityRequirement\0229\n\007sc" + + "hemes\030\001 \003(\0132(.a2a.v1.SecurityRequirement" + + ".SchemesEntry\032B\n\014SchemesEntry\022\013\n\003key\030\001 \001" + + "(\t\022!\n\005value\030\002 \001(\0132\022.a2a.v1.StringList:\0028" + + "\001\"\361\002\n\016SecurityScheme\022?\n\027api_key_security" + + "_scheme\030\001 \001(\0132\034.a2a.v1.APIKeySecuritySch" + + "emeH\000\022C\n\031http_auth_security_scheme\030\002 \001(\013" + + "2\036.a2a.v1.HTTPAuthSecuritySchemeH\000\022>\n\026oa" + + "uth2_security_scheme\030\003 \001(\0132\034.a2a.v1.OAut" + + "h2SecuritySchemeH\000\022N\n\037open_id_connect_se" + + "curity_scheme\030\004 \001(\0132#.a2a.v1.OpenIdConne" + + "ctSecuritySchemeH\000\022?\n\024mtls_security_sche" + + "me\030\005 \001(\0132\037.a2a.v1.MutualTlsSecuritySchem" + + "eH\000B\010\n\006scheme\"U\n\024APIKeySecurityScheme\022\023\n" + + "\013description\030\001 \001(\t\022\025\n\010location\030\002 \001(\tB\003\340A" + + "\002\022\021\n\004name\030\003 \001(\tB\003\340A\002\"Y\n\026HTTPAuthSecurity" + + "Scheme\022\023\n\013description\030\001 \001(\t\022\023\n\006scheme\030\002 " + + "\001(\tB\003\340A\002\022\025\n\rbearer_format\030\003 \001(\t\"p\n\024OAuth" + + "2SecurityScheme\022\023\n\013description\030\001 \001(\t\022&\n\005" + + "flows\030\002 \001(\0132\022.a2a.v1.OAuthFlowsB\003\340A\002\022\033\n\023" + + "oauth2_metadata_url\030\003 \001(\t\"T\n\033OpenIdConne" + + "ctSecurityScheme\022\023\n\013description\030\001 \001(\t\022 \n" + + "\023open_id_connect_url\030\002 \001(\tB\003\340A\002\".\n\027Mutua" + + "lTlsSecurityScheme\022\023\n\013description\030\001 \001(\t\"" + + "\262\002\n\nOAuthFlows\022@\n\022authorization_code\030\001 \001" + + "(\0132\".a2a.v1.AuthorizationCodeOAuthFlowH\000" + + "\022@\n\022client_credentials\030\002 \001(\0132\".a2a.v1.Cl" + + "ientCredentialsOAuthFlowH\000\0221\n\010implicit\030\003" + + " \001(\0132\031.a2a.v1.ImplicitOAuthFlowB\002\030\001H\000\0221\n" + + "\010password\030\004 \001(\0132\031.a2a.v1.PasswordOAuthFl" + + "owB\002\030\001H\000\0222\n\013device_code\030\005 \001(\0132\033.a2a.v1.D" + + "eviceCodeOAuthFlowH\000B\006\n\004flow\"\364\001\n\032Authori" + + "zationCodeOAuthFlow\022\036\n\021authorization_url" + + "\030\001 \001(\tB\003\340A\002\022\026\n\ttoken_url\030\002 \001(\tB\003\340A\002\022\023\n\013r" + + "efresh_url\030\003 \001(\t\022C\n\006scopes\030\004 \003(\0132..a2a.v" + + "1.AuthorizationCodeOAuthFlow.ScopesEntry" + + "B\003\340A\002\022\025\n\rpkce_required\030\005 \001(\010\032-\n\013ScopesEn" + + "try\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\275\001\n\032" + + "ClientCredentialsOAuthFlow\022\026\n\ttoken_url\030" + + "\001 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\002 \001(\t\022C\n\006scope" + + "s\030\003 \003(\0132..a2a.v1.ClientCredentialsOAuthF" + + "low.ScopesEntryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003k" + + "ey\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\251\001\n\021Implicit" + + "OAuthFlow\022\031\n\021authorization_url\030\001 \001(\t\022\023\n\013" + + "refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%.a2a." + + "v1.ImplicitOAuthFlow.ScopesEntry\032-\n\013Scop" + + "esEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001\"" + + "\241\001\n\021PasswordOAuthFlow\022\021\n\ttoken_url\030\001 \001(\t" + + "\022\023\n\013refresh_url\030\002 \001(\t\0225\n\006scopes\030\003 \003(\0132%." + + "a2a.v1.PasswordOAuthFlow.ScopesEntry\032-\n\013" + + "ScopesEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:" + + "\0028\001\"\326\001\n\023DeviceCodeOAuthFlow\022%\n\030device_au" + + "thorization_url\030\001 \001(\tB\003\340A\002\022\026\n\ttoken_url\030" + + "\002 \001(\tB\003\340A\002\022\023\n\013refresh_url\030\003 \001(\t\022<\n\006scope" + + "s\030\004 \003(\0132\'.a2a.v1.DeviceCodeOAuthFlow.Sco" + + "pesEntryB\003\340A\002\032-\n\013ScopesEntry\022\013\n\003key\030\001 \001(" + + "\t\022\r\n\005value\030\002 \001(\t:\0028\001\"\257\001\n\022SendMessageRequ" + + "est\022\016\n\006tenant\030\004 \001(\t\022%\n\007message\030\001 \001(\0132\017.a" + + "2a.v1.MessageB\003\340A\002\0227\n\rconfiguration\030\002 \001(" + + "\0132 .a2a.v1.SendMessageConfiguration\022)\n\010m" + + "etadata\030\003 \001(\0132\027.google.protobuf.Struct\"a" + + "\n\016GetTaskRequest\022\016\n\006tenant\030\003 \001(\t\022\017\n\002id\030\001" + + " \001(\tB\003\340A\002\022\033\n\016history_length\030\002 \001(\005H\000\210\001\001B\021" + + "\n\017_history_length\"\265\002\n\020ListTasksRequest\022\016" + + "\n\006tenant\030\t \001(\t\022\022\n\ncontext_id\030\001 \001(\t\022!\n\006st" + + "atus\030\002 \001(\0162\021.a2a.v1.TaskState\022\026\n\tpage_si" + + "ze\030\003 \001(\005H\000\210\001\001\022\022\n\npage_token\030\004 \001(\t\022\033\n\016his" + + "tory_length\030\005 \001(\005H\001\210\001\001\022:\n\026status_timesta" + + "mp_after\030\006 \001(\0132\032.google.protobuf.Timesta" + + "mp\022\036\n\021include_artifacts\030\007 \001(\010H\002\210\001\001B\014\n\n_p" + + "age_sizeB\021\n\017_history_lengthB\024\n\022_include_" + + "artifacts\"\204\001\n\021ListTasksResponse\022 \n\005tasks" + + "\030\001 \003(\0132\014.a2a.v1.TaskB\003\340A\002\022\034\n\017next_page_t" + + "oken\030\002 \001(\tB\003\340A\002\022\026\n\tpage_size\030\003 \001(\005B\003\340A\002\022" + + "\027\n\ntotal_size\030\004 \001(\005B\003\340A\002\"4\n\021CancelTaskRe" + + "quest\022\016\n\006tenant\030\002 \001(\t\022\017\n\002id\030\001 \001(\tB\003\340A\002\"]" + + "\n$GetTaskPushNotificationConfigRequest\022\016" + + "\n\006tenant\030\002 \001(\t\022\024\n\007task_id\030\003 \001(\tB\003\340A\002\022\017\n\002" + + "id\030\001 \001(\tB\003\340A\002\"`\n\'DeleteTaskPushNotificat" + + "ionConfigRequest\022\016\n\006tenant\030\002 \001(\t\022\024\n\007task" + + "_id\030\003 \001(\tB\003\340A\002\022\017\n\002id\030\001 \001(\tB\003\340A\002\"\242\001\n\'Crea" + + "teTaskPushNotificationConfigRequest\022\016\n\006t" + + "enant\030\004 \001(\t\022\024\n\007task_id\030\001 \001(\tB\003\340A\002\022\026\n\tcon" + + "fig_id\030\002 \001(\tB\003\340A\002\0223\n\006config\030\005 \001(\0132\036.a2a." + + "v1.PushNotificationConfigB\003\340A\002J\004\010\003\020\004\"9\n\026" + + "SubscribeToTaskRequest\022\016\n\006tenant\030\002 \001(\t\022\017" + + "\n\002id\030\001 \001(\tB\003\340A\002\"t\n%ListTaskPushNotificat" + + "ionConfigRequest\022\016\n\006tenant\030\004 \001(\t\022\024\n\007task" + + "_id\030\001 \001(\tB\003\340A\002\022\021\n\tpage_size\030\002 \001(\005\022\022\n\npag" + + "e_token\030\003 \001(\t\"-\n\033GetExtendedAgentCardReq" + + "uest\022\016\n\006tenant\030\001 \001(\t\"b\n\023SendMessageRespo" + + "nse\022\034\n\004task\030\001 \001(\0132\014.a2a.v1.TaskH\000\022\"\n\007mes" + + "sage\030\002 \001(\0132\017.a2a.v1.MessageH\000B\t\n\007payload" + + "\"\321\001\n\016StreamResponse\022\034\n\004task\030\001 \001(\0132\014.a2a." + + "v1.TaskH\000\022\"\n\007message\030\002 \001(\0132\017.a2a.v1.Mess" + + "ageH\000\0226\n\rstatus_update\030\003 \001(\0132\035.a2a.v1.Ta" + + "skStatusUpdateEventH\000\022:\n\017artifact_update" + + "\030\004 \001(\0132\037.a2a.v1.TaskArtifactUpdateEventH" + + "\000B\t\n\007payload\"v\n&ListTaskPushNotification" + + "ConfigResponse\0223\n\007configs\030\001 \003(\0132\".a2a.v1" + + ".TaskPushNotificationConfig\022\027\n\017next_page" + + "_token\030\002 \001(\t*\371\001\n\tTaskState\022\032\n\026TASK_STATE" + + "_UNSPECIFIED\020\000\022\030\n\024TASK_STATE_SUBMITTED\020\001" + + "\022\026\n\022TASK_STATE_WORKING\020\002\022\030\n\024TASK_STATE_C" + + "OMPLETED\020\003\022\025\n\021TASK_STATE_FAILED\020\004\022\027\n\023TAS" + + "K_STATE_CANCELED\020\005\022\035\n\031TASK_STATE_INPUT_R" + + "EQUIRED\020\006\022\027\n\023TASK_STATE_REJECTED\020\007\022\034\n\030TA" + + "SK_STATE_AUTH_REQUIRED\020\010*;\n\004Role\022\024\n\020ROLE" + + "_UNSPECIFIED\020\000\022\r\n\tROLE_USER\020\001\022\016\n\nROLE_AG" + + "ENT\020\0022\352\016\n\nA2AService\022}\n\013SendMessage\022\032.a2" + + "a.v1.SendMessageRequest\032\033.a2a.v1.SendMes" + + "sageResponse\"5\202\323\344\223\002/\"\r/message:send:\001*Z\033" + + "\"\026/{tenant}/message:send:\001*\022\207\001\n\024SendStre" + + "amingMessage\022\032.a2a.v1.SendMessageRequest" + + "\032\026.a2a.v1.StreamResponse\"9\202\323\344\223\0023\"\017/messa" + + "ge:stream:\001*Z\035\"\030/{tenant}/message:stream" + + ":\001*0\001\022e\n\007GetTask\022\026.a2a.v1.GetTaskRequest" + + "\032\014.a2a.v1.Task\"4\332A\002id\202\323\344\223\002)\022\r/tasks/{id=" + + "*}Z\030\022\026/{tenant}/tasks/{id=*}\022c\n\tListTask" + + "s\022\030.a2a.v1.ListTasksRequest\032\031.a2a.v1.Lis" + + "tTasksResponse\"!\202\323\344\223\002\033\022\006/tasksZ\021\022\017/{tena" + + "nt}/tasks\022z\n\nCancelTask\022\031.a2a.v1.CancelT" + + "askRequest\032\014.a2a.v1.Task\"C\202\323\344\223\002=\"\024/tasks" + + "/{id=*}:cancel:\001*Z\"\"\035/{tenant}/tasks/{id" + + "=*}:cancel:\001*\022\220\001\n\017SubscribeToTask\022\036.a2a." + + "v1.SubscribeToTaskRequest\032\026.a2a.v1.Strea" + + "mResponse\"C\202\323\344\223\002=\022\027/tasks/{id=*}:subscri" + + "beZ\"\022 /{tenant}/tasks/{id=*}:subscribe0\001" + + "\022\204\002\n CreateTaskPushNotificationConfig\022/." + + "a2a.v1.CreateTaskPushNotificationConfigR" + + "equest\032\".a2a.v1.TaskPushNotificationConf" + + "ig\"\212\001\332A\016task_id,config\202\323\344\223\002s\"*/tasks/{ta" + + "sk_id=*}/pushNotificationConfigs:\006config" + + "Z=\"3/{tenant}/tasks/{task_id=*}/pushNoti" + + "ficationConfigs:\006config\022\370\001\n\035GetTaskPushN" + + "otificationConfig\022,.a2a.v1.GetTaskPushNo" + + "tificationConfigRequest\032\".a2a.v1.TaskPus" + + "hNotificationConfig\"\204\001\332A\ntask_id,id\202\323\344\223\002" + + "q\0221/tasks/{task_id=*}/pushNotificationCo" + + "nfigs/{id=*}Z<\022:/{tenant}/tasks/{task_id" + + "=*}/pushNotificationConfigs/{id=*}\022\364\001\n\036L" + + "istTaskPushNotificationConfig\022-.a2a.v1.L" + + "istTaskPushNotificationConfigRequest\032..a" + + "2a.v1.ListTaskPushNotificationConfigResp" + + "onse\"s\332A\007task_id\202\323\344\223\002c\022*/tasks/{task_id=" + + "*}/pushNotificationConfigsZ5\0223/{tenant}/" + + "tasks/{task_id=*}/pushNotificationConfig" + + "s\022\211\001\n\024GetExtendedAgentCard\022#.a2a.v1.GetE" + + "xtendedAgentCardRequest\032\021.a2a.v1.AgentCa" + + "rd\"9\202\323\344\223\0023\022\022/extendedAgentCardZ\035\022\033/{tena" + + "nt}/extendedAgentCard\022\362\001\n DeleteTaskPush" + + "NotificationConfig\022/.a2a.v1.DeleteTaskPu" + + "shNotificationConfigRequest\032\026.google.pro" + + "tobuf.Empty\"\204\001\332A\ntask_id,id\202\323\344\223\002q*1/task" + + "s/{task_id=*}/pushNotificationConfigs/{i" + + "d=*}Z<*:/{tenant}/tasks/{task_id=*}/push" + + "NotificationConfigs/{id=*}B7\n\013io.a2a.grp" + + "cB\003A2AP\001Z\030google.golang.org/a2a/v1\252\002\006A2a" + + ".V1b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -560,67 +583,55 @@ public static void registerAllExtensions( internal_static_a2a_v1_Part_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_Part_descriptor, - new java.lang.String[] { "Text", "File", "Data", "Metadata", "Part", }); - internal_static_a2a_v1_FilePart_descriptor = - getDescriptor().getMessageType(4); - internal_static_a2a_v1_FilePart_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_FilePart_descriptor, - new java.lang.String[] { "FileWithUri", "FileWithBytes", "MediaType", "Name", "File", }); - internal_static_a2a_v1_DataPart_descriptor = - getDescriptor().getMessageType(5); - internal_static_a2a_v1_DataPart_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_DataPart_descriptor, - new java.lang.String[] { "Data", }); + new java.lang.String[] { "Text", "Raw", "Url", "Data", "Metadata", "Filename", "MediaType", "Content", }); internal_static_a2a_v1_Message_descriptor = - getDescriptor().getMessageType(6); + getDescriptor().getMessageType(4); internal_static_a2a_v1_Message_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_Message_descriptor, new java.lang.String[] { "MessageId", "ContextId", "TaskId", "Role", "Parts", "Metadata", "Extensions", "ReferenceTaskIds", }); internal_static_a2a_v1_Artifact_descriptor = - getDescriptor().getMessageType(7); + getDescriptor().getMessageType(5); internal_static_a2a_v1_Artifact_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_Artifact_descriptor, new java.lang.String[] { "ArtifactId", "Name", "Description", "Parts", "Metadata", "Extensions", }); internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor = - getDescriptor().getMessageType(8); + getDescriptor().getMessageType(6); internal_static_a2a_v1_TaskStatusUpdateEvent_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskStatusUpdateEvent_descriptor, - new java.lang.String[] { "TaskId", "ContextId", "Status", "Final", "Metadata", }); + new java.lang.String[] { "TaskId", "ContextId", "Status", "Metadata", }); internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor = - getDescriptor().getMessageType(9); + getDescriptor().getMessageType(7); internal_static_a2a_v1_TaskArtifactUpdateEvent_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskArtifactUpdateEvent_descriptor, new java.lang.String[] { "TaskId", "ContextId", "Artifact", "Append", "LastChunk", "Metadata", }); internal_static_a2a_v1_PushNotificationConfig_descriptor = - getDescriptor().getMessageType(10); + getDescriptor().getMessageType(8); internal_static_a2a_v1_PushNotificationConfig_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_PushNotificationConfig_descriptor, new java.lang.String[] { "Id", "Url", "Token", "Authentication", }); internal_static_a2a_v1_AuthenticationInfo_descriptor = - getDescriptor().getMessageType(11); + getDescriptor().getMessageType(9); internal_static_a2a_v1_AuthenticationInfo_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AuthenticationInfo_descriptor, - new java.lang.String[] { "Schemes", "Credentials", }); + new java.lang.String[] { "Scheme", "Credentials", }); internal_static_a2a_v1_AgentInterface_descriptor = - getDescriptor().getMessageType(12); + getDescriptor().getMessageType(10); internal_static_a2a_v1_AgentInterface_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentInterface_descriptor, - new java.lang.String[] { "Url", "ProtocolBinding", "Tenant", }); + new java.lang.String[] { "Url", "ProtocolBinding", "Tenant", "ProtocolVersion", }); internal_static_a2a_v1_AgentCard_descriptor = - getDescriptor().getMessageType(13); + getDescriptor().getMessageType(11); internal_static_a2a_v1_AgentCard_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCard_descriptor, - new java.lang.String[] { "ProtocolVersions", "Name", "Description", "SupportedInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "Security", "DefaultInputModes", "DefaultOutputModes", "Skills", "Signatures", "IconUrl", }); + new java.lang.String[] { "Name", "Description", "SupportedInterfaces", "Provider", "Version", "DocumentationUrl", "Capabilities", "SecuritySchemes", "SecurityRequirements", "DefaultInputModes", "DefaultOutputModes", "Skills", "Signatures", "IconUrl", }); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor = internal_static_a2a_v1_AgentCard_descriptor.getNestedType(0); internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_fieldAccessorTable = new @@ -628,103 +639,103 @@ public static void registerAllExtensions( internal_static_a2a_v1_AgentCard_SecuritySchemesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_AgentProvider_descriptor = - getDescriptor().getMessageType(14); + getDescriptor().getMessageType(12); internal_static_a2a_v1_AgentProvider_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentProvider_descriptor, new java.lang.String[] { "Url", "Organization", }); internal_static_a2a_v1_AgentCapabilities_descriptor = - getDescriptor().getMessageType(15); + getDescriptor().getMessageType(13); internal_static_a2a_v1_AgentCapabilities_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCapabilities_descriptor, new java.lang.String[] { "Streaming", "PushNotifications", "Extensions", "ExtendedAgentCard", }); internal_static_a2a_v1_AgentExtension_descriptor = - getDescriptor().getMessageType(16); + getDescriptor().getMessageType(14); internal_static_a2a_v1_AgentExtension_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentExtension_descriptor, new java.lang.String[] { "Uri", "Description", "Required", "Params", }); internal_static_a2a_v1_AgentSkill_descriptor = - getDescriptor().getMessageType(17); + getDescriptor().getMessageType(15); internal_static_a2a_v1_AgentSkill_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentSkill_descriptor, - new java.lang.String[] { "Id", "Name", "Description", "Tags", "Examples", "InputModes", "OutputModes", "Security", }); + new java.lang.String[] { "Id", "Name", "Description", "Tags", "Examples", "InputModes", "OutputModes", "SecurityRequirements", }); internal_static_a2a_v1_AgentCardSignature_descriptor = - getDescriptor().getMessageType(18); + getDescriptor().getMessageType(16); internal_static_a2a_v1_AgentCardSignature_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AgentCardSignature_descriptor, new java.lang.String[] { "Protected", "Signature", "Header", }); internal_static_a2a_v1_TaskPushNotificationConfig_descriptor = - getDescriptor().getMessageType(19); + getDescriptor().getMessageType(17); internal_static_a2a_v1_TaskPushNotificationConfig_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_TaskPushNotificationConfig_descriptor, - new java.lang.String[] { "Name", "PushNotificationConfig", }); + new java.lang.String[] { "Tenant", "Id", "TaskId", "PushNotificationConfig", }); internal_static_a2a_v1_StringList_descriptor = - getDescriptor().getMessageType(20); + getDescriptor().getMessageType(18); internal_static_a2a_v1_StringList_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_StringList_descriptor, new java.lang.String[] { "List", }); - internal_static_a2a_v1_Security_descriptor = - getDescriptor().getMessageType(21); - internal_static_a2a_v1_Security_fieldAccessorTable = new + internal_static_a2a_v1_SecurityRequirement_descriptor = + getDescriptor().getMessageType(19); + internal_static_a2a_v1_SecurityRequirement_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_Security_descriptor, + internal_static_a2a_v1_SecurityRequirement_descriptor, new java.lang.String[] { "Schemes", }); - internal_static_a2a_v1_Security_SchemesEntry_descriptor = - internal_static_a2a_v1_Security_descriptor.getNestedType(0); - internal_static_a2a_v1_Security_SchemesEntry_fieldAccessorTable = new + internal_static_a2a_v1_SecurityRequirement_SchemesEntry_descriptor = + internal_static_a2a_v1_SecurityRequirement_descriptor.getNestedType(0); + internal_static_a2a_v1_SecurityRequirement_SchemesEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_a2a_v1_Security_SchemesEntry_descriptor, + internal_static_a2a_v1_SecurityRequirement_SchemesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_SecurityScheme_descriptor = - getDescriptor().getMessageType(22); + getDescriptor().getMessageType(20); internal_static_a2a_v1_SecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SecurityScheme_descriptor, new java.lang.String[] { "ApiKeySecurityScheme", "HttpAuthSecurityScheme", "Oauth2SecurityScheme", "OpenIdConnectSecurityScheme", "MtlsSecurityScheme", "Scheme", }); internal_static_a2a_v1_APIKeySecurityScheme_descriptor = - getDescriptor().getMessageType(23); + getDescriptor().getMessageType(21); internal_static_a2a_v1_APIKeySecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_APIKeySecurityScheme_descriptor, new java.lang.String[] { "Description", "Location", "Name", }); internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor = - getDescriptor().getMessageType(24); + getDescriptor().getMessageType(22); internal_static_a2a_v1_HTTPAuthSecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_HTTPAuthSecurityScheme_descriptor, new java.lang.String[] { "Description", "Scheme", "BearerFormat", }); internal_static_a2a_v1_OAuth2SecurityScheme_descriptor = - getDescriptor().getMessageType(25); + getDescriptor().getMessageType(23); internal_static_a2a_v1_OAuth2SecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OAuth2SecurityScheme_descriptor, new java.lang.String[] { "Description", "Flows", "Oauth2MetadataUrl", }); internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor = - getDescriptor().getMessageType(26); + getDescriptor().getMessageType(24); internal_static_a2a_v1_OpenIdConnectSecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OpenIdConnectSecurityScheme_descriptor, new java.lang.String[] { "Description", "OpenIdConnectUrl", }); internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor = - getDescriptor().getMessageType(27); + getDescriptor().getMessageType(25); internal_static_a2a_v1_MutualTlsSecurityScheme_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_MutualTlsSecurityScheme_descriptor, new java.lang.String[] { "Description", }); internal_static_a2a_v1_OAuthFlows_descriptor = - getDescriptor().getMessageType(28); + getDescriptor().getMessageType(26); internal_static_a2a_v1_OAuthFlows_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_OAuthFlows_descriptor, - new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "DeviceCode", "Flow", }); + new java.lang.String[] { "AuthorizationCode", "ClientCredentials", "Implicit", "Password", "DeviceCode", "Flow", }); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor = - getDescriptor().getMessageType(29); + getDescriptor().getMessageType(27); internal_static_a2a_v1_AuthorizationCodeOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_AuthorizationCodeOAuthFlow_descriptor, @@ -736,7 +747,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_AuthorizationCodeOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor = - getDescriptor().getMessageType(30); + getDescriptor().getMessageType(28); internal_static_a2a_v1_ClientCredentialsOAuthFlow_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ClientCredentialsOAuthFlow_descriptor, @@ -747,6 +758,30 @@ public static void registerAllExtensions( com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ClientCredentialsOAuthFlow_ScopesEntry_descriptor, new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor = + getDescriptor().getMessageType(29); + internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor, + new java.lang.String[] { "AuthorizationUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_ImplicitOAuthFlow_descriptor.getNestedType(0); + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); + internal_static_a2a_v1_PasswordOAuthFlow_descriptor = + getDescriptor().getMessageType(30); + internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_PasswordOAuthFlow_descriptor, + new java.lang.String[] { "TokenUrl", "RefreshUrl", "Scopes", }); + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor = + internal_static_a2a_v1_PasswordOAuthFlow_descriptor.getNestedType(0); + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, + new java.lang.String[] { "Key", "Value", }); internal_static_a2a_v1_DeviceCodeOAuthFlow_descriptor = getDescriptor().getMessageType(31); internal_static_a2a_v1_DeviceCodeOAuthFlow_fieldAccessorTable = new @@ -770,7 +805,7 @@ public static void registerAllExtensions( internal_static_a2a_v1_GetTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskRequest_descriptor, - new java.lang.String[] { "Tenant", "Name", "HistoryLength", }); + new java.lang.String[] { "Tenant", "Id", "HistoryLength", }); internal_static_a2a_v1_ListTasksRequest_descriptor = getDescriptor().getMessageType(34); internal_static_a2a_v1_ListTasksRequest_fieldAccessorTable = new @@ -788,37 +823,37 @@ public static void registerAllExtensions( internal_static_a2a_v1_CancelTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CancelTaskRequest_descriptor, - new java.lang.String[] { "Tenant", "Name", }); + new java.lang.String[] { "Tenant", "Id", }); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageType(37); internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_GetTaskPushNotificationConfigRequest_descriptor, - new java.lang.String[] { "Tenant", "Name", }); + new java.lang.String[] { "Tenant", "TaskId", "Id", }); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageType(38); internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_DeleteTaskPushNotificationConfigRequest_descriptor, - new java.lang.String[] { "Tenant", "Name", }); + new java.lang.String[] { "Tenant", "TaskId", "Id", }); internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageType(39); internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_CreateTaskPushNotificationConfigRequest_descriptor, - new java.lang.String[] { "Tenant", "Parent", "ConfigId", "Config", }); + new java.lang.String[] { "Tenant", "TaskId", "ConfigId", "Config", }); internal_static_a2a_v1_SubscribeToTaskRequest_descriptor = getDescriptor().getMessageType(40); internal_static_a2a_v1_SubscribeToTaskRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_SubscribeToTaskRequest_descriptor, - new java.lang.String[] { "Tenant", "Name", }); + new java.lang.String[] { "Tenant", "Id", }); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor = getDescriptor().getMessageType(41); internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_a2a_v1_ListTaskPushNotificationConfigRequest_descriptor, - new java.lang.String[] { "Tenant", "Parent", "PageSize", "PageToken", }); + new java.lang.String[] { "Tenant", "TaskId", "PageSize", "PageToken", }); internal_static_a2a_v1_GetExtendedAgentCardRequest_descriptor = getDescriptor().getMessageType(42); internal_static_a2a_v1_GetExtendedAgentCardRequest_fieldAccessorTable = new diff --git a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java index 522c9083b..6d179b674 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/A2AServiceGrpc.java @@ -475,7 +475,7 @@ default void cancelTask(io.a2a.grpc.CancelTaskRequest request, /** *

          * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
    -     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
    +     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, canceled, rejected).
          * 
    */ default void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, @@ -625,7 +625,7 @@ public void cancelTask(io.a2a.grpc.CancelTaskRequest request, /** *
          * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
    -     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
    +     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, canceled, rejected).
          * 
    */ public void subscribeToTask(io.a2a.grpc.SubscribeToTaskRequest request, @@ -764,7 +764,7 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) throws /** *
          * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
    -     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
    +     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, canceled, rejected).
          * 
    */ @io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918") @@ -898,7 +898,7 @@ public io.a2a.grpc.Task cancelTask(io.a2a.grpc.CancelTaskRequest request) { /** *
          * SubscribeToTask allows subscribing to task updates for tasks not in terminal state.
    -     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, cancelled, rejected).
    +     * Returns UnsupportedOperationError if task is in terminal state (completed, failed, canceled, rejected).
          * 
    */ public java.util.Iterator subscribeToTask( diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java index 5111e4f12..2b06f54b8 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCard.java @@ -36,14 +36,12 @@ private AgentCard(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } private AgentCard() { - protocolVersions_ = - com.google.protobuf.LazyStringArrayList.emptyList(); name_ = ""; description_ = ""; supportedInterfaces_ = java.util.Collections.emptyList(); version_ = ""; documentationUrl_ = ""; - security_ = java.util.Collections.emptyList(); + securityRequirements_ = java.util.Collections.emptyList(); defaultInputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); defaultOutputModes_ = @@ -79,71 +77,6 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl } private int bitField0_; - public static final int PROTOCOL_VERSIONS_FIELD_NUMBER = 16; - @SuppressWarnings("serial") - private com.google.protobuf.LazyStringArrayList protocolVersions_ = - com.google.protobuf.LazyStringArrayList.emptyList(); - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return A list containing the protocolVersions. - */ - public com.google.protobuf.ProtocolStringList - getProtocolVersionsList() { - return protocolVersions_; - } - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The count of protocolVersions. - */ - public int getProtocolVersionsCount() { - return protocolVersions_.size(); - } - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the element to return. - * @return The protocolVersions at the given index. - */ - public java.lang.String getProtocolVersions(int index) { - return protocolVersions_.get(index); - } - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the value to return. - * @return The bytes of the protocolVersions at the given index. - */ - public com.google.protobuf.ByteString - getProtocolVersionsBytes(int index) { - return protocolVersions_.getByteString(index); - } - public static final int NAME_FIELD_NUMBER = 1; @SuppressWarnings("serial") private volatile java.lang.Object name_ = ""; @@ -584,70 +517,65 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow( return map.get(key); } - public static final int SECURITY_FIELD_NUMBER = 9; + public static final int SECURITY_REQUIREMENTS_FIELD_NUMBER = 13; @SuppressWarnings("serial") - private java.util.List security_; + private java.util.List securityRequirements_; /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ @java.lang.Override - public java.util.List getSecurityList() { - return security_; + public java.util.List getSecurityRequirementsList() { + return securityRequirements_; } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ @java.lang.Override - public java.util.List - getSecurityOrBuilderList() { - return security_; + public java.util.List + getSecurityRequirementsOrBuilderList() { + return securityRequirements_; } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ @java.lang.Override - public int getSecurityCount() { - return security_.size(); + public int getSecurityRequirementsCount() { + return securityRequirements_.size(); } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ @java.lang.Override - public io.a2a.grpc.Security getSecurity(int index) { - return security_.get(index); + public io.a2a.grpc.SecurityRequirement getSecurityRequirements(int index) { + return securityRequirements_.get(index); } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ @java.lang.Override - public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + public io.a2a.grpc.SecurityRequirementOrBuilder getSecurityRequirementsOrBuilder( int index) { - return security_.get(index); + return securityRequirements_.get(index); } public static final int DEFAULT_INPUT_MODES_FIELD_NUMBER = 10; @@ -993,9 +921,6 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) internalGetSecuritySchemes(), SecuritySchemesDefaultEntryHolder.defaultEntry, 8); - for (int i = 0; i < security_.size(); i++) { - output.writeMessage(9, security_.get(i)); - } for (int i = 0; i < defaultInputModes_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 10, defaultInputModes_.getRaw(i)); } @@ -1005,8 +930,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < skills_.size(); i++) { output.writeMessage(12, skills_.get(i)); } - for (int i = 0; i < protocolVersions_.size(); i++) { - com.google.protobuf.GeneratedMessage.writeString(output, 16, protocolVersions_.getRaw(i)); + for (int i = 0; i < securityRequirements_.size(); i++) { + output.writeMessage(13, securityRequirements_.get(i)); } for (int i = 0; i < signatures_.size(); i++) { output.writeMessage(17, signatures_.get(i)); @@ -1056,10 +981,6 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(8, securitySchemes__); } - for (int i = 0; i < security_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(9, security_.get(i)); - } { int dataSize = 0; for (int i = 0; i < defaultInputModes_.size(); i++) { @@ -1080,13 +1001,9 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(12, skills_.get(i)); } - { - int dataSize = 0; - for (int i = 0; i < protocolVersions_.size(); i++) { - dataSize += computeStringSizeNoTag(protocolVersions_.getRaw(i)); - } - size += dataSize; - size += 2 * getProtocolVersionsList().size(); + for (int i = 0; i < securityRequirements_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(13, securityRequirements_.get(i)); } for (int i = 0; i < signatures_.size(); i++) { size += com.google.protobuf.CodedOutputStream @@ -1114,8 +1031,6 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.AgentCard other = (io.a2a.grpc.AgentCard) obj; - if (!getProtocolVersionsList() - .equals(other.getProtocolVersionsList())) return false; if (!getName() .equals(other.getName())) return false; if (!getDescription() @@ -1141,8 +1056,8 @@ public boolean equals(final java.lang.Object obj) { } if (!internalGetSecuritySchemes().equals( other.internalGetSecuritySchemes())) return false; - if (!getSecurityList() - .equals(other.getSecurityList())) return false; + if (!getSecurityRequirementsList() + .equals(other.getSecurityRequirementsList())) return false; if (!getDefaultInputModesList() .equals(other.getDefaultInputModesList())) return false; if (!getDefaultOutputModesList() @@ -1167,10 +1082,6 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); - if (getProtocolVersionsCount() > 0) { - hash = (37 * hash) + PROTOCOL_VERSIONS_FIELD_NUMBER; - hash = (53 * hash) + getProtocolVersionsList().hashCode(); - } hash = (37 * hash) + NAME_FIELD_NUMBER; hash = (53 * hash) + getName().hashCode(); hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER; @@ -1197,9 +1108,9 @@ public int hashCode() { hash = (37 * hash) + SECURITY_SCHEMES_FIELD_NUMBER; hash = (53 * hash) + internalGetSecuritySchemes().hashCode(); } - if (getSecurityCount() > 0) { - hash = (37 * hash) + SECURITY_FIELD_NUMBER; - hash = (53 * hash) + getSecurityList().hashCode(); + if (getSecurityRequirementsCount() > 0) { + hash = (37 * hash) + SECURITY_REQUIREMENTS_FIELD_NUMBER; + hash = (53 * hash) + getSecurityRequirementsList().hashCode(); } if (getDefaultInputModesCount() > 0) { hash = (37 * hash) + DEFAULT_INPUT_MODES_FIELD_NUMBER; @@ -1384,7 +1295,7 @@ private void maybeForceBuilderInitialization() { internalGetSupportedInterfacesFieldBuilder(); internalGetProviderFieldBuilder(); internalGetCapabilitiesFieldBuilder(); - internalGetSecurityFieldBuilder(); + internalGetSecurityRequirementsFieldBuilder(); internalGetSkillsFieldBuilder(); internalGetSignaturesFieldBuilder(); } @@ -1393,8 +1304,6 @@ private void maybeForceBuilderInitialization() { public Builder clear() { super.clear(); bitField0_ = 0; - protocolVersions_ = - com.google.protobuf.LazyStringArrayList.emptyList(); name_ = ""; description_ = ""; if (supportedInterfacesBuilder_ == null) { @@ -1403,7 +1312,7 @@ public Builder clear() { supportedInterfaces_ = null; supportedInterfacesBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000004); provider_ = null; if (providerBuilder_ != null) { providerBuilder_.dispose(); @@ -1417,13 +1326,13 @@ public Builder clear() { capabilitiesBuilder_ = null; } internalGetMutableSecuritySchemes().clear(); - if (securityBuilder_ == null) { - security_ = java.util.Collections.emptyList(); + if (securityRequirementsBuilder_ == null) { + securityRequirements_ = java.util.Collections.emptyList(); } else { - security_ = null; - securityBuilder_.clear(); + securityRequirements_ = null; + securityRequirementsBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00000200); + bitField0_ = (bitField0_ & ~0x00000100); defaultInputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); defaultOutputModes_ = @@ -1434,14 +1343,14 @@ public Builder clear() { skills_ = null; skillsBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00001000); + bitField0_ = (bitField0_ & ~0x00000800); if (signaturesBuilder_ == null) { signatures_ = java.util.Collections.emptyList(); } else { signatures_ = null; signaturesBuilder_.clear(); } - bitField0_ = (bitField0_ & ~0x00002000); + bitField0_ = (bitField0_ & ~0x00001000); iconUrl_ = ""; return this; } @@ -1477,36 +1386,36 @@ public io.a2a.grpc.AgentCard buildPartial() { private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) { if (supportedInterfacesBuilder_ == null) { - if (((bitField0_ & 0x00000008) != 0)) { + if (((bitField0_ & 0x00000004) != 0)) { supportedInterfaces_ = java.util.Collections.unmodifiableList(supportedInterfaces_); - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000004); } result.supportedInterfaces_ = supportedInterfaces_; } else { result.supportedInterfaces_ = supportedInterfacesBuilder_.build(); } - if (securityBuilder_ == null) { - if (((bitField0_ & 0x00000200) != 0)) { - security_ = java.util.Collections.unmodifiableList(security_); - bitField0_ = (bitField0_ & ~0x00000200); + if (securityRequirementsBuilder_ == null) { + if (((bitField0_ & 0x00000100) != 0)) { + securityRequirements_ = java.util.Collections.unmodifiableList(securityRequirements_); + bitField0_ = (bitField0_ & ~0x00000100); } - result.security_ = security_; + result.securityRequirements_ = securityRequirements_; } else { - result.security_ = securityBuilder_.build(); + result.securityRequirements_ = securityRequirementsBuilder_.build(); } if (skillsBuilder_ == null) { - if (((bitField0_ & 0x00001000) != 0)) { + if (((bitField0_ & 0x00000800) != 0)) { skills_ = java.util.Collections.unmodifiableList(skills_); - bitField0_ = (bitField0_ & ~0x00001000); + bitField0_ = (bitField0_ & ~0x00000800); } result.skills_ = skills_; } else { result.skills_ = skillsBuilder_.build(); } if (signaturesBuilder_ == null) { - if (((bitField0_ & 0x00002000) != 0)) { + if (((bitField0_ & 0x00001000) != 0)) { signatures_ = java.util.Collections.unmodifiableList(signatures_); - bitField0_ = (bitField0_ & ~0x00002000); + bitField0_ = (bitField0_ & ~0x00001000); } result.signatures_ = signatures_; } else { @@ -1517,47 +1426,43 @@ private void buildPartialRepeatedFields(io.a2a.grpc.AgentCard result) { private void buildPartial0(io.a2a.grpc.AgentCard result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - protocolVersions_.makeImmutable(); - result.protocolVersions_ = protocolVersions_; - } - if (((from_bitField0_ & 0x00000002) != 0)) { result.name_ = name_; } - if (((from_bitField0_ & 0x00000004) != 0)) { + if (((from_bitField0_ & 0x00000002) != 0)) { result.description_ = description_; } int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000010) != 0)) { + if (((from_bitField0_ & 0x00000008) != 0)) { result.provider_ = providerBuilder_ == null ? provider_ : providerBuilder_.build(); to_bitField0_ |= 0x00000001; } - if (((from_bitField0_ & 0x00000020) != 0)) { + if (((from_bitField0_ & 0x00000010) != 0)) { result.version_ = version_; } - if (((from_bitField0_ & 0x00000040) != 0)) { + if (((from_bitField0_ & 0x00000020) != 0)) { result.documentationUrl_ = documentationUrl_; to_bitField0_ |= 0x00000002; } - if (((from_bitField0_ & 0x00000080) != 0)) { + if (((from_bitField0_ & 0x00000040) != 0)) { result.capabilities_ = capabilitiesBuilder_ == null ? capabilities_ : capabilitiesBuilder_.build(); to_bitField0_ |= 0x00000004; } - if (((from_bitField0_ & 0x00000100) != 0)) { + if (((from_bitField0_ & 0x00000080) != 0)) { result.securitySchemes_ = internalGetSecuritySchemes().build(SecuritySchemesDefaultEntryHolder.defaultEntry); } - if (((from_bitField0_ & 0x00000400) != 0)) { + if (((from_bitField0_ & 0x00000200) != 0)) { defaultInputModes_.makeImmutable(); result.defaultInputModes_ = defaultInputModes_; } - if (((from_bitField0_ & 0x00000800) != 0)) { + if (((from_bitField0_ & 0x00000400) != 0)) { defaultOutputModes_.makeImmutable(); result.defaultOutputModes_ = defaultOutputModes_; } - if (((from_bitField0_ & 0x00004000) != 0)) { + if (((from_bitField0_ & 0x00002000) != 0)) { result.iconUrl_ = iconUrl_; to_bitField0_ |= 0x00000008; } @@ -1576,31 +1481,21 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (other == io.a2a.grpc.AgentCard.getDefaultInstance()) return this; - if (!other.protocolVersions_.isEmpty()) { - if (protocolVersions_.isEmpty()) { - protocolVersions_ = other.protocolVersions_; - bitField0_ |= 0x00000001; - } else { - ensureProtocolVersionsIsMutable(); - protocolVersions_.addAll(other.protocolVersions_); - } - onChanged(); - } if (!other.getName().isEmpty()) { name_ = other.name_; - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000001; onChanged(); } if (!other.getDescription().isEmpty()) { description_ = other.description_; - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000002; onChanged(); } if (supportedInterfacesBuilder_ == null) { if (!other.supportedInterfaces_.isEmpty()) { if (supportedInterfaces_.isEmpty()) { supportedInterfaces_ = other.supportedInterfaces_; - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000004); } else { ensureSupportedInterfacesIsMutable(); supportedInterfaces_.addAll(other.supportedInterfaces_); @@ -1613,7 +1508,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { supportedInterfacesBuilder_.dispose(); supportedInterfacesBuilder_ = null; supportedInterfaces_ = other.supportedInterfaces_; - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000004); supportedInterfacesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetSupportedInterfacesFieldBuilder() : null; @@ -1627,12 +1522,12 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } if (!other.getVersion().isEmpty()) { version_ = other.version_; - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000010; onChanged(); } if (other.hasDocumentationUrl()) { documentationUrl_ = other.documentationUrl_; - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000020; onChanged(); } if (other.hasCapabilities()) { @@ -1640,37 +1535,37 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } internalGetMutableSecuritySchemes().mergeFrom( other.internalGetSecuritySchemes()); - bitField0_ |= 0x00000100; - if (securityBuilder_ == null) { - if (!other.security_.isEmpty()) { - if (security_.isEmpty()) { - security_ = other.security_; - bitField0_ = (bitField0_ & ~0x00000200); + bitField0_ |= 0x00000080; + if (securityRequirementsBuilder_ == null) { + if (!other.securityRequirements_.isEmpty()) { + if (securityRequirements_.isEmpty()) { + securityRequirements_ = other.securityRequirements_; + bitField0_ = (bitField0_ & ~0x00000100); } else { - ensureSecurityIsMutable(); - security_.addAll(other.security_); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.addAll(other.securityRequirements_); } onChanged(); } } else { - if (!other.security_.isEmpty()) { - if (securityBuilder_.isEmpty()) { - securityBuilder_.dispose(); - securityBuilder_ = null; - security_ = other.security_; - bitField0_ = (bitField0_ & ~0x00000200); - securityBuilder_ = + if (!other.securityRequirements_.isEmpty()) { + if (securityRequirementsBuilder_.isEmpty()) { + securityRequirementsBuilder_.dispose(); + securityRequirementsBuilder_ = null; + securityRequirements_ = other.securityRequirements_; + bitField0_ = (bitField0_ & ~0x00000100); + securityRequirementsBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - internalGetSecurityFieldBuilder() : null; + internalGetSecurityRequirementsFieldBuilder() : null; } else { - securityBuilder_.addAllMessages(other.security_); + securityRequirementsBuilder_.addAllMessages(other.securityRequirements_); } } } if (!other.defaultInputModes_.isEmpty()) { if (defaultInputModes_.isEmpty()) { defaultInputModes_ = other.defaultInputModes_; - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000200; } else { ensureDefaultInputModesIsMutable(); defaultInputModes_.addAll(other.defaultInputModes_); @@ -1680,7 +1575,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (!other.defaultOutputModes_.isEmpty()) { if (defaultOutputModes_.isEmpty()) { defaultOutputModes_ = other.defaultOutputModes_; - bitField0_ |= 0x00000800; + bitField0_ |= 0x00000400; } else { ensureDefaultOutputModesIsMutable(); defaultOutputModes_.addAll(other.defaultOutputModes_); @@ -1691,7 +1586,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (!other.skills_.isEmpty()) { if (skills_.isEmpty()) { skills_ = other.skills_; - bitField0_ = (bitField0_ & ~0x00001000); + bitField0_ = (bitField0_ & ~0x00000800); } else { ensureSkillsIsMutable(); skills_.addAll(other.skills_); @@ -1704,7 +1599,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { skillsBuilder_.dispose(); skillsBuilder_ = null; skills_ = other.skills_; - bitField0_ = (bitField0_ & ~0x00001000); + bitField0_ = (bitField0_ & ~0x00000800); skillsBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetSkillsFieldBuilder() : null; @@ -1717,7 +1612,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { if (!other.signatures_.isEmpty()) { if (signatures_.isEmpty()) { signatures_ = other.signatures_; - bitField0_ = (bitField0_ & ~0x00002000); + bitField0_ = (bitField0_ & ~0x00001000); } else { ensureSignaturesIsMutable(); signatures_.addAll(other.signatures_); @@ -1730,7 +1625,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { signaturesBuilder_.dispose(); signaturesBuilder_ = null; signatures_ = other.signatures_; - bitField0_ = (bitField0_ & ~0x00002000); + bitField0_ = (bitField0_ & ~0x00001000); signaturesBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? internalGetSignaturesFieldBuilder() : null; @@ -1741,7 +1636,7 @@ public Builder mergeFrom(io.a2a.grpc.AgentCard other) { } if (other.hasIconUrl()) { iconUrl_ = other.iconUrl_; - bitField0_ |= 0x00004000; + bitField0_ |= 0x00002000; onChanged(); } this.mergeUnknownFields(other.getUnknownFields()); @@ -1772,36 +1667,36 @@ public Builder mergeFrom( break; case 10: { name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000001; break; } // case 10 case 18: { description_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000002; break; } // case 18 case 34: { input.readMessage( internalGetProviderFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000008; break; } // case 34 case 42: { version_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000020; + bitField0_ |= 0x00000010; break; } // case 42 case 50: { documentationUrl_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000040; + bitField0_ |= 0x00000020; break; } // case 50 case 58: { input.readMessage( internalGetCapabilitiesFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000080; + bitField0_ |= 0x00000040; break; } // case 58 case 66: { @@ -1810,22 +1705,9 @@ public Builder mergeFrom( SecuritySchemesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); internalGetMutableSecuritySchemes().ensureBuilderMap().put( securitySchemes__.getKey(), securitySchemes__.getValue()); - bitField0_ |= 0x00000100; + bitField0_ |= 0x00000080; break; } // case 66 - case 74: { - io.a2a.grpc.Security m = - input.readMessage( - io.a2a.grpc.Security.parser(), - extensionRegistry); - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.add(m); - } else { - securityBuilder_.addMessage(m); - } - break; - } // case 74 case 82: { java.lang.String s = input.readStringRequireUtf8(); ensureDefaultInputModesIsMutable(); @@ -1851,12 +1733,19 @@ public Builder mergeFrom( } break; } // case 98 - case 130: { - java.lang.String s = input.readStringRequireUtf8(); - ensureProtocolVersionsIsMutable(); - protocolVersions_.add(s); + case 106: { + io.a2a.grpc.SecurityRequirement m = + input.readMessage( + io.a2a.grpc.SecurityRequirement.parser(), + extensionRegistry); + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(m); + } else { + securityRequirementsBuilder_.addMessage(m); + } break; - } // case 130 + } // case 106 case 138: { io.a2a.grpc.AgentCardSignature m = input.readMessage( @@ -1872,7 +1761,7 @@ public Builder mergeFrom( } // case 138 case 146: { iconUrl_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00004000; + bitField0_ |= 0x00002000; break; } // case 146 case 154: { @@ -1905,180 +1794,6 @@ public Builder mergeFrom( } private int bitField0_; - private com.google.protobuf.LazyStringArrayList protocolVersions_ = - com.google.protobuf.LazyStringArrayList.emptyList(); - private void ensureProtocolVersionsIsMutable() { - if (!protocolVersions_.isModifiable()) { - protocolVersions_ = new com.google.protobuf.LazyStringArrayList(protocolVersions_); - } - bitField0_ |= 0x00000001; - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return A list containing the protocolVersions. - */ - public com.google.protobuf.ProtocolStringList - getProtocolVersionsList() { - protocolVersions_.makeImmutable(); - return protocolVersions_; - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The count of protocolVersions. - */ - public int getProtocolVersionsCount() { - return protocolVersions_.size(); - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the element to return. - * @return The protocolVersions at the given index. - */ - public java.lang.String getProtocolVersions(int index) { - return protocolVersions_.get(index); - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the value to return. - * @return The bytes of the protocolVersions at the given index. - */ - public com.google.protobuf.ByteString - getProtocolVersionsBytes(int index) { - return protocolVersions_.getByteString(index); - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index to set the value at. - * @param value The protocolVersions to set. - * @return This builder for chaining. - */ - public Builder setProtocolVersions( - int index, java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - ensureProtocolVersionsIsMutable(); - protocolVersions_.set(index, value); - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param value The protocolVersions to add. - * @return This builder for chaining. - */ - public Builder addProtocolVersions( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - ensureProtocolVersionsIsMutable(); - protocolVersions_.add(value); - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param values The protocolVersions to add. - * @return This builder for chaining. - */ - public Builder addAllProtocolVersions( - java.lang.Iterable values) { - ensureProtocolVersionsIsMutable(); - com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, protocolVersions_); - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return This builder for chaining. - */ - public Builder clearProtocolVersions() { - protocolVersions_ = - com.google.protobuf.LazyStringArrayList.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001);; - onChanged(); - return this; - } - /** - *
    -     * The versions of the A2A protocol this agent supports.
    -     * For stable versions (1.x+), list only the latest supported minor version per major version.
    -     * For legacy experimental versions (0.x), explicitly list each supported version.
    -     * Default: ["1.0"]
    -     * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes of the protocolVersions to add. - * @return This builder for chaining. - */ - public Builder addProtocolVersionsBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - ensureProtocolVersionsIsMutable(); - protocolVersions_.add(value); - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - private java.lang.Object name_ = ""; /** *
    @@ -2137,7 +1852,7 @@ public Builder setName(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           name_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000001;
           onChanged();
           return this;
         }
    @@ -2152,7 +1867,7 @@ public Builder setName(
          */
         public Builder clearName() {
           name_ = getDefaultInstance().getName();
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000001);
           onChanged();
           return this;
         }
    @@ -2171,7 +1886,7 @@ public Builder setNameBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           name_ = value;
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000001;
           onChanged();
           return this;
         }
    @@ -2237,7 +1952,7 @@ public Builder setDescription(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           description_ = value;
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -2253,7 +1968,7 @@ public Builder setDescription(
          */
         public Builder clearDescription() {
           description_ = getDefaultInstance().getDescription();
    -      bitField0_ = (bitField0_ & ~0x00000004);
    +      bitField0_ = (bitField0_ & ~0x00000002);
           onChanged();
           return this;
         }
    @@ -2273,7 +1988,7 @@ public Builder setDescriptionBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           description_ = value;
    -      bitField0_ |= 0x00000004;
    +      bitField0_ |= 0x00000002;
           onChanged();
           return this;
         }
    @@ -2281,9 +1996,9 @@ public Builder setDescriptionBytes(
         private java.util.List supportedInterfaces_ =
           java.util.Collections.emptyList();
         private void ensureSupportedInterfacesIsMutable() {
    -      if (!((bitField0_ & 0x00000008) != 0)) {
    +      if (!((bitField0_ & 0x00000004) != 0)) {
             supportedInterfaces_ = new java.util.ArrayList(supportedInterfaces_);
    -        bitField0_ |= 0x00000008;
    +        bitField0_ |= 0x00000004;
            }
         }
     
    @@ -2477,7 +2192,7 @@ public Builder addAllSupportedInterfaces(
         public Builder clearSupportedInterfaces() {
           if (supportedInterfacesBuilder_ == null) {
             supportedInterfaces_ = java.util.Collections.emptyList();
    -        bitField0_ = (bitField0_ & ~0x00000008);
    +        bitField0_ = (bitField0_ & ~0x00000004);
             onChanged();
           } else {
             supportedInterfacesBuilder_.clear();
    @@ -2582,7 +2297,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder(
             supportedInterfacesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
                 io.a2a.grpc.AgentInterface, io.a2a.grpc.AgentInterface.Builder, io.a2a.grpc.AgentInterfaceOrBuilder>(
                     supportedInterfaces_,
    -                ((bitField0_ & 0x00000008) != 0),
    +                ((bitField0_ & 0x00000004) != 0),
                     getParentForChildren(),
                     isClean());
             supportedInterfaces_ = null;
    @@ -2602,7 +2317,7 @@ public io.a2a.grpc.AgentInterface.Builder addSupportedInterfacesBuilder(
          * @return Whether the provider field is set.
          */
         public boolean hasProvider() {
    -      return ((bitField0_ & 0x00000010) != 0);
    +      return ((bitField0_ & 0x00000008) != 0);
         }
         /**
          * 
    @@ -2635,7 +2350,7 @@ public Builder setProvider(io.a2a.grpc.AgentProvider value) {
           } else {
             providerBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -2653,7 +2368,7 @@ public Builder setProvider(
           } else {
             providerBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -2666,7 +2381,7 @@ public Builder setProvider(
          */
         public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
           if (providerBuilder_ == null) {
    -        if (((bitField0_ & 0x00000010) != 0) &&
    +        if (((bitField0_ & 0x00000008) != 0) &&
               provider_ != null &&
               provider_ != io.a2a.grpc.AgentProvider.getDefaultInstance()) {
               getProviderBuilder().mergeFrom(value);
    @@ -2677,7 +2392,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
             providerBuilder_.mergeFrom(value);
           }
           if (provider_ != null) {
    -        bitField0_ |= 0x00000010;
    +        bitField0_ |= 0x00000008;
             onChanged();
           }
           return this;
    @@ -2690,7 +2405,7 @@ public Builder mergeProvider(io.a2a.grpc.AgentProvider value) {
          * .a2a.v1.AgentProvider provider = 4;
          */
         public Builder clearProvider() {
    -      bitField0_ = (bitField0_ & ~0x00000010);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           provider_ = null;
           if (providerBuilder_ != null) {
             providerBuilder_.dispose();
    @@ -2707,7 +2422,7 @@ public Builder clearProvider() {
          * .a2a.v1.AgentProvider provider = 4;
          */
         public io.a2a.grpc.AgentProvider.Builder getProviderBuilder() {
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return internalGetProviderFieldBuilder().getBuilder();
         }
    @@ -2805,7 +2520,7 @@ public Builder setVersion(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           version_ = value;
    -      bitField0_ |= 0x00000020;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return this;
         }
    @@ -2820,7 +2535,7 @@ public Builder setVersion(
          */
         public Builder clearVersion() {
           version_ = getDefaultInstance().getVersion();
    -      bitField0_ = (bitField0_ & ~0x00000020);
    +      bitField0_ = (bitField0_ & ~0x00000010);
           onChanged();
           return this;
         }
    @@ -2839,7 +2554,7 @@ public Builder setVersionBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           version_ = value;
    -      bitField0_ |= 0x00000020;
    +      bitField0_ |= 0x00000010;
           onChanged();
           return this;
         }
    @@ -2854,7 +2569,7 @@ public Builder setVersionBytes(
          * @return Whether the documentationUrl field is set.
          */
         public boolean hasDocumentationUrl() {
    -      return ((bitField0_ & 0x00000040) != 0);
    +      return ((bitField0_ & 0x00000020) != 0);
         }
         /**
          * 
    @@ -2910,7 +2625,7 @@ public Builder setDocumentationUrl(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           documentationUrl_ = value;
    -      bitField0_ |= 0x00000040;
    +      bitField0_ |= 0x00000020;
           onChanged();
           return this;
         }
    @@ -2924,7 +2639,7 @@ public Builder setDocumentationUrl(
          */
         public Builder clearDocumentationUrl() {
           documentationUrl_ = getDefaultInstance().getDocumentationUrl();
    -      bitField0_ = (bitField0_ & ~0x00000040);
    +      bitField0_ = (bitField0_ & ~0x00000020);
           onChanged();
           return this;
         }
    @@ -2942,7 +2657,7 @@ public Builder setDocumentationUrlBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           documentationUrl_ = value;
    -      bitField0_ |= 0x00000040;
    +      bitField0_ |= 0x00000020;
           onChanged();
           return this;
         }
    @@ -2959,7 +2674,7 @@ public Builder setDocumentationUrlBytes(
          * @return Whether the capabilities field is set.
          */
         public boolean hasCapabilities() {
    -      return ((bitField0_ & 0x00000080) != 0);
    +      return ((bitField0_ & 0x00000040) != 0);
         }
         /**
          * 
    @@ -2992,7 +2707,7 @@ public Builder setCapabilities(io.a2a.grpc.AgentCapabilities value) {
           } else {
             capabilitiesBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000080;
    +      bitField0_ |= 0x00000040;
           onChanged();
           return this;
         }
    @@ -3010,7 +2725,7 @@ public Builder setCapabilities(
           } else {
             capabilitiesBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000080;
    +      bitField0_ |= 0x00000040;
           onChanged();
           return this;
         }
    @@ -3023,7 +2738,7 @@ public Builder setCapabilities(
          */
         public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) {
           if (capabilitiesBuilder_ == null) {
    -        if (((bitField0_ & 0x00000080) != 0) &&
    +        if (((bitField0_ & 0x00000040) != 0) &&
               capabilities_ != null &&
               capabilities_ != io.a2a.grpc.AgentCapabilities.getDefaultInstance()) {
               getCapabilitiesBuilder().mergeFrom(value);
    @@ -3034,7 +2749,7 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) {
             capabilitiesBuilder_.mergeFrom(value);
           }
           if (capabilities_ != null) {
    -        bitField0_ |= 0x00000080;
    +        bitField0_ |= 0x00000040;
             onChanged();
           }
           return this;
    @@ -3047,7 +2762,7 @@ public Builder mergeCapabilities(io.a2a.grpc.AgentCapabilities value) {
          * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED];
          */
         public Builder clearCapabilities() {
    -      bitField0_ = (bitField0_ & ~0x00000080);
    +      bitField0_ = (bitField0_ & ~0x00000040);
           capabilities_ = null;
           if (capabilitiesBuilder_ != null) {
             capabilitiesBuilder_.dispose();
    @@ -3064,7 +2779,7 @@ public Builder clearCapabilities() {
          * .a2a.v1.AgentCapabilities capabilities = 7 [(.google.api.field_behavior) = REQUIRED];
          */
         public io.a2a.grpc.AgentCapabilities.Builder getCapabilitiesBuilder() {
    -      bitField0_ |= 0x00000080;
    +      bitField0_ |= 0x00000040;
           onChanged();
           return internalGetCapabilitiesFieldBuilder().getBuilder();
         }
    @@ -3132,7 +2847,7 @@ public com.google.protobuf.MapEntry(securitySchemesConverter);
           }
    -      bitField0_ |= 0x00000100;
    +      bitField0_ |= 0x00000080;
           onChanged();
           return securitySchemes_;
         }
    @@ -3206,7 +2921,7 @@ public io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow(
           return securitySchemesConverter.build(map.get(key));
         }
         public Builder clearSecuritySchemes() {
    -      bitField0_ = (bitField0_ & ~0x00000100);
    +      bitField0_ = (bitField0_ & ~0x00000080);
           internalGetMutableSecuritySchemes().clear();
           return this;
         }
    @@ -3230,7 +2945,7 @@ public Builder removeSecuritySchemes(
         @java.lang.Deprecated
         public java.util.Map
             getMutableSecuritySchemes() {
    -      bitField0_ |= 0x00000100;
    +      bitField0_ |= 0x00000080;
           return internalGetMutableSecuritySchemes().ensureMessageMap();
         }
         /**
    @@ -3247,7 +2962,7 @@ public Builder putSecuritySchemes(
           if (value == null) { throw new NullPointerException("map value"); }
           internalGetMutableSecuritySchemes().ensureBuilderMap()
               .put(key, value);
    -      bitField0_ |= 0x00000100;
    +      bitField0_ |= 0x00000080;
           return this;
         }
         /**
    @@ -3266,7 +2981,7 @@ public Builder putAllSecuritySchemes(
           }
           internalGetMutableSecuritySchemes().ensureBuilderMap()
               .putAll(values);
    -      bitField0_ |= 0x00000100;
    +      bitField0_ |= 0x00000080;
           return this;
         }
         /**
    @@ -3291,334 +3006,316 @@ public io.a2a.grpc.SecurityScheme.Builder putSecuritySchemesBuilderIfAbsent(
           return (io.a2a.grpc.SecurityScheme.Builder) entry;
         }
     
    -    private java.util.List security_ =
    +    private java.util.List securityRequirements_ =
           java.util.Collections.emptyList();
    -    private void ensureSecurityIsMutable() {
    -      if (!((bitField0_ & 0x00000200) != 0)) {
    -        security_ = new java.util.ArrayList(security_);
    -        bitField0_ |= 0x00000200;
    +    private void ensureSecurityRequirementsIsMutable() {
    +      if (!((bitField0_ & 0x00000100) != 0)) {
    +        securityRequirements_ = new java.util.ArrayList(securityRequirements_);
    +        bitField0_ |= 0x00000100;
            }
         }
     
         private com.google.protobuf.RepeatedFieldBuilder<
    -        io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> securityBuilder_;
    +        io.a2a.grpc.SecurityRequirement, io.a2a.grpc.SecurityRequirement.Builder, io.a2a.grpc.SecurityRequirementOrBuilder> securityRequirementsBuilder_;
     
         /**
          * 
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public java.util.List getSecurityList() { - if (securityBuilder_ == null) { - return java.util.Collections.unmodifiableList(security_); + public java.util.List getSecurityRequirementsList() { + if (securityRequirementsBuilder_ == null) { + return java.util.Collections.unmodifiableList(securityRequirements_); } else { - return securityBuilder_.getMessageList(); + return securityRequirementsBuilder_.getMessageList(); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public int getSecurityCount() { - if (securityBuilder_ == null) { - return security_.size(); + public int getSecurityRequirementsCount() { + if (securityRequirementsBuilder_ == null) { + return securityRequirements_.size(); } else { - return securityBuilder_.getCount(); + return securityRequirementsBuilder_.getCount(); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public io.a2a.grpc.Security getSecurity(int index) { - if (securityBuilder_ == null) { - return security_.get(index); + public io.a2a.grpc.SecurityRequirement getSecurityRequirements(int index) { + if (securityRequirementsBuilder_ == null) { + return securityRequirements_.get(index); } else { - return securityBuilder_.getMessage(index); + return securityRequirementsBuilder_.getMessage(index); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder setSecurity( - int index, io.a2a.grpc.Security value) { - if (securityBuilder_ == null) { + public Builder setSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement value) { + if (securityRequirementsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureSecurityIsMutable(); - security_.set(index, value); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.set(index, value); onChanged(); } else { - securityBuilder_.setMessage(index, value); + securityRequirementsBuilder_.setMessage(index, value); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder setSecurity( - int index, io.a2a.grpc.Security.Builder builderForValue) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.set(index, builderForValue.build()); + public Builder setSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement.Builder builderForValue) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.set(index, builderForValue.build()); onChanged(); } else { - securityBuilder_.setMessage(index, builderForValue.build()); + securityRequirementsBuilder_.setMessage(index, builderForValue.build()); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder addSecurity(io.a2a.grpc.Security value) { - if (securityBuilder_ == null) { + public Builder addSecurityRequirements(io.a2a.grpc.SecurityRequirement value) { + if (securityRequirementsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureSecurityIsMutable(); - security_.add(value); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(value); onChanged(); } else { - securityBuilder_.addMessage(value); + securityRequirementsBuilder_.addMessage(value); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder addSecurity( - int index, io.a2a.grpc.Security value) { - if (securityBuilder_ == null) { + public Builder addSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement value) { + if (securityRequirementsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureSecurityIsMutable(); - security_.add(index, value); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(index, value); onChanged(); } else { - securityBuilder_.addMessage(index, value); + securityRequirementsBuilder_.addMessage(index, value); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder addSecurity( - io.a2a.grpc.Security.Builder builderForValue) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.add(builderForValue.build()); + public Builder addSecurityRequirements( + io.a2a.grpc.SecurityRequirement.Builder builderForValue) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(builderForValue.build()); onChanged(); } else { - securityBuilder_.addMessage(builderForValue.build()); + securityRequirementsBuilder_.addMessage(builderForValue.build()); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder addSecurity( - int index, io.a2a.grpc.Security.Builder builderForValue) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.add(index, builderForValue.build()); + public Builder addSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement.Builder builderForValue) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(index, builderForValue.build()); onChanged(); } else { - securityBuilder_.addMessage(index, builderForValue.build()); + securityRequirementsBuilder_.addMessage(index, builderForValue.build()); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder addAllSecurity( - java.lang.Iterable values) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); + public Builder addAllSecurityRequirements( + java.lang.Iterable values) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, security_); + values, securityRequirements_); onChanged(); } else { - securityBuilder_.addAllMessages(values); + securityRequirementsBuilder_.addAllMessages(values); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder clearSecurity() { - if (securityBuilder_ == null) { - security_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000200); + public Builder clearSecurityRequirements() { + if (securityRequirementsBuilder_ == null) { + securityRequirements_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000100); onChanged(); } else { - securityBuilder_.clear(); + securityRequirementsBuilder_.clear(); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public Builder removeSecurity(int index) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.remove(index); + public Builder removeSecurityRequirements(int index) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.remove(index); onChanged(); } else { - securityBuilder_.remove(index); + securityRequirementsBuilder_.remove(index); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public io.a2a.grpc.Security.Builder getSecurityBuilder( + public io.a2a.grpc.SecurityRequirement.Builder getSecurityRequirementsBuilder( int index) { - return internalGetSecurityFieldBuilder().getBuilder(index); + return internalGetSecurityRequirementsFieldBuilder().getBuilder(index); } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + public io.a2a.grpc.SecurityRequirementOrBuilder getSecurityRequirementsOrBuilder( int index) { - if (securityBuilder_ == null) { - return security_.get(index); } else { - return securityBuilder_.getMessageOrBuilder(index); + if (securityRequirementsBuilder_ == null) { + return securityRequirements_.get(index); } else { + return securityRequirementsBuilder_.getMessageOrBuilder(index); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public java.util.List - getSecurityOrBuilderList() { - if (securityBuilder_ != null) { - return securityBuilder_.getMessageOrBuilderList(); + public java.util.List + getSecurityRequirementsOrBuilderList() { + if (securityRequirementsBuilder_ != null) { + return securityRequirementsBuilder_.getMessageOrBuilderList(); } else { - return java.util.Collections.unmodifiableList(security_); + return java.util.Collections.unmodifiableList(securityRequirements_); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public io.a2a.grpc.Security.Builder addSecurityBuilder() { - return internalGetSecurityFieldBuilder().addBuilder( - io.a2a.grpc.Security.getDefaultInstance()); + public io.a2a.grpc.SecurityRequirement.Builder addSecurityRequirementsBuilder() { + return internalGetSecurityRequirementsFieldBuilder().addBuilder( + io.a2a.grpc.SecurityRequirement.getDefaultInstance()); } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public io.a2a.grpc.Security.Builder addSecurityBuilder( + public io.a2a.grpc.SecurityRequirement.Builder addSecurityRequirementsBuilder( int index) { - return internalGetSecurityFieldBuilder().addBuilder( - index, io.a2a.grpc.Security.getDefaultInstance()); + return internalGetSecurityRequirementsFieldBuilder().addBuilder( + index, io.a2a.grpc.SecurityRequirement.getDefaultInstance()); } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security requirements for contacting the agent.
          * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - public java.util.List - getSecurityBuilderList() { - return internalGetSecurityFieldBuilder().getBuilderList(); + public java.util.List + getSecurityRequirementsBuilderList() { + return internalGetSecurityRequirementsFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> - internalGetSecurityFieldBuilder() { - if (securityBuilder_ == null) { - securityBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder>( - security_, - ((bitField0_ & 0x00000200) != 0), + io.a2a.grpc.SecurityRequirement, io.a2a.grpc.SecurityRequirement.Builder, io.a2a.grpc.SecurityRequirementOrBuilder> + internalGetSecurityRequirementsFieldBuilder() { + if (securityRequirementsBuilder_ == null) { + securityRequirementsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.SecurityRequirement, io.a2a.grpc.SecurityRequirement.Builder, io.a2a.grpc.SecurityRequirementOrBuilder>( + securityRequirements_, + ((bitField0_ & 0x00000100) != 0), getParentForChildren(), isClean()); - security_ = null; + securityRequirements_ = null; } - return securityBuilder_; + return securityRequirementsBuilder_; } private com.google.protobuf.LazyStringArrayList defaultInputModes_ = @@ -3627,7 +3324,7 @@ private void ensureDefaultInputModesIsMutable() { if (!defaultInputModes_.isModifiable()) { defaultInputModes_ = new com.google.protobuf.LazyStringArrayList(defaultInputModes_); } - bitField0_ |= 0x00000400; + bitField0_ |= 0x00000200; } /** *
    @@ -3703,7 +3400,7 @@ public Builder setDefaultInputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultInputModesIsMutable();
           defaultInputModes_.set(index, value);
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000200;
           onChanged();
           return this;
         }
    @@ -3723,7 +3420,7 @@ public Builder addDefaultInputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultInputModesIsMutable();
           defaultInputModes_.add(value);
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000200;
           onChanged();
           return this;
         }
    @@ -3743,7 +3440,7 @@ public Builder addAllDefaultInputModes(
           ensureDefaultInputModesIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
               values, defaultInputModes_);
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000200;
           onChanged();
           return this;
         }
    @@ -3760,7 +3457,7 @@ public Builder addAllDefaultInputModes(
         public Builder clearDefaultInputModes() {
           defaultInputModes_ =
             com.google.protobuf.LazyStringArrayList.emptyList();
    -      bitField0_ = (bitField0_ & ~0x00000400);;
    +      bitField0_ = (bitField0_ & ~0x00000200);;
           onChanged();
           return this;
         }
    @@ -3781,7 +3478,7 @@ public Builder addDefaultInputModesBytes(
           checkByteStringIsUtf8(value);
           ensureDefaultInputModesIsMutable();
           defaultInputModes_.add(value);
    -      bitField0_ |= 0x00000400;
    +      bitField0_ |= 0x00000200;
           onChanged();
           return this;
         }
    @@ -3792,7 +3489,7 @@ private void ensureDefaultOutputModesIsMutable() {
           if (!defaultOutputModes_.isModifiable()) {
             defaultOutputModes_ = new com.google.protobuf.LazyStringArrayList(defaultOutputModes_);
           }
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000400;
         }
         /**
          * 
    @@ -3858,7 +3555,7 @@ public Builder setDefaultOutputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultOutputModesIsMutable();
           defaultOutputModes_.set(index, value);
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -3876,7 +3573,7 @@ public Builder addDefaultOutputModes(
           if (value == null) { throw new NullPointerException(); }
           ensureDefaultOutputModesIsMutable();
           defaultOutputModes_.add(value);
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -3894,7 +3591,7 @@ public Builder addAllDefaultOutputModes(
           ensureDefaultOutputModesIsMutable();
           com.google.protobuf.AbstractMessageLite.Builder.addAll(
               values, defaultOutputModes_);
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -3909,7 +3606,7 @@ public Builder addAllDefaultOutputModes(
         public Builder clearDefaultOutputModes() {
           defaultOutputModes_ =
             com.google.protobuf.LazyStringArrayList.emptyList();
    -      bitField0_ = (bitField0_ & ~0x00000800);;
    +      bitField0_ = (bitField0_ & ~0x00000400);;
           onChanged();
           return this;
         }
    @@ -3928,7 +3625,7 @@ public Builder addDefaultOutputModesBytes(
           checkByteStringIsUtf8(value);
           ensureDefaultOutputModesIsMutable();
           defaultOutputModes_.add(value);
    -      bitField0_ |= 0x00000800;
    +      bitField0_ |= 0x00000400;
           onChanged();
           return this;
         }
    @@ -3936,9 +3633,9 @@ public Builder addDefaultOutputModesBytes(
         private java.util.List skills_ =
           java.util.Collections.emptyList();
         private void ensureSkillsIsMutable() {
    -      if (!((bitField0_ & 0x00001000) != 0)) {
    +      if (!((bitField0_ & 0x00000800) != 0)) {
             skills_ = new java.util.ArrayList(skills_);
    -        bitField0_ |= 0x00001000;
    +        bitField0_ |= 0x00000800;
            }
         }
     
    @@ -4154,7 +3851,7 @@ public Builder addAllSkills(
         public Builder clearSkills() {
           if (skillsBuilder_ == null) {
             skills_ = java.util.Collections.emptyList();
    -        bitField0_ = (bitField0_ & ~0x00001000);
    +        bitField0_ = (bitField0_ & ~0x00000800);
             onChanged();
           } else {
             skillsBuilder_.clear();
    @@ -4273,7 +3970,7 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder(
             skillsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
                 io.a2a.grpc.AgentSkill, io.a2a.grpc.AgentSkill.Builder, io.a2a.grpc.AgentSkillOrBuilder>(
                     skills_,
    -                ((bitField0_ & 0x00001000) != 0),
    +                ((bitField0_ & 0x00000800) != 0),
                     getParentForChildren(),
                     isClean());
             skills_ = null;
    @@ -4284,9 +3981,9 @@ public io.a2a.grpc.AgentSkill.Builder addSkillsBuilder(
         private java.util.List signatures_ =
           java.util.Collections.emptyList();
         private void ensureSignaturesIsMutable() {
    -      if (!((bitField0_ & 0x00002000) != 0)) {
    +      if (!((bitField0_ & 0x00001000) != 0)) {
             signatures_ = new java.util.ArrayList(signatures_);
    -        bitField0_ |= 0x00002000;
    +        bitField0_ |= 0x00001000;
            }
         }
     
    @@ -4480,7 +4177,7 @@ public Builder addAllSignatures(
         public Builder clearSignatures() {
           if (signaturesBuilder_ == null) {
             signatures_ = java.util.Collections.emptyList();
    -        bitField0_ = (bitField0_ & ~0x00002000);
    +        bitField0_ = (bitField0_ & ~0x00001000);
             onChanged();
           } else {
             signaturesBuilder_.clear();
    @@ -4585,7 +4282,7 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder(
             signaturesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
                 io.a2a.grpc.AgentCardSignature, io.a2a.grpc.AgentCardSignature.Builder, io.a2a.grpc.AgentCardSignatureOrBuilder>(
                     signatures_,
    -                ((bitField0_ & 0x00002000) != 0),
    +                ((bitField0_ & 0x00001000) != 0),
                     getParentForChildren(),
                     isClean());
             signatures_ = null;
    @@ -4603,7 +4300,7 @@ public io.a2a.grpc.AgentCardSignature.Builder addSignaturesBuilder(
          * @return Whether the iconUrl field is set.
          */
         public boolean hasIconUrl() {
    -      return ((bitField0_ & 0x00004000) != 0);
    +      return ((bitField0_ & 0x00002000) != 0);
         }
         /**
          * 
    @@ -4659,7 +4356,7 @@ public Builder setIconUrl(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
           iconUrl_ = value;
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00002000;
           onChanged();
           return this;
         }
    @@ -4673,7 +4370,7 @@ public Builder setIconUrl(
          */
         public Builder clearIconUrl() {
           iconUrl_ = getDefaultInstance().getIconUrl();
    -      bitField0_ = (bitField0_ & ~0x00004000);
    +      bitField0_ = (bitField0_ & ~0x00002000);
           onChanged();
           return this;
         }
    @@ -4691,7 +4388,7 @@ public Builder setIconUrlBytes(
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
           iconUrl_ = value;
    -      bitField0_ |= 0x00004000;
    +      bitField0_ |= 0x00002000;
           onChanged();
           return this;
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java
    index 8c31e3053..379ec0282 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentCardOrBuilder.java
    @@ -10,59 +10,6 @@ public interface AgentCardOrBuilder extends
         // @@protoc_insertion_point(interface_extends:a2a.v1.AgentCard)
         com.google.protobuf.MessageOrBuilder {
     
    -  /**
    -   * 
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return A list containing the protocolVersions. - */ - java.util.List - getProtocolVersionsList(); - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @return The count of protocolVersions. - */ - int getProtocolVersionsCount(); - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the element to return. - * @return The protocolVersions at the given index. - */ - java.lang.String getProtocolVersions(int index); - /** - *
    -   * The versions of the A2A protocol this agent supports.
    -   * For stable versions (1.x+), list only the latest supported minor version per major version.
    -   * For legacy experimental versions (0.x), explicitly list each supported version.
    -   * Default: ["1.0"]
    -   * 
    - * - * repeated string protocol_versions = 16 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the value to return. - * @return The bytes of the protocolVersions at the given index. - */ - com.google.protobuf.ByteString - getProtocolVersionsBytes(int index); - /** *
        * A human readable name for the agent.
    @@ -314,51 +261,46 @@ io.a2a.grpc.SecurityScheme getSecuritySchemesOrThrow(
     
       /**
        * 
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - java.util.List - getSecurityList(); + java.util.List + getSecurityRequirementsList(); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - io.a2a.grpc.Security getSecurity(int index); + io.a2a.grpc.SecurityRequirement getSecurityRequirements(int index); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - int getSecurityCount(); + int getSecurityRequirementsCount(); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - java.util.List - getSecurityOrBuilderList(); + java.util.List + getSecurityRequirementsOrBuilderList(); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security requirements for contacting the agent.
        * 
    * - * repeated .a2a.v1.Security security = 9; + * repeated .a2a.v1.SecurityRequirement security_requirements = 13; */ - io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + io.a2a.grpc.SecurityRequirementOrBuilder getSecurityRequirementsOrBuilder( int index); /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java index 17db01b94..ac8001a8e 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterface.java @@ -8,7 +8,7 @@ /** *
      * --8<-- [start:AgentInterface]
    - * Declares a combination of a target URL and a transport protocol for interacting with the agent.
    + * Declares a combination of a target URL, transport and protocol version for interacting with the agent.
      * This allows agents to expose the same functionality over multiple protocol binding mechanisms.
      * 
    * @@ -37,6 +37,7 @@ private AgentInterface() { url_ = ""; protocolBinding_ = ""; tenant_ = ""; + protocolVersion_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -199,6 +200,57 @@ public java.lang.String getTenant() { } } + public static final int PROTOCOL_VERSION_FIELD_NUMBER = 4; + @SuppressWarnings("serial") + private volatile java.lang.Object protocolVersion_ = ""; + /** + *
    +   * The version of the A2A protocol this interface exposes.
    +   * Use the latest supported minor version per major version.
    +   * Examples: "0.3", "1.0"
    +   * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return The protocolVersion. + */ + @java.lang.Override + public java.lang.String getProtocolVersion() { + java.lang.Object ref = protocolVersion_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protocolVersion_ = s; + return s; + } + } + /** + *
    +   * The version of the A2A protocol this interface exposes.
    +   * Use the latest supported minor version per major version.
    +   * Examples: "0.3", "1.0"
    +   * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protocolVersion. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getProtocolVersionBytes() { + java.lang.Object ref = protocolVersion_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + protocolVersion_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -222,6 +274,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { com.google.protobuf.GeneratedMessage.writeString(output, 3, tenant_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, protocolVersion_); + } getUnknownFields().writeTo(output); } @@ -240,6 +295,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(3, tenant_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(protocolVersion_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, protocolVersion_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -261,6 +319,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getProtocolBinding())) return false; if (!getTenant() .equals(other.getTenant())) return false; + if (!getProtocolVersion() + .equals(other.getProtocolVersion())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -278,6 +338,8 @@ public int hashCode() { hash = (53 * hash) + getProtocolBinding().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); + hash = (37 * hash) + PROTOCOL_VERSION_FIELD_NUMBER; + hash = (53 * hash) + getProtocolVersion().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -378,7 +440,7 @@ protected Builder newBuilderForType( /** *
        * --8<-- [start:AgentInterface]
    -   * Declares a combination of a target URL and a transport protocol for interacting with the agent.
    +   * Declares a combination of a target URL, transport and protocol version for interacting with the agent.
        * This allows agents to expose the same functionality over multiple protocol binding mechanisms.
        * 
    * @@ -418,6 +480,7 @@ public Builder clear() { url_ = ""; protocolBinding_ = ""; tenant_ = ""; + protocolVersion_ = ""; return this; } @@ -460,6 +523,9 @@ private void buildPartial0(io.a2a.grpc.AgentInterface result) { if (((from_bitField0_ & 0x00000004) != 0)) { result.tenant_ = tenant_; } + if (((from_bitField0_ & 0x00000008) != 0)) { + result.protocolVersion_ = protocolVersion_; + } } @java.lang.Override @@ -489,6 +555,11 @@ public Builder mergeFrom(io.a2a.grpc.AgentInterface other) { bitField0_ |= 0x00000004; onChanged(); } + if (!other.getProtocolVersion().isEmpty()) { + protocolVersion_ = other.protocolVersion_; + bitField0_ |= 0x00000008; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -530,6 +601,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000004; break; } // case 26 + case 34: { + protocolVersion_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000008; + break; + } // case 34 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -838,6 +914,108 @@ public Builder setTenantBytes( return this; } + private java.lang.Object protocolVersion_ = ""; + /** + *
    +     * The version of the A2A protocol this interface exposes.
    +     * Use the latest supported minor version per major version.
    +     * Examples: "0.3", "1.0"
    +     * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return The protocolVersion. + */ + public java.lang.String getProtocolVersion() { + java.lang.Object ref = protocolVersion_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + protocolVersion_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The version of the A2A protocol this interface exposes.
    +     * Use the latest supported minor version per major version.
    +     * Examples: "0.3", "1.0"
    +     * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protocolVersion. + */ + public com.google.protobuf.ByteString + getProtocolVersionBytes() { + java.lang.Object ref = protocolVersion_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + protocolVersion_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The version of the A2A protocol this interface exposes.
    +     * Use the latest supported minor version per major version.
    +     * Examples: "0.3", "1.0"
    +     * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @param value The protocolVersion to set. + * @return This builder for chaining. + */ + public Builder setProtocolVersion( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + protocolVersion_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + /** + *
    +     * The version of the A2A protocol this interface exposes.
    +     * Use the latest supported minor version per major version.
    +     * Examples: "0.3", "1.0"
    +     * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearProtocolVersion() { + protocolVersion_ = getDefaultInstance().getProtocolVersion(); + bitField0_ = (bitField0_ & ~0x00000008); + onChanged(); + return this; + } + /** + *
    +     * The version of the A2A protocol this interface exposes.
    +     * Use the latest supported minor version per major version.
    +     * Examples: "0.3", "1.0"
    +     * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for protocolVersion to set. + * @return This builder for chaining. + */ + public Builder setProtocolVersionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + protocolVersion_ = value; + bitField0_ |= 0x00000008; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.AgentInterface) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java index cfc8b7ad9..3dfe76f41 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentInterfaceOrBuilder.java @@ -75,4 +75,28 @@ public interface AgentInterfaceOrBuilder extends */ com.google.protobuf.ByteString getTenantBytes(); + + /** + *
    +   * The version of the A2A protocol this interface exposes.
    +   * Use the latest supported minor version per major version.
    +   * Examples: "0.3", "1.0"
    +   * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return The protocolVersion. + */ + java.lang.String getProtocolVersion(); + /** + *
    +   * The version of the A2A protocol this interface exposes.
    +   * Use the latest supported minor version per major version.
    +   * Examples: "0.3", "1.0"
    +   * 
    + * + * string protocol_version = 4 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for protocolVersion. + */ + com.google.protobuf.ByteString + getProtocolVersionBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java index 19596fe14..087596019 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkill.java @@ -44,7 +44,7 @@ private AgentSkill() { com.google.protobuf.LazyStringArrayList.emptyList(); outputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); - security_ = java.util.Collections.emptyList(); + securityRequirements_ = java.util.Collections.emptyList(); } public static final com.google.protobuf.Descriptors.Descriptor @@ -413,70 +413,65 @@ public java.lang.String getOutputModes(int index) { return outputModes_.getByteString(index); } - public static final int SECURITY_FIELD_NUMBER = 8; + public static final int SECURITY_REQUIREMENTS_FIELD_NUMBER = 8; @SuppressWarnings("serial") - private java.util.List security_; + private java.util.List securityRequirements_; /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ @java.lang.Override - public java.util.List getSecurityList() { - return security_; + public java.util.List getSecurityRequirementsList() { + return securityRequirements_; } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ @java.lang.Override - public java.util.List - getSecurityOrBuilderList() { - return security_; + public java.util.List + getSecurityRequirementsOrBuilderList() { + return securityRequirements_; } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ @java.lang.Override - public int getSecurityCount() { - return security_.size(); + public int getSecurityRequirementsCount() { + return securityRequirements_.size(); } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ @java.lang.Override - public io.a2a.grpc.Security getSecurity(int index) { - return security_.get(index); + public io.a2a.grpc.SecurityRequirement getSecurityRequirements(int index) { + return securityRequirements_.get(index); } /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ @java.lang.Override - public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + public io.a2a.grpc.SecurityRequirementOrBuilder getSecurityRequirementsOrBuilder( int index) { - return security_.get(index); + return securityRequirements_.get(index); } private byte memoizedIsInitialized = -1; @@ -514,8 +509,8 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < outputModes_.size(); i++) { com.google.protobuf.GeneratedMessage.writeString(output, 7, outputModes_.getRaw(i)); } - for (int i = 0; i < security_.size(); i++) { - output.writeMessage(8, security_.get(i)); + for (int i = 0; i < securityRequirements_.size(); i++) { + output.writeMessage(8, securityRequirements_.get(i)); } getUnknownFields().writeTo(output); } @@ -567,9 +562,9 @@ public int getSerializedSize() { size += dataSize; size += 1 * getOutputModesList().size(); } - for (int i = 0; i < security_.size(); i++) { + for (int i = 0; i < securityRequirements_.size(); i++) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, security_.get(i)); + .computeMessageSize(8, securityRequirements_.get(i)); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -600,8 +595,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getInputModesList())) return false; if (!getOutputModesList() .equals(other.getOutputModesList())) return false; - if (!getSecurityList() - .equals(other.getSecurityList())) return false; + if (!getSecurityRequirementsList() + .equals(other.getSecurityRequirementsList())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -635,9 +630,9 @@ public int hashCode() { hash = (37 * hash) + OUTPUT_MODES_FIELD_NUMBER; hash = (53 * hash) + getOutputModesList().hashCode(); } - if (getSecurityCount() > 0) { - hash = (37 * hash) + SECURITY_FIELD_NUMBER; - hash = (53 * hash) + getSecurityList().hashCode(); + if (getSecurityRequirementsCount() > 0) { + hash = (37 * hash) + SECURITY_REQUIREMENTS_FIELD_NUMBER; + hash = (53 * hash) + getSecurityRequirementsList().hashCode(); } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; @@ -786,11 +781,11 @@ public Builder clear() { com.google.protobuf.LazyStringArrayList.emptyList(); outputModes_ = com.google.protobuf.LazyStringArrayList.emptyList(); - if (securityBuilder_ == null) { - security_ = java.util.Collections.emptyList(); + if (securityRequirementsBuilder_ == null) { + securityRequirements_ = java.util.Collections.emptyList(); } else { - security_ = null; - securityBuilder_.clear(); + securityRequirements_ = null; + securityRequirementsBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00000080); return this; @@ -826,14 +821,14 @@ public io.a2a.grpc.AgentSkill buildPartial() { } private void buildPartialRepeatedFields(io.a2a.grpc.AgentSkill result) { - if (securityBuilder_ == null) { + if (securityRequirementsBuilder_ == null) { if (((bitField0_ & 0x00000080) != 0)) { - security_ = java.util.Collections.unmodifiableList(security_); + securityRequirements_ = java.util.Collections.unmodifiableList(securityRequirements_); bitField0_ = (bitField0_ & ~0x00000080); } - result.security_ = security_; + result.securityRequirements_ = securityRequirements_; } else { - result.security_ = securityBuilder_.build(); + result.securityRequirements_ = securityRequirementsBuilder_.build(); } } @@ -933,29 +928,29 @@ public Builder mergeFrom(io.a2a.grpc.AgentSkill other) { } onChanged(); } - if (securityBuilder_ == null) { - if (!other.security_.isEmpty()) { - if (security_.isEmpty()) { - security_ = other.security_; + if (securityRequirementsBuilder_ == null) { + if (!other.securityRequirements_.isEmpty()) { + if (securityRequirements_.isEmpty()) { + securityRequirements_ = other.securityRequirements_; bitField0_ = (bitField0_ & ~0x00000080); } else { - ensureSecurityIsMutable(); - security_.addAll(other.security_); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.addAll(other.securityRequirements_); } onChanged(); } } else { - if (!other.security_.isEmpty()) { - if (securityBuilder_.isEmpty()) { - securityBuilder_.dispose(); - securityBuilder_ = null; - security_ = other.security_; + if (!other.securityRequirements_.isEmpty()) { + if (securityRequirementsBuilder_.isEmpty()) { + securityRequirementsBuilder_.dispose(); + securityRequirementsBuilder_ = null; + securityRequirements_ = other.securityRequirements_; bitField0_ = (bitField0_ & ~0x00000080); - securityBuilder_ = + securityRequirementsBuilder_ = com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - internalGetSecurityFieldBuilder() : null; + internalGetSecurityRequirementsFieldBuilder() : null; } else { - securityBuilder_.addAllMessages(other.security_); + securityRequirementsBuilder_.addAllMessages(other.securityRequirements_); } } } @@ -1025,15 +1020,15 @@ public Builder mergeFrom( break; } // case 58 case 66: { - io.a2a.grpc.Security m = + io.a2a.grpc.SecurityRequirement m = input.readMessage( - io.a2a.grpc.Security.parser(), + io.a2a.grpc.SecurityRequirement.parser(), extensionRegistry); - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.add(m); + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(m); } else { - securityBuilder_.addMessage(m); + securityRequirementsBuilder_.addMessage(m); } break; } // case 66 @@ -1918,334 +1913,316 @@ public Builder addOutputModesBytes( return this; } - private java.util.List security_ = + private java.util.List securityRequirements_ = java.util.Collections.emptyList(); - private void ensureSecurityIsMutable() { + private void ensureSecurityRequirementsIsMutable() { if (!((bitField0_ & 0x00000080) != 0)) { - security_ = new java.util.ArrayList(security_); + securityRequirements_ = new java.util.ArrayList(securityRequirements_); bitField0_ |= 0x00000080; } } private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> securityBuilder_; + io.a2a.grpc.SecurityRequirement, io.a2a.grpc.SecurityRequirement.Builder, io.a2a.grpc.SecurityRequirementOrBuilder> securityRequirementsBuilder_; /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public java.util.List getSecurityList() { - if (securityBuilder_ == null) { - return java.util.Collections.unmodifiableList(security_); + public java.util.List getSecurityRequirementsList() { + if (securityRequirementsBuilder_ == null) { + return java.util.Collections.unmodifiableList(securityRequirements_); } else { - return securityBuilder_.getMessageList(); + return securityRequirementsBuilder_.getMessageList(); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public int getSecurityCount() { - if (securityBuilder_ == null) { - return security_.size(); + public int getSecurityRequirementsCount() { + if (securityRequirementsBuilder_ == null) { + return securityRequirements_.size(); } else { - return securityBuilder_.getCount(); + return securityRequirementsBuilder_.getCount(); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public io.a2a.grpc.Security getSecurity(int index) { - if (securityBuilder_ == null) { - return security_.get(index); + public io.a2a.grpc.SecurityRequirement getSecurityRequirements(int index) { + if (securityRequirementsBuilder_ == null) { + return securityRequirements_.get(index); } else { - return securityBuilder_.getMessage(index); + return securityRequirementsBuilder_.getMessage(index); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder setSecurity( - int index, io.a2a.grpc.Security value) { - if (securityBuilder_ == null) { + public Builder setSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement value) { + if (securityRequirementsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureSecurityIsMutable(); - security_.set(index, value); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.set(index, value); onChanged(); } else { - securityBuilder_.setMessage(index, value); + securityRequirementsBuilder_.setMessage(index, value); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder setSecurity( - int index, io.a2a.grpc.Security.Builder builderForValue) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.set(index, builderForValue.build()); + public Builder setSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement.Builder builderForValue) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.set(index, builderForValue.build()); onChanged(); } else { - securityBuilder_.setMessage(index, builderForValue.build()); + securityRequirementsBuilder_.setMessage(index, builderForValue.build()); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder addSecurity(io.a2a.grpc.Security value) { - if (securityBuilder_ == null) { + public Builder addSecurityRequirements(io.a2a.grpc.SecurityRequirement value) { + if (securityRequirementsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureSecurityIsMutable(); - security_.add(value); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(value); onChanged(); } else { - securityBuilder_.addMessage(value); + securityRequirementsBuilder_.addMessage(value); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder addSecurity( - int index, io.a2a.grpc.Security value) { - if (securityBuilder_ == null) { + public Builder addSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement value) { + if (securityRequirementsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureSecurityIsMutable(); - security_.add(index, value); + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(index, value); onChanged(); } else { - securityBuilder_.addMessage(index, value); + securityRequirementsBuilder_.addMessage(index, value); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder addSecurity( - io.a2a.grpc.Security.Builder builderForValue) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.add(builderForValue.build()); + public Builder addSecurityRequirements( + io.a2a.grpc.SecurityRequirement.Builder builderForValue) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(builderForValue.build()); onChanged(); } else { - securityBuilder_.addMessage(builderForValue.build()); + securityRequirementsBuilder_.addMessage(builderForValue.build()); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder addSecurity( - int index, io.a2a.grpc.Security.Builder builderForValue) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.add(index, builderForValue.build()); + public Builder addSecurityRequirements( + int index, io.a2a.grpc.SecurityRequirement.Builder builderForValue) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.add(index, builderForValue.build()); onChanged(); } else { - securityBuilder_.addMessage(index, builderForValue.build()); + securityRequirementsBuilder_.addMessage(index, builderForValue.build()); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder addAllSecurity( - java.lang.Iterable values) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); + public Builder addAllSecurityRequirements( + java.lang.Iterable values) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, security_); + values, securityRequirements_); onChanged(); } else { - securityBuilder_.addAllMessages(values); + securityRequirementsBuilder_.addAllMessages(values); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder clearSecurity() { - if (securityBuilder_ == null) { - security_ = java.util.Collections.emptyList(); + public Builder clearSecurityRequirements() { + if (securityRequirementsBuilder_ == null) { + securityRequirements_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000080); onChanged(); } else { - securityBuilder_.clear(); + securityRequirementsBuilder_.clear(); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public Builder removeSecurity(int index) { - if (securityBuilder_ == null) { - ensureSecurityIsMutable(); - security_.remove(index); + public Builder removeSecurityRequirements(int index) { + if (securityRequirementsBuilder_ == null) { + ensureSecurityRequirementsIsMutable(); + securityRequirements_.remove(index); onChanged(); } else { - securityBuilder_.remove(index); + securityRequirementsBuilder_.remove(index); } return this; } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public io.a2a.grpc.Security.Builder getSecurityBuilder( + public io.a2a.grpc.SecurityRequirement.Builder getSecurityRequirementsBuilder( int index) { - return internalGetSecurityFieldBuilder().getBuilder(index); + return internalGetSecurityRequirementsFieldBuilder().getBuilder(index); } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + public io.a2a.grpc.SecurityRequirementOrBuilder getSecurityRequirementsOrBuilder( int index) { - if (securityBuilder_ == null) { - return security_.get(index); } else { - return securityBuilder_.getMessageOrBuilder(index); + if (securityRequirementsBuilder_ == null) { + return securityRequirements_.get(index); } else { + return securityRequirementsBuilder_.getMessageOrBuilder(index); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public java.util.List - getSecurityOrBuilderList() { - if (securityBuilder_ != null) { - return securityBuilder_.getMessageOrBuilderList(); + public java.util.List + getSecurityRequirementsOrBuilderList() { + if (securityRequirementsBuilder_ != null) { + return securityRequirementsBuilder_.getMessageOrBuilderList(); } else { - return java.util.Collections.unmodifiableList(security_); + return java.util.Collections.unmodifiableList(securityRequirements_); } } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public io.a2a.grpc.Security.Builder addSecurityBuilder() { - return internalGetSecurityFieldBuilder().addBuilder( - io.a2a.grpc.Security.getDefaultInstance()); + public io.a2a.grpc.SecurityRequirement.Builder addSecurityRequirementsBuilder() { + return internalGetSecurityRequirementsFieldBuilder().addBuilder( + io.a2a.grpc.SecurityRequirement.getDefaultInstance()); } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public io.a2a.grpc.Security.Builder addSecurityBuilder( + public io.a2a.grpc.SecurityRequirement.Builder addSecurityRequirementsBuilder( int index) { - return internalGetSecurityFieldBuilder().addBuilder( - index, io.a2a.grpc.Security.getDefaultInstance()); + return internalGetSecurityRequirementsFieldBuilder().addBuilder( + index, io.a2a.grpc.SecurityRequirement.getDefaultInstance()); } /** *
    -     * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
          * Security schemes necessary for this skill.
          * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - public java.util.List - getSecurityBuilderList() { - return internalGetSecurityFieldBuilder().getBuilderList(); + public java.util.List + getSecurityRequirementsBuilderList() { + return internalGetSecurityRequirementsFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder> - internalGetSecurityFieldBuilder() { - if (securityBuilder_ == null) { - securityBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - io.a2a.grpc.Security, io.a2a.grpc.Security.Builder, io.a2a.grpc.SecurityOrBuilder>( - security_, + io.a2a.grpc.SecurityRequirement, io.a2a.grpc.SecurityRequirement.Builder, io.a2a.grpc.SecurityRequirementOrBuilder> + internalGetSecurityRequirementsFieldBuilder() { + if (securityRequirementsBuilder_ == null) { + securityRequirementsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + io.a2a.grpc.SecurityRequirement, io.a2a.grpc.SecurityRequirement.Builder, io.a2a.grpc.SecurityRequirementOrBuilder>( + securityRequirements_, ((bitField0_ & 0x00000080) != 0), getParentForChildren(), isClean()); - security_ = null; + securityRequirements_ = null; } - return securityBuilder_; + return securityRequirementsBuilder_; } // @@protoc_insertion_point(builder_scope:a2a.v1.AgentSkill) diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java index c9615febc..8cb65a6c5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AgentSkillOrBuilder.java @@ -236,50 +236,45 @@ public interface AgentSkillOrBuilder extends /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - java.util.List - getSecurityList(); + java.util.List + getSecurityRequirementsList(); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - io.a2a.grpc.Security getSecurity(int index); + io.a2a.grpc.SecurityRequirement getSecurityRequirements(int index); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - int getSecurityCount(); + int getSecurityRequirementsCount(); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - java.util.List - getSecurityOrBuilderList(); + java.util.List + getSecurityRequirementsOrBuilderList(); /** *
    -   * protolint:disable REPEATED_FIELD_NAMES_PLURALIZED
        * Security schemes necessary for this skill.
        * 
    * - * repeated .a2a.v1.Security security = 8; + * repeated .a2a.v1.SecurityRequirement security_requirements = 8; */ - io.a2a.grpc.SecurityOrBuilder getSecurityOrBuilder( + io.a2a.grpc.SecurityRequirementOrBuilder getSecurityRequirementsOrBuilder( int index); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java index 38b9e065c..703a6da52 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfo.java @@ -33,8 +33,7 @@ private AuthenticationInfo(com.google.protobuf.GeneratedMessage.Builder build super(builder); } private AuthenticationInfo() { - schemes_ = - com.google.protobuf.LazyStringArrayList.emptyList(); + scheme_ = ""; credentials_ = ""; } @@ -51,57 +50,53 @@ private AuthenticationInfo() { io.a2a.grpc.AuthenticationInfo.class, io.a2a.grpc.AuthenticationInfo.Builder.class); } - public static final int SCHEMES_FIELD_NUMBER = 1; + public static final int SCHEME_FIELD_NUMBER = 1; @SuppressWarnings("serial") - private com.google.protobuf.LazyStringArrayList schemes_ = - com.google.protobuf.LazyStringArrayList.emptyList(); + private volatile java.lang.Object scheme_ = ""; /** *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +   * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +   * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
        * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return A list containing the schemes. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The scheme. */ - public com.google.protobuf.ProtocolStringList - getSchemesList() { - return schemes_; - } - /** - *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -   * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The count of schemes. - */ - public int getSchemesCount() { - return schemes_.size(); - } - /** - *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -   * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the element to return. - * @return The schemes at the given index. - */ - public java.lang.String getSchemes(int index) { - return schemes_.get(index); + @java.lang.Override + public java.lang.String getScheme() { + java.lang.Object ref = scheme_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + scheme_ = s; + return s; + } } /** *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +   * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +   * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
        * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the value to return. - * @return The bytes of the schemes at the given index. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for scheme. */ + @java.lang.Override public com.google.protobuf.ByteString - getSchemesBytes(int index) { - return schemes_.getByteString(index); + getSchemeBytes() { + java.lang.Object ref = scheme_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scheme_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } } public static final int CREDENTIALS_FIELD_NUMBER = 2; @@ -109,7 +104,7 @@ public java.lang.String getSchemes(int index) { private volatile java.lang.Object credentials_ = ""; /** *
    -   * Optional credentials
    +   * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
        * 
    * * string credentials = 2; @@ -130,7 +125,7 @@ public java.lang.String getCredentials() { } /** *
    -   * Optional credentials
    +   * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
        * 
    * * string credentials = 2; @@ -165,8 +160,8 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - for (int i = 0; i < schemes_.size(); i++) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, schemes_.getRaw(i)); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(scheme_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, scheme_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(credentials_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, credentials_); @@ -180,13 +175,8 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - { - int dataSize = 0; - for (int i = 0; i < schemes_.size(); i++) { - dataSize += computeStringSizeNoTag(schemes_.getRaw(i)); - } - size += dataSize; - size += 1 * getSchemesList().size(); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(scheme_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, scheme_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(credentials_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, credentials_); @@ -206,8 +196,8 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.AuthenticationInfo other = (io.a2a.grpc.AuthenticationInfo) obj; - if (!getSchemesList() - .equals(other.getSchemesList())) return false; + if (!getScheme() + .equals(other.getScheme())) return false; if (!getCredentials() .equals(other.getCredentials())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; @@ -221,10 +211,8 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); - if (getSchemesCount() > 0) { - hash = (37 * hash) + SCHEMES_FIELD_NUMBER; - hash = (53 * hash) + getSchemesList().hashCode(); - } + hash = (37 * hash) + SCHEME_FIELD_NUMBER; + hash = (53 * hash) + getScheme().hashCode(); hash = (37 * hash) + CREDENTIALS_FIELD_NUMBER; hash = (53 * hash) + getCredentials().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); @@ -363,8 +351,7 @@ private Builder( public Builder clear() { super.clear(); bitField0_ = 0; - schemes_ = - com.google.protobuf.LazyStringArrayList.emptyList(); + scheme_ = ""; credentials_ = ""; return this; } @@ -400,8 +387,7 @@ public io.a2a.grpc.AuthenticationInfo buildPartial() { private void buildPartial0(io.a2a.grpc.AuthenticationInfo result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - schemes_.makeImmutable(); - result.schemes_ = schemes_; + result.scheme_ = scheme_; } if (((from_bitField0_ & 0x00000002) != 0)) { result.credentials_ = credentials_; @@ -420,14 +406,9 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.AuthenticationInfo other) { if (other == io.a2a.grpc.AuthenticationInfo.getDefaultInstance()) return this; - if (!other.schemes_.isEmpty()) { - if (schemes_.isEmpty()) { - schemes_ = other.schemes_; - bitField0_ |= 0x00000001; - } else { - ensureSchemesIsMutable(); - schemes_.addAll(other.schemes_); - } + if (!other.getScheme().isEmpty()) { + scheme_ = other.scheme_; + bitField0_ |= 0x00000001; onChanged(); } if (!other.getCredentials().isEmpty()) { @@ -462,9 +443,8 @@ public Builder mergeFrom( done = true; break; case 10: { - java.lang.String s = input.readStringRequireUtf8(); - ensureSchemesIsMutable(); - schemes_.add(s); + scheme_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; break; } // case 10 case 18: { @@ -489,148 +469,98 @@ public Builder mergeFrom( } private int bitField0_; - private com.google.protobuf.LazyStringArrayList schemes_ = - com.google.protobuf.LazyStringArrayList.emptyList(); - private void ensureSchemesIsMutable() { - if (!schemes_.isModifiable()) { - schemes_ = new com.google.protobuf.LazyStringArrayList(schemes_); - } - bitField0_ |= 0x00000001; - } - /** - *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -     * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return A list containing the schemes. - */ - public com.google.protobuf.ProtocolStringList - getSchemesList() { - schemes_.makeImmutable(); - return schemes_; - } - /** - *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -     * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The count of schemes. - */ - public int getSchemesCount() { - return schemes_.size(); - } + private java.lang.Object scheme_ = ""; /** *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +     * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +     * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
          * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the element to return. - * @return The schemes at the given index. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The scheme. */ - public java.lang.String getSchemes(int index) { - return schemes_.get(index); + public java.lang.String getScheme() { + java.lang.Object ref = scheme_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + scheme_ = s; + return s; + } else { + return (java.lang.String) ref; + } } /** *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +     * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +     * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
          * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the value to return. - * @return The bytes of the schemes at the given index. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for scheme. */ public com.google.protobuf.ByteString - getSchemesBytes(int index) { - return schemes_.getByteString(index); - } - /** - *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -     * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index to set the value at. - * @param value The schemes to set. - * @return This builder for chaining. - */ - public Builder setSchemes( - int index, java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - ensureSchemesIsMutable(); - schemes_.set(index, value); - bitField0_ |= 0x00000001; - onChanged(); - return this; + getSchemeBytes() { + java.lang.Object ref = scheme_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scheme_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } } /** *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +     * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +     * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
          * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The schemes to add. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The scheme to set. * @return This builder for chaining. */ - public Builder addSchemes( + public Builder setScheme( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - ensureSchemesIsMutable(); - schemes_.add(value); - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -     * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param values The schemes to add. - * @return This builder for chaining. - */ - public Builder addAllSchemes( - java.lang.Iterable values) { - ensureSchemesIsMutable(); - com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, schemes_); + scheme_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +     * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +     * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
          * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearSchemes() { - schemes_ = - com.google.protobuf.LazyStringArrayList.emptyList(); - bitField0_ = (bitField0_ & ~0x00000001);; + public Builder clearScheme() { + scheme_ = getDefaultInstance().getScheme(); + bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
    -     * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +     * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +     * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
          * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes of the schemes to add. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for scheme to set. * @return This builder for chaining. */ - public Builder addSchemesBytes( + public Builder setSchemeBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - ensureSchemesIsMutable(); - schemes_.add(value); + scheme_ = value; bitField0_ |= 0x00000001; onChanged(); return this; @@ -639,7 +569,7 @@ public Builder addSchemesBytes( private java.lang.Object credentials_ = ""; /** *
    -     * Optional credentials
    +     * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
          * 
    * * string credentials = 2; @@ -659,7 +589,7 @@ public java.lang.String getCredentials() { } /** *
    -     * Optional credentials
    +     * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
          * 
    * * string credentials = 2; @@ -680,7 +610,7 @@ public java.lang.String getCredentials() { } /** *
    -     * Optional credentials
    +     * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
          * 
    * * string credentials = 2; @@ -697,7 +627,7 @@ public Builder setCredentials( } /** *
    -     * Optional credentials
    +     * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
          * 
    * * string credentials = 2; @@ -711,7 +641,7 @@ public Builder clearCredentials() { } /** *
    -     * Optional credentials
    +     * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
          * 
    * * string credentials = 2; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java index 37c96ed29..b142c3670 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/AuthenticationInfoOrBuilder.java @@ -12,48 +12,29 @@ public interface AuthenticationInfoOrBuilder extends /** *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +   * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +   * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
        * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return A list containing the schemes. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The scheme. */ - java.util.List - getSchemesList(); + java.lang.String getScheme(); /** *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    +   * HTTP Authentication Scheme from the [IANA registry](https://www.iana.org/assignments/http-authschemes/).
    +   * Common values: `Bearer`, `Basic`, `Digest`. Scheme names are case-insensitive per [RFC 9110 Section 11.1](https://www.rfc-editor.org/rfc/rfc9110#section-11.1).
        * 
    * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The count of schemes. - */ - int getSchemesCount(); - /** - *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -   * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the element to return. - * @return The schemes at the given index. - */ - java.lang.String getSchemes(int index); - /** - *
    -   * A list of supported authentication schemes (e.g., 'Basic', 'Bearer').
    -   * 
    - * - * repeated string schemes = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param index The index of the value to return. - * @return The bytes of the schemes at the given index. + * string scheme = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for scheme. */ com.google.protobuf.ByteString - getSchemesBytes(int index); + getSchemeBytes(); /** *
    -   * Optional credentials
    +   * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
        * 
    * * string credentials = 2; @@ -62,7 +43,7 @@ public interface AuthenticationInfoOrBuilder extends java.lang.String getCredentials(); /** *
    -   * Optional credentials
    +   * Push Notification credentials. Format depends on the scheme (e.g., token for Bearer).
        * 
    * * string credentials = 2; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java index cb1e12880..3aa57435f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequest.java @@ -34,7 +34,7 @@ private CancelTaskRequest(com.google.protobuf.GeneratedMessage.Builder builde } private CancelTaskRequest() { tenant_ = ""; - name_ = ""; + id_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -97,49 +97,47 @@ public java.lang.String getTenant() { } } - public static final int NAME_FIELD_NUMBER = 1; + public static final int ID_FIELD_NUMBER = 1; @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; + private volatile java.lang.Object id_ = ""; /** *
    -   * The resource name of the task to cancel.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to cancel.
        * 
    * - * string name = 1; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getId() { + java.lang.Object ref = id_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + id_ = s; return s; } } /** *
    -   * The resource name of the task to cancel.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to cancel.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ @java.lang.Override public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getIdBytes() { + java.lang.Object ref = id_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -160,8 +158,8 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); @@ -175,8 +173,8 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); @@ -198,8 +196,8 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getName() - .equals(other.getName())) return false; + if (!getId() + .equals(other.getId())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -213,8 +211,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -352,7 +350,7 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - name_ = ""; + id_ = ""; return this; } @@ -390,7 +388,7 @@ private void buildPartial0(io.a2a.grpc.CancelTaskRequest result) { result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.name_ = name_; + result.id_ = id_; } } @@ -411,8 +409,8 @@ public Builder mergeFrom(io.a2a.grpc.CancelTaskRequest other) { bitField0_ |= 0x00000001; onChanged(); } - if (!other.getName().isEmpty()) { - name_ = other.name_; + if (!other.getId().isEmpty()) { + id_ = other.id_; bitField0_ |= 0x00000002; onChanged(); } @@ -443,7 +441,7 @@ public Builder mergeFrom( done = true; break; case 10: { - name_ = input.readStringRequireUtf8(); + id_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 10 @@ -561,23 +559,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object name_ = ""; + private java.lang.Object id_ = ""; /** *
    -     * The resource name of the task to cancel.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to cancel.
          * 
    * - * string name = 1; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getId() { + java.lang.Object ref = id_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + id_ = s; return s; } else { return (java.lang.String) ref; @@ -585,21 +582,20 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the task to cancel.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to cancel.
          * 
    * - * string name = 1; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getIdBytes() { + java.lang.Object ref = id_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -607,52 +603,49 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the task to cancel.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to cancel.
          * 
    * - * string name = 1; - * @param value The name to set. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The id to set. * @return This builder for chaining. */ - public Builder setName( + public Builder setId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - name_ = value; + id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The resource name of the task to cancel.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to cancel.
          * 
    * - * string name = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); + public Builder clearId() { + id_ = getDefaultInstance().getId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The resource name of the task to cancel.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to cancel.
          * 
    * - * string name = 1; - * @param value The bytes for name to set. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for id to set. * @return This builder for chaining. */ - public Builder setNameBytes( + public Builder setIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; + id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java index b8a5e4f11..1e3251328 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CancelTaskRequestOrBuilder.java @@ -32,23 +32,21 @@ public interface CancelTaskRequestOrBuilder extends /** *
    -   * The resource name of the task to cancel.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to cancel.
        * 
    * - * string name = 1; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ - java.lang.String getName(); + java.lang.String getId(); /** *
    -   * The resource name of the task to cancel.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to cancel.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ com.google.protobuf.ByteString - getNameBytes(); + getIdBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java index 5f8143cb3..f445a86d2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequest.java @@ -34,7 +34,7 @@ private CreateTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMes } private CreateTaskPushNotificationConfigRequest() { tenant_ = ""; - parent_ = ""; + taskId_ = ""; configId_ = ""; } @@ -99,49 +99,47 @@ public java.lang.String getTenant() { } } - public static final int PARENT_FIELD_NUMBER = 1; + public static final int TASK_ID_FIELD_NUMBER = 1; @SuppressWarnings("serial") - private volatile java.lang.Object parent_ = ""; + private volatile java.lang.Object taskId_ = ""; /** *
    -   * The parent task resource for this config.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ @java.lang.Override - public java.lang.String getParent() { - java.lang.Object ref = parent_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - parent_ = s; + taskId_ = s; return s; } } /** *
    -   * The parent task resource for this config.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ @java.lang.Override public com.google.protobuf.ByteString - getParentBytes() { - java.lang.Object ref = parent_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - parent_ = b; + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -195,14 +193,14 @@ public java.lang.String getConfigId() { } } - public static final int CONFIG_FIELD_NUMBER = 3; - private io.a2a.grpc.TaskPushNotificationConfig config_; + public static final int CONFIG_FIELD_NUMBER = 5; + private io.a2a.grpc.PushNotificationConfig config_; /** *
        * The configuration to create.
        * 
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ @java.lang.Override @@ -214,23 +212,23 @@ public boolean hasConfig() { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ @java.lang.Override - public io.a2a.grpc.TaskPushNotificationConfig getConfig() { - return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + public io.a2a.grpc.PushNotificationConfig getConfig() { + return config_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : config_; } /** *
        * The configuration to create.
        * 
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ @java.lang.Override - public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { - return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + public io.a2a.grpc.PushNotificationConfigOrBuilder getConfigOrBuilder() { + return config_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : config_; } private byte memoizedIsInitialized = -1; @@ -247,18 +245,18 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, parent_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, taskId_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(configId_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, configId_); } - if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(3, getConfig()); - } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { com.google.protobuf.GeneratedMessage.writeString(output, 4, tenant_); } + if (((bitField0_ & 0x00000001) != 0)) { + output.writeMessage(5, getConfig()); + } getUnknownFields().writeTo(output); } @@ -268,19 +266,19 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, parent_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, taskId_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(configId_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, configId_); } - if (((bitField0_ & 0x00000001) != 0)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, getConfig()); - } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(4, tenant_); } + if (((bitField0_ & 0x00000001) != 0)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, getConfig()); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -298,8 +296,8 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getParent() - .equals(other.getParent())) return false; + if (!getTaskId() + .equals(other.getTaskId())) return false; if (!getConfigId() .equals(other.getConfigId())) return false; if (hasConfig() != other.hasConfig()) return false; @@ -320,8 +318,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + PARENT_FIELD_NUMBER; - hash = (53 * hash) + getParent().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); hash = (37 * hash) + CONFIG_ID_FIELD_NUMBER; hash = (53 * hash) + getConfigId().hashCode(); if (hasConfig()) { @@ -471,7 +469,7 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - parent_ = ""; + taskId_ = ""; configId_ = ""; config_ = null; if (configBuilder_ != null) { @@ -515,7 +513,7 @@ private void buildPartial0(io.a2a.grpc.CreateTaskPushNotificationConfigRequest r result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.parent_ = parent_; + result.taskId_ = taskId_; } if (((from_bitField0_ & 0x00000004) != 0)) { result.configId_ = configId_; @@ -547,8 +545,8 @@ public Builder mergeFrom(io.a2a.grpc.CreateTaskPushNotificationConfigRequest oth bitField0_ |= 0x00000001; onChanged(); } - if (!other.getParent().isEmpty()) { - parent_ = other.parent_; + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; bitField0_ |= 0x00000002; onChanged(); } @@ -587,7 +585,7 @@ public Builder mergeFrom( done = true; break; case 10: { - parent_ = input.readStringRequireUtf8(); + taskId_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 10 @@ -596,18 +594,18 @@ public Builder mergeFrom( bitField0_ |= 0x00000004; break; } // case 18 - case 26: { - input.readMessage( - internalGetConfigFieldBuilder().getBuilder(), - extensionRegistry); - bitField0_ |= 0x00000008; - break; - } // case 26 case 34: { tenant_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000001; break; } // case 34 + case 42: { + input.readMessage( + internalGetConfigFieldBuilder().getBuilder(), + extensionRegistry); + bitField0_ |= 0x00000008; + break; + } // case 42 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -717,23 +715,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object parent_ = ""; + private java.lang.Object taskId_ = ""; /** *
    -     * The parent task resource for this config.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - public java.lang.String getParent() { - java.lang.Object ref = parent_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - parent_ = s; + taskId_ = s; return s; } else { return (java.lang.String) ref; @@ -741,21 +738,20 @@ public java.lang.String getParent() { } /** *
    -     * The parent task resource for this config.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ public com.google.protobuf.ByteString - getParentBytes() { - java.lang.Object ref = parent_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - parent_ = b; + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -763,52 +759,49 @@ public java.lang.String getParent() { } /** *
    -     * The parent task resource for this config.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The parent to set. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The taskId to set. * @return This builder for chaining. */ - public Builder setParent( + public Builder setTaskId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - parent_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The parent task resource for this config.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearParent() { - parent_ = getDefaultInstance().getParent(); + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The parent task resource for this config.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes for parent to set. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for taskId to set. * @return This builder for chaining. */ - public Builder setParentBytes( + public Builder setTaskIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - parent_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; @@ -906,15 +899,15 @@ public Builder setConfigIdBytes( return this; } - private io.a2a.grpc.TaskPushNotificationConfig config_; + private io.a2a.grpc.PushNotificationConfig config_; private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> configBuilder_; + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> configBuilder_; /** *
          * The configuration to create.
          * 
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ public boolean hasConfig() { @@ -925,12 +918,12 @@ public boolean hasConfig() { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ - public io.a2a.grpc.TaskPushNotificationConfig getConfig() { + public io.a2a.grpc.PushNotificationConfig getConfig() { if (configBuilder_ == null) { - return config_ == null ? io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + return config_ == null ? io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : config_; } else { return configBuilder_.getMessage(); } @@ -940,9 +933,9 @@ public io.a2a.grpc.TaskPushNotificationConfig getConfig() { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ - public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { + public Builder setConfig(io.a2a.grpc.PushNotificationConfig value) { if (configBuilder_ == null) { if (value == null) { throw new NullPointerException(); @@ -960,10 +953,10 @@ public Builder setConfig(io.a2a.grpc.TaskPushNotificationConfig value) { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder setConfig( - io.a2a.grpc.TaskPushNotificationConfig.Builder builderForValue) { + io.a2a.grpc.PushNotificationConfig.Builder builderForValue) { if (configBuilder_ == null) { config_ = builderForValue.build(); } else { @@ -978,13 +971,13 @@ public Builder setConfig( * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ - public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { + public Builder mergeConfig(io.a2a.grpc.PushNotificationConfig value) { if (configBuilder_ == null) { if (((bitField0_ & 0x00000008) != 0) && config_ != null && - config_ != io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()) { + config_ != io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) { getConfigBuilder().mergeFrom(value); } else { config_ = value; @@ -1003,7 +996,7 @@ public Builder mergeConfig(io.a2a.grpc.TaskPushNotificationConfig value) { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ public Builder clearConfig() { bitField0_ = (bitField0_ & ~0x00000008); @@ -1020,9 +1013,9 @@ public Builder clearConfig() { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ - public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { + public io.a2a.grpc.PushNotificationConfig.Builder getConfigBuilder() { bitField0_ |= 0x00000008; onChanged(); return internalGetConfigFieldBuilder().getBuilder(); @@ -1032,14 +1025,14 @@ public io.a2a.grpc.TaskPushNotificationConfig.Builder getConfigBuilder() { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ - public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { + public io.a2a.grpc.PushNotificationConfigOrBuilder getConfigOrBuilder() { if (configBuilder_ != null) { return configBuilder_.getMessageOrBuilder(); } else { return config_ == null ? - io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance() : config_; + io.a2a.grpc.PushNotificationConfig.getDefaultInstance() : config_; } } /** @@ -1047,14 +1040,14 @@ public io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder() { * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder> + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> internalGetConfigFieldBuilder() { if (configBuilder_ == null) { configBuilder_ = new com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.TaskPushNotificationConfig, io.a2a.grpc.TaskPushNotificationConfig.Builder, io.a2a.grpc.TaskPushNotificationConfigOrBuilder>( + io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder>( getConfig(), getParentForChildren(), isClean()); diff --git a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java index 536c31416..77a661f26 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/CreateTaskPushNotificationConfigRequestOrBuilder.java @@ -32,25 +32,23 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends /** *
    -   * The parent task resource for this config.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - java.lang.String getParent(); + java.lang.String getTaskId(); /** *
    -   * The parent task resource for this config.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ com.google.protobuf.ByteString - getParentBytes(); + getTaskIdBytes(); /** *
    @@ -77,7 +75,7 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends
        * The configuration to create.
        * 
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; * @return Whether the config field is set. */ boolean hasConfig(); @@ -86,16 +84,16 @@ public interface CreateTaskPushNotificationConfigRequestOrBuilder extends * The configuration to create. *
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; * @return The config. */ - io.a2a.grpc.TaskPushNotificationConfig getConfig(); + io.a2a.grpc.PushNotificationConfig getConfig(); /** *
        * The configuration to create.
        * 
    * - * .a2a.v1.TaskPushNotificationConfig config = 3 [(.google.api.field_behavior) = REQUIRED]; + * .a2a.v1.PushNotificationConfig config = 5 [(.google.api.field_behavior) = REQUIRED]; */ - io.a2a.grpc.TaskPushNotificationConfigOrBuilder getConfigOrBuilder(); + io.a2a.grpc.PushNotificationConfigOrBuilder getConfigOrBuilder(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java deleted file mode 100644 index 92d0a6e6e..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPart.java +++ /dev/null @@ -1,617 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// NO CHECKED-IN PROTOBUF GENCODE -// source: a2a.proto -// Protobuf Java Version: 4.33.1 - -package io.a2a.grpc; - -/** - *
    - * --8<-- [start:DataPart]
    - * DataPart represents a structured blob.
    - * 
    - * - * Protobuf type {@code a2a.v1.DataPart} - */ -@com.google.protobuf.Generated -public final class DataPart extends - com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.DataPart) - DataPartOrBuilder { -private static final long serialVersionUID = 0L; - static { - com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( - com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, - /* major= */ 4, - /* minor= */ 33, - /* patch= */ 1, - /* suffix= */ "", - "DataPart"); - } - // Use DataPart.newBuilder() to construct. - private DataPart(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - } - private DataPart() { - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.a2a.grpc.DataPart.class, io.a2a.grpc.DataPart.Builder.class); - } - - private int bitField0_; - public static final int DATA_FIELD_NUMBER = 1; - private com.google.protobuf.Struct data_; - /** - *
    -   * A JSON object containing arbitrary data.
    -   * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return Whether the data field is set. - */ - @java.lang.Override - public boolean hasData() { - return ((bitField0_ & 0x00000001) != 0); - } - /** - *
    -   * A JSON object containing arbitrary data.
    -   * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The data. - */ - @java.lang.Override - public com.google.protobuf.Struct getData() { - return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; - } - /** - *
    -   * A JSON object containing arbitrary data.
    -   * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - @java.lang.Override - public com.google.protobuf.StructOrBuilder getDataOrBuilder() { - return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(1, getData()); - } - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) != 0)) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(1, getData()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.a2a.grpc.DataPart)) { - return super.equals(obj); - } - io.a2a.grpc.DataPart other = (io.a2a.grpc.DataPart) obj; - - if (hasData() != other.hasData()) return false; - if (hasData()) { - if (!getData() - .equals(other.getData())) return false; - } - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - if (hasData()) { - hash = (37 * hash) + DATA_FIELD_NUMBER; - hash = (53 * hash) + getData().hashCode(); - } - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.a2a.grpc.DataPart parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.DataPart parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.DataPart parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.DataPart parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.DataPart parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.DataPart parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.DataPart parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input); - } - public static io.a2a.grpc.DataPart parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.a2a.grpc.DataPart parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.a2a.grpc.DataPart parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.a2a.grpc.DataPart parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input); - } - public static io.a2a.grpc.DataPart parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.a2a.grpc.DataPart prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - *
    -   * --8<-- [start:DataPart]
    -   * DataPart represents a structured blob.
    -   * 
    - * - * Protobuf type {@code a2a.v1.DataPart} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.DataPart) - io.a2a.grpc.DataPartOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.a2a.grpc.DataPart.class, io.a2a.grpc.DataPart.Builder.class); - } - - // Construct using io.a2a.grpc.DataPart.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage - .alwaysUseFieldBuilders) { - internalGetDataFieldBuilder(); - } - } - @java.lang.Override - public Builder clear() { - super.clear(); - bitField0_ = 0; - data_ = null; - if (dataBuilder_ != null) { - dataBuilder_.dispose(); - dataBuilder_ = null; - } - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_DataPart_descriptor; - } - - @java.lang.Override - public io.a2a.grpc.DataPart getDefaultInstanceForType() { - return io.a2a.grpc.DataPart.getDefaultInstance(); - } - - @java.lang.Override - public io.a2a.grpc.DataPart build() { - io.a2a.grpc.DataPart result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.a2a.grpc.DataPart buildPartial() { - io.a2a.grpc.DataPart result = new io.a2a.grpc.DataPart(this); - if (bitField0_ != 0) { buildPartial0(result); } - onBuilt(); - return result; - } - - private void buildPartial0(io.a2a.grpc.DataPart result) { - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) != 0)) { - result.data_ = dataBuilder_ == null - ? data_ - : dataBuilder_.build(); - to_bitField0_ |= 0x00000001; - } - result.bitField0_ |= to_bitField0_; - } - - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.DataPart) { - return mergeFrom((io.a2a.grpc.DataPart)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.a2a.grpc.DataPart other) { - if (other == io.a2a.grpc.DataPart.getDefaultInstance()) return this; - if (other.hasData()) { - mergeData(other.getData()); - } - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - case 10: { - input.readMessage( - internalGetDataFieldBuilder().getBuilder(), - extensionRegistry); - bitField0_ |= 0x00000001; - break; - } // case 10 - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - private int bitField0_; - - private com.google.protobuf.Struct data_; - private com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> dataBuilder_; - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return Whether the data field is set. - */ - public boolean hasData() { - return ((bitField0_ & 0x00000001) != 0); - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The data. - */ - public com.google.protobuf.Struct getData() { - if (dataBuilder_ == null) { - return data_ == null ? com.google.protobuf.Struct.getDefaultInstance() : data_; - } else { - return dataBuilder_.getMessage(); - } - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder setData(com.google.protobuf.Struct value) { - if (dataBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - data_ = value; - } else { - dataBuilder_.setMessage(value); - } - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder setData( - com.google.protobuf.Struct.Builder builderForValue) { - if (dataBuilder_ == null) { - data_ = builderForValue.build(); - } else { - dataBuilder_.setMessage(builderForValue.build()); - } - bitField0_ |= 0x00000001; - onChanged(); - return this; - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder mergeData(com.google.protobuf.Struct value) { - if (dataBuilder_ == null) { - if (((bitField0_ & 0x00000001) != 0) && - data_ != null && - data_ != com.google.protobuf.Struct.getDefaultInstance()) { - getDataBuilder().mergeFrom(value); - } else { - data_ = value; - } - } else { - dataBuilder_.mergeFrom(value); - } - if (data_ != null) { - bitField0_ |= 0x00000001; - onChanged(); - } - return this; - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - public Builder clearData() { - bitField0_ = (bitField0_ & ~0x00000001); - data_ = null; - if (dataBuilder_ != null) { - dataBuilder_.dispose(); - dataBuilder_ = null; - } - onChanged(); - return this; - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - public com.google.protobuf.Struct.Builder getDataBuilder() { - bitField0_ |= 0x00000001; - onChanged(); - return internalGetDataFieldBuilder().getBuilder(); - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - public com.google.protobuf.StructOrBuilder getDataOrBuilder() { - if (dataBuilder_ != null) { - return dataBuilder_.getMessageOrBuilder(); - } else { - return data_ == null ? - com.google.protobuf.Struct.getDefaultInstance() : data_; - } - } - /** - *
    -     * A JSON object containing arbitrary data.
    -     * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - private com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> - internalGetDataFieldBuilder() { - if (dataBuilder_ == null) { - dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< - com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder>( - getData(), - getParentForChildren(), - isClean()); - data_ = null; - } - return dataBuilder_; - } - - // @@protoc_insertion_point(builder_scope:a2a.v1.DataPart) - } - - // @@protoc_insertion_point(class_scope:a2a.v1.DataPart) - private static final io.a2a.grpc.DataPart DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.a2a.grpc.DataPart(); - } - - public static io.a2a.grpc.DataPart getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public DataPart parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.a2a.grpc.DataPart getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - -} - diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java deleted file mode 100644 index 4b61fc851..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/DataPartOrBuilder.java +++ /dev/null @@ -1,39 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// NO CHECKED-IN PROTOBUF GENCODE -// source: a2a.proto -// Protobuf Java Version: 4.33.1 - -package io.a2a.grpc; - -@com.google.protobuf.Generated -public interface DataPartOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.DataPart) - com.google.protobuf.MessageOrBuilder { - - /** - *
    -   * A JSON object containing arbitrary data.
    -   * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return Whether the data field is set. - */ - boolean hasData(); - /** - *
    -   * A JSON object containing arbitrary data.
    -   * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The data. - */ - com.google.protobuf.Struct getData(); - /** - *
    -   * A JSON object containing arbitrary data.
    -   * 
    - * - * .google.protobuf.Struct data = 1 [(.google.api.field_behavior) = REQUIRED]; - */ - com.google.protobuf.StructOrBuilder getDataOrBuilder(); -} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java index cb6914e0b..bb1ac53b5 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequest.java @@ -34,7 +34,8 @@ private DeleteTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMes } private DeleteTaskPushNotificationConfigRequest() { tenant_ = ""; - name_ = ""; + taskId_ = ""; + id_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -97,49 +98,94 @@ public java.lang.String getTenant() { } } - public static final int NAME_FIELD_NUMBER = 1; + public static final int TASK_ID_FIELD_NUMBER = 3; @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; + private volatile java.lang.Object taskId_ = ""; /** *
    -   * The resource name of the config to delete.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + taskId_ = s; return s; } } /** *
    -   * The resource name of the config to delete.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ @java.lang.Override public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + *
    +   * The resource id of the config to delete.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + *
    +   * The resource id of the config to delete.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -160,12 +206,15 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, taskId_); + } getUnknownFields().writeTo(output); } @@ -175,12 +224,15 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, taskId_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -198,8 +250,10 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getName() - .equals(other.getName())) return false; + if (!getTaskId() + .equals(other.getTaskId())) return false; + if (!getId() + .equals(other.getId())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -213,8 +267,10 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -352,7 +408,8 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - name_ = ""; + taskId_ = ""; + id_ = ""; return this; } @@ -390,7 +447,10 @@ private void buildPartial0(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest r result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.name_ = name_; + result.taskId_ = taskId_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.id_ = id_; } } @@ -411,11 +471,16 @@ public Builder mergeFrom(io.a2a.grpc.DeleteTaskPushNotificationConfigRequest oth bitField0_ |= 0x00000001; onChanged(); } - if (!other.getName().isEmpty()) { - name_ = other.name_; + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; bitField0_ |= 0x00000002; onChanged(); } + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000004; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -443,8 +508,8 @@ public Builder mergeFrom( done = true; break; case 10: { - name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000002; + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; break; } // case 10 case 18: { @@ -452,6 +517,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000001; break; } // case 18 + case 26: { + taskId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -561,23 +631,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object name_ = ""; + private java.lang.Object taskId_ = ""; /** *
    -     * The resource name of the config to delete.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @return The name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + taskId_ = s; return s; } else { return (java.lang.String) ref; @@ -585,21 +654,20 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the config to delete.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @return The bytes for name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -607,57 +675,146 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the config to delete.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @param value The name to set. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The taskId to set. * @return This builder for chaining. */ - public Builder setName( + public Builder setTaskId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - name_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The resource name of the config to delete.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The resource name of the config to delete.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @param value The bytes for name to set. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for taskId to set. * @return This builder for chaining. */ - public Builder setNameBytes( + public Builder setTaskIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } + private java.lang.Object id_ = ""; + /** + *
    +     * The resource id of the config to delete.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The resource id of the config to delete.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The resource id of the config to delete.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + id_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
    +     * The resource id of the config to delete.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
    +     * The resource id of the config to delete.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.DeleteTaskPushNotificationConfigRequest) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java index 336d7eba7..4219aea04 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/DeleteTaskPushNotificationConfigRequestOrBuilder.java @@ -32,23 +32,41 @@ public interface DeleteTaskPushNotificationConfigRequestOrBuilder extends /** *
    -   * The resource name of the config to delete.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - java.lang.String getName(); + java.lang.String getTaskId(); /** *
    -   * The resource name of the config to delete.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ com.google.protobuf.ByteString - getNameBytes(); + getTaskIdBytes(); + + /** + *
    +   * The resource id of the config to delete.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + java.lang.String getId(); + /** + *
    +   * The resource id of the config to delete.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + com.google.protobuf.ByteString + getIdBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java deleted file mode 100644 index 8d2526d62..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePart.java +++ /dev/null @@ -1,1109 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// NO CHECKED-IN PROTOBUF GENCODE -// source: a2a.proto -// Protobuf Java Version: 4.33.1 - -package io.a2a.grpc; - -/** - *
    - * --8<-- [start:FilePart]
    - * FilePart represents the different ways files can be provided. If files are
    - * small, directly feeding the bytes is supported via file_with_bytes. If the
    - * file is large, the agent should read the content as appropriate directly
    - * from the file_with_uri source.
    - * 
    - * - * Protobuf type {@code a2a.v1.FilePart} - */ -@com.google.protobuf.Generated -public final class FilePart extends - com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.FilePart) - FilePartOrBuilder { -private static final long serialVersionUID = 0L; - static { - com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( - com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, - /* major= */ 4, - /* minor= */ 33, - /* patch= */ 1, - /* suffix= */ "", - "FilePart"); - } - // Use FilePart.newBuilder() to construct. - private FilePart(com.google.protobuf.GeneratedMessage.Builder builder) { - super(builder); - } - private FilePart() { - mediaType_ = ""; - name_ = ""; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.a2a.grpc.FilePart.class, io.a2a.grpc.FilePart.Builder.class); - } - - private int fileCase_ = 0; - @SuppressWarnings("serial") - private java.lang.Object file_; - public enum FileCase - implements com.google.protobuf.Internal.EnumLite, - com.google.protobuf.AbstractMessage.InternalOneOfEnum { - FILE_WITH_URI(1), - FILE_WITH_BYTES(2), - FILE_NOT_SET(0); - private final int value; - private FileCase(int value) { - this.value = value; - } - /** - * @param value The number of the enum to look for. - * @return The enum associated with the given number. - * @deprecated Use {@link #forNumber(int)} instead. - */ - @java.lang.Deprecated - public static FileCase valueOf(int value) { - return forNumber(value); - } - - public static FileCase forNumber(int value) { - switch (value) { - case 1: return FILE_WITH_URI; - case 2: return FILE_WITH_BYTES; - case 0: return FILE_NOT_SET; - default: return null; - } - } - public int getNumber() { - return this.value; - } - }; - - public FileCase - getFileCase() { - return FileCase.forNumber( - fileCase_); - } - - public static final int FILE_WITH_URI_FIELD_NUMBER = 1; - /** - *
    -   * A URL pointing to the file's content.
    -   * 
    - * - * string file_with_uri = 1; - * @return Whether the fileWithUri field is set. - */ - public boolean hasFileWithUri() { - return fileCase_ == 1; - } - /** - *
    -   * A URL pointing to the file's content.
    -   * 
    - * - * string file_with_uri = 1; - * @return The fileWithUri. - */ - public java.lang.String getFileWithUri() { - java.lang.Object ref = ""; - if (fileCase_ == 1) { - ref = file_; - } - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (fileCase_ == 1) { - file_ = s; - } - return s; - } - } - /** - *
    -   * A URL pointing to the file's content.
    -   * 
    - * - * string file_with_uri = 1; - * @return The bytes for fileWithUri. - */ - public com.google.protobuf.ByteString - getFileWithUriBytes() { - java.lang.Object ref = ""; - if (fileCase_ == 1) { - ref = file_; - } - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - if (fileCase_ == 1) { - file_ = b; - } - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int FILE_WITH_BYTES_FIELD_NUMBER = 2; - /** - *
    -   * The base64-encoded content of the file.
    -   * 
    - * - * bytes file_with_bytes = 2; - * @return Whether the fileWithBytes field is set. - */ - @java.lang.Override - public boolean hasFileWithBytes() { - return fileCase_ == 2; - } - /** - *
    -   * The base64-encoded content of the file.
    -   * 
    - * - * bytes file_with_bytes = 2; - * @return The fileWithBytes. - */ - @java.lang.Override - public com.google.protobuf.ByteString getFileWithBytes() { - if (fileCase_ == 2) { - return (com.google.protobuf.ByteString) file_; - } - return com.google.protobuf.ByteString.EMPTY; - } - - public static final int MEDIA_TYPE_FIELD_NUMBER = 3; - @SuppressWarnings("serial") - private volatile java.lang.Object mediaType_ = ""; - /** - *
    -   * The media type of the file (e.g., "application/pdf").
    -   * 
    - * - * string media_type = 3; - * @return The mediaType. - */ - @java.lang.Override - public java.lang.String getMediaType() { - java.lang.Object ref = mediaType_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - mediaType_ = s; - return s; - } - } - /** - *
    -   * The media type of the file (e.g., "application/pdf").
    -   * 
    - * - * string media_type = 3; - * @return The bytes for mediaType. - */ - @java.lang.Override - public com.google.protobuf.ByteString - getMediaTypeBytes() { - java.lang.Object ref = mediaType_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - mediaType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int NAME_FIELD_NUMBER = 4; - @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; - /** - *
    -   * An optional name for the file (e.g., "document.pdf").
    -   * 
    - * - * string name = 4; - * @return The name. - */ - @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - name_ = s; - return s; - } - } - /** - *
    -   * An optional name for the file (e.g., "document.pdf").
    -   * 
    - * - * string name = 4; - * @return The bytes for name. - */ - @java.lang.Override - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private byte memoizedIsInitialized = -1; - @java.lang.Override - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; - - memoizedIsInitialized = 1; - return true; - } - - @java.lang.Override - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (fileCase_ == 1) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, file_); - } - if (fileCase_ == 2) { - output.writeBytes( - 2, (com.google.protobuf.ByteString) file_); - } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mediaType_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 3, mediaType_); - } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 4, name_); - } - getUnknownFields().writeTo(output); - } - - @java.lang.Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; - - size = 0; - if (fileCase_ == 1) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, file_); - } - if (fileCase_ == 2) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize( - 2, (com.google.protobuf.ByteString) file_); - } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mediaType_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(3, mediaType_); - } - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(4, name_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSize = size; - return size; - } - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.a2a.grpc.FilePart)) { - return super.equals(obj); - } - io.a2a.grpc.FilePart other = (io.a2a.grpc.FilePart) obj; - - if (!getMediaType() - .equals(other.getMediaType())) return false; - if (!getName() - .equals(other.getName())) return false; - if (!getFileCase().equals(other.getFileCase())) return false; - switch (fileCase_) { - case 1: - if (!getFileWithUri() - .equals(other.getFileWithUri())) return false; - break; - case 2: - if (!getFileWithBytes() - .equals(other.getFileWithBytes())) return false; - break; - case 0: - default: - } - if (!getUnknownFields().equals(other.getUnknownFields())) return false; - return true; - } - - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + MEDIA_TYPE_FIELD_NUMBER; - hash = (53 * hash) + getMediaType().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); - switch (fileCase_) { - case 1: - hash = (37 * hash) + FILE_WITH_URI_FIELD_NUMBER; - hash = (53 * hash) + getFileWithUri().hashCode(); - break; - case 2: - hash = (37 * hash) + FILE_WITH_BYTES_FIELD_NUMBER; - hash = (53 * hash) + getFileWithBytes().hashCode(); - break; - case 0: - default: - } - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.a2a.grpc.FilePart parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.FilePart parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.FilePart parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.FilePart parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.FilePart parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static io.a2a.grpc.FilePart parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static io.a2a.grpc.FilePart parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input); - } - public static io.a2a.grpc.FilePart parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.a2a.grpc.FilePart parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.a2a.grpc.FilePart parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static io.a2a.grpc.FilePart parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input); - } - public static io.a2a.grpc.FilePart parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessage - .parseWithIOException(PARSER, input, extensionRegistry); - } - - @java.lang.Override - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(io.a2a.grpc.FilePart prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - @java.lang.Override - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - *
    -   * --8<-- [start:FilePart]
    -   * FilePart represents the different ways files can be provided. If files are
    -   * small, directly feeding the bytes is supported via file_with_bytes. If the
    -   * file is large, the agent should read the content as appropriate directly
    -   * from the file_with_uri source.
    -   * 
    - * - * Protobuf type {@code a2a.v1.FilePart} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.FilePart) - io.a2a.grpc.FilePartOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_descriptor; - } - - @java.lang.Override - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.a2a.grpc.FilePart.class, io.a2a.grpc.FilePart.Builder.class); - } - - // Construct using io.a2a.grpc.FilePart.newBuilder() - private Builder() { - - } - - private Builder( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - super(parent); - - } - @java.lang.Override - public Builder clear() { - super.clear(); - bitField0_ = 0; - mediaType_ = ""; - name_ = ""; - fileCase_ = 0; - file_ = null; - return this; - } - - @java.lang.Override - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_FilePart_descriptor; - } - - @java.lang.Override - public io.a2a.grpc.FilePart getDefaultInstanceForType() { - return io.a2a.grpc.FilePart.getDefaultInstance(); - } - - @java.lang.Override - public io.a2a.grpc.FilePart build() { - io.a2a.grpc.FilePart result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @java.lang.Override - public io.a2a.grpc.FilePart buildPartial() { - io.a2a.grpc.FilePart result = new io.a2a.grpc.FilePart(this); - if (bitField0_ != 0) { buildPartial0(result); } - buildPartialOneofs(result); - onBuilt(); - return result; - } - - private void buildPartial0(io.a2a.grpc.FilePart result) { - int from_bitField0_ = bitField0_; - if (((from_bitField0_ & 0x00000004) != 0)) { - result.mediaType_ = mediaType_; - } - if (((from_bitField0_ & 0x00000008) != 0)) { - result.name_ = name_; - } - } - - private void buildPartialOneofs(io.a2a.grpc.FilePart result) { - result.fileCase_ = fileCase_; - result.file_ = this.file_; - } - - @java.lang.Override - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.FilePart) { - return mergeFrom((io.a2a.grpc.FilePart)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.a2a.grpc.FilePart other) { - if (other == io.a2a.grpc.FilePart.getDefaultInstance()) return this; - if (!other.getMediaType().isEmpty()) { - mediaType_ = other.mediaType_; - bitField0_ |= 0x00000004; - onChanged(); - } - if (!other.getName().isEmpty()) { - name_ = other.name_; - bitField0_ |= 0x00000008; - onChanged(); - } - switch (other.getFileCase()) { - case FILE_WITH_URI: { - fileCase_ = 1; - file_ = other.file_; - onChanged(); - break; - } - case FILE_WITH_BYTES: { - setFileWithBytes(other.getFileWithBytes()); - break; - } - case FILE_NOT_SET: { - break; - } - } - this.mergeUnknownFields(other.getUnknownFields()); - onChanged(); - return this; - } - - @java.lang.Override - public final boolean isInitialized() { - return true; - } - - @java.lang.Override - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - if (extensionRegistry == null) { - throw new java.lang.NullPointerException(); - } - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - case 10: { - java.lang.String s = input.readStringRequireUtf8(); - fileCase_ = 1; - file_ = s; - break; - } // case 10 - case 18: { - file_ = input.readBytes(); - fileCase_ = 2; - break; - } // case 18 - case 26: { - mediaType_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000004; - break; - } // case 26 - case 34: { - name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000008; - break; - } // case 34 - default: { - if (!super.parseUnknownField(input, extensionRegistry, tag)) { - done = true; // was an endgroup tag - } - break; - } // default: - } // switch (tag) - } // while (!done) - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } finally { - onChanged(); - } // finally - return this; - } - private int fileCase_ = 0; - private java.lang.Object file_; - public FileCase - getFileCase() { - return FileCase.forNumber( - fileCase_); - } - - public Builder clearFile() { - fileCase_ = 0; - file_ = null; - onChanged(); - return this; - } - - private int bitField0_; - - /** - *
    -     * A URL pointing to the file's content.
    -     * 
    - * - * string file_with_uri = 1; - * @return Whether the fileWithUri field is set. - */ - @java.lang.Override - public boolean hasFileWithUri() { - return fileCase_ == 1; - } - /** - *
    -     * A URL pointing to the file's content.
    -     * 
    - * - * string file_with_uri = 1; - * @return The fileWithUri. - */ - @java.lang.Override - public java.lang.String getFileWithUri() { - java.lang.Object ref = ""; - if (fileCase_ == 1) { - ref = file_; - } - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - if (fileCase_ == 1) { - file_ = s; - } - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * A URL pointing to the file's content.
    -     * 
    - * - * string file_with_uri = 1; - * @return The bytes for fileWithUri. - */ - @java.lang.Override - public com.google.protobuf.ByteString - getFileWithUriBytes() { - java.lang.Object ref = ""; - if (fileCase_ == 1) { - ref = file_; - } - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - if (fileCase_ == 1) { - file_ = b; - } - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * A URL pointing to the file's content.
    -     * 
    - * - * string file_with_uri = 1; - * @param value The fileWithUri to set. - * @return This builder for chaining. - */ - public Builder setFileWithUri( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - fileCase_ = 1; - file_ = value; - onChanged(); - return this; - } - /** - *
    -     * A URL pointing to the file's content.
    -     * 
    - * - * string file_with_uri = 1; - * @return This builder for chaining. - */ - public Builder clearFileWithUri() { - if (fileCase_ == 1) { - fileCase_ = 0; - file_ = null; - onChanged(); - } - return this; - } - /** - *
    -     * A URL pointing to the file's content.
    -     * 
    - * - * string file_with_uri = 1; - * @param value The bytes for fileWithUri to set. - * @return This builder for chaining. - */ - public Builder setFileWithUriBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - fileCase_ = 1; - file_ = value; - onChanged(); - return this; - } - - /** - *
    -     * The base64-encoded content of the file.
    -     * 
    - * - * bytes file_with_bytes = 2; - * @return Whether the fileWithBytes field is set. - */ - public boolean hasFileWithBytes() { - return fileCase_ == 2; - } - /** - *
    -     * The base64-encoded content of the file.
    -     * 
    - * - * bytes file_with_bytes = 2; - * @return The fileWithBytes. - */ - public com.google.protobuf.ByteString getFileWithBytes() { - if (fileCase_ == 2) { - return (com.google.protobuf.ByteString) file_; - } - return com.google.protobuf.ByteString.EMPTY; - } - /** - *
    -     * The base64-encoded content of the file.
    -     * 
    - * - * bytes file_with_bytes = 2; - * @param value The fileWithBytes to set. - * @return This builder for chaining. - */ - public Builder setFileWithBytes(com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - fileCase_ = 2; - file_ = value; - onChanged(); - return this; - } - /** - *
    -     * The base64-encoded content of the file.
    -     * 
    - * - * bytes file_with_bytes = 2; - * @return This builder for chaining. - */ - public Builder clearFileWithBytes() { - if (fileCase_ == 2) { - fileCase_ = 0; - file_ = null; - onChanged(); - } - return this; - } - - private java.lang.Object mediaType_ = ""; - /** - *
    -     * The media type of the file (e.g., "application/pdf").
    -     * 
    - * - * string media_type = 3; - * @return The mediaType. - */ - public java.lang.String getMediaType() { - java.lang.Object ref = mediaType_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - mediaType_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * The media type of the file (e.g., "application/pdf").
    -     * 
    - * - * string media_type = 3; - * @return The bytes for mediaType. - */ - public com.google.protobuf.ByteString - getMediaTypeBytes() { - java.lang.Object ref = mediaType_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - mediaType_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * The media type of the file (e.g., "application/pdf").
    -     * 
    - * - * string media_type = 3; - * @param value The mediaType to set. - * @return This builder for chaining. - */ - public Builder setMediaType( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - mediaType_ = value; - bitField0_ |= 0x00000004; - onChanged(); - return this; - } - /** - *
    -     * The media type of the file (e.g., "application/pdf").
    -     * 
    - * - * string media_type = 3; - * @return This builder for chaining. - */ - public Builder clearMediaType() { - mediaType_ = getDefaultInstance().getMediaType(); - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - return this; - } - /** - *
    -     * The media type of the file (e.g., "application/pdf").
    -     * 
    - * - * string media_type = 3; - * @param value The bytes for mediaType to set. - * @return This builder for chaining. - */ - public Builder setMediaTypeBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - mediaType_ = value; - bitField0_ |= 0x00000004; - onChanged(); - return this; - } - - private java.lang.Object name_ = ""; - /** - *
    -     * An optional name for the file (e.g., "document.pdf").
    -     * 
    - * - * string name = 4; - * @return The name. - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - name_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - /** - *
    -     * An optional name for the file (e.g., "document.pdf").
    -     * 
    - * - * string name = 4; - * @return The bytes for name. - */ - public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - *
    -     * An optional name for the file (e.g., "document.pdf").
    -     * 
    - * - * string name = 4; - * @param value The name to set. - * @return This builder for chaining. - */ - public Builder setName( - java.lang.String value) { - if (value == null) { throw new NullPointerException(); } - name_ = value; - bitField0_ |= 0x00000008; - onChanged(); - return this; - } - /** - *
    -     * An optional name for the file (e.g., "document.pdf").
    -     * 
    - * - * string name = 4; - * @return This builder for chaining. - */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - return this; - } - /** - *
    -     * An optional name for the file (e.g., "document.pdf").
    -     * 
    - * - * string name = 4; - * @param value The bytes for name to set. - * @return This builder for chaining. - */ - public Builder setNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { throw new NullPointerException(); } - checkByteStringIsUtf8(value); - name_ = value; - bitField0_ |= 0x00000008; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:a2a.v1.FilePart) - } - - // @@protoc_insertion_point(class_scope:a2a.v1.FilePart) - private static final io.a2a.grpc.FilePart DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new io.a2a.grpc.FilePart(); - } - - public static io.a2a.grpc.FilePart getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - @java.lang.Override - public FilePart parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (com.google.protobuf.UninitializedMessageException e) { - throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException(e) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - @java.lang.Override - public io.a2a.grpc.FilePart getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - -} - diff --git a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java deleted file mode 100644 index 2fbad2bf2..000000000 --- a/spec-grpc/src/main/java/io/a2a/grpc/FilePartOrBuilder.java +++ /dev/null @@ -1,102 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// NO CHECKED-IN PROTOBUF GENCODE -// source: a2a.proto -// Protobuf Java Version: 4.33.1 - -package io.a2a.grpc; - -@com.google.protobuf.Generated -public interface FilePartOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.FilePart) - com.google.protobuf.MessageOrBuilder { - - /** - *
    -   * A URL pointing to the file's content.
    -   * 
    - * - * string file_with_uri = 1; - * @return Whether the fileWithUri field is set. - */ - boolean hasFileWithUri(); - /** - *
    -   * A URL pointing to the file's content.
    -   * 
    - * - * string file_with_uri = 1; - * @return The fileWithUri. - */ - java.lang.String getFileWithUri(); - /** - *
    -   * A URL pointing to the file's content.
    -   * 
    - * - * string file_with_uri = 1; - * @return The bytes for fileWithUri. - */ - com.google.protobuf.ByteString - getFileWithUriBytes(); - - /** - *
    -   * The base64-encoded content of the file.
    -   * 
    - * - * bytes file_with_bytes = 2; - * @return Whether the fileWithBytes field is set. - */ - boolean hasFileWithBytes(); - /** - *
    -   * The base64-encoded content of the file.
    -   * 
    - * - * bytes file_with_bytes = 2; - * @return The fileWithBytes. - */ - com.google.protobuf.ByteString getFileWithBytes(); - - /** - *
    -   * The media type of the file (e.g., "application/pdf").
    -   * 
    - * - * string media_type = 3; - * @return The mediaType. - */ - java.lang.String getMediaType(); - /** - *
    -   * The media type of the file (e.g., "application/pdf").
    -   * 
    - * - * string media_type = 3; - * @return The bytes for mediaType. - */ - com.google.protobuf.ByteString - getMediaTypeBytes(); - - /** - *
    -   * An optional name for the file (e.g., "document.pdf").
    -   * 
    - * - * string name = 4; - * @return The name. - */ - java.lang.String getName(); - /** - *
    -   * An optional name for the file (e.g., "document.pdf").
    -   * 
    - * - * string name = 4; - * @return The bytes for name. - */ - com.google.protobuf.ByteString - getNameBytes(); - - io.a2a.grpc.FilePart.FileCase getFileCase(); -} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java index 5d5819669..e254a9022 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequest.java @@ -33,7 +33,8 @@ private GetTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessag } private GetTaskPushNotificationConfigRequest() { tenant_ = ""; - name_ = ""; + taskId_ = ""; + id_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -96,49 +97,94 @@ public java.lang.String getTenant() { } } - public static final int NAME_FIELD_NUMBER = 1; + public static final int TASK_ID_FIELD_NUMBER = 3; @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; + private volatile java.lang.Object taskId_ = ""; /** *
    -   * The resource name of the config to retrieve.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + taskId_ = s; return s; } } /** *
    -   * The resource name of the config to retrieve.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ @java.lang.Override public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + *
    +   * The resource id of the config to retrieve.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + *
    +   * The resource id of the config to retrieve.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -159,12 +205,15 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, taskId_); + } getUnknownFields().writeTo(output); } @@ -174,12 +223,15 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, taskId_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -197,8 +249,10 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getName() - .equals(other.getName())) return false; + if (!getTaskId() + .equals(other.getTaskId())) return false; + if (!getId() + .equals(other.getId())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -212,8 +266,10 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -350,7 +406,8 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - name_ = ""; + taskId_ = ""; + id_ = ""; return this; } @@ -388,7 +445,10 @@ private void buildPartial0(io.a2a.grpc.GetTaskPushNotificationConfigRequest resu result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.name_ = name_; + result.taskId_ = taskId_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.id_ = id_; } } @@ -409,11 +469,16 @@ public Builder mergeFrom(io.a2a.grpc.GetTaskPushNotificationConfigRequest other) bitField0_ |= 0x00000001; onChanged(); } - if (!other.getName().isEmpty()) { - name_ = other.name_; + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; bitField0_ |= 0x00000002; onChanged(); } + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000004; + onChanged(); + } this.mergeUnknownFields(other.getUnknownFields()); onChanged(); return this; @@ -441,8 +506,8 @@ public Builder mergeFrom( done = true; break; case 10: { - name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000002; + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; break; } // case 10 case 18: { @@ -450,6 +515,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000001; break; } // case 18 + case 26: { + taskId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 26 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -559,23 +629,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object name_ = ""; + private java.lang.Object taskId_ = ""; /** *
    -     * The resource name of the config to retrieve.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @return The name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + taskId_ = s; return s; } else { return (java.lang.String) ref; @@ -583,21 +652,20 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the config to retrieve.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @return The bytes for name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -605,57 +673,146 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the config to retrieve.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @param value The name to set. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The taskId to set. * @return This builder for chaining. */ - public Builder setName( + public Builder setTaskId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - name_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The resource name of the config to retrieve.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The resource name of the config to retrieve.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * The parent task resource id.
          * 
    * - * string name = 1; - * @param value The bytes for name to set. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for taskId to set. * @return This builder for chaining. */ - public Builder setNameBytes( + public Builder setTaskIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } + private java.lang.Object id_ = ""; + /** + *
    +     * The resource id of the config to retrieve.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The resource id of the config to retrieve.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The resource id of the config to retrieve.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + id_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
    +     * The resource id of the config to retrieve.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
    +     * The resource id of the config to retrieve.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.GetTaskPushNotificationConfigRequest) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java index 8224a30a2..5e9047f46 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskPushNotificationConfigRequestOrBuilder.java @@ -32,23 +32,41 @@ public interface GetTaskPushNotificationConfigRequestOrBuilder extends /** *
    -   * The resource name of the config to retrieve.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - java.lang.String getName(); + java.lang.String getTaskId(); /** *
    -   * The resource name of the config to retrieve.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * The parent task resource id.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ com.google.protobuf.ByteString - getNameBytes(); + getTaskIdBytes(); + + /** + *
    +   * The resource id of the config to retrieve.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + java.lang.String getId(); + /** + *
    +   * The resource id of the config to retrieve.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + com.google.protobuf.ByteString + getIdBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java index 76d9264a0..9fa872c52 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequest.java @@ -34,7 +34,7 @@ private GetTaskRequest(com.google.protobuf.GeneratedMessage.Builder builder) } private GetTaskRequest() { tenant_ = ""; - name_ = ""; + id_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -98,49 +98,47 @@ public java.lang.String getTenant() { } } - public static final int NAME_FIELD_NUMBER = 1; + public static final int ID_FIELD_NUMBER = 1; @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; + private volatile java.lang.Object id_ = ""; /** *
    -   * The resource name of the task.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task.
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getId() { + java.lang.Object ref = id_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + id_ = s; return s; } } /** *
    -   * The resource name of the task.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task.
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ @java.lang.Override public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getIdBytes() { + java.lang.Object ref = id_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -194,8 +192,8 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); } if (((bitField0_ & 0x00000001) != 0)) { output.writeInt32(2, historyLength_); @@ -212,8 +210,8 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream @@ -239,8 +237,8 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getName() - .equals(other.getName())) return false; + if (!getId() + .equals(other.getId())) return false; if (hasHistoryLength() != other.hasHistoryLength()) return false; if (hasHistoryLength()) { if (getHistoryLength() @@ -259,8 +257,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); if (hasHistoryLength()) { hash = (37 * hash) + HISTORY_LENGTH_FIELD_NUMBER; hash = (53 * hash) + getHistoryLength(); @@ -402,7 +400,7 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - name_ = ""; + id_ = ""; historyLength_ = 0; return this; } @@ -441,7 +439,7 @@ private void buildPartial0(io.a2a.grpc.GetTaskRequest result) { result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.name_ = name_; + result.id_ = id_; } int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000004) != 0)) { @@ -468,8 +466,8 @@ public Builder mergeFrom(io.a2a.grpc.GetTaskRequest other) { bitField0_ |= 0x00000001; onChanged(); } - if (!other.getName().isEmpty()) { - name_ = other.name_; + if (!other.getId().isEmpty()) { + id_ = other.id_; bitField0_ |= 0x00000002; onChanged(); } @@ -503,7 +501,7 @@ public Builder mergeFrom( done = true; break; case 10: { - name_ = input.readStringRequireUtf8(); + id_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 10 @@ -626,23 +624,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object name_ = ""; + private java.lang.Object id_ = ""; /** *
    -     * The resource name of the task.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task.
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getId() { + java.lang.Object ref = id_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + id_ = s; return s; } else { return (java.lang.String) ref; @@ -650,21 +647,20 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the task.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task.
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getIdBytes() { + java.lang.Object ref = id_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -672,52 +668,49 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the task.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task.
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The name to set. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The id to set. * @return This builder for chaining. */ - public Builder setName( + public Builder setId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - name_ = value; + id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The resource name of the task.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task.
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); + public Builder clearId() { + id_ = getDefaultInstance().getId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The resource name of the task.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task.
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes for name to set. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for id to set. * @return This builder for chaining. */ - public Builder setNameBytes( + public Builder setIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; + id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java index 1ef78600d..7a86eb56c 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/GetTaskRequestOrBuilder.java @@ -32,25 +32,23 @@ public interface GetTaskRequestOrBuilder extends /** *
    -   * The resource name of the task.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task.
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ - java.lang.String getName(); + java.lang.String getId(); /** *
    -   * The resource name of the task.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task.
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ com.google.protobuf.ByteString - getNameBytes(); + getIdBytes(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java
    new file mode 100644
    index 000000000..e1fd0601c
    --- /dev/null
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlow.java
    @@ -0,0 +1,1050 @@
    +// Generated by the protocol buffer compiler.  DO NOT EDIT!
    +// NO CHECKED-IN PROTOBUF GENCODE
    +// source: a2a.proto
    +// Protobuf Java Version: 4.33.1
    +
    +package io.a2a.grpc;
    +
    +/**
    + * 
    + * DEPRECATED
    + * 
    + * + * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} + */ +@com.google.protobuf.Generated +public final class ImplicitOAuthFlow extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.ImplicitOAuthFlow) + ImplicitOAuthFlowOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 1, + /* suffix= */ "", + "ImplicitOAuthFlow"); + } + // Use ImplicitOAuthFlow.newBuilder() to construct. + private ImplicitOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private ImplicitOAuthFlow() { + authorizationUrl_ = ""; + refreshUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ImplicitOAuthFlow.class, io.a2a.grpc.ImplicitOAuthFlow.Builder.class); + } + + public static final int AUTHORIZATION_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object authorizationUrl_ = ""; + /** + *
    +   * The authorization URL to be used for this flow. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS
    +   * 
    + * + * string authorization_url = 1; + * @return The authorizationUrl. + */ + @java.lang.Override + public java.lang.String getAuthorizationUrl() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + authorizationUrl_ = s; + return s; + } + } + /** + *
    +   * The authorization URL to be used for this flow. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS
    +   * 
    + * + * string authorization_url = 1; + * @return The bytes for authorizationUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getAuthorizationUrlBytes() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + authorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REFRESH_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object refreshUrl_ = ""; + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The refreshUrl. + */ + @java.lang.Override + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } + } + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The bytes for refreshUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCOPES_FIELD_NUMBER = 3; + private static final class ScopesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_ScopesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, authorizationUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetScopes(), + ScopesDefaultEntryHolder.defaultEntry, + 3); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(authorizationUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, authorizationUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); + } + for (java.util.Map.Entry entry + : internalGetScopes().getMap().entrySet()) { + com.google.protobuf.MapEntry + scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, scopes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.ImplicitOAuthFlow)) { + return super.equals(obj); + } + io.a2a.grpc.ImplicitOAuthFlow other = (io.a2a.grpc.ImplicitOAuthFlow) obj; + + if (!getAuthorizationUrl() + .equals(other.getAuthorizationUrl())) return false; + if (!getRefreshUrl() + .equals(other.getRefreshUrl())) return false; + if (!internalGetScopes().equals( + other.internalGetScopes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + AUTHORIZATION_URL_FIELD_NUMBER; + hash = (53 * hash) + getAuthorizationUrl().hashCode(); + hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; + hash = (53 * hash) + getRefreshUrl().hashCode(); + if (!internalGetScopes().getMap().isEmpty()) { + hash = (37 * hash) + SCOPES_FIELD_NUMBER; + hash = (53 * hash) + internalGetScopes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.ImplicitOAuthFlow parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.ImplicitOAuthFlow parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.ImplicitOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.ImplicitOAuthFlow prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
    +   * DEPRECATED
    +   * 
    + * + * Protobuf type {@code a2a.v1.ImplicitOAuthFlow} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.ImplicitOAuthFlow) + io.a2a.grpc.ImplicitOAuthFlowOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetMutableScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.ImplicitOAuthFlow.class, io.a2a.grpc.ImplicitOAuthFlow.Builder.class); + } + + // Construct using io.a2a.grpc.ImplicitOAuthFlow.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + authorizationUrl_ = ""; + refreshUrl_ = ""; + internalGetMutableScopes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_ImplicitOAuthFlow_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow build() { + io.a2a.grpc.ImplicitOAuthFlow result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow buildPartial() { + io.a2a.grpc.ImplicitOAuthFlow result = new io.a2a.grpc.ImplicitOAuthFlow(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.ImplicitOAuthFlow result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.authorizationUrl_ = authorizationUrl_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.scopes_ = internalGetScopes(); + result.scopes_.makeImmutable(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.ImplicitOAuthFlow) { + return mergeFrom((io.a2a.grpc.ImplicitOAuthFlow)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.ImplicitOAuthFlow other) { + if (other == io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance()) return this; + if (!other.getAuthorizationUrl().isEmpty()) { + authorizationUrl_ = other.authorizationUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getRefreshUrl().isEmpty()) { + refreshUrl_ = other.refreshUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + internalGetMutableScopes().mergeFrom( + other.internalGetScopes()); + bitField0_ |= 0x00000004; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + authorizationUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + com.google.protobuf.MapEntry + scopes__ = input.readMessage( + ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableScopes().getMutableMap().put( + scopes__.getKey(), scopes__.getValue()); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object authorizationUrl_ = ""; + /** + *
    +     * The authorization URL to be used for this flow. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS
    +     * 
    + * + * string authorization_url = 1; + * @return The authorizationUrl. + */ + public java.lang.String getAuthorizationUrl() { + java.lang.Object ref = authorizationUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + authorizationUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The authorization URL to be used for this flow. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS
    +     * 
    + * + * string authorization_url = 1; + * @return The bytes for authorizationUrl. + */ + public com.google.protobuf.ByteString + getAuthorizationUrlBytes() { + java.lang.Object ref = authorizationUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + authorizationUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The authorization URL to be used for this flow. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS
    +     * 
    + * + * string authorization_url = 1; + * @param value The authorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setAuthorizationUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + authorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * The authorization URL to be used for this flow. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS
    +     * 
    + * + * string authorization_url = 1; + * @return This builder for chaining. + */ + public Builder clearAuthorizationUrl() { + authorizationUrl_ = getDefaultInstance().getAuthorizationUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * The authorization URL to be used for this flow. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS
    +     * 
    + * + * string authorization_url = 1; + * @param value The bytes for authorizationUrl to set. + * @return This builder for chaining. + */ + public Builder setAuthorizationUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + authorizationUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object refreshUrl_ = ""; + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @return The refreshUrl. + */ + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @return The bytes for refreshUrl. + */ + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @param value The refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @return This builder for chaining. + */ + public Builder clearRefreshUrl() { + refreshUrl_ = getDefaultInstance().getRefreshUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @param value The bytes for refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + private com.google.protobuf.MapField + internalGetMutableScopes() { + if (scopes_ == null) { + scopes_ = com.google.protobuf.MapField.newMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + if (!scopes_.isMutable()) { + scopes_ = scopes_.copy(); + } + bitField0_ |= 0x00000004; + onChanged(); + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + public Builder clearScopes() { + bitField0_ = (bitField0_ & ~0x00000004); + internalGetMutableScopes().getMutableMap() + .clear(); + return this; + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + public Builder removeScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableScopes().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableScopes() { + bitField0_ |= 0x00000004; + return internalGetMutableScopes().getMutableMap(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + public Builder putScopes( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableScopes().getMutableMap() + .put(key, value); + bitField0_ |= 0x00000004; + return this; + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + public Builder putAllScopes( + java.util.Map values) { + internalGetMutableScopes().getMutableMap() + .putAll(values); + bitField0_ |= 0x00000004; + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.ImplicitOAuthFlow) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.ImplicitOAuthFlow) + private static final io.a2a.grpc.ImplicitOAuthFlow DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.ImplicitOAuthFlow(); + } + + public static io.a2a.grpc.ImplicitOAuthFlow getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ImplicitOAuthFlow parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.ImplicitOAuthFlow getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java new file mode 100644 index 000000000..3b0180dbc --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/ImplicitOAuthFlowOrBuilder.java @@ -0,0 +1,115 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.33.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface ImplicitOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.ImplicitOAuthFlow) + com.google.protobuf.MessageOrBuilder { + + /** + *
    +   * The authorization URL to be used for this flow. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS
    +   * 
    + * + * string authorization_url = 1; + * @return The authorizationUrl. + */ + java.lang.String getAuthorizationUrl(); + /** + *
    +   * The authorization URL to be used for this flow. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS
    +   * 
    + * + * string authorization_url = 1; + * @return The bytes for authorizationUrl. + */ + com.google.protobuf.ByteString + getAuthorizationUrlBytes(); + + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The refreshUrl. + */ + java.lang.String getRefreshUrl(); + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The bytes for refreshUrl. + */ + com.google.protobuf.ByteString + getRefreshUrlBytes(); + + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + int getScopesCount(); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + boolean containsScopes( + java.lang.String key); + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getScopes(); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + java.util.Map + getScopesMap(); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + java.lang.String getScopesOrThrow( + java.lang.String key); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java index cbcdcfd18..b80396df7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequest.java @@ -33,7 +33,7 @@ private ListTaskPushNotificationConfigRequest(com.google.protobuf.GeneratedMessa } private ListTaskPushNotificationConfigRequest() { tenant_ = ""; - parent_ = ""; + taskId_ = ""; pageToken_ = ""; } @@ -97,49 +97,47 @@ public java.lang.String getTenant() { } } - public static final int PARENT_FIELD_NUMBER = 1; + public static final int TASK_ID_FIELD_NUMBER = 1; @SuppressWarnings("serial") - private volatile java.lang.Object parent_ = ""; + private volatile java.lang.Object taskId_ = ""; /** *
    -   * The parent task resource.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1; - * @return The parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ @java.lang.Override - public java.lang.String getParent() { - java.lang.Object ref = parent_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - parent_ = s; + taskId_ = s; return s; } } /** *
    -   * The parent task resource.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1; - * @return The bytes for parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ @java.lang.Override public com.google.protobuf.ByteString - getParentBytes() { - java.lang.Object ref = parent_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - parent_ = b; + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -222,8 +220,8 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, parent_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, taskId_); } if (pageSize_ != 0) { output.writeInt32(2, pageSize_); @@ -243,8 +241,8 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(parent_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, parent_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, taskId_); } if (pageSize_ != 0) { size += com.google.protobuf.CodedOutputStream @@ -273,8 +271,8 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getParent() - .equals(other.getParent())) return false; + if (!getTaskId() + .equals(other.getTaskId())) return false; if (getPageSize() != other.getPageSize()) return false; if (!getPageToken() @@ -292,8 +290,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + PARENT_FIELD_NUMBER; - hash = (53 * hash) + getParent().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); hash = (37 * hash) + PAGE_SIZE_FIELD_NUMBER; hash = (53 * hash) + getPageSize(); hash = (37 * hash) + PAGE_TOKEN_FIELD_NUMBER; @@ -434,7 +432,7 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - parent_ = ""; + taskId_ = ""; pageSize_ = 0; pageToken_ = ""; return this; @@ -474,7 +472,7 @@ private void buildPartial0(io.a2a.grpc.ListTaskPushNotificationConfigRequest res result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.parent_ = parent_; + result.taskId_ = taskId_; } if (((from_bitField0_ & 0x00000004) != 0)) { result.pageSize_ = pageSize_; @@ -501,8 +499,8 @@ public Builder mergeFrom(io.a2a.grpc.ListTaskPushNotificationConfigRequest other bitField0_ |= 0x00000001; onChanged(); } - if (!other.getParent().isEmpty()) { - parent_ = other.parent_; + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; bitField0_ |= 0x00000002; onChanged(); } @@ -541,7 +539,7 @@ public Builder mergeFrom( done = true; break; case 10: { - parent_ = input.readStringRequireUtf8(); + taskId_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 10 @@ -669,23 +667,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object parent_ = ""; + private java.lang.Object taskId_ = ""; /** *
    -     * The parent task resource.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1; - * @return The parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - public java.lang.String getParent() { - java.lang.Object ref = parent_; + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - parent_ = s; + taskId_ = s; return s; } else { return (java.lang.String) ref; @@ -693,21 +690,20 @@ public java.lang.String getParent() { } /** *
    -     * The parent task resource.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1; - * @return The bytes for parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ public com.google.protobuf.ByteString - getParentBytes() { - java.lang.Object ref = parent_; + getTaskIdBytes() { + java.lang.Object ref = taskId_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - parent_ = b; + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -715,52 +711,49 @@ public java.lang.String getParent() { } /** *
    -     * The parent task resource.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1; - * @param value The parent to set. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The taskId to set. * @return This builder for chaining. */ - public Builder setParent( + public Builder setTaskId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - parent_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The parent task resource.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1; + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearParent() { - parent_ = getDefaultInstance().getParent(); + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The parent task resource.
    -     * Format: tasks/{task_id}
    +     * The parent task resource id.
          * 
    * - * string parent = 1; - * @param value The bytes for parent to set. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for taskId to set. * @return This builder for chaining. */ - public Builder setParentBytes( + public Builder setTaskIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - parent_ = value; + taskId_ = value; bitField0_ |= 0x00000002; onChanged(); return this; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java index 690539ac2..ba43bb3d7 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/ListTaskPushNotificationConfigRequestOrBuilder.java @@ -32,25 +32,23 @@ public interface ListTaskPushNotificationConfigRequestOrBuilder extends /** *
    -   * The parent task resource.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1; - * @return The parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. */ - java.lang.String getParent(); + java.lang.String getTaskId(); /** *
    -   * The parent task resource.
    -   * Format: tasks/{task_id}
    +   * The parent task resource id.
        * 
    * - * string parent = 1; - * @return The bytes for parent. + * string task_id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. */ com.google.protobuf.ByteString - getParentBytes(); + getTaskIdBytes(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java
    index e4fd7c957..8b1a661f7 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlows.java
    @@ -56,6 +56,8 @@ public enum FlowCase
               com.google.protobuf.AbstractMessage.InternalOneOfEnum {
         AUTHORIZATION_CODE(1),
         CLIENT_CREDENTIALS(2),
    +    @java.lang.Deprecated IMPLICIT(3),
    +    @java.lang.Deprecated PASSWORD(4),
         DEVICE_CODE(5),
         FLOW_NOT_SET(0);
         private final int value;
    @@ -76,6 +78,8 @@ public static FlowCase forNumber(int value) {
           switch (value) {
             case 1: return AUTHORIZATION_CODE;
             case 2: return CLIENT_CREDENTIALS;
    +        case 3: return IMPLICIT;
    +        case 4: return PASSWORD;
             case 5: return DEVICE_CODE;
             case 0: return FLOW_NOT_SET;
             default: return null;
    @@ -178,6 +182,76 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui
         return io.a2a.grpc.ClientCredentialsOAuthFlow.getDefaultInstance();
       }
     
    +  public static final int IMPLICIT_FIELD_NUMBER = 3;
    +  /**
    +   * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.implicit is deprecated.
    +   *     See a2a.proto;l=633
    +   * @return Whether the implicit field is set.
    +   */
    +  @java.lang.Override
    +  @java.lang.Deprecated public boolean hasImplicit() {
    +    return flowCase_ == 3;
    +  }
    +  /**
    +   * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.implicit is deprecated.
    +   *     See a2a.proto;l=633
    +   * @return The implicit.
    +   */
    +  @java.lang.Override
    +  @java.lang.Deprecated public io.a2a.grpc.ImplicitOAuthFlow getImplicit() {
    +    if (flowCase_ == 3) {
    +       return (io.a2a.grpc.ImplicitOAuthFlow) flow_;
    +    }
    +    return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance();
    +  }
    +  /**
    +   * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +   */
    +  @java.lang.Override
    +  @java.lang.Deprecated public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() {
    +    if (flowCase_ == 3) {
    +       return (io.a2a.grpc.ImplicitOAuthFlow) flow_;
    +    }
    +    return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance();
    +  }
    +
    +  public static final int PASSWORD_FIELD_NUMBER = 4;
    +  /**
    +   * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.password is deprecated.
    +   *     See a2a.proto;l=634
    +   * @return Whether the password field is set.
    +   */
    +  @java.lang.Override
    +  @java.lang.Deprecated public boolean hasPassword() {
    +    return flowCase_ == 4;
    +  }
    +  /**
    +   * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.password is deprecated.
    +   *     See a2a.proto;l=634
    +   * @return The password.
    +   */
    +  @java.lang.Override
    +  @java.lang.Deprecated public io.a2a.grpc.PasswordOAuthFlow getPassword() {
    +    if (flowCase_ == 4) {
    +       return (io.a2a.grpc.PasswordOAuthFlow) flow_;
    +    }
    +    return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance();
    +  }
    +  /**
    +   * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +   */
    +  @java.lang.Override
    +  @java.lang.Deprecated public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() {
    +    if (flowCase_ == 4) {
    +       return (io.a2a.grpc.PasswordOAuthFlow) flow_;
    +    }
    +    return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance();
    +  }
    +
       public static final int DEVICE_CODE_FIELD_NUMBER = 5;
       /**
        * 
    @@ -241,6 +315,12 @@ public void writeTo(com.google.protobuf.CodedOutputStream output)
         if (flowCase_ == 2) {
           output.writeMessage(2, (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_);
         }
    +    if (flowCase_ == 3) {
    +      output.writeMessage(3, (io.a2a.grpc.ImplicitOAuthFlow) flow_);
    +    }
    +    if (flowCase_ == 4) {
    +      output.writeMessage(4, (io.a2a.grpc.PasswordOAuthFlow) flow_);
    +    }
         if (flowCase_ == 5) {
           output.writeMessage(5, (io.a2a.grpc.DeviceCodeOAuthFlow) flow_);
         }
    @@ -261,6 +341,14 @@ public int getSerializedSize() {
           size += com.google.protobuf.CodedOutputStream
             .computeMessageSize(2, (io.a2a.grpc.ClientCredentialsOAuthFlow) flow_);
         }
    +    if (flowCase_ == 3) {
    +      size += com.google.protobuf.CodedOutputStream
    +        .computeMessageSize(3, (io.a2a.grpc.ImplicitOAuthFlow) flow_);
    +    }
    +    if (flowCase_ == 4) {
    +      size += com.google.protobuf.CodedOutputStream
    +        .computeMessageSize(4, (io.a2a.grpc.PasswordOAuthFlow) flow_);
    +    }
         if (flowCase_ == 5) {
           size += com.google.protobuf.CodedOutputStream
             .computeMessageSize(5, (io.a2a.grpc.DeviceCodeOAuthFlow) flow_);
    @@ -290,6 +378,14 @@ public boolean equals(final java.lang.Object obj) {
             if (!getClientCredentials()
                 .equals(other.getClientCredentials())) return false;
             break;
    +      case 3:
    +        if (!getImplicit()
    +            .equals(other.getImplicit())) return false;
    +        break;
    +      case 4:
    +        if (!getPassword()
    +            .equals(other.getPassword())) return false;
    +        break;
           case 5:
             if (!getDeviceCode()
                 .equals(other.getDeviceCode())) return false;
    @@ -317,6 +413,14 @@ public int hashCode() {
             hash = (37 * hash) + CLIENT_CREDENTIALS_FIELD_NUMBER;
             hash = (53 * hash) + getClientCredentials().hashCode();
             break;
    +      case 3:
    +        hash = (37 * hash) + IMPLICIT_FIELD_NUMBER;
    +        hash = (53 * hash) + getImplicit().hashCode();
    +        break;
    +      case 4:
    +        hash = (37 * hash) + PASSWORD_FIELD_NUMBER;
    +        hash = (53 * hash) + getPassword().hashCode();
    +        break;
           case 5:
             hash = (37 * hash) + DEVICE_CODE_FIELD_NUMBER;
             hash = (53 * hash) + getDeviceCode().hashCode();
    @@ -466,6 +570,12 @@ public Builder clear() {
           if (clientCredentialsBuilder_ != null) {
             clientCredentialsBuilder_.clear();
           }
    +      if (implicitBuilder_ != null) {
    +        implicitBuilder_.clear();
    +      }
    +      if (passwordBuilder_ != null) {
    +        passwordBuilder_.clear();
    +      }
           if (deviceCodeBuilder_ != null) {
             deviceCodeBuilder_.clear();
           }
    @@ -518,6 +628,14 @@ private void buildPartialOneofs(io.a2a.grpc.OAuthFlows result) {
               clientCredentialsBuilder_ != null) {
             result.flow_ = clientCredentialsBuilder_.build();
           }
    +      if (flowCase_ == 3 &&
    +          implicitBuilder_ != null) {
    +        result.flow_ = implicitBuilder_.build();
    +      }
    +      if (flowCase_ == 4 &&
    +          passwordBuilder_ != null) {
    +        result.flow_ = passwordBuilder_.build();
    +      }
           if (flowCase_ == 5 &&
               deviceCodeBuilder_ != null) {
             result.flow_ = deviceCodeBuilder_.build();
    @@ -545,6 +663,14 @@ public Builder mergeFrom(io.a2a.grpc.OAuthFlows other) {
               mergeClientCredentials(other.getClientCredentials());
               break;
             }
    +        case IMPLICIT: {
    +          mergeImplicit(other.getImplicit());
    +          break;
    +        }
    +        case PASSWORD: {
    +          mergePassword(other.getPassword());
    +          break;
    +        }
             case DEVICE_CODE: {
               mergeDeviceCode(other.getDeviceCode());
               break;
    @@ -593,6 +719,20 @@ public Builder mergeFrom(
                   flowCase_ = 2;
                   break;
                 } // case 18
    +            case 26: {
    +              input.readMessage(
    +                  internalGetImplicitFieldBuilder().getBuilder(),
    +                  extensionRegistry);
    +              flowCase_ = 3;
    +              break;
    +            } // case 26
    +            case 34: {
    +              input.readMessage(
    +                  internalGetPasswordFieldBuilder().getBuilder(),
    +                  extensionRegistry);
    +              flowCase_ = 4;
    +              break;
    +            } // case 34
                 case 42: {
                   input.readMessage(
                       internalGetDeviceCodeFieldBuilder().getBuilder(),
    @@ -988,6 +1128,298 @@ public io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBui
           return clientCredentialsBuilder_;
         }
     
    +    private com.google.protobuf.SingleFieldBuilder<
    +        io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> implicitBuilder_;
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     * @deprecated a2a.v1.OAuthFlows.implicit is deprecated.
    +     *     See a2a.proto;l=633
    +     * @return Whether the implicit field is set.
    +     */
    +    @java.lang.Override
    +    @java.lang.Deprecated public boolean hasImplicit() {
    +      return flowCase_ == 3;
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     * @deprecated a2a.v1.OAuthFlows.implicit is deprecated.
    +     *     See a2a.proto;l=633
    +     * @return The implicit.
    +     */
    +    @java.lang.Override
    +    @java.lang.Deprecated public io.a2a.grpc.ImplicitOAuthFlow getImplicit() {
    +      if (implicitBuilder_ == null) {
    +        if (flowCase_ == 3) {
    +          return (io.a2a.grpc.ImplicitOAuthFlow) flow_;
    +        }
    +        return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance();
    +      } else {
    +        if (flowCase_ == 3) {
    +          return implicitBuilder_.getMessage();
    +        }
    +        return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance();
    +      }
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder setImplicit(io.a2a.grpc.ImplicitOAuthFlow value) {
    +      if (implicitBuilder_ == null) {
    +        if (value == null) {
    +          throw new NullPointerException();
    +        }
    +        flow_ = value;
    +        onChanged();
    +      } else {
    +        implicitBuilder_.setMessage(value);
    +      }
    +      flowCase_ = 3;
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder setImplicit(
    +        io.a2a.grpc.ImplicitOAuthFlow.Builder builderForValue) {
    +      if (implicitBuilder_ == null) {
    +        flow_ = builderForValue.build();
    +        onChanged();
    +      } else {
    +        implicitBuilder_.setMessage(builderForValue.build());
    +      }
    +      flowCase_ = 3;
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder mergeImplicit(io.a2a.grpc.ImplicitOAuthFlow value) {
    +      if (implicitBuilder_ == null) {
    +        if (flowCase_ == 3 &&
    +            flow_ != io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance()) {
    +          flow_ = io.a2a.grpc.ImplicitOAuthFlow.newBuilder((io.a2a.grpc.ImplicitOAuthFlow) flow_)
    +              .mergeFrom(value).buildPartial();
    +        } else {
    +          flow_ = value;
    +        }
    +        onChanged();
    +      } else {
    +        if (flowCase_ == 3) {
    +          implicitBuilder_.mergeFrom(value);
    +        } else {
    +          implicitBuilder_.setMessage(value);
    +        }
    +      }
    +      flowCase_ = 3;
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder clearImplicit() {
    +      if (implicitBuilder_ == null) {
    +        if (flowCase_ == 3) {
    +          flowCase_ = 0;
    +          flow_ = null;
    +          onChanged();
    +        }
    +      } else {
    +        if (flowCase_ == 3) {
    +          flowCase_ = 0;
    +          flow_ = null;
    +        }
    +        implicitBuilder_.clear();
    +      }
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public io.a2a.grpc.ImplicitOAuthFlow.Builder getImplicitBuilder() {
    +      return internalGetImplicitFieldBuilder().getBuilder();
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    @java.lang.Override
    +    @java.lang.Deprecated public io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder() {
    +      if ((flowCase_ == 3) && (implicitBuilder_ != null)) {
    +        return implicitBuilder_.getMessageOrBuilder();
    +      } else {
    +        if (flowCase_ == 3) {
    +          return (io.a2a.grpc.ImplicitOAuthFlow) flow_;
    +        }
    +        return io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance();
    +      }
    +    }
    +    /**
    +     * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +     */
    +    private com.google.protobuf.SingleFieldBuilder<
    +        io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder> 
    +        internalGetImplicitFieldBuilder() {
    +      if (implicitBuilder_ == null) {
    +        if (!(flowCase_ == 3)) {
    +          flow_ = io.a2a.grpc.ImplicitOAuthFlow.getDefaultInstance();
    +        }
    +        implicitBuilder_ = new com.google.protobuf.SingleFieldBuilder<
    +            io.a2a.grpc.ImplicitOAuthFlow, io.a2a.grpc.ImplicitOAuthFlow.Builder, io.a2a.grpc.ImplicitOAuthFlowOrBuilder>(
    +                (io.a2a.grpc.ImplicitOAuthFlow) flow_,
    +                getParentForChildren(),
    +                isClean());
    +        flow_ = null;
    +      }
    +      flowCase_ = 3;
    +      onChanged();
    +      return implicitBuilder_;
    +    }
    +
    +    private com.google.protobuf.SingleFieldBuilder<
    +        io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> passwordBuilder_;
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     * @deprecated a2a.v1.OAuthFlows.password is deprecated.
    +     *     See a2a.proto;l=634
    +     * @return Whether the password field is set.
    +     */
    +    @java.lang.Override
    +    @java.lang.Deprecated public boolean hasPassword() {
    +      return flowCase_ == 4;
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     * @deprecated a2a.v1.OAuthFlows.password is deprecated.
    +     *     See a2a.proto;l=634
    +     * @return The password.
    +     */
    +    @java.lang.Override
    +    @java.lang.Deprecated public io.a2a.grpc.PasswordOAuthFlow getPassword() {
    +      if (passwordBuilder_ == null) {
    +        if (flowCase_ == 4) {
    +          return (io.a2a.grpc.PasswordOAuthFlow) flow_;
    +        }
    +        return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance();
    +      } else {
    +        if (flowCase_ == 4) {
    +          return passwordBuilder_.getMessage();
    +        }
    +        return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance();
    +      }
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder setPassword(io.a2a.grpc.PasswordOAuthFlow value) {
    +      if (passwordBuilder_ == null) {
    +        if (value == null) {
    +          throw new NullPointerException();
    +        }
    +        flow_ = value;
    +        onChanged();
    +      } else {
    +        passwordBuilder_.setMessage(value);
    +      }
    +      flowCase_ = 4;
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder setPassword(
    +        io.a2a.grpc.PasswordOAuthFlow.Builder builderForValue) {
    +      if (passwordBuilder_ == null) {
    +        flow_ = builderForValue.build();
    +        onChanged();
    +      } else {
    +        passwordBuilder_.setMessage(builderForValue.build());
    +      }
    +      flowCase_ = 4;
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder mergePassword(io.a2a.grpc.PasswordOAuthFlow value) {
    +      if (passwordBuilder_ == null) {
    +        if (flowCase_ == 4 &&
    +            flow_ != io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance()) {
    +          flow_ = io.a2a.grpc.PasswordOAuthFlow.newBuilder((io.a2a.grpc.PasswordOAuthFlow) flow_)
    +              .mergeFrom(value).buildPartial();
    +        } else {
    +          flow_ = value;
    +        }
    +        onChanged();
    +      } else {
    +        if (flowCase_ == 4) {
    +          passwordBuilder_.mergeFrom(value);
    +        } else {
    +          passwordBuilder_.setMessage(value);
    +        }
    +      }
    +      flowCase_ = 4;
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public Builder clearPassword() {
    +      if (passwordBuilder_ == null) {
    +        if (flowCase_ == 4) {
    +          flowCase_ = 0;
    +          flow_ = null;
    +          onChanged();
    +        }
    +      } else {
    +        if (flowCase_ == 4) {
    +          flowCase_ = 0;
    +          flow_ = null;
    +        }
    +        passwordBuilder_.clear();
    +      }
    +      return this;
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    @java.lang.Deprecated public io.a2a.grpc.PasswordOAuthFlow.Builder getPasswordBuilder() {
    +      return internalGetPasswordFieldBuilder().getBuilder();
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    @java.lang.Override
    +    @java.lang.Deprecated public io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder() {
    +      if ((flowCase_ == 4) && (passwordBuilder_ != null)) {
    +        return passwordBuilder_.getMessageOrBuilder();
    +      } else {
    +        if (flowCase_ == 4) {
    +          return (io.a2a.grpc.PasswordOAuthFlow) flow_;
    +        }
    +        return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance();
    +      }
    +    }
    +    /**
    +     * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +     */
    +    private com.google.protobuf.SingleFieldBuilder<
    +        io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder> 
    +        internalGetPasswordFieldBuilder() {
    +      if (passwordBuilder_ == null) {
    +        if (!(flowCase_ == 4)) {
    +          flow_ = io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance();
    +        }
    +        passwordBuilder_ = new com.google.protobuf.SingleFieldBuilder<
    +            io.a2a.grpc.PasswordOAuthFlow, io.a2a.grpc.PasswordOAuthFlow.Builder, io.a2a.grpc.PasswordOAuthFlowOrBuilder>(
    +                (io.a2a.grpc.PasswordOAuthFlow) flow_,
    +                getParentForChildren(),
    +                isClean());
    +        flow_ = null;
    +      }
    +      flowCase_ = 4;
    +      onChanged();
    +      return passwordBuilder_;
    +    }
    +
         private com.google.protobuf.SingleFieldBuilder<
             io.a2a.grpc.DeviceCodeOAuthFlow, io.a2a.grpc.DeviceCodeOAuthFlow.Builder, io.a2a.grpc.DeviceCodeOAuthFlowOrBuilder> deviceCodeBuilder_;
         /**
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java
    index cb84fa83e..1debd6d12 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/OAuthFlowsOrBuilder.java
    @@ -64,6 +64,44 @@ public interface OAuthFlowsOrBuilder extends
        */
       io.a2a.grpc.ClientCredentialsOAuthFlowOrBuilder getClientCredentialsOrBuilder();
     
    +  /**
    +   * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.implicit is deprecated.
    +   *     See a2a.proto;l=633
    +   * @return Whether the implicit field is set.
    +   */
    +  @java.lang.Deprecated boolean hasImplicit();
    +  /**
    +   * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.implicit is deprecated.
    +   *     See a2a.proto;l=633
    +   * @return The implicit.
    +   */
    +  @java.lang.Deprecated io.a2a.grpc.ImplicitOAuthFlow getImplicit();
    +  /**
    +   * .a2a.v1.ImplicitOAuthFlow implicit = 3 [deprecated = true];
    +   */
    +  @java.lang.Deprecated io.a2a.grpc.ImplicitOAuthFlowOrBuilder getImplicitOrBuilder();
    +
    +  /**
    +   * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.password is deprecated.
    +   *     See a2a.proto;l=634
    +   * @return Whether the password field is set.
    +   */
    +  @java.lang.Deprecated boolean hasPassword();
    +  /**
    +   * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +   * @deprecated a2a.v1.OAuthFlows.password is deprecated.
    +   *     See a2a.proto;l=634
    +   * @return The password.
    +   */
    +  @java.lang.Deprecated io.a2a.grpc.PasswordOAuthFlow getPassword();
    +  /**
    +   * .a2a.v1.PasswordOAuthFlow password = 4 [deprecated = true];
    +   */
    +  @java.lang.Deprecated io.a2a.grpc.PasswordOAuthFlowOrBuilder getPasswordOrBuilder();
    +
       /**
        * 
        * Configuration for the OAuth Device Code flow.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Part.java b/spec-grpc/src/main/java/io/a2a/grpc/Part.java
    index 511d10dae..879380c96 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/Part.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/Part.java
    @@ -35,6 +35,8 @@ private Part(com.google.protobuf.GeneratedMessage.Builder builder) {
         super(builder);
       }
       private Part() {
    +    filename_ = "";
    +    mediaType_ = "";
       }
     
       public static final com.google.protobuf.Descriptors.Descriptor
    @@ -51,18 +53,19 @@ private Part() {
       }
     
       private int bitField0_;
    -  private int partCase_ = 0;
    +  private int contentCase_ = 0;
       @SuppressWarnings("serial")
    -  private java.lang.Object part_;
    -  public enum PartCase
    +  private java.lang.Object content_;
    +  public enum ContentCase
           implements com.google.protobuf.Internal.EnumLite,
               com.google.protobuf.AbstractMessage.InternalOneOfEnum {
         TEXT(1),
    -    FILE(2),
    -    DATA(3),
    -    PART_NOT_SET(0);
    +    RAW(2),
    +    URL(3),
    +    DATA(4),
    +    CONTENT_NOT_SET(0);
         private final int value;
    -    private PartCase(int value) {
    +    private ContentCase(int value) {
           this.value = value;
         }
         /**
    @@ -71,16 +74,17 @@ private PartCase(int value) {
          * @deprecated Use {@link #forNumber(int)} instead.
          */
         @java.lang.Deprecated
    -    public static PartCase valueOf(int value) {
    +    public static ContentCase valueOf(int value) {
           return forNumber(value);
         }
     
    -    public static PartCase forNumber(int value) {
    +    public static ContentCase forNumber(int value) {
           switch (value) {
             case 1: return TEXT;
    -        case 2: return FILE;
    -        case 3: return DATA;
    -        case 0: return PART_NOT_SET;
    +        case 2: return RAW;
    +        case 3: return URL;
    +        case 4: return DATA;
    +        case 0: return CONTENT_NOT_SET;
             default: return null;
           }
         }
    @@ -89,10 +93,10 @@ public int getNumber() {
         }
       };
     
    -  public PartCase
    -  getPartCase() {
    -    return PartCase.forNumber(
    -        partCase_);
    +  public ContentCase
    +  getContentCase() {
    +    return ContentCase.forNumber(
    +        contentCase_);
       }
     
       public static final int TEXT_FIELD_NUMBER = 1;
    @@ -105,7 +109,7 @@ public int getNumber() {
        * @return Whether the text field is set.
        */
       public boolean hasText() {
    -    return partCase_ == 1;
    +    return contentCase_ == 1;
       }
       /**
        * 
    @@ -117,8 +121,8 @@ public boolean hasText() {
        */
       public java.lang.String getText() {
         java.lang.Object ref = "";
    -    if (partCase_ == 1) {
    -      ref = part_;
    +    if (contentCase_ == 1) {
    +      ref = content_;
         }
         if (ref instanceof java.lang.String) {
           return (java.lang.String) ref;
    @@ -126,8 +130,8 @@ public java.lang.String getText() {
           com.google.protobuf.ByteString bs = 
               (com.google.protobuf.ByteString) ref;
           java.lang.String s = bs.toStringUtf8();
    -      if (partCase_ == 1) {
    -        part_ = s;
    +      if (contentCase_ == 1) {
    +        content_ = s;
           }
           return s;
         }
    @@ -143,15 +147,15 @@ public java.lang.String getText() {
       public com.google.protobuf.ByteString
           getTextBytes() {
         java.lang.Object ref = "";
    -    if (partCase_ == 1) {
    -      ref = part_;
    +    if (contentCase_ == 1) {
    +      ref = content_;
         }
         if (ref instanceof java.lang.String) {
           com.google.protobuf.ByteString b = 
               com.google.protobuf.ByteString.copyFromUtf8(
                   (java.lang.String) ref);
    -      if (partCase_ == 1) {
    -        part_ = b;
    +      if (contentCase_ == 1) {
    +        content_ = b;
           }
           return b;
         } else {
    @@ -159,100 +163,150 @@ public java.lang.String getText() {
         }
       }
     
    -  public static final int FILE_FIELD_NUMBER = 2;
    +  public static final int RAW_FIELD_NUMBER = 2;
       /**
        * 
    -   * The file content, represented as either a URI or as base64-encoded bytes.
    +   * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
        * 
    * - * .a2a.v1.FilePart file = 2; - * @return Whether the file field is set. + * bytes raw = 2; + * @return Whether the raw field is set. */ @java.lang.Override - public boolean hasFile() { - return partCase_ == 2; + public boolean hasRaw() { + return contentCase_ == 2; } /** *
    -   * The file content, represented as either a URI or as base64-encoded bytes.
    +   * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
        * 
    * - * .a2a.v1.FilePart file = 2; - * @return The file. + * bytes raw = 2; + * @return The raw. */ @java.lang.Override - public io.a2a.grpc.FilePart getFile() { - if (partCase_ == 2) { - return (io.a2a.grpc.FilePart) part_; + public com.google.protobuf.ByteString getRaw() { + if (contentCase_ == 2) { + return (com.google.protobuf.ByteString) content_; } - return io.a2a.grpc.FilePart.getDefaultInstance(); + return com.google.protobuf.ByteString.EMPTY; } + + public static final int URL_FIELD_NUMBER = 3; /** *
    -   * The file content, represented as either a URI or as base64-encoded bytes.
    +   * A URL pointing to the file's content.
        * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @return Whether the url field is set. */ - @java.lang.Override - public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { - if (partCase_ == 2) { - return (io.a2a.grpc.FilePart) part_; + public boolean hasUrl() { + return contentCase_ == 3; + } + /** + *
    +   * A URL pointing to the file's content.
    +   * 
    + * + * string url = 3; + * @return The url. + */ + public java.lang.String getUrl() { + java.lang.Object ref = ""; + if (contentCase_ == 3) { + ref = content_; + } + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (contentCase_ == 3) { + content_ = s; + } + return s; + } + } + /** + *
    +   * A URL pointing to the file's content.
    +   * 
    + * + * string url = 3; + * @return The bytes for url. + */ + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = ""; + if (contentCase_ == 3) { + ref = content_; + } + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (contentCase_ == 3) { + content_ = b; + } + return b; + } else { + return (com.google.protobuf.ByteString) ref; } - return io.a2a.grpc.FilePart.getDefaultInstance(); } - public static final int DATA_FIELD_NUMBER = 3; + public static final int DATA_FIELD_NUMBER = 4; /** *
    -   * The structured data content.
    +   * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
        * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; * @return Whether the data field is set. */ @java.lang.Override public boolean hasData() { - return partCase_ == 3; + return contentCase_ == 4; } /** *
    -   * The structured data content.
    +   * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
        * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; * @return The data. */ @java.lang.Override - public io.a2a.grpc.DataPart getData() { - if (partCase_ == 3) { - return (io.a2a.grpc.DataPart) part_; + public com.google.protobuf.Value getData() { + if (contentCase_ == 4) { + return (com.google.protobuf.Value) content_; } - return io.a2a.grpc.DataPart.getDefaultInstance(); + return com.google.protobuf.Value.getDefaultInstance(); } /** *
    -   * The structured data content.
    +   * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
        * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ @java.lang.Override - public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { - if (partCase_ == 3) { - return (io.a2a.grpc.DataPart) part_; + public com.google.protobuf.ValueOrBuilder getDataOrBuilder() { + if (contentCase_ == 4) { + return (com.google.protobuf.Value) content_; } - return io.a2a.grpc.DataPart.getDefaultInstance(); + return com.google.protobuf.Value.getDefaultInstance(); } - public static final int METADATA_FIELD_NUMBER = 4; + public static final int METADATA_FIELD_NUMBER = 5; private com.google.protobuf.Struct metadata_; /** *
        * Optional metadata associated with this part.
        * 
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; * @return Whether the metadata field is set. */ @java.lang.Override @@ -264,7 +318,7 @@ public boolean hasMetadata() { * Optional metadata associated with this part. *
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; * @return The metadata. */ @java.lang.Override @@ -276,13 +330,109 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata associated with this part. *
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ @java.lang.Override public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { return metadata_ == null ? com.google.protobuf.Struct.getDefaultInstance() : metadata_; } + public static final int FILENAME_FIELD_NUMBER = 6; + @SuppressWarnings("serial") + private volatile java.lang.Object filename_ = ""; + /** + *
    +   * An optional name for the file (e.g., "document.pdf").
    +   * 
    + * + * string filename = 6; + * @return The filename. + */ + @java.lang.Override + public java.lang.String getFilename() { + java.lang.Object ref = filename_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + filename_ = s; + return s; + } + } + /** + *
    +   * An optional name for the file (e.g., "document.pdf").
    +   * 
    + * + * string filename = 6; + * @return The bytes for filename. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getFilenameBytes() { + java.lang.Object ref = filename_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + filename_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int MEDIA_TYPE_FIELD_NUMBER = 7; + @SuppressWarnings("serial") + private volatile java.lang.Object mediaType_ = ""; + /** + *
    +   * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +   * This field is available for all part types.
    +   * 
    + * + * string media_type = 7; + * @return The mediaType. + */ + @java.lang.Override + public java.lang.String getMediaType() { + java.lang.Object ref = mediaType_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + mediaType_ = s; + return s; + } + } + /** + *
    +   * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +   * This field is available for all part types.
    +   * 
    + * + * string media_type = 7; + * @return The bytes for mediaType. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getMediaTypeBytes() { + java.lang.Object ref = mediaType_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + mediaType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -297,17 +447,27 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (partCase_ == 1) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, part_); + if (contentCase_ == 1) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, content_); } - if (partCase_ == 2) { - output.writeMessage(2, (io.a2a.grpc.FilePart) part_); + if (contentCase_ == 2) { + output.writeBytes( + 2, (com.google.protobuf.ByteString) content_); } - if (partCase_ == 3) { - output.writeMessage(3, (io.a2a.grpc.DataPart) part_); + if (contentCase_ == 3) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, content_); + } + if (contentCase_ == 4) { + output.writeMessage(4, (com.google.protobuf.Value) content_); } if (((bitField0_ & 0x00000001) != 0)) { - output.writeMessage(4, getMetadata()); + output.writeMessage(5, getMetadata()); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(filename_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 6, filename_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mediaType_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 7, mediaType_); } getUnknownFields().writeTo(output); } @@ -318,20 +478,30 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (partCase_ == 1) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, part_); + if (contentCase_ == 1) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, content_); } - if (partCase_ == 2) { + if (contentCase_ == 2) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, (io.a2a.grpc.FilePart) part_); + .computeBytesSize( + 2, (com.google.protobuf.ByteString) content_); + } + if (contentCase_ == 3) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, content_); } - if (partCase_ == 3) { + if (contentCase_ == 4) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, (io.a2a.grpc.DataPart) part_); + .computeMessageSize(4, (com.google.protobuf.Value) content_); } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, getMetadata()); + .computeMessageSize(5, getMetadata()); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(filename_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(6, filename_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(mediaType_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(7, mediaType_); } size += getUnknownFields().getSerializedSize(); memoizedSize = size; @@ -353,17 +523,25 @@ public boolean equals(final java.lang.Object obj) { if (!getMetadata() .equals(other.getMetadata())) return false; } - if (!getPartCase().equals(other.getPartCase())) return false; - switch (partCase_) { + if (!getFilename() + .equals(other.getFilename())) return false; + if (!getMediaType() + .equals(other.getMediaType())) return false; + if (!getContentCase().equals(other.getContentCase())) return false; + switch (contentCase_) { case 1: if (!getText() .equals(other.getText())) return false; break; case 2: - if (!getFile() - .equals(other.getFile())) return false; + if (!getRaw() + .equals(other.getRaw())) return false; break; case 3: + if (!getUrl() + .equals(other.getUrl())) return false; + break; + case 4: if (!getData() .equals(other.getData())) return false; break; @@ -385,16 +563,24 @@ public int hashCode() { hash = (37 * hash) + METADATA_FIELD_NUMBER; hash = (53 * hash) + getMetadata().hashCode(); } - switch (partCase_) { + hash = (37 * hash) + FILENAME_FIELD_NUMBER; + hash = (53 * hash) + getFilename().hashCode(); + hash = (37 * hash) + MEDIA_TYPE_FIELD_NUMBER; + hash = (53 * hash) + getMediaType().hashCode(); + switch (contentCase_) { case 1: hash = (37 * hash) + TEXT_FIELD_NUMBER; hash = (53 * hash) + getText().hashCode(); break; case 2: - hash = (37 * hash) + FILE_FIELD_NUMBER; - hash = (53 * hash) + getFile().hashCode(); + hash = (37 * hash) + RAW_FIELD_NUMBER; + hash = (53 * hash) + getRaw().hashCode(); break; case 3: + hash = (37 * hash) + URL_FIELD_NUMBER; + hash = (53 * hash) + getUrl().hashCode(); + break; + case 4: hash = (37 * hash) + DATA_FIELD_NUMBER; hash = (53 * hash) + getData().hashCode(); break; @@ -545,9 +731,6 @@ private void maybeForceBuilderInitialization() { public Builder clear() { super.clear(); bitField0_ = 0; - if (fileBuilder_ != null) { - fileBuilder_.clear(); - } if (dataBuilder_ != null) { dataBuilder_.clear(); } @@ -556,8 +739,10 @@ public Builder clear() { metadataBuilder_.dispose(); metadataBuilder_ = null; } - partCase_ = 0; - part_ = null; + filename_ = ""; + mediaType_ = ""; + contentCase_ = 0; + content_ = null; return this; } @@ -593,25 +778,27 @@ public io.a2a.grpc.Part buildPartial() { private void buildPartial0(io.a2a.grpc.Part result) { int from_bitField0_ = bitField0_; int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000008) != 0)) { + if (((from_bitField0_ & 0x00000010) != 0)) { result.metadata_ = metadataBuilder_ == null ? metadata_ : metadataBuilder_.build(); to_bitField0_ |= 0x00000001; } + if (((from_bitField0_ & 0x00000020) != 0)) { + result.filename_ = filename_; + } + if (((from_bitField0_ & 0x00000040) != 0)) { + result.mediaType_ = mediaType_; + } result.bitField0_ |= to_bitField0_; } private void buildPartialOneofs(io.a2a.grpc.Part result) { - result.partCase_ = partCase_; - result.part_ = this.part_; - if (partCase_ == 2 && - fileBuilder_ != null) { - result.part_ = fileBuilder_.build(); - } - if (partCase_ == 3 && + result.contentCase_ = contentCase_; + result.content_ = this.content_; + if (contentCase_ == 4 && dataBuilder_ != null) { - result.part_ = dataBuilder_.build(); + result.content_ = dataBuilder_.build(); } } @@ -630,22 +817,38 @@ public Builder mergeFrom(io.a2a.grpc.Part other) { if (other.hasMetadata()) { mergeMetadata(other.getMetadata()); } - switch (other.getPartCase()) { + if (!other.getFilename().isEmpty()) { + filename_ = other.filename_; + bitField0_ |= 0x00000020; + onChanged(); + } + if (!other.getMediaType().isEmpty()) { + mediaType_ = other.mediaType_; + bitField0_ |= 0x00000040; + onChanged(); + } + switch (other.getContentCase()) { case TEXT: { - partCase_ = 1; - part_ = other.part_; + contentCase_ = 1; + content_ = other.content_; onChanged(); break; } - case FILE: { - mergeFile(other.getFile()); + case RAW: { + setRaw(other.getRaw()); + break; + } + case URL: { + contentCase_ = 3; + content_ = other.content_; + onChanged(); break; } case DATA: { mergeData(other.getData()); break; } - case PART_NOT_SET: { + case CONTENT_NOT_SET: { break; } } @@ -677,31 +880,45 @@ public Builder mergeFrom( break; case 10: { java.lang.String s = input.readStringRequireUtf8(); - partCase_ = 1; - part_ = s; + contentCase_ = 1; + content_ = s; break; } // case 10 case 18: { - input.readMessage( - internalGetFileFieldBuilder().getBuilder(), - extensionRegistry); - partCase_ = 2; + content_ = input.readBytes(); + contentCase_ = 2; break; } // case 18 case 26: { + java.lang.String s = input.readStringRequireUtf8(); + contentCase_ = 3; + content_ = s; + break; + } // case 26 + case 34: { input.readMessage( internalGetDataFieldBuilder().getBuilder(), extensionRegistry); - partCase_ = 3; + contentCase_ = 4; break; - } // case 26 - case 34: { + } // case 34 + case 42: { input.readMessage( internalGetMetadataFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; break; - } // case 34 + } // case 42 + case 50: { + filename_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000020; + break; + } // case 50 + case 58: { + mediaType_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000040; + break; + } // case 58 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -717,17 +934,17 @@ public Builder mergeFrom( } // finally return this; } - private int partCase_ = 0; - private java.lang.Object part_; - public PartCase - getPartCase() { - return PartCase.forNumber( - partCase_); + private int contentCase_ = 0; + private java.lang.Object content_; + public ContentCase + getContentCase() { + return ContentCase.forNumber( + contentCase_); } - public Builder clearPart() { - partCase_ = 0; - part_ = null; + public Builder clearContent() { + contentCase_ = 0; + content_ = null; onChanged(); return this; } @@ -744,7 +961,7 @@ public Builder clearPart() { */ @java.lang.Override public boolean hasText() { - return partCase_ == 1; + return contentCase_ == 1; } /** *
    @@ -757,15 +974,15 @@ public boolean hasText() {
         @java.lang.Override
         public java.lang.String getText() {
           java.lang.Object ref = "";
    -      if (partCase_ == 1) {
    -        ref = part_;
    +      if (contentCase_ == 1) {
    +        ref = content_;
           }
           if (!(ref instanceof java.lang.String)) {
             com.google.protobuf.ByteString bs =
                 (com.google.protobuf.ByteString) ref;
             java.lang.String s = bs.toStringUtf8();
    -        if (partCase_ == 1) {
    -          part_ = s;
    +        if (contentCase_ == 1) {
    +          content_ = s;
             }
             return s;
           } else {
    @@ -784,15 +1001,15 @@ public java.lang.String getText() {
         public com.google.protobuf.ByteString
             getTextBytes() {
           java.lang.Object ref = "";
    -      if (partCase_ == 1) {
    -        ref = part_;
    +      if (contentCase_ == 1) {
    +        ref = content_;
           }
           if (ref instanceof String) {
             com.google.protobuf.ByteString b = 
                 com.google.protobuf.ByteString.copyFromUtf8(
                     (java.lang.String) ref);
    -        if (partCase_ == 1) {
    -          part_ = b;
    +        if (contentCase_ == 1) {
    +          content_ = b;
             }
             return b;
           } else {
    @@ -811,8 +1028,8 @@ public java.lang.String getText() {
         public Builder setText(
             java.lang.String value) {
           if (value == null) { throw new NullPointerException(); }
    -      partCase_ = 1;
    -      part_ = value;
    +      contentCase_ = 1;
    +      content_ = value;
           onChanged();
           return this;
         }
    @@ -825,9 +1042,9 @@ public Builder setText(
          * @return This builder for chaining.
          */
         public Builder clearText() {
    -      if (partCase_ == 1) {
    -        partCase_ = 0;
    -        part_ = null;
    +      if (contentCase_ == 1) {
    +        contentCase_ = 0;
    +        content_ = null;
             onChanged();
           }
           return this;
    @@ -845,309 +1062,306 @@ public Builder setTextBytes(
             com.google.protobuf.ByteString value) {
           if (value == null) { throw new NullPointerException(); }
           checkByteStringIsUtf8(value);
    -      partCase_ = 1;
    -      part_ = value;
    +      contentCase_ = 1;
    +      content_ = value;
           onChanged();
           return this;
         }
     
    -    private com.google.protobuf.SingleFieldBuilder<
    -        io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> fileBuilder_;
         /**
          * 
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
          * 
    * - * .a2a.v1.FilePart file = 2; - * @return Whether the file field is set. + * bytes raw = 2; + * @return Whether the raw field is set. */ - @java.lang.Override - public boolean hasFile() { - return partCase_ == 2; + public boolean hasRaw() { + return contentCase_ == 2; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
          * 
    * - * .a2a.v1.FilePart file = 2; - * @return The file. + * bytes raw = 2; + * @return The raw. */ - @java.lang.Override - public io.a2a.grpc.FilePart getFile() { - if (fileBuilder_ == null) { - if (partCase_ == 2) { - return (io.a2a.grpc.FilePart) part_; - } - return io.a2a.grpc.FilePart.getDefaultInstance(); - } else { - if (partCase_ == 2) { - return fileBuilder_.getMessage(); - } - return io.a2a.grpc.FilePart.getDefaultInstance(); + public com.google.protobuf.ByteString getRaw() { + if (contentCase_ == 2) { + return (com.google.protobuf.ByteString) content_; } + return com.google.protobuf.ByteString.EMPTY; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
          * 
    * - * .a2a.v1.FilePart file = 2; + * bytes raw = 2; + * @param value The raw to set. + * @return This builder for chaining. */ - public Builder setFile(io.a2a.grpc.FilePart value) { - if (fileBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - part_ = value; - onChanged(); - } else { - fileBuilder_.setMessage(value); - } - partCase_ = 2; + public Builder setRaw(com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + contentCase_ = 2; + content_ = value; + onChanged(); return this; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
          * 
    * - * .a2a.v1.FilePart file = 2; + * bytes raw = 2; + * @return This builder for chaining. */ - public Builder setFile( - io.a2a.grpc.FilePart.Builder builderForValue) { - if (fileBuilder_ == null) { - part_ = builderForValue.build(); + public Builder clearRaw() { + if (contentCase_ == 2) { + contentCase_ = 0; + content_ = null; onChanged(); - } else { - fileBuilder_.setMessage(builderForValue.build()); } - partCase_ = 2; return this; } + /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * A URL pointing to the file's content.
          * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @return Whether the url field is set. */ - public Builder mergeFile(io.a2a.grpc.FilePart value) { - if (fileBuilder_ == null) { - if (partCase_ == 2 && - part_ != io.a2a.grpc.FilePart.getDefaultInstance()) { - part_ = io.a2a.grpc.FilePart.newBuilder((io.a2a.grpc.FilePart) part_) - .mergeFrom(value).buildPartial(); - } else { - part_ = value; + @java.lang.Override + public boolean hasUrl() { + return contentCase_ == 3; + } + /** + *
    +     * A URL pointing to the file's content.
    +     * 
    + * + * string url = 3; + * @return The url. + */ + @java.lang.Override + public java.lang.String getUrl() { + java.lang.Object ref = ""; + if (contentCase_ == 3) { + ref = content_; + } + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (contentCase_ == 3) { + content_ = s; } - onChanged(); + return s; } else { - if (partCase_ == 2) { - fileBuilder_.mergeFrom(value); - } else { - fileBuilder_.setMessage(value); - } + return (java.lang.String) ref; } - partCase_ = 2; - return this; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * A URL pointing to the file's content.
          * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @return The bytes for url. */ - public Builder clearFile() { - if (fileBuilder_ == null) { - if (partCase_ == 2) { - partCase_ = 0; - part_ = null; - onChanged(); + @java.lang.Override + public com.google.protobuf.ByteString + getUrlBytes() { + java.lang.Object ref = ""; + if (contentCase_ == 3) { + ref = content_; + } + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + if (contentCase_ == 3) { + content_ = b; } + return b; } else { - if (partCase_ == 2) { - partCase_ = 0; - part_ = null; - } - fileBuilder_.clear(); + return (com.google.protobuf.ByteString) ref; } - return this; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * A URL pointing to the file's content.
          * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @param value The url to set. + * @return This builder for chaining. */ - public io.a2a.grpc.FilePart.Builder getFileBuilder() { - return internalGetFileFieldBuilder().getBuilder(); + public Builder setUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + contentCase_ = 3; + content_ = value; + onChanged(); + return this; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * A URL pointing to the file's content.
          * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @return This builder for chaining. */ - @java.lang.Override - public io.a2a.grpc.FilePartOrBuilder getFileOrBuilder() { - if ((partCase_ == 2) && (fileBuilder_ != null)) { - return fileBuilder_.getMessageOrBuilder(); - } else { - if (partCase_ == 2) { - return (io.a2a.grpc.FilePart) part_; - } - return io.a2a.grpc.FilePart.getDefaultInstance(); + public Builder clearUrl() { + if (contentCase_ == 3) { + contentCase_ = 0; + content_ = null; + onChanged(); } + return this; } /** *
    -     * The file content, represented as either a URI or as base64-encoded bytes.
    +     * A URL pointing to the file's content.
          * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @param value The bytes for url to set. + * @return This builder for chaining. */ - private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder> - internalGetFileFieldBuilder() { - if (fileBuilder_ == null) { - if (!(partCase_ == 2)) { - part_ = io.a2a.grpc.FilePart.getDefaultInstance(); - } - fileBuilder_ = new com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.FilePart, io.a2a.grpc.FilePart.Builder, io.a2a.grpc.FilePartOrBuilder>( - (io.a2a.grpc.FilePart) part_, - getParentForChildren(), - isClean()); - part_ = null; - } - partCase_ = 2; + public Builder setUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + contentCase_ = 3; + content_ = value; onChanged(); - return fileBuilder_; + return this; } private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> dataBuilder_; + com.google.protobuf.Value, com.google.protobuf.Value.Builder, com.google.protobuf.ValueOrBuilder> dataBuilder_; /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; * @return Whether the data field is set. */ @java.lang.Override public boolean hasData() { - return partCase_ == 3; + return contentCase_ == 4; } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; * @return The data. */ @java.lang.Override - public io.a2a.grpc.DataPart getData() { + public com.google.protobuf.Value getData() { if (dataBuilder_ == null) { - if (partCase_ == 3) { - return (io.a2a.grpc.DataPart) part_; + if (contentCase_ == 4) { + return (com.google.protobuf.Value) content_; } - return io.a2a.grpc.DataPart.getDefaultInstance(); + return com.google.protobuf.Value.getDefaultInstance(); } else { - if (partCase_ == 3) { + if (contentCase_ == 4) { return dataBuilder_.getMessage(); } - return io.a2a.grpc.DataPart.getDefaultInstance(); + return com.google.protobuf.Value.getDefaultInstance(); } } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ - public Builder setData(io.a2a.grpc.DataPart value) { + public Builder setData(com.google.protobuf.Value value) { if (dataBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - part_ = value; + content_ = value; onChanged(); } else { dataBuilder_.setMessage(value); } - partCase_ = 3; + contentCase_ = 4; return this; } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ public Builder setData( - io.a2a.grpc.DataPart.Builder builderForValue) { + com.google.protobuf.Value.Builder builderForValue) { if (dataBuilder_ == null) { - part_ = builderForValue.build(); + content_ = builderForValue.build(); onChanged(); } else { dataBuilder_.setMessage(builderForValue.build()); } - partCase_ = 3; + contentCase_ = 4; return this; } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ - public Builder mergeData(io.a2a.grpc.DataPart value) { + public Builder mergeData(com.google.protobuf.Value value) { if (dataBuilder_ == null) { - if (partCase_ == 3 && - part_ != io.a2a.grpc.DataPart.getDefaultInstance()) { - part_ = io.a2a.grpc.DataPart.newBuilder((io.a2a.grpc.DataPart) part_) + if (contentCase_ == 4 && + content_ != com.google.protobuf.Value.getDefaultInstance()) { + content_ = com.google.protobuf.Value.newBuilder((com.google.protobuf.Value) content_) .mergeFrom(value).buildPartial(); } else { - part_ = value; + content_ = value; } onChanged(); } else { - if (partCase_ == 3) { + if (contentCase_ == 4) { dataBuilder_.mergeFrom(value); } else { dataBuilder_.setMessage(value); } } - partCase_ = 3; + contentCase_ = 4; return this; } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ public Builder clearData() { if (dataBuilder_ == null) { - if (partCase_ == 3) { - partCase_ = 0; - part_ = null; + if (contentCase_ == 4) { + contentCase_ = 0; + content_ = null; onChanged(); } } else { - if (partCase_ == 3) { - partCase_ = 0; - part_ = null; + if (contentCase_ == 4) { + contentCase_ = 0; + content_ = null; } dataBuilder_.clear(); } @@ -1155,54 +1369,54 @@ public Builder clearData() { } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ - public io.a2a.grpc.DataPart.Builder getDataBuilder() { + public com.google.protobuf.Value.Builder getDataBuilder() { return internalGetDataFieldBuilder().getBuilder(); } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ @java.lang.Override - public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { - if ((partCase_ == 3) && (dataBuilder_ != null)) { + public com.google.protobuf.ValueOrBuilder getDataOrBuilder() { + if ((contentCase_ == 4) && (dataBuilder_ != null)) { return dataBuilder_.getMessageOrBuilder(); } else { - if (partCase_ == 3) { - return (io.a2a.grpc.DataPart) part_; + if (contentCase_ == 4) { + return (com.google.protobuf.Value) content_; } - return io.a2a.grpc.DataPart.getDefaultInstance(); + return com.google.protobuf.Value.getDefaultInstance(); } } /** *
    -     * The structured data content.
    +     * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
          * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ private com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder> + com.google.protobuf.Value, com.google.protobuf.Value.Builder, com.google.protobuf.ValueOrBuilder> internalGetDataFieldBuilder() { if (dataBuilder_ == null) { - if (!(partCase_ == 3)) { - part_ = io.a2a.grpc.DataPart.getDefaultInstance(); + if (!(contentCase_ == 4)) { + content_ = com.google.protobuf.Value.getDefaultInstance(); } dataBuilder_ = new com.google.protobuf.SingleFieldBuilder< - io.a2a.grpc.DataPart, io.a2a.grpc.DataPart.Builder, io.a2a.grpc.DataPartOrBuilder>( - (io.a2a.grpc.DataPart) part_, + com.google.protobuf.Value, com.google.protobuf.Value.Builder, com.google.protobuf.ValueOrBuilder>( + (com.google.protobuf.Value) content_, getParentForChildren(), isClean()); - part_ = null; + content_ = null; } - partCase_ = 3; + contentCase_ = 4; onChanged(); return dataBuilder_; } @@ -1215,18 +1429,18 @@ public io.a2a.grpc.DataPartOrBuilder getDataOrBuilder() { * Optional metadata associated with this part. *
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; * @return Whether the metadata field is set. */ public boolean hasMetadata() { - return ((bitField0_ & 0x00000008) != 0); + return ((bitField0_ & 0x00000010) != 0); } /** *
          * Optional metadata associated with this part.
          * 
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; * @return The metadata. */ public com.google.protobuf.Struct getMetadata() { @@ -1241,7 +1455,7 @@ public com.google.protobuf.Struct getMetadata() { * Optional metadata associated with this part. *
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ public Builder setMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { @@ -1252,7 +1466,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { } else { metadataBuilder_.setMessage(value); } - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); return this; } @@ -1261,7 +1475,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) { * Optional metadata associated with this part. *
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ public Builder setMetadata( com.google.protobuf.Struct.Builder builderForValue) { @@ -1270,7 +1484,7 @@ public Builder setMetadata( } else { metadataBuilder_.setMessage(builderForValue.build()); } - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); return this; } @@ -1279,11 +1493,11 @@ public Builder setMetadata( * Optional metadata associated with this part. *
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ public Builder mergeMetadata(com.google.protobuf.Struct value) { if (metadataBuilder_ == null) { - if (((bitField0_ & 0x00000008) != 0) && + if (((bitField0_ & 0x00000010) != 0) && metadata_ != null && metadata_ != com.google.protobuf.Struct.getDefaultInstance()) { getMetadataBuilder().mergeFrom(value); @@ -1294,7 +1508,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { metadataBuilder_.mergeFrom(value); } if (metadata_ != null) { - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); } return this; @@ -1304,10 +1518,10 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) { * Optional metadata associated with this part. * * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ public Builder clearMetadata() { - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000010); metadata_ = null; if (metadataBuilder_ != null) { metadataBuilder_.dispose(); @@ -1321,10 +1535,10 @@ public Builder clearMetadata() { * Optional metadata associated with this part. * * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ public com.google.protobuf.Struct.Builder getMetadataBuilder() { - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000010; onChanged(); return internalGetMetadataFieldBuilder().getBuilder(); } @@ -1333,7 +1547,7 @@ public com.google.protobuf.Struct.Builder getMetadataBuilder() { * Optional metadata associated with this part. * * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { @@ -1348,7 +1562,7 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { * Optional metadata associated with this part. * * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> @@ -1364,6 +1578,195 @@ public com.google.protobuf.StructOrBuilder getMetadataOrBuilder() { return metadataBuilder_; } + private java.lang.Object filename_ = ""; + /** + *
    +     * An optional name for the file (e.g., "document.pdf").
    +     * 
    + * + * string filename = 6; + * @return The filename. + */ + public java.lang.String getFilename() { + java.lang.Object ref = filename_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + filename_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * An optional name for the file (e.g., "document.pdf").
    +     * 
    + * + * string filename = 6; + * @return The bytes for filename. + */ + public com.google.protobuf.ByteString + getFilenameBytes() { + java.lang.Object ref = filename_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + filename_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * An optional name for the file (e.g., "document.pdf").
    +     * 
    + * + * string filename = 6; + * @param value The filename to set. + * @return This builder for chaining. + */ + public Builder setFilename( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + filename_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + /** + *
    +     * An optional name for the file (e.g., "document.pdf").
    +     * 
    + * + * string filename = 6; + * @return This builder for chaining. + */ + public Builder clearFilename() { + filename_ = getDefaultInstance().getFilename(); + bitField0_ = (bitField0_ & ~0x00000020); + onChanged(); + return this; + } + /** + *
    +     * An optional name for the file (e.g., "document.pdf").
    +     * 
    + * + * string filename = 6; + * @param value The bytes for filename to set. + * @return This builder for chaining. + */ + public Builder setFilenameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + filename_ = value; + bitField0_ |= 0x00000020; + onChanged(); + return this; + } + + private java.lang.Object mediaType_ = ""; + /** + *
    +     * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +     * This field is available for all part types.
    +     * 
    + * + * string media_type = 7; + * @return The mediaType. + */ + public java.lang.String getMediaType() { + java.lang.Object ref = mediaType_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + mediaType_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +     * This field is available for all part types.
    +     * 
    + * + * string media_type = 7; + * @return The bytes for mediaType. + */ + public com.google.protobuf.ByteString + getMediaTypeBytes() { + java.lang.Object ref = mediaType_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + mediaType_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +     * This field is available for all part types.
    +     * 
    + * + * string media_type = 7; + * @param value The mediaType to set. + * @return This builder for chaining. + */ + public Builder setMediaType( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + mediaType_ = value; + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + /** + *
    +     * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +     * This field is available for all part types.
    +     * 
    + * + * string media_type = 7; + * @return This builder for chaining. + */ + public Builder clearMediaType() { + mediaType_ = getDefaultInstance().getMediaType(); + bitField0_ = (bitField0_ & ~0x00000040); + onChanged(); + return this; + } + /** + *
    +     * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +     * This field is available for all part types.
    +     * 
    + * + * string media_type = 7; + * @param value The bytes for mediaType to set. + * @return This builder for chaining. + */ + public Builder setMediaTypeBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + mediaType_ = value; + bitField0_ |= 0x00000040; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:a2a.v1.Part) } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java index 8c8657625..0216594f2 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PartOrBuilder.java @@ -41,64 +41,85 @@ public interface PartOrBuilder extends /** *
    -   * The file content, represented as either a URI or as base64-encoded bytes.
    +   * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
        * 
    * - * .a2a.v1.FilePart file = 2; - * @return Whether the file field is set. + * bytes raw = 2; + * @return Whether the raw field is set. */ - boolean hasFile(); + boolean hasRaw(); /** *
    -   * The file content, represented as either a URI or as base64-encoded bytes.
    +   * The raw byte content of a file. In JSON serialization, this is encoded as a base64 string.
        * 
    * - * .a2a.v1.FilePart file = 2; - * @return The file. + * bytes raw = 2; + * @return The raw. */ - io.a2a.grpc.FilePart getFile(); + com.google.protobuf.ByteString getRaw(); + + /** + *
    +   * A URL pointing to the file's content.
    +   * 
    + * + * string url = 3; + * @return Whether the url field is set. + */ + boolean hasUrl(); + /** + *
    +   * A URL pointing to the file's content.
    +   * 
    + * + * string url = 3; + * @return The url. + */ + java.lang.String getUrl(); /** *
    -   * The file content, represented as either a URI or as base64-encoded bytes.
    +   * A URL pointing to the file's content.
        * 
    * - * .a2a.v1.FilePart file = 2; + * string url = 3; + * @return The bytes for url. */ - io.a2a.grpc.FilePartOrBuilder getFileOrBuilder(); + com.google.protobuf.ByteString + getUrlBytes(); /** *
    -   * The structured data content.
    +   * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
        * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; * @return Whether the data field is set. */ boolean hasData(); /** *
    -   * The structured data content.
    +   * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
        * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; * @return The data. */ - io.a2a.grpc.DataPart getData(); + com.google.protobuf.Value getData(); /** *
    -   * The structured data content.
    +   * Arbitrary structured data as a JSON value (object, array, string, number, boolean, or null).
        * 
    * - * .a2a.v1.DataPart data = 3; + * .google.protobuf.Value data = 4; */ - io.a2a.grpc.DataPartOrBuilder getDataOrBuilder(); + com.google.protobuf.ValueOrBuilder getDataOrBuilder(); /** *
        * Optional metadata associated with this part.
        * 
    * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; * @return Whether the metadata field is set. */ boolean hasMetadata(); @@ -107,7 +128,7 @@ public interface PartOrBuilder extends * Optional metadata associated with this part. * * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; * @return The metadata. */ com.google.protobuf.Struct getMetadata(); @@ -116,9 +137,51 @@ public interface PartOrBuilder extends * Optional metadata associated with this part. * * - * .google.protobuf.Struct metadata = 4; + * .google.protobuf.Struct metadata = 5; */ com.google.protobuf.StructOrBuilder getMetadataOrBuilder(); - io.a2a.grpc.Part.PartCase getPartCase(); + /** + *
    +   * An optional name for the file (e.g., "document.pdf").
    +   * 
    + * + * string filename = 6; + * @return The filename. + */ + java.lang.String getFilename(); + /** + *
    +   * An optional name for the file (e.g., "document.pdf").
    +   * 
    + * + * string filename = 6; + * @return The bytes for filename. + */ + com.google.protobuf.ByteString + getFilenameBytes(); + + /** + *
    +   * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +   * This field is available for all part types.
    +   * 
    + * + * string media_type = 7; + * @return The mediaType. + */ + java.lang.String getMediaType(); + /** + *
    +   * The media type (MIME type) of the part content (e.g., "text/plain", "application/json", "image/png").
    +   * This field is available for all part types.
    +   * 
    + * + * string media_type = 7; + * @return The bytes for mediaType. + */ + com.google.protobuf.ByteString + getMediaTypeBytes(); + + io.a2a.grpc.Part.ContentCase getContentCase(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java new file mode 100644 index 000000000..50f6032bb --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlow.java @@ -0,0 +1,1050 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.33.1 + +package io.a2a.grpc; + +/** + *
    + * DEPRECATED
    + * 
    + * + * Protobuf type {@code a2a.v1.PasswordOAuthFlow} + */ +@com.google.protobuf.Generated +public final class PasswordOAuthFlow extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:a2a.v1.PasswordOAuthFlow) + PasswordOAuthFlowOrBuilder { +private static final long serialVersionUID = 0L; + static { + com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( + com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC, + /* major= */ 4, + /* minor= */ 33, + /* patch= */ 1, + /* suffix= */ "", + "PasswordOAuthFlow"); + } + // Use PasswordOAuthFlow.newBuilder() to construct. + private PasswordOAuthFlow(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private PasswordOAuthFlow() { + tokenUrl_ = ""; + refreshUrl_ = ""; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + @java.lang.Override + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.PasswordOAuthFlow.class, io.a2a.grpc.PasswordOAuthFlow.Builder.class); + } + + public static final int TOKEN_URL_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object tokenUrl_ = ""; + /** + *
    +   * The token URL to be used for this flow. This MUST be in the form of a URL.
    +   * The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string token_url = 1; + * @return The tokenUrl. + */ + @java.lang.Override + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } + } + /** + *
    +   * The token URL to be used for this flow. This MUST be in the form of a URL.
    +   * The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string token_url = 1; + * @return The bytes for tokenUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int REFRESH_URL_FIELD_NUMBER = 2; + @SuppressWarnings("serial") + private volatile java.lang.Object refreshUrl_ = ""; + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The refreshUrl. + */ + @java.lang.Override + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } + } + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The bytes for refreshUrl. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCOPES_FIELD_NUMBER = 3; + private static final class ScopesDefaultEntryHolder { + static final com.google.protobuf.MapEntry< + java.lang.String, java.lang.String> defaultEntry = + com.google.protobuf.MapEntry + .newDefaultInstance( + io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_ScopesEntry_descriptor, + com.google.protobuf.WireFormat.FieldType.STRING, + "", + com.google.protobuf.WireFormat.FieldType.STRING, + ""); + } + @SuppressWarnings("serial") + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 2, refreshUrl_); + } + com.google.protobuf.GeneratedMessage + .serializeStringMapTo( + output, + internalGetScopes(), + ScopesDefaultEntryHolder.defaultEntry, + 3); + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tokenUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, tokenUrl_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(refreshUrl_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(2, refreshUrl_); + } + for (java.util.Map.Entry entry + : internalGetScopes().getMap().entrySet()) { + com.google.protobuf.MapEntry + scopes__ = ScopesDefaultEntryHolder.defaultEntry.newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build(); + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, scopes__); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.a2a.grpc.PasswordOAuthFlow)) { + return super.equals(obj); + } + io.a2a.grpc.PasswordOAuthFlow other = (io.a2a.grpc.PasswordOAuthFlow) obj; + + if (!getTokenUrl() + .equals(other.getTokenUrl())) return false; + if (!getRefreshUrl() + .equals(other.getRefreshUrl())) return false; + if (!internalGetScopes().equals( + other.internalGetScopes())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TOKEN_URL_FIELD_NUMBER; + hash = (53 * hash) + getTokenUrl().hashCode(); + hash = (37 * hash) + REFRESH_URL_FIELD_NUMBER; + hash = (53 * hash) + getRefreshUrl().hashCode(); + if (!internalGetScopes().getMap().isEmpty()) { + hash = (37 * hash) + SCOPES_FIELD_NUMBER; + hash = (53 * hash) + internalGetScopes().hashCode(); + } + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.a2a.grpc.PasswordOAuthFlow parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.a2a.grpc.PasswordOAuthFlow parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input); + } + public static io.a2a.grpc.PasswordOAuthFlow parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessage + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.a2a.grpc.PasswordOAuthFlow prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
    +   * DEPRECATED
    +   * 
    + * + * Protobuf type {@code a2a.v1.PasswordOAuthFlow} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:a2a.v1.PasswordOAuthFlow) + io.a2a.grpc.PasswordOAuthFlowOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + } + + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @SuppressWarnings({"rawtypes"}) + protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFieldReflection( + int number) { + switch (number) { + case 3: + return internalGetMutableScopes(); + default: + throw new RuntimeException( + "Invalid map field number: " + number); + } + } + @java.lang.Override + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.a2a.grpc.PasswordOAuthFlow.class, io.a2a.grpc.PasswordOAuthFlow.Builder.class); + } + + // Construct using io.a2a.grpc.PasswordOAuthFlow.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + tokenUrl_ = ""; + refreshUrl_ = ""; + internalGetMutableScopes().clear(); + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.a2a.grpc.A2A.internal_static_a2a_v1_PasswordOAuthFlow_descriptor; + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow getDefaultInstanceForType() { + return io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance(); + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow build() { + io.a2a.grpc.PasswordOAuthFlow result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow buildPartial() { + io.a2a.grpc.PasswordOAuthFlow result = new io.a2a.grpc.PasswordOAuthFlow(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.a2a.grpc.PasswordOAuthFlow result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.tokenUrl_ = tokenUrl_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.refreshUrl_ = refreshUrl_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.scopes_ = internalGetScopes(); + result.scopes_.makeImmutable(); + } + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.a2a.grpc.PasswordOAuthFlow) { + return mergeFrom((io.a2a.grpc.PasswordOAuthFlow)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.a2a.grpc.PasswordOAuthFlow other) { + if (other == io.a2a.grpc.PasswordOAuthFlow.getDefaultInstance()) return this; + if (!other.getTokenUrl().isEmpty()) { + tokenUrl_ = other.tokenUrl_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (!other.getRefreshUrl().isEmpty()) { + refreshUrl_ = other.refreshUrl_; + bitField0_ |= 0x00000002; + onChanged(); + } + internalGetMutableScopes().mergeFrom( + other.internalGetScopes()); + bitField0_ |= 0x00000004; + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + tokenUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: { + refreshUrl_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; + break; + } // case 18 + case 26: { + com.google.protobuf.MapEntry + scopes__ = input.readMessage( + ScopesDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); + internalGetMutableScopes().getMutableMap().put( + scopes__.getKey(), scopes__.getValue()); + bitField0_ |= 0x00000004; + break; + } // case 26 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object tokenUrl_ = ""; + /** + *
    +     * The token URL to be used for this flow. This MUST be in the form of a URL.
    +     * The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string token_url = 1; + * @return The tokenUrl. + */ + public java.lang.String getTokenUrl() { + java.lang.Object ref = tokenUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + tokenUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The token URL to be used for this flow. This MUST be in the form of a URL.
    +     * The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string token_url = 1; + * @return The bytes for tokenUrl. + */ + public com.google.protobuf.ByteString + getTokenUrlBytes() { + java.lang.Object ref = tokenUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + tokenUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The token URL to be used for this flow. This MUST be in the form of a URL.
    +     * The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string token_url = 1; + * @param value The tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + tokenUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + *
    +     * The token URL to be used for this flow. This MUST be in the form of a URL.
    +     * The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string token_url = 1; + * @return This builder for chaining. + */ + public Builder clearTokenUrl() { + tokenUrl_ = getDefaultInstance().getTokenUrl(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + *
    +     * The token URL to be used for this flow. This MUST be in the form of a URL.
    +     * The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string token_url = 1; + * @param value The bytes for tokenUrl to set. + * @return This builder for chaining. + */ + public Builder setTokenUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + tokenUrl_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private java.lang.Object refreshUrl_ = ""; + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @return The refreshUrl. + */ + public java.lang.String getRefreshUrl() { + java.lang.Object ref = refreshUrl_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + refreshUrl_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @return The bytes for refreshUrl. + */ + public com.google.protobuf.ByteString + getRefreshUrlBytes() { + java.lang.Object ref = refreshUrl_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + refreshUrl_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @param value The refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrl( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @return This builder for chaining. + */ + public Builder clearRefreshUrl() { + refreshUrl_ = getDefaultInstance().getRefreshUrl(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
    +     * The URL to be used for obtaining refresh tokens. This MUST be in the
    +     * form of a URL. The OAuth2 standard requires the use of TLS.
    +     * 
    + * + * string refresh_url = 2; + * @param value The bytes for refreshUrl to set. + * @return This builder for chaining. + */ + public Builder setRefreshUrlBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + refreshUrl_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private com.google.protobuf.MapField< + java.lang.String, java.lang.String> scopes_; + private com.google.protobuf.MapField + internalGetScopes() { + if (scopes_ == null) { + return com.google.protobuf.MapField.emptyMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + return scopes_; + } + private com.google.protobuf.MapField + internalGetMutableScopes() { + if (scopes_ == null) { + scopes_ = com.google.protobuf.MapField.newMapField( + ScopesDefaultEntryHolder.defaultEntry); + } + if (!scopes_.isMutable()) { + scopes_ = scopes_.copy(); + } + bitField0_ |= 0x00000004; + onChanged(); + return scopes_; + } + public int getScopesCount() { + return internalGetScopes().getMap().size(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public boolean containsScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + return internalGetScopes().getMap().containsKey(key); + } + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Override + @java.lang.Deprecated + public java.util.Map getScopes() { + return getScopesMap(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.util.Map getScopesMap() { + return internalGetScopes().getMap(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + return map.containsKey(key) ? map.get(key) : defaultValue; + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + @java.lang.Override + public java.lang.String getScopesOrThrow( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + java.util.Map map = + internalGetScopes().getMap(); + if (!map.containsKey(key)) { + throw new java.lang.IllegalArgumentException(); + } + return map.get(key); + } + public Builder clearScopes() { + bitField0_ = (bitField0_ & ~0x00000004); + internalGetMutableScopes().getMutableMap() + .clear(); + return this; + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + public Builder removeScopes( + java.lang.String key) { + if (key == null) { throw new NullPointerException("map key"); } + internalGetMutableScopes().getMutableMap() + .remove(key); + return this; + } + /** + * Use alternate mutation accessors instead. + */ + @java.lang.Deprecated + public java.util.Map + getMutableScopes() { + bitField0_ |= 0x00000004; + return internalGetMutableScopes().getMutableMap(); + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + public Builder putScopes( + java.lang.String key, + java.lang.String value) { + if (key == null) { throw new NullPointerException("map key"); } + if (value == null) { throw new NullPointerException("map value"); } + internalGetMutableScopes().getMutableMap() + .put(key, value); + bitField0_ |= 0x00000004; + return this; + } + /** + *
    +     * The available scopes for the OAuth2 security scheme. A map between the
    +     * scope name and a short description for it. The map MAY be empty.
    +     * 
    + * + * map<string, string> scopes = 3; + */ + public Builder putAllScopes( + java.util.Map values) { + internalGetMutableScopes().getMutableMap() + .putAll(values); + bitField0_ |= 0x00000004; + return this; + } + + // @@protoc_insertion_point(builder_scope:a2a.v1.PasswordOAuthFlow) + } + + // @@protoc_insertion_point(class_scope:a2a.v1.PasswordOAuthFlow) + private static final io.a2a.grpc.PasswordOAuthFlow DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.a2a.grpc.PasswordOAuthFlow(); + } + + public static io.a2a.grpc.PasswordOAuthFlow getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public PasswordOAuthFlow parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.a2a.grpc.PasswordOAuthFlow getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java new file mode 100644 index 000000000..d319dd1e7 --- /dev/null +++ b/spec-grpc/src/main/java/io/a2a/grpc/PasswordOAuthFlowOrBuilder.java @@ -0,0 +1,115 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// NO CHECKED-IN PROTOBUF GENCODE +// source: a2a.proto +// Protobuf Java Version: 4.33.1 + +package io.a2a.grpc; + +@com.google.protobuf.Generated +public interface PasswordOAuthFlowOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.PasswordOAuthFlow) + com.google.protobuf.MessageOrBuilder { + + /** + *
    +   * The token URL to be used for this flow. This MUST be in the form of a URL.
    +   * The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string token_url = 1; + * @return The tokenUrl. + */ + java.lang.String getTokenUrl(); + /** + *
    +   * The token URL to be used for this flow. This MUST be in the form of a URL.
    +   * The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string token_url = 1; + * @return The bytes for tokenUrl. + */ + com.google.protobuf.ByteString + getTokenUrlBytes(); + + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The refreshUrl. + */ + java.lang.String getRefreshUrl(); + /** + *
    +   * The URL to be used for obtaining refresh tokens. This MUST be in the
    +   * form of a URL. The OAuth2 standard requires the use of TLS.
    +   * 
    + * + * string refresh_url = 2; + * @return The bytes for refreshUrl. + */ + com.google.protobuf.ByteString + getRefreshUrlBytes(); + + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + int getScopesCount(); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + boolean containsScopes( + java.lang.String key); + /** + * Use {@link #getScopesMap()} instead. + */ + @java.lang.Deprecated + java.util.Map + getScopes(); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + java.util.Map + getScopesMap(); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + /* nullable */ +java.lang.String getScopesOrDefault( + java.lang.String key, + /* nullable */ +java.lang.String defaultValue); + /** + *
    +   * The available scopes for the OAuth2 security scheme. A map between the
    +   * scope name and a short description for it. The map MAY be empty.
    +   * 
    + * + * map<string, string> scopes = 3; + */ + java.lang.String getScopesOrThrow( + java.lang.String key); +} diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java index cec0fdf08..5c23d23ba 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfig.java @@ -197,7 +197,7 @@ public java.lang.String getToken() { private io.a2a.grpc.AuthenticationInfo authentication_; /** *
    -   * Information about the authentication to sent with the notification
    +   * Authentication information required to send the notification
        * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -209,7 +209,7 @@ public boolean hasAuthentication() { } /** *
    -   * Information about the authentication to sent with the notification
    +   * Authentication information required to send the notification
        * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -221,7 +221,7 @@ public io.a2a.grpc.AuthenticationInfo getAuthentication() { } /** *
    -   * Information about the authentication to sent with the notification
    +   * Authentication information required to send the notification
        * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -904,7 +904,7 @@ public Builder setTokenBytes( io.a2a.grpc.AuthenticationInfo, io.a2a.grpc.AuthenticationInfo.Builder, io.a2a.grpc.AuthenticationInfoOrBuilder> authenticationBuilder_; /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -915,7 +915,7 @@ public boolean hasAuthentication() { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -930,7 +930,7 @@ public io.a2a.grpc.AuthenticationInfo getAuthentication() { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -950,7 +950,7 @@ public Builder setAuthentication(io.a2a.grpc.AuthenticationInfo value) { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -968,7 +968,7 @@ public Builder setAuthentication( } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -993,7 +993,7 @@ public Builder mergeAuthentication(io.a2a.grpc.AuthenticationInfo value) { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -1010,7 +1010,7 @@ public Builder clearAuthentication() { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -1022,7 +1022,7 @@ public io.a2a.grpc.AuthenticationInfo.Builder getAuthenticationBuilder() { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -1037,7 +1037,7 @@ public io.a2a.grpc.AuthenticationInfoOrBuilder getAuthenticationOrBuilder() { } /** *
    -     * Information about the authentication to sent with the notification
    +     * Authentication information required to send the notification
          * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java index bcfaf4c2e..b65c0d5a0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/PushNotificationConfigOrBuilder.java @@ -72,7 +72,7 @@ public interface PushNotificationConfigOrBuilder extends /** *
    -   * Information about the authentication to sent with the notification
    +   * Authentication information required to send the notification
        * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -81,7 +81,7 @@ public interface PushNotificationConfigOrBuilder extends boolean hasAuthentication(); /** *
    -   * Information about the authentication to sent with the notification
    +   * Authentication information required to send the notification
        * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; @@ -90,7 +90,7 @@ public interface PushNotificationConfigOrBuilder extends io.a2a.grpc.AuthenticationInfo getAuthentication(); /** *
    -   * Information about the authentication to sent with the notification
    +   * Authentication information required to send the notification
        * 
    * * .a2a.v1.AuthenticationInfo authentication = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/Security.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityRequirement.java similarity index 84% rename from spec-grpc/src/main/java/io/a2a/grpc/Security.java rename to spec-grpc/src/main/java/io/a2a/grpc/SecurityRequirement.java index f04498f98..945b2608d 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/Security.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityRequirement.java @@ -6,13 +6,13 @@ package io.a2a.grpc; /** - * Protobuf type {@code a2a.v1.Security} + * Protobuf type {@code a2a.v1.SecurityRequirement} */ @com.google.protobuf.Generated -public final class Security extends +public final class SecurityRequirement extends com.google.protobuf.GeneratedMessage implements - // @@protoc_insertion_point(message_implements:a2a.v1.Security) - SecurityOrBuilder { + // @@protoc_insertion_point(message_implements:a2a.v1.SecurityRequirement) + SecurityRequirementOrBuilder { private static final long serialVersionUID = 0L; static { com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion( @@ -21,18 +21,18 @@ public final class Security extends /* minor= */ 33, /* patch= */ 1, /* suffix= */ "", - "Security"); + "SecurityRequirement"); } - // Use Security.newBuilder() to construct. - private Security(com.google.protobuf.GeneratedMessage.Builder builder) { + // Use SecurityRequirement.newBuilder() to construct. + private SecurityRequirement(com.google.protobuf.GeneratedMessage.Builder builder) { super(builder); } - private Security() { + private SecurityRequirement() { } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityRequirement_descriptor; } @SuppressWarnings({"rawtypes"}) @@ -50,9 +50,9 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMapFieldRefl @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityRequirement_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.Security.class, io.a2a.grpc.Security.Builder.class); + io.a2a.grpc.SecurityRequirement.class, io.a2a.grpc.SecurityRequirement.Builder.class); } public static final int SCHEMES_FIELD_NUMBER = 1; @@ -61,7 +61,7 @@ private static final class SchemesDefaultEntryHolder { java.lang.String, io.a2a.grpc.StringList> defaultEntry = com.google.protobuf.MapEntry .newDefaultInstance( - io.a2a.grpc.A2A.internal_static_a2a_v1_Security_SchemesEntry_descriptor, + io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityRequirement_SchemesEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.MESSAGE, @@ -183,10 +183,10 @@ public boolean equals(final java.lang.Object obj) { if (obj == this) { return true; } - if (!(obj instanceof io.a2a.grpc.Security)) { + if (!(obj instanceof io.a2a.grpc.SecurityRequirement)) { return super.equals(obj); } - io.a2a.grpc.Security other = (io.a2a.grpc.Security) obj; + io.a2a.grpc.SecurityRequirement other = (io.a2a.grpc.SecurityRequirement) obj; if (!internalGetSchemes().equals( other.internalGetSchemes())) return false; @@ -210,44 +210,44 @@ public int hashCode() { return hash; } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.Security parseFrom(byte[] data) + public static io.a2a.grpc.SecurityRequirement parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } - public static io.a2a.grpc.Security parseFrom(java.io.InputStream input) + public static io.a2a.grpc.SecurityRequirement parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -255,26 +255,26 @@ public static io.a2a.grpc.Security parseFrom( .parseWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.Security parseDelimitedFrom(java.io.InputStream input) + public static io.a2a.grpc.SecurityRequirement parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input); } - public static io.a2a.grpc.Security parseDelimitedFrom( + public static io.a2a.grpc.SecurityRequirement parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseDelimitedWithIOException(PARSER, input, extensionRegistry); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessage .parseWithIOException(PARSER, input); } - public static io.a2a.grpc.Security parseFrom( + public static io.a2a.grpc.SecurityRequirement parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -287,7 +287,7 @@ public static io.a2a.grpc.Security parseFrom( public static Builder newBuilder() { return DEFAULT_INSTANCE.toBuilder(); } - public static Builder newBuilder(io.a2a.grpc.Security prototype) { + public static Builder newBuilder(io.a2a.grpc.SecurityRequirement prototype) { return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); } @java.lang.Override @@ -303,15 +303,15 @@ protected Builder newBuilderForType( return builder; } /** - * Protobuf type {@code a2a.v1.Security} + * Protobuf type {@code a2a.v1.SecurityRequirement} */ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements - // @@protoc_insertion_point(builder_implements:a2a.v1.Security) - io.a2a.grpc.SecurityOrBuilder { + // @@protoc_insertion_point(builder_implements:a2a.v1.SecurityRequirement) + io.a2a.grpc.SecurityRequirementOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityRequirement_descriptor; } @SuppressWarnings({"rawtypes"}) @@ -339,12 +339,12 @@ protected com.google.protobuf.MapFieldReflectionAccessor internalGetMutableMapFi @java.lang.Override protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_fieldAccessorTable + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityRequirement_fieldAccessorTable .ensureFieldAccessorsInitialized( - io.a2a.grpc.Security.class, io.a2a.grpc.Security.Builder.class); + io.a2a.grpc.SecurityRequirement.class, io.a2a.grpc.SecurityRequirement.Builder.class); } - // Construct using io.a2a.grpc.Security.newBuilder() + // Construct using io.a2a.grpc.SecurityRequirement.newBuilder() private Builder() { } @@ -365,17 +365,17 @@ public Builder clear() { @java.lang.Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.a2a.grpc.A2A.internal_static_a2a_v1_Security_descriptor; + return io.a2a.grpc.A2A.internal_static_a2a_v1_SecurityRequirement_descriptor; } @java.lang.Override - public io.a2a.grpc.Security getDefaultInstanceForType() { - return io.a2a.grpc.Security.getDefaultInstance(); + public io.a2a.grpc.SecurityRequirement getDefaultInstanceForType() { + return io.a2a.grpc.SecurityRequirement.getDefaultInstance(); } @java.lang.Override - public io.a2a.grpc.Security build() { - io.a2a.grpc.Security result = buildPartial(); + public io.a2a.grpc.SecurityRequirement build() { + io.a2a.grpc.SecurityRequirement result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } @@ -383,14 +383,14 @@ public io.a2a.grpc.Security build() { } @java.lang.Override - public io.a2a.grpc.Security buildPartial() { - io.a2a.grpc.Security result = new io.a2a.grpc.Security(this); + public io.a2a.grpc.SecurityRequirement buildPartial() { + io.a2a.grpc.SecurityRequirement result = new io.a2a.grpc.SecurityRequirement(this); if (bitField0_ != 0) { buildPartial0(result); } onBuilt(); return result; } - private void buildPartial0(io.a2a.grpc.Security result) { + private void buildPartial0(io.a2a.grpc.SecurityRequirement result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { result.schemes_ = internalGetSchemes().build(SchemesDefaultEntryHolder.defaultEntry); @@ -399,16 +399,16 @@ private void buildPartial0(io.a2a.grpc.Security result) { @java.lang.Override public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.a2a.grpc.Security) { - return mergeFrom((io.a2a.grpc.Security)other); + if (other instanceof io.a2a.grpc.SecurityRequirement) { + return mergeFrom((io.a2a.grpc.SecurityRequirement)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(io.a2a.grpc.Security other) { - if (other == io.a2a.grpc.Security.getDefaultInstance()) return this; + public Builder mergeFrom(io.a2a.grpc.SecurityRequirement other) { + if (other == io.a2a.grpc.SecurityRequirement.getDefaultInstance()) return this; internalGetMutableSchemes().mergeFrom( other.internalGetSchemes()); bitField0_ |= 0x00000001; @@ -619,23 +619,23 @@ public io.a2a.grpc.StringList.Builder putSchemesBuilderIfAbsent( return (io.a2a.grpc.StringList.Builder) entry; } - // @@protoc_insertion_point(builder_scope:a2a.v1.Security) + // @@protoc_insertion_point(builder_scope:a2a.v1.SecurityRequirement) } - // @@protoc_insertion_point(class_scope:a2a.v1.Security) - private static final io.a2a.grpc.Security DEFAULT_INSTANCE; + // @@protoc_insertion_point(class_scope:a2a.v1.SecurityRequirement) + private static final io.a2a.grpc.SecurityRequirement DEFAULT_INSTANCE; static { - DEFAULT_INSTANCE = new io.a2a.grpc.Security(); + DEFAULT_INSTANCE = new io.a2a.grpc.SecurityRequirement(); } - public static io.a2a.grpc.Security getDefaultInstance() { + public static io.a2a.grpc.SecurityRequirement getDefaultInstance() { return DEFAULT_INSTANCE; } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { @java.lang.Override - public Security parsePartialFrom( + public SecurityRequirement parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { @@ -654,17 +654,17 @@ public Security parsePartialFrom( } }; - public static com.google.protobuf.Parser parser() { + public static com.google.protobuf.Parser parser() { return PARSER; } @java.lang.Override - public com.google.protobuf.Parser getParserForType() { + public com.google.protobuf.Parser getParserForType() { return PARSER; } @java.lang.Override - public io.a2a.grpc.Security getDefaultInstanceForType() { + public io.a2a.grpc.SecurityRequirement getDefaultInstanceForType() { return DEFAULT_INSTANCE; } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SecurityRequirementOrBuilder.java similarity index 94% rename from spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java rename to spec-grpc/src/main/java/io/a2a/grpc/SecurityRequirementOrBuilder.java index cb1b2372f..14b23d252 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SecurityOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SecurityRequirementOrBuilder.java @@ -6,8 +6,8 @@ package io.a2a.grpc; @com.google.protobuf.Generated -public interface SecurityOrBuilder extends - // @@protoc_insertion_point(interface_extends:a2a.v1.Security) +public interface SecurityRequirementOrBuilder extends + // @@protoc_insertion_point(interface_extends:a2a.v1.SecurityRequirement) com.google.protobuf.MessageOrBuilder { /** diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java index 6bec1fb51..57374039f 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfiguration.java @@ -179,7 +179,7 @@ public int getHistoryLength() { private boolean blocking_ = false; /** *
    -   * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
    +   * If true, the operation waits until the task reaches a terminal or interrupted state before returning. Default is false.
        * 
    * * bool blocking = 4; @@ -977,7 +977,7 @@ public Builder clearHistoryLength() { private boolean blocking_ ; /** *
    -     * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
    +     * If true, the operation waits until the task reaches a terminal or interrupted state before returning. Default is false.
          * 
    * * bool blocking = 4; @@ -989,7 +989,7 @@ public boolean getBlocking() { } /** *
    -     * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
    +     * If true, the operation waits until the task reaches a terminal or interrupted state before returning. Default is false.
          * 
    * * bool blocking = 4; @@ -1005,7 +1005,7 @@ public Builder setBlocking(boolean value) { } /** *
    -     * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
    +     * If true, the operation waits until the task reaches a terminal or interrupted state before returning. Default is false.
          * 
    * * bool blocking = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java index 2fed6a6af..a9e9fc401 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SendMessageConfigurationOrBuilder.java @@ -105,7 +105,7 @@ public interface SendMessageConfigurationOrBuilder extends /** *
    -   * If true, the operation waits until the task reaches a terminal state before returning. Default is false.
    +   * If true, the operation waits until the task reaches a terminal or interrupted state before returning. Default is false.
        * 
    * * bool blocking = 4; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java index cdef789c1..82a95620a 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequest.java @@ -33,7 +33,7 @@ private SubscribeToTaskRequest(com.google.protobuf.GeneratedMessage.Builder b } private SubscribeToTaskRequest() { tenant_ = ""; - name_ = ""; + id_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -96,49 +96,47 @@ public java.lang.String getTenant() { } } - public static final int NAME_FIELD_NUMBER = 1; + public static final int ID_FIELD_NUMBER = 1; @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; + private volatile java.lang.Object id_ = ""; /** *
    -   * The resource name of the task to subscribe to.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to subscribe to.
        * 
    * - * string name = 1; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getId() { + java.lang.Object ref = id_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + id_ = s; return s; } } /** *
    -   * The resource name of the task to subscribe to.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to subscribe to.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ @java.lang.Override public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getIdBytes() { + java.lang.Object ref = id_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -159,8 +157,8 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { com.google.protobuf.GeneratedMessage.writeString(output, 2, tenant_); @@ -174,8 +172,8 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); } if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { size += com.google.protobuf.GeneratedMessage.computeStringSize(2, tenant_); @@ -197,8 +195,8 @@ public boolean equals(final java.lang.Object obj) { if (!getTenant() .equals(other.getTenant())) return false; - if (!getName() - .equals(other.getName())) return false; + if (!getId() + .equals(other.getId())) return false; if (!getUnknownFields().equals(other.getUnknownFields())) return false; return true; } @@ -212,8 +210,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TENANT_FIELD_NUMBER; hash = (53 * hash) + getTenant().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -350,7 +348,7 @@ public Builder clear() { super.clear(); bitField0_ = 0; tenant_ = ""; - name_ = ""; + id_ = ""; return this; } @@ -388,7 +386,7 @@ private void buildPartial0(io.a2a.grpc.SubscribeToTaskRequest result) { result.tenant_ = tenant_; } if (((from_bitField0_ & 0x00000002) != 0)) { - result.name_ = name_; + result.id_ = id_; } } @@ -409,8 +407,8 @@ public Builder mergeFrom(io.a2a.grpc.SubscribeToTaskRequest other) { bitField0_ |= 0x00000001; onChanged(); } - if (!other.getName().isEmpty()) { - name_ = other.name_; + if (!other.getId().isEmpty()) { + id_ = other.id_; bitField0_ |= 0x00000002; onChanged(); } @@ -441,7 +439,7 @@ public Builder mergeFrom( done = true; break; case 10: { - name_ = input.readStringRequireUtf8(); + id_ = input.readStringRequireUtf8(); bitField0_ |= 0x00000002; break; } // case 10 @@ -559,23 +557,22 @@ public Builder setTenantBytes( return this; } - private java.lang.Object name_ = ""; + private java.lang.Object id_ = ""; /** *
    -     * The resource name of the task to subscribe to.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to subscribe to.
          * 
    * - * string name = 1; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getId() { + java.lang.Object ref = id_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + id_ = s; return s; } else { return (java.lang.String) ref; @@ -583,21 +580,20 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the task to subscribe to.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to subscribe to.
          * 
    * - * string name = 1; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getIdBytes() { + java.lang.Object ref = id_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + id_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -605,52 +601,49 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the task to subscribe to.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to subscribe to.
          * 
    * - * string name = 1; - * @param value The name to set. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The id to set. * @return This builder for chaining. */ - public Builder setName( + public Builder setId( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - name_ = value; + id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; } /** *
    -     * The resource name of the task to subscribe to.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to subscribe to.
          * 
    * - * string name = 1; + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; * @return This builder for chaining. */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); + public Builder clearId() { + id_ = getDefaultInstance().getId(); bitField0_ = (bitField0_ & ~0x00000002); onChanged(); return this; } /** *
    -     * The resource name of the task to subscribe to.
    -     * Format: tasks/{task_id}
    +     * The resource id of the task to subscribe to.
          * 
    * - * string name = 1; - * @param value The bytes for name to set. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for id to set. * @return This builder for chaining. */ - public Builder setNameBytes( + public Builder setIdBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; + id_ = value; bitField0_ |= 0x00000002; onChanged(); return this; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java index 0d4a4fda9..39f287b30 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/SubscribeToTaskRequestOrBuilder.java @@ -32,23 +32,21 @@ public interface SubscribeToTaskRequestOrBuilder extends /** *
    -   * The resource name of the task to subscribe to.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to subscribe to.
        * 
    * - * string name = 1; - * @return The name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. */ - java.lang.String getName(); + java.lang.String getId(); /** *
    -   * The resource name of the task to subscribe to.
    -   * Format: tasks/{task_id}
    +   * The resource id of the task to subscribe to.
        * 
    * - * string name = 1; - * @return The bytes for name. + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. */ com.google.protobuf.ByteString - getNameBytes(); + getIdBytes(); } diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java index e33fb5cf4..745594565 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfig.java @@ -34,7 +34,9 @@ private TaskPushNotificationConfig(com.google.protobuf.GeneratedMessage.Builder< super(builder); } private TaskPushNotificationConfig() { - name_ = ""; + tenant_ = ""; + id_ = ""; + taskId_ = ""; } public static final com.google.protobuf.Descriptors.Descriptor @@ -51,49 +53,141 @@ private TaskPushNotificationConfig() { } private int bitField0_; - public static final int NAME_FIELD_NUMBER = 1; + public static final int TENANT_FIELD_NUMBER = 4; @SuppressWarnings("serial") - private volatile java.lang.Object name_ = ""; + private volatile java.lang.Object tenant_ = ""; /** *
    -   * The resource name of the config.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * Optional tenant
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The name. + * string tenant = 4; + * @return The tenant. */ @java.lang.Override - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; if (ref instanceof java.lang.String) { return (java.lang.String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + tenant_ = s; return s; } } /** *
    -   * The resource name of the config.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * Optional tenant
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for name. + * string tenant = 4; + * @return The bytes for tenant. */ @java.lang.Override public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getTenantBytes() { + java.lang.Object ref = tenant_; if (ref instanceof java.lang.String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + tenant_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int ID_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + *
    +   * The id of the config.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + *
    +   * The id of the config.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TASK_ID_FIELD_NUMBER = 3; + @SuppressWarnings("serial") + private volatile java.lang.Object taskId_ = ""; + /** + *
    +   * The id of the task this config is associated with.
    +   * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. + */ + @java.lang.Override + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } + } + /** + *
    +   * The id of the task this config is associated with.
    +   * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -152,12 +246,18 @@ public final boolean isInitialized() { @java.lang.Override public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - com.google.protobuf.GeneratedMessage.writeString(output, 1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 1, id_); } if (((bitField0_ & 0x00000001) != 0)) { output.writeMessage(2, getPushNotificationConfig()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 3, taskId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + com.google.protobuf.GeneratedMessage.writeString(output, 4, tenant_); + } getUnknownFields().writeTo(output); } @@ -167,13 +267,19 @@ public int getSerializedSize() { if (size != -1) return size; size = 0; - if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) { - size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_); + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(1, id_); } if (((bitField0_ & 0x00000001) != 0)) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(2, getPushNotificationConfig()); } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(taskId_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(3, taskId_); + } + if (!com.google.protobuf.GeneratedMessage.isStringEmpty(tenant_)) { + size += com.google.protobuf.GeneratedMessage.computeStringSize(4, tenant_); + } size += getUnknownFields().getSerializedSize(); memoizedSize = size; return size; @@ -189,8 +295,12 @@ public boolean equals(final java.lang.Object obj) { } io.a2a.grpc.TaskPushNotificationConfig other = (io.a2a.grpc.TaskPushNotificationConfig) obj; - if (!getName() - .equals(other.getName())) return false; + if (!getTenant() + .equals(other.getTenant())) return false; + if (!getId() + .equals(other.getId())) return false; + if (!getTaskId() + .equals(other.getTaskId())) return false; if (hasPushNotificationConfig() != other.hasPushNotificationConfig()) return false; if (hasPushNotificationConfig()) { if (!getPushNotificationConfig() @@ -207,8 +317,12 @@ public int hashCode() { } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + TENANT_FIELD_NUMBER; + hash = (53 * hash) + getTenant().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); + hash = (37 * hash) + TASK_ID_FIELD_NUMBER; + hash = (53 * hash) + getTaskId().hashCode(); if (hasPushNotificationConfig()) { hash = (37 * hash) + PUSH_NOTIFICATION_CONFIG_FIELD_NUMBER; hash = (53 * hash) + getPushNotificationConfig().hashCode(); @@ -356,7 +470,9 @@ private void maybeForceBuilderInitialization() { public Builder clear() { super.clear(); bitField0_ = 0; - name_ = ""; + tenant_ = ""; + id_ = ""; + taskId_ = ""; pushNotificationConfig_ = null; if (pushNotificationConfigBuilder_ != null) { pushNotificationConfigBuilder_.dispose(); @@ -396,10 +512,16 @@ public io.a2a.grpc.TaskPushNotificationConfig buildPartial() { private void buildPartial0(io.a2a.grpc.TaskPushNotificationConfig result) { int from_bitField0_ = bitField0_; if (((from_bitField0_ & 0x00000001) != 0)) { - result.name_ = name_; + result.tenant_ = tenant_; } - int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000002) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000004) != 0)) { + result.taskId_ = taskId_; + } + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000008) != 0)) { result.pushNotificationConfig_ = pushNotificationConfigBuilder_ == null ? pushNotificationConfig_ : pushNotificationConfigBuilder_.build(); @@ -420,11 +542,21 @@ public Builder mergeFrom(com.google.protobuf.Message other) { public Builder mergeFrom(io.a2a.grpc.TaskPushNotificationConfig other) { if (other == io.a2a.grpc.TaskPushNotificationConfig.getDefaultInstance()) return this; - if (!other.getName().isEmpty()) { - name_ = other.name_; + if (!other.getTenant().isEmpty()) { + tenant_ = other.tenant_; bitField0_ |= 0x00000001; onChanged(); } + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000002; + onChanged(); + } + if (!other.getTaskId().isEmpty()) { + taskId_ = other.taskId_; + bitField0_ |= 0x00000004; + onChanged(); + } if (other.hasPushNotificationConfig()) { mergePushNotificationConfig(other.getPushNotificationConfig()); } @@ -455,17 +587,27 @@ public Builder mergeFrom( done = true; break; case 10: { - name_ = input.readStringRequireUtf8(); - bitField0_ |= 0x00000001; + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000002; break; } // case 10 case 18: { input.readMessage( internalGetPushNotificationConfigFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000008; break; } // case 18 + case 26: { + taskId_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000004; + break; + } // case 26 + case 34: { + tenant_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 34 default: { if (!super.parseUnknownField(input, extensionRegistry, tag)) { done = true; // was an endgroup tag @@ -483,23 +625,22 @@ public Builder mergeFrom( } private int bitField0_; - private java.lang.Object name_ = ""; + private java.lang.Object tenant_ = ""; /** *
    -     * The resource name of the config.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * Optional tenant
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The name. + * string tenant = 4; + * @return The tenant. */ - public java.lang.String getName() { - java.lang.Object ref = name_; + public java.lang.String getTenant() { + java.lang.Object ref = tenant_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); - name_ = s; + tenant_ = s; return s; } else { return (java.lang.String) ref; @@ -507,21 +648,20 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the config.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * Optional tenant
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for name. + * string tenant = 4; + * @return The bytes for tenant. */ public com.google.protobuf.ByteString - getNameBytes() { - java.lang.Object ref = name_; + getTenantBytes() { + java.lang.Object ref = tenant_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (java.lang.String) ref); - name_ = b; + tenant_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; @@ -529,57 +669,238 @@ public java.lang.String getName() { } /** *
    -     * The resource name of the config.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * Optional tenant
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The name to set. + * string tenant = 4; + * @param value The tenant to set. * @return This builder for chaining. */ - public Builder setName( + public Builder setTenant( java.lang.String value) { if (value == null) { throw new NullPointerException(); } - name_ = value; + tenant_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } /** *
    -     * The resource name of the config.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * Optional tenant
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; + * string tenant = 4; * @return This builder for chaining. */ - public Builder clearName() { - name_ = getDefaultInstance().getName(); + public Builder clearTenant() { + tenant_ = getDefaultInstance().getTenant(); bitField0_ = (bitField0_ & ~0x00000001); onChanged(); return this; } /** *
    -     * The resource name of the config.
    -     * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +     * Optional tenant
          * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @param value The bytes for name to set. + * string tenant = 4; + * @param value The bytes for tenant to set. * @return This builder for chaining. */ - public Builder setNameBytes( + public Builder setTenantBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); - name_ = value; + tenant_ = value; bitField0_ |= 0x00000001; onChanged(); return this; } + private java.lang.Object id_ = ""; + /** + *
    +     * The id of the config.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The id of the config.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + public com.google.protobuf.ByteString + getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The id of the config.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + id_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + *
    +     * The id of the config.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + return this; + } + /** + *
    +     * The id of the config.
    +     * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + + private java.lang.Object taskId_ = ""; + /** + *
    +     * The id of the task this config is associated with.
    +     * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. + */ + public java.lang.String getTaskId() { + java.lang.Object ref = taskId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + taskId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
    +     * The id of the task this config is associated with.
    +     * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. + */ + public com.google.protobuf.ByteString + getTaskIdBytes() { + java.lang.Object ref = taskId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + taskId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
    +     * The id of the task this config is associated with.
    +     * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskId( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + taskId_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + /** + *
    +     * The id of the task this config is associated with.
    +     * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return This builder for chaining. + */ + public Builder clearTaskId() { + taskId_ = getDefaultInstance().getTaskId(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + return this; + } + /** + *
    +     * The id of the task this config is associated with.
    +     * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @param value The bytes for taskId to set. + * @return This builder for chaining. + */ + public Builder setTaskIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + taskId_ = value; + bitField0_ |= 0x00000004; + onChanged(); + return this; + } + private io.a2a.grpc.PushNotificationConfig pushNotificationConfig_; private com.google.protobuf.SingleFieldBuilder< io.a2a.grpc.PushNotificationConfig, io.a2a.grpc.PushNotificationConfig.Builder, io.a2a.grpc.PushNotificationConfigOrBuilder> pushNotificationConfigBuilder_; @@ -592,7 +913,7 @@ public Builder setNameBytes( * @return Whether the pushNotificationConfig field is set. */ public boolean hasPushNotificationConfig() { - return ((bitField0_ & 0x00000002) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** *
    @@ -625,7 +946,7 @@ public Builder setPushNotificationConfig(io.a2a.grpc.PushNotificationConfig valu
           } else {
             pushNotificationConfigBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -643,7 +964,7 @@ public Builder setPushNotificationConfig(
           } else {
             pushNotificationConfigBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -656,7 +977,7 @@ public Builder setPushNotificationConfig(
          */
         public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig value) {
           if (pushNotificationConfigBuilder_ == null) {
    -        if (((bitField0_ & 0x00000002) != 0) &&
    +        if (((bitField0_ & 0x00000008) != 0) &&
               pushNotificationConfig_ != null &&
               pushNotificationConfig_ != io.a2a.grpc.PushNotificationConfig.getDefaultInstance()) {
               getPushNotificationConfigBuilder().mergeFrom(value);
    @@ -667,7 +988,7 @@ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig va
             pushNotificationConfigBuilder_.mergeFrom(value);
           }
           if (pushNotificationConfig_ != null) {
    -        bitField0_ |= 0x00000002;
    +        bitField0_ |= 0x00000008;
             onChanged();
           }
           return this;
    @@ -680,7 +1001,7 @@ public Builder mergePushNotificationConfig(io.a2a.grpc.PushNotificationConfig va
          * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED];
          */
         public Builder clearPushNotificationConfig() {
    -      bitField0_ = (bitField0_ & ~0x00000002);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           pushNotificationConfig_ = null;
           if (pushNotificationConfigBuilder_ != null) {
             pushNotificationConfigBuilder_.dispose();
    @@ -697,7 +1018,7 @@ public Builder clearPushNotificationConfig() {
          * .a2a.v1.PushNotificationConfig push_notification_config = 2 [(.google.api.field_behavior) = REQUIRED];
          */
         public io.a2a.grpc.PushNotificationConfig.Builder getPushNotificationConfigBuilder() {
    -      bitField0_ |= 0x00000002;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return internalGetPushNotificationConfigFieldBuilder().getBuilder();
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java
    index 741c9b4cf..2da32ae41 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskPushNotificationConfigOrBuilder.java
    @@ -12,25 +12,63 @@ public interface TaskPushNotificationConfigOrBuilder extends
     
       /**
        * 
    -   * The resource name of the config.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * Optional tenant
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The name. + * string tenant = 4; + * @return The tenant. */ - java.lang.String getName(); + java.lang.String getTenant(); /** *
    -   * The resource name of the config.
    -   * Format: tasks/{task_id}/pushNotificationConfigs/{config_id}
    +   * Optional tenant
        * 
    * - * string name = 1 [(.google.api.field_behavior) = REQUIRED]; - * @return The bytes for name. + * string tenant = 4; + * @return The bytes for tenant. */ com.google.protobuf.ByteString - getNameBytes(); + getTenantBytes(); + + /** + *
    +   * The id of the config.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The id. + */ + java.lang.String getId(); + /** + *
    +   * The id of the config.
    +   * 
    + * + * string id = 1 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for id. + */ + com.google.protobuf.ByteString + getIdBytes(); + + /** + *
    +   * The id of the task this config is associated with.
    +   * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The taskId. + */ + java.lang.String getTaskId(); + /** + *
    +   * The id of the task this config is associated with.
    +   * 
    + * + * string task_id = 3 [(.google.api.field_behavior) = REQUIRED]; + * @return The bytes for taskId. + */ + com.google.protobuf.ByteString + getTaskIdBytes(); /** *
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java
    index c5f4bccd6..9fc6b6441 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskState.java
    @@ -58,13 +58,13 @@ public enum TaskState
       TASK_STATE_FAILED(4),
       /**
        * 
    -   * Represents the status a task was cancelled before it finished.
    +   * Represents the status a task was canceled before it finished.
        * This is a terminal state.
        * 
    * - * TASK_STATE_CANCELLED = 5; + * TASK_STATE_CANCELED = 5; */ - TASK_STATE_CANCELLED(5), + TASK_STATE_CANCELED(5), /** *
        * Represents the status that the task requires information to complete.
    @@ -87,8 +87,7 @@ public enum TaskState
       /**
        * 
        * Represents the state that some authentication is needed from the upstream
    -   * client. Authentication is expected to come out-of-band thus this is not
    -   * an interrupted or terminal state.
    +   * client. This is an interrupted state. Authentication is expected to come out-of-band.
        * 
    * * TASK_STATE_AUTH_REQUIRED = 8; @@ -148,13 +147,13 @@ public enum TaskState public static final int TASK_STATE_FAILED_VALUE = 4; /** *
    -   * Represents the status a task was cancelled before it finished.
    +   * Represents the status a task was canceled before it finished.
        * This is a terminal state.
        * 
    * - * TASK_STATE_CANCELLED = 5; + * TASK_STATE_CANCELED = 5; */ - public static final int TASK_STATE_CANCELLED_VALUE = 5; + public static final int TASK_STATE_CANCELED_VALUE = 5; /** *
        * Represents the status that the task requires information to complete.
    @@ -177,8 +176,7 @@ public enum TaskState
       /**
        * 
        * Represents the state that some authentication is needed from the upstream
    -   * client. Authentication is expected to come out-of-band thus this is not
    -   * an interrupted or terminal state.
    +   * client. This is an interrupted state. Authentication is expected to come out-of-band.
        * 
    * * TASK_STATE_AUTH_REQUIRED = 8; @@ -215,7 +213,7 @@ public static TaskState forNumber(int value) { case 2: return TASK_STATE_WORKING; case 3: return TASK_STATE_COMPLETED; case 4: return TASK_STATE_FAILED; - case 5: return TASK_STATE_CANCELLED; + case 5: return TASK_STATE_CANCELED; case 6: return TASK_STATE_INPUT_REQUIRED; case 7: return TASK_STATE_REJECTED; case 8: return TASK_STATE_AUTH_REQUIRED; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java index 38b6b4768..06cb104e0 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEvent.java @@ -184,21 +184,6 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { return status_ == null ? io.a2a.grpc.TaskStatus.getDefaultInstance() : status_; } - public static final int FINAL_FIELD_NUMBER = 4; - private boolean final_ = false; - /** - *
    -   * If true, this is the final event in the stream for this interaction.
    -   * 
    - * - * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; - * @return The final. - */ - @java.lang.Override - public boolean getFinal() { - return final_; - } - public static final int METADATA_FIELD_NUMBER = 5; private com.google.protobuf.Struct metadata_; /** @@ -260,9 +245,6 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) != 0)) { output.writeMessage(3, getStatus()); } - if (final_ != false) { - output.writeBool(4, final_); - } if (((bitField0_ & 0x00000002) != 0)) { output.writeMessage(5, getMetadata()); } @@ -285,10 +267,6 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, getStatus()); } - if (final_ != false) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(4, final_); - } if (((bitField0_ & 0x00000002) != 0)) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(5, getMetadata()); @@ -317,8 +295,6 @@ public boolean equals(final java.lang.Object obj) { if (!getStatus() .equals(other.getStatus())) return false; } - if (getFinal() - != other.getFinal()) return false; if (hasMetadata() != other.hasMetadata()) return false; if (hasMetadata()) { if (!getMetadata() @@ -343,9 +319,6 @@ public int hashCode() { hash = (37 * hash) + STATUS_FIELD_NUMBER; hash = (53 * hash) + getStatus().hashCode(); } - hash = (37 * hash) + FINAL_FIELD_NUMBER; - hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( - getFinal()); if (hasMetadata()) { hash = (37 * hash) + METADATA_FIELD_NUMBER; hash = (53 * hash) + getMetadata().hashCode(); @@ -501,7 +474,6 @@ public Builder clear() { statusBuilder_.dispose(); statusBuilder_ = null; } - final_ = false; metadata_ = null; if (metadataBuilder_ != null) { metadataBuilder_.dispose(); @@ -554,9 +526,6 @@ private void buildPartial0(io.a2a.grpc.TaskStatusUpdateEvent result) { to_bitField0_ |= 0x00000001; } if (((from_bitField0_ & 0x00000008) != 0)) { - result.final_ = final_; - } - if (((from_bitField0_ & 0x00000010) != 0)) { result.metadata_ = metadataBuilder_ == null ? metadata_ : metadataBuilder_.build(); @@ -590,9 +559,6 @@ public Builder mergeFrom(io.a2a.grpc.TaskStatusUpdateEvent other) { if (other.hasStatus()) { mergeStatus(other.getStatus()); } - if (other.getFinal() != false) { - setFinal(other.getFinal()); - } if (other.hasMetadata()) { mergeMetadata(other.getMetadata()); } @@ -639,16 +605,11 @@ public Builder mergeFrom( bitField0_ |= 0x00000004; break; } // case 26 - case 32: { - final_ = input.readBool(); - bitField0_ |= 0x00000008; - break; - } // case 32 case 42: { input.readMessage( internalGetMetadataFieldBuilder().getBuilder(), extensionRegistry); - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000008; break; } // case 42 default: { @@ -1009,50 +970,6 @@ public io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder() { return statusBuilder_; } - private boolean final_ ; - /** - *
    -     * If true, this is the final event in the stream for this interaction.
    -     * 
    - * - * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; - * @return The final. - */ - @java.lang.Override - public boolean getFinal() { - return final_; - } - /** - *
    -     * If true, this is the final event in the stream for this interaction.
    -     * 
    - * - * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; - * @param value The final to set. - * @return This builder for chaining. - */ - public Builder setFinal(boolean value) { - - final_ = value; - bitField0_ |= 0x00000008; - onChanged(); - return this; - } - /** - *
    -     * If true, this is the final event in the stream for this interaction.
    -     * 
    - * - * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; - * @return This builder for chaining. - */ - public Builder clearFinal() { - bitField0_ = (bitField0_ & ~0x00000008); - final_ = false; - onChanged(); - return this; - } - private com.google.protobuf.Struct metadata_; private com.google.protobuf.SingleFieldBuilder< com.google.protobuf.Struct, com.google.protobuf.Struct.Builder, com.google.protobuf.StructOrBuilder> metadataBuilder_; @@ -1065,7 +982,7 @@ public Builder clearFinal() { * @return Whether the metadata field is set. */ public boolean hasMetadata() { - return ((bitField0_ & 0x00000010) != 0); + return ((bitField0_ & 0x00000008) != 0); } /** *
    @@ -1098,7 +1015,7 @@ public Builder setMetadata(com.google.protobuf.Struct value) {
           } else {
             metadataBuilder_.setMessage(value);
           }
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -1116,7 +1033,7 @@ public Builder setMetadata(
           } else {
             metadataBuilder_.setMessage(builderForValue.build());
           }
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return this;
         }
    @@ -1129,7 +1046,7 @@ public Builder setMetadata(
          */
         public Builder mergeMetadata(com.google.protobuf.Struct value) {
           if (metadataBuilder_ == null) {
    -        if (((bitField0_ & 0x00000010) != 0) &&
    +        if (((bitField0_ & 0x00000008) != 0) &&
               metadata_ != null &&
               metadata_ != com.google.protobuf.Struct.getDefaultInstance()) {
               getMetadataBuilder().mergeFrom(value);
    @@ -1140,7 +1057,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) {
             metadataBuilder_.mergeFrom(value);
           }
           if (metadata_ != null) {
    -        bitField0_ |= 0x00000010;
    +        bitField0_ |= 0x00000008;
             onChanged();
           }
           return this;
    @@ -1153,7 +1070,7 @@ public Builder mergeMetadata(com.google.protobuf.Struct value) {
          * .google.protobuf.Struct metadata = 5;
          */
         public Builder clearMetadata() {
    -      bitField0_ = (bitField0_ & ~0x00000010);
    +      bitField0_ = (bitField0_ & ~0x00000008);
           metadata_ = null;
           if (metadataBuilder_ != null) {
             metadataBuilder_.dispose();
    @@ -1170,7 +1087,7 @@ public Builder clearMetadata() {
          * .google.protobuf.Struct metadata = 5;
          */
         public com.google.protobuf.Struct.Builder getMetadataBuilder() {
    -      bitField0_ |= 0x00000010;
    +      bitField0_ |= 0x00000008;
           onChanged();
           return internalGetMetadataFieldBuilder().getBuilder();
         }
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java
    index 6e086c3f3..225000e2a 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/TaskStatusUpdateEventOrBuilder.java
    @@ -77,16 +77,6 @@ public interface TaskStatusUpdateEventOrBuilder extends
        */
       io.a2a.grpc.TaskStatusOrBuilder getStatusOrBuilder();
     
    -  /**
    -   * 
    -   * If true, this is the final event in the stream for this interaction.
    -   * 
    - * - * bool final = 4 [(.google.api.field_behavior) = REQUIRED]; - * @return The final. - */ - boolean getFinal(); - /** *
        * Optional metadata to associate with the task update.
    diff --git a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
    index 0acdc5d3e..45063b3ed 100644
    --- a/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
    +++ b/spec-grpc/src/main/java/io/a2a/grpc/mapper/A2ACommonFieldMapper.java
    @@ -173,13 +173,14 @@ default Map structToMap(Struct struct) {
         /**
          * Converts a Java Object to protobuf Value.
          * 

    - * Supports String, Number, Boolean, Map, and List types. - * Used internally for struct conversion. + * Supports String, Number, Boolean, Map, List types, and null. + * Used for struct conversion and arbitrary JSON data. * * @param value the Java object * @return protobuf Value */ - private Value objectToValue(Object value) { + @SuppressWarnings("unchecked") + default Value objectToValue(Object value) { Value.Builder valueBuilder = Value.newBuilder(); if (value instanceof String) { valueBuilder.setStringValue((String) value); @@ -198,13 +199,21 @@ private Value objectToValue(Object value) { /** * Converts protobuf Value to Java Object. *

    - * Returns appropriate Java type based on Value's kind. - * Used internally for struct conversion. + * Returns appropriate Java type based on Value's kind: + *